@json-to-office/shared 0.8.0 → 0.12.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/dist/index.d.ts CHANGED
@@ -3,6 +3,8 @@ export { AddWarningFunction, GenerationWarning } from './types/warnings.js';
3
3
  export { DEFAULT_ERROR_CONFIG, ERROR_EMOJIS, ErrorFormatterConfig, calculatePosition, clearComponentNamesCache, createErrorConfig, createJsonParseError, extractStandardComponentNames, formatErrorMessage, formatErrorSummary, getLiteralValue, getObjectSchemaPropertyNames, getSchemaMetadata, groupErrorsByPath, isLiteralSchema, isObjectSchema, isUnionSchema, transformValueError, transformValueErrors } from './validation/unified/index.js';
4
4
  export { T as TransformedError, V as ValidationError, a as ValidationResult } from './types-BWFZ7OaO.js';
5
5
  export { ComponentValidationError, ComponentValidationResult, ComponentVersion, ComponentVersionMap, CustomComponent, DuplicateComponentError, PluginValidationOptions, PluginValidationResult, RenderContext, RenderFunction, createComponent, createVersion, getValidationSummary, isValidationSuccess, resolveComponentVersion, validateCustomComponentProps } from './plugin/index.js';
6
+ import { F as FontRegistryEntry, a as FontRuntimeOpts, R as ResolvedFontSource, b as ResolvedFont } from './types-CL0Hbw6x.js';
7
+ export { c as FontFamilyNameSchema, d as FontRegistryDefinition, e as FontRegistryEntrySchema, f as FontRegistrySchema, g as FontSource, h as FontSourceSchema, S as SAFE_FONTS, i as SafeFontName, j as isSafeFont } from './types-CL0Hbw6x.js';
6
8
  export { ParsedSemver, compareSemver, isValidSemver, latestVersion, parseSemver } from './utils/semver.js';
7
9
  import '@sinclair/typebox';
8
10
  import '@sinclair/typebox/value';
@@ -10,14 +12,451 @@ import '@sinclair/typebox/value';
10
12
  /**
11
13
  * Service configuration types for external integrations (e.g. Highcharts export server)
12
14
  */
15
+ type HighchartsHeaders = Record<string, string>;
16
+ type HighchartsHeadersResolver = (body: unknown) => HighchartsHeaders | Promise<HighchartsHeaders>;
13
17
  interface HighchartsServiceConfig {
14
18
  serverUrl?: string;
15
- headers?: Record<string, string>;
19
+ headers?: HighchartsHeaders | HighchartsHeadersResolver;
16
20
  }
17
21
  interface ServicesConfig {
18
22
  highcharts?: HighchartsServiceConfig;
19
23
  }
20
24
 
25
+ /** Scan an arbitrary doc tree (DOCX or PPTX) for every font family referenced. */
26
+ declare function collectFontNames(doc: unknown): Set<string>;
27
+ /** Scan a DOCX document tree for every font family name referenced. */
28
+ declare const collectFontNamesFromDocx: typeof collectFontNames;
29
+ /** Scan a PPTX presentation tree for every font family name referenced. */
30
+ declare const collectFontNamesFromPptx: typeof collectFontNames;
31
+
32
+ /**
33
+ * Validate that every font name referenced in a document is either
34
+ * in SAFE_FONTS or present in the document's fontRegistry / runtime overrides.
35
+ *
36
+ * Used at generate-start; emits warnings for unresolved names so pipelines
37
+ * can surface them via their existing warning channels.
38
+ */
39
+
40
+ /**
41
+ * Warning codes for font resolution + rendering.
42
+ *
43
+ * - `FONT_UNRESOLVED` — family not in SAFE_FONTS and not registered.
44
+ * - `FONT_MODE_SUBSTITUTED` — non-safe families rewritten to safe equivalents.
45
+ * - `FONT_MODE_CUSTOM` — export mode "custom" — refs kept as-is.
46
+ */
47
+ type FontIssueCode = 'FONT_UNRESOLVED' | 'FONT_MODE_SUBSTITUTED' | 'FONT_MODE_CUSTOM';
48
+ interface FontResolutionIssue {
49
+ code: FontIssueCode;
50
+ family: string;
51
+ message: string;
52
+ }
53
+ interface FontValidationResult {
54
+ /** Names that resolved via SAFE_FONTS or the registry. */
55
+ resolved: string[];
56
+ /** Names with no resolution path. */
57
+ unresolved: string[];
58
+ /** One warning per unresolved name. */
59
+ warnings: FontResolutionIssue[];
60
+ }
61
+ interface FontValidationInput {
62
+ /** Font names referenced in the document (from collectFontNamesFromDocx / FromPptx). */
63
+ referencedNames: Iterable<string>;
64
+ /** Runtime-registered entries (e.g. from FontRuntimeOpts.extraEntries). */
65
+ registeredEntries?: FontRegistryEntry[];
66
+ }
67
+ /**
68
+ * Validate referenced font names against SAFE_FONTS + runtime-registered entries.
69
+ * Does not perform network fetches — this runs purely off schema + opts content.
70
+ */
71
+ declare function validateFontReferences(input: FontValidationInput): FontValidationResult;
72
+
73
+ /**
74
+ * Map a (family, weight, italic) tuple to the pair of
75
+ * `(familyName, { bold, italic })` the renderer should actually use.
76
+ *
77
+ * OOXML runs can only carry a bold/italic toggle, not a numeric weight.
78
+ * For weights outside the RIBBI quad (400/700 × roman/italic), Word
79
+ * resolves intermediate weights via **separate sub-family faces** whose
80
+ * internal family name is the canonical Google-Fonts-style subfamily,
81
+ * e.g. `Inter Light`, `Inter ExtraBold Italic`. Rewriting the run's
82
+ * `family` to that synthetic name lets Word pick the right face when the
83
+ * recipient has the full family installed, and lets the LibreOffice
84
+ * preview resolve the matching staged TTF by its internal name.
85
+ *
86
+ * No embedding involved — this is purely a name transform applied at
87
+ * render time. Safe fonts and unrecognised weights fall back to the
88
+ * bold-only heuristic (`weight >= 600 → bold`).
89
+ */
90
+ /** Human-readable labels for the canonical font-weight numbers. */
91
+ declare const WEIGHT_LABELS: Record<number, string>;
92
+ interface SynthesizedFamily {
93
+ /** The family name to emit in `rFonts`/`fontFace`. */
94
+ family: string;
95
+ /** Whether to also set the run's bold toggle. */
96
+ bold: boolean;
97
+ /** Whether to also set the run's italic toggle. */
98
+ italic: boolean;
99
+ /**
100
+ * `true` when the input `weight` was not one of the canonical
101
+ * 100/200/.../900 labels. The canonical family name is returned with
102
+ * a `weight >= 600 → bold` fallback, but the run will not match a
103
+ * dedicated sub-family face — callers should surface this so authors
104
+ * know the weight was effectively rounded to Regular or Bold.
105
+ */
106
+ nonCanonicalWeight: boolean;
107
+ }
108
+ /**
109
+ * Translate `(family, weight, italic)` into the rendering-time family name
110
+ * plus the bold/italic toggles to emit on the run.
111
+ *
112
+ * - RIBBI (weights 400 + 700, roman + italic) stays on the canonical family
113
+ * name and uses native bold/italic toggles.
114
+ * - Other canonical weights become `"<Family> <Weight>"` (e.g.
115
+ * `"Inter Light"`) with bold/italic toggles cleared; any italic flag is
116
+ * folded into the name (`"Inter Light Italic"`).
117
+ * - Non-canonical weights (floating or out-of-range) fall back to
118
+ * `bold = weight >= 600` and leave the family name untouched.
119
+ */
120
+ declare function synthesizeFamilyName(family: string, weight: number | undefined, italic: boolean): SynthesizedFamily;
121
+
122
+ /**
123
+ * Rewrite a TTF/OTF's `name` table so `nameID` 1 / 4 / 6 / 16 carry the
124
+ * supplied synthetic family name. Used by the preview-side font stagers
125
+ * so that running-text references like `"Inter Light"` resolve to the
126
+ * correct face when the stager registers it with Core Text / fontconfig
127
+ * / GDI (all of which index by the font's internal `name` table rather
128
+ * than the filename).
129
+ *
130
+ * The transform rebuilds the whole font: new `name` table bytes, new
131
+ * table directory with shifted offsets, recomputed per-table checksums,
132
+ * and the magic `head.checkSumAdjustment` recomputed against the whole
133
+ * output buffer. Nothing else is touched.
134
+ *
135
+ * OTF (CFF-flavoured) and TTF (glyf-flavoured) share the sfnt outer
136
+ * structure, so the same code handles both.
137
+ */
138
+ /**
139
+ * Return a copy of `input` whose name table has `nameID` 1/4/6/16 rewritten
140
+ * to `newFamily`. Returns the original buffer unchanged if the font has no
141
+ * `name` table or the sfnt header is invalid.
142
+ */
143
+ declare function rewriteFontFamilyName(input: Buffer, newFamily: string): Buffer;
144
+
145
+ /**
146
+ * FontRegistry — merges catalog + document registry + runtime entries
147
+ * and materializes referenced fonts into ResolvedFont records.
148
+ *
149
+ * Resolution rules, per referenced name:
150
+ * 1. Registry match (by family or id, case-insensitive). Runtime entries win
151
+ * on collision with document entries. Materialize each source.
152
+ * 2. SAFE_FONTS membership → empty sources.
153
+ * 3. Otherwise → empty sources with FONT_UNRESOLVED warning.
154
+ */
155
+
156
+ /**
157
+ * Minimal interface the registry needs from a disk cache. The concrete
158
+ * implementation ships in `./cache/disk-cache` but is Node-only (uses fs/crypto).
159
+ * Callers on Node inject an instance; browser callers pass nothing.
160
+ */
161
+ interface FontDiskCacheLike$1 {
162
+ get(key: string): Promise<Buffer | undefined>;
163
+ set(key: string, value: Buffer): Promise<void>;
164
+ }
165
+ /**
166
+ * Minimal interface for a file-loader. Same reasoning as FontDiskCacheLike:
167
+ * concrete impl is Node-only, callers inject when on Node.
168
+ */
169
+ type FontFileLoader = (input: {
170
+ path: string;
171
+ weight?: number;
172
+ italic?: boolean;
173
+ baseDir?: string;
174
+ }) => Promise<ResolvedFontSource>;
175
+ /**
176
+ * Minimal interface for the variable-font fetcher. `subset-font` (the
177
+ * harfbuzz-wasm wrapper we use for axis pinning) reaches for `fs` at
178
+ * init time, which crashes in the browser. Injection keeps that import
179
+ * behind the Node-only subpath; browser bundles never pull it in, and
180
+ * browser callers simply won't see `kind: 'variable'` fonts resolved
181
+ * (the registry warns and skips instead).
182
+ */
183
+ type FontVariableLoader = (input: {
184
+ url: string;
185
+ weight: number;
186
+ italic: boolean;
187
+ axes?: Record<string, number>;
188
+ fetchTimeoutMs?: number;
189
+ memoryCache?: {
190
+ get(key: string): Buffer | undefined;
191
+ set(key: string, value: Buffer): void;
192
+ };
193
+ diskCache?: {
194
+ get(key: string): Promise<Buffer | undefined>;
195
+ set(key: string, value: Buffer): Promise<void>;
196
+ };
197
+ }) => Promise<{
198
+ source?: ResolvedFontSource;
199
+ warnings?: string[];
200
+ }>;
201
+ interface FontRegistryInput {
202
+ /** Runtime options — entries come from opts.extraEntries. */
203
+ opts?: FontRuntimeOpts;
204
+ /** Optional disk cache (Node only). Pass an instance of FontDiskCache. */
205
+ diskCache?: FontDiskCacheLike$1;
206
+ /**
207
+ * Optional `kind: "file"` loader (Node only). Inject `loadFileFontSource`
208
+ * from `@json-to-office/shared/fonts/sources/file-loader` on Node. Browser
209
+ * callers pass nothing; `kind: "file"` sources then warn and skip.
210
+ */
211
+ fileLoader?: FontFileLoader;
212
+ /**
213
+ * Optional `kind: "variable"` loader (Node only). Inject
214
+ * `fetchVariableFontSource` from `@json-to-office/shared/fonts/node` on
215
+ * Node. Browser callers pass nothing; `kind: "variable"` sources then
216
+ * warn and skip. Keeping this injected avoids dragging subset-font (and
217
+ * its `fs.promises.readFile` bootstrap) into client bundles.
218
+ */
219
+ variableLoader?: FontVariableLoader;
220
+ }
221
+ declare class FontRegistry {
222
+ private readonly index;
223
+ private readonly cache;
224
+ private readonly opts;
225
+ private readonly memoryCache;
226
+ private readonly diskCache;
227
+ private readonly fileLoader;
228
+ private readonly variableLoader;
229
+ constructor(input?: FontRegistryInput);
230
+ private addEntry;
231
+ /** Resolve every referenced name in one pass. Order preserved. */
232
+ resolveMany(names: Iterable<string>): Promise<ResolvedFont[]>;
233
+ resolve(name: string): Promise<ResolvedFont>;
234
+ private materializeEntry;
235
+ private materializeSource;
236
+ }
237
+
238
+ /**
239
+ * Font format detection from magic bytes.
240
+ * Source: OpenType spec + WOFF1/WOFF2 W3C specs.
241
+ */
242
+
243
+ declare function detectFontFormat(buf: Buffer): ResolvedFontSource['format'];
244
+
245
+ /**
246
+ * Curated list of popular Google Fonts for picker autocomplete.
247
+ *
248
+ * Not exhaustive — the full Google Fonts library has ~1500 families.
249
+ * This is ~30 names known to cover most real-world use cases.
250
+ */
251
+ interface PopularGoogleFont {
252
+ family: string;
253
+ category: 'sans' | 'serif' | 'mono' | 'display' | 'handwriting';
254
+ /** Weights available on Google Fonts for this family. */
255
+ weights: number[];
256
+ /** Whether italic variants exist. */
257
+ hasItalic: boolean;
258
+ }
259
+ declare const POPULAR_GOOGLE_FONTS: readonly PopularGoogleFont[];
260
+
261
+ /**
262
+ * Per-family upstream overrides for popular Google Fonts whose
263
+ * redistribution on fonts.google.com has known defects we can't fix via
264
+ * metadata patching alone.
265
+ *
266
+ * When `autoGoogleFontEntries` hits a family present in this table, it
267
+ * builds override sources instead of issuing `kind: "google"` CSS requests.
268
+ * Each entry is either:
269
+ *
270
+ * - `{ kind: "url", url, weight, italic? }` — a direct HTTPS TTF/OTF.
271
+ * Use when a clean per-weight static exists on a stable CDN.
272
+ *
273
+ * - `{ kind: "variable", url, weight, italic? }` — points at a variable
274
+ * TTF with an `fvar` table. The registry downloads the variable font
275
+ * once and harfbuzz-pins the `wght` axis to the specified weight,
276
+ * producing a clean static TTF. Use when the upstream ships a variable
277
+ * font but no per-weight statics on a CDN (rsms/inter is the
278
+ * canonical example — variable font on jsDelivr, per-weight statics
279
+ * only in GitHub release zips).
280
+ *
281
+ * Pick `variable` over `url` when both are available: the instancer
282
+ * produces per-weight glyph outlines that diverge correctly at every
283
+ * axis value. Google's static redistributions collapse adjacent weights
284
+ * onto the same instance — Inter Thin and ExtraLight both source at
285
+ * ~wght=250 in Google's pipeline, so their static TTFs have 98% identical
286
+ * glyph outlines. Instancing the upstream variable font at exactly wght=100
287
+ * vs wght=200 gives properly distinct geometry.
288
+ *
289
+ * Validate new entries with a HEAD request before adding — the fetchers
290
+ * reject non-TTF responses, but a failed override silently falls back to
291
+ * the Google path, defeating the purpose.
292
+ */
293
+ /** One upstream variant source. Type matches the FontSource schema so we
294
+ * can pass the entry directly into `FontRegistry`'s materialize pipeline. */
295
+ type UpstreamVariant = {
296
+ kind: 'url';
297
+ url: string;
298
+ weight: number;
299
+ italic?: boolean;
300
+ } | {
301
+ kind: 'variable';
302
+ url: string;
303
+ weight: number;
304
+ italic?: boolean;
305
+ /** Extra axis pins merged on top of the derived `wght` pin. */
306
+ axes?: Record<string, number>;
307
+ };
308
+ interface UpstreamOverride {
309
+ /** Human-readable for logs/diagnostics only. */
310
+ reason: string;
311
+ variants: UpstreamVariant[];
312
+ }
313
+ declare const UPSTREAM_OVERRIDES: Record<string, UpstreamOverride>;
314
+ /** Case-insensitive lookup. Returns undefined when the family has no override. */
315
+ declare function getUpstreamOverride(family: string): UpstreamOverride | undefined;
316
+
317
+ /**
318
+ * In-process LRU cache for resolved font buffers.
319
+ * Scoped to a single process — do not share across requests on a server.
320
+ */
321
+ interface MemoryCacheOptions {
322
+ /** Approximate soft cap in bytes. LRU-evict when exceeded. */
323
+ maxBytes?: number;
324
+ }
325
+ declare class FontMemoryCache {
326
+ private readonly store;
327
+ private bytes;
328
+ private readonly maxBytes;
329
+ constructor(opts?: MemoryCacheOptions);
330
+ get(key: string): Buffer | undefined;
331
+ set(key: string, value: Buffer): void;
332
+ size(): number;
333
+ }
334
+
335
+ /**
336
+ * Google Fonts fetcher.
337
+ *
338
+ * Hits the CSS API v2 with an older User-Agent that returns TTF (default UA
339
+ * gets WOFF2, which Office cannot embed as-is). Parses the `src: url(...)` line
340
+ * and downloads the binary.
341
+ *
342
+ * Uses memory + optional disk cache keyed by `${family}|${weight}|${italic}`.
343
+ */
344
+
345
+ interface FontDiskCacheLike {
346
+ get(key: string): Promise<Buffer | undefined>;
347
+ set(key: string, value: Buffer): Promise<void>;
348
+ }
349
+ interface GoogleFetchOptions {
350
+ family: string;
351
+ weights: number[];
352
+ italics?: boolean;
353
+ memoryCache?: FontMemoryCache;
354
+ diskCache?: FontDiskCacheLike;
355
+ fetchTimeoutMs?: number;
356
+ /** Override for tests. */
357
+ fetcher?: typeof fetch;
358
+ }
359
+ interface GoogleFetchResult {
360
+ sources: ResolvedFontSource[];
361
+ warnings: string[];
362
+ }
363
+ declare function fetchGoogleFontSources(opts: GoogleFetchOptions): Promise<GoogleFetchResult>;
364
+
365
+ /**
366
+ * Font family substitution: rewrite every non-safe family reference in
367
+ * the doc tree + theme to a SAFE_FONTS equivalent. Used by the
368
+ * `'substitute'` export mode (`FontRuntimeOpts.mode`) so that non-safe
369
+ * fonts (Playfair Display, Inter, …) ship as Georgia/Calibri and the
370
+ * document renders identically on every recipient machine — no embed
371
+ * bytes, no Word-for-Mac intermediate-weight surprises.
372
+ *
373
+ * The walker mirrors the shape used by `collectFontNamesFromDocx/Pptx`
374
+ * so the two stay in sync: whatever `collect` scans, `rewrite` will
375
+ * rewrite. Future component-schema additions that introduce new font
376
+ * keys go in `FONT_NAME_KEYS` / `THEME_FONT_KEYS` once, both sides pick
377
+ * them up.
378
+ */
379
+
380
+ /** One swap recorded during a rewrite. */
381
+ interface FontSubstitution {
382
+ from: string;
383
+ to: string;
384
+ }
385
+ interface ApplyFontSubstitutionResult<T> {
386
+ doc: T;
387
+ substitutions: FontSubstitution[];
388
+ }
389
+ /**
390
+ * Walk a doc tree + swap every non-safe family reference per `mapping`.
391
+ * Returns a new tree (structural clone) plus the list of `(from, to)`
392
+ * swaps made, deduped by source name.
393
+ *
394
+ * Families already in SAFE_FONTS are never rewritten (even if a mapping
395
+ * entry targets them as a key — safe fonts don't need substitution).
396
+ * Families with no mapping entry are left untouched — callers should
397
+ * feed the result of `buildDefaultSubstitutionMap` to ensure every
398
+ * non-safe reference gets a fallback.
399
+ */
400
+ declare function applyFontSubstitution<T>(doc: T, mapping: Record<string, string>): ApplyFontSubstitutionResult<T>;
401
+ /**
402
+ * Pick the safe-font fallback for a single non-safe family. Precedence:
403
+ * 1. Explicit override in `EXPLICIT_OVERRIDES`.
404
+ * 2. Category lookup in `POPULAR_GOOGLE_FONTS`.
405
+ * 3. Final default (`Calibri`).
406
+ *
407
+ * Exposed for the playground dialog so it can pre-populate the per-family
408
+ * picker with the same defaults the CLI would apply.
409
+ */
410
+ declare function defaultSubstituteFor(family: string): string;
411
+ /**
412
+ * Build a substitution map for every non-safe family in `referencedNames`.
413
+ * Safe fonts are omitted from the result since they don't need swapping.
414
+ * Caller can override individual entries before passing to
415
+ * `applyFontSubstitution`.
416
+ */
417
+ declare function buildDefaultSubstitutionMap(referencedNames: Iterable<string>): Record<string, string>;
418
+
419
+ /**
420
+ * Cache-key suffix used to scope generator outputs by export mode. When
421
+ * `fonts.mode === 'substitute'` the doc tree is rewritten pre-render, so
422
+ * a substitute-mode buffer and a custom-mode buffer for the same base
423
+ * theme must not collide in the byte cache. Keep this as a single
424
+ * helper so a typo in one caller can't silently alias one mode onto the
425
+ * other's cache slot.
426
+ */
427
+ declare function scopedThemeName(baseThemeName: string, fontMode: string | undefined): string;
428
+
429
+ interface ApplyExportModeInput<D, T> {
430
+ doc: D;
431
+ theme: T;
432
+ fonts?: FontRuntimeOpts;
433
+ }
434
+ interface ApplyExportModeWarning {
435
+ code: 'FONT_MODE_CUSTOM' | 'FONT_MODE_SUBSTITUTED';
436
+ message: string;
437
+ }
438
+ interface ApplyExportModeResult<D, T> {
439
+ doc: D;
440
+ theme: T;
441
+ warnings: ApplyExportModeWarning[];
442
+ }
443
+ /**
444
+ * Inspect `fonts.mode` and apply the pre-resolution rewrite for the
445
+ * requested mode.
446
+ *
447
+ * - `'custom'` (default) — no rewrite. Font references stay as authored;
448
+ * recipients need the font installed or Word falls back. The
449
+ * LibreOffice preview stager registers resolved bytes so preview
450
+ * fidelity matches the recipient-side experience when the font is
451
+ * installed.
452
+ * - `'substitute'` — rewrite every non-safe family in doc + theme to its
453
+ * mapped safe equivalent. Fills in defaults via
454
+ * `buildDefaultSubstitutionMap` for any non-safe reference not present
455
+ * in `fonts.substitution`. Emits one `FONT_MODE_SUBSTITUTED` warning
456
+ * listing every swap.
457
+ */
458
+ declare function applyExportMode<D, T>(input: ApplyExportModeInput<D, T>): ApplyExportModeResult<D, T>;
459
+
21
460
  /**
22
461
  * Deep Merge Utilities
23
462
  * Generic deep-merge helpers used by both docx and pptx
@@ -31,4 +470,4 @@ interface ServicesConfig {
31
470
  */
32
471
  declare function mergeWithDefaults<T>(userConfig: T, themeDefaults: Partial<T>): T;
33
472
 
34
- export { type HighchartsServiceConfig, type ServicesConfig, mergeWithDefaults };
473
+ export { type FontIssueCode, FontRegistry, FontRegistryEntry, type FontRegistryInput, type FontResolutionIssue, FontRuntimeOpts, type FontSubstitution, type FontValidationInput, type FontValidationResult, type HighchartsHeaders, type HighchartsHeadersResolver, type HighchartsServiceConfig, POPULAR_GOOGLE_FONTS, type PopularGoogleFont, ResolvedFont, ResolvedFontSource, type ServicesConfig, type SynthesizedFamily, UPSTREAM_OVERRIDES, type UpstreamOverride, type UpstreamVariant, WEIGHT_LABELS, applyExportMode, applyFontSubstitution, buildDefaultSubstitutionMap, collectFontNamesFromDocx, collectFontNamesFromPptx, defaultSubstituteFor, detectFontFormat, fetchGoogleFontSources, getUpstreamOverride, mergeWithDefaults, rewriteFontFamilyName, scopedThemeName, synthesizeFamilyName, validateFontReferences };