Skip to content

Commit d11ff41

Browse files
Merge pull request #476 from SixLabors/js/fix-468
Allow invalid coverage tables.
2 parents 09ca137 + 7a3f6fd commit d11ff41

File tree

11 files changed

+148
-39
lines changed

11 files changed

+148
-39
lines changed

src/SixLabors.Fonts/Tables/AdvancedTypographic/CoverageTable.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

4+
using static SixLabors.Fonts.Tables.AdvancedTypographic.CoverageFormat2Table;
5+
46
namespace SixLabors.Fonts.Tables.AdvancedTypographic;
57

68
/// <summary>
@@ -23,13 +25,16 @@ public static CoverageTable Load(BigEndianBinaryReader reader, long offset)
2325
{
2426
1 => CoverageFormat1Table.Load(reader),
2527
2 => CoverageFormat2Table.Load(reader),
26-
_ => throw new InvalidFontFileException($"Invalid value for 'coverageFormat' {coverageFormat}. Should be '1' or '2'.")
28+
29+
// Harfbuzz (Coverage.hh) treats this as an empty table and does not throw.
30+
// SofiaSans Condensed can trigger this. See https://github.com/SixLabors/Fonts/issues/470
31+
_ => EmptyCoverageTable.Instance
2732
};
2833
}
2934

3035
public static CoverageTable[] LoadArray(BigEndianBinaryReader reader, long offset, ReadOnlySpan<ushort> coverageOffsets)
3136
{
32-
var tables = new CoverageTable[coverageOffsets.Length];
37+
CoverageTable[] tables = new CoverageTable[coverageOffsets.Length];
3338
for (int i = 0; i < tables.Length; i++)
3439
{
3540
tables[i] = Load(reader, offset + coverageOffsets[i]);
@@ -103,7 +108,7 @@ public static CoverageFormat2Table Load(BigEndianBinaryReader reader)
103108
// | RangeRecord | rangeRecords[rangeCount] | Array of glyph ranges — ordered by startGlyphID. |
104109
// +-------------+--------------------------+--------------------------------------------------+
105110
ushort rangeCount = reader.ReadUInt16();
106-
var records = new CoverageRangeRecord[rangeCount];
111+
CoverageRangeRecord[] records = new CoverageRangeRecord[rangeCount];
107112

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

125130
return new CoverageFormat2Table(records);
126131
}
132+
133+
internal sealed class EmptyCoverageTable : CoverageTable
134+
{
135+
private EmptyCoverageTable()
136+
{
137+
}
138+
139+
public static EmptyCoverageTable Instance { get; } = new();
140+
141+
public override int CoverageIndexOf(ushort glyphId) => -1;
142+
}
127143
}

src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/AnchorTable.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ namespace SixLabors.Fonts.Tables.AdvancedTypographic.GPos;
1111
[DebuggerDisplay("X: {XCoordinate}, Y: {YCoordinate}")]
1212
internal abstract class AnchorTable
1313
{
14-
private static readonly AnchorTable Empty = new EmptyAnchor();
15-
1614
/// <summary>
1715
/// Initializes a new instance of the <see cref="AnchorTable"/> class.
1816
/// </summary>
@@ -53,9 +51,9 @@ public static AnchorTable Load(BigEndianBinaryReader reader, long offset)
5351
2 => AnchorFormat2.Load(reader),
5452
3 => AnchorFormat3.Load(reader),
5553

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

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

188-
internal sealed class EmptyAnchor : AnchorTable
186+
internal sealed class EmptyAnchorTable : AnchorTable
189187
{
190-
public EmptyAnchor()
188+
private EmptyAnchorTable()
191189
: base(0, 0)
192190
{
193191
}
194192

193+
public static EmptyAnchorTable Instance { get; } = new();
194+
195195
public override AnchorXY GetAnchor(
196196
FontMetrics fontMetrics,
197197
GlyphShapingData data,
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
190 KB
Binary file not shown.
Binary file not shown.
448 KB
Binary file not shown.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using System.Text;
5+
6+
namespace SixLabors.Fonts.Tests.Issues;
7+
8+
public class Issues_468
9+
{
10+
[Fact]
11+
public void TestIssue_468()
12+
{
13+
StringBuilder stringBuilder = new();
14+
stringBuilder
15+
.AppendLine("Latin: The quick brown fox jumps over the lazy dog.")
16+
.AppendLine("Arabic (RTL & Shaping): نص حكيم له سر قاطع وذو شأن عظيم");
17+
18+
string text = stringBuilder.ToString();
19+
FontCollection fontCollection = new();
20+
21+
string consola = fontCollection.Add(TestFonts.Consola).Name;
22+
string arabic = fontCollection.Add(TestFonts.NotoSansArabicRegular).Name;
23+
24+
FontFamily mainFontFamily = fontCollection.Get(consola);
25+
Font mainFont = mainFontFamily.CreateFont(50, FontStyle.Regular);
26+
27+
TextOptions options = new(mainFont)
28+
{
29+
FallbackFontFamilies =
30+
[
31+
fontCollection.Get(arabic),
32+
],
33+
};
34+
35+
// There are too many metrics to validate here so we just ensure no exceptions are thrown
36+
// and the rendering looks correct by inspecting the snapshot.
37+
TextLayoutTestUtilities.TestLayout(
38+
text,
39+
options,
40+
includeGeometry: false);
41+
}
42+
}

tests/SixLabors.Fonts.Tests/Issues/Issues_469.cs

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,6 @@ public class Issues_469
1010
[Fact]
1111
public void Test_Issue_469()
1212
{
13-
const string arialFontName = "Arial";
14-
const string inconsolataFontName = "Inconsolata";
15-
const string nanumGothicCodingFontName = "NanumGothicCoding";
16-
const string cousineFontName = "Cousine";
17-
const string notoSansScThinFontName = "Noto Sans SC Thin";
18-
const string notoSansJpThinFontName = "Noto Sans JP Thin";
19-
const string notoNaskhArabicFontName = "Noto Naskh Arabic";
20-
const string sarabunFontName = "Sarabun";
21-
const string hindFontName = "Hind";
22-
2313
StringBuilder stringBuilder = new();
2414
stringBuilder.AppendLine("Latin: The quick brown fox jumps over the lazy dog.")
2515
.AppendLine("Cyrillic: Съешь же ещё этих мягких французских булок.")
@@ -33,33 +23,33 @@ public void Test_Issue_469()
3323
.AppendLine("Devanagari (Conjuncts): ऋषियों को सताने वाले राक्षसों का अंत हो गया");
3424

3525
string text = stringBuilder.ToString();
36-
FontCollection fontCollection = new();
37-
fontCollection.Add(TestFonts.Arial);
38-
fontCollection.Add(TestFonts.CousineRegular);
39-
fontCollection.Add(TestFonts.HindRegular);
40-
fontCollection.Add(TestFonts.NanumGothicCodingRegular);
41-
fontCollection.Add(TestFonts.InconsolataRegular);
42-
fontCollection.Add(TestFonts.NotoNaskhArabicRegular);
43-
fontCollection.Add(TestFonts.NotoSansHKVariableFontWght);
44-
fontCollection.Add(TestFonts.NotoSansJPRegular);
45-
fontCollection.Add(TestFonts.NotoSansSCRegular);
46-
fontCollection.Add(TestFonts.SarabunRegular);
4726

48-
FontFamily mainFontFamily = fontCollection.Get(arialFontName);
27+
FontCollection fontCollection = new();
28+
string arial = fontCollection.Add(TestFonts.Arial).Name;
29+
string cousine = fontCollection.Add(TestFonts.CousineRegular).Name;
30+
string hind = fontCollection.Add(TestFonts.HindRegular).Name;
31+
string nanumGothicCoding = fontCollection.Add(TestFonts.NanumGothicCodingRegular).Name;
32+
string inconsolata = fontCollection.Add(TestFonts.InconsolataRegular).Name;
33+
string notoNaskhArabic = fontCollection.Add(TestFonts.NotoNaskhArabicRegular).Name;
34+
string notoSansJpThin = fontCollection.Add(TestFonts.NotoSansJPRegular).Name;
35+
string notoSansScThin = fontCollection.Add(TestFonts.NotoSansSCRegular).Name;
36+
string sarabun = fontCollection.Add(TestFonts.SarabunRegular).Name;
37+
38+
FontFamily mainFontFamily = fontCollection.Get(arial);
4939
Font mainFont = mainFontFamily.CreateFont(30, FontStyle.Regular);
5040

5141
TextOptions options = new(mainFont)
5242
{
5343
FallbackFontFamilies =
5444
[
55-
fontCollection.Get(inconsolataFontName),
56-
fontCollection.Get(nanumGothicCodingFontName),
57-
fontCollection.Get(cousineFontName),
58-
fontCollection.Get(notoSansScThinFontName),
59-
fontCollection.Get(notoSansJpThinFontName),
60-
fontCollection.Get(notoNaskhArabicFontName),
61-
fontCollection.Get(sarabunFontName),
62-
fontCollection.Get(hindFontName),
45+
fontCollection.Get(inconsolata),
46+
fontCollection.Get(nanumGothicCoding),
47+
fontCollection.Get(cousine),
48+
fontCollection.Get(notoSansScThin),
49+
fontCollection.Get(notoSansJpThin),
50+
fontCollection.Get(notoNaskhArabic),
51+
fontCollection.Get(sarabun),
52+
fontCollection.Get(hind),
6353
],
6454
};
6555

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using System.Text;
5+
6+
namespace SixLabors.Fonts.Tests.Issues;
7+
8+
public class Issues_470
9+
{
10+
[Fact]
11+
public void Test_Issue_470()
12+
{
13+
StringBuilder stringBuilder = new();
14+
stringBuilder.AppendLine("Latin: The quick brown fox jumps over the lazy dog.")
15+
.AppendLine("Cyrillic: Съешь же ещё этих мягких французских булок.")
16+
.AppendLine("Greek: Ζαφείρι δέξου πάγκαλο, βαθῶν ψυχῆς τὸ σῆμα.")
17+
.AppendLine("Chinese: 敏捷的棕色狐狸跳过了懒狗")
18+
.AppendLine("Japanese: いろはにほへと ちりぬるを")
19+
.AppendLine("Korean: 다람쥐 헌 쳇바퀴에 타고파")
20+
.AppendLine("Arabic (RTL & Shaping): نص حكيم له سر قاطع وذو شأن عظيم")
21+
.AppendLine("Hebrew (RTL): דג סקרן שט בים מאוכזב ולפתע מצא חברה")
22+
.AppendLine("Thai (Complex): เป็นมนุษย์สุดประเสริฐเลิศคุณค่า")
23+
.AppendLine("Devanagari (Conjuncts): ऋषियों को सताने वाले राक्षसों का अंत हो गया");
24+
25+
string text = stringBuilder.ToString();
26+
27+
FontCollection fontCollection = new();
28+
string arial = fontCollection.Add(TestFonts.Arial).Name;
29+
string sofia = fontCollection.Add(TestFonts.SofiaSansCondensedLight).Name;
30+
31+
FontFamily mainFontFamily = fontCollection.Get(arial);
32+
Font mainFont = mainFontFamily.CreateFont(30, FontStyle.Regular);
33+
34+
TextOptions options = new(mainFont)
35+
{
36+
FallbackFontFamilies =
37+
[
38+
fontCollection.Get(sofia),
39+
],
40+
};
41+
42+
// There are too many metrics to validate here so we just ensure no exceptions are thrown
43+
// and the rendering looks correct by inspecting the snapshot.
44+
TextLayoutTestUtilities.TestLayout(
45+
text,
46+
options,
47+
includeGeometry: false);
48+
}
49+
}

0 commit comments

Comments
 (0)