Merge pull request #179 from Bebo-Maker/main

Implement subscript/superscript text
This commit is contained in:
Marcin Ziąbek
2022-05-06 00:09:31 +02:00
committed by GitHub
7 changed files with 140 additions and 9 deletions
+45 -1
View File
@@ -36,7 +36,51 @@ namespace QuestPDF.Examples
});
});
}
[Test]
public void SuperscriptSubscript()
{
RenderingTest
.Create()
.PageSize(500, 300)
.ProduceImages()
.ShowResults()
.Render(container =>
{
container
.Padding(5)
.MinimalBox()
.Border(1)
.Padding(10)
.Text(text =>
{
text.Span("E=mc");
text.Span("2").Superscript();
text.EmptyLine();
text.Span("H");
text.Span("2").Subscript();
text.Span("O");
text.EmptyLine();
text.ParagraphSpacing(2);
var style = TextStyle.Default.Underline();
text.Span("Subscript").Style(style).Subscript().BackgroundColor(Colors.Green.Medium);
text.Span("Normal").Style(style).NormalPosition().BackgroundColor(Colors.Blue.Medium);
text.Line("Superscript").Style(style).Superscript().BackgroundColor(Colors.Red.Medium);
text.Line("Superscript").Style(style).Superscript().BackgroundColor(Colors.Green.Medium);
text.Line("Normal").Style(style).NormalPosition().BackgroundColor(Colors.Blue.Medium);
text.Line("Subscript").Style(style).Subscript().BackgroundColor(Colors.Red.Medium);
});
});
}
[Test]
public void ParagraphSpacing()
{
+16 -2
View File
@@ -2,6 +2,7 @@
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using QuestPDF.Fluent;
using QuestPDF.Infrastructure;
using SkiaSharp;
@@ -68,7 +69,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 +77,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);
@@ -98,7 +104,15 @@ namespace QuestPDF.Drawing
internal static SKFontMetrics ToFontMetrics(this TextStyle style)
{
return FontMetrics.GetOrAdd(style.FontMetricsKey, key => style.ToPaint().FontMetrics);
return FontMetrics.GetOrAdd(style.FontMetricsKey, key => style.NormalPosition().ToPaint().FontMetrics);
}
private static float GetTextScale(TextStyle style)
{
if (style.FontPosition == FontPosition.Superscript || style.FontPosition == FontPosition.Subscript)
return 0.65f;
return 1;
}
}
}
+15 -3
View File
@@ -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;
}
}
}
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,9 @@
namespace QuestPDF.Infrastructure
{
internal enum FontPosition
{
Normal = 0,
Subscript,
Superscript,
}
}
+5 -1
View File
@@ -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;