Example #1
0
 public void UnitConverterProfiler()
 {
     SvgUnitConverter.Parse("1pt".AsSpan());
     SvgUnitConverter.Parse("1.25px".AsSpan());
     SvgUnitConverter.Parse("1pc".AsSpan());
     SvgUnitConverter.Parse("15px".AsSpan());
     SvgUnitConverter.Parse("1mm".AsSpan());
     SvgUnitConverter.Parse("3.543307px".AsSpan());
     SvgUnitConverter.Parse("1cm".AsSpan());
     SvgUnitConverter.Parse("35.43307px".AsSpan());
     SvgUnitConverter.Parse("1in".AsSpan());
     SvgUnitConverter.Parse("90px".AsSpan());
     SvgUnitConverter.Parse("15em".AsSpan());
     SvgUnitConverter.Parse("0.2822222mm".AsSpan());
     SvgUnitConverter.Parse("3990".AsSpan());
     SvgUnitConverter.Parse("1990".AsSpan());
     SvgUnitConverter.Parse("-50".AsSpan());
     SvgUnitConverter.Parse(".4in".AsSpan());
     SvgUnitConverter.Parse(".25em".AsSpan());
     SvgUnitConverter.Parse("10%".AsSpan());
     SvgUnitConverter.Parse("1%".AsSpan());
     SvgUnitConverter.Parse("0%".AsSpan());
     SvgUnitConverter.Parse("100%".AsSpan());
     SvgUnitConverter.Parse("1.2em".AsSpan());
     SvgUnitConverter.Parse("medium".AsSpan());
     SvgUnitConverter.Parse("x-small".AsSpan());
     SvgUnitConverter.Parse("xx-large".AsSpan());
     SvgUnitConverter.Parse("657.45".AsSpan());
     SvgUnitConverter.Parse("12.5".AsSpan());
     SvgUnitConverter.Parse("0".AsSpan());
     SvgUnitConverter.Parse("12".AsSpan());
 }
Example #2
0
            public void DrawString(string value)
            {
                // Get any defined anchors
                var xAnchors = GetValues(value.Length, e => e._x, UnitRenderingType.HorizontalOffset);
                var yAnchors = GetValues(value.Length, e => e._y, UnitRenderingType.VerticalOffset);

                using (var fontManager = this.Element.OwnerDocument?.FontManager == null ? new SvgFontManager() : null)
                    using (var font = this.Element.GetFont(this.Renderer, fontManager))
                    {
                        var            fontBaselineHeight = font.Ascent(this.Renderer);
                        PathStatistics pathStats          = null;
                        var            pathScale          = 1.0;
                        if (BaselinePath != null)
                        {
                            pathStats = new PathStatistics(BaselinePath.PathData);
                            if (_authorPathLength > 0)
                            {
                                pathScale = _authorPathLength / pathStats.TotalLength;
                            }
                        }

                        // Get all of the offsets (explicit and defined by spacing)
                        IList <float> xOffsets;
                        IList <float> yOffsets;
                        IList <float> rotations;
                        float         baselineShift = 0.0f;

                        try
                        {
                            this.Renderer.SetBoundable(new FontBoundable(font, (float)(pathStats == null ? 1 : pathStats.TotalLength)));
                            xOffsets = GetValues(value.Length, e => e._dx, UnitRenderingType.Horizontal);
                            yOffsets = GetValues(value.Length, e => e._dy, UnitRenderingType.Vertical);
                            if (StartOffsetAdjust != 0.0f)
                            {
                                if (xOffsets.Count < 1)
                                {
                                    xOffsets.Add(StartOffsetAdjust);
                                }
                                else
                                {
                                    xOffsets[0] += StartOffsetAdjust;
                                }
                            }

                            if (this.Element.LetterSpacing.Value != 0.0f || this.Element.WordSpacing.Value != 0.0f || this.LetterSpacingAdjust != 0.0f)
                            {
                                var spacing     = this.Element.LetterSpacing.ToDeviceValue(this.Renderer, UnitRenderingType.Horizontal, this.Element) + this.LetterSpacingAdjust;
                                var wordSpacing = this.Element.WordSpacing.ToDeviceValue(this.Renderer, UnitRenderingType.Horizontal, this.Element);
                                if (this.Parent == null && this.NumChars == 0 && xOffsets.Count < 1)
                                {
                                    xOffsets.Add(0);
                                }
                                for (int i = (this.Parent == null && this.NumChars == 0 ? 1 : 0); i < value.Length; i++)
                                {
                                    if (i >= xOffsets.Count)
                                    {
                                        xOffsets.Add(spacing + (char.IsWhiteSpace(value[i]) ? wordSpacing : 0));
                                    }
                                    else
                                    {
                                        xOffsets[i] += spacing + (char.IsWhiteSpace(value[i]) ? wordSpacing : 0);
                                    }
                                }
                            }

                            rotations = GetValues(value.Length, e => e._rotations);

                            // Calculate Y-offset due to baseline shift. Don't inherit the value so that it is not accumulated multiple times.
                            var baselineShiftText = Element.BaselineShift.Trim().ToLowerInvariant();
                            if (string.IsNullOrEmpty(baselineShiftText))
                            {
                                baselineShiftText = "baseline";
                            }

                            switch (baselineShiftText)
                            {
                            case "baseline":
                                // do nothing
                                break;

                            case "sub":
                                baselineShift = new SvgUnit(SvgUnitType.Ex, 1).ToDeviceValue(this.Renderer, UnitRenderingType.Vertical, this.Element);
                                break;

                            case "super":
                                baselineShift = -1f * new SvgUnit(SvgUnitType.Ex, 1).ToDeviceValue(this.Renderer, UnitRenderingType.Vertical, this.Element);
                                break;

                            default:
                                var convert   = new SvgUnitConverter();
                                var shiftUnit = (SvgUnit)convert.ConvertFromInvariantString(baselineShiftText);
                                baselineShift = -1f * shiftUnit.ToDeviceValue(this.Renderer, UnitRenderingType.Vertical, this.Element);
                                break;
                            }

                            if (baselineShift != 0.0f)
                            {
                                if (yOffsets.Any())
                                {
                                    yOffsets[0] += baselineShift;
                                }
                                else
                                {
                                    yOffsets.Add(baselineShift);
                                }
                            }
                        }
                        finally
                        {
                            this.Renderer.PopBoundable();
                        }

                        var xTextStart = Current.X;
                        // NOTE: Assuming a horizontal left-to-right font
                        // Render absolutely positioned items in the horizontal direction
                        var yPos = Current.Y;
                        for (int i = 0; i < xAnchors.Count - 1; i++)
                        {
                            FlushPath();
                            _xAnchor = xAnchors[i] + (xOffsets.Count > i ? xOffsets[i] : 0);
                            EnsurePath();
                            yPos = (yAnchors.Count > i ? yAnchors[i] : yPos) + (yOffsets.Count > i ? yOffsets[i] : 0);

                            xTextStart = xTextStart.Equals(Current.X) ? _xAnchor : xTextStart;
                            DrawStringOnCurrPath(value[i].ToString(), font, new PointF(_xAnchor, yPos),
                                                 fontBaselineHeight, (rotations.Count > i ? rotations[i] : rotations.LastOrDefault()));
                        }

                        // Render any remaining characters
                        var renderChar = 0;
                        var xPos       = this.Current.X;
                        if (xAnchors.Any())
                        {
                            FlushPath();
                            renderChar = xAnchors.Count - 1;
                            xPos       = xAnchors.Last();
                            _xAnchor   = xPos;
                        }
                        EnsurePath();


                        // Render individual characters as necessary
                        var lastIndividualChar = renderChar + Math.Max(Math.Max(Math.Max(Math.Max(xOffsets.Count, yOffsets.Count), yAnchors.Count), rotations.Count) - renderChar - 1, 0);
                        if (rotations.LastOrDefault() != 0.0f || pathStats != null)
                        {
                            lastIndividualChar = value.Length;
                        }
                        if (lastIndividualChar > renderChar)
                        {
                            var    charBounds = font.MeasureCharacters(this.Renderer, value.Substring(renderChar, Math.Min(lastIndividualChar + 1, value.Length) - renderChar));
                            PointF pathPoint;
                            float  rotation;
                            float  halfWidth;
                            for (int i = renderChar; i < lastIndividualChar; i++)
                            {
                                xPos += (float)pathScale * (xOffsets.Count > i ? xOffsets[i] : 0) + (charBounds[i - renderChar].X - (i == renderChar ? 0 : charBounds[i - renderChar - 1].X));
                                yPos  = (yAnchors.Count > i ? yAnchors[i] : yPos) + (yOffsets.Count > i ? yOffsets[i] : 0);
                                if (pathStats == null)
                                {
                                    xTextStart = xTextStart.Equals(Current.X) ? xPos : xTextStart;
                                    DrawStringOnCurrPath(value[i].ToString(), font, new PointF(xPos, yPos),
                                                         fontBaselineHeight, (rotations.Count > i ? rotations[i] : rotations.LastOrDefault()));
                                }
                                else
                                {
                                    xPos      = Math.Max(xPos, 0);
                                    halfWidth = charBounds[i - renderChar].Width / 2;
                                    if (pathStats.OffsetOnPath(xPos + halfWidth))
                                    {
                                        pathStats.LocationAngleAtOffset(xPos + halfWidth, out pathPoint, out rotation);
                                        pathPoint = new PointF((float)(pathPoint.X - halfWidth * Math.Cos(rotation * Math.PI / 180) - (float)pathScale * yPos * Math.Sin(rotation * Math.PI / 180)),
                                                               (float)(pathPoint.Y - halfWidth * Math.Sin(rotation * Math.PI / 180) + (float)pathScale * yPos * Math.Cos(rotation * Math.PI / 180)));
                                        xTextStart = xTextStart.Equals(Current.X) ? pathPoint.X : xTextStart;
                                        DrawStringOnCurrPath(value[i].ToString(), font, pathPoint, fontBaselineHeight, rotation);
                                    }
                                }
                            }

                            // Add the kerning to the next character
                            if (lastIndividualChar < value.Length)
                            {
                                xPos += charBounds[charBounds.Count - 1].X - charBounds[charBounds.Count - 2].X;
                            }
                            else
                            {
                                xPos += charBounds.Last().Width;
                            }
                        }

                        // Render the string normally
                        if (lastIndividualChar < value.Length)
                        {
                            xPos += (xOffsets.Count > lastIndividualChar ? xOffsets[lastIndividualChar] : 0);
                            yPos  = (yAnchors.Count > lastIndividualChar ? yAnchors[lastIndividualChar] : yPos) +
                                    (yOffsets.Count > lastIndividualChar ? yOffsets[lastIndividualChar] : 0);
                            xTextStart = xTextStart.Equals(Current.X) ? xPos : xTextStart;
                            DrawStringOnCurrPath(value.Substring(lastIndividualChar), font, new PointF(xPos, yPos),
                                                 fontBaselineHeight, rotations.LastOrDefault());
                            var bounds = font.MeasureString(this.Renderer, value.Substring(lastIndividualChar));
                            xPos += bounds.Width;
                        }


                        NumChars += value.Length;
                        // Undo any baseline shift.  This is not persisted, unlike normal vertical offsets.
                        this.Current    = new PointF(xPos, yPos - baselineShift);
                        this.TextBounds = new RectangleF(xTextStart, 0, this.Current.X - xTextStart, 0);
                    }
            }