-
Notifications
You must be signed in to change notification settings - Fork 312
Add support for True Type Collection file parsing in SystemFontFinder #1247
Copy link
Copy link
Open
Labels
Description
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();
}Reactions are currently unavailable