コード例 #1
0
ファイル: SvgTextBase.cs プロジェクト: jlcsmtwjt/SVG
        /// <summary>
        /// Sets the path on this element and all child elements.  Uses the state
        /// object to track the state of the drawing
        /// </summary>
        /// <param name="state">State of the drawing operation</param>
        /// <param name="doMeasurements">If true, calculate and apply text length adjustments.</param>
        private void SetPath(TextDrawingState state, bool doMeasurements)
        {
            TextDrawingState origState       = null;
            bool             alignOnBaseline = state.BaselinePath != null && (this.TextAnchor == SvgTextAnchor.Middle || this.TextAnchor == SvgTextAnchor.End);

            if (doMeasurements)
            {
                if (this.TextLength != SvgUnit.None)
                {
                    origState = state.Clone();
                }
                else if (alignOnBaseline)
                {
                    origState          = state.Clone();
                    state.BaselinePath = null;
                }
            }

            foreach (var node in GetContentNodes())
            {
                SvgTextBase textNode = node as SvgTextBase;

                if (textNode == null)
                {
                    if (!string.IsNullOrEmpty(node.Content))
                    {
                        state.DrawString(PrepareText(node.Content));
                    }
                }
                else
                {
                    TextDrawingState newState = new TextDrawingState(state, textNode);

                    textNode.SetPath(newState);
                    state.NumChars += newState.NumChars;
                    state.Current   = newState.Current;
                }
            }

            var path = state.GetPath() ?? new GraphicsPath();

            // Apply any text length adjustments
            if (doMeasurements)
            {
                if (this.TextLength != SvgUnit.None)
                {
                    var specLength = this.TextLength.ToDeviceValue(state.Renderer, UnitRenderingType.Horizontal, this);
                    var actLength  = state.TextBounds.Width;
                    var diff       = (actLength - specLength);
                    if (Math.Abs(diff) > 1.5)
                    {
                        if (this.LengthAdjust == SvgTextLengthAdjust.Spacing)
                        {
                            if (this.X.Count < 2)
                            {
                                var numCharDiff = state.NumChars - origState.NumChars - 1;
                                if (numCharDiff != 0)
                                {
                                    origState.LetterSpacingAdjust = -1 * diff / numCharDiff;
                                    SetPath(origState, false);
                                    return;
                                }
                            }
                        }
                        else
                        {
                            using (var matrix = new Matrix())
                            {
                                matrix.Translate(-1 * state.TextBounds.X, 0, MatrixOrder.Append);
                                matrix.Scale(specLength / actLength, 1, MatrixOrder.Append);
                                matrix.Translate(state.TextBounds.X, 0, MatrixOrder.Append);
                                path.Transform(matrix);
                            }
                        }
                    }
                }
                else if (alignOnBaseline)
                {
                    var bounds = path.GetBounds();
                    if (this.TextAnchor == SvgTextAnchor.Middle)
                    {
                        origState.StartOffsetAdjust = -1 * bounds.Width / 2;
                    }
                    else
                    {
                        origState.StartOffsetAdjust = -1 * bounds.Width;
                    }
                    SetPath(origState, false);
                    return;
                }
            }


            _path            = path;
            this.IsPathDirty = false;
        }
コード例 #2
0
        /// <summary>
        /// Common code for rendering a marker once the orientation angle has been calculated
        /// </summary>
        /// <param name="fAngle"></param>
        /// <param name="pRenderer"></param>
        /// <param name="pOwner"></param>
        /// <param name="pMarkerPoint"></param>
        private void RenderPart2(float fAngle, ISvgRenderer pRenderer, SvgVisualElement pOwner, PointF pMarkerPoint)
        {
            using (var pRenderPen = CreatePen(pOwner, pRenderer))
            {
                using (var markerPath = GetClone(pOwner, pRenderer))
                {
                    using (var transMatrix = new Matrix())
                    {
                        transMatrix.Translate(pMarkerPoint.X, pMarkerPoint.Y);
                        if (Orient.IsAuto)
                        {
                            transMatrix.Rotate(fAngle);
                        }
                        else
                        {
                            transMatrix.Rotate(Orient.Angle);
                        }
                        switch (MarkerUnits)
                        {
                        case SvgMarkerUnits.StrokeWidth:
                            if (ViewBox.Width > 0 && ViewBox.Height > 0)
                            {
                                transMatrix.Scale(MarkerWidth, MarkerHeight);
                                var strokeWidth = pOwner.StrokeWidth.ToDeviceValue(pRenderer, UnitRenderingType.Other, this);
                                transMatrix.Translate(AdjustForViewBoxWidth(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this) *
                                                                            strokeWidth),
                                                      AdjustForViewBoxHeight(-RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this) *
                                                                             strokeWidth));
                            }
                            else
                            {
                                // SvgMarkerUnits.UserSpaceOnUse
                                // TODO: We know this isn't correct.
                                //        But use this until the TODOs from AdjustForViewBoxWidth and AdjustForViewBoxHeight are done.
                                //  MORE see Unit Test "MakerEndTest.TestArrowCodeCreation()"
                                transMatrix.Translate(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this),
                                                      -RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this));
                            }
                            break;

                        case SvgMarkerUnits.UserSpaceOnUse:
                            transMatrix.Translate(-RefX.ToDeviceValue(pRenderer, UnitRenderingType.Horizontal, this),
                                                  -RefY.ToDeviceValue(pRenderer, UnitRenderingType.Vertical, this));
                            break;
                        }

                        if (MarkerElement != null && MarkerElement.Transforms != null)
                        {
                            using (var matrix = MarkerElement.Transforms.GetMatrix())
                                transMatrix.Multiply(matrix);
                        }
                        markerPath.Transform(transMatrix);
                        if (pRenderPen != null)
                        {
                            pRenderer.DrawPath(pRenderPen, markerPath);
                        }

                        SvgPaintServer pFill     = this.Children.First().Fill;
                        SvgFillRule    pFillRule = FillRule; // TODO: What do we use the fill rule for?

                        if (pFill != null)
                        {
                            using (var pBrush = pFill.GetBrush(this, pRenderer, FixOpacityValue(FillOpacity)))
                            {
                                pRenderer.FillPath(pBrush, markerPath);
                            }
                        }
                    }
                }
            }
        }
コード例 #3
0
        protected override Brush CreateBrush(SvgVisualElement renderingElement, ISvgRenderer renderer, float opacity, bool forStroke)
        {
            // TODO: figure out how to do the brush transform in the presence of FocalRadius
            try
            {
                if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox)
                {
                    renderer.SetBoundable(renderingElement);
                }

                // Calculate the path and transform it appropriately
                var center = new PointF(NormalizeUnit(CenterX).ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
                                        NormalizeUnit(CenterY).ToDeviceValue(renderer, UnitRenderingType.Vertical, this));
                var focals = new PointF[] { new PointF(NormalizeUnit(FocalX).ToDeviceValue(renderer, UnitRenderingType.Horizontal, this),
                                                       NormalizeUnit(FocalY).ToDeviceValue(renderer, UnitRenderingType.Vertical, this)) };
                var specifiedRadius = NormalizeUnit(Radius).ToDeviceValue(renderer, UnitRenderingType.Other, this);
                var path            = new GraphicsPath();
                path.AddEllipse(
                    center.X - specifiedRadius, center.Y - specifiedRadius,
                    specifiedRadius * 2, specifiedRadius * 2
                    );

                using (var transform = EffectiveGradientTransform)
                {
                    var bounds = renderer.GetBoundable().Bounds;
                    transform.Translate(bounds.X, bounds.Y, MatrixOrder.Prepend);
                    if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox)
                    {
                        transform.Scale(bounds.Width, bounds.Height, MatrixOrder.Prepend);
                    }
                    path.Transform(transform);
                    transform.TransformPoints(focals);
                }


                // Calculate any required scaling
                var scaleBounds = RectangleF.Inflate(renderingElement.Bounds, renderingElement.StrokeWidth, renderingElement.StrokeWidth);
                var scale       = CalcScale(scaleBounds, path);

                // Not ideal, but this makes sure that the rest of the shape gets properly filled or drawn
                if (scale > 1.0f && SpreadMethod == SvgGradientSpreadMethod.Pad)
                {
                    var stop        = Stops.Last();
                    var origColor   = stop.GetColor(renderingElement);
                    var renderColor = System.Drawing.Color.FromArgb((int)Math.Round(opacity * stop.StopOpacity * 255), origColor);

                    var origClip = renderer.GetClip();
                    try
                    {
                        using (var solidBrush = new SolidBrush(renderColor))
                        {
                            var newClip = origClip.Clone();
                            newClip.Exclude(path);
                            renderer.SetClip(newClip);

                            var renderPath = (GraphicsPath)renderingElement.Path(renderer);
                            if (forStroke)
                            {
                                using (var pen = new Pen(solidBrush, renderingElement.StrokeWidth.ToDeviceValue(renderer, UnitRenderingType.Other, renderingElement)))
                                {
                                    renderer.DrawPath(pen, renderPath);
                                }
                            }
                            else
                            {
                                renderer.FillPath(solidBrush, renderPath);
                            }
                        }
                    }
                    finally
                    {
                        renderer.SetClip(origClip);
                    }
                }

                // Get the color blend and any tweak to the scaling
                var blend = CalculateColorBlend(renderer, opacity, scale, out scale);

                // Transform the path based on the scaling
                var gradBounds  = path.GetBounds();
                var transCenter = new PointF(gradBounds.Left + gradBounds.Width / 2, gradBounds.Top + gradBounds.Height / 2);
                using (var scaleMat = new Matrix())
                {
                    scaleMat.Translate(-1 * transCenter.X, -1 * transCenter.Y, MatrixOrder.Append);
                    scaleMat.Scale(scale, scale, MatrixOrder.Append);
                    scaleMat.Translate(transCenter.X, transCenter.Y, MatrixOrder.Append);
                    path.Transform(scaleMat);
                }

                // calculate the brush
                var brush = new PathGradientBrush(path);
                brush.CenterPoint         = focals[0];
                brush.InterpolationColors = blend;

                return(brush);
            }
            finally
            {
                if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox)
                {
                    renderer.PopBoundable();
                }
            }
        }
コード例 #4
0
ファイル: SvgFontDefn.cs プロジェクト: carbon/SVG
        private GraphicsPath GetPath(ISvgRenderer renderer, string text, IList <RectangleF> ranges, bool measureSpaces)
        {
            EnsureDictionaries();

            RectangleF   bounds;
            SvgGlyph     glyph;
            SvgKern      kern;
            GraphicsPath path;
            SvgGlyph     prevGlyph = null;
            Matrix       scaleMatrix;
            float        xPos = 0;

            var ascent = Ascent(renderer);

            var result = new GraphicsPath();

            if (string.IsNullOrEmpty(text))
            {
                return(result);
            }

            for (int i = 0; i < text.Length; i++)
            {
                if (!_glyphs.TryGetValue(text.Substring(i, 1), out glyph))
                {
                    glyph = _font.Descendants().OfType <SvgMissingGlyph>().First();
                }
                if (prevGlyph != null && _kerning.TryGetValue(prevGlyph.GlyphName + "|" + glyph.GlyphName, out kern))
                {
                    xPos -= kern.Kerning * _emScale;
                }
                path        = (GraphicsPath)glyph.Path(renderer).Clone();
                scaleMatrix = new Matrix();
                scaleMatrix.Scale(_emScale, -1 * _emScale, MatrixOrder.Append);
                scaleMatrix.Translate(xPos, ascent, MatrixOrder.Append);
                path.Transform(scaleMatrix);
                scaleMatrix.Dispose();

                bounds = path.GetBounds();
                if (ranges != null)
                {
                    if (measureSpaces && bounds == RectangleF.Empty)
                    {
                        ranges.Add(new RectangleF(xPos, 0, glyph.HorizAdvX * _emScale, ascent));
                    }
                    else
                    {
                        ranges.Add(bounds);
                    }
                }
                if (path.PointCount > 0)
                {
                    result.AddPath(path, false);
                }

                xPos     += glyph.HorizAdvX * _emScale;
                prevGlyph = glyph;
            }

            return(result);
        }
コード例 #5
0
ファイル: SvgPatternServer.cs プロジェクト: github188/Disney
        /// <summary>
        /// Gets a <see cref="Brush"/> representing the current paint server.
        /// </summary>
        /// <param name="renderingElement">The owner <see cref="SvgVisualElement"/>.</param>
        /// <param name="opacity">The opacity of the brush.</param>
        public override Brush GetBrush(SvgVisualElement renderingElement, ISvgRenderer renderer, float opacity, bool forStroke = false)
        {
            var chain = new List <SvgPatternServer>();
            var curr  = this;

            while (curr != null)
            {
                chain.Add(curr);
                curr = SvgDeferredPaintServer.TryGet <SvgPatternServer>(curr._inheritGradient, renderingElement);
            }

            var childElem = chain.Where((p) => p.Children != null && p.Children.Count > 0).FirstOrDefault();

            if (childElem == null)
            {
                return(null);
            }
            var widthElem  = chain.Where((p) => p.Width != null && p.Width != SvgUnit.None).FirstOrDefault();
            var heightElem = chain.Where((p) => p.Height != null && p.Height != SvgUnit.None).FirstOrDefault();

            if (widthElem == null && heightElem == null)
            {
                return(null);
            }

            var viewBoxElem = chain.Where((p) => p.ViewBox != null && p.ViewBox != SvgViewBox.Empty).FirstOrDefault();
            var viewBox     = viewBoxElem == null ? SvgViewBox.Empty : viewBoxElem.ViewBox;
            var xElem       = chain.Where((p) => p.X != null && p.X != SvgUnit.None).FirstOrDefault();
            var yElem       = chain.Where((p) => p.Y != null && p.Y != SvgUnit.None).FirstOrDefault();
            var xUnit       = xElem == null ? SvgUnit.Empty : xElem.X;
            var yUnit       = yElem == null ? SvgUnit.Empty : yElem.Y;

            var patternUnitElem        = chain.Where((p) => p.PatternUnits != SvgCoordinateUnits.Inherit).FirstOrDefault();
            var patternUnits           = (patternUnitElem == null ? SvgCoordinateUnits.ObjectBoundingBox : patternUnitElem.PatternUnits);
            var patternContentUnitElem = chain.Where((p) => p.PatternContentUnits != SvgCoordinateUnits.Inherit).FirstOrDefault();
            var patternContentUnits    = (patternContentUnitElem == null ? SvgCoordinateUnits.UserSpaceOnUse : patternContentUnitElem.PatternContentUnits);

            try
            {
                if (patternUnits == SvgCoordinateUnits.ObjectBoundingBox)
                {
                    renderer.SetBoundable(renderingElement);
                }

                using (var patternMatrix = new Matrix())
                {
                    var bounds = renderer.GetBoundable().Bounds;
                    var xScale = (patternUnits == SvgCoordinateUnits.ObjectBoundingBox ? bounds.Width : 1);
                    var yScale = (patternUnits == SvgCoordinateUnits.ObjectBoundingBox ? bounds.Height : 1);

                    float x = xScale * NormalizeUnit(xUnit).ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
                    float y = yScale * NormalizeUnit(yUnit).ToDeviceValue(renderer, UnitRenderingType.Vertical, this);

                    float width  = xScale * NormalizeUnit(widthElem.Width).ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
                    float height = yScale * NormalizeUnit(heightElem.Height).ToDeviceValue(renderer, UnitRenderingType.Vertical, this);

                    // Apply a scale if needed
                    patternMatrix.Scale((patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox ? bounds.Width : 1) *
                                        (viewBox.Width > 0 ? width / viewBox.Width : 1),
                                        (patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox ? bounds.Height : 1) *
                                        (viewBox.Height > 0 ? height / viewBox.Height : 1), MatrixOrder.Prepend);

                    Bitmap image = new Bitmap((int)width, (int)height);
                    using (var iRenderer = SvgRenderer.FromImage(image))
                    {
                        iRenderer.SetBoundable((_patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox) ? new GenericBoundable(0, 0, width, height) : renderer.GetBoundable());
                        iRenderer.Transform     = patternMatrix;
                        iRenderer.SmoothingMode = SmoothingMode.AntiAlias;
                        iRenderer.SetClip(new Region(new RectangleF(0, 0,
                                                                    viewBox.Width > 0 ? viewBox.Width : width,
                                                                    viewBox.Height > 0 ? viewBox.Height : height)));

                        foreach (SvgElement child in childElem.Children)
                        {
                            child.RenderElement(iRenderer);
                        }
                    }

                    TextureBrush textureBrush   = new TextureBrush(image);
                    var          brushTransform = EffectivePatternTransform.Clone();
                    brushTransform.Translate(x, y, MatrixOrder.Append);
                    textureBrush.Transform = brushTransform;
                    return(textureBrush);
                }
            }
            finally
            {
                if (this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox)
                {
                    renderer.PopBoundable();
                }
            }
        }
コード例 #6
0
 public void ScaleTransform(float sx, float sy, MatrixOrder order = MatrixOrder.Append)
 {
     _innerMatrix.Scale(sx, sy, order);
 }
コード例 #7
0
        /// <summary>
        /// Gets a <see cref="Brush"/> representing the current paint server.
        /// </summary>
        /// <param name="renderingElement">The owner <see cref="SvgVisualElement"/>.</param>
        /// <param name="opacity">The opacity of the brush.</param>
        public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity)
        {
            // If there aren't any children, return null
            if (this.Children.Count == 0)
            {
                return(null);
            }

            // Can't render if there are no dimensions
            if (this._width.Value == 0.0f || this._height.Value == 0.0f)
            {
                return(null);
            }

            try
            {
                if (this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox)
                {
                    renderer.Boundable(renderingElement);
                }

                float width  = this._width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this);
                float height = this._height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this);

                Matrix patternMatrix = new Matrix();
                // Apply a translate if needed
                if (this._x.Value > 0.0f || this._y.Value > 0.0f)
                {
                    float x = this._x.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this);
                    float y = this._y.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this);

                    patternMatrix.Translate(x + -1.0f, y + -1.0f);
                }
                else
                {
                    patternMatrix.Translate(-1, -1);
                }

                if (this.ViewBox.Height > 0 || this.ViewBox.Width > 0)
                {
                    patternMatrix.Scale(this.Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / this.ViewBox.Width,
                                        this.Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this) / this.ViewBox.Height);
                }

                Bitmap image = new Bitmap((int)width, (int)height);
                using (SvgRenderer iRenderer = SvgRenderer.FromImage(image))
                {
                    iRenderer.Boundable((_patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox) ? new GenericBoundable(0, 0, width, height) : renderer.Boundable());
                    iRenderer.Transform          = patternMatrix;
                    iRenderer.CompositingQuality = CompositingQuality.HighQuality;
                    iRenderer.SmoothingMode      = SmoothingMode.AntiAlias;
                    iRenderer.PixelOffsetMode    = PixelOffsetMode.Half;

                    foreach (SvgElement child in this.Children)
                    {
                        child.RenderElement(iRenderer);
                    }

                    iRenderer.Save();
                }

                image.Save(string.Format(@"C:\test{0:D3}.png", imgNumber++));

                TextureBrush textureBrush = new TextureBrush(image);

                return(textureBrush);
            }
            finally
            {
                if (this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox)
                {
                    renderer.PopBoundable();
                }
            }
        }
コード例 #8
0
        /// <summary>
        /// Sets the path on this element and all child elements.  Uses the state
        /// object to track the state of the drawing
        /// </summary>
        /// <param name="state">State of the drawing operation</param>
        private void SetPath(TextDrawingState state, bool doMeasurements)
        {
            TextDrawingState origState       = null;
            bool             alignOnBaseline = state.BaselinePath != null && (this.TextAnchor == SvgTextAnchor.Middle || this.TextAnchor == SvgTextAnchor.End);

            if (doMeasurements)
            {
                if (this.TextLength != SvgUnit.None)
                {
                    origState = state.Clone();
                }
                else if (alignOnBaseline)
                {
                    origState          = state.Clone();
                    state.BaselinePath = null;
                }
            }

            foreach (var node in GetContentNodes())
            {
                SvgTextBase textNode = node as SvgTextBase;

                if (textNode == null)
                {
                    if (!string.IsNullOrEmpty(node.Content))
                    {
                        state.DrawString(PrepareText(node.Content));
                    }
                }
                else
                {
                    TextDrawingState newState = new TextDrawingState(state, textNode);

                    textNode.SetPath(newState);
                    state.NumChars += newState.NumChars;
                    state.Current   = newState.Current;
                }
            }

            var path = state.GetPath() ?? new GraphicsPath();

            // Apply any text length adjustments
            if (doMeasurements)
            {
                if (this.TextLength != SvgUnit.None)
                {
                    var bounds     = path.GetBounds();
                    var specLength = this.TextLength.ToDeviceValue(state.Renderer, UnitRenderingType.Horizontal, this);
                    var actLength  = bounds.Width;
                    var diff       = (actLength - specLength);
                    if (Math.Abs(diff) > 1.5)
                    {
                        if (this.LengthAdjust == SvgTextLengthAdjust.spacing)
                        {
                            origState.LetterSpacingAdjust = -1 * diff / (state.NumChars - origState.NumChars - 1);
                            SetPath(origState, false);
                            return;
                        }
                        else
                        {
                            using (var matrix = new Matrix())
                            {
                                matrix.Translate(-1 * bounds.X, 0, MatrixOrder.Append);
                                matrix.Scale(specLength / actLength, 1, MatrixOrder.Append);
                                matrix.Translate(bounds.X, 0, MatrixOrder.Append);
                                path.Transform(matrix);
                            }
                        }
                    }
                }
                else if (alignOnBaseline)
                {
                    var bounds = path.GetBounds();
                    if (this.TextAnchor == SvgTextAnchor.Middle)
                    {
                        origState.StartOffsetAdjust = -1 * bounds.Width / 2;
                    }
                    else
                    {
                        origState.StartOffsetAdjust = -1 * bounds.Width;
                    }
                    SetPath(origState, false);
                    return;
                }
            }

            _path            = path;
            this.IsPathDirty = false;

            // If we have child tspans we need to correct the position to account for text-anchor
            var childTSpans = GetContentNodes().OfType <SvgTextSpan>().ToList();

            if (childTSpans.Count > 1) // If there's only one tspan we'll allow it to adjust itself in FlushPath()
            {
                if (!(TextAnchor == SvgTextAnchor.Middle || TextAnchor == SvgTextAnchor.End))
                {
                    return;
                }

                // BUG: If we have rotations things break
                // Skip them for now
                if (this.Transforms.OfType <SvgRotate>().Any())
                {
                    return;
                }

                // Need to split tspans by line
                // An element with an x attribute set seems to start a new render line
                var lines = new List <List <SvgTextSpan> >();
                foreach (var tspan in childTSpans)
                {
                    if (lines.Count == 0 || tspan.X.Any())
                    {
                        lines.Add(new List <SvgTextSpan>());
                    }

                    lines.Last().Add(tspan);
                }

                foreach (var line in lines)
                {
                    // Find total width of line
                    var totalLineWidth = line.Sum(o => o.Bounds.Width);
                    // BUG: If we have tspans with spaces in the source between them (like '<a></a> <b></b>'), they should be
                    // rendered with that space, but there's a bug elsewhere which does this wrong and ignores it.
                    // I'm also ignoring it here, because I don't know how to fix this correctly.

                    float xOffset = 0;
                    switch (TextAnchor)
                    {
                    case SvgTextAnchor.Middle:
                        xOffset = -totalLineWidth / 2;
                        break;

                    case SvgTextAnchor.End:
                        xOffset = -totalLineWidth;
                        break;

                    default:
                        throw new InvalidOperationException("Shouldn't have got here with TextAnchor " + TextAnchor);
                    }

                    using (var matrix = new Matrix())
                    {
                        matrix.Translate(xOffset, 0);

                        foreach (var node in line)
                        {
                            node._path.Transform(matrix);
                        }
                    }
                }

                // The path for the parent text node renders a black copy of the text for some reason
                _path.Reset();
            }
        }