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
14 changes: 13 additions & 1 deletion src/SixLabors.Fonts/GlyphPositioningCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,8 @@ public void UpdatePosition(FontMetrics fontMetrics, int index)
bool isDirtyWH = data.Bounds.IsDirtyWH;
if (!isDirtyXY && !isDirtyWH)
{
// No change required but the glyph has been processed.
data.IsPositioned = true;
return;
}

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

if (isDirtyWH)
{
m.SetAdvanceWidth((ushort)data.Bounds.Width);
m.SetAdvanceHeight((ushort)data.Bounds.Height);
data.IsPositioned = true;
}
}
}
Expand Down Expand Up @@ -362,7 +366,15 @@ public void Advance(FontMetrics fontMetrics, int index, ushort glyphId, short dx
/// <param name="index">The zero-based index of the elements to position.</param>
/// <returns><see langword="true"/> if the element should be processed; otherwise, <see langword="false"/>.</returns>
public bool ShouldProcess(FontMetrics fontMetrics, int index)
=> this.glyphs[index].Metrics.FontMetrics == fontMetrics;
{
GlyphPositioningData data = this.glyphs[index];
if (data.Data.IsPositioned)
{
return false;
}

return data.Metrics.FontMetrics == fontMetrics;
}

[DebuggerDisplay("{DebuggerDisplay,nq}")]
private class GlyphPositioningData
Expand Down
13 changes: 13 additions & 0 deletions src/SixLabors.Fonts/GlyphShapingData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ public GlyphShapingData(GlyphShapingData data, bool clearFeatures = false)
this.CursiveAttachment = data.CursiveAttachment;
this.IsSubstituted = data.IsSubstituted;
this.IsDecomposed = data.IsDecomposed;
this.IsPositioned = data.IsPositioned;
this.IsKerned = data.IsKerned;

if (data.UniversalShapingEngineInfo != null)
{
this.UniversalShapingEngineInfo = new(
Expand Down Expand Up @@ -144,6 +147,16 @@ public GlyphShapingData(GlyphShapingData data, bool clearFeatures = false)
/// </summary>
public bool IsDecomposed { get; set; }

/// <summary>
/// Gets or sets a value indicating whether this glyph has been positioned.
/// </summary>
public bool IsPositioned { get; set; }

/// <summary>
/// Gets or sets a value indicating whether this glyph has been kerned.
/// </summary>
public bool IsKerned { get; set; }

/// <summary>
/// Gets or sets the universal shaping information.
/// </summary>
Expand Down
5 changes: 3 additions & 2 deletions src/SixLabors.Fonts/Tables/General/Kern/Format0SubTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ public static Format0SubTable Load(BigEndianBinaryReader reader, in KerningCover
// -------|---------------|--------------------------------------------------------
// uint16 | nPairs | This gives the number of kerning pairs in the table.
// 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.
// 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).
// 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).
// 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.
ushort pairCount = reader.ReadUInt16();
ushort searchRange = reader.ReadUInt16();
ushort entrySelector = reader.ReadUInt16();
ushort rangeShift = reader.ReadUInt16();

var pairs = new KerningPair[pairCount];
KerningPair[] pairs = new KerningPair[pairCount];
for (int i = 0; i < pairCount; i++)
{
pairs[i] = KerningPair.Read(reader);
Expand Down
2 changes: 1 addition & 1 deletion src/SixLabors.Fonts/Tables/General/Kern/KerningSubTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public KerningSubTable(KerningCoverage coverage)
// +--------+----------+----------------------------------------------------------+
ushort subVersion = reader.ReadUInt16();
ushort length = reader.ReadUInt16();
var coverage = KerningCoverage.Read(reader);
KerningCoverage coverage = KerningCoverage.Read(reader);
if (coverage.Format == 0)
{
return Format0SubTable.Load(reader, coverage);
Expand Down
20 changes: 14 additions & 6 deletions src/SixLabors.Fonts/Tables/General/Kern/KerningTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static KerningTable Load(FontReader fontReader)
if (!fontReader.TryGetReaderAtTablePosition(TableName, out BigEndianBinaryReader? binaryReader))
{
// this table is optional.
return new KerningTable(Array.Empty<KerningSubTable>());
return new KerningTable([]);
}

using (binaryReader)
Expand Down Expand Up @@ -52,7 +52,7 @@ public static KerningTable Load(BigEndianBinaryReader reader)
}
}

return new KerningTable(tables.ToArray());
return new KerningTable([.. tables]);
}

public void UpdatePositions(FontMetrics fontMetrics, GlyphPositioningCollection collection, int left, int right)
Expand All @@ -62,12 +62,20 @@ public void UpdatePositions(FontMetrics fontMetrics, GlyphPositioningCollection
return;
}

ushort current = collection[left].GlyphId;
ushort next = collection[right].GlyphId;
GlyphShapingData current = collection[left];
if (current.IsKerned)
{
// Already kerned via previous processing.
return;
}

ushort currentId = current.GlyphId;
ushort nextId = collection[right].GlyphId;

if (this.TryGetKerningOffset(current, next, out Vector2 result))
if (this.TryGetKerningOffset(currentId, nextId, out Vector2 result))
{
collection.Advance(fontMetrics, left, current, (short)result.X, (short)result.Y);
collection.Advance(fontMetrics, left, currentId, (short)result.X, (short)result.Y);
current.IsKerned = true;
}
}

Expand Down
Loading
Loading