Skip to content

Add support for True Type Collection file parsing in SystemFontFinder #1247

@BobLd

Description

@BobLd

Adding support for True Type Collection file parsing in SystemFontFinder could help support more font (e.g. the MS Gothic font is contained in a .ttc file, not .ttf. As a result it is currently not loaded).

Adding support should be straightforward. Basic implementation below (written with help of AI assistant). Initial tests are promising.

    public static IReadOnlyList<TrueTypeFont> ParseTtcFonts(byte[] input, int maxFonts = int.MaxValue)
    {
        var bytes = input;
        if (bytes.Length < 12 || bytes[0] != 0x74 || bytes[1] != 0x74 || bytes[2] != 0x63 || bytes[3] != 0x66)
        {
            // Not TTC
            throw new InvalidOperationException("Input data is not a valid TrueType Collection (TTC) file.");
        }

        // TTC detected: ttcf at offset 0
        uint version = BinaryPrimitives.ReadUInt32BigEndian(bytes.AsSpan(4, 4));
        if (version != 0x00010000 && version != 0x00020000)
        {
            throw new InvalidOperationException($"Unsupported TTC version: {version:X8}");
        }

        uint numFonts = BinaryPrimitives.ReadUInt32BigEndian(bytes.AsSpan(8, 4));
        if (numFonts == 0)
        {
            throw new InvalidOperationException("TTC contains no fonts.");
        }

        var offsets = new List<uint>((int)Math.Min(numFonts, maxFonts));
        for (int i = 0; i < numFonts && offsets.Count < maxFonts; i++)
        {
            uint offset = BinaryPrimitives.ReadUInt32BigEndian(bytes.AsSpan(12 + i * 4, 4));
            if (offset > 0)
            {
                offsets.Add(offset);
            }
        }

        var subInput = new TrueTypeDataBytes(bytes);

        var fonts = new List<TrueTypeFont>(offsets.Count);
        foreach (uint offset in offsets)
        {
            if (offset >= bytes.Length)
            {
                continue; // Invalid offset, skip
            }

            try
            {
                subInput.Seek(offset); // TTF starts at offset
                var font = TrueTypeFontParser.Parse(subInput);
                fonts.Add(font);
            }
            catch (Exception ex)
            {
                // Log or handle parse error for this sub-font
                Console.WriteLine($"Failed to parse TTC font at offset {offset}: {ex.Message}");
            }
        }

        return fonts.AsReadOnly();
    }

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions