@format.dev/fonts 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -20
- package/dist/index.cjs +82 -0
- package/dist/index.d.cts +84 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +84 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +77 -0
- package/dist/index.mjs.map +1 -0
- package/dist/mode-B8fVsgsV.mjs +26 -0
- package/dist/mode-B8fVsgsV.mjs.map +1 -0
- package/dist/mode-BXEMdEsE.d.cts +26 -0
- package/dist/mode-BXEMdEsE.d.cts.map +1 -0
- package/dist/mode-CtSoe83u.d.mts +26 -0
- package/dist/mode-CtSoe83u.d.mts.map +1 -0
- package/dist/mode-DzsrOPvc.cjs +37 -0
- package/dist/mode.cjs +4 -0
- package/dist/mode.d.cts +2 -0
- package/dist/mode.d.mts +2 -0
- package/dist/mode.mjs +3 -0
- package/fonts/emoji/Noto-COLRv1.ttf +0 -0
- package/fonts/emoji/format-emoji-0.woff2 +0 -0
- package/fonts/emoji/format-emoji-1.woff2 +0 -0
- package/fonts/emoji/format-emoji-2.woff2 +0 -0
- package/fonts/emoji/format-emoji-3.woff2 +0 -0
- package/fonts/emoji/format-emoji-4.woff2 +0 -0
- package/fonts/emoji/format-emoji-5.woff2 +0 -0
- package/fonts/emoji/format-emoji-6.woff2 +0 -0
- package/fonts/emoji/format-emoji-7.woff2 +0 -0
- package/fonts/emoji/format-emoji-8.woff2 +0 -0
- package/fonts/emoji/format-emoji-9.woff2 +0 -0
- package/fonts/fonts.json +1129 -0
- package/fonts/math/STIXTwoMath-Regular.ttf +0 -0
- package/fonts/math/format-math.woff2 +0 -0
- package/fonts/static/mono/FormatMono-100-Italic.ttf +0 -0
- package/fonts/static/mono/FormatMono-100-Italic.woff2 +0 -0
- package/fonts/static/mono/FormatMono-100.ttf +0 -0
- package/fonts/static/mono/FormatMono-100.woff2 +0 -0
- package/fonts/static/mono/FormatMono-200-Italic.ttf +0 -0
- package/fonts/static/mono/FormatMono-200-Italic.woff2 +0 -0
- package/fonts/static/mono/FormatMono-200.ttf +0 -0
- package/fonts/static/mono/FormatMono-200.woff2 +0 -0
- package/fonts/static/mono/FormatMono-300-Italic.ttf +0 -0
- package/fonts/static/mono/FormatMono-300-Italic.woff2 +0 -0
- package/fonts/static/mono/FormatMono-300.ttf +0 -0
- package/fonts/static/mono/FormatMono-300.woff2 +0 -0
- package/fonts/static/mono/FormatMono-400-Italic.ttf +0 -0
- package/fonts/static/mono/FormatMono-400-Italic.woff2 +0 -0
- package/fonts/static/mono/FormatMono-400.ttf +0 -0
- package/fonts/static/mono/FormatMono-400.woff2 +0 -0
- package/fonts/static/mono/FormatMono-500-Italic.ttf +0 -0
- package/fonts/static/mono/FormatMono-500-Italic.woff2 +0 -0
- package/fonts/static/mono/FormatMono-500.ttf +0 -0
- package/fonts/static/mono/FormatMono-500.woff2 +0 -0
- package/fonts/static/mono/FormatMono-600-Italic.ttf +0 -0
- package/fonts/static/mono/FormatMono-600-Italic.woff2 +0 -0
- package/fonts/static/mono/FormatMono-600.ttf +0 -0
- package/fonts/static/mono/FormatMono-600.woff2 +0 -0
- package/fonts/static/mono/FormatMono-700-Italic.ttf +0 -0
- package/fonts/static/mono/FormatMono-700-Italic.woff2 +0 -0
- package/fonts/static/mono/FormatMono-700.ttf +0 -0
- package/fonts/static/mono/FormatMono-700.woff2 +0 -0
- package/fonts/static/mono/FormatMono-800-Italic.ttf +0 -0
- package/fonts/static/mono/FormatMono-800-Italic.woff2 +0 -0
- package/fonts/static/mono/FormatMono-800.ttf +0 -0
- package/fonts/static/mono/FormatMono-800.woff2 +0 -0
- package/fonts/static/sans/FormatSans-100-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSans-100-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSans-100.ttf +0 -0
- package/fonts/static/sans/FormatSans-100.woff2 +0 -0
- package/fonts/static/sans/FormatSans-200-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSans-200-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSans-200.ttf +0 -0
- package/fonts/static/sans/FormatSans-200.woff2 +0 -0
- package/fonts/static/sans/FormatSans-300-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSans-300-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSans-300.ttf +0 -0
- package/fonts/static/sans/FormatSans-300.woff2 +0 -0
- package/fonts/static/sans/FormatSans-400-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSans-400-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSans-400.ttf +0 -0
- package/fonts/static/sans/FormatSans-400.woff2 +0 -0
- package/fonts/static/sans/FormatSans-500-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSans-500-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSans-500.ttf +0 -0
- package/fonts/static/sans/FormatSans-500.woff2 +0 -0
- package/fonts/static/sans/FormatSans-600-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSans-600-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSans-600.ttf +0 -0
- package/fonts/static/sans/FormatSans-600.woff2 +0 -0
- package/fonts/static/sans/FormatSans-700-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSans-700-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSans-700.ttf +0 -0
- package/fonts/static/sans/FormatSans-700.woff2 +0 -0
- package/fonts/static/sans/FormatSans-800-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSans-800-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSans-800.ttf +0 -0
- package/fonts/static/sans/FormatSans-800.woff2 +0 -0
- package/fonts/static/sans/FormatSans-900-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSans-900-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSans-900.ttf +0 -0
- package/fonts/static/sans/FormatSans-900.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-100-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-100-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-100.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-100.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-200-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-200-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-200.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-200.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-300-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-300-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-300.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-300.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-400-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-400-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-400.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-400.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-500-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-500-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-500.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-500.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-600-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-600-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-600.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-600.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-700-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-700-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-700.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-700.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-800-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-800-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-800.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-800.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-900-Italic.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-900-Italic.woff2 +0 -0
- package/fonts/static/sans/FormatSansDisplay-900.ttf +0 -0
- package/fonts/static/sans/FormatSansDisplay-900.woff2 +0 -0
- package/fonts/static/serif/FormatSerif-400-Italic.ttf +0 -0
- package/fonts/static/serif/FormatSerif-400-Italic.woff2 +0 -0
- package/fonts/static/serif/FormatSerif-400.ttf +0 -0
- package/fonts/static/serif/FormatSerif-400.woff2 +0 -0
- package/fonts/static/serif/FormatSerif-500-Italic.ttf +0 -0
- package/fonts/static/serif/FormatSerif-500-Italic.woff2 +0 -0
- package/fonts/static/serif/FormatSerif-500.ttf +0 -0
- package/fonts/static/serif/FormatSerif-500.woff2 +0 -0
- package/fonts/static/serif/FormatSerif-600-Italic.ttf +0 -0
- package/fonts/static/serif/FormatSerif-600-Italic.woff2 +0 -0
- package/fonts/static/serif/FormatSerif-600.ttf +0 -0
- package/fonts/static/serif/FormatSerif-600.woff2 +0 -0
- package/fonts/static/serif/FormatSerif-700-Italic.ttf +0 -0
- package/fonts/static/serif/FormatSerif-700-Italic.woff2 +0 -0
- package/fonts/static/serif/FormatSerif-700.ttf +0 -0
- package/fonts/static/serif/FormatSerif-700.woff2 +0 -0
- package/fonts/static/serif/FormatSerif-800-Italic.ttf +0 -0
- package/fonts/static/serif/FormatSerif-800-Italic.woff2 +0 -0
- package/fonts/static/serif/FormatSerif-800.ttf +0 -0
- package/fonts/static/serif/FormatSerif-800.woff2 +0 -0
- package/fonts/static.css +582 -0
- package/fonts/tofu/FormatTofu-Regular.ttf +0 -0
- package/fonts/tofu/format-tofu.woff2 +0 -0
- package/fonts/variable/mono/FormatMono-Italic.ttf +0 -0
- package/fonts/variable/mono/FormatMono-Italic.woff2 +0 -0
- package/fonts/variable/mono/FormatMono.ttf +0 -0
- package/fonts/variable/mono/FormatMono.woff2 +0 -0
- package/fonts/variable/sans/FormatSans-Italic.ttf +0 -0
- package/fonts/variable/sans/FormatSans-Italic.woff2 +0 -0
- package/fonts/variable/sans/FormatSans.ttf +0 -0
- package/fonts/variable/sans/FormatSans.woff2 +0 -0
- package/fonts/variable/serif/FormatSerif-Italic.ttf +0 -0
- package/fonts/variable/serif/FormatSerif-Italic.woff2 +0 -0
- package/fonts/variable/serif/FormatSerif.ttf +0 -0
- package/fonts/variable/serif/FormatSerif.woff2 +0 -0
- package/fonts/variable.css +190 -0
- package/manifest.json +91 -0
- package/package.json +65 -39
- package/index.d.ts +0 -2
- package/index.js +0 -3
package/README.md
CHANGED
|
@@ -1,32 +1,100 @@
|
|
|
1
1
|
# @format.dev/fonts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Format's built-in font families — Sans, Serif, Mono, Emoji, Math, and Tofu — packaged as one source of truth. The previews (Studio, Engine Dyno) and the PDF renderer (press) load their `@font-face` definitions from here, so a document previews with the same typeface the PDF embeds. No metric or optical drift sits between what an author sees and what ships.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
package: it contains no implementation, and installing it gets you an empty
|
|
7
|
-
module.
|
|
5
|
+
This README is the design record for the package; it replaces the earlier planning note.
|
|
8
6
|
|
|
9
|
-
##
|
|
7
|
+
## Two ways to embed a variable font
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
package that already exists. Creating these names ahead of the first release is
|
|
13
|
-
what lets every real release go out through a reviewed, credential-free
|
|
14
|
-
workflow rather than through a token somebody holds.
|
|
9
|
+
The built-in families are **variable** fonts: each carries its full weight range along an axis, in a single file. A PDF embeds that font in one of two ways, and the two differ in size, optical sizing, and how consistently viewers draw the weight.
|
|
15
10
|
|
|
16
|
-
|
|
11
|
+
- **Variable master → Type 3.** The PDF engine cannot subset a variable font, so it re-expresses every glyph as a Type 3 drawing procedure. The result runs larger — roughly 100 KB across a typical six-face document — but keeps the variable's automatic optical sizing, and every glyph draws as a plain filled path, so the weight stays consistent from one viewer to the next.
|
|
12
|
+
- **Static instance → CID subset.** Pinning the axis at one point produces an ordinary static font, which embeds as a compact, subset CID TrueType. The file shrinks, but the instance holds one fixed optical size, and CoreGraphics-based viewers (macOS Preview, Quick Look) stem-darken a real font — heavier strokes than the Type 3 path at the same outline.
|
|
17
13
|
|
|
18
|
-
|
|
14
|
+
No option is small, optically size-aware, _and_ identical across every viewer at once: only Type 3 escapes stem-darkening, and Type 3 is the larger embedding.
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
so `latest` points at this placeholder until Format reaches general
|
|
22
|
-
availability. That is why the deprecation warning is there.
|
|
16
|
+
## Choosing a mode
|
|
23
17
|
|
|
24
|
-
|
|
25
|
-
- General availability is when `latest` moves to a real release.
|
|
18
|
+
A document picks its strategy through the `fonts` prop, which the renderer maps to a variant:
|
|
26
19
|
|
|
27
|
-
|
|
20
|
+
```tsx
|
|
21
|
+
<Document title="Q3 report">…</Document> // static instances → CID subset (default)
|
|
22
|
+
<Document title="Q3 report" fonts="fidelity">…</Document> // variable masters → Type 3
|
|
23
|
+
```
|
|
28
24
|
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
| `fonts` mode | Embedding | File size | Optical sizing | Weight across viewers |
|
|
26
|
+
| --------------------- | ------------------- | --------- | ------------------- | --------------------- |
|
|
27
|
+
| `compact` _(default)_ | static → CID subset | small | one fixed size | varies by viewer |
|
|
28
|
+
| `fidelity` | variable → Type 3 | larger | exact (`opsz auto`) | consistent |
|
|
31
29
|
|
|
32
|
-
|
|
30
|
+
Left unset, a document renders in `compact`: the platform defaults to small, deterministic files and accepts the fixed optical size. The `fidelity` mode is the opt-in for authors who need exact optical sizing and viewer-consistent weight and accept the larger file. Both modes share the same family names, so the choice changes the embedding alone — author markup never changes. The default lives in one place, `DEFAULT_FONT_MODE` in `src/mode.ts`.
|
|
31
|
+
|
|
32
|
+
## Compact PDF text
|
|
33
|
+
|
|
34
|
+
A static cut embeds as a CID subset, and Format writes its text in one of two ways. A run of glyphs becomes one drawing instruction when every glyph lands exactly where its advance width predicts. If a glyph lands even slightly off, the run closes and the writer starts a new one at a fresh position, so the output drifts toward one instruction per glyph and grows to several times its packed size.
|
|
35
|
+
|
|
36
|
+
A glyph lands exactly only when its advance is exact in binary floating point. That advance is the glyph's stored width times the font size over the units-per-em, and the division is exact only when the units-per-em is a power of two and the size is a whole pixel. Mono's source, JetBrains Mono, carries a 1000-unit em, so the division leaves a remainder that accrues on every glyph, and Format writes a separate instruction for nearly every one.
|
|
37
|
+
|
|
38
|
+
The build therefore re-ems any static face that isn't already on a power-of-two em onto a 2048-unit grid (`scale_upem` in `build-fonts.py`). Only the internal grid moves: outlines, advances, kerning, and vertical metrics all scale by the same ratio, so the face looks identical, and the largest coordinate shift lands a few thousandths of a pixel at reading size. The two 1000-em faces, Mono and the copied-through Math face, drop from one instruction per glyph to fully packed runs. Serif (1024) and Sans (2048) are already on a power-of-two em, so they pack as-is and pass through. Emoji and Tofu are colour faces the PDF draws per-glyph regardless, so they pass through too; Tofu is Format's own font, so its em is fixed on a power of two in the tofu package's generator, not here. The variable masters keep their source em, since the `fidelity` path embeds them as Type 3 and writes each glyph separately regardless of the em.
|
|
39
|
+
|
|
40
|
+
## Families
|
|
41
|
+
|
|
42
|
+
The package exposes seven canonical family names through `FONT_FAMILIES`. These names are the public contract: the source typeface behind a name can change; the name stays fixed. They must also match the engine's resolved-name table in `packages/engine/src/utils/fonts.rs`.
|
|
43
|
+
|
|
44
|
+
Only Sans (Adwaita) carries an `opsz` axis, so the static variant ships it in two optical cuts — `Format Sans` for text and `Format Sans Display` for headings — and an author selects Display for large text. Serif (FMT Crimson Pro) and Mono (JetBrains Mono) vary by weight alone. Emoji, Math, and Tofu are already static and pass through unchanged.
|
|
45
|
+
|
|
46
|
+
## Integrating the package
|
|
47
|
+
|
|
48
|
+
`variantForMode()` maps a document's `fonts` value to the variant (it lives in the Node-free `@format.dev/fonts/mode`, so a browser preview can import it without the filesystem helpers), and `fontFaceCss()` builds the `@font-face` rules for a given variant and delivery. `documentFontCss()` does both in one call, for a server consumer that injects its own `<style>`:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { documentFontCss, variantForMode } from '@format.dev/fonts'
|
|
52
|
+
|
|
53
|
+
documentFontCss(doc.fonts, { delivery: 'local' }) // mode → @font-face CSS, in one call
|
|
54
|
+
variantForMode(doc.fonts) // 'fidelity' → 'variable'; anything else (incl. unset) → 'static'
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Delivery decides where the bytes come from:
|
|
58
|
+
|
|
59
|
+
- The previews use `url` delivery — woff2 served over HTTP (`src: url(...)`) — because an end user's machine carries no Format fonts.
|
|
60
|
+
- press uses `local` delivery — installed system fonts (`src: local("<PostScript>")`) — so the cuts ship in the image and one memory-mapped typeface serves every render with no per-request fetch or woff2 decode.
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
// a preview server that injects its own <style>: mode → CSS in one call
|
|
64
|
+
documentFontCss(doc.fonts, { delivery: 'url', baseUrl: '/format-assets/fonts' })
|
|
65
|
+
|
|
66
|
+
// press: cuts installed as system fonts in the image
|
|
67
|
+
documentFontCss(doc.fonts, { delivery: 'local' })
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
For bundlers (Vite), the package also ships the two stylesheets prebuilt — `@format.dev/fonts/static.css` and `@format.dev/fonts/variable.css` — each with `url` delivery and relative paths. A bundler preview (the Engine Dyno) imports the one `variantForMode()` selects, and the bundler fingerprints the referenced woff2. A server preview that controls its own `<style>` (Studio) uses `documentFontCss()` instead, so it can swap the variant in place when a document's mode changes.
|
|
71
|
+
|
|
72
|
+
## Layout
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
sources/ variable masters (Sans/Serif/Mono) + static Emoji/Math/Tofu — the inputs
|
|
76
|
+
manifest.json families, weight ranges and step, optical sizes, emoji unicode-ranges
|
|
77
|
+
scripts/
|
|
78
|
+
build-fonts.py instances, rebrands, re-ems any non-power-of-two face to 2048 (Mono; Math on copy-through), and compresses the masters to woff2 + ttf; copies the static families; writes fonts/fonts.json
|
|
79
|
+
emit-css.mjs writes fonts/static.css and fonts/variable.css from fonts.json
|
|
80
|
+
fonts/ generated, committed: woff2 + ttf, fonts.json descriptors, static.css, variable.css
|
|
81
|
+
src/
|
|
82
|
+
mode.ts FontMode, FontVariant, DEFAULT_FONT_MODE, variantForMode() — pure, no Node imports (the @format.dev/fonts/mode export)
|
|
83
|
+
css.ts fontFaceCss(), documentFontCss(), loadDescriptors(), fontsDir — Node, reads fonts.json
|
|
84
|
+
index.ts FONT_FAMILIES + the public re-exports
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Regenerating the fonts
|
|
88
|
+
|
|
89
|
+
`fonts/` is committed, so an ordinary `pnpm build` (JavaScript only) needs no font toolchain. Regenerate after editing a master or the manifest:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
pip install -r requirements.txt # fonttools + brotli
|
|
93
|
+
pnpm --filter "@format.dev/fonts" build:fonts # rebuilds fonts/ and re-emits the stylesheets
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
CI runs `pnpm --filter @format.dev/fonts check:fonts`, which fails if `fonts/` is stale.
|
|
97
|
+
|
|
98
|
+
## Future: demand-driven instancing
|
|
99
|
+
|
|
100
|
+
The two modes force a choice between small files and exact optical sizing. A third path would remove it: the engine collects the exact `(opsz, wght)` coordinates a document uses, instances those into static subsets at render time, and embeds them as compact CID — small files _and_ per-document optical sizing in one mode. The same stem-darkening as any CID embedding still applies, and the approach needs runtime instancing in the engine, so it stays a future direction rather than a third `fonts` value for now.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const require_mode = require('./mode-DzsrOPvc.cjs');
|
|
2
|
+
let node_fs = require("node:fs");
|
|
3
|
+
let node_url = require("node:url");
|
|
4
|
+
let node_path = require("node:path");
|
|
5
|
+
|
|
6
|
+
//#region src/css.ts
|
|
7
|
+
/** Absolute path to the generated font files. Serve this from a static server. */
|
|
8
|
+
const fontsDir = (0, node_path.join)((0, node_path.dirname)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href)), "..", "fonts");
|
|
9
|
+
/** The descriptor list emitted by `scripts/build-fonts.py` — the single source the names/paths come from. */
|
|
10
|
+
function loadDescriptors() {
|
|
11
|
+
return JSON.parse((0, node_fs.readFileSync)((0, node_path.join)(fontsDir, "fonts.json"), "utf8"));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Generate the `@font-face` CSS for one variant + delivery.
|
|
15
|
+
*
|
|
16
|
+
* `static` emits `Format Sans` (text cuts) + `Format Sans Display` + the Serif/Mono
|
|
17
|
+
* weight palettes; `variable` emits the variable masters (weight ranges, `opsz auto`).
|
|
18
|
+
* Emoji/Math/Tofu are always included. `baseUrl` (the URL `fontsDir` is served under)
|
|
19
|
+
* is required for `url` delivery and ignored for `local`.
|
|
20
|
+
*/
|
|
21
|
+
function fontFaceCss(opts) {
|
|
22
|
+
const variant = opts.variant ?? "static";
|
|
23
|
+
const delivery = opts.delivery ?? "url";
|
|
24
|
+
if (delivery === "url" && !opts.baseUrl) throw new Error("fontFaceCss: `baseUrl` is required for url delivery");
|
|
25
|
+
const base = (opts.baseUrl ?? "").replace(/\/+$/, "");
|
|
26
|
+
return loadDescriptors().filter((d) => d.variant === variant || d.variant === "shared").filter((d) => d.deliveries.includes(delivery)).map((d) => {
|
|
27
|
+
const weight = d.weightRange ? `${d.weightRange[0]} ${d.weightRange[1]}` : `${d.weight}`;
|
|
28
|
+
const range = d.unicodeRange ? `\n\tunicode-range: ${d.unicodeRange};` : "";
|
|
29
|
+
const src = delivery === "local" ? `local("${d.postscriptName}")` : `url('${base}/${d.woff2}') format('woff2')`;
|
|
30
|
+
return [
|
|
31
|
+
"@font-face {",
|
|
32
|
+
`\tfont-family: '${d.family}';`,
|
|
33
|
+
`\tfont-style: ${d.style};`,
|
|
34
|
+
`\tfont-weight: ${weight};`,
|
|
35
|
+
" font-display: block;",
|
|
36
|
+
`\tsrc: ${src};${range}`,
|
|
37
|
+
"}"
|
|
38
|
+
].join("\n");
|
|
39
|
+
}).join("\n");
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Resolve a document's `fonts` mode straight to its `@font-face` CSS — the mode
|
|
43
|
+
* mapping and the CSS generation in one call, so a consumer never composes them
|
|
44
|
+
* itself. press uses `local` delivery (installed system fonts); a preview
|
|
45
|
+
* server that injects its own `<style>` uses `url` delivery with the base URL
|
|
46
|
+
* its woff2 are served under.
|
|
47
|
+
*/
|
|
48
|
+
function documentFontCss(mode, opts) {
|
|
49
|
+
return fontFaceCss({
|
|
50
|
+
variant: require_mode.variantForMode(mode),
|
|
51
|
+
...opts
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/index.ts
|
|
57
|
+
/**
|
|
58
|
+
* Canonical, durable family names. These are the public contract — the source
|
|
59
|
+
* typeface behind each may change, the name does not. Must stay in sync with the
|
|
60
|
+
* engine's resolved-name table (`packages/engine/src/utils/fonts.rs`).
|
|
61
|
+
*
|
|
62
|
+
* `sansDisplay` exists because Sans is the only family with an `opsz` axis; it is
|
|
63
|
+
* a static-variant family (authors opt into it for large text).
|
|
64
|
+
*/
|
|
65
|
+
const FONT_FAMILIES = {
|
|
66
|
+
sans: "Format Sans",
|
|
67
|
+
sansDisplay: "Format Sans Display",
|
|
68
|
+
serif: "Format Serif",
|
|
69
|
+
mono: "Format Mono",
|
|
70
|
+
emoji: "Format Emoji",
|
|
71
|
+
math: "Format Math",
|
|
72
|
+
tofu: "Format Tofu"
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
//#endregion
|
|
76
|
+
exports.DEFAULT_FONT_MODE = require_mode.DEFAULT_FONT_MODE;
|
|
77
|
+
exports.FONT_FAMILIES = FONT_FAMILIES;
|
|
78
|
+
exports.documentFontCss = documentFontCss;
|
|
79
|
+
exports.fontFaceCss = fontFaceCss;
|
|
80
|
+
exports.fontsDir = fontsDir;
|
|
81
|
+
exports.loadDescriptors = loadDescriptors;
|
|
82
|
+
exports.variantForMode = require_mode.variantForMode;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { i as variantForMode, n as FontMode, r as FontVariant, t as DEFAULT_FONT_MODE } from "./mode-BXEMdEsE.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/css.d.ts
|
|
4
|
+
/** Absolute path to the generated font files. Serve this from a static server. */
|
|
5
|
+
declare const fontsDir: string;
|
|
6
|
+
/**
|
|
7
|
+
* How the bytes reach the browser:
|
|
8
|
+
* - `url` — served woff2, `src: url(...)`. For the previews (Studio/Dyno); the
|
|
9
|
+
* end user's machine has no Format fonts installed.
|
|
10
|
+
* - `local` — system TTF, `src: local("<PostScript>")`. For press, where the
|
|
11
|
+
* cuts are installed in the image — one mmap'd, process-shared typeface,
|
|
12
|
+
* no per-request fetch/decode.
|
|
13
|
+
*/
|
|
14
|
+
type FontDelivery = 'url' | 'local';
|
|
15
|
+
interface FontDescriptor {
|
|
16
|
+
/** `static`/`variable` are variant-specific; `shared` (emoji/math/tofu) belongs to both. */
|
|
17
|
+
variant: 'static' | 'variable' | 'shared';
|
|
18
|
+
family: string;
|
|
19
|
+
/** Single CSS weight (static/shared faces). */
|
|
20
|
+
weight?: number;
|
|
21
|
+
/** `[min, max]` weight range (variable faces). */
|
|
22
|
+
weightRange?: [number, number];
|
|
23
|
+
style: 'normal' | 'italic';
|
|
24
|
+
/** Sans-only optical cut; `null`/absent for weight-only families. */
|
|
25
|
+
optical?: 'text' | 'display' | null;
|
|
26
|
+
/** Which deliveries this face supports. */
|
|
27
|
+
deliveries: FontDelivery[];
|
|
28
|
+
/** woff2 path relative to `fontsDir` (present for `url` faces). */
|
|
29
|
+
woff2?: string;
|
|
30
|
+
/** ttf path relative to `fontsDir` (present for `local` faces; install these in the image). */
|
|
31
|
+
ttf?: string;
|
|
32
|
+
/** PostScript name for `src: local(...)` (present for `local` faces). */
|
|
33
|
+
postscriptName?: string;
|
|
34
|
+
unicodeRange?: string;
|
|
35
|
+
}
|
|
36
|
+
/** The descriptor list emitted by `scripts/build-fonts.py` — the single source the names/paths come from. */
|
|
37
|
+
declare function loadDescriptors(): FontDescriptor[];
|
|
38
|
+
/**
|
|
39
|
+
* Generate the `@font-face` CSS for one variant + delivery.
|
|
40
|
+
*
|
|
41
|
+
* `static` emits `Format Sans` (text cuts) + `Format Sans Display` + the Serif/Mono
|
|
42
|
+
* weight palettes; `variable` emits the variable masters (weight ranges, `opsz auto`).
|
|
43
|
+
* Emoji/Math/Tofu are always included. `baseUrl` (the URL `fontsDir` is served under)
|
|
44
|
+
* is required for `url` delivery and ignored for `local`.
|
|
45
|
+
*/
|
|
46
|
+
declare function fontFaceCss(opts: {
|
|
47
|
+
variant?: FontVariant;
|
|
48
|
+
delivery?: FontDelivery;
|
|
49
|
+
baseUrl?: string;
|
|
50
|
+
}): string;
|
|
51
|
+
/**
|
|
52
|
+
* Resolve a document's `fonts` mode straight to its `@font-face` CSS — the mode
|
|
53
|
+
* mapping and the CSS generation in one call, so a consumer never composes them
|
|
54
|
+
* itself. press uses `local` delivery (installed system fonts); a preview
|
|
55
|
+
* server that injects its own `<style>` uses `url` delivery with the base URL
|
|
56
|
+
* its woff2 are served under.
|
|
57
|
+
*/
|
|
58
|
+
declare function documentFontCss(mode: string | null | undefined, opts: {
|
|
59
|
+
delivery: FontDelivery;
|
|
60
|
+
baseUrl?: string;
|
|
61
|
+
}): string;
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/index.d.ts
|
|
64
|
+
/**
|
|
65
|
+
* Canonical, durable family names. These are the public contract — the source
|
|
66
|
+
* typeface behind each may change, the name does not. Must stay in sync with the
|
|
67
|
+
* engine's resolved-name table (`packages/engine/src/utils/fonts.rs`).
|
|
68
|
+
*
|
|
69
|
+
* `sansDisplay` exists because Sans is the only family with an `opsz` axis; it is
|
|
70
|
+
* a static-variant family (authors opt into it for large text).
|
|
71
|
+
*/
|
|
72
|
+
declare const FONT_FAMILIES: {
|
|
73
|
+
readonly sans: "Format Sans";
|
|
74
|
+
readonly sansDisplay: "Format Sans Display";
|
|
75
|
+
readonly serif: "Format Serif";
|
|
76
|
+
readonly mono: "Format Mono";
|
|
77
|
+
readonly emoji: "Format Emoji";
|
|
78
|
+
readonly math: "Format Math";
|
|
79
|
+
readonly tofu: "Format Tofu";
|
|
80
|
+
};
|
|
81
|
+
type FontFamilyKey = keyof typeof FONT_FAMILIES;
|
|
82
|
+
//#endregion
|
|
83
|
+
export { DEFAULT_FONT_MODE, FONT_FAMILIES, type FontDelivery, type FontDescriptor, FontFamilyKey, type FontMode, type FontVariant, documentFontCss, fontFaceCss, fontsDir, loadDescriptors, variantForMode };
|
|
84
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/css.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;cAMa;AAAb;AAUA;AAEA;AAuBA;AAYA;AAuCA;;;KA5EY,YAAA;ACHC,UDKI,cAAA,CCGP;EAEE;;;;;;;;;;;cDOC;;;;;;;;;;iBAWG,eAAA,CAAA,GAAmB;;;;;;;;;iBAYnB,WAAA;YAA8B;aAAwB;;;;;;;;;;iBAuCtD,eAAA;YAEG;;;;;AA9EnB;AAEA;AAuBA;AAYA;AAuCA;;;;AC/Ea,cAAA,aAQH,EAAA;EAEE,SAAA,IAAA,EAAA,aAA6B;;;;;;;;KAA7B,aAAA,gBAA6B"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { i as variantForMode, n as FontMode, r as FontVariant, t as DEFAULT_FONT_MODE } from "./mode-CtSoe83u.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/css.d.ts
|
|
4
|
+
/** Absolute path to the generated font files. Serve this from a static server. */
|
|
5
|
+
declare const fontsDir: string;
|
|
6
|
+
/**
|
|
7
|
+
* How the bytes reach the browser:
|
|
8
|
+
* - `url` — served woff2, `src: url(...)`. For the previews (Studio/Dyno); the
|
|
9
|
+
* end user's machine has no Format fonts installed.
|
|
10
|
+
* - `local` — system TTF, `src: local("<PostScript>")`. For press, where the
|
|
11
|
+
* cuts are installed in the image — one mmap'd, process-shared typeface,
|
|
12
|
+
* no per-request fetch/decode.
|
|
13
|
+
*/
|
|
14
|
+
type FontDelivery = 'url' | 'local';
|
|
15
|
+
interface FontDescriptor {
|
|
16
|
+
/** `static`/`variable` are variant-specific; `shared` (emoji/math/tofu) belongs to both. */
|
|
17
|
+
variant: 'static' | 'variable' | 'shared';
|
|
18
|
+
family: string;
|
|
19
|
+
/** Single CSS weight (static/shared faces). */
|
|
20
|
+
weight?: number;
|
|
21
|
+
/** `[min, max]` weight range (variable faces). */
|
|
22
|
+
weightRange?: [number, number];
|
|
23
|
+
style: 'normal' | 'italic';
|
|
24
|
+
/** Sans-only optical cut; `null`/absent for weight-only families. */
|
|
25
|
+
optical?: 'text' | 'display' | null;
|
|
26
|
+
/** Which deliveries this face supports. */
|
|
27
|
+
deliveries: FontDelivery[];
|
|
28
|
+
/** woff2 path relative to `fontsDir` (present for `url` faces). */
|
|
29
|
+
woff2?: string;
|
|
30
|
+
/** ttf path relative to `fontsDir` (present for `local` faces; install these in the image). */
|
|
31
|
+
ttf?: string;
|
|
32
|
+
/** PostScript name for `src: local(...)` (present for `local` faces). */
|
|
33
|
+
postscriptName?: string;
|
|
34
|
+
unicodeRange?: string;
|
|
35
|
+
}
|
|
36
|
+
/** The descriptor list emitted by `scripts/build-fonts.py` — the single source the names/paths come from. */
|
|
37
|
+
declare function loadDescriptors(): FontDescriptor[];
|
|
38
|
+
/**
|
|
39
|
+
* Generate the `@font-face` CSS for one variant + delivery.
|
|
40
|
+
*
|
|
41
|
+
* `static` emits `Format Sans` (text cuts) + `Format Sans Display` + the Serif/Mono
|
|
42
|
+
* weight palettes; `variable` emits the variable masters (weight ranges, `opsz auto`).
|
|
43
|
+
* Emoji/Math/Tofu are always included. `baseUrl` (the URL `fontsDir` is served under)
|
|
44
|
+
* is required for `url` delivery and ignored for `local`.
|
|
45
|
+
*/
|
|
46
|
+
declare function fontFaceCss(opts: {
|
|
47
|
+
variant?: FontVariant;
|
|
48
|
+
delivery?: FontDelivery;
|
|
49
|
+
baseUrl?: string;
|
|
50
|
+
}): string;
|
|
51
|
+
/**
|
|
52
|
+
* Resolve a document's `fonts` mode straight to its `@font-face` CSS — the mode
|
|
53
|
+
* mapping and the CSS generation in one call, so a consumer never composes them
|
|
54
|
+
* itself. press uses `local` delivery (installed system fonts); a preview
|
|
55
|
+
* server that injects its own `<style>` uses `url` delivery with the base URL
|
|
56
|
+
* its woff2 are served under.
|
|
57
|
+
*/
|
|
58
|
+
declare function documentFontCss(mode: string | null | undefined, opts: {
|
|
59
|
+
delivery: FontDelivery;
|
|
60
|
+
baseUrl?: string;
|
|
61
|
+
}): string;
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/index.d.ts
|
|
64
|
+
/**
|
|
65
|
+
* Canonical, durable family names. These are the public contract — the source
|
|
66
|
+
* typeface behind each may change, the name does not. Must stay in sync with the
|
|
67
|
+
* engine's resolved-name table (`packages/engine/src/utils/fonts.rs`).
|
|
68
|
+
*
|
|
69
|
+
* `sansDisplay` exists because Sans is the only family with an `opsz` axis; it is
|
|
70
|
+
* a static-variant family (authors opt into it for large text).
|
|
71
|
+
*/
|
|
72
|
+
declare const FONT_FAMILIES: {
|
|
73
|
+
readonly sans: "Format Sans";
|
|
74
|
+
readonly sansDisplay: "Format Sans Display";
|
|
75
|
+
readonly serif: "Format Serif";
|
|
76
|
+
readonly mono: "Format Mono";
|
|
77
|
+
readonly emoji: "Format Emoji";
|
|
78
|
+
readonly math: "Format Math";
|
|
79
|
+
readonly tofu: "Format Tofu";
|
|
80
|
+
};
|
|
81
|
+
type FontFamilyKey = keyof typeof FONT_FAMILIES;
|
|
82
|
+
//#endregion
|
|
83
|
+
export { DEFAULT_FONT_MODE, FONT_FAMILIES, type FontDelivery, type FontDescriptor, FontFamilyKey, type FontMode, type FontVariant, documentFontCss, fontFaceCss, fontsDir, loadDescriptors, variantForMode };
|
|
84
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/css.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;cAMa;AAAb;AAUA;AAEA;AAuBA;AAYA;AAuCA;;;KA5EY,YAAA;ACHC,UDKI,cAAA,CCGP;EAEE;;;;;;;;;;;cDOC;;;;;;;;;;iBAWG,eAAA,CAAA,GAAmB;;;;;;;;;iBAYnB,WAAA;YAA8B;aAAwB;;;;;;;;;;iBAuCtD,eAAA;YAEG;;;;;AA9EnB;AAEA;AAuBA;AAYA;AAuCA;;;;AC/Ea,cAAA,aAQH,EAAA;EAEE,SAAA,IAAA,EAAA,aAA6B;;;;;;;;KAA7B,aAAA,gBAA6B"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { n as variantForMode, t as DEFAULT_FONT_MODE } from "./mode-B8fVsgsV.mjs";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
|
|
6
|
+
//#region src/css.ts
|
|
7
|
+
/** Absolute path to the generated font files. Serve this from a static server. */
|
|
8
|
+
const fontsDir = join(dirname(fileURLToPath(import.meta.url)), "..", "fonts");
|
|
9
|
+
/** The descriptor list emitted by `scripts/build-fonts.py` — the single source the names/paths come from. */
|
|
10
|
+
function loadDescriptors() {
|
|
11
|
+
return JSON.parse(readFileSync(join(fontsDir, "fonts.json"), "utf8"));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Generate the `@font-face` CSS for one variant + delivery.
|
|
15
|
+
*
|
|
16
|
+
* `static` emits `Format Sans` (text cuts) + `Format Sans Display` + the Serif/Mono
|
|
17
|
+
* weight palettes; `variable` emits the variable masters (weight ranges, `opsz auto`).
|
|
18
|
+
* Emoji/Math/Tofu are always included. `baseUrl` (the URL `fontsDir` is served under)
|
|
19
|
+
* is required for `url` delivery and ignored for `local`.
|
|
20
|
+
*/
|
|
21
|
+
function fontFaceCss(opts) {
|
|
22
|
+
const variant = opts.variant ?? "static";
|
|
23
|
+
const delivery = opts.delivery ?? "url";
|
|
24
|
+
if (delivery === "url" && !opts.baseUrl) throw new Error("fontFaceCss: `baseUrl` is required for url delivery");
|
|
25
|
+
const base = (opts.baseUrl ?? "").replace(/\/+$/, "");
|
|
26
|
+
return loadDescriptors().filter((d) => d.variant === variant || d.variant === "shared").filter((d) => d.deliveries.includes(delivery)).map((d) => {
|
|
27
|
+
const weight = d.weightRange ? `${d.weightRange[0]} ${d.weightRange[1]}` : `${d.weight}`;
|
|
28
|
+
const range = d.unicodeRange ? `\n\tunicode-range: ${d.unicodeRange};` : "";
|
|
29
|
+
const src = delivery === "local" ? `local("${d.postscriptName}")` : `url('${base}/${d.woff2}') format('woff2')`;
|
|
30
|
+
return [
|
|
31
|
+
"@font-face {",
|
|
32
|
+
`\tfont-family: '${d.family}';`,
|
|
33
|
+
`\tfont-style: ${d.style};`,
|
|
34
|
+
`\tfont-weight: ${weight};`,
|
|
35
|
+
" font-display: block;",
|
|
36
|
+
`\tsrc: ${src};${range}`,
|
|
37
|
+
"}"
|
|
38
|
+
].join("\n");
|
|
39
|
+
}).join("\n");
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Resolve a document's `fonts` mode straight to its `@font-face` CSS — the mode
|
|
43
|
+
* mapping and the CSS generation in one call, so a consumer never composes them
|
|
44
|
+
* itself. press uses `local` delivery (installed system fonts); a preview
|
|
45
|
+
* server that injects its own `<style>` uses `url` delivery with the base URL
|
|
46
|
+
* its woff2 are served under.
|
|
47
|
+
*/
|
|
48
|
+
function documentFontCss(mode, opts) {
|
|
49
|
+
return fontFaceCss({
|
|
50
|
+
variant: variantForMode(mode),
|
|
51
|
+
...opts
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/index.ts
|
|
57
|
+
/**
|
|
58
|
+
* Canonical, durable family names. These are the public contract — the source
|
|
59
|
+
* typeface behind each may change, the name does not. Must stay in sync with the
|
|
60
|
+
* engine's resolved-name table (`packages/engine/src/utils/fonts.rs`).
|
|
61
|
+
*
|
|
62
|
+
* `sansDisplay` exists because Sans is the only family with an `opsz` axis; it is
|
|
63
|
+
* a static-variant family (authors opt into it for large text).
|
|
64
|
+
*/
|
|
65
|
+
const FONT_FAMILIES = {
|
|
66
|
+
sans: "Format Sans",
|
|
67
|
+
sansDisplay: "Format Sans Display",
|
|
68
|
+
serif: "Format Serif",
|
|
69
|
+
mono: "Format Mono",
|
|
70
|
+
emoji: "Format Emoji",
|
|
71
|
+
math: "Format Math",
|
|
72
|
+
tofu: "Format Tofu"
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
//#endregion
|
|
76
|
+
export { DEFAULT_FONT_MODE, FONT_FAMILIES, documentFontCss, fontFaceCss, fontsDir, loadDescriptors, variantForMode };
|
|
77
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/css.ts","../src/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { dirname, join } from 'node:path'\nimport { variantForMode, type FontVariant } from './mode.js'\n\n/** Absolute path to the generated font files. Serve this from a static server. */\nexport const fontsDir = join(dirname(fileURLToPath(import.meta.url)), '..', 'fonts')\n\n/**\n * How the bytes reach the browser:\n * - `url` — served woff2, `src: url(...)`. For the previews (Studio/Dyno); the\n * end user's machine has no Format fonts installed.\n * - `local` — system TTF, `src: local(\"<PostScript>\")`. For press, where the\n * cuts are installed in the image — one mmap'd, process-shared typeface,\n * no per-request fetch/decode.\n */\nexport type FontDelivery = 'url' | 'local'\n\nexport interface FontDescriptor {\n\t/** `static`/`variable` are variant-specific; `shared` (emoji/math/tofu) belongs to both. */\n\tvariant: 'static' | 'variable' | 'shared'\n\tfamily: string\n\t/** Single CSS weight (static/shared faces). */\n\tweight?: number\n\t/** `[min, max]` weight range (variable faces). */\n\tweightRange?: [number, number]\n\tstyle: 'normal' | 'italic'\n\t/** Sans-only optical cut; `null`/absent for weight-only families. */\n\toptical?: 'text' | 'display' | null\n\t/** Which deliveries this face supports. */\n\tdeliveries: FontDelivery[]\n\t/** woff2 path relative to `fontsDir` (present for `url` faces). */\n\twoff2?: string\n\t/** ttf path relative to `fontsDir` (present for `local` faces; install these in the image). */\n\tttf?: string\n\t/** PostScript name for `src: local(...)` (present for `local` faces). */\n\tpostscriptName?: string\n\tunicodeRange?: string\n}\n\n/** The descriptor list emitted by `scripts/build-fonts.py` — the single source the names/paths come from. */\nexport function loadDescriptors(): FontDescriptor[] {\n\treturn JSON.parse(readFileSync(join(fontsDir, 'fonts.json'), 'utf8')) as FontDescriptor[]\n}\n\n/**\n * Generate the `@font-face` CSS for one variant + delivery.\n *\n * `static` emits `Format Sans` (text cuts) + `Format Sans Display` + the Serif/Mono\n * weight palettes; `variable` emits the variable masters (weight ranges, `opsz auto`).\n * Emoji/Math/Tofu are always included. `baseUrl` (the URL `fontsDir` is served under)\n * is required for `url` delivery and ignored for `local`.\n */\nexport function fontFaceCss(opts: { variant?: FontVariant; delivery?: FontDelivery; baseUrl?: string }): string {\n\tconst variant = opts.variant ?? 'static'\n\tconst delivery = opts.delivery ?? 'url'\n\n\tif (delivery === 'url' && !opts.baseUrl) {\n\t\tthrow new Error('fontFaceCss: `baseUrl` is required for url delivery')\n\t}\n\tconst base = (opts.baseUrl ?? '').replace(/\\/+$/, '')\n\n\tconst faces = loadDescriptors()\n\t\t.filter(d => d.variant === variant || d.variant === 'shared')\n\t\t.filter(d => d.deliveries.includes(delivery))\n\n\treturn faces\n\t\t.map(d => {\n\t\t\tconst weight = d.weightRange ? `${d.weightRange[0]} ${d.weightRange[1]}` : `${d.weight}`\n\t\t\tconst range = d.unicodeRange ? `\\n\\tunicode-range: ${d.unicodeRange};` : ''\n\t\t\tconst src = delivery === 'local' ? `local(\"${d.postscriptName}\")` : `url('${base}/${d.woff2}') format('woff2')`\n\n\t\t\treturn [\n\t\t\t\t'@font-face {',\n\t\t\t\t`\\tfont-family: '${d.family}';`,\n\t\t\t\t`\\tfont-style: ${d.style};`,\n\t\t\t\t`\\tfont-weight: ${weight};`,\n\t\t\t\t'\\tfont-display: block;',\n\t\t\t\t`\\tsrc: ${src};${range}`,\n\t\t\t\t'}'\n\t\t\t].join('\\n')\n\t\t})\n\t\t.join('\\n')\n}\n\n/**\n * Resolve a document's `fonts` mode straight to its `@font-face` CSS — the mode\n * mapping and the CSS generation in one call, so a consumer never composes them\n * itself. press uses `local` delivery (installed system fonts); a preview\n * server that injects its own `<style>` uses `url` delivery with the base URL\n * its woff2 are served under.\n */\nexport function documentFontCss(\n\tmode: string | null | undefined,\n\topts: { delivery: FontDelivery; baseUrl?: string }\n): string {\n\treturn fontFaceCss({ variant: variantForMode(mode), ...opts })\n}\n","export { fontFaceCss, documentFontCss, loadDescriptors, fontsDir } from './css.js'\nexport type { FontDelivery, FontDescriptor } from './css.js'\nexport { variantForMode, DEFAULT_FONT_MODE } from './mode.js'\nexport type { FontVariant, FontMode } from './mode.js'\n\n/**\n * Canonical, durable family names. These are the public contract — the source\n * typeface behind each may change, the name does not. Must stay in sync with the\n * engine's resolved-name table (`packages/engine/src/utils/fonts.rs`).\n *\n * `sansDisplay` exists because Sans is the only family with an `opsz` axis; it is\n * a static-variant family (authors opt into it for large text).\n */\nexport const FONT_FAMILIES = {\n\tsans: 'Format Sans',\n\tsansDisplay: 'Format Sans Display',\n\tserif: 'Format Serif',\n\tmono: 'Format Mono',\n\temoji: 'Format Emoji',\n\tmath: 'Format Math',\n\ttofu: 'Format Tofu'\n} as const\n\nexport type FontFamilyKey = keyof typeof FONT_FAMILIES\n"],"mappings":";;;;;;;AAMA,MAAa,WAAW,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC,EAAE,MAAM,QAAQ;;AAmCpF,SAAgB,kBAAoC;AACnD,QAAO,KAAK,MAAM,aAAa,KAAK,UAAU,aAAa,EAAE,OAAO,CAAC;;;;;;;;;;AAWtE,SAAgB,YAAY,MAAoF;CAC/G,MAAM,UAAU,KAAK,WAAW;CAChC,MAAM,WAAW,KAAK,YAAY;AAElC,KAAI,aAAa,SAAS,CAAC,KAAK,QAC/B,OAAM,IAAI,MAAM,sDAAsD;CAEvE,MAAM,QAAQ,KAAK,WAAW,IAAI,QAAQ,QAAQ,GAAG;AAMrD,QAJc,iBAAiB,CAC7B,QAAO,MAAK,EAAE,YAAY,WAAW,EAAE,YAAY,SAAS,CAC5D,QAAO,MAAK,EAAE,WAAW,SAAS,SAAS,CAAC,CAG5C,KAAI,MAAK;EACT,MAAM,SAAS,EAAE,cAAc,GAAG,EAAE,YAAY,GAAG,GAAG,EAAE,YAAY,OAAO,GAAG,EAAE;EAChF,MAAM,QAAQ,EAAE,eAAe,sBAAsB,EAAE,aAAa,KAAK;EACzE,MAAM,MAAM,aAAa,UAAU,UAAU,EAAE,eAAe,MAAM,QAAQ,KAAK,GAAG,EAAE,MAAM;AAE5F,SAAO;GACN;GACA,mBAAmB,EAAE,OAAO;GAC5B,iBAAiB,EAAE,MAAM;GACzB,kBAAkB,OAAO;GACzB;GACA,UAAU,IAAI,GAAG;GACjB;GACA,CAAC,KAAK,KAAK;GACX,CACD,KAAK,KAAK;;;;;;;;;AAUb,SAAgB,gBACf,MACA,MACS;AACT,QAAO,YAAY;EAAE,SAAS,eAAe,KAAK;EAAE,GAAG;EAAM,CAAC;;;;;;;;;;;;;ACnF/D,MAAa,gBAAgB;CAC5B,MAAM;CACN,aAAa;CACb,OAAO;CACP,MAAM;CACN,OAAO;CACP,MAAM;CACN,MAAM;CACN"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/mode.ts
|
|
2
|
+
/**
|
|
3
|
+
* The mode an unset or unrecognised `fonts` value resolves to. Compact embeds
|
|
4
|
+
* static instances (CID subset): smaller files, one fixed optical size. Most
|
|
5
|
+
* documents take this default. Fidelity is the opt-in for exact optical sizing
|
|
6
|
+
* and viewer-consistent weight, at a larger file.
|
|
7
|
+
*/
|
|
8
|
+
const DEFAULT_FONT_MODE = "compact";
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the author-facing mode to an embedding variant. Fidelity embeds the
|
|
11
|
+
* variable masters (Type 3: exact optical sizing, identical weight in every
|
|
12
|
+
* viewer); compact embeds static instances (CID subset: smaller files, fixed
|
|
13
|
+
* optical size). An absent or unknown value resolves to DEFAULT_FONT_MODE.
|
|
14
|
+
*
|
|
15
|
+
* This is the one definition of the mapping and the default, so the previews,
|
|
16
|
+
* press, and Studio can never disagree on either. It has no Node imports, so
|
|
17
|
+
* a browser preview can call it without pulling the filesystem-backed helpers in
|
|
18
|
+
* `css.ts`.
|
|
19
|
+
*/
|
|
20
|
+
function variantForMode(mode) {
|
|
21
|
+
return (mode === "fidelity" || mode === "compact" ? mode : DEFAULT_FONT_MODE) === "fidelity" ? "variable" : "static";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
export { variantForMode as n, DEFAULT_FONT_MODE as t };
|
|
26
|
+
//# sourceMappingURL=mode-B8fVsgsV.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mode-B8fVsgsV.mjs","names":["DEFAULT_FONT_MODE: FontMode"],"sources":["../src/mode.ts"],"sourcesContent":["export type FontVariant = 'static' | 'variable'\n\n/** Author-facing font strategy, set on `<Document fonts>`. */\nexport type FontMode = 'fidelity' | 'compact'\n\n/**\n * The mode an unset or unrecognised `fonts` value resolves to. Compact embeds\n * static instances (CID subset): smaller files, one fixed optical size. Most\n * documents take this default. Fidelity is the opt-in for exact optical sizing\n * and viewer-consistent weight, at a larger file.\n */\nexport const DEFAULT_FONT_MODE: FontMode = 'compact'\n\n/**\n * Resolve the author-facing mode to an embedding variant. Fidelity embeds the\n * variable masters (Type 3: exact optical sizing, identical weight in every\n * viewer); compact embeds static instances (CID subset: smaller files, fixed\n * optical size). An absent or unknown value resolves to DEFAULT_FONT_MODE.\n *\n * This is the one definition of the mapping and the default, so the previews,\n * press, and Studio can never disagree on either. It has no Node imports, so\n * a browser preview can call it without pulling the filesystem-backed helpers in\n * `css.ts`.\n */\nexport function variantForMode(mode?: string | null): FontVariant {\n\tconst resolved = mode === 'fidelity' || mode === 'compact' ? mode : DEFAULT_FONT_MODE\n\treturn resolved === 'fidelity' ? 'variable' : 'static'\n}\n"],"mappings":";;;;;;;AAWA,MAAaA,oBAA8B;;;;;;;;;;;;AAa3C,SAAgB,eAAe,MAAmC;AAEjE,SADiB,SAAS,cAAc,SAAS,YAAY,OAAO,uBAChD,aAAa,aAAa"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/mode.d.ts
|
|
2
|
+
type FontVariant = 'static' | 'variable';
|
|
3
|
+
/** Author-facing font strategy, set on `<Document fonts>`. */
|
|
4
|
+
type FontMode = 'fidelity' | 'compact';
|
|
5
|
+
/**
|
|
6
|
+
* The mode an unset or unrecognised `fonts` value resolves to. Compact embeds
|
|
7
|
+
* static instances (CID subset): smaller files, one fixed optical size. Most
|
|
8
|
+
* documents take this default. Fidelity is the opt-in for exact optical sizing
|
|
9
|
+
* and viewer-consistent weight, at a larger file.
|
|
10
|
+
*/
|
|
11
|
+
declare const DEFAULT_FONT_MODE: FontMode;
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the author-facing mode to an embedding variant. Fidelity embeds the
|
|
14
|
+
* variable masters (Type 3: exact optical sizing, identical weight in every
|
|
15
|
+
* viewer); compact embeds static instances (CID subset: smaller files, fixed
|
|
16
|
+
* optical size). An absent or unknown value resolves to DEFAULT_FONT_MODE.
|
|
17
|
+
*
|
|
18
|
+
* This is the one definition of the mapping and the default, so the previews,
|
|
19
|
+
* press, and Studio can never disagree on either. It has no Node imports, so
|
|
20
|
+
* a browser preview can call it without pulling the filesystem-backed helpers in
|
|
21
|
+
* `css.ts`.
|
|
22
|
+
*/
|
|
23
|
+
declare function variantForMode(mode?: string | null): FontVariant;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { variantForMode as i, FontMode as n, FontVariant as r, DEFAULT_FONT_MODE as t };
|
|
26
|
+
//# sourceMappingURL=mode-BXEMdEsE.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mode-BXEMdEsE.d.cts","names":[],"sources":["../src/mode.ts"],"sourcesContent":[],"mappings":";KAAY,WAAA;AAAZ;AAGY,KAAA,QAAA,GAAQ,UAAA,GAAA,SAAA;AAQpB;AAaA;;;;;cAba,mBAAmB;;;;;;;;;;;;iBAahB,cAAA,wBAAsC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/mode.d.ts
|
|
2
|
+
type FontVariant = 'static' | 'variable';
|
|
3
|
+
/** Author-facing font strategy, set on `<Document fonts>`. */
|
|
4
|
+
type FontMode = 'fidelity' | 'compact';
|
|
5
|
+
/**
|
|
6
|
+
* The mode an unset or unrecognised `fonts` value resolves to. Compact embeds
|
|
7
|
+
* static instances (CID subset): smaller files, one fixed optical size. Most
|
|
8
|
+
* documents take this default. Fidelity is the opt-in for exact optical sizing
|
|
9
|
+
* and viewer-consistent weight, at a larger file.
|
|
10
|
+
*/
|
|
11
|
+
declare const DEFAULT_FONT_MODE: FontMode;
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the author-facing mode to an embedding variant. Fidelity embeds the
|
|
14
|
+
* variable masters (Type 3: exact optical sizing, identical weight in every
|
|
15
|
+
* viewer); compact embeds static instances (CID subset: smaller files, fixed
|
|
16
|
+
* optical size). An absent or unknown value resolves to DEFAULT_FONT_MODE.
|
|
17
|
+
*
|
|
18
|
+
* This is the one definition of the mapping and the default, so the previews,
|
|
19
|
+
* press, and Studio can never disagree on either. It has no Node imports, so
|
|
20
|
+
* a browser preview can call it without pulling the filesystem-backed helpers in
|
|
21
|
+
* `css.ts`.
|
|
22
|
+
*/
|
|
23
|
+
declare function variantForMode(mode?: string | null): FontVariant;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { variantForMode as i, FontMode as n, FontVariant as r, DEFAULT_FONT_MODE as t };
|
|
26
|
+
//# sourceMappingURL=mode-CtSoe83u.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mode-CtSoe83u.d.mts","names":[],"sources":["../src/mode.ts"],"sourcesContent":[],"mappings":";KAAY,WAAA;AAAZ;AAGY,KAAA,QAAA,GAAQ,UAAA,GAAA,SAAA;AAQpB;AAaA;;;;;cAba,mBAAmB;;;;;;;;;;;;iBAahB,cAAA,wBAAsC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/mode.ts
|
|
3
|
+
/**
|
|
4
|
+
* The mode an unset or unrecognised `fonts` value resolves to. Compact embeds
|
|
5
|
+
* static instances (CID subset): smaller files, one fixed optical size. Most
|
|
6
|
+
* documents take this default. Fidelity is the opt-in for exact optical sizing
|
|
7
|
+
* and viewer-consistent weight, at a larger file.
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_FONT_MODE = "compact";
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the author-facing mode to an embedding variant. Fidelity embeds the
|
|
12
|
+
* variable masters (Type 3: exact optical sizing, identical weight in every
|
|
13
|
+
* viewer); compact embeds static instances (CID subset: smaller files, fixed
|
|
14
|
+
* optical size). An absent or unknown value resolves to DEFAULT_FONT_MODE.
|
|
15
|
+
*
|
|
16
|
+
* This is the one definition of the mapping and the default, so the previews,
|
|
17
|
+
* press, and Studio can never disagree on either. It has no Node imports, so
|
|
18
|
+
* a browser preview can call it without pulling the filesystem-backed helpers in
|
|
19
|
+
* `css.ts`.
|
|
20
|
+
*/
|
|
21
|
+
function variantForMode(mode) {
|
|
22
|
+
return (mode === "fidelity" || mode === "compact" ? mode : DEFAULT_FONT_MODE) === "fidelity" ? "variable" : "static";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
//#endregion
|
|
26
|
+
Object.defineProperty(exports, 'DEFAULT_FONT_MODE', {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
get: function () {
|
|
29
|
+
return DEFAULT_FONT_MODE;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
Object.defineProperty(exports, 'variantForMode', {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
get: function () {
|
|
35
|
+
return variantForMode;
|
|
36
|
+
}
|
|
37
|
+
});
|
package/dist/mode.cjs
ADDED
package/dist/mode.d.cts
ADDED
package/dist/mode.d.mts
ADDED
package/dist/mode.mjs
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|