{"record":{"id":"67eaa96adcc9a68b","repo":"siyuan-note/siyuan","slug":"only-ttf-and-otf-font-files-are-supported","errorCode":null,"errorMessage":"only TTF and OTF font files are supported","messagePattern":"only TTF and OTF font files are supported","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/custom_font.go","lineNumber":312,"sourceCode":"\t\tif _, active := customFontTemps[tempPath]; !active {\n\t\t\t_ = os.Remove(tempPath)\n\t\t}\n\t}\n}\n\nfunc detectCustomFontExtension(reader io.Reader) (string, error) {\n\theader := make([]byte, 4)\n\tif _, err := io.ReadFull(reader, header); err != nil {\n\t\treturn \"\", errors.New(\"font file is invalid\")\n\t}\n\n\tswitch string(header) {\n\tcase \"\\x00\\x01\\x00\\x00\", \"true\":\n\t\treturn \".ttf\", nil\n\tcase \"OTTO\":\n\t\treturn \".otf\", nil\n\tdefault:\n\t\treturn \"\", errors.New(\"only TTF and OTF font files are supported\")\n\t}\n}\n\nfunc customFontHash(reader io.Reader) (string, error) {\n\thash := sha256.New()\n\tif _, err := io.Copy(hash, reader); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn hex.EncodeToString(hash.Sum(nil)), nil\n}\n\nfunc parseCustomFontFile(fontFile *os.File) (ret *Font, err error) {\n\tdefer func() {\n\t\tif recovered := recover(); recovered != nil {\n\t\t\tret = nil\n\t\t\terr = fmt.Errorf(\"parse font failed: %v\", recovered)\n\t\t}\n\t}()","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/util/custom_font.go#L294-L330","documentation":"The font sniffer only recognizes TrueType (magic \\x00\\x01\\x00\\x00 or 'true') and OpenType/CFF ('OTTO') headers. This error is thrown when the first 4 bytes match neither, meaning the file is not a supported TTF/OTF font — commonly a WOFF/WOFF2, a .ttc collection, or an entirely different file type renamed to .ttf.","triggerScenarios":"Calling InstallCustomFont with a WOFF/WOFF2 webfont, a TrueType Collection (.ttc), a renamed ZIP/archive, or any non-font file; also triggered by loadCustomFontsLocked on files in the custom fonts dir that fail sniffing (silently skipped there).","commonSituations":"Browsers/services that only offered WOFF2 downloads; users renaming .woff2 or .ttc to .ttf hoping it works; downloading an HTML error page saved as a font; picking variable-font TTC bundles.","solutions":["Convert the font to TTF/OTF first (e.g. fonttools: ttx/woff2 decompress, or an online woff2->ttf converter)","Extract a single .ttf from a .ttc collection before installing","Verify the download is actually a font (check the magic bytes: 00 01 00 00, 'true', or 'OTTO') and not an HTML/error page","Use a plain (non-web) TTF/OTF build of the font from the foundry"],"exampleFix":"// before\ncurl -o font.ttf https://example.com/font.woff2\nutil.InstallCustomFont(\"font.ttf\") // rejected\n// after\nwoff2_decompress font.woff2            # produces font.ttf\nutil.InstallCustomFont(\"font.ttf\")    // OK: TTF/OTF only","handlingStrategy":"validation","validationCode":"func sniffFont(b []byte) string {\n    switch string(b[:4]) {\n    case \"\\x00\\x01\\x00\\x00\", \"true\": return \".ttf\"\n    case \"OTTO\": return \".otf\"\n    default: return \"\"\n    }\n}","typeGuard":null,"tryCatchPattern":"font, _, err := util.InstallCustomFont(tempPath)\nif err != nil && strings.Contains(err.Error(), \"only TTF and OTF\") {\n    // ask user to convert (woff2/ttc -> ttf/otf) and retry\n}","preventionTips":["Accept only .ttf/.otf uploads client-side and sniff magic bytes before staging","Convert WOFF/WOFF2 to TTF before installing","Never rely on file extension; check the header"],"tags":["validation","font-format","unsupported-type"],"backgroundTag":"unsupported-enum-value","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}