Initial Commit
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user