Skip to content

Commit dd78e5d

Browse files
Merge pull request #489 from SixLabors/js/true-type-interpreter-fixes
Fix interpreter issues and prevent multple kerning operations
2 parents ffddf2d + 8cf0e58 commit dd78e5d

File tree

16 files changed

+359
-55
lines changed

16 files changed

+359
-55
lines changed

src/SixLabors.Fonts/GlyphPositioningCollection.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,8 @@ public void UpdatePosition(FontMetrics fontMetrics, int index)
300300
bool isDirtyWH = data.Bounds.IsDirtyWH;
301301
if (!isDirtyXY && !isDirtyWH)
302302
{
303+
// No change required but the glyph has been processed.
304+
data.IsPositioned = true;
303305
return;
304306
}
305307

@@ -311,12 +313,14 @@ public void UpdatePosition(FontMetrics fontMetrics, int index)
311313
if (isDirtyXY)
312314
{
313315
m.ApplyOffset((short)data.Bounds.X, (short)data.Bounds.Y);
316+
data.IsPositioned = true;
314317
}
315318

316319
if (isDirtyWH)
317320
{
318321
m.SetAdvanceWidth((ushort)data.Bounds.Width);
319322
m.SetAdvanceHeight((ushort)data.Bounds.Height);
323+
data.IsPositioned = true;
320324
}
321325
}
322326
}
@@ -362,7 +366,15 @@ public void Advance(FontMetrics fontMetrics, int index, ushort glyphId, short dx
362366
/// <param name="index">The zero-based index of the elements to position.</param>
363367
/// <returns><see langword="true"/> if the element should be processed; otherwise, <see langword="false"/>.</returns>
364368
public bool ShouldProcess(FontMetrics fontMetrics, int index)
365-
=> this.glyphs[index].Metrics.FontMetrics == fontMetrics;
369+
{
370+
GlyphPositioningData data = this.glyphs[index];
371+
if (data.Data.IsPositioned)
372+
{
373+
return false;
374+
}
375+
376+
return data.Metrics.FontMetrics == fontMetrics;
377+
}
366378

367379
[DebuggerDisplay("{DebuggerDisplay,nq}")]
368380
private class GlyphPositioningData

src/SixLabors.Fonts/GlyphShapingData.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ public GlyphShapingData(GlyphShapingData data, bool clearFeatures = false)
3939
this.CursiveAttachment = data.CursiveAttachment;
4040
this.IsSubstituted = data.IsSubstituted;
4141
this.IsDecomposed = data.IsDecomposed;
42+
this.IsPositioned = data.IsPositioned;
43+
this.IsKerned = data.IsKerned;
44+
4245
if (data.UniversalShapingEngineInfo != null)
4346
{
4447
this.UniversalShapingEngineInfo = new(
@@ -144,6 +147,16 @@ public GlyphShapingData(GlyphShapingData data, bool clearFeatures = false)
144147
/// </summary>
145148
public bool IsDecomposed { get; set; }
146149

150+
/// <summary>
151+
/// Gets or sets a value indicating whether this glyph has been positioned.
152+
/// </summary>
153+
public bool IsPositioned { get; set; }
154+
155+
/// <summary>
156+
/// Gets or sets a value indicating whether this glyph has been kerned.
157+
/// </summary>
158+
public bool IsKerned { get; set; }
159+
147160
/// <summary>
148161
/// Gets or sets the universal shaping information.
149162
/// </summary>

src/SixLabors.Fonts/Tables/General/Kern/Format0SubTable.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@ public static Format0SubTable Load(BigEndianBinaryReader reader, in KerningCover
1717
// -------|---------------|--------------------------------------------------------
1818
// uint16 | nPairs | This gives the number of kerning pairs in the table.
1919
// uint16 | searchRange | The largest power of two less than or equal to the value of nPairs, multiplied by the size in bytes of an entry in the table.
20-
// uint16 | entrySelector | This is calculated as log2 of the largest power of two less than or equal to the value of nPairs.This value indicates how many iterations of the search loop will have to be made. (For example, in a list of eight items, there would have to be three iterations of the loop).
20+
// uint16 | entrySelector | This is calculated as log2 of the largest power of two less than or equal to the value of nPairs.
21+
// | | This value indicates how many iterations of the search loop will have to be made. (For example, in a list of eight items, there would have to be three iterations of the loop).
2122
// uint16 | rangeShift | The value of nPairs minus the largest power of two less than or equal to nPairs, and then multiplied by the size in bytes of an entry in the table.
2223
ushort pairCount = reader.ReadUInt16();
2324
ushort searchRange = reader.ReadUInt16();
2425
ushort entrySelector = reader.ReadUInt16();
2526
ushort rangeShift = reader.ReadUInt16();
2627

27-
var pairs = new KerningPair[pairCount];
28+
KerningPair[] pairs = new KerningPair[pairCount];
2829
for (int i = 0; i < pairCount; i++)
2930
{
3031
pairs[i] = KerningPair.Read(reader);

src/SixLabors.Fonts/Tables/General/Kern/KerningSubTable.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public KerningSubTable(KerningCoverage coverage)
2727
// +--------+----------+----------------------------------------------------------+
2828
ushort subVersion = reader.ReadUInt16();
2929
ushort length = reader.ReadUInt16();
30-
var coverage = KerningCoverage.Read(reader);
30+
KerningCoverage coverage = KerningCoverage.Read(reader);
3131
if (coverage.Format == 0)
3232
{
3333
return Format0SubTable.Load(reader, coverage);

src/SixLabors.Fonts/Tables/General/Kern/KerningTable.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public static KerningTable Load(FontReader fontReader)
2020
if (!fontReader.TryGetReaderAtTablePosition(TableName, out BigEndianBinaryReader? binaryReader))
2121
{
2222
// this table is optional.
23-
return new KerningTable(Array.Empty<KerningSubTable>());
23+
return new KerningTable([]);
2424
}
2525

2626
using (binaryReader)
@@ -52,7 +52,7 @@ public static KerningTable Load(BigEndianBinaryReader reader)
5252
}
5353
}
5454

55-
return new KerningTable(tables.ToArray());
55+
return new KerningTable([.. tables]);
5656
}
5757

5858
public void UpdatePositions(FontMetrics fontMetrics, GlyphPositioningCollection collection, int left, int right)
@@ -62,12 +62,20 @@ public void UpdatePositions(FontMetrics fontMetrics, GlyphPositioningCollection
6262
return;
6363
}
6464

65-
ushort current = collection[left].GlyphId;
66-
ushort next = collection[right].GlyphId;
65+
GlyphShapingData current = collection[left];
66+
if (current.IsKerned)
67+
{
68+
// Already kerned via previous processing.
69+
return;
70+
}
71+
72+
ushort currentId = current.GlyphId;
73+
ushort nextId = collection[right].GlyphId;
6774

68-
if (this.TryGetKerningOffset(current, next, out Vector2 result))
75+
if (this.TryGetKerningOffset(currentId, nextId, out Vector2 result))
6976
{
70-
collection.Advance(fontMetrics, left, current, (short)result.X, (short)result.Y);
77+
collection.Advance(fontMetrics, left, currentId, (short)result.X, (short)result.Y);
78+
current.IsKerned = true;
7179
}
7280
}
7381

0 commit comments

Comments
 (0)