Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support text runs. #251

Merged
merged 52 commits into from
Mar 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
4f9f992
Initial implementation
JimBobSquarePants Mar 2, 2022
b04fcaf
Merge branch 'main' into js/text-runs
JimBobSquarePants Mar 2, 2022
2ceb25a
Fix bool assignment
JimBobSquarePants Mar 2, 2022
2ef2353
Trim down to a max of two runs
JimBobSquarePants Mar 2, 2022
65d8384
Ensure runs cannot overlap
JimBobSquarePants Mar 2, 2022
7b74864
Validate run Start and End properties before slicing.
JimBobSquarePants Mar 2, 2022
a4a0e4d
Add some TextRun tests, simplify TextLayout
JimBobSquarePants Mar 3, 2022
3796290
Assert Font is set
JimBobSquarePants Mar 3, 2022
e2b7bd5
Persist TextRun font sizes
JimBobSquarePants Mar 3, 2022
3b62bab
Do groundwork for attribute support.
JimBobSquarePants Mar 4, 2022
9283316
Fix tests build
JimBobSquarePants Mar 4, 2022
ac767a8
First pass at POST table
Redth Mar 4, 2022
f05c44e
Merge pull request #252 from Redth/main
JimBobSquarePants Mar 4, 2022
9790ad6
Wire up post table
JimBobSquarePants Mar 4, 2022
afa057d
Add TODO: notes
JimBobSquarePants Mar 4, 2022
45b5c9c
Superscript/Subscript scaling works.
JimBobSquarePants Mar 4, 2022
7107da3
Scale and offset subscript + superscript
JimBobSquarePants Mar 4, 2022
91916c7
Add TextRun available to GlyphRendererParameters
JimBobSquarePants Mar 4, 2022
3439a98
Optimize scaled transform to a single call
JimBobSquarePants Mar 4, 2022
af80bc8
Restore good API
JimBobSquarePants Mar 4, 2022
a51f4fa
Render underlines/strikeout parts.
tocsoft Mar 4, 2022
c34b1c2
add attributes
tocsoft Mar 4, 2022
74512e3
use better left hand side
tocsoft Mar 4, 2022
a1441e9
drop commented code
tocsoft Mar 4, 2022
1eb941a
Merge pull request #254 from SixLabors/sw/text-run-lines
JimBobSquarePants Mar 4, 2022
f256682
Fix scaled line offset, lockdown glyph rendering, fix bidi tests
JimBobSquarePants Mar 5, 2022
f3de9c9
Fix null check and optimize
JimBobSquarePants Mar 5, 2022
09654f9
better position underline/strikeout with better thinkness
tocsoft Mar 5, 2022
953fb9b
Fix line and layout rendering of Arabic scripts.
JimBobSquarePants Mar 5, 2022
5a09926
Merge branch 'js/text-runs' of https://github.com/SixLabors/Fonts int…
JimBobSquarePants Mar 5, 2022
4b63c79
Update Program.cs
JimBobSquarePants Mar 5, 2022
513d035
Fix overlong stroke
JimBobSquarePants Mar 6, 2022
a75eb02
Don't use rsb for fake advance measurements
JimBobSquarePants Mar 6, 2022
b002415
Always Ceil string measuring results.
JimBobSquarePants Mar 6, 2022
d30b285
Pixel perfect stroke. (Tested with drawing PR)
JimBobSquarePants Mar 6, 2022
205cb94
Split TextDecoration and TextAttribute and add decoration renderer
tocsoft Mar 6, 2022
9889bde
stylecoped
tocsoft Mar 6, 2022
8360ce7
Offset overline to avoid clipping.
JimBobSquarePants Mar 7, 2022
847c668
Fixed fix (ha!)
JimBobSquarePants Mar 7, 2022
1303b7c
Add font metrics tests for new properties
JimBobSquarePants Mar 7, 2022
5d8fdfe
Update enum names to match runtime standard
JimBobSquarePants Mar 7, 2022
642999d
Fix horizontal variable font size line-height handling
JimBobSquarePants Mar 11, 2022
41687c7
Fix initial vertical y offset
JimBobSquarePants Mar 11, 2022
ceb5656
Fix vertical layout + linebreak
JimBobSquarePants Mar 11, 2022
d7b4326
Update test to match fixes
JimBobSquarePants Mar 11, 2022
db51f98
Drop precision for .NET FX
JimBobSquarePants Mar 13, 2022
6a883af
Fix line height
JimBobSquarePants Mar 14, 2022
a43a592
layout box is a fools errand. Leading FTW
JimBobSquarePants Mar 14, 2022
39773e6
Use graphemes, fix line-gap usage, fix mixed line-height
JimBobSquarePants Mar 20, 2022
cfec9a5
Tweak adjusted advance ratio based on testing
JimBobSquarePants Mar 21, 2022
6c3d00b
Fix font hinting
JimBobSquarePants Mar 24, 2022
7b3ec08
Don't upscale already scaled ppem
JimBobSquarePants Mar 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 33 additions & 5 deletions samples/DrawWithImageSharp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Numerics;
using System.Text;
using DrawWithImageSharp;
using SixLabors.Fonts.Unicode;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing;
using SixLabors.ImageSharp.Drawing.Processing;
Expand All @@ -33,22 +34,43 @@ public static void Main(string[] args)
FontFamily colorEmoji = fonts.Add(@"Fonts\Twemoji Mozilla.ttf");
FontFamily font2 = fonts.Add(@"Fonts\OpenSans-Regular.ttf");
FontFamily sunflower = fonts.Add(@"Fonts\Sunflower-Medium.ttf");
FontFamily bugzilla = fonts.Add(@"Fonts\me_quran_volt_newmet.ttf");

#if OS_WINDOWS
FontFamily emojiFont = SystemFonts.Get("Segoe UI Emoji");
FontFamily uiFont = SystemFonts.Get("Segoe UI");
FontFamily arabicFont = SystemFonts.Get("Dubai");

FontFamily tahoma = SystemFonts.Get("Tahoma");
var textRuns = new List<TextRun>
{
new TextRun { Start = 4, End = 9, Font = uiFont.CreateFont(72, FontStyle.Bold), TextAttributes = TextAttributes.Superscript, TextDecorations = TextDecorations.Underline | TextDecorations.Strikeout | TextDecorations.Overline },
new TextRun { Start = 26, End = 30, Font = uiFont.CreateFont(72, FontStyle.Italic), TextAttributes = TextAttributes.Subscript, TextDecorations = TextDecorations.Strikeout | TextDecorations.Underline | TextDecorations.Overline }
};

RenderText(uiFont, "The quick brown fox jumps over the lazy dog", pointSize: 72, textRuns: textRuns);

string arabic = "بِسْمِ ٱللَّهِ ٱلرَّحْمَٟنِ ٱلرَّحِيمِ";
textRuns = new List<TextRun>
{
new TextRun { Start = 0, End = CodePoint.GetCodePointCount(arabic), TextDecorations = TextDecorations.Underline }
};

RenderText(uiFont, arabic, pointSize: 72, textRuns: textRuns);

textRuns = new List<TextRun>
{
new TextRun { Start = 0, End = CodePoint.GetCodePointCount(arabic), TextDecorations = TextDecorations.Underline }
};
RenderText(bugzilla, arabic, pointSize: 72, textRuns: textRuns);

RenderText(font2, "\uFB01", pointSize: 11.25F);
RenderText(fontWoff2, "\uFB01", pointSize: 11.25F);
RenderText(tahoma, "p", pointSize: 11.25F);
RenderText(tahoma, "Lorem ipsum dolor sit amet", pointSize: 11.25F);
RenderText(uiFont, "Soft\u00ADHyphen", pointSize: 72);
FontFamily bugzilla = fonts.Add(@"Fonts\me_quran_volt_newmet.ttf");

RenderText(uiFont, "Soft\u00ADHyphen", pointSize: 72);
RenderText(bugzilla, "بِسْمِ ٱللَّهِ ٱلرَّحْمَٟنِ ٱلرَّحِيمِ", pointSize: 72);

RenderText(uiFont, "first\n\n\n\nl", pointSize: 20, fallbackFonts: new[] { font2 });

Expand Down Expand Up @@ -182,12 +204,18 @@ public static void RenderText(TextOptions options, string text)
SaveImage(options, text, (int)size.Width, (int)size.Height, options.Font.Name, text + ".png");
}

public static void RenderText(FontFamily font, string text, float pointSize = 12, IEnumerable<FontFamily> fallbackFonts = null)
public static void RenderText(
FontFamily font,
string text,
float pointSize = 12,
IEnumerable<FontFamily> fallbackFonts = null,
IEnumerable<TextRun> textRuns = null)
=> RenderText(
new TextOptions(new Font(font, pointSize))
{
WrappingLength = 400,
FallbackFontFamilies = fallbackFonts?.ToArray()
// WrappingLength = 400,
FallbackFontFamilies = fallbackFonts?.ToArray(),
TextRuns = textRuns?.ToArray()
},
text);

Expand Down
9 changes: 3 additions & 6 deletions src/SixLabors.Fonts/BigEndianBinaryReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,14 @@ public TEnum ReadInt16<TEnum>()
public ushort ReadUFWORD() => this.ReadUInt16();

/// <summary>
/// Reads a 32-bit signed integer from the stream.
/// Reads a fixed 32-bit value from the stream.
/// 4 bytes are read.
/// </summary>
/// <returns>The 32-bit integer read.</returns>
/// <returns>The 32-bit value read.</returns>
public float ReadFixed()
{
this.ReadInternal(this.buffer, 4);

int value = BinaryPrimitives.ReadInt32BigEndian(this.buffer);

return Unsafe.As<int, float>(ref value);
return BinaryPrimitives.ReadInt32BigEndian(this.buffer) / 65536F;
}

/// <summary>
Expand Down
39 changes: 39 additions & 0 deletions src/SixLabors.Fonts/FileFontMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,45 @@ internal FileFontMetrics(FontDescription description, string path, long offset)
/// <inheritdoc/>
public override short AdvanceHeightMax => this.metrics.Value.AdvanceHeightMax;

/// <inheritdoc/>
public override short SubscriptXSize => this.metrics.Value.SubscriptXSize;

/// <inheritdoc/>
public override short SubscriptYSize => this.metrics.Value.SubscriptYSize;

/// <inheritdoc/>
public override short SubscriptXOffset => this.metrics.Value.SubscriptXOffset;

/// <inheritdoc/>
public override short SubscriptYOffset => this.metrics.Value.SubscriptYOffset;

/// <inheritdoc/>
public override short SuperscriptXSize => this.metrics.Value.SuperscriptXSize;

/// <inheritdoc/>
public override short SuperscriptYSize => this.metrics.Value.SuperscriptYSize;

/// <inheritdoc/>
public override short SuperscriptXOffset => this.metrics.Value.SuperscriptXOffset;

/// <inheritdoc/>
public override short SuperscriptYOffset => this.metrics.Value.SuperscriptYOffset;

/// <inheritdoc/>
public override short StrikeoutSize => this.metrics.Value.StrikeoutSize;

/// <inheritdoc/>
public override short StrikeoutPosition => this.metrics.Value.StrikeoutPosition;

/// <inheritdoc/>
public override short UnderlinePosition => this.metrics.Value.UnderlinePosition;

/// <inheritdoc/>
public override short UnderlineThickness => this.metrics.Value.UnderlineThickness;

/// <inheritdoc/>
public override float ItalicAngle => this.metrics.Value.ItalicAngle;

/// <inheritdoc/>
internal override bool TryGetGlyphId(CodePoint codePoint, out ushort glyphId)
=> this.metrics.Value.TryGetGlyphId(codePoint, out glyphId);
Expand Down
13 changes: 12 additions & 1 deletion src/SixLabors.Fonts/Font.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,21 @@ public bool TryGetPath([NotNullWhen(true)] out string? path)
/// <param name="support">Options for enabling color font support during layout and rendering.</param>
/// <returns>Returns the glyph</returns>
public IEnumerable<Glyph> GetGlyphs(CodePoint codePoint, ColorFontSupport support)
=> this.GetGlyphs(codePoint, TextAttributes.None, support);

/// <summary>
/// Gets the glyphs for the given codepoint.
/// </summary>
/// <param name="codePoint">The code point of the character.</param>
/// <param name="textAttributes">The text attributes to apply to the glyphs.</param>
/// <param name="support">Options for enabling color font support during layout and rendering.</param>
/// <returns>Returns the glyph</returns>
public IEnumerable<Glyph> GetGlyphs(CodePoint codePoint, TextAttributes textAttributes, ColorFontSupport support)
{
TextRun textRun = new() { Start = 0, End = 1, Font = this, TextAttributes = textAttributes };
foreach (GlyphMetrics metrics in this.FontMetrics.GetGlyphMetrics(codePoint, support))
{
yield return new(metrics, this.Size);
yield return new(GlyphMetrics.CloneForRendering(metrics, textRun, codePoint), this.Size);
}
}

Expand Down
66 changes: 66 additions & 0 deletions src/SixLabors.Fonts/FontMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,72 @@ internal FontMetrics()
/// </summary>
public abstract short AdvanceHeightMax { get; }

/// <summary>
/// Gets the recommended horizontal size in font design units for subscripts for this font.
/// </summary>
public abstract short SubscriptXSize { get; }

/// <summary>
/// Gets the recommended vertical size in font design units for subscripts for this font.
/// </summary>
public abstract short SubscriptYSize { get; }

/// <summary>
/// Gets the recommended horizontal offset in font design units for subscripts for this font.
/// </summary>
public abstract short SubscriptXOffset { get; }

/// <summary>
/// Gets the recommended vertical offset in font design units for subscripts for this font.
/// </summary>
public abstract short SubscriptYOffset { get; }

/// <summary>
/// Gets the recommended horizontal size in font design units for superscripts for this font.
/// </summary>
public abstract short SuperscriptXSize { get; }

/// <summary>
/// Gets the recommended vertical size in font design units for superscripts for this font.
/// </summary>
public abstract short SuperscriptYSize { get; }

/// <summary>
/// Gets the recommended horizontal offset in font design units for superscripts for this font.
/// </summary>
public abstract short SuperscriptXOffset { get; }

/// <summary>
/// Gets the recommended vertical offset in font design units for superscripts for this font.
/// </summary>
public abstract short SuperscriptYOffset { get; }

/// <summary>
/// Gets thickness of the strikeout stroke in font design units.
/// </summary>
public abstract short StrikeoutSize { get; }

/// <summary>
/// Gets the position of the top of the strikeout stroke relative to the baseline in font design units.
/// </summary>
public abstract short StrikeoutPosition { get; }

/// <summary>
/// Gets the suggested distance of the top of the underline from the baseline (negative values indicate below baseline).
/// </summary>
public abstract short UnderlinePosition { get; }

/// <summary>
/// Gets the suggested values for the underline thickness. In general, the underline thickness should match the thickness of
/// the underscore character (U+005F LOW LINE), and should also match the strikeout thickness, which is specified in the OS/2 table.
/// </summary>
public abstract short UnderlineThickness { get; }

/// <summary>
/// Gets the italic angle in counter-clockwise degrees from the vertical. Zero for upright text, negative for text that leans to the right (forward).
/// </summary>
public abstract float ItalicAngle { get; }

/// <summary>
/// Gets the specified glyph id matching the codepoint.
/// </summary>
Expand Down
73 changes: 38 additions & 35 deletions src/SixLabors.Fonts/GlyphLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.

using System.Numerics;
using System.Text;
using SixLabors.Fonts.Unicode;

namespace SixLabors.Fonts
Expand All @@ -15,14 +14,21 @@ internal readonly struct GlyphLayout
internal GlyphLayout(
Glyph glyph,
Vector2 location,
float ascender,
float descender,
float linegap,
float lineHeight,
float width,
float height,
float lineHeight,
bool isStartOfLine)
{
this.LineHeight = lineHeight;
this.Glyph = glyph;
this.CodePoint = glyph.GlyphMetrics.CodePoint;
this.Location = location;
this.Ascender = ascender;
this.Descender = descender;
this.LineGap = linegap;
this.LineHeight = lineHeight;
this.Width = width;
this.Height = height;
this.IsStartOfLine = isStartOfLine;
Expand All @@ -33,26 +39,46 @@ internal GlyphLayout(
/// </summary>
public Glyph Glyph { get; }

/// <summary>
/// Gets the codepoint represented by this glyph.
/// </summary>
public CodePoint CodePoint { get; }

/// <summary>
/// Gets the location.
/// </summary>
public Vector2 Location { get; }

/// <summary>
/// Gets the width.
/// Gets the ascender
/// </summary>
public float Width { get; }
public float Ascender { get; }

/// <summary>
/// Gets the height.
/// Gets the ascender
/// </summary>
public float Height { get; }
public float Descender { get; }

/// <summary>
/// Gets the lie gap
/// </summary>
public float LineGap { get; }

/// <summary>
/// Gets the line height of the glyph.
/// </summary>
public float LineHeight { get; }

/// <summary>
/// Gets the width.
/// </summary>
public float Width { get; }

/// <summary>
/// Gets the height.
/// </summary>
public float Height { get; }

/// <summary>
/// Gets a value indicating whether this glyph is the first glyph on a new line.
/// </summary>
Expand All @@ -62,7 +88,7 @@ internal GlyphLayout(
/// Gets a value indicating whether the glyph represents a whitespace character.
/// </summary>
/// <returns>The <see cref="bool"/>.</returns>
public bool IsWhiteSpace() => CodePoint.IsWhiteSpace(this.Glyph.GlyphMetrics.CodePoint);
public bool IsWhiteSpace() => CodePoint.IsWhiteSpace(this.CodePoint);

internal FontRectangle BoundingBox(float dpi)
{
Expand All @@ -79,33 +105,10 @@ internal FontRectangle BoundingBox(float dpi)
/// <inheritdoc/>
public override string ToString()
{
var sb = new StringBuilder();
if (this.IsStartOfLine)
{
sb.Append('@');
sb.Append(' ');
}

if (this.IsWhiteSpace())
{
sb.Append('!');
}

sb.Append('\'');
sb.Append(this.Glyph.GlyphMetrics.CodePoint.ToDebuggerDisplay());

sb.Append('\'');
sb.Append(' ');

sb.Append(this.Location.X);
sb.Append(',');
sb.Append(this.Location.Y);
sb.Append(' ');
sb.Append(this.Width);
sb.Append('x');
sb.Append(this.Height);

return sb.ToString();
string s = this.IsStartOfLine ? "@ " : string.Empty;
string ws = this.IsWhiteSpace() ? "!" : string.Empty;
Vector2 l = this.Location;
return $"{s}{ws}{this.CodePoint.ToDebuggerDisplay()} {l.X},{l.Y} {this.Width}x{this.Height}";
}
}
}
Loading