Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
# BinaryFontPacker
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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
```typescript
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
```typescript
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
```text
[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.