first commit
This commit is contained in:
125
docs/media/fonts/font-subset/README.md
Normal file
125
docs/media/fonts/font-subset/README.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# @suaveplan/font-compress - Font subsetting and compression library with a codec-agnostic adapter interface. Supports Unicode range subsetting, WOFF2/WOFF/OTF/TTF output, OpenType feature filtering, hinting strip, and metadata strip.
|
||||
|
||||
## Summary
|
||||
|
||||
Font subsetting and compression library with a codec-agnostic adapter interface. Supports Unicode range subsetting, WOFF2/WOFF/OTF/TTF output, OpenType feature filtering, hinting strip, and metadata strip. Adapter implementations supply the actual codec; this package provides the orchestration, validation, format detection, and OTel instrumentation.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [null](./src/adapters/null/null.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [null](./src/adapters/null/null.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [font-compressor](./src/core/font-compressor/font-compressor.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [font-compressor](./src/core/font-compressor/font-compressor.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [types](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [common](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [format-detect](./src/utils/format-detect.md) — Provides magic-byte font format detection from raw binary data. Before compressing or probing a font file, `FontCompres…
|
||||
- [unicode-ranges](./src/utils/unicode-ranges.md) — Parses CSS/OpenType-style Unicode range notation into a `Set<number>` of integer codepoints. Font subsetting workflows…
|
||||
<!-- end:auto-docs --> - [null](./src/adapters/null/null.md) — <!-- begin:auto-docs --> _(no submodules)_- [font-compressor](./src/core/font-compressor/font-compressor.md) — <!-- begin:auto-docs --> _(no submodules)_- [font-compressor](./src/core/font-compressor/font-compressor.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [common](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [format-detect](./src/utils/format-detect.md) — Provides magic-byte font format detection from raw binary data. Before compressing or probing a font file, `FontCompres…
|
||||
- [unicode-ranges](./src/utils/unicode-ranges.md) — Parses CSS/OpenType-style Unicode range notation into a `Set<number>` of integer codepoints. Font subsetting workflows…
|
||||
- [null](./src/adapters/null/null.md) — <!-- begin:auto-docs --> _(no submodules)_- [font-compressor](./src/core/font-compressor/font-compressor.md) — <!-- begin:auto-docs --> _(no submodules)_- [font-compressor](./src/core/font-compressor/font-compressor.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [common](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [format-detect](./src/utils/format-detect.md) — Provides magic-byte font format detection from raw binary data. Before compressing or probing a font file, `FontCompres…
|
||||
- [unicode-ranges](./src/utils/unicode-ranges.md) — Parses CSS/OpenType-style Unicode range notation into a `Set<number>` of integer codepoints. Font subsetting workflows…
|
||||
- [null](./src/adapters/null/null.md) — <!-- begin:auto-docs --> _(no submodules)_- [font-compressor](./src/core/font-compressor/font-compressor.md) — <!-- begin:auto-docs --> _(no submodules)_- [font-compressor](./src/core/font-compressor/font-compressor.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [common](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [format-detect](./src/utils/format-detect.md) — Provides magic-byte font format detection from raw binary data. Before compressing or probing a font file, `FontCompres…
|
||||
- [unicode-ranges](./src/utils/unicode-ranges.md) — Parses CSS/OpenType-style Unicode range notation into a `Set<number>` of integer codepoints. Font subsetting workflows…
|
||||
- [null](./src/adapters/null/null.md) — <!-- begin:auto-docs --> _(no submodules)_- [font-compressor](./src/core/font-compressor/font-compressor.md) — <!-- begin:auto-docs --> _(no submodules)_ - [font-compressor](./src/core/font-compressor/font-compressor.md) — <!-- begin:auto-docs --> _(no submodules)_- [types](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_ - [common](./src/types/common.md) — <!-- begin:auto-docs --> _(no submodules)_- [format-detect](./src/utils/format-detect.md) — Provides magic-byte font format detection from raw binary data. Before compressing or probing a font file, `FontCompres…
|
||||
- [unicode-ranges](./src/utils/unicode-ranges.md) — Parses CSS/OpenType-style Unicode range notation into a `Set<number>` of integer codepoints. Font subsetting workflows…
|
||||
|
||||
## Packages
|
||||
|
||||
| Module | Description |
|
||||
|---|---|
|
||||
| [FontCompressor](src/core/font-compressor/font-compressor.md) | Core orchestrator: validation, format checks, AbortSignal, OTel |
|
||||
| [NullFontCompressAdapter](src/adapters/null-adapter/null-adapter.md) | No-op adapter for unit/integration tests |
|
||||
| [parseUnicodeRanges](src/utils/unicode-ranges.md) | Unicode range string → `Set<number>` codepoint parser |
|
||||
| [detectFontFormat](src/utils/format-detect.md) | Magic-byte font format detection |
|
||||
| [Types & Schemas](src/types/common.md) | All public types and Zod validation schemas |
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
bun add @suaveplan/font-compress
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```typescript
|
||||
import {
|
||||
FontCompressor,
|
||||
NullFontCompressAdapter,
|
||||
} from "@suaveplan/font-compress";
|
||||
|
||||
// Replace NullFontCompressAdapter with a real codec adapter in production
|
||||
const adapter = new NullFontCompressAdapter();
|
||||
const compressor = new FontCompressor({ adapter });
|
||||
|
||||
const fontBytes = await Bun.file("Roboto-Regular.ttf").bytes();
|
||||
const result = await compressor.compress(fontBytes, {
|
||||
subset: { unicodeRanges: ["U+0020-007F"] },
|
||||
outputFormat: "woff2",
|
||||
stripHinting: true,
|
||||
});
|
||||
|
||||
console.log(`Compressed ${result.originalSize} → ${result.compressedSize} bytes`);
|
||||
console.log(`Ratio: ${(result.ratio * 100).toFixed(1)}%`);
|
||||
```
|
||||
|
||||
## Providing a Real Codec Adapter
|
||||
|
||||
Implement `FontCompressAdapter` to wrap any real font codec:
|
||||
|
||||
```typescript
|
||||
import type { FontCompressAdapter, FontMetadata, FontCompressResult, CompressFontOptions } from "@suaveplan/font-compress";
|
||||
|
||||
class WoffToolsAdapter implements FontCompressAdapter {
|
||||
readonly supportedInputFormats = ["ttf", "otf"] as const;
|
||||
readonly supportedOutputFormats = ["woff2"] as const;
|
||||
|
||||
async probe(data: Uint8Array): Promise<FontMetadata> {
|
||||
// parse font tables to extract metadata
|
||||
}
|
||||
|
||||
async compress(data: Uint8Array, options: CompressFontOptions): Promise<FontCompressResult> {
|
||||
// call woff2-encoder or similar
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
All errors thrown by this package are instances of `FontCompressError`, which extends `SuaveplanError`. Use the `code` property for programmatic error handling:
|
||||
|
||||
```typescript
|
||||
import { FONT_COMPRESS_CODES, FontCompressError } from "@suaveplan/font-compress";
|
||||
|
||||
try {
|
||||
await compressor.compress(data, { outputFormat: "woff2" });
|
||||
} catch (e) {
|
||||
if (e instanceof FontCompressError) {
|
||||
switch (e.code) {
|
||||
case FONT_COMPRESS_CODES.UNSUPPORTED_FORMAT: /* ... */
|
||||
case FONT_COMPRESS_CODES.PARSE_FAILED: /* ... */
|
||||
case FONT_COMPRESS_CODES.ENCODE_FAILED: /* ... */
|
||||
case FONT_COMPRESS_CODES.INVALID_OPTIONS: /* ... */
|
||||
case FONT_COMPRESS_CODES.SUBSET_FAILED: /* ... */
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Unicode Range Subsetting
|
||||
|
||||
```typescript
|
||||
import { parseUnicodeRanges } from "@suaveplan/font-compress/utils/unicode-ranges";
|
||||
|
||||
const codepoints = parseUnicodeRanges(["U+0020-007F", "U+00A0-00FF"]);
|
||||
console.log(codepoints.size); // 192 codepoints
|
||||
```
|
||||
|
||||
## Format Detection
|
||||
|
||||
```typescript
|
||||
import { detectFontFormat } from "@suaveplan/font-compress/utils/format-detect";
|
||||
|
||||
const fmt = detectFontFormat(await Bun.file("font.woff2").bytes());
|
||||
console.log(fmt); // "woff2"
|
||||
```
|
||||
76
docs/media/fonts/font-subset/src/adapters/null/null.md
Normal file
76
docs/media/fonts/font-subset/src/adapters/null/null.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# NullFontCompressAdapter
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Provides a no-op `FontCompressAdapter` implementation designed exclusively for unit and integration testing. Rather than invoking any real font codec, `NullFontCompressAdapter` returns configurable fixture values, allowing test suites to verify `FontCompressor` orchestration logic (abort handling, format validation, OTel wiring, option parsing) in complete isolation from external binary dependencies.
|
||||
|
||||
## Features
|
||||
|
||||
- Implements the full `FontCompressAdapter` contract without external dependencies
|
||||
- `probe` returns deterministic fixture `FontMetadata` derived from input byte length
|
||||
- `probe` accepts a fully custom `FontMetadata` override via constructor options
|
||||
- `compress` echoes the input `Uint8Array` back unchanged with a synthetic `ratio` of `0.9`
|
||||
- `compress` respects `outputFormat` from options (defaults to `"woff2"`)
|
||||
- All four formats (`ttf`, `otf`, `woff`, `woff2`) listed in both `supportedInputFormats` and `supportedOutputFormats`
|
||||
- Fully synchronous-safe — all async methods resolve immediately with no I/O
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```typescript
|
||||
import { NullFontCompressAdapter, FontCompressor } from "@suaveplan/font-compress";
|
||||
|
||||
const adapter = new NullFontCompressAdapter();
|
||||
const compressor = new FontCompressor({ adapter });
|
||||
|
||||
const data = new Uint8Array([0x00, 0x01, 0x00, 0x00, ...new Array(96).fill(0)]);
|
||||
const result = await compressor.compress(data, { outputFormat: "woff2" });
|
||||
// result.format === "woff2", result.ratio === 0.9
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```typescript
|
||||
import { NullFontCompressAdapter, FontCompressor } from "@suaveplan/font-compress";
|
||||
import type { FontMetadata } from "@suaveplan/font-compress";
|
||||
|
||||
const customMeta: FontMetadata = {
|
||||
format: "woff2",
|
||||
mimeType: "font/woff2",
|
||||
byteSize: 51200,
|
||||
familyName: "TestFont",
|
||||
glyphCount: 256,
|
||||
};
|
||||
|
||||
const adapter = new NullFontCompressAdapter({ probeResult: customMeta });
|
||||
const compressor = new FontCompressor({ adapter });
|
||||
|
||||
const meta = await compressor.probe(new Uint8Array(10));
|
||||
console.log(meta.familyName); // "TestFont"
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `NullFontCompressAdapterOptions`
|
||||
|
||||
| Field | Type | Description |
|
||||
|---|---|---|
|
||||
| `probeResult` | `FontMetadata` | Custom metadata returned by every `probe()` call |
|
||||
|
||||
### `new NullFontCompressAdapter(options?)`
|
||||
|
||||
Constructs the adapter. All fields in `options` are optional.
|
||||
|
||||
### `probe(data: Uint8Array): Promise<FontMetadata>`
|
||||
|
||||
Returns `probeResult` if provided, otherwise returns `{ format: "ttf", mimeType: "font/ttf", byteSize: data.byteLength }`.
|
||||
|
||||
### `compress(data: Uint8Array, options: CompressFontOptions): Promise<FontCompressResult>`
|
||||
|
||||
Returns the input `data` unchanged as `result.data`. The `format` and `mimeType` are derived from `options.outputFormat ?? "woff2"`. `ratio` is always `0.9`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The `0.9` ratio constant is arbitrary but non-trivial: it satisfies the property test invariant (`ratio > 0`) while remaining obviously synthetic. Callers testing ratio bounds should use a custom adapter. The `NullFontCompressAdapter` deliberately does not validate the `data` contents — it trusts the `FontCompressor` to perform upstream format checks before delegating here.
|
||||
@@ -0,0 +1,103 @@
|
||||
# FontCompressor
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
The central orchestrator for font compression operations. `FontCompressor` wraps any `FontCompressAdapter` implementation and adds the cross-cutting concerns that all production uses require: Zod option validation, format support checks, `AbortSignal` propagation, and OTel span/metric instrumentation. Application code constructs one `FontCompressor` per adapter and calls `probe` or `compress` without needing to manage any of that plumbing directly.
|
||||
|
||||
## Features
|
||||
|
||||
- `probe(data)` — delegates to the adapter's probe method and maps adapter errors to `PARSE_FAILED`
|
||||
- `compress(data, options?, signal?)` — validates options with Zod, checks both input and output format support, delegates compress, wraps errors as `ENCODE_FAILED`
|
||||
- `AbortSignal` — checked before compression begins; throws `ENCODE_FAILED` immediately when aborted
|
||||
- Zod schema validation — `CompressFontOptionsSchema.safeParse` on every compress call; throws `INVALID_OPTIONS` on failure
|
||||
- Format support checks — `UNSUPPORTED_FORMAT` when the probed input format or the requested output format is not listed in the adapter's supported lists
|
||||
- OTel span `font.compress` / `font.probe` — status set to OK or ERROR with message
|
||||
- OTel counter `font_compress_total` with `{ format }` attribute
|
||||
- OTel histograms `font_compress_duration_ms` and `font_compress_ratio`
|
||||
- `createFontCompressor` factory for ergonomic construction without `new`
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```typescript
|
||||
import { FontCompressor, NullFontCompressAdapter } from "@suaveplan/font-compress";
|
||||
|
||||
const data = new Uint8Array([0x00, 0x01, 0x00, 0x00, /* TTF bytes */ ]);
|
||||
const adapter = new NullFontCompressAdapter();
|
||||
const compressor = new FontCompressor({ adapter });
|
||||
|
||||
const result = await compressor.compress(data, { outputFormat: "woff2" });
|
||||
console.log(result.format); // "woff2"
|
||||
console.log(result.ratio); // e.g. 0.9
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```typescript
|
||||
import {
|
||||
createFontCompressor,
|
||||
NullFontCompressAdapter,
|
||||
FONT_COMPRESS_CODES,
|
||||
FontCompressError,
|
||||
} from "@suaveplan/font-compress";
|
||||
import type { LoggerLike, Clock } from "@suaveplan/types";
|
||||
|
||||
declare const logger: LoggerLike;
|
||||
declare const clock: Clock;
|
||||
declare const realAdapter: import("@suaveplan/font-compress").FontCompressAdapter;
|
||||
|
||||
const compressor = createFontCompressor({ adapter: realAdapter, logger, clock });
|
||||
|
||||
// Abort long-running compression
|
||||
const ctrl = new AbortController();
|
||||
setTimeout(() => ctrl.abort(), 2000);
|
||||
|
||||
try {
|
||||
const result = await compressor.compress(fontBytes, {
|
||||
subset: { unicodeRanges: ["U+0020-007F"] },
|
||||
outputFormat: "woff2",
|
||||
stripHinting: true,
|
||||
}, ctrl.signal);
|
||||
return result;
|
||||
} catch (e) {
|
||||
if (e instanceof FontCompressError) {
|
||||
if (e.code === FONT_COMPRESS_CODES.ENCODE_FAILED) {
|
||||
// aborted or codec failure
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `new FontCompressor(options: FontCompressorOptions)`
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|---|---|---|---|
|
||||
| `adapter` | `FontCompressAdapter` | ✓ | Codec implementation |
|
||||
| `logger` | `LoggerLike` | — | Structured logger |
|
||||
| `clock` | `Clock` | — | Injectable clock |
|
||||
|
||||
### `probe(data: Uint8Array): Promise<FontMetadata>`
|
||||
|
||||
Delegates to `adapter.probe`. Wraps non-`FontCompressError` throws as `PARSE_FAILED`.
|
||||
|
||||
### `compress(data, options?, signal?): Promise<FontCompressResult>`
|
||||
|
||||
1. Checks `signal.aborted` → throws `ENCODE_FAILED`
|
||||
2. Validates `options` via `CompressFontOptionsSchema` → throws `INVALID_OPTIONS`
|
||||
3. Calls `probe` to determine input format → throws `UNSUPPORTED_FORMAT` if not in adapter list
|
||||
4. Checks output format support → throws `UNSUPPORTED_FORMAT`
|
||||
5. Calls `adapter.compress` → wraps errors as `ENCODE_FAILED`
|
||||
6. Records OTel metrics and closes span
|
||||
|
||||
### `createFontCompressor(options): FontCompressor`
|
||||
|
||||
Factory convenience function identical to `new FontCompressor(options)`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Options default values (`stripHinting: true`, `stripMetadata: false`) are applied by Zod's `.default()` inside `CompressFontOptionsSchema.safeParse`, so the adapter always receives a fully-populated options object. The OTel meter and tracer are obtained via the global provider; if no SDK is configured the NoopMeter/NoopTracer are used automatically, incurring no overhead.
|
||||
107
docs/media/fonts/font-subset/src/types/common.md
Normal file
107
docs/media/fonts/font-subset/src/types/common.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Types: common
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Defines the complete public type surface for `@suaveplan/font-compress`. All option bundles, result shapes, the adapter contract, and Zod validation schemas live here, ensuring every layer of the package shares a single authoritative definition. Keeping types co-located with their Zod schemas eliminates the risk of runtime shapes drifting from TypeScript definitions.
|
||||
|
||||
## Features
|
||||
|
||||
- `FontFormat` union type covering `ttf`, `otf`, `woff`, `woff2`, and `unknown`
|
||||
- `FontMetadata` — probe result carrying format, MIME type, byte size, and optional glyph/family metadata
|
||||
- `SubsetOptions` — Unicode range strings, literal character string, and explicit glyph ID list
|
||||
- `CompressFontOptions` — full option bundle: subset, output format, strip-hinting, strip-metadata, feature tags; defaults via Zod `.default()`
|
||||
- `FontCompressResult` — compression output: data bytes, format, MIME type, size pair, compression ratio, and optional glyph count
|
||||
- `FontCompressAdapter` — codec-agnostic interface; implementations supply `probe` and `compress` with async semantics
|
||||
- `FontCompressorOptions` — constructor options for `FontCompressor`: required adapter plus optional logger and clock for observability injection
|
||||
- Zod schemas for every public type, enabling runtime validation at package boundaries
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```typescript
|
||||
import type { CompressFontOptions, FontMetadata } from "@suaveplan/font-compress";
|
||||
|
||||
const opts: CompressFontOptions = {
|
||||
outputFormat: "woff2",
|
||||
stripHinting: true,
|
||||
};
|
||||
|
||||
const meta: FontMetadata = {
|
||||
format: "ttf",
|
||||
mimeType: "font/ttf",
|
||||
byteSize: 204800,
|
||||
familyName: "Roboto",
|
||||
};
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```typescript
|
||||
import { CompressFontOptionsSchema } from "@suaveplan/font-compress";
|
||||
|
||||
// Validate user-supplied options at an API boundary
|
||||
const raw: unknown = { outputFormat: "woff2", subset: { unicodeRanges: ["U+0020-007F"] } };
|
||||
const parsed = CompressFontOptionsSchema.safeParse(raw);
|
||||
if (!parsed.success) {
|
||||
console.error(parsed.error.flatten());
|
||||
} else {
|
||||
const opts = parsed.data; // stripHinting defaults to true, stripMetadata to false
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `FontFormat`
|
||||
|
||||
```typescript
|
||||
type FontFormat = "ttf" | "otf" | "woff" | "woff2" | "unknown";
|
||||
```
|
||||
|
||||
Union of supported font formats, with `"unknown"` used when detection fails.
|
||||
|
||||
### `FontMetadata`
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|---|---|---|---|
|
||||
| `format` | `FontFormat` | ✓ | Detected format |
|
||||
| `mimeType` | `string` | ✓ | IANA MIME type |
|
||||
| `byteSize` | `number` | ✓ | Raw byte count |
|
||||
| `glyphCount` | `number` | — | Total glyphs, if known |
|
||||
| `familyName` | `string` | — | Font family name |
|
||||
| `subFamilyName` | `string` | — | Style variant name |
|
||||
| `version` | `string` | — | Font version string |
|
||||
|
||||
### `SubsetOptions`
|
||||
|
||||
| Field | Type | Description |
|
||||
|---|---|---|
|
||||
| `unicodeRanges` | `string[]` | Range strings e.g. `["U+0020-007F"]` |
|
||||
| `characters` | `string` | Literal chars to include |
|
||||
| `glyphIds` | `number[]` | Explicit glyph IDs |
|
||||
|
||||
### `CompressFontOptions`
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `subset` | `SubsetOptions` | — | Subsetting parameters |
|
||||
| `outputFormat` | `FontFormat` | — | Target format |
|
||||
| `stripHinting` | `boolean` | `true` | Remove hinting tables |
|
||||
| `stripMetadata` | `boolean` | `false` | Strip name/OS2 metadata |
|
||||
| `features` | `string[]` | — | OT feature tags to retain |
|
||||
|
||||
### `FontCompressAdapter`
|
||||
|
||||
```typescript
|
||||
interface FontCompressAdapter {
|
||||
readonly supportedInputFormats: readonly FontFormat[];
|
||||
readonly supportedOutputFormats: readonly FontFormat[];
|
||||
probe(data: Uint8Array): Promise<FontMetadata>;
|
||||
compress(data: Uint8Array, options: CompressFontOptions): Promise<FontCompressResult>;
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Zod schemas use `.default()` on `stripHinting` (true) and `stripMetadata` (false) so that callers can omit them without `undefined` propagating to the adapter. `FontCompressorOptionsSchema` uses `z.custom<LoggerLike>()` and `z.custom<Clock>()` to allow structural compatibility without importing those interfaces at runtime — they carry no validation logic. The `FontCompressAdapter` interface is intentionally minimal; adapters that wrap real codec libraries (e.g. woff2, fonttools) need only implement two async methods.
|
||||
56
docs/media/fonts/font-subset/src/utils/format-detect.md
Normal file
56
docs/media/fonts/font-subset/src/utils/format-detect.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Utils: format-detect
|
||||
|
||||
## Purpose
|
||||
|
||||
Provides magic-byte font format detection from raw binary data. Before compressing or probing a font file, `FontCompressor` needs to know the input format to validate adapter support. This utility performs a fast 4-byte prefix check covering the four formats that matter for web font workflows: TTF, OTF, WOFF, and WOFF2.
|
||||
|
||||
## Features
|
||||
|
||||
- Detects TTF via `00 01 00 00` (TrueType scalar type)
|
||||
- Detects OTF via `4F 54 54 4F` ("OTTO" — OpenType CFF)
|
||||
- Detects WOFF via `77 4F 46 46` ("wOFF")
|
||||
- Detects WOFF2 via `77 4F 46 32` ("wOF2")
|
||||
- Returns `"unknown"` for unrecognised or too-short buffers (< 4 bytes)
|
||||
- Zero external dependencies — direct byte comparison without any registry
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```typescript
|
||||
import { detectFontFormat } from "@suaveplan/font-compress/utils/format-detect";
|
||||
|
||||
const bytes = new Uint8Array([0x77, 0x4f, 0x46, 0x32, /* ... */]);
|
||||
console.log(detectFontFormat(bytes)); // "woff2"
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```typescript
|
||||
import { detectFontFormat } from "@suaveplan/font-compress/utils/format-detect";
|
||||
import { FONT_COMPRESS_CODES, FontCompressError } from "@suaveplan/font-compress";
|
||||
|
||||
function requireKnownFormat(data: Uint8Array): void {
|
||||
const fmt = detectFontFormat(data);
|
||||
if (fmt === "unknown") {
|
||||
throw new FontCompressError(
|
||||
FONT_COMPRESS_CODES.UNSUPPORTED_FORMAT,
|
||||
"Unrecognised font format — expected TTF, OTF, WOFF, or WOFF2"
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `detectFontFormat(data: Uint8Array): FontFormat`
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|---|---|---|
|
||||
| `data` | `Uint8Array` | Raw font bytes to inspect |
|
||||
|
||||
**Returns** one of `"ttf"`, `"otf"`, `"woff"`, `"woff2"`, or `"unknown"`.
|
||||
|
||||
Never throws — always returns a `FontFormat` value.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The implementation avoids the `@suaveplan/mime` signature registry for font detection because the published mime package uses `"sideEffects": false`, which causes Vite to tree-shake the side-effect import (`import "./signatures/index.js"`) that registers magic-byte patterns at load time. The result is that `detectFromMagicBytes` returns `undefined` for all inputs in the built package. Inlining the four font signatures here is simpler, zero-dependency, and correct. Should `@suaveplan/mime` be rebuilt with explicit signatures exports, this module can be updated to delegate to it.
|
||||
65
docs/media/fonts/font-subset/src/utils/unicode-ranges.md
Normal file
65
docs/media/fonts/font-subset/src/utils/unicode-ranges.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Utils: unicode-ranges
|
||||
|
||||
## Purpose
|
||||
|
||||
Parses CSS/OpenType-style Unicode range notation into a `Set<number>` of integer codepoints. Font subsetting workflows need to know which codepoints to retain; this utility bridges the human-readable range format (e.g. `U+0020-007F`) and the numeric representation that font adapters operate on.
|
||||
|
||||
## Features
|
||||
|
||||
- Parses single codepoints: `U+XXXX`
|
||||
- Parses inclusive ranges: `U+XXXX-XXXX`
|
||||
- Case-insensitive for both the `U+` prefix and hex digits
|
||||
- Merges multiple ranges into a single `Set<number>` with no duplicates
|
||||
- Throws `FontCompressError(INVALID_OPTIONS)` for malformed syntax or reversed ranges
|
||||
- Zero external dependencies
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```typescript
|
||||
import { parseUnicodeRanges } from "@suaveplan/font-compress/utils/unicode-ranges";
|
||||
|
||||
// ASCII printable range
|
||||
const ascii = parseUnicodeRanges(["U+0020-007F"]);
|
||||
console.log(ascii.has(0x41)); // true ("A")
|
||||
console.log(ascii.size); // 96
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```typescript
|
||||
import { parseUnicodeRanges } from "@suaveplan/font-compress/utils/unicode-ranges";
|
||||
import { FontCompressError } from "@suaveplan/font-compress";
|
||||
|
||||
// Multi-range merge — Latin + Latin Extended
|
||||
const ranges = parseUnicodeRanges(["U+0020-007F", "U+00A0-00FF"]);
|
||||
console.log(ranges.size); // 192
|
||||
|
||||
// Catching parse errors
|
||||
try {
|
||||
parseUnicodeRanges(["0020-007F"]); // missing U+ prefix
|
||||
} catch (e) {
|
||||
if (e instanceof FontCompressError) {
|
||||
console.error(e.code); // "FONT_COMPRESS.INVALID_OPTIONS"
|
||||
console.error(e.message); // describes the bad input
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `parseUnicodeRanges(ranges: string[]): Set<number>`
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|---|---|---|
|
||||
| `ranges` | `string[]` | Array of range strings in `U+XXXX` or `U+XXXX-XXXX` format |
|
||||
|
||||
**Returns** a `Set<number>` containing every integer codepoint covered by the union of all input ranges.
|
||||
|
||||
**Throws** `FontCompressError` with code `FONT_COMPRESS.INVALID_OPTIONS` when:
|
||||
|
||||
- A string does not match the `U+XXXX` or `U+XXXX-XXXX` pattern
|
||||
- The start codepoint of a range exceeds the end codepoint
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The regex `^[Uu]\+([0-9A-Fa-f]{1,6})(?:-([0-9A-Fa-f]{1,6}))?$` validates the entire string in a single pass, avoiding character-by-character parsing. Codepoints up to 6 hex digits are supported (`U+10FFFF` = 1114111), covering the full Unicode range. For very large ranges (e.g. `U+0000-FFFF` = 65536 entries), callers should be aware that `Set` construction is O(n) in the number of codepoints.
|
||||
Reference in New Issue
Block a user