Problem locating 'cmap' subtables in TrueType file (C#)

51 Views Asked by At

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.

0

There are 0 best solutions below