# @suaveplan/font-decompress - BTF (Binary TypeFace) binary unpacking. Reverses @suaveplan/font-compress's BinaryFontPacker output back into typeface.js JSON, ready for Three.js / R3F / game-font rendering pipelines. Universal. BTF (Binary TypeFace) binary unpacking. Reverses `@suaveplan/font-compress`'s `BinaryFontPacker` output into a typeface.js JSON shape ready for Three.js / R3F / game-font rendering. Universal, store-agnostic, free of `node:` / `bun:` imports. ## Documentation tree - [error-codes](./src/error-codes/error-codes.md) — The `@suaveplan/font-decompress` package centralises every failure mode it can produce behind a single registry built w… - [error-codes](./src/error-codes/error-codes.md) — The `@suaveplan/font-decompress` package centralises every failure mode it can produce behind a single registry built w… - [binary-font-unpacker](./src/unpacker/binary-font-unpacker/binary-font-unpacker.md) — `BinaryFontUnpacker` reverses the BTF (Binary TypeFace) wire format produced by `BinaryFontPacker` from `@suaveplan/fon… - [binary-font-unpacker](./src/unpacker/binary-font-unpacker/binary-font-unpacker.md) — `BinaryFontUnpacker` reverses the BTF (Binary TypeFace) wire format produced by `BinaryFontPacker` from `@suaveplan/fon… - [utils](./src/utils/decode-string.md) — `decodeString` reads a length-prefixed UTF-8 string from a `Uint8Array` at a given offset. The BTF wire format encodes… - [decode-string](./src/utils/decode-string.md) — `decodeString` reads a length-prefixed UTF-8 string from a `Uint8Array` at a given offset. The BTF wire format encodes… ## Purpose Compressed BTF buffers ship from build-time tooling to the renderer as compact `Uint8Array` blobs — they are far smaller than the underlying typeface.js JSON for non-trivial fonts. The renderer needs the JSON back to hand to Three.js's `FontLoader.parse(...)`. This package owns that reverse step; the sibling `@suaveplan/font-compress` owns the forward step. The split exists because pack and unpack are independent concerns: build tooling never unpacks, runtime renderers never pack. Shipping a unified package forces consumers to drag the other half. Splitting lets each side install only what it actually executes. ## Features - `BinaryFontUnpacker` class + `createBinaryFontUnpacker()` factory - Strict boundary validation on every read — raises `FontDecompressError` with `FONT_DECOMPRESS.INVALID_DATA` - Underlying primitive exceptions (DataView, TextDecoder) wrapped as `FontDecompressError` with `FONT_DECOMPRESS.UNPACK_FAILED` and `cause` preserved - All five BTF glyph commands (`m`/`l`/`b`/`q`/`z`) round-trip byte-for-byte - Unicode glyph keys (multi-byte UTF-8) round-trip correctly - Negative coordinates round-trip correctly - 100% statement / branch / function / line Istanbul coverage - 5 tree-shakeable subpath exports ## Usage: Basic ```ts import { createBinaryFontUnpacker } from "@suaveplan/font-decompress"; const unpacker = createBinaryFontUnpacker(); const font = unpacker.unpack(btfBuffer); console.log(font.familyName); // "Inter Variable" console.log(font.glyphs["A"]?.ha); // glyph advance width ``` ## Usage: Advanced ```ts import { BinaryFontUnpacker, FONT_DECOMPRESS_CODES, FontDecompressError, } from "@suaveplan/font-decompress"; // Reuse a single instance — stateless and safe. const unpacker = new BinaryFontUnpacker(); async function loadCdnFont(url: string) { const buf = new Uint8Array( await (await fetch(url)).arrayBuffer(), ); try { return unpacker.unpack(buf); } catch (err) { if ( err instanceof FontDecompressError && err.code === FONT_DECOMPRESS_CODES.INVALID_DATA ) { console.warn("Corrupted BTF from CDN:", err.message); return null; } throw err; } } ``` ## API Reference ### Public exports - `class BinaryFontUnpacker` — `unpack(data: Uint8Array): TypefaceFont` - `createBinaryFontUnpacker(): BinaryFontUnpacker` - `FONT_DECOMPRESS_CODES` — frozen registry with `UNPACK_FAILED`, `INVALID_DATA` - `class FontDecompressError extends SuaveplanError` - `type FontDecompressErrorCode` The `TypefaceFont` and `PathCommand` types come from `@suaveplan/font-convert`; consumers import them directly from there rather than via this package's surface. ### Subpath exports | Subpath | What | | -------------------------------------------- | ----------------------------- | | `.` | Root barrel (above) | | `./error-codes` | `FONT_DECOMPRESS_CODES`, `FontDecompressError` | | `./constants` | BTF wire-format constants | | `./utils/decode-string` | `decodeString` helper | | `./unpacker/binary-font-unpacker` | `BinaryFontUnpacker`, factory | ## Implementation Notes The package is **universal**: zero `node:` / `bun:` imports anywhere in `src/`. The only platform-aware calls are `DataView`, `TextDecoder`, and `String.fromCharCode` — all available in every supported runtime (Node ≥ 18, Bun, modern browsers, Web Workers). The BTF wire-format constants (`CMD_M` … `CMD_Z`, `BYTE_TO_CMD`, `CMD_COORD_COUNTS`, `METRICS_COUNT`) are duplicated from `@suaveplan/font-compress`. Duplicating 19 lines of magic numbers avoids a third "fonts-shared" package and keeps each package self-contained. If the BTF format ever changes — which is a coordinated breaking event — both packages get updated in the same commit. `@suaveplan/font-compress` is a **devDependency** for round-trip tests only. The runtime dependency graph is `font-decompress → font-convert + error`; the test surface adds `font-compress` solely to generate known-vector fixtures.