132 lines
5.8 KiB
Markdown
132 lines
5.8 KiB
Markdown
# @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. Store-agnostic, free of `node:` / `bun:`
|
|
imports.
|
|
|
|
<!-- begin:auto-docs -->
|
|
## 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…
|
|
<!-- end:auto-docs -->
|
|
|
|
## 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<FontDecompressErrorCode>`
|
|
- `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.
|
|
|