Files
suaveplan/packages/media/fonts/font-compress/src/packer/binary-font-packer/binary-font-packer.md
2026-05-27 23:47:53 +01:00

3.7 KiB
Raw Blame History

BinaryFontPacker

(no submodules)

Purpose

BinaryFontPacker converts a TypefaceFont (the typeface.js JSON structure) into a compact binary BTF (Binary TypeFace) buffer. It builds a shared integer dictionary from all unique coordinate values in the font and encodes every number as a 16-bit dictionary index rather than a string, dramatically reducing the payload compared to raw JSON.

The BTF format is designed for Three.js and game-engine font pipelines where fonts must be shipped as efficient binary assets rather than verbose JSON strings.

Features

  • Dictionary-based compression: all unique integer values stored once in an Int16Array; all subsequent references are 16-bit indexes
  • Full font round-trip: header strings, reversed flag, 9 scalar metrics, bounding box, per-glyph ha/x_min/x_max, and full outline data
  • Supports all typeface.js command types: m, l, b (cubic), q (quadratic), z
  • Little-endian byte layout throughout for cross-platform compatibility
  • Wraps internal errors in FontCompressError with stable code PACK_FAILED

Usage: Basic Example

import { createBinaryFontPacker } from "@suaveplan/font-compress/packer/binary-font-packer";
import type { TypefaceFont } from "@suaveplan/font-convert";

const font: TypefaceFont = { /* ... */ };
const packer = createBinaryFontPacker();
const buffer: Uint8Array = packer.pack(font);

Usage: Advanced Example

import { BinaryFontPacker } from "@suaveplan/font-compress/packer/binary-font-packer";
import { FontCompressError } from "@suaveplan/font-compress";

const packer = new BinaryFontPacker();

try {
    const packed = packer.pack(font);
    // Write to disk, send over the wire, or store in IndexedDB
    await Bun.write("font.btf", packed);
} catch (err) {
    if (err instanceof FontCompressError) {
        console.error("Pack failed:", err.code, err.message);
    }
}

API Reference

BinaryFontPacker

pack(font: TypefaceFont): Uint8Array

Packs a TypefaceFont into a BTF binary buffer.

Parameters:

  • font — the typeface.js font object, as produced by @suaveplan/font-convert

Returns: Uint8Array containing the binary BTF data.

Throws: FontCompressError with code PACK_FAILED if packing fails for any reason.

createBinaryFontPacker(): BinaryFontPacker

Factory function that creates a new BinaryFontPacker instance.

Implementation Notes

BTF Binary Layout

[familyName: encodeString]
[cssFontWeight: encodeString]
[cssFontStyle: encodeString]
[reversed: uint8 (0 or 1)]
[dictLen: uint32 LE]
[dict: Int16Array × dictLen]
[metricsIdxs: Uint16Array × 9]   ← ascender,descender,ulPos,ulThk,res,yMin,xMin,yMax,xMax
[glyphCount: uint32 LE]
per-glyph:
  [charCode: uint32 LE]
  [indexCount: uint32 LE]
  [indexes: Uint16Array × indexCount]  ← ha,x_min,x_max, then (cmd_byte+coord_indexes)...

encodeString format: [uint32 length LE][UTF-8 bytes].

Command bytes map to: 0=m, 1=l, 2=b, 3=q, 4=z. Coordinate counts per command: m=2, l=2, b=6, q=4, z=0.

Dictionary Construction

All unique coordinate values across the entire font (metrics, bounding box, all glyph coordinates) are collected into a sorted Int16Array. This single pass over the font data is O(n) in glyph count and produces optimal deduplication. Because typeface.js coordinates are integers in a bounded range (typically ±2048), the dictionary fits comfortably in an Int16Array.

Alignment

All multi-byte typed arrays are written via Uint8Array byte copies, not direct TypedArray buffer views, so there are no alignment requirements on the output buffer. Readers (BinaryFontUnpacker) use DataView to safely read values at arbitrary offsets.