Files
2026-05-27 23:29:53 +01:00

123 lines
3.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OpenSpec Change: enterprise-font-compress-btf
- **Change ID**: enterprise-font-compress-btf
- **Package**: `@suaveplan/font-compress`
- **Category**: universal
- **Target path**: `universal/compression/font-compress`
- **Status**: PROPOSED
- **Date**: 2026-04-25
- **Reference source**: `/mnt/s/web/suaveplan/packages/universal/font-compress/src/packer/`
---
## Summary
Deliver `@suaveplan/font-compress` on the genesis branch from scratch at enterprise quality. The package implements BTF (Binary TypeFace) binary packing: takes a `TypefaceFont` JSON (from `@suaveplan/font-convert`) and produces a compact `Uint8Array` using dictionary-based compression with `@suaveplan/bitpacker`. Also provides `BinaryFontUnpacker` which reverses the process (BTF binary → `TypefaceFont` JSON).
---
## Current-state audit (genesis)
The package does not exist on genesis. The original logic was split across `@suaveplan/font-compress` (packing) and `@suaveplan/font-decompress` (unpacking) on main. In genesis we co-locate packer and unpacker here since they share the binary format and dictionary logic.
---
## BTF binary format
```
[metadata: familyName string][cssFontWeight string][cssFontStyle string]
[reversed: 1 byte]
[dictLen: uint32][dictionary: Int16Array × dictLen]
[metrics: 9 × uint16 (indexes into dictionary)]
[glyphCount: uint32]
[per glyph: char codepoint uint32][ha/x_min/x_max: 3 × uint16][cmdCount: uint16]
[per cmd: type byte + coord indexes uint16 × 2|4|0]
```
String encoding: `[length: uint16][utf8 bytes]`
---
## Available workspace deps on genesis
| Package | Role |
|---|---|
| `@suaveplan/error` (`workspace:^`) | Base class for `FontCompressError`; `FONT_COMPRESS.*` codes |
| `@suaveplan/types` (`workspace:^`) | `LoggerLike`, `Clock` |
| `@suaveplan/font-convert` (`workspace:^`) | `TypefaceFont`, `TypefaceGlyph`, `PathCommand` types |
| `@suaveplan/bitpacker` (`workspace:^`) | `BitWriter`/`BitReader` for bit-packed command encoding |
| `zod` (`catalog:forms`) | Option schemas |
| `@opentelemetry/api` (`catalog:observability`) | Spans + metrics |
---
## Public API
```ts
class FontCompressError extends SuaveplanError<FontCompressErrorCode>
type FontCompressErrorCode =
| 'FONT_COMPRESS_PACK_FAILED'
| 'FONT_COMPRESS_UNPACK_FAILED'
| 'FONT_COMPRESS_INVALID_DATA'
class BinaryFontPacker {
pack(font: TypefaceFont): Uint8Array
}
class BinaryFontUnpacker {
unpack(data: Uint8Array): TypefaceFont
}
function createBinaryFontPacker(): BinaryFontPacker
function createBinaryFontUnpacker(): BinaryFontUnpacker
```
---
## File layout
```text
universal/compression/font-compress/
package.json
tsconfig.lib.json
vitest.config.ts
vite.config.ts
src/
index.ts
error-codes.ts
constants/
index.ts
packer/
binary-font-packer/
binary-font-packer.ts
binary-font-packer.md
binary-font-packer.test.ts
index.ts
unpacker/
binary-font-unpacker/
binary-font-unpacker.ts
binary-font-unpacker.md
binary-font-unpacker.test.ts
index.ts
utils/
encode-string.ts
encode-string.md
encode-string.test.ts
parse-path.ts
parse-path.md
parse-path.test.ts
__benchmarks__/
font-compress.bench.ts
tests/
roundtrip.test.ts
property.test.ts
```
---
## Impact
**Downstream**: Three.js font loading pipelines.
**Prerequisites**: `@suaveplan/font-convert`, `@suaveplan/bitpacker`.