diff --git a/Source/SVGImage/SVG/Shapes/TextRender.cs b/Source/SVGImage/SVG/Shapes/TextRender.cs
index ad49657..b3931d6 100644
--- a/Source/SVGImage/SVG/Shapes/TextRender.cs
+++ b/Source/SVGImage/SVG/Shapes/TextRender.cs
@@ -1,363 +1,367 @@
-using System.Collections.Generic;
-using System.Windows.Media;
-using System.Windows;
-
-namespace SVGImage.SVG.Shapes
-{
- using Utils;
- using System.Linq;
- using System.Windows.Markup;
- using System;
- using System.Reflection;
- using System.Windows.Documents;
-
- ///
- /// This class is responsible for rendering text shapes in SVG.
- ///
- public sealed partial class TextRender : TextRenderBase
- {
- public override GeometryGroup BuildTextGeometry(TextShape text)
- {
-
- using(TextRenderState state = new TextRenderState())
- {
- if (!state.Setup(text))
- {
- return null; // No characters to render
- }
- return CreateGeometry(text, state);
- }
- }
-
-
-
- public static readonly DependencyProperty TextSpanTextStyleProperty = DependencyProperty.RegisterAttached(
- "TextSpanTextStyle", typeof(TextStyle), typeof(DependencyObject));
- private static void SetTextSpanTextStyle(DependencyObject obj, TextStyle value)
- {
- obj.SetValue(TextSpanTextStyleProperty, value);
- }
- public static TextStyle GetTextSpanTextStyle(DependencyObject obj)
- {
- return (TextStyle)obj.GetValue(TextSpanTextStyleProperty);
- }
-
- private sealed class TextChunk
- {
- public List GlyphRuns { get; set; } = new List();
- public Dictionary> BackgroundDecorations { get; set; } = new Dictionary>();
- public Dictionary> ForegroundDecorations { get; set; } = new Dictionary>();
- public Dictionary GlyphRunBounds { get; set; } = new Dictionary();
- public Dictionary TextStyles { get; set; } = new Dictionary();
- public Dictionary TextContainers { get; set; } = new Dictionary();
- public TextAlignment TextAlignment { get; set; }
-
- public GeometryGroup BuildGeometry()
- {
- double alignmentOffset = GetAlignmentOffset();
- bool nonZeroAlignmentOffset = !alignmentOffset.IsNearlyZero();
- GeometryGroup geometryGroup = new GeometryGroup();
- foreach (var glyphRun in GlyphRuns)
- {
- var runGeometry = !nonZeroAlignmentOffset ? glyphRun.BuildGeometry() : glyphRun.CreateOffsetRun(alignmentOffset, 0).BuildGeometry();
+using System.Collections.Generic;
+using System.Windows.Media;
+using System.Windows;
+
+namespace SVGImage.SVG.Shapes
+{
+ using Utils;
+ using System.Linq;
+ using System.Windows.Markup;
+ using System;
+ using System.Reflection;
+ using System.Windows.Documents;
+
+ ///
+ /// This class is responsible for rendering text shapes in SVG.
+ ///
+ public sealed partial class TextRender : TextRenderBase
+ {
+ public override GeometryGroup BuildTextGeometry(TextShape text)
+ {
+
+ using(TextRenderState state = new TextRenderState())
+ {
+ if (!state.Setup(text))
+ {
+ return null; // No characters to render
+ }
+ return CreateGeometry(text, state);
+ }
+ }
+
+
+
+ public static readonly DependencyProperty TextSpanTextStyleProperty = DependencyProperty.RegisterAttached(
+ "TextSpanTextStyle", typeof(TextStyle), typeof(DependencyObject));
+ private static void SetTextSpanTextStyle(DependencyObject obj, TextStyle value)
+ {
+ obj.SetValue(TextSpanTextStyleProperty, value);
+ }
+ public static TextStyle GetTextSpanTextStyle(DependencyObject obj)
+ {
+ return (TextStyle)obj.GetValue(TextSpanTextStyleProperty);
+ }
+
+ private sealed class TextChunk
+ {
+ public List GlyphRuns { get; set; } = new List();
+ public Dictionary> BackgroundDecorations { get; set; } = new Dictionary>();
+ public Dictionary> ForegroundDecorations { get; set; } = new Dictionary>();
+ public Dictionary GlyphRunBounds { get; set; } = new Dictionary();
+ public Dictionary TextStyles { get; set; } = new Dictionary();
+ public Dictionary TextContainers { get; set; } = new Dictionary();
+ public TextAlignment TextAlignment { get; set; }
+
+ public GeometryGroup BuildGeometry()
+ {
+ double alignmentOffset = GetAlignmentOffset();
+ bool nonZeroAlignmentOffset = !alignmentOffset.IsNearlyZero();
+ GeometryGroup geometryGroup = new GeometryGroup();
+ foreach (var glyphRun in GlyphRuns)
+ {
+ var runGeometry = !nonZeroAlignmentOffset ? glyphRun.BuildGeometry() : glyphRun.CreateOffsetRun(alignmentOffset, 0).BuildGeometry();
if (runGeometry.IsFrozen)
{
runGeometry = runGeometry.CloneCurrentValue();
- }
- geometryGroup.Children.Add(runGeometry);
- if (TextStyles.TryGetValue(glyphRun, out TextStyle textStyle))
- {
- TextRender.SetTextSpanTextStyle(runGeometry, textStyle);
- }
- if (TextContainers.TryGetValue(glyphRun, out TextShapeBase textContainer))
- {
- TextRender.SetElement(runGeometry, textContainer);
- }
- if (BackgroundDecorations.TryGetValue(glyphRun, out List backgroundDecorations))
- {
- foreach (var decoration in backgroundDecorations)
- {
- if (nonZeroAlignmentOffset)
- {
- decoration.Offset(alignmentOffset, 0);
- }
- //Underline and OverLine should be drawn behind the text
- geometryGroup.Children.Insert(0, new RectangleGeometry(decoration));
- }
- }
- if (ForegroundDecorations.TryGetValue(glyphRun, out List foregroundDecorations))
- {
- foreach (var decoration in foregroundDecorations)
- {
- if (nonZeroAlignmentOffset)
- {
- decoration.Offset(alignmentOffset, 0);
- }
- //Strikethrough should be drawn on top of the text
- geometryGroup.Children.Add(new RectangleGeometry(decoration));
- }
- }
- }
- return geometryGroup;
- }
-
- private Rect GetBoundingBox()
- {
- if (GlyphRunBounds.Count == 0)
- {
- return Rect.Empty;
- }
- Rect boundingBox = GlyphRunBounds.First().Value;
- foreach (var kvp in GlyphRunBounds)
- {
- boundingBox.Union(kvp.Value);
- }
- return boundingBox;
- }
-
- private double GetAlignmentOffset()
- {
- if(TextAlignment == TextAlignment.Left)
- {
- return 0; // No offset needed for left alignment
- }
- var boundingBox = GetBoundingBox();
- double totalWidth = boundingBox.Width;
- double alignmentOffset = 0;
- switch (TextAlignment)
- {
- case TextAlignment.Left:
- break;
- case TextAlignment.Right:
- alignmentOffset = totalWidth;
- break;
- case TextAlignment.Center:
- alignmentOffset = totalWidth / 2d;
- break;
- case TextAlignment.Justify:
- // Justify is not implemented
- break;
- default:
- break;
- }
- return alignmentOffset;
- }
- }
- private static GeometryGroup CreateGeometry(TextShape root, TextRenderState state)
- {
- state.Resolve(root);
-
- List textStrings = new List();
- TextStyleStack textStyleStacks = new TextStyleStack();
- PopulateTextStrings(textStrings, root, textStyleStacks);
- GeometryGroup mainGeometryGroup = new GeometryGroup();
- var baselineOrigin = new Point(root.X.FirstOrDefault().Value, root.Y.FirstOrDefault().Value);
- var isSideways = root.WritingMode == WritingMode.HorizontalTopToBottom;
- TextAlignment currentTextAlignment = root.TextStyle.TextAlignment;
- List textChunks = new List();
- bool newTextChunk = false;
- TextChunk currentTextChunk = null;
- foreach (TextString textString in textStrings)
- {
- var textStyle = textString.TextStyle;
- Typeface font = textString.TextStyle.GetTypeface();
- if (CreateRun(textString, state, font, isSideways, baselineOrigin, out Point newBaseline, out newTextChunk, ref currentTextAlignment) is GlyphRun run)
- {
- if (newTextChunk)
- {
- if(currentTextChunk != null)
- {
- // Add the current text chunk to the list
- textChunks.Add(currentTextChunk);
- }
- currentTextChunk = new TextChunk();
- currentTextChunk.TextAlignment = currentTextAlignment;
- }
- var runGeometry = run.BuildGeometry();
- currentTextChunk.GlyphRuns.Add(run);
- currentTextChunk.TextStyles[run] = textStyle;
- currentTextChunk.GlyphRunBounds[run] = runGeometry.Bounds;
- currentTextChunk.TextContainers[run] = (TextShapeBase)textString.Parent;
- if (textStyle.TextDecoration != null && textStyle.TextDecoration.Count > 0)
- {
- GetTextDecorations(runGeometry, textStyle, font, baselineOrigin, out List backgroundDecorations, out List foregroundDecorations);
- if(backgroundDecorations.Count > 0)
- {
- currentTextChunk.BackgroundDecorations[run] = backgroundDecorations;
- }
- if (foregroundDecorations.Count > 0)
- {
- currentTextChunk.ForegroundDecorations[run] = foregroundDecorations;
- }
- }
- }
- baselineOrigin = newBaseline;
- }
- textChunks.Add(currentTextChunk);
-
- foreach(var textChunk in textChunks)
- {
- if (textChunk.GlyphRuns.Count == 0)
- {
- continue; // No glyphs to render in this chunk
- }
- GeometryGroup geometryGroup = textChunk.BuildGeometry();
- mainGeometryGroup.Children.Add(geometryGroup);
- }
-
- mainGeometryGroup.Transform = root.Transform;
- return mainGeometryGroup;
- }
-
- ///
- ///
- ///
- ///
- /// Not perfect, the lines are not continuous across multiple text strings.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- private static void GetTextDecorations(Geometry geometry, TextStyle textStyle, Typeface font, Point baselineOrigin, out List backgroundDecorations, out List foregroundDecorations)
- {
- backgroundDecorations = new List();
- foregroundDecorations = new List();
- double decorationPos = 0;
- double decorationThickness = 0;
- double fontSize = textStyle.FontSize;
- double baselineY = baselineOrigin.Y;
- foreach(TextDecorationLocation textDecorationLocation in textStyle.TextDecoration.Select(td=>td.Location))
- {
- if (textDecorationLocation == TextDecorationLocation.Strikethrough)
- {
- decorationPos = baselineY - (font.StrikethroughPosition * fontSize);
- decorationThickness = font.StrikethroughThickness * fontSize;
- Rect bounds = new Rect(geometry.Bounds.Left, decorationPos, geometry.Bounds.Width, decorationThickness);
- foregroundDecorations.Add(bounds);
- }
- else if (textDecorationLocation == TextDecorationLocation.Underline)
- {
- decorationPos = baselineY - (font.UnderlinePosition * fontSize);
- decorationThickness = font.UnderlineThickness * fontSize;
- Rect bounds = new Rect(geometry.Bounds.Left, decorationPos, geometry.Bounds.Width, decorationThickness);
- backgroundDecorations.Add(bounds);
- }
- else if (textDecorationLocation == TextDecorationLocation.OverLine)
- {
- decorationPos = baselineY - fontSize;
- decorationThickness = font.StrikethroughThickness * fontSize;
- Rect bounds = new Rect(geometry.Bounds.Left, decorationPos, geometry.Bounds.Width, decorationThickness);
- backgroundDecorations.Add(bounds);
- }
- }
- }
-
- private static GlyphRun CreateRun(TextString textString, TextRenderState state, Typeface font, bool isSideways, Point baselineOrigin, out Point newBaseline, out bool newTextChunk, ref TextAlignment currentTextAlignment)
- {
- newTextChunk = textString.FirstCharacter.GlobalIndex == 0;
- var textStyle = textString.TextStyle;
- var characterInfos = textString.GetCharacters();
- if(characterInfos is null ||characterInfos.Length == 0)
- {
- newBaseline = baselineOrigin;
- return null;
- }
- if (!font.TryGetGlyphTypeface(out var glyphFace))
- {
- newBaseline = baselineOrigin;
- return null;
- }
- string deviceFontName = null;
- IList clusterMap = null;
- IList caretStops = null;
- XmlLanguage language = null;
- var glyphOffsets = characterInfos.Select(c => new Point(c.DX, -c.DY)).ToList();
- var renderingEmSize = textStyle.FontSize;
- var characters = characterInfos.Select(c => c.Character).ToArray();
- var glyphIndices = characters.Select(c => glyphFace.CharacterToGlyphMap[c]).ToList();
- var advanceWidths = WrapInThousandthOfEmRealDoubles(renderingEmSize, glyphIndices.Select(c => glyphFace.AdvanceWidths[c] * renderingEmSize).ToArray());
-
- if (characterInfos[0].DoesPositionX)
- {
- baselineOrigin.X = characterInfos[0].X;
- newTextChunk = true;
- }
- if (characterInfos[0].DoesPositionY)
- {
- baselineOrigin.Y = characterInfos[0].Y;
- newTextChunk = true;
- }
- else if(textString.TextStyle.TextAlignment != currentTextAlignment)
- {
- newTextChunk = true;
- }
-
- double baselineShift = 0;
- baselineShift += BaselineHelper.EstimateBaselineShiftValue(textStyle, textString.Parent);
- //if (textStyle.BaseLineShift == "sub")
- //{
- // baselineShift += textStyle.FontSize * 0.5; /* * cap height ? fontSize*/
- //}
- //else if (textStyle.BaseLineShift == "super")
- //{
- // baselineShift -= textStyle.FontSize + (textStyle.FontSize * 0.25)/*font.CapsHeight * fontSize*/;
- //}
-
- double totalWidth = advanceWidths.Sum();
-
-
- GlyphRun run = new GlyphRun(glyphFace, state.BidiLevel, isSideways, renderingEmSize,
-#if !DOTNET40 && !DOTNET45 && !DOTNET46
- (float)DpiUtil.PixelsPerDip,
-#endif
- glyphIndices, new Point(baselineOrigin.X, baselineOrigin.Y + baselineShift), advanceWidths, glyphOffsets, characters, deviceFontName, clusterMap, caretStops, language);
-
- var newX = baselineOrigin.X + totalWidth;
- var newY = baselineOrigin.Y ;
-
- newBaseline = new Point(newX, newY);
- return run;
- }
-
- private static readonly Type _thousandthOfEmRealDoublesType = Type.GetType("MS.Internal.TextFormatting.ThousandthOfEmRealDoubles, PresentationCore, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
- private static readonly ConstructorInfo _thousandthOfEmRealDoublesConstructor = _thousandthOfEmRealDoublesType?.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(double), typeof(IList)}, null);
-
- ///
- /// Microsoft's GlyphRun converts the advance widths to thousandths of an em when using EndInit.
- /// This wrapper method is used to compare apples to apples
- ///
- ///
- ///
- ///
- private static IList WrapInThousandthOfEmRealDoubles(double renderingEmSize, IList advanceWidths)
- {
- return (IList)_thousandthOfEmRealDoublesConstructor?.Invoke(new object[] { renderingEmSize, advanceWidths }) ?? advanceWidths;
- }
-
-
- private static void PopulateTextStrings(List textStrings, ITextNode node, TextStyleStack textStyleStacks)
- {
- if(node is TextShapeBase span)
- {
- textStyleStacks.Push(span.TextStyle);
- foreach (var child in span.Children)
- {
- PopulateTextStrings(textStrings, child, textStyleStacks);
- }
- _ = textStyleStacks.Pop();
- }
- else if(node is TextString textString)
- {
- textString.TextStyle = textStyleStacks.Peek();
- textStrings.Add(textString);
- }
- }
- }
-
-
-
-
-}
+ }
+ geometryGroup.Children.Add(runGeometry);
+ if (TextStyles.TryGetValue(glyphRun, out TextStyle textStyle))
+ {
+ TextRender.SetTextSpanTextStyle(runGeometry, textStyle);
+ }
+ if (TextContainers.TryGetValue(glyphRun, out TextShapeBase textContainer))
+ {
+ TextRender.SetElement(runGeometry, textContainer);
+ }
+ if (BackgroundDecorations.TryGetValue(glyphRun, out List backgroundDecorations))
+ {
+ foreach (var decoration in backgroundDecorations)
+ {
+ if (nonZeroAlignmentOffset)
+ {
+ decoration.Offset(alignmentOffset, 0);
+ }
+ //Underline and OverLine should be drawn behind the text
+ geometryGroup.Children.Insert(0, new RectangleGeometry(decoration));
+ }
+ }
+ if (ForegroundDecorations.TryGetValue(glyphRun, out List foregroundDecorations))
+ {
+ foreach (var decoration in foregroundDecorations)
+ {
+ if (nonZeroAlignmentOffset)
+ {
+ decoration.Offset(alignmentOffset, 0);
+ }
+ //Strikethrough should be drawn on top of the text
+ geometryGroup.Children.Add(new RectangleGeometry(decoration));
+ }
+ }
+ }
+ return geometryGroup;
+ }
+
+ private Rect GetBoundingBox()
+ {
+ if (GlyphRunBounds.Count == 0)
+ {
+ return Rect.Empty;
+ }
+ Rect boundingBox = GlyphRunBounds.First().Value;
+ foreach (var kvp in GlyphRunBounds)
+ {
+ boundingBox.Union(kvp.Value);
+ }
+ return boundingBox;
+ }
+
+ private double GetAlignmentOffset()
+ {
+ if(TextAlignment == TextAlignment.Left)
+ {
+ return 0; // No offset needed for left alignment
+ }
+ var boundingBox = GetBoundingBox();
+ double totalWidth = boundingBox.Width;
+ double alignmentOffset = 0;
+ switch (TextAlignment)
+ {
+ case TextAlignment.Left:
+ break;
+ case TextAlignment.Right:
+ alignmentOffset = totalWidth;
+ break;
+ case TextAlignment.Center:
+ alignmentOffset = totalWidth / 2d;
+ break;
+ case TextAlignment.Justify:
+ // Justify is not implemented
+ break;
+ default:
+ break;
+ }
+ return alignmentOffset;
+ }
+ }
+ private static GeometryGroup CreateGeometry(TextShape root, TextRenderState state)
+ {
+ state.Resolve(root);
+
+ List textStrings = new List();
+ TextStyleStack textStyleStacks = new TextStyleStack();
+ PopulateTextStrings(textStrings, root, textStyleStacks);
+ GeometryGroup mainGeometryGroup = new GeometryGroup();
+ var baselineOrigin = new Point(root.X.FirstOrDefault().Value, root.Y.FirstOrDefault().Value);
+ var isSideways = root.WritingMode == WritingMode.HorizontalTopToBottom;
+ TextAlignment currentTextAlignment = root.TextStyle.TextAlignment;
+ List textChunks = new List();
+ bool newTextChunk = false;
+ TextChunk currentTextChunk = null;
+ foreach (TextString textString in textStrings)
+ {
+ var textStyle = textString.TextStyle;
+ Typeface font = textString.TextStyle.GetTypeface();
+ if (CreateRun(textString, state, font, isSideways, baselineOrigin, out Point newBaseline, out newTextChunk, ref currentTextAlignment) is GlyphRun run)
+ {
+ if (newTextChunk)
+ {
+ if(currentTextChunk != null)
+ {
+ // Add the current text chunk to the list
+ textChunks.Add(currentTextChunk);
+ }
+ currentTextChunk = new TextChunk();
+ currentTextChunk.TextAlignment = currentTextAlignment;
+ }
+ var runGeometry = run.BuildGeometry();
+ currentTextChunk.GlyphRuns.Add(run);
+ currentTextChunk.TextStyles[run] = textStyle;
+ currentTextChunk.GlyphRunBounds[run] = runGeometry.Bounds;
+ currentTextChunk.TextContainers[run] = (TextShapeBase)textString.Parent;
+ if (textStyle.TextDecoration != null && textStyle.TextDecoration.Count > 0)
+ {
+ GetTextDecorations(runGeometry, textStyle, font, baselineOrigin, out List backgroundDecorations, out List foregroundDecorations);
+ if(backgroundDecorations.Count > 0)
+ {
+ currentTextChunk.BackgroundDecorations[run] = backgroundDecorations;
+ }
+ if (foregroundDecorations.Count > 0)
+ {
+ currentTextChunk.ForegroundDecorations[run] = foregroundDecorations;
+ }
+ }
+ }
+ baselineOrigin = newBaseline;
+ }
+
+ if (currentTextChunk != null)
+ {
+ textChunks.Add(currentTextChunk);
+ }
+
+ foreach (TextChunk textChunk in textChunks)
+ {
+ if (textChunk.GlyphRuns.Count == 0)
+ {
+ continue; // No glyphs to render in this chunk
+ }
+ GeometryGroup geometryGroup = textChunk.BuildGeometry();
+ mainGeometryGroup.Children.Add(geometryGroup);
+ }
+
+ mainGeometryGroup.Transform = root.Transform;
+ return mainGeometryGroup;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ /// Not perfect, the lines are not continuous across multiple text strings.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private static void GetTextDecorations(Geometry geometry, TextStyle textStyle, Typeface font, Point baselineOrigin, out List backgroundDecorations, out List foregroundDecorations)
+ {
+ backgroundDecorations = new List();
+ foregroundDecorations = new List();
+ double decorationPos = 0;
+ double decorationThickness = 0;
+ double fontSize = textStyle.FontSize;
+ double baselineY = baselineOrigin.Y;
+ foreach(TextDecorationLocation textDecorationLocation in textStyle.TextDecoration.Select(td=>td.Location))
+ {
+ if (textDecorationLocation == TextDecorationLocation.Strikethrough)
+ {
+ decorationPos = baselineY - (font.StrikethroughPosition * fontSize);
+ decorationThickness = font.StrikethroughThickness * fontSize;
+ Rect bounds = new Rect(geometry.Bounds.Left, decorationPos, geometry.Bounds.Width, decorationThickness);
+ foregroundDecorations.Add(bounds);
+ }
+ else if (textDecorationLocation == TextDecorationLocation.Underline)
+ {
+ decorationPos = baselineY - (font.UnderlinePosition * fontSize);
+ decorationThickness = font.UnderlineThickness * fontSize;
+ Rect bounds = new Rect(geometry.Bounds.Left, decorationPos, geometry.Bounds.Width, decorationThickness);
+ backgroundDecorations.Add(bounds);
+ }
+ else if (textDecorationLocation == TextDecorationLocation.OverLine)
+ {
+ decorationPos = baselineY - fontSize;
+ decorationThickness = font.StrikethroughThickness * fontSize;
+ Rect bounds = new Rect(geometry.Bounds.Left, decorationPos, geometry.Bounds.Width, decorationThickness);
+ backgroundDecorations.Add(bounds);
+ }
+ }
+ }
+
+ private static GlyphRun CreateRun(TextString textString, TextRenderState state, Typeface font, bool isSideways, Point baselineOrigin, out Point newBaseline, out bool newTextChunk, ref TextAlignment currentTextAlignment)
+ {
+ newTextChunk = textString.FirstCharacter.GlobalIndex == 0;
+ var textStyle = textString.TextStyle;
+ var characterInfos = textString.GetCharacters();
+ if(characterInfos is null ||characterInfos.Length == 0)
+ {
+ newBaseline = baselineOrigin;
+ return null;
+ }
+ if (!font.TryGetGlyphTypeface(out var glyphFace))
+ {
+ newBaseline = baselineOrigin;
+ return null;
+ }
+ string deviceFontName = null;
+ IList clusterMap = null;
+ IList caretStops = null;
+ XmlLanguage language = null;
+ var glyphOffsets = characterInfos.Select(c => new Point(c.DX, -c.DY)).ToList();
+ var renderingEmSize = textStyle.FontSize;
+ var characters = characterInfos.Select(c => c.Character).ToArray();
+ var glyphIndices = characters.Select(c => glyphFace.CharacterToGlyphMap[c]).ToList();
+ var advanceWidths = WrapInThousandthOfEmRealDoubles(renderingEmSize, glyphIndices.Select(c => glyphFace.AdvanceWidths[c] * renderingEmSize).ToArray());
+
+ if (characterInfos[0].DoesPositionX)
+ {
+ baselineOrigin.X = characterInfos[0].X;
+ newTextChunk = true;
+ }
+ if (characterInfos[0].DoesPositionY)
+ {
+ baselineOrigin.Y = characterInfos[0].Y;
+ newTextChunk = true;
+ }
+ else if(textString.TextStyle.TextAlignment != currentTextAlignment)
+ {
+ newTextChunk = true;
+ }
+
+ double baselineShift = 0;
+ baselineShift += BaselineHelper.EstimateBaselineShiftValue(textStyle, textString.Parent);
+ //if (textStyle.BaseLineShift == "sub")
+ //{
+ // baselineShift += textStyle.FontSize * 0.5; /* * cap height ? fontSize*/
+ //}
+ //else if (textStyle.BaseLineShift == "super")
+ //{
+ // baselineShift -= textStyle.FontSize + (textStyle.FontSize * 0.25)/*font.CapsHeight * fontSize*/;
+ //}
+
+ double totalWidth = advanceWidths.Sum();
+
+
+ GlyphRun run = new GlyphRun(glyphFace, state.BidiLevel, isSideways, renderingEmSize,
+#if !DOTNET40 && !DOTNET45 && !DOTNET46
+ (float)DpiUtil.PixelsPerDip,
+#endif
+ glyphIndices, new Point(baselineOrigin.X, baselineOrigin.Y + baselineShift), advanceWidths, glyphOffsets, characters, deviceFontName, clusterMap, caretStops, language);
+
+ var newX = baselineOrigin.X + totalWidth;
+ var newY = baselineOrigin.Y ;
+
+ newBaseline = new Point(newX, newY);
+ return run;
+ }
+
+ private static readonly Type _thousandthOfEmRealDoublesType = Type.GetType("MS.Internal.TextFormatting.ThousandthOfEmRealDoubles, PresentationCore, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
+ private static readonly ConstructorInfo _thousandthOfEmRealDoublesConstructor = _thousandthOfEmRealDoublesType?.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(double), typeof(IList)}, null);
+
+ ///
+ /// Microsoft's GlyphRun converts the advance widths to thousandths of an em when using EndInit.
+ /// This wrapper method is used to compare apples to apples
+ ///
+ ///
+ ///
+ ///
+ private static IList WrapInThousandthOfEmRealDoubles(double renderingEmSize, IList advanceWidths)
+ {
+ return (IList)_thousandthOfEmRealDoublesConstructor?.Invoke(new object[] { renderingEmSize, advanceWidths }) ?? advanceWidths;
+ }
+
+
+ private static void PopulateTextStrings(List textStrings, ITextNode node, TextStyleStack textStyleStacks)
+ {
+ if(node is TextShapeBase span)
+ {
+ textStyleStacks.Push(span.TextStyle);
+ foreach (var child in span.Children)
+ {
+ PopulateTextStrings(textStrings, child, textStyleStacks);
+ }
+ _ = textStyleStacks.Pop();
+ }
+ else if(node is TextString textString)
+ {
+ textString.TextStyle = textStyleStacks.Peek();
+ textStrings.Add(textString);
+ }
+ }
+ }
+
+
+
+
+}