diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/Kerning/KernPair.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/Kerning/KernPair.cs
new file mode 100644
index 00000000..dd778b65
--- /dev/null
+++ b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/Kerning/KernPair.cs
@@ -0,0 +1,33 @@
+namespace UglyToad.PdfPig.Fonts.TrueType.Tables.Kerning
+{
+ internal struct KernPair
+ {
+ ///
+ /// The index of the left-hand glyph.
+ ///
+ public int LeftGlyphIndex { get; }
+
+ ///
+ /// The index of the right-hand glyph.
+ ///
+ public int RightGlyphIndex { get; }
+
+ ///
+ /// The kerning value. For values greater than zero the characters are moved apart.
+ /// For values less than zero the characters are moved closer together.
+ ///
+ public short Value { get; }
+
+ public KernPair(int leftGlyphIndex, int rightGlyphIndex, short value)
+ {
+ LeftGlyphIndex = leftGlyphIndex;
+ RightGlyphIndex = rightGlyphIndex;
+ Value = value;
+ }
+
+ public override string ToString()
+ {
+ return $"Left: {LeftGlyphIndex}, Right: {RightGlyphIndex}, Value {Value}.";
+ }
+ }
+}
diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/Kerning/KerningSubTable.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/Kerning/KerningSubTable.cs
new file mode 100644
index 00000000..bdb80198
--- /dev/null
+++ b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/Kerning/KerningSubTable.cs
@@ -0,0 +1,20 @@
+namespace UglyToad.PdfPig.Fonts.TrueType.Tables.Kerning
+{
+ using System.Collections.Generic;
+
+ internal class KerningSubTable
+ {
+ public int Version { get; }
+
+ public KernCoverage Coverage { get; }
+
+ public IReadOnlyList Pairs { get; }
+
+ public KerningSubTable(int version, KernCoverage coverage, IReadOnlyList pairs)
+ {
+ Version = version;
+ Coverage = coverage;
+ Pairs = pairs;
+ }
+ }
+}
diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/KerningTable.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/KerningTable.cs
index be76d077..ed5773a1 100644
--- a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/KerningTable.cs
+++ b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/KerningTable.cs
@@ -1,9 +1,27 @@
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
{
+ using System.Collections.Generic;
using Kerning;
internal class KerningTable
{
+ public IReadOnlyList KerningTables { get; }
+
+ public KerningTable(IReadOnlyList kerningTables)
+ {
+ var notNull = new List();
+
+ foreach (var kerningTable in kerningTables)
+ {
+ if (kerningTable != null)
+ {
+ notNull.Add(kerningTable);
+ }
+ }
+
+ KerningTables = notNull;
+ }
+
public static KerningTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable headerTable)
{
data.Seek(headerTable.Offset);
@@ -12,17 +30,111 @@
var numberOfSubtables = data.ReadUnsignedShort();
+ var subTables = new KerningSubTable[numberOfSubtables];
+
for (var i = 0; i < numberOfSubtables; i++)
{
+ var currentOffset = data.Position;
+
var subtableVersion = data.ReadUnsignedShort();
var subtableLength = data.ReadUnsignedShort();
var coverage = data.ReadUnsignedShort();
var kernCoverage = (KernCoverage) coverage;
var format = ((coverage & 255) >> 8);
+
+ switch (format)
+ {
+ case 0:
+ subTables[i] = ReadFormat0Table(subtableVersion, data, kernCoverage);
+ break;
+ case 2:
+ subTables[i] = ReadFormat2Table(subtableVersion, data, kernCoverage, currentOffset);
+ break;
+ }
}
- return new KerningTable();
+ return new KerningTable(subTables);
+ }
+
+ private static KerningSubTable ReadFormat0Table(int version, TrueTypeDataBytes data, KernCoverage coverage)
+ {
+ var numberOfPairs = data.ReadUnsignedShort();
+ // ReSharper disable once UnusedVariable
+ var searchRange = data.ReadUnsignedShort();
+ // ReSharper disable once UnusedVariable
+ var entrySelector = data.ReadUnsignedShort();
+ // ReSharper disable once UnusedVariable
+ var rangeShift = data.ReadUnsignedShort();
+
+ var pairs = new KernPair[numberOfPairs];
+
+ for (int i = 0; i < numberOfPairs; i++)
+ {
+ var leftGlyphIndex = data.ReadUnsignedShort();
+ var rightGlyphIndex = data.ReadUnsignedShort();
+
+ var value = data.ReadSignedShort();
+
+ pairs[i] = new KernPair(leftGlyphIndex, rightGlyphIndex, value);
+ }
+
+ return new KerningSubTable(version, coverage, pairs);
+ }
+
+ private static KerningSubTable ReadFormat2Table(int version, TrueTypeDataBytes data, KernCoverage coverage, long tableStartOffset)
+ {
+ // TODO: Implement and test this;
+ return null;
+
+ var rowWidth = data.ReadUnsignedShort();
+
+ var leftClassTableOffset = data.ReadUnsignedShort();
+ var rightClassTableOffset = data.ReadUnsignedShort();
+
+ var kerningArrayOffset = data.ReadUnsignedShort();
+
+ data.Seek(tableStartOffset + leftClassTableOffset);
+
+ var leftTableFirstGlyph = data.ReadUnsignedShort();
+ var numberOfLeftGlyphs = data.ReadUnsignedShort();
+
+ var leftGlyphClassValues = new int[numberOfLeftGlyphs];
+
+ for (var i = 0; i < numberOfLeftGlyphs; i++)
+ {
+ leftGlyphClassValues[i] = data.ReadUnsignedShort();
+ }
+
+ data.Seek(tableStartOffset + rightClassTableOffset);
+
+ var rightTableFirstGlyph = data.ReadUnsignedShort();
+ var numberOfRightGlyphs = data.ReadUnsignedShort();
+
+ var rightGlyphClassValues = new int[numberOfRightGlyphs];
+
+ for (var i = 0; i < numberOfRightGlyphs; i++)
+ {
+ rightGlyphClassValues[i] = data.ReadUnsignedShort();
+ }
+
+ data.Seek(tableStartOffset + kerningArrayOffset);
+
+ var pairs = new List(numberOfRightGlyphs * numberOfLeftGlyphs);
+
+ // Data is a [left glyph count, right glyph count] array:
+ for (int i = 0; i < numberOfLeftGlyphs; i++)
+ {
+ var leftClassValue = leftGlyphClassValues[i];
+ for (int j = 0; j < numberOfRightGlyphs; j++)
+ {
+ var rightClassValue = rightGlyphClassValues[j];
+
+ pairs.Add(new KernPair(leftClassValue, rightClassValue, data.ReadSignedShort()));
+ }
+ }
+
+ return new KerningSubTable(version, coverage, pairs);
}
}
}