siyuan-note/siyuan · error

only TTF and OTF font files are supported

Error message

only TTF and OTF font files are supported

What it means

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.

Solutions

  1. Convert the font to TTF/OTF first (e.g. fonttools: ttx/woff2 decompress, or an online woff2->ttf converter)
  2. Extract a single .ttf from a .ttc collection before installing
  3. Verify the download is actually a font (check the magic bytes: 00 01 00 00, 'true', or 'OTTO') and not an HTML/error page
  4. Use a plain (non-web) TTF/OTF build of the font from the foundry

Example fix

// before
curl -o font.ttf https://example.com/font.woff2
util.InstallCustomFont("font.ttf") // rejected
// after
woff2_decompress font.woff2            # produces font.ttf
util.InstallCustomFont("font.ttf")    // OK: TTF/OTF only
Defensive patterns

Strategy: validation

Validate before calling

func sniffFont(b []byte) string {
    switch string(b[:4]) {
    case "\x00\x01\x00\x00", "true": return ".ttf"
    case "OTTO": return ".otf"
    default: return ""
    }
}

Try / catch

font, _, err := util.InstallCustomFont(tempPath)
if err != nil && strings.Contains(err.Error(), "only TTF and OTF") {
    // ask user to convert (woff2/ttc -> ttf/otf) and retry
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/67eaa96adcc9a68b. Report an issue: GitHub.

Appendix: source

Thrown at kernel/util/custom_font.go:312

		if _, active := customFontTemps[tempPath]; !active {
			_ = os.Remove(tempPath)
		}
	}
}

func detectCustomFontExtension(reader io.Reader) (string, error) {
	header := make([]byte, 4)
	if _, err := io.ReadFull(reader, header); err != nil {
		return "", errors.New("font file is invalid")
	}

	switch string(header) {
	case "\x00\x01\x00\x00", "true":
		return ".ttf", nil
	case "OTTO":
		return ".otf", nil
	default:
		return "", errors.New("only TTF and OTF font files are supported")
	}
}

func customFontHash(reader io.Reader) (string, error) {
	hash := sha256.New()
	if _, err := io.Copy(hash, reader); err != nil {
		return "", err
	}
	return hex.EncodeToString(hash.Sum(nil)), nil
}

func parseCustomFontFile(fontFile *os.File) (ret *Font, err error) {
	defer func() {
		if recovered := recover(); recovered != nil {
			ret = nil
			err = fmt.Errorf("parse font failed: %v", recovered)
		}
	}()

View on GitHub (pinned to 9f775e8a12)