{"record":{"id":"0a660adc24a28dcd","repo":"d2lang/d2","slug":"invalid-sfnt-table-directory","errorCode":null,"errorMessage":"invalid sfnt table directory","messagePattern":"invalid sfnt table directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/textmeasure/fontface.go","lineNumber":208,"sourceCode":"\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]))\n\t\tlength := int(binary.BigEndian.Uint32(record[12:16]))\n\t\tif offset < 0 || length < 0 || offset > len(src)-length {\n\t\t\treturn nil, fmt.Errorf(\"invalid %s table bounds\", tag)\n\t\t}\n\t\treturn src[offset : offset+length], nil\n\t}\n\treturn nil, nil\n}\n","sourceCodeStart":190,"sourceCodeEnd":224,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/lib/textmeasure/fontface.go#L190-L224","documentation":"The sfnt table directory declares a number of table records (bytes 4–6, 16 bytes each); the parser verifies this count fits within the remaining bytes before iterating. A count larger than the available space means the header is corrupt, so parsing is aborted to avoid out-of-bounds reads.","triggerScenarios":"parseFont on a font whose numTables field exceeds the number of 16-byte records that actually fit in the data — i.e. the file is truncated relative to its declared table count.","commonSituations":"Interrupted downloads; fonts partially extracted from installers or documents; fuzzed/malformed font files.","solutions":["Replace the font with an intact copy and verify checksum.","Rebuild the font with fonttools (open and re-save repairs directory consistency).","If the file is a subset intentionally trimmed, re-subset properly so the table directory matches the remaining data."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"func tableDirFits(data []byte) bool {\n    if len(data) < 12 { return false }\n    base := 0\n    if string(data[:4]) == \"ttcf\" {\n        if len(data) < 16 { return false }\n        base = int(binary.BigEndian.Uint32(data[12:16]))\n        if base < 0 || base+12 > len(data) { return false }\n    }\n    n := int(binary.BigEndian.Uint16(data[base+4 : base+6]))\n    return n <= (len(data)-base-12)/16\n}","typeGuard":null,"tryCatchPattern":"if err := loadAndParseFont(path); err != nil {\n    log.Printf(\"font %s has invalid table directory: %v\", path, err)\n    return defaultFace\n}","preventionTips":["Verify font integrity (size + checksum) at download and build time","Detect truncated downloads before saving font assets","Rebuild or re-download fonts rather than shipping partially extracted ones","Parse all bundled fonts in unit tests so corruption fails the build"],"tags":["font","sfnt","corrupt-file"],"backgroundTag":"invalid-font-file","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}