Browse Source

Implement superscript and subscript.

Bebo-Maker 3 years ago
parent
commit
3ffab6e933

+ 14 - 1
QuestPDF/Drawing/FontManager.cs

@@ -68,7 +68,7 @@ namespace QuestPDF.Drawing
                 {
                     Color = SKColor.Parse(style.Color),
                     Typeface = GetTypeface(style),
-                    TextSize = style.Size ?? 12,
+                    TextSize = (style.Size ?? 12) * GetTextScale(style),
                     IsAntialias = true,
                 };
             }
@@ -76,6 +76,11 @@ namespace QuestPDF.Drawing
             static SKTypeface GetTypeface(TextStyle style)
             {
                 var weight = (SKFontStyleWeight)(style.FontWeight ?? FontWeight.Normal);
+
+                //Extra weight for superscript and subscript
+                if (style.FontPosition == FontPosition.Superscript || style.FontPosition == FontPosition.Subscript)
+                    weight = (SKFontStyleWeight)((int)weight + 100);
+
                 var slant = (style.IsItalic ?? false) ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright;
 
                 var fontStyle = new SKFontStyle(weight, SKFontStyleWidth.Normal, slant);
@@ -100,5 +105,13 @@ namespace QuestPDF.Drawing
         {
             return FontMetrics.GetOrAdd(style.FontMetricsKey, key => style.ToPaint().FontMetrics);
         }
+
+        private static float GetTextScale(TextStyle style)
+        {
+            if (style.FontPosition == FontPosition.Superscript || style.FontPosition == FontPosition.Subscript)
+                return 0.65f;
+
+            return 1;
+        }
     }
 }

+ 15 - 3
QuestPDF/Elements/Text/Items/TextBlockSpan.cs

@@ -126,23 +126,35 @@ namespace QuestPDF.Elements.Text.Items
         {
             var fontMetrics = Style.ToFontMetrics();
 
+            var glyphOffsetY = GetVerticalGlyphOffsetForStyle(Style);
+
             var text = Text.Substring(request.StartIndex, request.EndIndex - request.StartIndex);
             
             request.Canvas.DrawRectangle(new Position(0, request.TotalAscent), new Size(request.TextSize.Width, request.TextSize.Height), Style.BackgroundColor);
-            request.Canvas.DrawText(text, Position.Zero, Style);
+            request.Canvas.DrawText(text, new Position(0, glyphOffsetY), Style);
 
             // draw underline
             if ((Style.HasUnderline ?? false) && fontMetrics.UnderlinePosition.HasValue)
-                DrawLine(fontMetrics.UnderlinePosition.Value, fontMetrics.UnderlineThickness ?? 1);
+                DrawLine(glyphOffsetY + fontMetrics.UnderlinePosition.Value, fontMetrics.UnderlineThickness ?? 1);
             
             // draw stroke
             if ((Style.HasStrikethrough ?? false) && fontMetrics.StrikeoutPosition.HasValue)
-                DrawLine(fontMetrics.StrikeoutPosition.Value, fontMetrics.StrikeoutThickness ?? 1);
+                DrawLine(glyphOffsetY + fontMetrics.StrikeoutPosition.Value, fontMetrics.StrikeoutThickness ?? 1);
 
             void DrawLine(float offset, float thickness)
             {
                 request.Canvas.DrawRectangle(new Position(0, offset - thickness / 2f), new Size(request.TextSize.Width, thickness), Style.Color);
             }
         }
+
+        private static float GetVerticalGlyphOffsetForStyle(TextStyle style)
+        {
+            if (style.FontPosition == FontPosition.Superscript)
+                return (style.Size ?? 12f) * -0.35f;
+            if (style.FontPosition == FontPosition.Subscript)
+                return (style.Size ?? 12f) * 0.1f;
+
+            return 0;
+        }
     }
 }

+ 24 - 1
QuestPDF/Fluent/TextSpanDescriptorExtensions.cs

@@ -126,7 +126,30 @@ namespace QuestPDF.Fluent
         {
             return descriptor.Weight(FontWeight.ExtraBlack);
         }
-        
+
+        #endregion
+
+        #region Position
+        public static T NormalPosition<T>(this T descriptor) where T : TextSpanDescriptor
+        {
+            return descriptor.Position(FontPosition.Normal);
+        }
+
+        public static T Subscript<T>(this T descriptor) where T : TextSpanDescriptor
+        {
+            return descriptor.Position(FontPosition.Subscript);
+        }
+
+        public static T Superscript<T>(this T descriptor) where T : TextSpanDescriptor
+        {
+            return descriptor.Position(FontPosition.Superscript);
+        }
+
+        private static T Position<T>(this T descriptor, FontPosition fontPosition) where T : TextSpanDescriptor
+        {
+            descriptor.TextStyle.FontPosition = fontPosition;
+            return descriptor;
+        }
         #endregion
     }
 }

+ 26 - 1
QuestPDF/Fluent/TextStyleExtensions.cs

@@ -133,7 +133,32 @@ namespace QuestPDF.Fluent
         {
             return style.Weight(FontWeight.ExtraBlack);
         }
-        
+
+        #endregion
+
+        #region Position
+        public static TextStyle NormalPosition(this TextStyle style)
+        {
+            return style.Position(FontPosition.Normal);
+        }
+
+        public static TextStyle Subscript(this TextStyle style)
+        {
+            return style.Position(FontPosition.Subscript);
+        }
+
+        public static TextStyle Superscript(this TextStyle style)
+        {
+            return style.Position(FontPosition.Superscript);
+        }
+
+        private static TextStyle Position(this TextStyle style, FontPosition fontPosition)
+        {
+            if (style.FontPosition == fontPosition)
+                return style;
+
+            return style.Mutate(t => t.FontPosition = fontPosition);
+        }
         #endregion
     }
 }

+ 9 - 0
QuestPDF/Infrastructure/FontPosition.cs

@@ -0,0 +1,9 @@
+namespace QuestPDF.Infrastructure
+{
+    internal enum FontPosition
+    {
+        Normal = 0,
+        Subscript,
+        Superscript,
+    }
+}

+ 5 - 1
QuestPDF/Infrastructure/TextStyle.cs

@@ -13,6 +13,7 @@ namespace QuestPDF.Infrastructure
         internal float? Size { get; set; }
         internal float? LineHeight { get; set; }
         internal FontWeight? FontWeight { get; set; }
+        internal FontPosition? FontPosition { get; set; }
         internal bool? IsItalic { get; set; }
         internal bool? HasStrikethrough { get; set; }
         internal bool? HasUnderline { get; set; }
@@ -29,6 +30,7 @@ namespace QuestPDF.Infrastructure
             Size = 12,
             LineHeight = 1.2f,
             FontWeight = Infrastructure.FontWeight.Normal,
+            FontPosition = Infrastructure.FontPosition.Normal,
             IsItalic = false,
             HasStrikethrough = false,
             HasUnderline = false,
@@ -45,7 +47,7 @@ namespace QuestPDF.Infrastructure
             HasGlobalStyleApplied = true;
 
             ApplyParentStyle(globalStyle);
-            PaintKey ??= (FontFamily, Size, FontWeight, IsItalic, Color);
+            PaintKey ??= (FontFamily, Size, FontWeight, FontPosition, IsItalic, Color);
             FontMetricsKey ??= (FontFamily, Size, FontWeight, IsItalic);
         }
         
@@ -57,6 +59,7 @@ namespace QuestPDF.Infrastructure
             Size ??= parentStyle.Size;
             LineHeight ??= parentStyle.LineHeight;
             FontWeight ??= parentStyle.FontWeight;
+            FontPosition ??= parentStyle.FontPosition;
             IsItalic ??= parentStyle.IsItalic;
             HasStrikethrough ??= parentStyle.HasStrikethrough;
             HasUnderline ??= parentStyle.HasUnderline;
@@ -71,6 +74,7 @@ namespace QuestPDF.Infrastructure
             Size = parentStyle.Size ?? Size;
             LineHeight = parentStyle.LineHeight ?? LineHeight;
             FontWeight = parentStyle.FontWeight ?? FontWeight;
+            FontPosition = parentStyle.FontPosition ?? FontPosition;
             IsItalic = parentStyle.IsItalic ?? IsItalic;
             HasStrikethrough = parentStyle.HasStrikethrough ?? HasStrikethrough;
             HasUnderline = parentStyle.HasUnderline ?? HasUnderline;