I am writing a TrueType reader, mostly as an exercise to improve my understanding of both TrueType/OpenType and C#, but I have gotten stuck reading the tables, specifically the 'cmap' table (the first one I'm trying).
I have been able to read the start of the file, finding the header that details the tables in the file, and then I have started reading the 'cmap' table, and this is where my problem is. I can read the encoding record headings, getting their platform ids, encoding ids and offsets, but when I attempt to move to the table itself, I arrive at the wrong place, getting results like: Format: 18244, Length: 17734, Language: 1822 which seems very clearly wrong.
I'm pretty sure that the problem is with myself having the wrong start position where I am applying the offset from, but from I can't manage to learn any more from my reading of the Microsoft OpenType documentation.
internal class EncodingTable
{
public ushort Format { get; }
public ushort Length { get; }
public ushort Language { get; }
public byte[] GlyphIdArray { get; }
public EncodingTable(ushort format, ushort length, ushort language, byte[] glyphIdArrays)
{
Format = format;
Length = length;
Language = language;
GlyphIdArray = glyphIdArrays;
}
}
internal class EncodingRecord
{
public ushort PlatformId { get; }
public ushort EncodingId { get; }
public uint Offset { get; }
public EncodingTable Table;
public EncodingRecord(ushort platformId, ushort encodingId, uint recordOffset, BinaryReader reader, long tableStart)
{
PlatformId = platformId;
EncodingId = encodingId;
Offset = recordOffset;
_reader = reader;
Console.WriteLine("Platform: {0}, Encoding: {1}, Offset: {2}", PlatformId, EncodingId, Offset);
long start = reader.BaseStream.Position;
reader.BaseStream.Position = tableStart;
reader.BaseStream.Seek(Offset, SeekOrigin.Begin);
ushort format = reader.ReadUInt16();
ushort length = reader.ReadUInt16();
ushort language = reader.ReadUInt16();
Console.WriteLine("Format: {0}, Length: {1}, Language: {2}", format, length, language);
reader.BaseStream.Position = start;
}
}
internal class CmapTable : FontDataTable
{
public ushort Version { get; }
public ushort NumTables { get; }
public List<EncodingRecord> EncodingRecords { get; } = new List<EncodingRecord>();
public CmapTable(string tag, uint checksum, uint offset, uint length, BinaryReader reader, string name)
: base(tag, checksum, offset, length, reader, name)
{
long start = reader.BaseStream.Position;
_reader.BaseStream.Seek(_offset, SeekOrigin.Begin);
Version = reader.ReadUInt16();
NumTables = reader.ReadUInt16();
long pos = reader.BaseStream.Position;
long tableStart = pos + (NumTables * 8);
for (int i = 0; i < NumTables; i++)
{
EncodingRecords.Add(new EncodingRecord(reader.ReadUInt16(), reader.ReadUInt16(), reader.ReadUInt32(), reader, tableStart));
}
reader.BaseStream.Position = start;
}
}
'FontDataTable' is simply a base class for all the tables I will be reading, holding the common fields (such as _reader), and allowing an easy way to store them all in a collection. My general coding probably isn't great, and I'm certain it isn't the most efficient, but hopefully that'll improve as I learn more.