public void PaintMarker(GdiGraphicsRenderer renderer, GdiGraphics gr, SvgMarkerPosition markerPos, SvgStyleableElement refElement) { ISharpMarkerHost markerHostElm = (ISharpMarkerHost)refElement; SvgMarkerElement markerElm = (SvgMarkerElement)_svgElement; SvgPointF[] vertexPositions = markerHostElm.MarkerPositions; if (vertexPositions == null) { return; } var comparer = StringComparison.OrdinalIgnoreCase; bool mayHaveCurves = markerHostElm.MayHaveCurves; int start; int len; // Choose which part of the position array to use switch (markerPos) { case SvgMarkerPosition.Start: start = 0; len = 1; break; case SvgMarkerPosition.Mid: start = 1; len = vertexPositions.Length - 2; break; default: // == MarkerPosition.End start = vertexPositions.Length - 1; len = 1; break; } int end = start + len; for (int i = start; i < end; i++) { SvgPointF point = vertexPositions[i]; GdiGraphicsContainer gc = gr.BeginContainer(); gr.TranslateTransform(point.X, point.Y); if (markerElm.OrientType.AnimVal.Equals((ushort)SvgMarkerOrient.Angle)) { double scaleValue = markerElm.OrientAngle.AnimVal.Value; if (!scaleValue.Equals(0)) { gr.RotateTransform((float)scaleValue); } } else { double angle; switch (markerPos) { case SvgMarkerPosition.Start: angle = markerHostElm.GetStartAngle(i); //angle = markerHostElm.GetStartAngle(i + 1); if (vertexPositions.Length >= 2) { SvgPointF pMarkerPoint1 = vertexPositions[start]; SvgPointF pMarkerPoint2 = vertexPositions[end]; float xDiff = pMarkerPoint2.X - pMarkerPoint1.X; float yDiff = pMarkerPoint2.Y - pMarkerPoint1.Y; double angleMarker = (float)(Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI); if (!angleMarker.Equals(angle)) { angle = angleMarker; } } break; case SvgMarkerPosition.Mid: //angle = (markerHostElm.GetEndAngle(i) + markerHostElm.GetStartAngle(i + 1)) / 2; angle = SvgNumber.CalcAngleBisection(markerHostElm.GetEndAngle(i), markerHostElm.GetStartAngle(i + 1)); break; default: angle = markerHostElm.GetEndAngle(i - 1); //double angle2 = markerHostElm.GetEndAngle(i); if (vertexPositions.Length >= 2) { SvgPointF pMarkerPoint1 = vertexPositions[start - 1]; SvgPointF pMarkerPoint2 = vertexPositions[start]; float xDiff = pMarkerPoint2.X - pMarkerPoint1.X; float yDiff = pMarkerPoint2.Y - pMarkerPoint1.Y; double angleMarker = (float)(Math.Atan2(yDiff, xDiff) * 180.0 / Math.PI); if (!angleMarker.Equals(angle)) { angle = angleMarker; } } //if (mayHaveCurves) //{ // angle = this.GetAngleAt(start - 1, angle, markerPos, markerHostElm); //} break; } gr.RotateTransform((float)angle); } // 'viewBox' and 'preserveAspectRatio' attributes // viewBox -> viewport(0, 0, markerWidth, markerHeight) var spar = (SvgPreserveAspectRatio)markerElm.PreserveAspectRatio.AnimVal; double[] translateAndScale = spar.FitToViewBox((SvgRect)markerElm.ViewBox.AnimVal, new SvgRect(0, 0, markerElm.MarkerWidth.AnimVal.Value, markerElm.MarkerHeight.AnimVal.Value)); //// Warning at this time, refX and refY are relative to the painted element's coordinate system. //// We need to move the reference point to the marker's coordinate system //float refX = (float)markerElm.RefX.AnimVal.Value; //float refY = (float)markerElm.RefY.AnimVal.Value; ////if (!(refX.Equals(0) && refY.Equals(0))) ////{ //// var points = new PointF[] { new PointF(refX, refY) }; //// gr.Transform.TransformPoints(points); //// refX = points[0].X; //// refY = points[0].Y; //// gr.TranslateTransform(-refX, -refY); ////} //if (markerElm.MarkerUnits.AnimVal.Equals((ushort)SvgMarkerUnit.StrokeWidth)) //{ // SvgLength strokeWidthLength = new SvgLength(refElement, // "stroke-width", SvgLengthSource.Css, SvgLengthDirection.Viewport, "1"); // float strokeWidth = (float)strokeWidthLength.Value; // gr.ScaleTransform(strokeWidth, strokeWidth); //} //gr.TranslateTransform(-(float)(markerElm.RefX.AnimVal.Value * translateAndScale[2]), // -(float)(markerElm.RefY.AnimVal.Value * translateAndScale[3])); //gr.ScaleTransform((float)translateAndScale[2], (float)translateAndScale[3]); // compute an additional transform for 'strokeWidth' coordinate system ISvgAnimatedEnumeration markerUnits = markerElm.MarkerUnits; if (markerUnits.AnimVal.Equals((ushort)SvgMarkerUnit.StrokeWidth)) { SvgLength strokeWidthLength = new SvgLength(refElement, "stroke-width", SvgLengthSource.Css, SvgLengthDirection.Viewport, SvgConstants.ValOne); double strokeWidth = strokeWidthLength.Value; if (!strokeWidth.Equals(1)) { gr.ScaleTransform((float)strokeWidth, (float)strokeWidth); } } gr.TranslateTransform(-(float)(markerElm.RefX.AnimVal.Value * translateAndScale[2]), -(float)(markerElm.RefY.AnimVal.Value * translateAndScale[3])); if (!(translateAndScale[2].Equals(1) && translateAndScale[3].Equals(1))) { gr.ScaleTransform((float)translateAndScale[2], (float)translateAndScale[3]); } // gr.TranslateTransform(point.X, point.Y); RectangleF rectClip = RectangleF.Empty; if (markerUnits.AnimVal.Equals((ushort)SvgMarkerUnit.StrokeWidth)) { string overflowAttr = markerElm.GetAttribute("overflow"); if (string.IsNullOrWhiteSpace(overflowAttr) || overflowAttr.Equals("scroll", comparer) || overflowAttr.Equals(CssConstants.ValHidden, comparer)) { var markerClip = RectangleF.Empty; SvgRect clipRect = (SvgRect)markerElm.ViewBox.AnimVal; if (clipRect != null && !clipRect.IsEmpty) { rectClip = new RectangleF((float)clipRect.X, (float)clipRect.Y, (float)clipRect.Width, (float)clipRect.Height); } else if (markerElm.IsSizeDefined) { rectClip = new RectangleF(0, 0, (float)markerElm.MarkerWidth.AnimVal.Value, (float)markerElm.MarkerHeight.AnimVal.Value); } } } if (rectClip.IsEmpty) { SetClip(gr); } else { gr.SetClip(rectClip); } renderer.RenderChildren(markerElm); gr.EndContainer(gc); } }
protected void SetClip(GdiGraphics graphics) { if (_svgElement == null) { return; } SvgRenderingHint hint = _svgElement.RenderingHint; // todo: should we correct the clipping to adjust to the off-one-pixel drawing? graphics.TranslateClip(1, 1); #region Clip with clip // see http://www.w3.org/TR/SVG/masking.html#OverflowAndClipProperties if (_svgElement is ISvgSvgElement || _svgElement is ISvgMarkerElement || _svgElement is ISvgSymbolElement || _svgElement is ISvgPatternElement) { // check overflow property CssValue overflow = _svgElement.GetComputedCssValue("overflow", string.Empty) as CssValue; // TODO: clip can have "rect(10 10 auto 10)" CssPrimitiveValue clip = _svgElement.GetComputedCssValue("clip", string.Empty) as CssPrimitiveValue; string sOverflow = null; if (overflow != null || overflow.CssText == "") { sOverflow = overflow.CssText; } else { if (this is ISvgSvgElement) { sOverflow = "hidden"; } } if (sOverflow != null) { // "If the 'overflow' property has a value other than hidden or scroll, the property has no effect (i.e., a clipping rectangle is not created)." if (sOverflow == "hidden" || sOverflow == "scroll") { RectangleF clipRect = RectangleF.Empty; if (clip != null && clip.PrimitiveType == CssPrimitiveType.Rect) { if (_svgElement is ISvgSvgElement) { ISvgSvgElement svgElement = (ISvgSvgElement)_svgElement; SvgRect viewPort = svgElement.Viewport as SvgRect; clipRect = GdiConverter.ToRectangle(viewPort); ICssRect clipShape = (CssRect)clip.GetRectValue(); if (clipShape.Top.PrimitiveType != CssPrimitiveType.Ident) { clipRect.Y += (float)clipShape.Top.GetFloatValue(CssPrimitiveType.Number); } if (clipShape.Left.PrimitiveType != CssPrimitiveType.Ident) { clipRect.X += (float)clipShape.Left.GetFloatValue(CssPrimitiveType.Number); } if (clipShape.Right.PrimitiveType != CssPrimitiveType.Ident) { clipRect.Width = (clipRect.Right - clipRect.X) - (float)clipShape.Right.GetFloatValue(CssPrimitiveType.Number); } if (clipShape.Bottom.PrimitiveType != CssPrimitiveType.Ident) { clipRect.Height = (clipRect.Bottom - clipRect.Y) - (float)clipShape.Bottom.GetFloatValue(CssPrimitiveType.Number); } } } else if (clip == null || (clip.PrimitiveType == CssPrimitiveType.Ident && clip.GetStringValue() == "auto")) { if (_svgElement is ISvgSvgElement) { ISvgSvgElement svgElement = (ISvgSvgElement)_svgElement; SvgRect viewPort = svgElement.Viewport as SvgRect; clipRect = GdiConverter.ToRectangle(viewPort); } else if (_svgElement is ISvgMarkerElement || _svgElement is ISvgSymbolElement || _svgElement is ISvgPatternElement) { // TODO: what to do here? } } if (clipRect != RectangleF.Empty) { graphics.SetClip(clipRect); } } } } #endregion #region Clip with clip-path // see: http://www.w3.org/TR/SVG/masking.html#EstablishingANewClippingPath if (hint == SvgRenderingHint.Shape || hint == SvgRenderingHint.Text || hint == SvgRenderingHint.Clipping || hint == SvgRenderingHint.Masking || hint == SvgRenderingHint.Containment || hint == SvgRenderingHint.Image) { CssPrimitiveValue clipPath = _svgElement.GetComputedCssValue("clip-path", string.Empty) as CssPrimitiveValue; if (clipPath != null && clipPath.PrimitiveType == CssPrimitiveType.Uri) { string absoluteUri = _svgElement.ResolveUri(clipPath.GetStringValue()); SvgClipPathElement eClipPath = _svgElement.OwnerDocument.GetNodeByUri(absoluteUri) as SvgClipPathElement; if (eClipPath != null) { GraphicsPath gpClip = CreateClippingRegion(graphics, eClipPath); RectangleF clipBounds = gpClip != null?gpClip.GetBounds() : RectangleF.Empty; if (clipBounds.Width.Equals(0) || clipBounds.Height.Equals(0)) { return; } SvgUnitType pathUnits = (SvgUnitType)eClipPath.ClipPathUnits.AnimVal; if (pathUnits == SvgUnitType.ObjectBoundingBox) { SvgTransformableElement transElement = _svgElement as SvgTransformableElement; if (transElement != null) { ISvgRect bbox = transElement.GetBBox(); // scale clipping path Matrix matrix = new Matrix(); matrix.Scale((float)bbox.Width, (float)bbox.Height); gpClip.Transform(matrix); graphics.SetClip(gpClip); // offset clip graphics.TranslateClip((float)bbox.X, (float)bbox.Y); } else { throw new NotImplementedException("clip-path with SvgUnitType.ObjectBoundingBox " + "not supported for this type of element: " + _svgElement.GetType()); } } else { graphics.SetClip(gpClip); } gpClip.Dispose(); gpClip = null; } } } #endregion }
private void AddGraphicsPath(SvgTextContentElement element, ref PointF ctp, string text) { if (text.Length == 0) { return; } float emSize = GetComputedFontSize(element); FontFamily family = GetFontFamily(element); int style = GetFontStyle(element); StringFormat sf = GetStringFormat(element); GraphicsPath textGeometry = new GraphicsPath(); float xCorrection = 0; if (sf.Alignment == StringAlignment.Near) { xCorrection = emSize * 1 / 6; } else if (sf.Alignment == StringAlignment.Far) { xCorrection = -emSize * 1 / 6; } float yCorrection = (float)(family.GetCellAscent(FontStyle.Regular)) / (float)(family.GetEmHeight(FontStyle.Regular)) * emSize; // TODO: font property PointF p = new PointF(ctp.X - xCorrection, ctp.Y - yCorrection); textGeometry.AddString(text, family, style, emSize, p, sf); if (!textGeometry.GetBounds().IsEmpty) { float bboxWidth = textGeometry.GetBounds().Width; if (sf.Alignment == StringAlignment.Center) { bboxWidth /= 2; } else if (sf.Alignment == StringAlignment.Far) { bboxWidth = 0; } ctp.X += bboxWidth + emSize / 4; } if (_textMode == GdiTextMode.Outlining) { _graphicsPath.AddPath(textGeometry, false); return; } GdiSvgPaint fillPaint = new GdiSvgPaint(element, "fill"); Brush brush = fillPaint.GetBrush(textGeometry); GdiSvgPaint strokePaint = new GdiSvgPaint(element, "stroke"); Pen pen = strokePaint.GetPen(textGeometry); if (brush != null) { if (brush is PathGradientBrush) { var gps = fillPaint.PaintFill as GdiRadialGradientFill; _graphics.SetClip(gps.GetRadialRegion(textGeometry.GetBounds()), CombineMode.Exclude); SolidBrush tempBrush = new SolidBrush(((PathGradientBrush)brush).InterpolationColors.Colors[0]); _graphics.FillPath(this, tempBrush, textGeometry); tempBrush.Dispose(); _graphics.ResetClip(); } _graphics.FillPath(this, brush, textGeometry); brush.Dispose(); } if (pen != null) { if (pen.Brush is PathGradientBrush) { var gps = strokePaint.PaintFill as GdiRadialGradientFill; GdiGraphicsContainer container = _graphics.BeginContainer(); _graphics.SetClip(gps.GetRadialRegion(textGeometry.GetBounds()), CombineMode.Exclude); SolidBrush tempBrush = new SolidBrush(((PathGradientBrush)pen.Brush).InterpolationColors.Colors[0]); Pen tempPen = new Pen(tempBrush, pen.Width); _graphics.DrawPath(this, tempPen, textGeometry); tempPen.Dispose(); tempBrush.Dispose(); _graphics.EndContainer(container); } _graphics.DrawPath(this, pen, textGeometry); pen.Dispose(); } textGeometry.Dispose(); }