diff --git a/Samples/Example/Example.csproj b/Samples/Example/Example.csproj
index def0b7f..cc2f2ba 100644
--- a/Samples/Example/Example.csproj
+++ b/Samples/Example/Example.csproj
@@ -43,9 +43,11 @@
     
   
   
+    
     
     
     
+    
     
     
     
diff --git a/Samples/Example/Images/acid1_TextOnly.svg b/Samples/Example/Images/acid1_TextOnly.svg
new file mode 100644
index 0000000..0ef7b64
--- /dev/null
+++ b/Samples/Example/Images/acid1_TextOnly.svg
@@ -0,0 +1,160 @@
+
+     
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Samples/Example/Images/boldgreen.svg b/Samples/Example/Images/boldgreen.svg
new file mode 100644
index 0000000..e9edc0f
--- /dev/null
+++ b/Samples/Example/Images/boldgreen.svg
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/Source/SVGImage/DotNetProjects.SVGImage.csproj b/Source/SVGImage/DotNetProjects.SVGImage.csproj
index 02886db..414995a 100644
--- a/Source/SVGImage/DotNetProjects.SVGImage.csproj
+++ b/Source/SVGImage/DotNetProjects.SVGImage.csproj
@@ -29,7 +29,7 @@
 		images\dotnetprojects.png
 		svg wpf svg-icons svg-to-png svg-to-xaml svgimage svgimage-control
 		Readme.md
-		true
+		
 		
 		5.1.0
 	
diff --git a/Source/SVGImage/SVG/PaintServers/PaintServerManager.cs b/Source/SVGImage/SVG/PaintServers/PaintServerManager.cs
index d5dc1cc..b1159af 100644
--- a/Source/SVGImage/SVG/PaintServers/PaintServerManager.cs
+++ b/Source/SVGImage/SVG/PaintServers/PaintServerManager.cs
@@ -160,10 +160,11 @@ public static Color ParseHexColor(string value)
                 newval |= (@int & 0x00000f);
                 u = newval;
             }
-            else {
-                u = Convert.ToUInt64(value.Substring(start), 16);
-            }
-
+            else
+            {
+                u = Convert.ToUInt64(value.Substring(start), 16);
+            }
+
             byte a = (byte)((u & 0xff000000) >> 24);
             byte r = (byte)((u & 0x00ff0000) >> 16);
             byte g = (byte)((u & 0x0000ff00) >> 8);
@@ -223,10 +224,12 @@ private int ParseColorNumber(string value)
         {
             if (value.EndsWith("%"))
             {
-                var nr = int.Parse(value.Substring(0, value.Length - 1));
+                var nr = double.Parse(value.Substring(0, value.Length - 1));
                 if (nr < 0)
-                    nr = 255 - nr;
-                return nr * 255 / 100;
+                    nr = 255 - nr; //TODO: what is this trying to do?
+                var result = (int)Math.Round((nr * 255) / 100);
+
+                return MathUtil.Clamp(result, 0, 255);
             }
 
             return int.Parse(value);
diff --git a/Source/SVGImage/SVG/SVGRender.cs b/Source/SVGImage/SVG/SVGRender.cs
index 7039d93..24b1c64 100644
--- a/Source/SVGImage/SVG/SVGRender.cs
+++ b/Source/SVGImage/SVG/SVGRender.cs
@@ -3,6 +3,7 @@
 using System.Xml;
 using System.Linq;
 using System.Collections.Generic;
+using SVGImage.SVG.Utils;
 
 using System.Windows;
 using System.Windows.Media;
@@ -115,7 +116,8 @@ public DrawingGroup LoadDrawing(Stream stream)
 
         public DrawingGroup CreateDrawing(SVG svg)
         {
-            return this.LoadGroup(svg.Elements, svg.ViewBox, false);
+            var drawingGroup = this.LoadGroup(svg.Elements, svg.ViewBox, false);
+            return drawingGroup;
         }
 
         public DrawingGroup CreateDrawing(Shape shape)
@@ -443,9 +445,9 @@ internal DrawingGroup LoadGroup(IList elements, Rect? viewBox, bool isSwi
                     GeometryGroup gp = textRender2.BuildTextGeometry(textShape);
                     if (gp != null)
                     {
-                        foreach (Geometry gm in gp.Children)
+                        foreach (Geometry gm in GetStyledSpans(gp))
                         {
-                            if (TextRenderBase.GetElement(gm) is TextShapeBase tspan)
+                            if (TextRender.GetElement(gm) is TextShapeBase tspan)
                             {
                                 var di = this.NewDrawingItem(tspan, gm);
                                 AddDrawingToGroup(grp, shape, di);
@@ -480,6 +482,31 @@ internal DrawingGroup LoadGroup(IList elements, Rect? viewBox, bool isSwi
             return grp;
         }
 
+        private static IEnumerable GetStyledSpans(Geometry geometry)
+        {
+            if (geometry is GeometryGroup gg)
+            {
+                if (!(TextRender.GetElement(gg) is null))
+                {
+                    yield return geometry;
+                }
+                else
+                {
+                    foreach (var g in gg.Children)
+                    {
+                        foreach (var subg in GetStyledSpans(g))
+                        {
+                            yield return subg;
+                        }
+                    }
+                }
+            }
+            else
+            {
+                yield return geometry;
+            }
+        }
+
         private void AddDrawingToGroup(DrawingGroup grp, Shape shape, Drawing drawing)
         {
             if (shape.Clip != null || shape.Transform != null || shape.Filter != null)
diff --git a/Source/SVGImage/SVG/Shapes/LengthPercentageOrNumber.cs b/Source/SVGImage/SVG/Shapes/LengthPercentageOrNumber.cs
index 4b9ca9b..a255c51 100644
--- a/Source/SVGImage/SVG/Shapes/LengthPercentageOrNumber.cs
+++ b/Source/SVGImage/SVG/Shapes/LengthPercentageOrNumber.cs
@@ -6,7 +6,7 @@ namespace SVGImage.SVG.Shapes
 
     public struct LengthPercentageOrNumber
     {
-        private static readonly Regex _lengthRegex = new Regex(@"(?\d+(?:\.\d+)?)\s*(?%|\w+)?", RegexOptions.Compiled | RegexOptions.Singleline);
+        private static readonly Regex _lengthRegex = new Regex(@"(?-?\d+(?:\.\d+)?)\s*(?%|\w+)?", RegexOptions.Compiled | RegexOptions.Singleline);
         private readonly LengthContext _context;
         private readonly double _value;
         /// 
@@ -180,12 +180,12 @@ public static LengthPercentageOrNumber Parse(Shape owner, string value, LengthOr
             }
             else
             {
-                // Default to pixels if no unit is specified
-                context = new LengthContext(owner, LengthUnit.px);
+                // Default to Number if no unit is specified
+                context = new LengthContext(owner, LengthUnit.Number);
             }
             return new LengthPercentageOrNumber(d, context);
         }
-        
+
     }
 
 
diff --git a/Source/SVGImage/SVG/Shapes/Shape.cs b/Source/SVGImage/SVG/Shapes/Shape.cs
index 7d45c29..a44f6ed 100644
--- a/Source/SVGImage/SVG/Shapes/Shape.cs
+++ b/Source/SVGImage/SVG/Shapes/Shape.cs
@@ -125,6 +125,11 @@ public Stroke Stroke
         }
 
         protected virtual Fill DefaultFill()
+        {
+            return null;
+        }
+
+        protected virtual Fill GetParentFill()
         {
             var parent = this.Parent;
             while (parent != null)
@@ -141,7 +146,7 @@ protected virtual Fill DefaultFill()
 
         public Fill Fill
         {
-            get => m_fill ?? DefaultFill();
+            get => m_fill ?? GetParentFill() ?? DefaultFill();
             set => m_fill = value;
         }
 
diff --git a/Source/SVGImage/SVG/Shapes/TextRender.cs b/Source/SVGImage/SVG/Shapes/TextRender.cs
index cdd1a12..03fb88c 100644
--- a/Source/SVGImage/SVG/Shapes/TextRender.cs
+++ b/Source/SVGImage/SVG/Shapes/TextRender.cs
@@ -7,6 +7,9 @@ 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.
@@ -25,6 +28,117 @@ public override GeometryGroup BuildTextGeometry(TextShape text)
                 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();
+                    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);
@@ -35,33 +149,57 @@ private static GeometryGroup CreateGeometry(TextShape root, TextRenderState stat
             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)
             {
-                GeometryGroup geometryGroup = new GeometryGroup();
                 var textStyle = textString.TextStyle;
                 Typeface font = textString.TextStyle.GetTypeface();
-                if (CreateRun(textString, state, font, isSideways, baselineOrigin, out Point newBaseline) is GlyphRun run)
+                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();
-                    geometryGroup.Children.Add(runGeometry);
+                    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(geometryGroup, textStyle, font, baselineOrigin, out List backgroundDecorations, out List foregroundDecorations);
-                        foreach (var decoration in backgroundDecorations)
+                        GetTextDecorations(runGeometry, textStyle, font, baselineOrigin, out List backgroundDecorations, out List foregroundDecorations);
+                        if(backgroundDecorations.Count > 0)
                         {
-                            //Underline and OverLine should be drawn behind the text
-                            geometryGroup.Children.Insert(0, new RectangleGeometry(decoration));
+                            currentTextChunk.BackgroundDecorations[run] = backgroundDecorations;
                         }
-                        foreach (var decoration in foregroundDecorations)
+                        if (foregroundDecorations.Count > 0)
                         {
-                            //Strikethrough should be drawn on top of the text
-                            geometryGroup.Children.Add(new RectangleGeometry(decoration));
+                            currentTextChunk.ForegroundDecorations[run] = foregroundDecorations;
                         }
                     }
-                    mainGeometryGroup.Children.Add(geometryGroup);
                 }
                 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;
@@ -73,13 +211,13 @@ private static GeometryGroup CreateGeometry(TextShape root, TextRenderState stat
         /// 
         /// Not perfect, the lines are not continuous across multiple text strings.
         /// 
-        /// 
+        /// 
         /// 
         /// 
         /// 
         /// 
         /// 
-        private static void GetTextDecorations(GeometryGroup geometryGroup, TextStyle textStyle, Typeface font, Point baselineOrigin, out List backgroundDecorations, out List foregroundDecorations)
+        private static void GetTextDecorations(Geometry geometry, TextStyle textStyle, Typeface font, Point baselineOrigin, out List backgroundDecorations, out List foregroundDecorations)
         {
             backgroundDecorations = new List();
             foregroundDecorations = new List();
@@ -93,28 +231,29 @@ private static void GetTextDecorations(GeometryGroup geometryGroup, TextStyle te
                 {
                     decorationPos = baselineY - (font.StrikethroughPosition * fontSize);
                     decorationThickness = font.StrikethroughThickness * fontSize;
-                    Rect bounds = new Rect(geometryGroup.Bounds.Left, decorationPos, geometryGroup.Bounds.Width, decorationThickness);
+                    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(geometryGroup.Bounds.Left, decorationPos, geometryGroup.Bounds.Width, decorationThickness);
+                    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(geometryGroup.Bounds.Left, decorationPos, geometryGroup.Bounds.Width, decorationThickness);
+                    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)
+        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)
@@ -135,31 +274,64 @@ private static GlyphRun CreateRun(TextString textString, TextRenderState state,
             var renderingEmSize = textStyle.FontSize;
             var characters = characterInfos.Select(c => c.Character).ToArray();
             var glyphIndices = characters.Select(c => glyphFace.CharacterToGlyphMap[c]).ToList();
-            var advanceWidths = glyphIndices.Select(c => glyphFace.AdvanceWidths[c] * renderingEmSize).ToArray();
-
+            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, baselineOrigin, advanceWidths, glyphOffsets, characters, deviceFontName, clusterMap, caretStops, language);
-            
-            var newX = baselineOrigin.X + advanceWidths.Sum();
+                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)
diff --git a/Source/SVGImage/SVG/Shapes/TextRenderBase.cs b/Source/SVGImage/SVG/Shapes/TextRenderBase.cs
index ccec0cf..47c619b 100644
--- a/Source/SVGImage/SVG/Shapes/TextRenderBase.cs
+++ b/Source/SVGImage/SVG/Shapes/TextRenderBase.cs
@@ -7,14 +7,14 @@ public abstract class TextRenderBase
     {
         public abstract GeometryGroup BuildTextGeometry(TextShape text);
         public static DependencyProperty TSpanElementProperty = DependencyProperty.RegisterAttached(
-            "TSpanElement", typeof(ITextNode), typeof(DependencyObject));
-        public static void SetElement(DependencyObject obj, ITextNode value)
+            "TSpanElement", typeof(TextShapeBase), typeof(DependencyObject));
+        public static void SetElement(DependencyObject obj, TextShapeBase value)
         {
             obj.SetValue(TSpanElementProperty, value);
         }
-        public static ITextNode GetElement(DependencyObject obj)
+        public static TextShapeBase GetElement(DependencyObject obj)
         {
-            return (ITextNode)obj.GetValue(TSpanElementProperty);
+            return (TextShapeBase)obj.GetValue(TSpanElementProperty);
         }
     }
 
diff --git a/Source/SVGImage/SVG/Shapes/TextShape.cs b/Source/SVGImage/SVG/Shapes/TextShape.cs
index da28c7f..29e5b59 100644
--- a/Source/SVGImage/SVG/Shapes/TextShape.cs
+++ b/Source/SVGImage/SVG/Shapes/TextShape.cs
@@ -1,4 +1,5 @@
-using System.Xml;
+using System.Linq;
+using System.Xml;
 
 namespace SVGImage.SVG.Shapes
 {
@@ -6,7 +7,87 @@ public class TextShape : TextShapeBase
     {
         public TextShape(SVG svg, XmlNode node, Shape parent) : base(svg, node, parent)
         {
-            
+            CollapseWhitespaceBetweenTextNodes(this);
+        }
+        private static bool EndsWithWhitespace(string text)
+        {
+            return !string.IsNullOrEmpty(text) && char.IsWhiteSpace(text[text.Length - 1]);
+        }
+
+        private static bool StartsWithWhitespace(string text)
+        {
+            return !string.IsNullOrEmpty(text) && char.IsWhiteSpace(text[0]);
+        }
+        private static TextString GetFirstLeaf(ITextNode node)
+        {
+            if (node is TextString leaf)
+            {
+                return leaf;
+            }
+
+            if (node is TextShapeBase container)
+            {
+                foreach (var child in container.Children)
+                {
+                    var result = GetFirstLeaf(child);
+                    if (!(result is null))
+                    {
+                        return result;
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        private static TextString GetLastLeaf(ITextNode node)
+        {
+            if (node is TextString leaf)
+            {
+                return leaf;
+            }
+
+            if (node is TextShapeBase container)
+            {
+                for (int i = container.Children.Count - 1; i >= 0; i--)
+                {
+                    var result = GetLastLeaf(container.Children[i]);
+                    if (!(result is null))
+                    {
+                        return result;
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        private static void CollapseWhitespaceBetweenTextNodes(TextShape root)
+        {
+            TextString previousLeaf = null;
+            CollapseWhitespaceBetweenTextNodes(root, ref previousLeaf);
+        }
+
+        private static void CollapseWhitespaceBetweenTextNodes(ITextNode node, ref TextString previousLeaf)
+        {
+            if (node is TextString currentLeaf)
+            {
+                if (!(previousLeaf is null) &&
+                    EndsWithWhitespace(previousLeaf.Text) &&
+                    StartsWithWhitespace(currentLeaf.Text))
+                {
+                    currentLeaf.Characters = currentLeaf.Characters.Skip(1).ToArray();
+                }
+
+                previousLeaf = currentLeaf;
+            }
+            else if (node is TextShapeBase container)
+            {
+                foreach (var child in container.Children)
+                {
+                    CollapseWhitespaceBetweenTextNodes(child, ref previousLeaf);
+                }
+            }
         }
 
     }
diff --git a/Source/SVGImage/SVG/Shapes/TextShapeBase.cs b/Source/SVGImage/SVG/Shapes/TextShapeBase.cs
index 00e2053..43e5b7b 100644
--- a/Source/SVGImage/SVG/Shapes/TextShapeBase.cs
+++ b/Source/SVGImage/SVG/Shapes/TextShapeBase.cs
@@ -7,6 +7,7 @@ namespace SVGImage.SVG.Shapes
     using System.Linq;
     using System.Diagnostics;
     using System.Text;
+    using System.Text.RegularExpressions;
 
     [DebuggerDisplay("{DebugDisplayText}")]
     public class TextShapeBase: Shape, ITextNode
@@ -39,7 +40,15 @@ private string GetDebugDisplayText(StringBuilder sb)
             return sb.ToString();
         }
 
-        
+
+        protected override Fill DefaultFill()
+        {
+            return Fill.CreateDefault(Svg, "black");
+        }
+        protected override Stroke DefaultStroke()
+        {
+            return Stroke.CreateDefault(Svg, 0.1);
+        }
 
         public LengthPercentageOrNumberList X { get; protected set; }
         public LengthPercentageOrNumberList Y { get; protected set; } 
@@ -173,6 +182,7 @@ protected override void ParseAtStart(SVG svg, XmlNode node)
 
             ParseChildren(svg, node);
         }
+        private static readonly Regex _trimmedWhitespace = new Regex(@"\s+", RegexOptions.Compiled | RegexOptions.Singleline);
 
         protected void ParseChildren(SVG svg, XmlNode node)
         {
@@ -180,7 +190,7 @@ protected void ParseChildren(SVG svg, XmlNode node)
             {
                 if (child.NodeType == XmlNodeType.Text || child.NodeType == XmlNodeType.CDATA)
                 {
-                    var text = child.InnerText.Trim();
+                    var text = _trimmedWhitespace.Replace(child.InnerText, " ");
                     if (!string.IsNullOrWhiteSpace(text))
                     {
                         Children.Add(new TextString(this, text));
diff --git a/Source/SVGImage/SVG/Shapes/TextString.cs b/Source/SVGImage/SVG/Shapes/TextString.cs
index 7b0af19..4dc907f 100644
--- a/Source/SVGImage/SVG/Shapes/TextString.cs
+++ b/Source/SVGImage/SVG/Shapes/TextString.cs
@@ -23,7 +23,7 @@ public class TextString : ITextChild
         public TextString(Shape parent, string text)
         {
             Parent = parent;
-            string trimmed = _trimmedWhitespace.Replace(text.Trim(), " ");
+            string trimmed = _trimmedWhitespace.Replace(text, " ");
             Characters = new CharacterLayout[trimmed.Length];
             for(int i = 0; i < trimmed.Length; i++)
             {
diff --git a/Source/SVGImage/SVG/TextRender2.cs b/Source/SVGImage/SVG/TextRender2.cs
index b9289d9..537121d 100644
--- a/Source/SVGImage/SVG/TextRender2.cs
+++ b/Source/SVGImage/SVG/TextRender2.cs
@@ -51,7 +51,7 @@ static void BuildTextSpan(GeometryGroup gp, TextStyle textStyle,
                         baseline -= tspan.TextStyle.FontSize + (textString.TextStyle.FontSize * 0.25)/*font.CapsHeight * fontSize*/;
 
                     Geometry gm = BuildGlyphRun(textString.TextStyle, txt, x, baseline, ref totalwidth);
-                    TextRender2.SetElement(gm, textString);
+                    TextRender2.SetElement(gm, (TextShapeBase)textString.Parent);
                     gp.Children.Add(gm);
                     x += totalwidth;
                 }
diff --git a/Source/SVGImage/SVG/Utils/BaselineHelper.cs b/Source/SVGImage/SVG/Utils/BaselineHelper.cs
new file mode 100644
index 0000000..0896a16
--- /dev/null
+++ b/Source/SVGImage/SVG/Utils/BaselineHelper.cs
@@ -0,0 +1,66 @@
+using SVGImage.SVG.Shapes;
+using System;
+
+namespace SVGImage.SVG.Utils
+{
+    internal static class BaselineHelper
+    {
+        public static LengthPercentageOrNumber EstimateBaselineShift(Shape shape)
+        {
+            return EstimateBaselineShift(shape.TextStyle, shape);
+        }
+        /// 
+        /// The purpose of this method is to allow TextStrings which are not shapes themselves to use the same logic as TextShapes to estimate the baseline shift.
+        /// They can use this method to estimate the baseline shift based on the TextStyle of the TextString's parent Shape.
+        /// 
+        /// 
+        /// 
+        /// 
+        public static LengthPercentageOrNumber EstimateBaselineShift(TextStyle textStyle, Shape shape)
+        {
+            if (String.IsNullOrEmpty( textStyle.BaseLineShift) || textStyle.BaseLineShift == "baseline")
+            {
+                return new LengthPercentageOrNumber(0d, new LengthContext(shape, LengthUnit.Number));
+            }
+            else if (textStyle.BaseLineShift == "sub")
+            {
+                //Based on previous estimation
+                return new LengthPercentageOrNumber( textStyle.FontSize * 0.5, new LengthContext(shape, LengthUnit.Number));
+            }
+            else if (textStyle.BaseLineShift == "super")
+            {
+                //Based on previous estimation
+                return new LengthPercentageOrNumber((-1) * (textStyle.FontSize + (textStyle.FontSize * 0.25)), new LengthContext(shape, LengthUnit.Number));
+            }
+            else if(textStyle.BaseLineShift.EndsWith("%") && Double.TryParse(textStyle.BaseLineShift.Substring(0, textStyle.BaseLineShift.Length - 1), out double d))
+            {
+                try
+                {
+                    //The computed value of the property is this percentage multiplied by the computed "line-height" of the ‘text’ element.
+                    //for the purposes of processing the ‘font’ property in SVG, 'line-height' is assumed to be equal the value for property ‘font-size’
+                    return new LengthPercentageOrNumber(d, new LengthContext(shape, LengthUnit.rem));
+                }
+                catch
+                {
+                    //Continue
+                }
+            }
+            try
+            {
+                return LengthPercentageOrNumber.Parse(shape, textStyle.BaseLineShift, LengthOrientation.Vertical);
+            }
+            catch
+            {
+                return new LengthPercentageOrNumber(0d, new LengthContext(shape, LengthUnit.Number));
+            }
+        }
+        public static double EstimateBaselineShiftValue(Shape shape)
+        {
+            return EstimateBaselineShift(shape).Value;
+        }
+        public static double EstimateBaselineShiftValue(TextStyle textStyle, Shape shape)
+        {
+            return EstimateBaselineShift(textStyle, shape).Value;
+        }
+    }
+}
diff --git a/Source/SVGImage/SVG/Utils/DrawingGroupSerializer.cs b/Source/SVGImage/SVG/Utils/DrawingGroupSerializer.cs
new file mode 100644
index 0000000..db0478e
--- /dev/null
+++ b/Source/SVGImage/SVG/Utils/DrawingGroupSerializer.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Windows.Media;
+using System.Windows.Markup;
+using System.IO;
+using System.Xml;
+
+namespace SVGImage.SVG.Utils
+{
+    internal static class DrawingGroupSerializer
+    {
+        public static string SerializeToXaml(DrawingGroup drawingGroup)
+        {
+            if (drawingGroup is null)
+            {
+                throw new ArgumentNullException(nameof(drawingGroup));
+            }
+
+            // Freezing can help ensure serialization works without exceptions
+            if (drawingGroup.CanFreeze && !drawingGroup.IsFrozen)
+            {
+                drawingGroup.Freeze();
+            }
+
+            var settings = new XmlWriterSettings
+            {
+                Indent = true,
+                IndentChars = "  ",
+                OmitXmlDeclaration = true
+            };
+
+            using (var stringWriter = new StringWriter())
+            using (var xmlWriter = XmlWriter.Create(stringWriter, settings))
+            {
+                XamlWriter.Save(drawingGroup, xmlWriter);
+                return stringWriter.ToString();
+            }
+        }
+    }
+}
diff --git a/Source/SVGImage/SVG/Utils/FontResolver.cs b/Source/SVGImage/SVG/Utils/FontResolver.cs
index a64db0b..f753675 100644
--- a/Source/SVGImage/SVG/Utils/FontResolver.cs
+++ b/Source/SVGImage/SVG/Utils/FontResolver.cs
@@ -13,7 +13,7 @@ namespace SVGImage.SVG.Utils
     /// 
     public class FontResolver
     {
-        private readonly ConcurrentDictionary _fontCache = new ConcurrentDictionary();
+        private readonly ConcurrentDictionary _fontCache = new ConcurrentDictionary();
         private readonly Dictionary _availableFonts;
         private readonly Dictionary _normalizedFontNameMap;
 
@@ -43,7 +43,7 @@ public FontResolver(int maxLevenshteinDistance = 0)
         /// 
         /// Attempts to a font family based on the requested font name.
         /// 
-        /// The name of the font to resolve.
+        /// The name of the font or fonts to resolve. Multiple fonts should be separated by commas
         /// 
         /// A  if a match is found, otherwise null.
         /// 
@@ -51,6 +51,27 @@ public FontResolver(int maxLevenshteinDistance = 0)
         /// Thrown when the requested font name is null or empty.
         /// 
         public FontFamily ResolveFontFamily(string requestedFontName)
+        {
+            var fontList = requestedFontName
+                .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
+                .Select(font => ResolveFontFamilyInternal(font.Trim()))
+                .Where(font => font != null);
+
+            var resolvedFontNames = String.Join(",", fontList);
+            return new FontFamily(resolvedFontNames);
+        }
+
+        /// 
+        /// Attempts to a font family based on the requested font name.
+        /// 
+        /// The name of the font to resolve.
+        /// 
+        /// A  if a match is found, otherwise null.
+        /// 
+        /// 
+        /// Thrown when the requested font name is null or empty.
+        /// 
+        private string ResolveFontFamilyInternal(string requestedFontName)
         {
             if (string.IsNullOrWhiteSpace(requestedFontName))
             {
@@ -65,8 +86,8 @@ public FontFamily ResolveFontFamily(string requestedFontName)
             // 1. Exact match
             if (_availableFonts.TryGetValue(requestedFontName, out var exactMatch))
             {
-                _fontCache[requestedFontName] = exactMatch;
-                return exactMatch;
+                _fontCache[requestedFontName] = exactMatch.Source;
+                return exactMatch.Source;
             }
 
             // 2. Normalized match
@@ -74,8 +95,8 @@ public FontFamily ResolveFontFamily(string requestedFontName)
             if (_normalizedFontNameMap.TryGetValue(normalizedRequested, out var normalizedMatchName) &&
                 _availableFonts.TryGetValue(normalizedMatchName, out var normalizedMatch))
             {
-                _fontCache[requestedFontName] = normalizedMatch;
-                return normalizedMatch;
+                _fontCache[requestedFontName] = normalizedMatch.Source;
+                return normalizedMatch.Source;
             }
 
             // 3. Substring match
@@ -83,8 +104,8 @@ public FontFamily ResolveFontFamily(string requestedFontName)
                 .FirstOrDefault(kv => Normalize(kv.Key).Contains(normalizedRequested));
             if (substringMatch.Value != null)
             {
-                _fontCache[requestedFontName] = substringMatch.Value;
-                return substringMatch.Value;
+                _fontCache[requestedFontName] = substringMatch.Value.Source;
+                return substringMatch.Value.Source;
             }
 
             // 4. Levenshtein match (optional but slow)
@@ -102,16 +123,14 @@ public FontFamily ResolveFontFamily(string requestedFontName)
 
                 if (bestMatch != null && bestMatch.Distance <= 4)
                 {
-                    _fontCache[requestedFontName] = bestMatch.Font;
-                    return bestMatch.Font;
+                    _fontCache[requestedFontName] = bestMatch.Font.Source;
+                    return bestMatch.Font.Source;
                 }
             }
 
-            
-
             // 5. No match
-            _fontCache[requestedFontName] = null;
-            return null;
+            _fontCache[requestedFontName] = requestedFontName;
+            return requestedFontName;
         }
 
         /// 
diff --git a/Source/SVGImage/SVG/Utils/GlyphRunUtil.cs b/Source/SVGImage/SVG/Utils/GlyphRunUtil.cs
new file mode 100644
index 0000000..7a17079
--- /dev/null
+++ b/Source/SVGImage/SVG/Utils/GlyphRunUtil.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Windows;
+using System.Windows.Media;
+
+namespace SVGImage.SVG.Utils
+{
+    internal static class GlyphRunUtil
+    {
+        public static GlyphRun CreateOffsetRun(this GlyphRun value, double xOffset, double yOffset)
+        {
+            if (value == null)
+            {
+                throw new ArgumentNullException(nameof(value));
+            }
+            return new GlyphRun(
+                value.GlyphTypeface,
+                value.BidiLevel,
+                value.IsSideways,
+                value.FontRenderingEmSize,
+#if DPI_AWARE
+                value.PixelsPerDip,
+#endif
+                value.GlyphIndices,
+                new Point( value.BaselineOrigin.X + xOffset, value.BaselineOrigin.Y + yOffset),
+                value.AdvanceWidths,
+                value.GlyphOffsets,
+                value.Characters,
+                value.DeviceFontName,
+                value.ClusterMap,
+                value.CaretStops,
+                value.Language);
+        }
+    }
+}
diff --git a/Source/SVGImage/SVG/Utils/MathUtils.cs b/Source/SVGImage/SVG/Utils/MathUtils.cs
new file mode 100644
index 0000000..3e47909
--- /dev/null
+++ b/Source/SVGImage/SVG/Utils/MathUtils.cs
@@ -0,0 +1,40 @@
+using SVGImage.SVG.Shapes;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Media;
+
+namespace SVGImage.SVG.Utils
+{
+    internal static class MathUtil
+    {
+        public static bool IsNearlyZero(this double value, double epsilon = Double.Epsilon)
+        {
+            return Math.Abs(value) < epsilon;
+        }
+        public static bool IsNearlyEqual(this double a, double b, double epsilon = Double.Epsilon)
+        {
+            return Math.Abs(a - b) < epsilon;
+        }
+        public static int Clamp(int value, int min, int max)
+        {
+            if (min > max)
+            {
+                throw new ArgumentException("min must be less than or equal to max", nameof(min));
+            }
+
+            if (value < min)
+            {
+                return min;
+            }
+            else if (value > max)
+            {
+                return max;
+            }
+
+            return value;
+        }
+    }
+}
diff --git a/Source/SVGImage/SVG/Utils/SubAndSuperScriptHelper.cs b/Source/SVGImage/SVG/Utils/SubAndSuperScriptHelper.cs
new file mode 100644
index 0000000..1735c13
--- /dev/null
+++ b/Source/SVGImage/SVG/Utils/SubAndSuperScriptHelper.cs
@@ -0,0 +1,39 @@
+using System.Collections.Generic;
+using System.Text;
+
+namespace SVGImage.SVG.Utils
+{
+    internal static class SubAndSuperScriptHelper
+    {
+        private static readonly Dictionary _subscriptMap = new Dictionary
+        {
+            {'0', '₀'}, {'1', '₁'}, {'2', '₂'}, {'3', '₃'}, {'4', '₄'},
+            {'5', '₅'}, {'6', '₆'}, {'7', '₇'}, {'8', '₈'}, {'9', '₉'},
+            {'+', '₊'}, {'-', '₋'}, {'=', '₌'}
+        };
+        private static readonly Dictionary _superscriptMap = new Dictionary
+        {
+            {'0', '⁰'}, {'1', '¹'}, {'2', '²'}, {'3', '³'}, {'4', '⁴'},
+            {'5', '⁵'}, {'6', '⁶'}, {'7', '⁷'}, {'8', '⁸'}, {'9', '⁹'},
+            {'+', '⁺'}, {'-', '⁻'}, {'=', '⁼'}
+        };
+        public static string ToSubscript(this string input)
+        {
+            return Convert(input, _subscriptMap);
+        }
+        public static string ToSuperscript(this string input)
+        {
+            return Convert(input, _superscriptMap);
+        }
+
+        private static string Convert(string input, Dictionary map)
+        {
+            var sb = new StringBuilder();
+            foreach (var c in input)
+            {
+                sb.Append(map.TryGetValue(c, out var mappedChar) ? mappedChar : c);
+            }
+            return sb.ToString();
+        }
+    }
+}