Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
22 changes: 19 additions & 3 deletions src/SixLabors.Fonts/Tables/AdvancedTypographic/CoverageTable.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using static SixLabors.Fonts.Tables.AdvancedTypographic.CoverageFormat2Table;

namespace SixLabors.Fonts.Tables.AdvancedTypographic;

/// <summary>
Expand All @@ -23,13 +25,16 @@ public static CoverageTable Load(BigEndianBinaryReader reader, long offset)
{
1 => CoverageFormat1Table.Load(reader),
2 => CoverageFormat2Table.Load(reader),
_ => throw new InvalidFontFileException($"Invalid value for 'coverageFormat' {coverageFormat}. Should be '1' or '2'.")

// Harfbuzz (Coverage.hh) treats this as an empty table and does not throw.
// SofiaSans Condensed can trigger this. See https://github.com/SixLabors/Fonts/issues/470
_ => EmptyCoverageTable.Instance
};
}

public static CoverageTable[] LoadArray(BigEndianBinaryReader reader, long offset, ReadOnlySpan<ushort> coverageOffsets)
{
var tables = new CoverageTable[coverageOffsets.Length];
CoverageTable[] tables = new CoverageTable[coverageOffsets.Length];
for (int i = 0; i < tables.Length; i++)
{
tables[i] = Load(reader, offset + coverageOffsets[i]);
Expand Down Expand Up @@ -103,7 +108,7 @@ public static CoverageFormat2Table Load(BigEndianBinaryReader reader)
// | RangeRecord | rangeRecords[rangeCount] | Array of glyph ranges — ordered by startGlyphID. |
// +-------------+--------------------------+--------------------------------------------------+
ushort rangeCount = reader.ReadUInt16();
var records = new CoverageRangeRecord[rangeCount];
CoverageRangeRecord[] records = new CoverageRangeRecord[rangeCount];

for (int i = 0; i < records.Length; i++)
{
Expand All @@ -124,4 +129,15 @@ public static CoverageFormat2Table Load(BigEndianBinaryReader reader)

return new CoverageFormat2Table(records);
}

internal sealed class EmptyCoverageTable : CoverageTable
{
private EmptyCoverageTable()
{
}

public static EmptyCoverageTable Instance { get; } = new();

public override int CoverageIndexOf(ushort glyphId) => -1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ namespace SixLabors.Fonts.Tables.AdvancedTypographic.GPos;
[DebuggerDisplay("X: {XCoordinate}, Y: {YCoordinate}")]
internal abstract class AnchorTable
{
private static readonly AnchorTable Empty = new EmptyAnchor();

/// <summary>
/// Initializes a new instance of the <see cref="AnchorTable"/> class.
/// </summary>
Expand Down Expand Up @@ -53,9 +51,9 @@ public static AnchorTable Load(BigEndianBinaryReader reader, long offset)
2 => AnchorFormat2.Load(reader),
3 => AnchorFormat3.Load(reader),

// Harfbuzz (Anchor.hh) and FontKit appear to treat this as a default anchor and do not throw.
// Harfbuzz (Anchor.hh) treats this as an empty table and does not throw..
// NotoSans Regular can trigger this. See https://github.com/SixLabors/Fonts/issues/417
_ => Empty,
_ => EmptyAnchorTable.Instance,
};
}

Expand Down Expand Up @@ -185,13 +183,15 @@ public override AnchorXY GetAnchor(FontMetrics fontMetrics, GlyphShapingData dat
=> new(this.XCoordinate, this.YCoordinate);
}

internal sealed class EmptyAnchor : AnchorTable
internal sealed class EmptyAnchorTable : AnchorTable
{
public EmptyAnchor()
private EmptyAnchorTable()
: base(0, 0)
{
}

public static EmptyAnchorTable Instance { get; } = new();

public override AnchorXY GetAnchor(
FontMetrics fontMetrics,
GlyphShapingData data,
Expand Down
3 changes: 3 additions & 0 deletions tests/Images/ReferenceOutput/TestIssue_468-.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions tests/Images/ReferenceOutput/Test_Issue_470-.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file added tests/SixLabors.Fonts.Tests/Fonts/consola.ttf
Binary file not shown.
42 changes: 42 additions & 0 deletions tests/SixLabors.Fonts.Tests/Issues/Issues_468.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using System.Text;

namespace SixLabors.Fonts.Tests.Issues;

public class Issues_468
{
[Fact]
public void TestIssue_468()
{
StringBuilder stringBuilder = new();
stringBuilder
.AppendLine("Latin: The quick brown fox jumps over the lazy dog.")
.AppendLine("Arabic (RTL & Shaping): نص حكيم له سر قاطع وذو شأن عظيم");

string text = stringBuilder.ToString();
FontCollection fontCollection = new();

string consola = fontCollection.Add(TestFonts.Consola).Name;
string arabic = fontCollection.Add(TestFonts.NotoSansArabicRegular).Name;

FontFamily mainFontFamily = fontCollection.Get(consola);
Font mainFont = mainFontFamily.CreateFont(50, FontStyle.Regular);

TextOptions options = new(mainFont)
{
FallbackFontFamilies =
[
fontCollection.Get(arabic),
],
};

// There are too many metrics to validate here so we just ensure no exceptions are thrown
// and the rendering looks correct by inspecting the snapshot.
TextLayoutTestUtilities.TestLayout(
text,
options,
includeGeometry: false);
}
}
50 changes: 20 additions & 30 deletions tests/SixLabors.Fonts.Tests/Issues/Issues_469.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,6 @@ public class Issues_469
[Fact]
public void Test_Issue_469()
{
const string arialFontName = "Arial";
const string inconsolataFontName = "Inconsolata";
const string nanumGothicCodingFontName = "NanumGothicCoding";
const string cousineFontName = "Cousine";
const string notoSansScThinFontName = "Noto Sans SC Thin";
const string notoSansJpThinFontName = "Noto Sans JP Thin";
const string notoNaskhArabicFontName = "Noto Naskh Arabic";
const string sarabunFontName = "Sarabun";
const string hindFontName = "Hind";

StringBuilder stringBuilder = new();
stringBuilder.AppendLine("Latin: The quick brown fox jumps over the lazy dog.")
.AppendLine("Cyrillic: Съешь же ещё этих мягких французских булок.")
Expand All @@ -33,33 +23,33 @@ public void Test_Issue_469()
.AppendLine("Devanagari (Conjuncts): ऋषियों को सताने वाले राक्षसों का अंत हो गया");

string text = stringBuilder.ToString();
FontCollection fontCollection = new();
fontCollection.Add(TestFonts.Arial);
fontCollection.Add(TestFonts.CousineRegular);
fontCollection.Add(TestFonts.HindRegular);
fontCollection.Add(TestFonts.NanumGothicCodingRegular);
fontCollection.Add(TestFonts.InconsolataRegular);
fontCollection.Add(TestFonts.NotoNaskhArabicRegular);
fontCollection.Add(TestFonts.NotoSansHKVariableFontWght);
fontCollection.Add(TestFonts.NotoSansJPRegular);
fontCollection.Add(TestFonts.NotoSansSCRegular);
fontCollection.Add(TestFonts.SarabunRegular);

FontFamily mainFontFamily = fontCollection.Get(arialFontName);
FontCollection fontCollection = new();
string arial = fontCollection.Add(TestFonts.Arial).Name;
string cousine = fontCollection.Add(TestFonts.CousineRegular).Name;
string hind = fontCollection.Add(TestFonts.HindRegular).Name;
string nanumGothicCoding = fontCollection.Add(TestFonts.NanumGothicCodingRegular).Name;
string inconsolata = fontCollection.Add(TestFonts.InconsolataRegular).Name;
string notoNaskhArabic = fontCollection.Add(TestFonts.NotoNaskhArabicRegular).Name;
string notoSansJpThin = fontCollection.Add(TestFonts.NotoSansJPRegular).Name;
string notoSansScThin = fontCollection.Add(TestFonts.NotoSansSCRegular).Name;
string sarabun = fontCollection.Add(TestFonts.SarabunRegular).Name;

FontFamily mainFontFamily = fontCollection.Get(arial);
Font mainFont = mainFontFamily.CreateFont(30, FontStyle.Regular);

TextOptions options = new(mainFont)
{
FallbackFontFamilies =
[
fontCollection.Get(inconsolataFontName),
fontCollection.Get(nanumGothicCodingFontName),
fontCollection.Get(cousineFontName),
fontCollection.Get(notoSansScThinFontName),
fontCollection.Get(notoSansJpThinFontName),
fontCollection.Get(notoNaskhArabicFontName),
fontCollection.Get(sarabunFontName),
fontCollection.Get(hindFontName),
fontCollection.Get(inconsolata),
fontCollection.Get(nanumGothicCoding),
fontCollection.Get(cousine),
fontCollection.Get(notoSansScThin),
fontCollection.Get(notoSansJpThin),
fontCollection.Get(notoNaskhArabic),
fontCollection.Get(sarabun),
fontCollection.Get(hind),
],
};

Expand Down
49 changes: 49 additions & 0 deletions tests/SixLabors.Fonts.Tests/Issues/Issues_470.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using System.Text;

namespace SixLabors.Fonts.Tests.Issues;

public class Issues_470
{
[Fact]
public void Test_Issue_470()
{
StringBuilder stringBuilder = new();
stringBuilder.AppendLine("Latin: The quick brown fox jumps over the lazy dog.")
.AppendLine("Cyrillic: Съешь же ещё этих мягких французских булок.")
.AppendLine("Greek: Ζαφείρι δέξου πάγκαλο, βαθῶν ψυχῆς τὸ σῆμα.")
.AppendLine("Chinese: 敏捷的棕色狐狸跳过了懒狗")
.AppendLine("Japanese: いろはにほへと ちりぬるを")
.AppendLine("Korean: 다람쥐 헌 쳇바퀴에 타고파")
.AppendLine("Arabic (RTL & Shaping): نص حكيم له سر قاطع وذو شأن عظيم")
.AppendLine("Hebrew (RTL): דג סקרן שט בים מאוכזב ולפתע מצא חברה")
.AppendLine("Thai (Complex): เป็นมนุษย์สุดประเสริฐเลิศคุณค่า")
.AppendLine("Devanagari (Conjuncts): ऋषियों को सताने वाले राक्षसों का अंत हो गया");

string text = stringBuilder.ToString();

FontCollection fontCollection = new();
string arial = fontCollection.Add(TestFonts.Arial).Name;
string sofia = fontCollection.Add(TestFonts.SofiaSansCondensedLight).Name;

FontFamily mainFontFamily = fontCollection.Get(arial);
Font mainFont = mainFontFamily.CreateFont(30, FontStyle.Regular);

TextOptions options = new(mainFont)
{
FallbackFontFamilies =
[
fontCollection.Get(sofia),
],
};

// There are too many metrics to validate here so we just ensure no exceptions are thrown
// and the rendering looks correct by inspecting the snapshot.
TextLayoutTestUtilities.TestLayout(
text,
options,
includeGeometry: false);
}
}
6 changes: 6 additions & 0 deletions tests/SixLabors.Fonts.Tests/TestFonts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,12 @@

public static string SarabunRegular => GetFullPath("Sarabun-Regular.ttf");

public static string NotoSansArabicRegular => GetFullPath("NotoSansArabic-Regular.ttf");

public static string Consola => GetFullPath("consola.ttf");

public static string SofiaSansCondensedLight => GetFullPath("SofiaSansCondensed-ExtraLight-Regular.ttf");

public static Stream TwemojiMozillaData() => OpenStream(TwemojiMozillaFile);

public static Stream SegoeuiEmojiData() => OpenStream(SegoeuiEmojiFile);
Expand Down Expand Up @@ -336,7 +342,7 @@

public static string GetFullPath(string path)
{
string root = Path.GetDirectoryName(new Uri(typeof(TestFonts).GetTypeInfo().Assembly.CodeBase).LocalPath);

Check warning on line 345 in tests/SixLabors.Fonts.Tests/TestFonts.cs

View workflow job for this annotation

GitHub Actions / Build (false, macos-latest, net9.0, 9.0.x, true, -x64, false)

'Assembly.CodeBase' is obsolete: 'Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location.' (https://aka.ms/dotnet-warnings/SYSLIB0012)

Check warning on line 345 in tests/SixLabors.Fonts.Tests/TestFonts.cs

View workflow job for this annotation

GitHub Actions / Build (false, ubuntu-latest, net8.0, 8.0.x, -x64, false)

'Assembly.CodeBase' is obsolete: 'Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location.' (https://aka.ms/dotnet-warnings/SYSLIB0012)

Check warning on line 345 in tests/SixLabors.Fonts.Tests/TestFonts.cs

View workflow job for this annotation

GitHub Actions / Build (false, windows-latest, net8.0, 8.0.x, -x64, true)

'Assembly.CodeBase' is obsolete: 'Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location.' (https://aka.ms/dotnet-warnings/SYSLIB0012)

Check warning on line 345 in tests/SixLabors.Fonts.Tests/TestFonts.cs

View workflow job for this annotation

GitHub Actions / Build (false, windows-latest, net8.0, 8.0.x, -x64, true)

'Assembly.CodeBase' is obsolete: 'Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location.' (https://aka.ms/dotnet-warnings/SYSLIB0012)

string[] paths = new[]
{
Expand Down
Loading