{"record":{"id":"0c31d9c6bde6e555","repo":"d2lang/d2","slug":"invalid-font-collection","errorCode":null,"errorMessage":"invalid font collection","messagePattern":"invalid font collection","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/textmeasure/fontface.go","lineNumber":197,"sourceCode":"\tfor i := range pairs {\n\t\toffset := 18 + 6*i\n\t\tpairs[i] = kernPair{\n\t\t\tkey:   binary.BigEndian.Uint32(table[offset : offset+4]),\n\t\t\tvalue: int16(binary.BigEndian.Uint16(table[offset+4 : offset+6])),\n\t\t}\n\t}\n\treturn pairs, nil\n}\n\nfunc sfntTable(src []byte, tag string) ([]byte, error) {\n\tif len(src) < 12 {\n\t\treturn nil, fmt.Errorf(\"invalid sfnt: too short\")\n\t}\n\n\tbase := 0\n\tif string(src[:4]) == \"ttcf\" {\n\t\tif len(src) < 16 || binary.BigEndian.Uint32(src[8:12]) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"invalid font collection\")\n\t\t}\n\t\tbase = int(binary.BigEndian.Uint32(src[12:16]))\n\t\tif base < 0 || base+12 > len(src) {\n\t\t\treturn nil, fmt.Errorf(\"invalid font collection offset\")\n\t\t}\n\t}\n\n\tn := int(binary.BigEndian.Uint16(src[base+4 : base+6]))\n\trecords := base + 12\n\tif n > (len(src)-records)/16 {\n\t\treturn nil, fmt.Errorf(\"invalid sfnt table directory\")\n\t}\n\tfor i := 0; i < n; i++ {\n\t\trecord := src[records+16*i : records+16*(i+1)]\n\t\tif string(record[:4]) != tag {\n\t\t\tcontinue\n\t\t}\n\t\toffset := int(binary.BigEndian.Uint32(record[8:12]))","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/lib/textmeasure/fontface.go#L179-L215","documentation":"For TrueType collections (ttcf), bytes 8–12 hold the offset of the first font in the collection; it must be non-zero and the file must be at least 16 bytes. A zero offset or too-short collection header is rejected.","triggerScenarios":"parseFont is given a .ttc font collection whose header declares version 'ttcf' but has a zero first-font offset or a truncated (<16 byte) header.","commonSituations":"Corrupted or hand-merged .ttc files; fonts harvested from system font caches in partial form.","solutions":["Re-acquire the .ttc file from a trusted source.","Split the collection into single .ttf fonts (e.g. with fonttools: fonttools ttLib.ttCollection) and use individual fonts.","Validate the ttcf header (magic, version, non-zero offsets) before passing the data to the library."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"func validTTC(data []byte) bool {\n    if len(data) < 16 || string(data[:4]) != \"ttcf\" { return len(data) >= 12 }\n    return binary.BigEndian.Uint32(data[8:12]) != 0\n}","typeGuard":null,"tryCatchPattern":"if err := loadAndParseFont(path); err != nil {\n    log.Printf(\"font collection unusable (%v); falling back to single font\", err)\n    return loadSingleFont(fallbackPath)\n}","preventionTips":["Prefer single .ttf files over .ttc collections unless you control the collection build","Validate ttcf headers (magic, version, non-zero offsets) before loading","Regenerate .ttc files with fonttools ttCollection instead of merging binaries manually","Verify checksums of font collections after transfer"],"tags":["font","ttc","corrupt-file"],"backgroundTag":"invalid-font-file","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}