Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
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
9 changes: 1 addition & 8 deletions SixLabors.Fonts.sln
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{71A3
samples\Fonts.Samples.ruleset = samples\Fonts.Samples.ruleset
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DrawWithImageSharp", "samples\DrawWithImageSharp\DrawWithImageSharp.csproj", "{999EDFB3-9FE4-4E09-B669-CB02E597EC20}"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is temporary due to breaking changes re IGlyphRenderer

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a local version of SixLabors/ImageSharp.Drawing#211 that works with these changes.

EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ListFonts", "samples\ListFonts\ListFonts.csproj", "{6DF4C474-1FDF-4DE0-9CB2-9674D2CCE1BA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{7CDD4908-7CCD-4945-860C-D2F1732D3AE6}"
Expand Down Expand Up @@ -74,7 +72,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnicodeTestData", "UnicodeT
tests\UnicodeTestData\README.md = tests\UnicodeTestData\README.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SixLabors.Fonts.Benchmarks", "tests\SixLabors.Fonts.Benchmarks\SixLabors.Fonts.Benchmarks\SixLabors.Fonts.Benchmarks.csproj", "{FB8FDC5F-7FEB-4132-9133-C25E05C0B3D9}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SixLabors.Fonts.Benchmarks", "tests\SixLabors.Fonts.Benchmarks\SixLabors.Fonts.Benchmarks\SixLabors.Fonts.Benchmarks.csproj", "{FB8FDC5F-7FEB-4132-9133-C25E05C0B3D9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -90,10 +88,6 @@ Global
{F836E8E6-B4D9-4208-8346-140C74678B91}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F836E8E6-B4D9-4208-8346-140C74678B91}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F836E8E6-B4D9-4208-8346-140C74678B91}.Release|Any CPU.Build.0 = Release|Any CPU
{999EDFB3-9FE4-4E09-B669-CB02E597EC20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{999EDFB3-9FE4-4E09-B669-CB02E597EC20}.Debug|Any CPU.Build.0 = Debug|Any CPU
{999EDFB3-9FE4-4E09-B669-CB02E597EC20}.Release|Any CPU.ActiveCfg = Release|Any CPU
{999EDFB3-9FE4-4E09-B669-CB02E597EC20}.Release|Any CPU.Build.0 = Release|Any CPU
{6DF4C474-1FDF-4DE0-9CB2-9674D2CCE1BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6DF4C474-1FDF-4DE0-9CB2-9674D2CCE1BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6DF4C474-1FDF-4DE0-9CB2-9674D2CCE1BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand All @@ -113,7 +107,6 @@ Global
GlobalSection(NestedProjects) = preSolution
{09E744EC-4852-4FC7-BE78-C1B399F17967} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
{F836E8E6-B4D9-4208-8346-140C74678B91} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
{999EDFB3-9FE4-4E09-B669-CB02E597EC20} = {71A3911C-D6B9-4EBE-9691-2FE28BDF462E}
{6DF4C474-1FDF-4DE0-9CB2-9674D2CCE1BA} = {71A3911C-D6B9-4EBE-9691-2FE28BDF462E}
{7CDD4908-7CCD-4945-860C-D2F1732D3AE6} = {C317F1B1-D75E-4C6D-83EB-80367343E0D7}
{EC0F1812-C6AF-48D8-882B-5637730D2DB1} = {7CDD4908-7CCD-4945-860C-D2F1732D3AE6}
Expand Down
6 changes: 3 additions & 3 deletions samples/DrawWithImageSharp/CustomGlyphBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace DrawWithImageSharp
/// </summary>
internal class CustomGlyphBuilder : GlyphBuilder
{
private readonly List<FontRectangle> glyphBounds = new List<FontRectangle>();
private readonly List<FontRectangle> glyphBounds = new();

public CustomGlyphBuilder()
{
Expand All @@ -36,13 +36,13 @@ public CustomGlyphBuilder(Vector2 origin)
/// </summary>
public IPath TextBox { get; private set; }

protected override void BeginText(FontRectangle rect)
protected override void BeginText(in FontRectangle rect)
{
this.TextBox = new RectangularPolygon(rect.Location, rect.Size);
base.BeginText(rect);
}

protected override void BeginGlyph(FontRectangle rect)
protected override void BeginGlyph(in FontRectangle rect)
{
this.glyphBounds.Add(rect);
base.BeginText(rect);
Expand Down
83 changes: 49 additions & 34 deletions src/SixLabors.Fonts/FileFontMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Numerics;
using SixLabors.Fonts.Tables;
using SixLabors.Fonts.Tables.AdvancedTypographic;
using SixLabors.Fonts.Unicode;
Expand All @@ -19,7 +20,7 @@ namespace SixLabors.Fonts
/// </summary>
internal sealed class FileFontMetrics : FontMetrics
{
private readonly Lazy<StreamFontMetrics> metrics;
private readonly Lazy<StreamFontMetrics> fontMetrics;

public FileFontMetrics(string path)
: this(path, 0)
Expand All @@ -35,7 +36,7 @@ internal FileFontMetrics(FontDescription description, string path, long offset)
{
this.Description = description;
this.Path = path;
this.metrics = new Lazy<StreamFontMetrics>(() => StreamFontMetrics.LoadFont(path, offset));
this.fontMetrics = new Lazy<StreamFontMetrics>(() => StreamFontMetrics.LoadFont(path, offset));
}

/// <inheritdoc cref="FontMetrics.Description"/>
Expand All @@ -47,107 +48,121 @@ internal FileFontMetrics(FontDescription description, string path, long offset)
public string Path { get; }

/// <inheritdoc />
public override ushort UnitsPerEm => this.metrics.Value.UnitsPerEm;
public override ushort UnitsPerEm => this.fontMetrics.Value.UnitsPerEm;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/// <inheritdoc/>
internal override bool TryGetGlyphId(CodePoint codePoint, out ushort glyphId)
=> this.metrics.Value.TryGetGlyphId(codePoint, out glyphId);
=> this.fontMetrics.Value.TryGetGlyphId(codePoint, out glyphId);

/// <inheritdoc/>
internal override bool TryGetGlyphId(
CodePoint codePoint,
CodePoint? nextCodePoint,
out ushort glyphId,
out bool skipNextCodePoint)
=> this.metrics.Value.TryGetGlyphId(codePoint, nextCodePoint, out glyphId, out skipNextCodePoint);
=> this.fontMetrics.Value.TryGetGlyphId(codePoint, nextCodePoint, out glyphId, out skipNextCodePoint);

/// <inheritdoc/>
internal override bool TryGetGlyphClass(ushort glyphId, [NotNullWhen(true)] out GlyphClassDef? glyphClass)
=> this.metrics.Value.TryGetGlyphClass(glyphId, out glyphClass);
=> this.fontMetrics.Value.TryGetGlyphClass(glyphId, out glyphClass);

/// <inheritdoc/>
internal override bool TryGetMarkAttachmentClass(ushort glyphId, [NotNullWhen(true)] out GlyphClassDef? markAttachmentClass)
=> this.metrics.Value.TryGetMarkAttachmentClass(glyphId, out markAttachmentClass);
=> this.fontMetrics.Value.TryGetMarkAttachmentClass(glyphId, out markAttachmentClass);

/// <inheritdoc />
public override IEnumerable<GlyphMetrics> GetGlyphMetrics(CodePoint codePoint, ColorFontSupport support)
=> this.metrics.Value.GetGlyphMetrics(codePoint, support);
public override bool TryGetGlyphMetrics(
CodePoint codePoint,
TextAttributes textAttributes,
TextDecorations textDecorations,
ColorFontSupport support,
[NotNullWhen(true)] out IReadOnlyList<GlyphMetrics>? metrics)
=> this.fontMetrics.Value.TryGetGlyphMetrics(codePoint, textAttributes, textDecorations, support, out metrics);

/// <inheritdoc />
internal override IEnumerable<GlyphMetrics> GetGlyphMetrics(CodePoint codePoint, ushort glyphId, ColorFontSupport support)
=> this.metrics.Value.GetGlyphMetrics(codePoint, glyphId, support);
internal override IReadOnlyList<GlyphMetrics> GetGlyphMetrics(
CodePoint codePoint,
ushort glyphId,
TextAttributes textAttributes,
TextDecorations textDecorations,
ColorFontSupport support)
=> this.fontMetrics.Value.GetGlyphMetrics(codePoint, glyphId, textAttributes, textDecorations, support);

/// <inheritdoc />
internal override IReadOnlyList<CodePoint> GetAvailableCodePoints()
=> this.metrics.Value.GetAvailableCodePoints();
=> this.fontMetrics.Value.GetAvailableCodePoints();

/// <inheritdoc/>
internal override void ApplySubstitution(GlyphSubstitutionCollection collection)
=> this.metrics.Value.ApplySubstitution(collection);
=> this.fontMetrics.Value.ApplySubstitution(collection);

/// <inheritdoc/>
internal override bool TryGetKerningOffset(ushort previousId, ushort currentId, out Vector2 vector)
=> this.fontMetrics.Value.TryGetKerningOffset(previousId, currentId, out vector);

/// <inheritdoc/>
internal override void UpdatePositions(GlyphPositioningCollection collection)
=> this.metrics.Value.UpdatePositions(collection);
=> this.fontMetrics.Value.UpdatePositions(collection);

/// <summary>
/// Reads a <see cref="StreamFontMetrics"/> from the specified stream.
Expand Down
96 changes: 88 additions & 8 deletions src/SixLabors.Fonts/Font.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using SixLabors.Fonts.Unicode;

namespace SixLabors.Fonts
Expand Down Expand Up @@ -142,31 +143,110 @@ public bool TryGetPath([NotNullWhen(true)] out string? path)
return false;
}

/// <summary>
/// Gets the glyphs for the given codepoint.
/// </summary>
/// <param name="codePoint">The code point of the character.</param>
/// <param name="glyphs">
/// When this method returns, contains the glyphs for the given codepoint if the glyphs
/// are found; otherwise the default value. This parameter is passed uninitialized.
/// </param>
/// <returns>
/// <see langword="true"/> if the face contains glyphs for the specified codepoint; otherwise, <see langword="false"/>.
/// </returns>
public bool TryGetGlyphs(CodePoint codePoint, [NotNullWhen(true)] out IReadOnlyList<Glyph>? glyphs)
=> this.TryGetGlyphs(codePoint, TextAttributes.None, ColorFontSupport.None, out glyphs);

/// <summary>
/// Gets the glyphs for the given codepoint.
/// </summary>
/// <param name="codePoint">The code point of the character.</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, ColorFontSupport support)
=> this.GetGlyphs(codePoint, TextAttributes.None, support);
/// <param name="glyphs">
/// When this method returns, contains the glyphs for the given codepoint and color support if the glyphs
/// are found; otherwise the default value. This parameter is passed uninitialized.
/// </param>
/// <returns>
/// <see langword="true"/> if the face contains glyphs for the specified codepoint; otherwise, <see langword="false"/>.
/// </returns>
public bool TryGetGlyphs(CodePoint codePoint, ColorFontSupport support, [NotNullWhen(true)] out IReadOnlyList<Glyph>? glyphs)
=> this.TryGetGlyphs(codePoint, TextAttributes.None, support, out glyphs);

/// <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)
/// <param name="glyphs">
/// When this method returns, contains the glyphs for the given codepoint, attributes, and color support if the glyphs
/// are found; otherwise the default value. This parameter is passed uninitialized.
/// </param>
/// <returns>
/// <see langword="true"/> if the face contains glyphs for the specified codepoint; otherwise, <see langword="false"/>.
/// </returns>
public bool TryGetGlyphs(
CodePoint codePoint,
TextAttributes textAttributes,
ColorFontSupport support,
[NotNullWhen(true)] out IReadOnlyList<Glyph>? glyphs)
=> this.TryGetGlyphs(codePoint, textAttributes, TextDecorations.None, support, out glyphs);

/// <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="textDecorations">The text decorations to apply to the glyphs.</param>
/// <param name="support">Options for enabling color font support during layout and rendering.</param>
/// <param name="glyphs">
/// When this method returns, contains the glyphs for the given codepoint, attributes, and color support if the glyphs
/// are found; otherwise the default value. This parameter is passed uninitialized.
/// </param>
/// <returns>
/// <see langword="true"/> if the face contains glyphs for the specified codepoint; otherwise, <see langword="false"/>.
/// </returns>
public bool TryGetGlyphs(
CodePoint codePoint,
TextAttributes textAttributes,
TextDecorations textDecorations,
ColorFontSupport support,
[NotNullWhen(true)] out IReadOnlyList<Glyph>? glyphs)
{
TextRun textRun = new() { Start = 0, End = 1, Font = this, TextAttributes = textAttributes };
foreach (GlyphMetrics metrics in this.FontMetrics.GetGlyphMetrics(codePoint, support))
TextRun textRun = new() { Start = 0, End = 1, Font = this, TextAttributes = textAttributes, TextDecorations = textDecorations };
if (this.FontMetrics.TryGetGlyphMetrics(codePoint, textAttributes, textDecorations, support, out IReadOnlyList<GlyphMetrics>? metrics))
{
yield return new(metrics.CloneForRendering(textRun, codePoint), this.Size);
List<Glyph> g = new();
foreach (GlyphMetrics metric in metrics)
{
g.Add(new(metric.CloneForRendering(textRun), this.Size));
}

glyphs = g;
return true;
}

glyphs = default;
return false;
}

/// <summary>
/// Gets the amount, in font units, the <paramref name="current"/> glyph should be offset if it is proceeded by
/// the <paramref name="previous"/> glyph.
/// </summary>
/// <param name="previous">The previous glyph.</param>
/// <param name="current">The current glyph.</param>
/// <param name="vector">
/// When this method returns, contains the offset, in font units, that should be applied to the
/// <paramref name="current"/> glyph, if the offset is found; otherwise the default vector value.
/// This parameter is passed uninitialized.
/// </param>
/// <returns>
/// <see langword="true"/> if the face contains and offset for the glyph combination; otherwise, <see langword="false"/>.
/// </returns>
public bool TryGetKerningOffset(Glyph previous, Glyph current, out Vector2 vector)
=> this.FontMetrics.TryGetKerningOffset(previous.GlyphMetrics.GlyphId, current.GlyphMetrics.GlyphId, out vector);

private string LoadFontName()
=> this.metrics.Value?.Description.FontName(this.Family.Culture) ?? string.Empty;

Expand Down
Loading