{"record":{"id":"94a9d37c6649039f","repo":"d2lang/d2","slug":"invalid-kern-table-length","errorCode":null,"errorMessage":"invalid kern table length","messagePattern":"invalid kern table length","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/textmeasure/fontface.go","lineNumber":175,"sourceCode":"\t}\n\tif len(table) < 18 {\n\t\treturn nil, fmt.Errorf(\"invalid kern table: too short\")\n\t}\n\tif binary.BigEndian.Uint16(table[0:2]) != 0 {\n\t\treturn nil, fmt.Errorf(\"unsupported kern table version\")\n\t}\n\tif binary.BigEndian.Uint16(table[2:4]) == 0 {\n\t\treturn nil, fmt.Errorf(\"invalid kern table: no subtables\")\n\t}\n\n\tlength := int(binary.BigEndian.Uint16(table[6:8]))\n\tcoverage := binary.BigEndian.Uint16(table[8:10])\n\tif coverage != 0x0001 {\n\t\treturn nil, fmt.Errorf(\"unsupported kern table coverage 0x%04x\", coverage)\n\t}\n\tn := int(binary.BigEndian.Uint16(table[10:12]))\n\tif length < 14 || 6*n != length-14 || 4+length > len(table) {\n\t\treturn nil, fmt.Errorf(\"invalid kern table length\")\n\t}\n\n\tpairs := make([]kernPair, n)\n\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","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/lib/textmeasure/fontface.go#L157-L193","documentation":"Consistency check on the kern subtable: the declared length must be at least 14 bytes, exactly fit nPairs × 6-byte pair records, and fit within the extracted table bytes. Any mismatch means the table is corrupt or mis-declared, so it is rejected before out-of-bounds reads.","triggerScenarios":"parseFont reads a font where the kern subtable's length field, nPairs field, and actual table byte length disagree — a hallmark of truncation or corruption.","commonSituations":"Corrupted downloads; fonts extracted from PDFs with wrong table lengths; maliciously or accidentally malformed font files.","solutions":["Replace the font file with a verified-good copy and compare checksums.","Run fonttools to inspect/rebuild the kern table (f['kern']) and re-save the font.","Strip the kern table so the library skips kerning rather than failing."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// length := int(binary.BigEndian.Uint16(kern[6:8]))\n// n := int(binary.BigEndian.Uint16(kern[10:12]))\n// consistent := length >= 14 && 6*n == length-14 && 4+length <= len(kern)","typeGuard":null,"tryCatchPattern":"pairs, err := parseLegacyKern(fontData)\nif err != nil {\n    return fmt.Errorf(\"font kern table corrupt, re-provision font: %w\", err)\n}","preventionTips":["Checksum-verify fonts at build/deploy time","Avoid extracting fonts from PDFs or installers; use canonical font distributions","Re-subset fonts with fonttools so declared lengths match actual data","Log the font filename in parse errors so corrupt assets are easy to locate"],"tags":["font","kern","corrupt-file"],"backgroundTag":"invalid-font-kern-table","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}