@weasel-js/font 1.9.4 → 1.10.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 +13 -3
- package/dist/{chunk-ERSHQYJL.js → chunk-NM6K6M4Y.js} +460 -242
- package/dist/chunk-NM6K6M4Y.js.map +1 -0
- package/dist/index.d.ts +142 -77
- package/dist/index.js +148 -3
- package/dist/index.js.map +1 -1
- package/dist/{test-seams-BC5Ykf4D.d.ts → test-seams-BvULFsbo.d.ts} +170 -230
- package/dist/test-seams.d.ts +1 -1
- package/dist/test-seams.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-ERSHQYJL.js.map +0 -1
|
@@ -29,8 +29,43 @@ interface GlyphTextureSink {
|
|
|
29
29
|
upload(id: string, source: TexSource): string;
|
|
30
30
|
uploadR8(id: string, width: number, height: number, data: Uint8Array): void;
|
|
31
31
|
subImageR8(id: string, x: number, y: number, w: number, h: number, data: Uint8Array): void;
|
|
32
|
+
/** Create a texture of premultiplied RGBA texels that are a color, not a
|
|
33
|
+
* field, so it may be minified through mipmaps. */
|
|
34
|
+
uploadRGBA(id: string, width: number, height: number, data: Uint8Array): void;
|
|
35
|
+
subImageRGBA(id: string, x: number, y: number, w: number, h: number, data: Uint8Array): void;
|
|
32
36
|
}
|
|
33
37
|
|
|
38
|
+
/**
|
|
39
|
+
* The dynamic glyph atlas's pages: their CPU-side bytes, and bringing each
|
|
40
|
+
* renderer's texture of one up to date. Two sets: distance fields, one byte a
|
|
41
|
+
* texel, and color glyphs, four.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
interface DynamicPage {
|
|
45
|
+
data: Uint8Array;
|
|
46
|
+
/** Bumps on every write; each write appends a patch with seq = version. */
|
|
47
|
+
version: number;
|
|
48
|
+
patches: {
|
|
49
|
+
seq: number;
|
|
50
|
+
x: number;
|
|
51
|
+
y: number;
|
|
52
|
+
w: number;
|
|
53
|
+
h: number;
|
|
54
|
+
}[];
|
|
55
|
+
}
|
|
56
|
+
/** Texture-cache key for a dynamic field page (parallel to `textureCacheKey`). */
|
|
57
|
+
declare function dynamicPageTextureId(page: number): string;
|
|
58
|
+
/** Texture-cache key for a page of color glyphs. */
|
|
59
|
+
declare function colorPageTextureId(page: number): string;
|
|
60
|
+
/** Bring `cache`'s copy of field page `pageIndex` up to date: full R8 upload
|
|
61
|
+
* the first time, `texSubImage2D` patches after. Returns false if the page
|
|
62
|
+
* doesn't exist yet. */
|
|
63
|
+
declare function syncDynamicPageTexture(cache: GlyphTextureSink, pageIndex: number): boolean;
|
|
64
|
+
/** The same for a page of color glyphs, whose texels are premultiplied RGBA. */
|
|
65
|
+
declare function syncColorPageTexture(cache: GlyphTextureSink, pageIndex: number): boolean;
|
|
66
|
+
/** @internal test seam — inspect CPU-side field pages. */
|
|
67
|
+
declare function _getPagesForTests(): readonly DynamicPage[];
|
|
68
|
+
|
|
34
69
|
/**
|
|
35
70
|
* Canvas-2D glyph rasterizer for the dynamic SDF atlas. `fillText` is the
|
|
36
71
|
* only web-platform route to *installed* machine fonts (no outline access),
|
|
@@ -43,28 +78,18 @@ interface GlyphTextureSink {
|
|
|
43
78
|
* The bitmap includes a PAD border on all four sides so the SDF field has
|
|
44
79
|
* room to fall off. Blank glyphs (e.g. space) return width/height 0.
|
|
45
80
|
*
|
|
46
|
-
* On BAKE_SIZE
|
|
47
|
-
* against a direct `fillText` of the same glyph at each display size
|
|
48
|
-
* sub-pixel registration searched out so it measures fidelity and not
|
|
49
|
-
* alignment. Mean per-pixel coverage error, averaged over A/S/e/W/8:
|
|
81
|
+
* On BAKE_SIZE: mean per-pixel coverage error of the reconstructed field
|
|
82
|
+
* against a direct `fillText` of the same glyph at each display size:
|
|
50
83
|
*
|
|
51
84
|
* display px 12 16 24 32 48 64 96 128
|
|
52
85
|
* error .096 .083 .059 .039 .024 .023 .037 .049
|
|
53
86
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* the
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* The 12–16px end is the largest divergence, and it is not undersampling —
|
|
62
|
-
* 3×3 supersampling the reconstruction roughly halves the error at 24–48px
|
|
63
|
-
* but recovers almost nothing at 12–16px (.096 → .089, .083 → .068). What is
|
|
64
|
-
* left there is a hinted rasterizer placing stems on the pixel grid, which no
|
|
65
|
-
* size-independent field can encode. So there is nothing to buy with more
|
|
66
|
-
* taps, a bigger bake, or mipmaps (which a packed atlas cannot have anyway —
|
|
67
|
-
* mip levels blend across glyph rects).
|
|
87
|
+
* The error is minimized *at* the bake size, so raising BAKE_SIZE does not
|
|
88
|
+
* improve small text — it moves the sweet spot away from the 12–32px range
|
|
89
|
+
* and makes it worse. The 12–16px error is not undersampling: supersampling
|
|
90
|
+
* the reconstruction recovers almost nothing there. What is left is a hinted
|
|
91
|
+
* rasterizer placing stems on the pixel grid, which no size-independent field
|
|
92
|
+
* can encode, so more taps, a bigger bake, or mipmaps buy nothing.
|
|
68
93
|
*/
|
|
69
94
|
|
|
70
95
|
interface FaceMetrics$1 {
|
|
@@ -79,15 +104,29 @@ interface RasterizedGlyph {
|
|
|
79
104
|
top: number;
|
|
80
105
|
advance: number;
|
|
81
106
|
}
|
|
107
|
+
/** A glyph in its own colors. Placed as a {@link RasterizedGlyph} is. */
|
|
108
|
+
interface RasterizedColorGlyph {
|
|
109
|
+
width: number;
|
|
110
|
+
height: number;
|
|
111
|
+
/** Premultiplied RGBA, four bytes a texel. */
|
|
112
|
+
rgba: Uint8ClampedArray;
|
|
113
|
+
left: number;
|
|
114
|
+
top: number;
|
|
115
|
+
advance: number;
|
|
116
|
+
}
|
|
82
117
|
interface GlyphRasterizer {
|
|
83
118
|
faceMetrics(family: string, weight: number, style: FontStyle): FaceMetrics$1;
|
|
84
119
|
rasterize(family: string, weight: number, style: FontStyle, codepoint: number): RasterizedGlyph;
|
|
120
|
+
/** `text`, one grapheme cluster, as the color glyph the platform draws for
|
|
121
|
+
* it, or null when it draws none: no ink, or ink in the fill color alone. */
|
|
122
|
+
rasterizeColor?(family: string, weight: number, style: FontStyle, text: string): RasterizedColorGlyph | null;
|
|
85
123
|
}
|
|
86
124
|
|
|
87
125
|
/**
|
|
88
126
|
* DynamicGlyphAtlas — runtime single-channel SDF glyphs for canvas-sourced
|
|
89
127
|
* (installed machine) fonts. TinySDF technique: canvas fillText at 48 px →
|
|
90
|
-
* Euclidean distance transform → shelf-packed R8 pages (1024², max 4).
|
|
128
|
+
* Euclidean distance transform → shelf-packed R8 pages (1024², max 4). When
|
|
129
|
+
* the pages fill they are emptied whole and the text on screen bakes again.
|
|
91
130
|
*
|
|
92
131
|
* Baked MSDF atlases always win: this tier only serves families registered
|
|
93
132
|
* via `registerCanvasFont` that have no baked entry (see resolveFontVariant).
|
|
@@ -97,13 +136,13 @@ interface GlyphRasterizer {
|
|
|
97
136
|
* baked atlases. A char's advance is valid immediately (measureText); its
|
|
98
137
|
* atlas rect fills in when the bake lands (width 0 / page -1 until then, so
|
|
99
138
|
* layout advances the pen but emits no quad).
|
|
139
|
+
*
|
|
140
|
+
* An emoji is the one glyph kept as the canvas drew it: `requestCluster`
|
|
141
|
+
* rasterizes a whole grapheme cluster in its own colors onto a second set of
|
|
142
|
+
* pages, RGBA, that fill and empty with the field pages.
|
|
100
143
|
*/
|
|
101
144
|
|
|
102
|
-
/**
|
|
103
|
-
* deferring the rest to a later flush. Caps the frame cost of text that
|
|
104
|
-
* suddenly needs many new glyphs; the leftovers arrive a tick later and
|
|
105
|
-
* notify through `onGlyphReady`. */
|
|
106
|
-
declare const DEFAULT_BAKE_BUDGET = 16;
|
|
145
|
+
/** A face whose glyphs are rasterized on demand into a shared atlas page, rather than shipped as a prebuilt one. */
|
|
107
146
|
interface DynamicFace {
|
|
108
147
|
family: string;
|
|
109
148
|
weight: number;
|
|
@@ -113,18 +152,13 @@ interface DynamicFace {
|
|
|
113
152
|
/** Char record for `cp`, measured on first request (advance always valid
|
|
114
153
|
* immediately; atlas rect fills in when the bake lands). */
|
|
115
154
|
requestGlyph(cp: number): BmFontChar;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
x: number;
|
|
124
|
-
y: number;
|
|
125
|
-
w: number;
|
|
126
|
-
h: number;
|
|
127
|
-
}[];
|
|
155
|
+
/**
|
|
156
|
+
* The color glyph the platform draws for the grapheme cluster `text`,
|
|
157
|
+
* measured on first request as `requestGlyph`'s is, or null when it draws
|
|
158
|
+
* none. Its record is marked `color` and its rect is on a color page. It is
|
|
159
|
+
* not in `font.charMap`, which holds one code point each.
|
|
160
|
+
*/
|
|
161
|
+
requestCluster(text: string): BmFontChar | null;
|
|
128
162
|
}
|
|
129
163
|
/** One family served by the dynamic canvas-SDF tier. See `listCanvasFonts`. */
|
|
130
164
|
interface CanvasFontEntry {
|
|
@@ -149,75 +183,49 @@ declare function registerCanvasFont(family: string): void;
|
|
|
149
183
|
* so returning to `'canvas'` makes it `true` again.
|
|
150
184
|
* - never enrolled → `false`, including under `'canvas'` (that policy
|
|
151
185
|
* enrolls lazily, on the first miss).
|
|
152
|
-
*
|
|
153
|
-
* The membership reading would answer `true` for an auto-enrolled family
|
|
154
|
-
* under `'substitute'` / `'none'`, where nothing routes to this tier — a
|
|
155
|
-
* caller predicting what renders would be told the opposite of the truth.
|
|
156
|
-
* Mirrors `listFonts`, which reports the baked registry alone for the same
|
|
157
|
-
* reason: "enrolled" is not "will render".
|
|
158
186
|
*/
|
|
159
187
|
declare function isCanvasFont(family: string): boolean;
|
|
160
188
|
/**
|
|
161
189
|
* Every family the dynamic canvas-SDF tier will serve *right now* — the
|
|
162
190
|
* enumeration companion to `isCanvasFont`, which answers one family at a
|
|
163
191
|
* time. Same "service, not membership" rule: an auto-enrolled family appears
|
|
164
|
-
* only while the `'canvas'` policy is in force.
|
|
165
|
-
*
|
|
166
|
-
* Exists so a consumer can offer these families in a font picker.
|
|
167
|
-
* `listFonts` reports the baked registry alone, so without this the only
|
|
168
|
-
* ways to present canvas-served families were to hard-code a list beside the
|
|
169
|
-
* `registerCanvasFont` calls or to omit them — a UI inventing its own answer
|
|
170
|
-
* about what will render.
|
|
192
|
+
* only while the `'canvas'` policy is in force. `listFonts` reports the baked
|
|
193
|
+
* registry alone.
|
|
171
194
|
*
|
|
172
195
|
* `enrollment` distinguishes a consumer's explicit `registerCanvasFont` from
|
|
173
196
|
* a lazy enrollment the policy made on a miss; both render identically, but
|
|
174
197
|
* only the explicit one survives a policy change.
|
|
175
198
|
*/
|
|
176
199
|
declare function listCanvasFonts(): readonly CanvasFontEntry[];
|
|
177
|
-
/** Remove a canvas family. Its faces are dropped;
|
|
178
|
-
*
|
|
200
|
+
/** Remove a canvas family. Its faces are dropped; the room its glyphs took
|
|
201
|
+
* comes back when the pages next fill and are emptied. */
|
|
179
202
|
declare function unregisterCanvasFont(family: string): void;
|
|
180
|
-
/**
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
declare function syncDynamicPageTexture(cache: GlyphTextureSink, pageIndex: number): boolean;
|
|
203
|
+
/**
|
|
204
|
+
* Measure `family`'s canvas faces again, or every family's when none is named.
|
|
205
|
+
*
|
|
206
|
+
* The tier keeps each face's metrics and each glyph's shape and advance from
|
|
207
|
+
* the first time it is asked. A web font that finishes loading after that is
|
|
208
|
+
* picked up from `document.fonts` without this call; a face the document
|
|
209
|
+
* never announces, one loaded before it was added, needs it.
|
|
210
|
+
*/
|
|
211
|
+
declare function refreshCanvasFont(family?: string): void;
|
|
190
212
|
/** @internal test seam — inject a fake rasterizer (jsdom has no canvas
|
|
191
213
|
* metrics). Pass null to restore the lazy default. */
|
|
192
214
|
declare function __setGlyphRasterizerForTests(r: GlyphRasterizer | null): void;
|
|
193
|
-
/** @internal test seam — inspect CPU-side pages. */
|
|
194
|
-
declare function _getPagesForTests(): readonly DynamicPage[];
|
|
195
215
|
/** @internal test seam — clear all dynamic-font state. */
|
|
196
216
|
declare function _resetDynamicFontsForTests(): void;
|
|
197
217
|
|
|
198
218
|
/**
|
|
199
219
|
* The parsed-font seam for the outline tier.
|
|
200
220
|
*
|
|
201
|
-
* A face
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
* zoom, and position, which is the whole reason the tier exists: tessellate
|
|
206
|
-
* once, transform per instance.
|
|
207
|
-
*
|
|
208
|
-
* ### Why `d` and not a command stream
|
|
209
|
-
*
|
|
210
|
-
* `@weasel-js/font` is a Tier A leaf — `@weasel-js/core` depends on it and
|
|
211
|
-
* never the reverse (see `leaf-purity.test.ts`), so this package cannot name
|
|
212
|
-
* core's `PolygonPath`. The alternative to a string would be re-declaring
|
|
213
|
-
* core's `PATH_M`/`PATH_L`/… opcodes over here and trusting two packages to
|
|
214
|
-
* keep the same numbering forever. SVG `d` is the kit's documented language
|
|
215
|
-
* for geometry crossing a boundary (`docs/conventions.md`), core already
|
|
216
|
-
* ships `pathFromD`, and a glyph outline is exactly the case it describes.
|
|
221
|
+
* A face describes a glyph as **SVG path data in em space**: one unit is one
|
|
222
|
+
* em, y grows downward, and the origin sits on the baseline at the glyph's pen
|
|
223
|
+
* position. That is independent of size, zoom, and position, so a glyph is
|
|
224
|
+
* tessellated once and transformed per instance.
|
|
217
225
|
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
226
|
+
* `d` and not a command stream: `@weasel-js/core` depends on this package and
|
|
227
|
+
* never the reverse, so it cannot name core's `PolygonPath`, and SVG `d` is
|
|
228
|
+
* the kit's language for geometry crossing a boundary (`docs/conventions.md`).
|
|
221
229
|
*/
|
|
222
230
|
|
|
223
231
|
/** A parsed font face, viewed only as a source of glyph outlines. See the
|
|
@@ -242,15 +250,12 @@ interface OutlineFace {
|
|
|
242
250
|
faceMetrics?: FaceMetrics;
|
|
243
251
|
/**
|
|
244
252
|
* Em-space SVG path data for `cp`, or `null` when this face has no glyph
|
|
245
|
-
* for the codepoint *or* the glyph has no contours (a space).
|
|
246
|
-
* mean the same thing to a caller: there is nothing here to tessellate, so
|
|
247
|
-
* fall through to whatever tier would have drawn it.
|
|
253
|
+
* for the codepoint *or* the glyph has no contours (a space).
|
|
248
254
|
*/
|
|
249
255
|
glyphD(cp: number): string | null;
|
|
250
256
|
/**
|
|
251
257
|
* Advance width for `cp` in em units, or `null` when the face has no glyph
|
|
252
|
-
* for it.
|
|
253
|
-
* and no contours, and layout needs the advance either way.
|
|
258
|
+
* for it. A space has an advance and no contours.
|
|
254
259
|
*/
|
|
255
260
|
advanceOf(cp: number): number | null;
|
|
256
261
|
/** Kerning adjustment between two codepoints, in em units — negative pulls
|
|
@@ -258,20 +263,14 @@ interface OutlineFace {
|
|
|
258
263
|
kernOf(left: number, right: number): number;
|
|
259
264
|
}
|
|
260
265
|
/**
|
|
261
|
-
* Turn font file bytes into a face.
|
|
262
|
-
* dynamically imports its parser
|
|
263
|
-
* matters — and because a caller may want to hand back a face that finishes
|
|
264
|
-
* initializing off-thread.
|
|
266
|
+
* Turn font file bytes into a face. May be async: the default implementation
|
|
267
|
+
* dynamically imports its parser.
|
|
265
268
|
*/
|
|
266
269
|
type OutlineParser = (bytes: ArrayBuffer) => OutlineFace | Promise<OutlineFace>;
|
|
267
270
|
|
|
268
271
|
/**
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
* Variants are keyed by (family, weight, style). registerFont() takes a
|
|
272
|
-
* FontVariant alongside the family and the two URLs; the registry stores
|
|
273
|
-
* entries in a two-level Map so resolveFontVariant() can iterate a family's
|
|
274
|
-
* variants for the fallback chain.
|
|
272
|
+
* The atlas font registry, keyed by (family, weight, style), and the fallback
|
|
273
|
+
* chain that resolves a request against it.
|
|
275
274
|
*/
|
|
276
275
|
|
|
277
276
|
/** A registered face: its parsed metrics and the atlas image to sample. */
|
|
@@ -296,8 +295,7 @@ declare function getFont(family: string, weight?: number, style?: FontStyle): Fo
|
|
|
296
295
|
/**
|
|
297
296
|
* The decoration and script metrics of an exact `(family, weight, style)` —
|
|
298
297
|
* the atlas's when one is registered, else a loaded outline face's. Like
|
|
299
|
-
* {@link getFont}, it walks no fallback chain and enrolls no canvas font
|
|
300
|
-
* which suits a caller holding a CSS font name rather than a layout result.
|
|
298
|
+
* {@link getFont}, it walks no fallback chain and enrolls no canvas font.
|
|
301
299
|
* An idle outline face starts loading, as it would for layout.
|
|
302
300
|
* A run already resolved for layout reads `faceMetricsOf(result)` instead.
|
|
303
301
|
*/
|
|
@@ -312,17 +310,15 @@ interface RegisteredFont {
|
|
|
312
310
|
}[];
|
|
313
311
|
}
|
|
314
312
|
/**
|
|
315
|
-
* Enumerate the registry
|
|
316
|
-
*
|
|
317
|
-
* the output is stable enough to assert against.
|
|
313
|
+
* Enumerate the registry. Families come back in registration order; variants
|
|
314
|
+
* sorted by weight, then style.
|
|
318
315
|
*/
|
|
319
316
|
declare function listFonts(): readonly RegisteredFont[];
|
|
320
317
|
/**
|
|
321
318
|
* The weights `family` has a real face for, on the atlas tier or the outline
|
|
322
|
-
* tier, in either style
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
* rasterizes whatever weight it is asked for, so it has no list to report.
|
|
319
|
+
* tier, in either style. Ascending and deduplicated; empty for a family
|
|
320
|
+
* nothing registered. An outline face whose load failed is left out. The
|
|
321
|
+
* canvas tier is not consulted: it rasterizes whatever weight it is asked for.
|
|
326
322
|
*/
|
|
327
323
|
declare function listFontWeights(family: string): readonly number[];
|
|
328
324
|
/** Options for {@link registerFont}. */
|
|
@@ -342,8 +338,7 @@ interface RegisterFontOptions {
|
|
|
342
338
|
* present is a no-op.
|
|
343
339
|
*
|
|
344
340
|
* Eager by default: both files are fetched now. With `{ lazy: true }` they are
|
|
345
|
-
* fetched the first time text asks for the family
|
|
346
|
-
* the critical path of anything that draws no text.
|
|
341
|
+
* fetched the first time text asks for the family.
|
|
347
342
|
*/
|
|
348
343
|
declare function registerFont(family: string, variant: FontVariant, metricsUrl: string, atlasUrl: string, opts?: RegisterFontOptions): Promise<void>;
|
|
349
344
|
/** One face text is set in, the way a render asks for it — what
|
|
@@ -367,21 +362,18 @@ interface FontRequest {
|
|
|
367
362
|
* would draw instead when the requested one has no atlas; and the variant's
|
|
368
363
|
* outline face when one is registered.
|
|
369
364
|
*
|
|
370
|
-
* Starts each lazily declared variant and joins any fetch already running.
|
|
371
365
|
* Resolves once they have all landed; a registered variant counts as loaded.
|
|
372
366
|
* Rejects when a load fails, or for a family name `registerFont` never saw —
|
|
373
|
-
* a
|
|
374
|
-
* tiers serve families no atlas was registered for.
|
|
367
|
+
* a {@link FontRequest} for such a family is not an error, since the canvas
|
|
368
|
+
* and outline tiers serve families no atlas was registered for.
|
|
375
369
|
*/
|
|
376
370
|
declare function warmFonts(fonts?: readonly (string | FontRequest)[]): Promise<void>;
|
|
377
371
|
/**
|
|
378
372
|
* Could a registration that has not landed yet still serve this request?
|
|
379
373
|
* True while any `registerFont` for the family is fetching — the within-family
|
|
380
374
|
* chain may resolve to whichever variant it brings — or while an outline face
|
|
381
|
-
* registered for this exact variant has not finished loading.
|
|
382
|
-
*
|
|
383
|
-
* A face that resolves to nothing right now is either missing or merely late;
|
|
384
|
-
* this tells the two apart, so "not registered" is reported only when it is.
|
|
375
|
+
* registered for this exact variant has not finished loading. Tells a face
|
|
376
|
+
* that is missing from one that is merely late.
|
|
385
377
|
*/
|
|
386
378
|
declare function fontPending(family: string, weight?: number, style?: FontStyle): boolean;
|
|
387
379
|
/**
|
|
@@ -405,9 +397,8 @@ interface ResolveResult {
|
|
|
405
397
|
* including `family`, when the cross-family policy substituted a default.
|
|
406
398
|
*
|
|
407
399
|
* This is the atlas identity: pass all three to `getFont` /
|
|
408
|
-
* `textureCacheKey` and the lookup hits.
|
|
409
|
-
*
|
|
410
|
-
* resolves to no atlas at all and paints nothing.
|
|
400
|
+
* `textureCacheKey` and the lookup hits. Keying a draw on the *requested*
|
|
401
|
+
* family resolves to no atlas at all and paints nothing.
|
|
411
402
|
*
|
|
412
403
|
* The synthetic flags describe the gap between requested and resolved for
|
|
413
404
|
* shader-side compensation. When `entry` is null, these mirror the
|
|
@@ -435,9 +426,8 @@ interface ResolveResult {
|
|
|
435
426
|
outlineFace?: OutlineFace;
|
|
436
427
|
/**
|
|
437
428
|
* Set when the requested family was not registered and the fallback policy
|
|
438
|
-
* substituted a different one
|
|
439
|
-
*
|
|
440
|
-
* wonder why the family control did nothing.
|
|
429
|
+
* substituted a different one, so a UI can say "Inter — not loaded, showing
|
|
430
|
+
* Roboto".
|
|
441
431
|
*/
|
|
442
432
|
substituted?: {
|
|
443
433
|
requested: string;
|
|
@@ -455,23 +445,14 @@ declare function resolveFontVariant(family: string, weight: number, style: FontS
|
|
|
455
445
|
* Escalate a *single codepoint* to the dynamic tier when the atlas that
|
|
456
446
|
* resolved for the run has no glyph for it.
|
|
457
447
|
*
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
* — an em dash, a curly quote, anything outside the subset. The dynamic tier
|
|
462
|
-
* rasterizes on demand from installed fonts and can serve exactly those.
|
|
448
|
+
* A baked MSDF atlas covers a fixed charset, so a run it serves can still
|
|
449
|
+
* contain a character it never baked — an em dash, a curly quote. The dynamic
|
|
450
|
+
* tier rasterizes on demand from installed fonts and can serve those.
|
|
463
451
|
*
|
|
464
452
|
* Returns a canvas-tier `ResolveResult` whose `dynamicFace` the caller drives
|
|
465
|
-
* with `requestGlyph(cp)`, or `null` when escalation isn't available:
|
|
466
|
-
*
|
|
467
|
-
*
|
|
468
|
-
* another tier per codepoint would undo that, so it doesn't.
|
|
469
|
-
* - No canvas to rasterize into (SSR, a jsdom test without one) makes the
|
|
470
|
-
* dynamic tier constructible-but-broken; `getDynamicFace` throws and this
|
|
471
|
-
* reports the miss instead of taking the caller down with it.
|
|
472
|
-
*
|
|
473
|
-
* Cheap to call per missing codepoint: faces are cached by variant and glyphs
|
|
474
|
-
* by codepoint, so a repeat is two map lookups.
|
|
453
|
+
* with `requestGlyph(cp)`, or `null` when escalation isn't available: under
|
|
454
|
+
* policy `'none'`, which documents a miss as a *hard* miss, or with no canvas
|
|
455
|
+
* to rasterize into (SSR, a jsdom test without one).
|
|
475
456
|
*/
|
|
476
457
|
declare function resolveGlyphFallback(family: string, weight: number, style: FontStyle): ResolveResult | null;
|
|
477
458
|
|
|
@@ -481,10 +462,8 @@ declare function resolveGlyphFallback(family: string, weight: number, style: Fon
|
|
|
481
462
|
* baseline to its top edge (y grows down) and a script's `shift` positive
|
|
482
463
|
* for a rise.
|
|
483
464
|
*
|
|
484
|
-
* Both glyph tiers
|
|
485
|
-
*
|
|
486
|
-
* {@link faceMetricsFromTables}, so one font yields one set of numbers
|
|
487
|
-
* whichever tier ends up serving a run.
|
|
465
|
+
* Both glyph tiers derive the record through {@link faceMetricsFromTables},
|
|
466
|
+
* so one font yields one set of numbers whichever tier serves a run.
|
|
488
467
|
*/
|
|
489
468
|
|
|
490
469
|
/** One rule: the top edge's distance below the baseline, and its weight. */
|
|
@@ -552,12 +531,10 @@ interface FaceMetricTables {
|
|
|
552
531
|
* positive downward.
|
|
553
532
|
*
|
|
554
533
|
* The OpenType rule: `OS/2` `sTypoAscender`/`sTypoDescender` when the font
|
|
555
|
-
* sets `USE_TYPO_METRICS`, otherwise `hhea`.
|
|
556
|
-
* than a typographer's because the DOM edit overlay stands in for the canvas
|
|
557
|
-
* and can only ever get the browser's numbers. Firefox, and every engine on
|
|
534
|
+
* sets `USE_TYPO_METRICS`, otherwise `hhea`. Firefox, and every engine on
|
|
558
535
|
* Linux, follow it; Chromium and WebKit on macOS read `hhea` even when the
|
|
559
|
-
* flag is set, and the overlay's measured baseline correction
|
|
560
|
-
* difference there.
|
|
536
|
+
* flag is set, and the DOM edit overlay's measured baseline correction
|
|
537
|
+
* absorbs the difference there.
|
|
561
538
|
*
|
|
562
539
|
* Falls back to the typo pair, then the win pair, when `hhea` is absent or
|
|
563
540
|
* zero.
|
|
@@ -572,16 +549,14 @@ declare function verticalMetricsFromTables(t: FaceMetricTables): {
|
|
|
572
549
|
* `post.underlinePosition` and `OS/2.yStrikeoutPosition` are the rule's top
|
|
573
550
|
* edge, positive above the baseline. `ySubscriptYOffset` is positive
|
|
574
551
|
* *downward*, unlike its superscript twin. A zero weight or size is a font
|
|
575
|
-
* that left the field blank,
|
|
576
|
-
*
|
|
577
|
-
* `OS/2` table older than version 2 reads, since it has no heights at all.
|
|
552
|
+
* that left the field blank, so that entry is dropped and the caller's
|
|
553
|
+
* default applies.
|
|
578
554
|
*/
|
|
579
555
|
declare function faceMetricsFromTables(t: FaceMetricTables): FaceMetrics | undefined;
|
|
580
556
|
/**
|
|
581
557
|
* The face metrics behind a resolved run — the outline face's on the outline
|
|
582
|
-
* tier, the atlas's on the atlas tier. The canvas tier
|
|
583
|
-
*
|
|
584
|
-
* and callers fall back to their defaults.
|
|
558
|
+
* tier, the atlas's on the atlas tier. The canvas tier has none, so callers
|
|
559
|
+
* fall back to their defaults.
|
|
585
560
|
*/
|
|
586
561
|
declare function faceMetricsOf(result: ResolveResult): FaceMetrics | undefined;
|
|
587
562
|
|
|
@@ -616,6 +591,9 @@ interface BmFontChar {
|
|
|
616
591
|
yoffset: number;
|
|
617
592
|
xadvance: number;
|
|
618
593
|
page: number;
|
|
594
|
+
/** Set on a glyph whose atlas rect holds its own colors, on a page of the
|
|
595
|
+
* dynamic tier's color set: an emoji. It is painted as it is, untinted. */
|
|
596
|
+
color?: true;
|
|
619
597
|
}
|
|
620
598
|
/** A kerning pair: `amount` px to add to the advance when `second` follows
|
|
621
599
|
* `first`. Codepoints, not glyph indices. */
|
|
@@ -716,13 +694,9 @@ declare function _resetFallbackForTests(): void;
|
|
|
716
694
|
* sans-serif` in the page resolves to Helvetica. Anything the DOM sets in
|
|
717
695
|
* place of canvas text — the text edit overlay above all — asks here instead
|
|
718
696
|
* of writing the name through. The answer names a private `FontFace` loaded
|
|
719
|
-
* from the
|
|
720
|
-
* while that loads.
|
|
721
|
-
*
|
|
722
|
-
* The font file is the one `registerFontOutlines` holds; that registry is the
|
|
723
|
-
* kit's record of which faces have real bytes behind them. An atlas-only
|
|
724
|
-
* family has none, so the DOM gets the browser's own face for the name and a
|
|
725
|
-
* one-time warning says which registration would fix it.
|
|
697
|
+
* from the file `registerFontOutlines` holds, with the family itself as
|
|
698
|
+
* fallback while that loads. An atlas-only family has no file, so the DOM gets
|
|
699
|
+
* the browser's own face for the name.
|
|
726
700
|
*/
|
|
727
701
|
|
|
728
702
|
/**
|
|
@@ -733,15 +707,13 @@ declare function _resetFallbackForTests(): void;
|
|
|
733
707
|
* the `'canvas'` fallback policy) comes back unchanged: the DOM already
|
|
734
708
|
* resolves the name to the same face.
|
|
735
709
|
* - A family drawn from a baked atlas or from outlines comes back as a private
|
|
736
|
-
* face built from its `registerFontOutlines` file
|
|
737
|
-
* and style
|
|
738
|
-
* the way the canvas does — followed by `family` as the fallback.
|
|
710
|
+
* face built from its `registerFontOutlines` file, in every registered
|
|
711
|
+
* weight and style, followed by `family` as the fallback.
|
|
739
712
|
* - Under the `'substitute'` policy, the face is the substitute's.
|
|
740
713
|
*
|
|
741
714
|
* Starts loading the face on first call and never waits for it; ask
|
|
742
|
-
* {@link cssFontFamilyLoading} for that.
|
|
743
|
-
*
|
|
744
|
-
* — was built when they did. Outside a browser, `family` comes back unchanged.
|
|
715
|
+
* {@link cssFontFamilyLoading} for that. Outside a browser, `family` comes
|
|
716
|
+
* back unchanged.
|
|
745
717
|
*/
|
|
746
718
|
declare function cssFontFamily(family: string, variant?: FontVariant): string;
|
|
747
719
|
/**
|
|
@@ -765,57 +737,35 @@ declare function _resetDomFacesForTests(): void;
|
|
|
765
737
|
*
|
|
766
738
|
* ### What this tier is for
|
|
767
739
|
*
|
|
768
|
-
* A distance field is a *sampled* representation
|
|
769
|
-
* generated from outlines and holds up well; the dynamic canvas-SDF tier
|
|
740
|
+
* A distance field is a *sampled* representation: the dynamic canvas-SDF tier
|
|
770
741
|
* (`../dynamic/`) reconstructs its field from a 48px raster, so magnifying it
|
|
771
|
-
* past a few times the bake size exposes the raster as contour wobble
|
|
772
|
-
*
|
|
773
|
-
*
|
|
774
|
-
*
|
|
775
|
-
*
|
|
776
|
-
*
|
|
777
|
-
*
|
|
778
|
-
* the glyph's outline, and hand it to the path renderer the kit already has.
|
|
779
|
-
* Exact at every zoom, and — because a glyph becomes an ordinary path — it
|
|
780
|
-
* takes strokes and non-solid fills for free.
|
|
781
|
-
*
|
|
782
|
-
* Below the threshold the atlas still wins, and not narrowly: outlines carry
|
|
783
|
-
* no hinting and no stem darkening, so body text at 12–16px rendered from
|
|
784
|
-
* them looks *worse* than a platform rasterizer that puts stems on the pixel
|
|
785
|
-
* grid. Two tiers with a size threshold is the standard hybrid, not a
|
|
786
|
-
* compromise.
|
|
742
|
+
* past a few times the bake size exposes the raster as contour wobble. Above a
|
|
743
|
+
* size threshold the glyph's outline goes to the path renderer instead, which
|
|
744
|
+
* is exact at every zoom and takes strokes and non-solid fills.
|
|
745
|
+
*
|
|
746
|
+
* Below the threshold the atlas still wins: outlines carry no hinting and no
|
|
747
|
+
* stem darkening, so body text at 12–16px rendered from them looks *worse*
|
|
748
|
+
* than a platform rasterizer that puts stems on the pixel grid.
|
|
787
749
|
*
|
|
788
750
|
* ### Metric neutrality (the load-bearing invariant)
|
|
789
751
|
*
|
|
790
752
|
* This tier replaces how a glyph is *painted*, never where it sits. Advances,
|
|
791
|
-
* kerning, line breaking and baselines all keep coming from whichever tier
|
|
792
|
-
* resolved the run
|
|
793
|
-
*
|
|
794
|
-
*
|
|
795
|
-
* and `measureTextBounds` / `textLineBoxes` need to know nothing about
|
|
796
|
-
* outlines at all.
|
|
797
|
-
*
|
|
798
|
-
* The cost is that a face whose real advances disagree with the atlas's
|
|
799
|
-
* inherits the atlas's. For the bundled Inter that is exact (the subset ships
|
|
800
|
-
* from the same source the atlas was baked from), and for a machine font the
|
|
801
|
-
* outline bytes and the canvas metrics come from the same file.
|
|
753
|
+
* kerning, line breaking, and baselines all keep coming from whichever tier
|
|
754
|
+
* resolved the run, so zooming past the threshold cannot reflow text. The cost
|
|
755
|
+
* is that a face whose real advances disagree with the atlas's inherits the
|
|
756
|
+
* atlas's.
|
|
802
757
|
*
|
|
803
758
|
* ### Availability
|
|
804
759
|
*
|
|
805
|
-
*
|
|
806
|
-
*
|
|
807
|
-
*
|
|
808
|
-
*
|
|
809
|
-
* ladder to SDF. A family must never render *nothing* because the outline
|
|
810
|
-
* tier could not get bytes.
|
|
760
|
+
* Bytes arrive asynchronously, and in the local case behind a permission the
|
|
761
|
+
* user can refuse, so `glyphOutline` answers `null` until a face is loaded and
|
|
762
|
+
* forever if it failed, and the caller falls back to SDF. A family must never
|
|
763
|
+
* render *nothing* because the outline tier could not get bytes.
|
|
811
764
|
*/
|
|
812
765
|
|
|
813
766
|
/**
|
|
814
767
|
* Where a face's bytes come from. A URL is fetched; a buffer or blob is used
|
|
815
|
-
* as-is; a thunk is called at first use
|
|
816
|
-
* results be registered eagerly and read lazily — the permission prompt and
|
|
817
|
-
* the (potentially many megabytes of) blob only happen for a family the
|
|
818
|
-
* document actually sets text in.
|
|
768
|
+
* as-is; a thunk is called at first use.
|
|
819
769
|
*/
|
|
820
770
|
type OutlineSource = string | ArrayBuffer | Blob | (() => ArrayBuffer | Blob | Promise<ArrayBuffer | Blob>);
|
|
821
771
|
/** Which face within a family to register outlines for. Defaults to weight
|
|
@@ -855,13 +805,12 @@ declare const fontOutlineRegistry: Reflection<OutlineFaceInfo>;
|
|
|
855
805
|
*
|
|
856
806
|
* Registration is cheap and synchronous: nothing is fetched, and the parser
|
|
857
807
|
* is not even imported, until a frame actually asks for a glyph from this
|
|
858
|
-
* face.
|
|
808
|
+
* face.
|
|
859
809
|
*
|
|
860
810
|
* The variant must match exactly what the layout tier resolved — this is
|
|
861
811
|
* deliberately *not* a fallback chain. Painting 400-weight outlines at
|
|
862
|
-
* 700-weight advances
|
|
863
|
-
*
|
|
864
|
-
* back down the ladder instead, which is always safe.
|
|
812
|
+
* 700-weight advances looks worse than the SDF it replaced; a miss falls back
|
|
813
|
+
* to SDF instead.
|
|
865
814
|
*/
|
|
866
815
|
declare function registerFontOutlines(family: string, variant: OutlineVariant, source: OutlineSource, opts?: OutlineFontOptions): void;
|
|
867
816
|
/** Drop a registration. Glyphs already handed out stay valid — they are
|
|
@@ -869,9 +818,7 @@ declare function registerFontOutlines(family: string, variant: OutlineVariant, s
|
|
|
869
818
|
declare function unregisterFontOutlines(family: string, variant?: OutlineVariant): void;
|
|
870
819
|
/**
|
|
871
820
|
* Is an outline face registered for this exact variant? Answers `true` while
|
|
872
|
-
* the bytes are still loading
|
|
873
|
-
* outlines", which is what a caller deciding whether to *ask* wants; use
|
|
874
|
-
* `outlineStatus` for the narrower "can it right now".
|
|
821
|
+
* the bytes are still loading; use `outlineStatus` for "can it right now".
|
|
875
822
|
*/
|
|
876
823
|
declare function hasFontOutlines(family: string, weight?: number, style?: FontStyle): boolean;
|
|
877
824
|
/** Load state of a registered face; `null` when nothing is registered. */
|
|
@@ -881,41 +828,34 @@ declare function outlineStatus(family: string, weight?: number, style?: FontStyl
|
|
|
881
828
|
declare function listFontOutlines(): readonly OutlineFaceInfo[];
|
|
882
829
|
/**
|
|
883
830
|
* Em-space SVG path data for one glyph of a registered outline face, or `null`
|
|
884
|
-
* when
|
|
831
|
+
* when this tier cannot serve it — no registration, bytes still loading, load
|
|
832
|
+
* failed, no such glyph, or a glyph with no contours.
|
|
885
833
|
*
|
|
886
|
-
* Synchronous and non-throwing
|
|
887
|
-
*
|
|
888
|
-
*
|
|
889
|
-
* arrives. Results are cached per codepoint.
|
|
834
|
+
* Synchronous and non-throwing: a face that has not been parsed yet starts
|
|
835
|
+
* loading and answers `null` for now, so a renderer can call this every frame
|
|
836
|
+
* and fall through to another text tier until the outline arrives.
|
|
890
837
|
*/
|
|
891
838
|
declare function glyphOutline(family: string, weight: number, style: FontStyle, cp: number): string | null;
|
|
892
839
|
/**
|
|
893
840
|
* Metrics for a registered outline face, or `null` when this tier cannot
|
|
894
841
|
* supply them yet — nothing registered, bytes still loading, or a failed load.
|
|
895
842
|
*
|
|
896
|
-
* The face is the *only* metrics source for a family that has no atlas
|
|
897
|
-
*
|
|
898
|
-
*
|
|
899
|
-
* to the atlas long before anything asks here, so registering outlines still
|
|
900
|
-
* cannot move text that was already rendering.
|
|
843
|
+
* The face is the *only* metrics source for a family that has no atlas. A
|
|
844
|
+
* family that has an atlas resolves to the atlas before anything asks here,
|
|
845
|
+
* so registering outlines cannot move text that was already rendering.
|
|
901
846
|
*
|
|
902
|
-
* Starts the load on a first call and answers `null` for now,
|
|
903
|
-
* `glyphOutline
|
|
904
|
-
* frame after the bytes land.
|
|
847
|
+
* Starts the load on a first call and answers `null` for now, like
|
|
848
|
+
* `glyphOutline`.
|
|
905
849
|
*/
|
|
906
850
|
declare function outlineMetrics(family: string, weight: number, style: FontStyle): OutlineFace | null;
|
|
907
851
|
/**
|
|
908
852
|
* Load a registered face's bytes now and resolve with its settled status:
|
|
909
853
|
* `'ready'`, `'failed'`, or `null` when nothing is registered for the
|
|
910
|
-
* variant. Never rejects — a failed load is a status
|
|
911
|
-
*
|
|
912
|
-
*
|
|
913
|
-
* The per-frame readers start a load and answer `null` until it lands, which
|
|
914
|
-
* suits a renderer and nothing that needs the geometry in one go: converting
|
|
915
|
-
* text to paths awaits this first.
|
|
854
|
+
* variant. Never rejects — a failed load is a status. For a caller that needs
|
|
855
|
+
* the geometry in one go, such as converting text to paths.
|
|
916
856
|
*/
|
|
917
857
|
declare function loadFontOutlines(family: string, variant?: OutlineVariant): Promise<OutlineStatus | null>;
|
|
918
858
|
/** @internal Test seam — registry and warn-once keys are module state. */
|
|
919
859
|
declare function _resetFontOutlinesForTests(): void;
|
|
920
860
|
|
|
921
|
-
export {
|
|
861
|
+
export { outlineMetrics as $, ensureFontTexture as A, type BmFont as B, type CanvasFontEntry as C, type DynamicFace as D, faceMetricsFor as E, type FontStyle as F, type GlyphTextureSink as G, faceMetricsFromTables as H, faceMetricsOf as I, fontOutlineRegistry as J, fontPending as K, fontRegistry as L, getDefaultFontFamily as M, getFont as N, type OutlineFace as O, getFontFallbackPolicy as P, glyphOutline as Q, type ResolveResult as R, hasFontOutlines as S, type TexSource as T, isCanvasFont as U, listCanvasFonts as V, listFontOutlines as W, listFontWeights as X, listFonts as Y, loadFontOutlines as Z, markAllFontsNotUploaded as _, type BmFontChar as a, outlineStatus as a0, parseBmFont as a1, refreshCanvasFont as a2, registerCanvasFont as a3, registerFont as a4, registerFontOutlines as a5, resolveFontVariant as a6, resolveGlyphFallback as a7, setDefaultFontFamily as a8, setFontFallbackPolicy as a9, syncColorPageTexture as aa, syncDynamicPageTexture as ab, textureCacheKey as ac, unregisterCanvasFont as ad, unregisterFontOutlines as ae, verticalMetricsFromTables as af, warmFonts as ag, __setGlyphRasterizerForTests as ah, _getPagesForTests as ai, _resetDomFacesForTests as aj, _resetDynamicFontsForTests as ak, _resetFallbackForTests as al, _resetFontOutlinesForTests as am, _resetFontRegistryForTests as an, type BmFontCommon as b, type BmFontInfo as c, type BmFontKerning as d, FIXTURE_FONT as e, type FaceMetricTables as f, type FaceMetrics as g, type FaceRuleMetrics as h, type FaceScriptMetrics as i, type FontEntry as j, type FontFallbackPolicy as k, type FontFamilyFaces as l, type FontRequest as m, type FontVariant as n, type OutlineFaceInfo as o, type OutlineFontOptions as p, type OutlineParser as q, type OutlineSource as r, type OutlineStatus as s, type OutlineVariant as t, type RegisterFontOptions as u, type RegisteredFont as v, colorPageTextureId as w, cssFontFamily as x, cssFontFamilyLoading as y, dynamicPageTextureId as z };
|