@json-to-office/shared 2.5.0 → 2.6.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/fonts/node.d.ts +9 -1
- package/dist/fonts/node.js +23 -0
- package/dist/fonts/node.js.map +1 -1
- package/dist/index.d.ts +90 -3
- package/dist/index.js +155 -36
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/fonts/node.d.ts
CHANGED
|
@@ -123,5 +123,13 @@ declare function toRasterizeFontFaces(fonts: readonly ResolvedFont[], warnings?:
|
|
|
123
123
|
* matching how the registry keys resolved fonts.
|
|
124
124
|
*/
|
|
125
125
|
declare function fromRasterizeFontFaces(faces: readonly RasterizeFontFace[]): ResolvedFont[];
|
|
126
|
+
/**
|
|
127
|
+
* Flatten resolved fonts into the faces a Highcharts export server can be
|
|
128
|
+
* handed as inline `@font-face` rules. Wider than `toRasterizeFontFaces`:
|
|
129
|
+
* the chart is drawn by Chromium, which reads WOFF and WOFF2 as readily as
|
|
130
|
+
* an sfnt, so only formats no browser loads are dropped. Safe-only fonts
|
|
131
|
+
* carry no bytes and are skipped; the server's own host faces cover them.
|
|
132
|
+
*/
|
|
133
|
+
declare function toChartFontFaces(fonts: readonly ResolvedFont[]): RasterizeFontFace[];
|
|
126
134
|
|
|
127
|
-
export { FontDiskCache, type VariableFetchOptions, fetchVariableFontSource, fromRasterizeFontFaces, loadFileFontSource, toRasterizeFontFaces };
|
|
135
|
+
export { FontDiskCache, type VariableFetchOptions, fetchVariableFontSource, fromRasterizeFontFaces, loadFileFontSource, toChartFontFaces, toRasterizeFontFaces };
|
package/dist/fonts/node.js
CHANGED
|
@@ -279,11 +279,34 @@ function fromRasterizeFontFaces(faces) {
|
|
|
279
279
|
}
|
|
280
280
|
return [...byFamily.values()];
|
|
281
281
|
}
|
|
282
|
+
var BROWSER_FORMATS = /* @__PURE__ */ new Set([
|
|
283
|
+
"ttf",
|
|
284
|
+
"otf",
|
|
285
|
+
"woff",
|
|
286
|
+
"woff2"
|
|
287
|
+
]);
|
|
288
|
+
function toChartFontFaces(fonts) {
|
|
289
|
+
const faces = [];
|
|
290
|
+
for (const font of fonts) {
|
|
291
|
+
for (const source of font.sources) {
|
|
292
|
+
if (!BROWSER_FORMATS.has(source.format)) continue;
|
|
293
|
+
faces.push({
|
|
294
|
+
family: font.family,
|
|
295
|
+
weight: source.weight,
|
|
296
|
+
italic: source.italic,
|
|
297
|
+
data: source.data.toString("base64"),
|
|
298
|
+
format: source.format
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return faces;
|
|
303
|
+
}
|
|
282
304
|
export {
|
|
283
305
|
FontDiskCache,
|
|
284
306
|
fetchVariableFontSource,
|
|
285
307
|
fromRasterizeFontFaces,
|
|
286
308
|
loadFileFontSource,
|
|
309
|
+
toChartFontFaces,
|
|
287
310
|
toRasterizeFontFaces
|
|
288
311
|
};
|
|
289
312
|
//# sourceMappingURL=node.js.map
|
package/dist/fonts/node.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/fonts/sources/file-loader.ts","../../src/fonts/cache/disk-cache.ts","../../src/fonts/sources/variable-fetcher.ts","../../src/fonts/rasterize-faces.ts"],"sourcesContent":["/**\n * Load a .ttf/.otf file from disk.\n * Node-only — called from the render pipeline.\n */\n\nimport { readFile } from 'fs/promises';\nimport { isAbsolute, resolve as resolvePath } from 'path';\nimport type { ResolvedFontSource } from '../types';\nimport { detectFontFormat } from './format';\n\nexport interface FileSourceInput {\n path: string;\n weight?: number;\n italic?: boolean;\n baseDir?: string;\n}\n\n/** Read a font file and wrap as a ResolvedFontSource. */\nexport async function loadFileFontSource(\n input: FileSourceInput\n): Promise<ResolvedFontSource> {\n const fullPath = isAbsolute(input.path)\n ? input.path\n : resolvePath(input.baseDir ?? process.cwd(), input.path);\n const data = await readFile(fullPath);\n const format = detectFontFormat(data);\n if (format === 'unknown') {\n throw new Error(\n `Font file at \"${fullPath}\" is not a recognized font file (expected TTF/OTF/WOFF/WOFF2)`\n );\n }\n // No format rejection here: bytes flow to the LibreOffice preview\n // stager, which handles WOFF/WOFF2 natively via fontconfig on\n // Linux/macOS. Office output never embeds these bytes — substitute/\n // custom modes rely on recipient-side fonts.\n return {\n data,\n weight: input.weight ?? 400,\n italic: input.italic ?? false,\n format,\n };\n}\n","/**\n * On-disk cache for fetched Google Fonts TTFs.\n * Optional — only active when a cacheDir is provided. Node-only.\n */\n\nimport { createHash } from 'crypto';\nimport { mkdir, readFile, writeFile } from 'fs/promises';\nimport { join } from 'path';\n\nexport class FontDiskCache {\n private readonly dir: string;\n // In-flight promise dedupes the first-write mkdir across concurrent set()\n // calls. Without it, two simultaneous cold-cache writes could both see\n // `ensured=false`, both issue mkdir, and both flip the flag afterwards —\n // harmless today (recursive mkdir is idempotent) but the pattern is\n // right and leaves room to add per-directory locks if we ever need to.\n private ensurePromise: Promise<void> | null = null;\n\n constructor(dir: string) {\n this.dir = dir;\n }\n\n private ensureDir(): Promise<void> {\n if (!this.ensurePromise) {\n this.ensurePromise = mkdir(this.dir, { recursive: true }).then(\n () => undefined\n );\n }\n return this.ensurePromise;\n }\n\n private pathFor(key: string): string {\n const hash = createHash('sha256').update(key).digest('hex').slice(0, 24);\n return join(this.dir, `${hash}.bin`);\n }\n\n async get(key: string): Promise<Buffer | undefined> {\n try {\n return await readFile(this.pathFor(key));\n } catch {\n return undefined;\n }\n }\n\n async set(key: string, value: Buffer): Promise<void> {\n await this.ensureDir();\n await writeFile(this.pathFor(key), value);\n }\n}\n","/**\n * Variable-font instancer. Fetches a variable font once (disk-cached; TTF,\n * OTF, or WOFF/WOFF2 — fontverter converts compressed containers before\n * instancing), then pins its `wght` axis (plus any additional axes) to\n * produce a clean static TTF per requested weight. Uses harfbuzz via\n * `subset-font` — pure JS + WASM, no native toolchain.\n *\n * Why this exists. Google Fonts serves pre-instanced static TTFs for many\n * families, but the instancing step is lossy: Inter Thin (100) and\n * ExtraLight (200) both ship with `OS/2.usWeightClass=250` and near-\n * identical glyph outlines (xAvgCharWidth differs by 1.8%, glyf table\n * differs by 83 bytes out of 135 KB). Pinning the upstream variable TTF's\n * `wght` axis at exactly 100 vs 200 produces properly distinct instances.\n *\n * Cache strategy:\n * 1. Raw variable font cached at key `varsrc|<url>` — one download per URL\n * per process (+ optional disk layer).\n * 2. Instanced static TTF cached at `variable2|<url>|<weight>|<italic>` —\n * avoids re-running harfbuzz for weights we've already produced.\n *\n * Full-glyph retention. subset-font's `text` parameter drives which\n * codepoints' glyphs survive. We pass every BMP codepoint so the output\n * is effectively a full-glyph static (not a subset) for any Latin /\n * Cyrillic / Greek / Vietnamese-covering family — which includes every\n * entry in our POPULAR_GOOGLE_FONTS catalog. Supplementary-plane glyphs\n * (emoji) would be dropped, but those aren't in the variable families we\n * target. `preserveNameIds` keeps the human-readable name records our\n * downstream normalization expects.\n */\n\nimport type { ResolvedFontSource } from '../types';\nimport { detectFontFormat } from './format';\nimport { rewriteFontSubfamilyNames } from './ttf-name';\nimport { isAllowedFontUrl } from './url-allowlist';\n\n// `subset-font` carries a harfbuzz WASM payload and is Node-only. Lazy-load\n// so a browser bundler that chases the generic `sources/` tree doesn't pull\n// it in. Cached across calls so the WASM heap is created once per process.\nlet subsetFontPromise: Promise<typeof import('subset-font').default> | null =\n null;\nfunction loadSubsetFont(): Promise<typeof import('subset-font').default> {\n if (!subsetFontPromise) {\n subsetFontPromise = import('subset-font').then((m) => m.default);\n }\n return subsetFontPromise;\n}\n\nexport interface VariableFetchOptions {\n url: string;\n weight: number;\n italic: boolean;\n /** Extra axis pins merged on top of the derived `wght` pin (e.g. `ital`,\n * `opsz`, `slnt`). Rare — the `weight`/`italic` pair is usually enough. */\n axes?: Record<string, number>;\n /** Family label used in error messages and diagnostics. */\n familyLabel?: string;\n fetchTimeoutMs?: number;\n fetcher?: typeof fetch;\n memoryCache?: {\n get(key: string): Buffer | undefined;\n set(key: string, value: Buffer): void;\n };\n diskCache?: {\n get(key: string): Promise<Buffer | undefined>;\n set(key: string, value: Buffer): Promise<void>;\n };\n}\n\nfunction rawCacheKey(url: string): string {\n return `varsrc|${url}`;\n}\n\nfunction instanceCacheKey(\n url: string,\n weight: number,\n italic: boolean,\n axes?: Record<string, number>\n): string {\n // Axes go into the key deterministically so different axis pins don't\n // collide. Sorted so `{a:1,b:2}` and `{b:2,a:1}` hash the same.\n const axisPart = axes\n ? '|' +\n Object.entries(axes)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => `${k}=${v}`)\n .join(',')\n : '';\n // `variable2`: v2 stamps standard subfamily names into the instanced\n // output — bumped so persistent disk caches drop pre-stamp instances.\n return `variable2|${url}|${weight}|${italic ? 'i' : 'r'}${axisPart}`;\n}\n\n/**\n * String covering every assigned BMP codepoint (0x20-0xFFFF minus surrogate\n * range). Built lazily on first use — ~127 KiB of UTF-16 memory (0xFFFF\n * codepoints × 2 bytes per UTF-16 code unit, minus the surrogate range)\n * held for the lifetime of the process, which is negligible next to the\n * WASM heap harfbuzz already carries.\n */\nlet cachedBmpCharset: string | null = null;\nfunction bmpCharset(): string {\n if (cachedBmpCharset) return cachedBmpCharset;\n let s = '';\n for (let cp = 0x20; cp <= 0xffff; cp++) {\n // Surrogate range is structurally invalid as standalone codepoints —\n // harfbuzz rejects them. Skip.\n if (cp >= 0xd800 && cp <= 0xdfff) continue;\n s += String.fromCodePoint(cp);\n }\n cachedBmpCharset = s;\n return s;\n}\n\ntype FetchResult = { buf: Buffer } | { error: string };\n\nasync function fetchVariableSource(\n opts: VariableFetchOptions\n): Promise<FetchResult> {\n if (!isAllowedFontUrl(opts.url)) {\n return { error: 'host not in allowlist or non-HTTPS' };\n }\n const key = rawCacheKey(opts.url);\n const mem = opts.memoryCache?.get(key);\n if (mem) return { buf: mem };\n const disk = await opts.diskCache?.get(key);\n if (disk) {\n opts.memoryCache?.set(key, disk);\n return { buf: disk };\n }\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), opts.fetchTimeoutMs ?? 10000);\n try {\n const f = opts.fetcher ?? fetch;\n // redirect: 'manual' so the allowlist can't be bypassed via Location.\n let res = await f(opts.url, { signal: ctrl.signal, redirect: 'manual' });\n let hops = 0;\n while (res.status >= 300 && res.status < 400 && res.status !== 304) {\n const next = res.headers.get('location');\n if (!next) return { error: `${res.status} with no Location` };\n const resolved = new URL(next, opts.url).toString();\n if (!isAllowedFontUrl(resolved)) {\n return { error: `redirect to disallowed host: ${resolved}` };\n }\n if (++hops > 3) return { error: 'too many redirects' };\n res = await f(resolved, { signal: ctrl.signal, redirect: 'manual' });\n }\n if (!res.ok) return { error: `HTTP ${res.status} ${res.statusText}` };\n const ab = await res.arrayBuffer();\n const buf = Buffer.from(ab);\n // Sanity-check: reject sub-1KB or non-TTF responses up front. The\n // instancer would fail loudly on garbage, but a clear \"wrong URL\"\n // signal here shortens the debug cycle.\n if (buf.length < 1024)\n return { error: `response too small (${buf.length}B)` };\n const format = detectFontFormat(buf);\n // WOFF/WOFF2 sources are fine: subset-font funnels every input through\n // fontverter (sfnt/woff/woff2 → truetype) before harfbuzz sees it, and\n // the instanced output is always plain sfnt. Needed in practice —\n // rsms/inter publishes its italic variable master only as woff2.\n if (\n format !== 'ttf' &&\n format !== 'otf' &&\n format !== 'woff' &&\n format !== 'woff2'\n ) {\n return { error: `unexpected font format: ${format}` };\n }\n opts.memoryCache?.set(key, buf);\n await opts.diskCache?.set(key, buf);\n return { buf };\n } catch (err) {\n return { error: (err as Error).message };\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function fetchVariableFontSource(\n opts: VariableFetchOptions\n): Promise<{ source?: ResolvedFontSource; warnings?: string[] }> {\n const key = instanceCacheKey(opts.url, opts.weight, opts.italic, opts.axes);\n const mem = opts.memoryCache?.get(key);\n if (mem) {\n return {\n source: {\n data: mem,\n weight: opts.weight,\n italic: opts.italic,\n format: detectFontFormat(mem),\n },\n warnings: [],\n };\n }\n const disk = await opts.diskCache?.get(key);\n if (disk) {\n opts.memoryCache?.set(key, disk);\n return {\n source: {\n data: disk,\n weight: opts.weight,\n italic: opts.italic,\n format: detectFontFormat(disk),\n },\n warnings: [],\n };\n }\n\n const fetched = await fetchVariableSource(opts);\n if ('error' in fetched) {\n return {\n warnings: [\n `Variable font fetch \"${opts.url}\" for \"${opts.familyLabel ?? opts.url}\" weight ${opts.weight}: ${fetched.error}; falling back to host defaults.`,\n ],\n };\n }\n const raw = fetched.buf;\n\n // Harfbuzz refuses to emit WOFF2 for subset-font's default SFNT target,\n // but we need plain SFNT anyway — Office embeds TTFs, not compressed\n // formats. Pin the weight (and any extra axes) and preserve the name\n // records that our downstream name rewrites (`rewriteFontFamilyName`,\n // `rewriteFontSubfamilyNames`) depend on.\n //\n // Note: italic is encoded by URL (separate italic master), not by axis pin.\n // The `ital` axis exists on some fonts but not others (Inter ships a\n // separate InterVariable-Italic.ttf instead). Callers that want to force\n // an axis pin can pass `axes: { ital: 1 }` explicitly.\n const variationAxes: Record<string, number> = {\n wght: opts.weight,\n ...(opts.axes ?? {}),\n };\n\n let instanced: Buffer;\n try {\n const subsetFont = await loadSubsetFont();\n instanced = await subsetFont(raw, bmpCharset(), {\n targetFormat: 'sfnt',\n variationAxes,\n // Keep every common name record. harfbuzz drops the ones not in\n // this list; our downstream rewrites need 1/2/4/6/16/17 intact.\n preserveNameIds: [\n 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,\n 20, 21, 22, 23, 24, 25,\n ],\n });\n } catch (err) {\n return {\n warnings: [\n `Variable font instancing for \"${opts.familyLabel ?? opts.url}\" weight ${opts.weight}: ${(err as Error).message}`,\n ],\n };\n }\n\n // harfbuzz preserves the source's name records verbatim, so the instanced\n // static still carries the variable font's default-instance subfamily\n // (typically \"Regular\") in nameID 2/17 — which would trip\n // validateFontMetadata's FONT_METADATA_DEFECT warning for every non-\n // Regular weight. Stamp the standard subfamily for the pinned pair.\n instanced = rewriteFontSubfamilyNames(instanced, opts.weight, opts.italic);\n\n opts.memoryCache?.set(key, instanced);\n await opts.diskCache?.set(key, instanced);\n return {\n source: {\n data: instanced,\n weight: opts.weight,\n italic: opts.italic,\n format: detectFontFormat(instanced),\n },\n warnings: [],\n };\n}\n","/**\n * `ResolvedFont[]` ⇄ `RasterizeFontFace[]` — the one encoder/decoder pair for\n * shipping font bytes to the pptx rasterizer.\n *\n * The docx side encodes (core-docx, from `resolveDocumentFonts`) and the\n * rasterizer side decodes (jto-cli, before handing the faces to a\n * `FontStager`). Keeping both halves here means the two cannot drift on\n * base64 handling or on the family-name convention.\n *\n * FAMILY NAMES STAY UNSYNTHESIZED. The wire carries the catalog family\n * (\"Inter\"); the stager applies `synthesizeFamilyName` +\n * `rewriteFontFamilyName` to produce the sub-family the presentation\n * actually references (\"Inter Light\"). Encoding a pre-synthesized name here\n * would make the stager apply the suffix twice.\n *\n * Buffer-dependent → Node-only. Exported from `@json-to-office/shared/fonts/node`.\n */\n\nimport type { ResolvedFont, ResolvedFontSource } from './types';\nimport type { RasterizeFontFace } from '../types/services';\nimport type { GenerationWarning } from '../types/warnings';\n\n/**\n * Formats the rasterizer's native stagers can actually register.\n *\n * All three stagers (fontconfig, macOS Core Text, Windows GDI) write every\n * staged source as a `.ttf` and register it as a raw sfnt, and they rename\n * the face through `rewriteFontFamilyName`, which returns the buffer\n * UNCHANGED for anything without an sfnt header. So a WOFF/WOFF2 (or EOT, or\n * PostScript) source is staged as bytes no font system parses, under the\n * catalog family rather than the synthesized sub-family the presentation\n * references — it renders as fallback text, silently.\n *\n * Shipping those bytes anyway costs wire size, disk writes, and a distinct\n * rasterizer cache key for a render that is identical to the fontless one.\n * An allowlist (rather than a WOFF denylist) keeps any format added to\n * `ResolvedFontSource['format']` later excluded until a stager can handle it.\n */\nconst STAGEABLE_FORMATS = new Set<ResolvedFontSource['format']>(['ttf', 'otf']);\n\n/**\n * Flatten resolved fonts into the serializable wire faces (one face per\n * source variant). Entries with no sources — safe-only fonts, which the\n * renderer resolves against system faces — carry no bytes and are skipped,\n * as are sources in a format no stager can register.\n *\n * @param warnings - sink for one warning per dropped source, shaped like every\n * other generation warning so a caller can hand in the same array it already\n * collects. Both docx entry paths do: a dropped face renders as a fallback,\n * which is precisely the silent substitution this pipeline exists to make\n * visible, so it must not be discoverable only by reading the code.\n */\nexport function toRasterizeFontFaces(\n fonts: readonly ResolvedFont[],\n warnings?: GenerationWarning[]\n): RasterizeFontFace[] {\n const faces: RasterizeFontFace[] = [];\n for (const font of fonts) {\n if (font.sources.length === 0) continue;\n for (const source of font.sources) {\n if (!STAGEABLE_FORMATS.has(source.format)) {\n warnings?.push({\n component: 'fontRegistry',\n severity: 'warning',\n context: { code: 'FONT_FORMAT_NOT_RASTERIZABLE' },\n message:\n `\"${font.family}\" weight ${source.weight}` +\n `${source.italic ? ' italic' : ''} is ${source.format}; the rasterizer's ` +\n `font stagers only register TTF/OTF, so this face is omitted and the ` +\n `visual renders with a fallback face.`,\n });\n continue;\n }\n faces.push({\n family: font.family,\n weight: source.weight,\n italic: source.italic,\n data: source.data.toString('base64'),\n format: source.format as RasterizeFontFace['format'],\n });\n }\n }\n return faces;\n}\n\n/**\n * Inverse of {@link toRasterizeFontFaces}: regroup wire faces back into\n * `ResolvedFont[]` so the existing `FontStager.stage(ResolvedFont[], …)`\n * signature needs no change. Grouping is by exact (case-sensitive) family,\n * matching how the registry keys resolved fonts.\n */\nexport function fromRasterizeFontFaces(\n faces: readonly RasterizeFontFace[]\n): ResolvedFont[] {\n const byFamily = new Map<string, ResolvedFont>();\n for (const face of faces) {\n let font = byFamily.get(face.family);\n if (!font) {\n font = { family: face.family, sources: [], warnings: [] };\n byFamily.set(face.family, font);\n }\n const source: ResolvedFontSource = {\n data: Buffer.from(face.data, 'base64'),\n weight: face.weight,\n italic: face.italic,\n format: face.format ?? 'ttf',\n };\n font.sources.push(source);\n }\n return [...byFamily.values()];\n}\n"],"mappings":"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","names":["readFile"]}
|
|
1
|
+
{"version":3,"sources":["../../src/fonts/sources/file-loader.ts","../../src/fonts/cache/disk-cache.ts","../../src/fonts/sources/variable-fetcher.ts","../../src/fonts/rasterize-faces.ts"],"sourcesContent":["/**\n * Load a .ttf/.otf file from disk.\n * Node-only — called from the render pipeline.\n */\n\nimport { readFile } from 'fs/promises';\nimport { isAbsolute, resolve as resolvePath } from 'path';\nimport type { ResolvedFontSource } from '../types';\nimport { detectFontFormat } from './format';\n\nexport interface FileSourceInput {\n path: string;\n weight?: number;\n italic?: boolean;\n baseDir?: string;\n}\n\n/** Read a font file and wrap as a ResolvedFontSource. */\nexport async function loadFileFontSource(\n input: FileSourceInput\n): Promise<ResolvedFontSource> {\n const fullPath = isAbsolute(input.path)\n ? input.path\n : resolvePath(input.baseDir ?? process.cwd(), input.path);\n const data = await readFile(fullPath);\n const format = detectFontFormat(data);\n if (format === 'unknown') {\n throw new Error(\n `Font file at \"${fullPath}\" is not a recognized font file (expected TTF/OTF/WOFF/WOFF2)`\n );\n }\n // No format rejection here: bytes flow to the LibreOffice preview\n // stager, which handles WOFF/WOFF2 natively via fontconfig on\n // Linux/macOS. Office output never embeds these bytes — substitute/\n // custom modes rely on recipient-side fonts.\n return {\n data,\n weight: input.weight ?? 400,\n italic: input.italic ?? false,\n format,\n };\n}\n","/**\n * On-disk cache for fetched Google Fonts TTFs.\n * Optional — only active when a cacheDir is provided. Node-only.\n */\n\nimport { createHash } from 'crypto';\nimport { mkdir, readFile, writeFile } from 'fs/promises';\nimport { join } from 'path';\n\nexport class FontDiskCache {\n private readonly dir: string;\n // In-flight promise dedupes the first-write mkdir across concurrent set()\n // calls. Without it, two simultaneous cold-cache writes could both see\n // `ensured=false`, both issue mkdir, and both flip the flag afterwards —\n // harmless today (recursive mkdir is idempotent) but the pattern is\n // right and leaves room to add per-directory locks if we ever need to.\n private ensurePromise: Promise<void> | null = null;\n\n constructor(dir: string) {\n this.dir = dir;\n }\n\n private ensureDir(): Promise<void> {\n if (!this.ensurePromise) {\n this.ensurePromise = mkdir(this.dir, { recursive: true }).then(\n () => undefined\n );\n }\n return this.ensurePromise;\n }\n\n private pathFor(key: string): string {\n const hash = createHash('sha256').update(key).digest('hex').slice(0, 24);\n return join(this.dir, `${hash}.bin`);\n }\n\n async get(key: string): Promise<Buffer | undefined> {\n try {\n return await readFile(this.pathFor(key));\n } catch {\n return undefined;\n }\n }\n\n async set(key: string, value: Buffer): Promise<void> {\n await this.ensureDir();\n await writeFile(this.pathFor(key), value);\n }\n}\n","/**\n * Variable-font instancer. Fetches a variable font once (disk-cached; TTF,\n * OTF, or WOFF/WOFF2 — fontverter converts compressed containers before\n * instancing), then pins its `wght` axis (plus any additional axes) to\n * produce a clean static TTF per requested weight. Uses harfbuzz via\n * `subset-font` — pure JS + WASM, no native toolchain.\n *\n * Why this exists. Google Fonts serves pre-instanced static TTFs for many\n * families, but the instancing step is lossy: Inter Thin (100) and\n * ExtraLight (200) both ship with `OS/2.usWeightClass=250` and near-\n * identical glyph outlines (xAvgCharWidth differs by 1.8%, glyf table\n * differs by 83 bytes out of 135 KB). Pinning the upstream variable TTF's\n * `wght` axis at exactly 100 vs 200 produces properly distinct instances.\n *\n * Cache strategy:\n * 1. Raw variable font cached at key `varsrc|<url>` — one download per URL\n * per process (+ optional disk layer).\n * 2. Instanced static TTF cached at `variable2|<url>|<weight>|<italic>` —\n * avoids re-running harfbuzz for weights we've already produced.\n *\n * Full-glyph retention. subset-font's `text` parameter drives which\n * codepoints' glyphs survive. We pass every BMP codepoint so the output\n * is effectively a full-glyph static (not a subset) for any Latin /\n * Cyrillic / Greek / Vietnamese-covering family — which includes every\n * entry in our POPULAR_GOOGLE_FONTS catalog. Supplementary-plane glyphs\n * (emoji) would be dropped, but those aren't in the variable families we\n * target. `preserveNameIds` keeps the human-readable name records our\n * downstream normalization expects.\n */\n\nimport type { ResolvedFontSource } from '../types';\nimport { detectFontFormat } from './format';\nimport { rewriteFontSubfamilyNames } from './ttf-name';\nimport { isAllowedFontUrl } from './url-allowlist';\n\n// `subset-font` carries a harfbuzz WASM payload and is Node-only. Lazy-load\n// so a browser bundler that chases the generic `sources/` tree doesn't pull\n// it in. Cached across calls so the WASM heap is created once per process.\nlet subsetFontPromise: Promise<typeof import('subset-font').default> | null =\n null;\nfunction loadSubsetFont(): Promise<typeof import('subset-font').default> {\n if (!subsetFontPromise) {\n subsetFontPromise = import('subset-font').then((m) => m.default);\n }\n return subsetFontPromise;\n}\n\nexport interface VariableFetchOptions {\n url: string;\n weight: number;\n italic: boolean;\n /** Extra axis pins merged on top of the derived `wght` pin (e.g. `ital`,\n * `opsz`, `slnt`). Rare — the `weight`/`italic` pair is usually enough. */\n axes?: Record<string, number>;\n /** Family label used in error messages and diagnostics. */\n familyLabel?: string;\n fetchTimeoutMs?: number;\n fetcher?: typeof fetch;\n memoryCache?: {\n get(key: string): Buffer | undefined;\n set(key: string, value: Buffer): void;\n };\n diskCache?: {\n get(key: string): Promise<Buffer | undefined>;\n set(key: string, value: Buffer): Promise<void>;\n };\n}\n\nfunction rawCacheKey(url: string): string {\n return `varsrc|${url}`;\n}\n\nfunction instanceCacheKey(\n url: string,\n weight: number,\n italic: boolean,\n axes?: Record<string, number>\n): string {\n // Axes go into the key deterministically so different axis pins don't\n // collide. Sorted so `{a:1,b:2}` and `{b:2,a:1}` hash the same.\n const axisPart = axes\n ? '|' +\n Object.entries(axes)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => `${k}=${v}`)\n .join(',')\n : '';\n // `variable2`: v2 stamps standard subfamily names into the instanced\n // output — bumped so persistent disk caches drop pre-stamp instances.\n return `variable2|${url}|${weight}|${italic ? 'i' : 'r'}${axisPart}`;\n}\n\n/**\n * String covering every assigned BMP codepoint (0x20-0xFFFF minus surrogate\n * range). Built lazily on first use — ~127 KiB of UTF-16 memory (0xFFFF\n * codepoints × 2 bytes per UTF-16 code unit, minus the surrogate range)\n * held for the lifetime of the process, which is negligible next to the\n * WASM heap harfbuzz already carries.\n */\nlet cachedBmpCharset: string | null = null;\nfunction bmpCharset(): string {\n if (cachedBmpCharset) return cachedBmpCharset;\n let s = '';\n for (let cp = 0x20; cp <= 0xffff; cp++) {\n // Surrogate range is structurally invalid as standalone codepoints —\n // harfbuzz rejects them. Skip.\n if (cp >= 0xd800 && cp <= 0xdfff) continue;\n s += String.fromCodePoint(cp);\n }\n cachedBmpCharset = s;\n return s;\n}\n\ntype FetchResult = { buf: Buffer } | { error: string };\n\nasync function fetchVariableSource(\n opts: VariableFetchOptions\n): Promise<FetchResult> {\n if (!isAllowedFontUrl(opts.url)) {\n return { error: 'host not in allowlist or non-HTTPS' };\n }\n const key = rawCacheKey(opts.url);\n const mem = opts.memoryCache?.get(key);\n if (mem) return { buf: mem };\n const disk = await opts.diskCache?.get(key);\n if (disk) {\n opts.memoryCache?.set(key, disk);\n return { buf: disk };\n }\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), opts.fetchTimeoutMs ?? 10000);\n try {\n const f = opts.fetcher ?? fetch;\n // redirect: 'manual' so the allowlist can't be bypassed via Location.\n let res = await f(opts.url, { signal: ctrl.signal, redirect: 'manual' });\n let hops = 0;\n while (res.status >= 300 && res.status < 400 && res.status !== 304) {\n const next = res.headers.get('location');\n if (!next) return { error: `${res.status} with no Location` };\n const resolved = new URL(next, opts.url).toString();\n if (!isAllowedFontUrl(resolved)) {\n return { error: `redirect to disallowed host: ${resolved}` };\n }\n if (++hops > 3) return { error: 'too many redirects' };\n res = await f(resolved, { signal: ctrl.signal, redirect: 'manual' });\n }\n if (!res.ok) return { error: `HTTP ${res.status} ${res.statusText}` };\n const ab = await res.arrayBuffer();\n const buf = Buffer.from(ab);\n // Sanity-check: reject sub-1KB or non-TTF responses up front. The\n // instancer would fail loudly on garbage, but a clear \"wrong URL\"\n // signal here shortens the debug cycle.\n if (buf.length < 1024)\n return { error: `response too small (${buf.length}B)` };\n const format = detectFontFormat(buf);\n // WOFF/WOFF2 sources are fine: subset-font funnels every input through\n // fontverter (sfnt/woff/woff2 → truetype) before harfbuzz sees it, and\n // the instanced output is always plain sfnt. Needed in practice —\n // rsms/inter publishes its italic variable master only as woff2.\n if (\n format !== 'ttf' &&\n format !== 'otf' &&\n format !== 'woff' &&\n format !== 'woff2'\n ) {\n return { error: `unexpected font format: ${format}` };\n }\n opts.memoryCache?.set(key, buf);\n await opts.diskCache?.set(key, buf);\n return { buf };\n } catch (err) {\n return { error: (err as Error).message };\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function fetchVariableFontSource(\n opts: VariableFetchOptions\n): Promise<{ source?: ResolvedFontSource; warnings?: string[] }> {\n const key = instanceCacheKey(opts.url, opts.weight, opts.italic, opts.axes);\n const mem = opts.memoryCache?.get(key);\n if (mem) {\n return {\n source: {\n data: mem,\n weight: opts.weight,\n italic: opts.italic,\n format: detectFontFormat(mem),\n },\n warnings: [],\n };\n }\n const disk = await opts.diskCache?.get(key);\n if (disk) {\n opts.memoryCache?.set(key, disk);\n return {\n source: {\n data: disk,\n weight: opts.weight,\n italic: opts.italic,\n format: detectFontFormat(disk),\n },\n warnings: [],\n };\n }\n\n const fetched = await fetchVariableSource(opts);\n if ('error' in fetched) {\n return {\n warnings: [\n `Variable font fetch \"${opts.url}\" for \"${opts.familyLabel ?? opts.url}\" weight ${opts.weight}: ${fetched.error}; falling back to host defaults.`,\n ],\n };\n }\n const raw = fetched.buf;\n\n // Harfbuzz refuses to emit WOFF2 for subset-font's default SFNT target,\n // but we need plain SFNT anyway — Office embeds TTFs, not compressed\n // formats. Pin the weight (and any extra axes) and preserve the name\n // records that our downstream name rewrites (`rewriteFontFamilyName`,\n // `rewriteFontSubfamilyNames`) depend on.\n //\n // Note: italic is encoded by URL (separate italic master), not by axis pin.\n // The `ital` axis exists on some fonts but not others (Inter ships a\n // separate InterVariable-Italic.ttf instead). Callers that want to force\n // an axis pin can pass `axes: { ital: 1 }` explicitly.\n const variationAxes: Record<string, number> = {\n wght: opts.weight,\n ...(opts.axes ?? {}),\n };\n\n let instanced: Buffer;\n try {\n const subsetFont = await loadSubsetFont();\n instanced = await subsetFont(raw, bmpCharset(), {\n targetFormat: 'sfnt',\n variationAxes,\n // Keep every common name record. harfbuzz drops the ones not in\n // this list; our downstream rewrites need 1/2/4/6/16/17 intact.\n preserveNameIds: [\n 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,\n 20, 21, 22, 23, 24, 25,\n ],\n });\n } catch (err) {\n return {\n warnings: [\n `Variable font instancing for \"${opts.familyLabel ?? opts.url}\" weight ${opts.weight}: ${(err as Error).message}`,\n ],\n };\n }\n\n // harfbuzz preserves the source's name records verbatim, so the instanced\n // static still carries the variable font's default-instance subfamily\n // (typically \"Regular\") in nameID 2/17 — which would trip\n // validateFontMetadata's FONT_METADATA_DEFECT warning for every non-\n // Regular weight. Stamp the standard subfamily for the pinned pair.\n instanced = rewriteFontSubfamilyNames(instanced, opts.weight, opts.italic);\n\n opts.memoryCache?.set(key, instanced);\n await opts.diskCache?.set(key, instanced);\n return {\n source: {\n data: instanced,\n weight: opts.weight,\n italic: opts.italic,\n format: detectFontFormat(instanced),\n },\n warnings: [],\n };\n}\n","/**\n * `ResolvedFont[]` ⇄ `RasterizeFontFace[]` — the one encoder/decoder pair for\n * shipping font bytes to the pptx rasterizer.\n *\n * The docx side encodes (core-docx, from `resolveDocumentFonts`) and the\n * rasterizer side decodes (jto-cli, before handing the faces to a\n * `FontStager`). Keeping both halves here means the two cannot drift on\n * base64 handling or on the family-name convention.\n *\n * FAMILY NAMES STAY UNSYNTHESIZED. The wire carries the catalog family\n * (\"Inter\"); the stager applies `synthesizeFamilyName` +\n * `rewriteFontFamilyName` to produce the sub-family the presentation\n * actually references (\"Inter Light\"). Encoding a pre-synthesized name here\n * would make the stager apply the suffix twice.\n *\n * Buffer-dependent → Node-only. Exported from `@json-to-office/shared/fonts/node`.\n */\n\nimport type { ResolvedFont, ResolvedFontSource } from './types';\nimport type { RasterizeFontFace } from '../types/services';\nimport type { GenerationWarning } from '../types/warnings';\n\n/**\n * Formats the rasterizer's native stagers can actually register.\n *\n * All three stagers (fontconfig, macOS Core Text, Windows GDI) write every\n * staged source as a `.ttf` and register it as a raw sfnt, and they rename\n * the face through `rewriteFontFamilyName`, which returns the buffer\n * UNCHANGED for anything without an sfnt header. So a WOFF/WOFF2 (or EOT, or\n * PostScript) source is staged as bytes no font system parses, under the\n * catalog family rather than the synthesized sub-family the presentation\n * references — it renders as fallback text, silently.\n *\n * Shipping those bytes anyway costs wire size, disk writes, and a distinct\n * rasterizer cache key for a render that is identical to the fontless one.\n * An allowlist (rather than a WOFF denylist) keeps any format added to\n * `ResolvedFontSource['format']` later excluded until a stager can handle it.\n */\nconst STAGEABLE_FORMATS = new Set<ResolvedFontSource['format']>(['ttf', 'otf']);\n\n/**\n * Flatten resolved fonts into the serializable wire faces (one face per\n * source variant). Entries with no sources — safe-only fonts, which the\n * renderer resolves against system faces — carry no bytes and are skipped,\n * as are sources in a format no stager can register.\n *\n * @param warnings - sink for one warning per dropped source, shaped like every\n * other generation warning so a caller can hand in the same array it already\n * collects. Both docx entry paths do: a dropped face renders as a fallback,\n * which is precisely the silent substitution this pipeline exists to make\n * visible, so it must not be discoverable only by reading the code.\n */\nexport function toRasterizeFontFaces(\n fonts: readonly ResolvedFont[],\n warnings?: GenerationWarning[]\n): RasterizeFontFace[] {\n const faces: RasterizeFontFace[] = [];\n for (const font of fonts) {\n if (font.sources.length === 0) continue;\n for (const source of font.sources) {\n if (!STAGEABLE_FORMATS.has(source.format)) {\n warnings?.push({\n component: 'fontRegistry',\n severity: 'warning',\n context: { code: 'FONT_FORMAT_NOT_RASTERIZABLE' },\n message:\n `\"${font.family}\" weight ${source.weight}` +\n `${source.italic ? ' italic' : ''} is ${source.format}; the rasterizer's ` +\n `font stagers only register TTF/OTF, so this face is omitted and the ` +\n `visual renders with a fallback face.`,\n });\n continue;\n }\n faces.push({\n family: font.family,\n weight: source.weight,\n italic: source.italic,\n data: source.data.toString('base64'),\n format: source.format as RasterizeFontFace['format'],\n });\n }\n }\n return faces;\n}\n\n/**\n * Inverse of {@link toRasterizeFontFaces}: regroup wire faces back into\n * `ResolvedFont[]` so the existing `FontStager.stage(ResolvedFont[], …)`\n * signature needs no change. Grouping is by exact (case-sensitive) family,\n * matching how the registry keys resolved fonts.\n */\nexport function fromRasterizeFontFaces(\n faces: readonly RasterizeFontFace[]\n): ResolvedFont[] {\n const byFamily = new Map<string, ResolvedFont>();\n for (const face of faces) {\n let font = byFamily.get(face.family);\n if (!font) {\n font = { family: face.family, sources: [], warnings: [] };\n byFamily.set(face.family, font);\n }\n const source: ResolvedFontSource = {\n data: Buffer.from(face.data, 'base64'),\n weight: face.weight,\n italic: face.italic,\n format: face.format ?? 'ttf',\n };\n font.sources.push(source);\n }\n return [...byFamily.values()];\n}\n\n/** Formats a browser's `@font-face` can load; a chart is drawn by one. */\nconst BROWSER_FORMATS = new Set<ResolvedFontSource['format']>([\n 'ttf',\n 'otf',\n 'woff',\n 'woff2',\n]);\n\n/**\n * Flatten resolved fonts into the faces a Highcharts export server can be\n * handed as inline `@font-face` rules. Wider than `toRasterizeFontFaces`:\n * the chart is drawn by Chromium, which reads WOFF and WOFF2 as readily as\n * an sfnt, so only formats no browser loads are dropped. Safe-only fonts\n * carry no bytes and are skipped; the server's own host faces cover them.\n */\nexport function toChartFontFaces(\n fonts: readonly ResolvedFont[]\n): RasterizeFontFace[] {\n const faces: RasterizeFontFace[] = [];\n for (const font of fonts) {\n for (const source of font.sources) {\n if (!BROWSER_FORMATS.has(source.format)) continue;\n faces.push({\n family: font.family,\n weight: source.weight,\n italic: source.italic,\n data: source.data.toString('base64'),\n format: source.format as RasterizeFontFace['format'],\n });\n }\n }\n return faces;\n}\n"],"mappings":"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","names":["readFile"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { C as ComponentDefinition, a as ComponentSchemaConfig, c as convertToJsonSchema, b as createComponentSchema, d as createComponentSchemaObject, e as exportSchemaToFile, f as fixSchemaReferences } from './schema-utils-C5Qdzsgy.js';
|
|
2
2
|
export { AddWarningFunction, GenerationWarning } from './types/warnings.js';
|
|
3
|
-
import { F as FontRegistryEntry, c as FontRuntimeOpts, R as ResolvedFontSource, b as ResolvedFont } from './types-kcQwhOlf.js';
|
|
4
|
-
export { D as DEFAULT_VISUAL_DPI, d as FontFamilyNameSchema, e as FontRegistryDefinition, f as FontRegistryEntrySchema, g as FontRegistrySchema, h as FontSource, i as FontSourceSchema, H as HighchartsHeaders, j as HighchartsHeadersResolver, k as HighchartsServiceConfig, M as MAX_RASTERIZE_BATCH_SLIDES, l as MAX_RASTERIZE_FONTS, m as MAX_RASTERIZE_FONT_BYTES, n as MAX_VISUAL_DPI, o as MIN_VISUAL_DPI, P as PptxBatchRasterizer, p as PptxRasterizeBatchRequest, q as PptxRasterizeBatchResult, r as PptxRasterizeBatchSlide, s as PptxRasterizeBatchSlideResult, t as PptxRasterizeFailureStage, u as PptxRasterizeRequest, v as PptxRasterizeResult, w as PptxRasterizer, x as PptxServiceConfig, y as PptxServiceHeaders, z as PptxServiceHeadersResolver,
|
|
3
|
+
import { F as FontRegistryEntry, c as FontRuntimeOpts, R as ResolvedFontSource, b as ResolvedFont, a as RasterizeFontFace } from './types-kcQwhOlf.js';
|
|
4
|
+
export { D as DEFAULT_VISUAL_DPI, d as FontFamilyNameSchema, e as FontRegistryDefinition, f as FontRegistryEntrySchema, g as FontRegistrySchema, h as FontSource, i as FontSourceSchema, H as HighchartsHeaders, j as HighchartsHeadersResolver, k as HighchartsServiceConfig, M as MAX_RASTERIZE_BATCH_SLIDES, l as MAX_RASTERIZE_FONTS, m as MAX_RASTERIZE_FONT_BYTES, n as MAX_VISUAL_DPI, o as MIN_VISUAL_DPI, P as PptxBatchRasterizer, p as PptxRasterizeBatchRequest, q as PptxRasterizeBatchResult, r as PptxRasterizeBatchSlide, s as PptxRasterizeBatchSlideResult, t as PptxRasterizeFailureStage, u as PptxRasterizeRequest, v as PptxRasterizeResult, w as PptxRasterizer, x as PptxServiceConfig, y as PptxServiceHeaders, z as PptxServiceHeadersResolver, S as SAFE_FONTS, A as SafeFontName, B as ServicesConfig, C as clampVisualDpi, E as isSafeFont } from './types-kcQwhOlf.js';
|
|
5
5
|
export { F as FeatureRequirement, a as FeatureRequirementCollector, O as OfficeFormat, b as OfficeRenderer, R as RENDERER_DEPENDENCY_MISSING, c as RenderOptions, d as RendererDiagnostic, e as RendererDiagnosticSeverity, f as RendererRegistry, g as RendererStatus, h as UnsupportedRendererFeatureError, i as UnsupportedRendererFeatureErrorInit, j as assertNever, k as assertRendererSupports, l as diagnoseUnsupportedFeatures, p as partitionDiagnostics, r as rendererError, m as rendererWarning } from './capabilities-DtPF3aBj.js';
|
|
6
6
|
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';
|
|
7
7
|
export { T as TransformedError, V as ValidationError, a as ValidationResult } from './types-BWFZ7OaO.js';
|
|
@@ -1612,6 +1612,93 @@ declare function resolveDesignColor(value: string, colors: Record<string, string
|
|
|
1612
1612
|
/** Reject dangling/cyclic new tokens before they can become invalid OOXML. */
|
|
1613
1613
|
declare function validateDesignColors(system: DesignSystem, colors: Record<string, string | undefined>): void;
|
|
1614
1614
|
|
|
1615
|
+
/**
|
|
1616
|
+
* The document's typography, expressed as Highcharts options.
|
|
1617
|
+
*
|
|
1618
|
+
* A `highcharts` component is a PNG drawn by a browser that has never seen the
|
|
1619
|
+
* document, so nothing about the page's type reaches the chart on its own: the
|
|
1620
|
+
* axis labels, title and legend come out in the export server's default face
|
|
1621
|
+
* at Highcharts' own sizes, visibly foreign to the prose around them. The
|
|
1622
|
+
* palette already carries (see `chart-palette.ts`); this carries the type.
|
|
1623
|
+
*
|
|
1624
|
+
* Format-neutral on purpose. Each core reads its own theme shape into a
|
|
1625
|
+
* `ChartTypography` — family, colours and sizes in document points — and this
|
|
1626
|
+
* module turns that into the option paths Highcharts styles text through. An
|
|
1627
|
+
* explicit author value keeps winning, property by property, exactly as
|
|
1628
|
+
* `options.colors` does.
|
|
1629
|
+
*
|
|
1630
|
+
* Sizes are converted from points to chart pixels through the scale the chart
|
|
1631
|
+
* is placed at: a 900px chart set into a 450pt measure shrinks by half, so a
|
|
1632
|
+
* label that must read as 9pt on the page is drawn at 18px.
|
|
1633
|
+
*/
|
|
1634
|
+
|
|
1635
|
+
interface ChartTypography {
|
|
1636
|
+
/** CSS `font-family` for everything not styled otherwise (see `cssFontFamily`). */
|
|
1637
|
+
bodyFamily: string;
|
|
1638
|
+
/** CSS `font-family` for the chart title. */
|
|
1639
|
+
headingFamily: string;
|
|
1640
|
+
/** `#RRGGBB` for the title, legend and data labels. */
|
|
1641
|
+
textColor: string;
|
|
1642
|
+
/** `#RRGGBB` for axis text, subtitle, caption and credits. */
|
|
1643
|
+
mutedColor: string;
|
|
1644
|
+
/** Axis labels, axis titles, legend, subtitle and data labels, in points. */
|
|
1645
|
+
labelPt: number;
|
|
1646
|
+
/** Weight for legend items and data labels; Highcharts' own default when unset. */
|
|
1647
|
+
labelWeight?: number;
|
|
1648
|
+
/** Chart title, in points. */
|
|
1649
|
+
titlePt: number;
|
|
1650
|
+
/** Chart title weight; Highcharts' own default when unset. */
|
|
1651
|
+
titleWeight?: number;
|
|
1652
|
+
/** Credits (the source line) and caption, in points. */
|
|
1653
|
+
sourcePt: number;
|
|
1654
|
+
}
|
|
1655
|
+
/** Points per CSS pixel at the 96 dpi both formats assume for an unplaced chart. */
|
|
1656
|
+
declare const POINTS_PER_PIXEL_96DPI = 0.75;
|
|
1657
|
+
/**
|
|
1658
|
+
* How many document points one chart pixel occupies once the image is placed.
|
|
1659
|
+
* Unknown or degenerate widths fall back to 96 dpi, the size an unscaled
|
|
1660
|
+
* chart has in both formats.
|
|
1661
|
+
*/
|
|
1662
|
+
declare function chartPointsPerPixel(chartWidthPx: number, placedWidthPt: number | undefined): number;
|
|
1663
|
+
type FontCategory = NonNullable<FontRegistryEntry['category']>;
|
|
1664
|
+
/**
|
|
1665
|
+
* A CSS `font-family` list: the family, quoted, then the generic it belongs
|
|
1666
|
+
* to — from the registry category when the font is registered, from the
|
|
1667
|
+
* SAFE_FONTS list otherwise — so a face the export server lacks degrades to
|
|
1668
|
+
* the right shape rather than to the browser's default.
|
|
1669
|
+
*/
|
|
1670
|
+
declare function cssFontFamily(family: string, category?: FontCategory): string;
|
|
1671
|
+
/**
|
|
1672
|
+
* `cssFontFamily` bound to a theme: a registered family answers with its
|
|
1673
|
+
* registry category, an unregistered one with what SAFE_FONTS says of it.
|
|
1674
|
+
*/
|
|
1675
|
+
declare function chartFamilyResolver(theme: unknown): (family: string) => string;
|
|
1676
|
+
type Options = Record<string, unknown>;
|
|
1677
|
+
/**
|
|
1678
|
+
* The document's typography written into every Highcharts option path that
|
|
1679
|
+
* styles text, beneath whatever the author set. `ptPerPx` is the placement
|
|
1680
|
+
* scale from `chartPointsPerPixel`.
|
|
1681
|
+
*/
|
|
1682
|
+
declare function withChartTypography<T extends Options>(options: T, typography: ChartTypography, ptPerPx: number): T;
|
|
1683
|
+
/**
|
|
1684
|
+
* `@font-face` rules for the faces of `families`, inlined as data URIs, so an
|
|
1685
|
+
* export server draws a registered font from the same bytes the document
|
|
1686
|
+
* stages rather than from whatever its host happens to have installed. The
|
|
1687
|
+
* bytes go only to the export server, which already receives every data
|
|
1688
|
+
* point of the chart. Empty when no face matches.
|
|
1689
|
+
*/
|
|
1690
|
+
declare function chartFontFaceCss(faces: readonly RasterizeFontFace[], families: readonly string[]): string;
|
|
1691
|
+
/**
|
|
1692
|
+
* The `@font-face` rules for `families` written into a chart's `resources.css`
|
|
1693
|
+
* ahead of whatever the author supplied there. Nothing changes when no face
|
|
1694
|
+
* matches, so a chart set in safe fonts posts the same request it always did.
|
|
1695
|
+
*/
|
|
1696
|
+
declare function withChartFontFaceCss<T extends {
|
|
1697
|
+
resources?: {
|
|
1698
|
+
css?: string;
|
|
1699
|
+
};
|
|
1700
|
+
}>(props: T, faces: readonly RasterizeFontFace[], families: readonly string[]): T;
|
|
1701
|
+
|
|
1615
1702
|
/**
|
|
1616
1703
|
* Deep Merge Utilities
|
|
1617
1704
|
* Generic deep-merge helpers used by both docx and pptx
|
|
@@ -1625,4 +1712,4 @@ declare function validateDesignColors(system: DesignSystem, colors: Record<strin
|
|
|
1625
1712
|
*/
|
|
1626
1713
|
declare function mergeWithDefaults<T>(userConfig: T, themeDefaults: Partial<T>): T;
|
|
1627
1714
|
|
|
1628
|
-
export { CANVASES, ChromeSchema, DEFAULT_CHART_THEME_COLORS, type DesignCanvas, DesignSpacingSchema, type DesignSystem, DesignSystemProperties, DesignSystemSchema, FONT_URL_ALLOWLIST, type FontIssueCode, FontRegistry, FontRegistryEntry, type FontRegistryInput, type FontResolutionIssue, FontRuntimeOpts, type FontSubstitution, type FontValidationInput, type FontValidationResult, MotifSchema, POPULAR_GOOGLE_FONTS, PaletteSchema, type PopularGoogleFont, ROLE_SCALE_STEPS, ResolvedFont, ResolvedFontSource, type SynthesizedFamily, TYPE_ROLES, TextCaseSchema, type TypeRole, type TypeRoleName, TypeRoleNameSchema, TypeRoleSchema, TypeRolesSchema, TypographySchema, UPSTREAM_OVERRIDES, type UpstreamOverride, type UpstreamVariant, WEIGHT_LABELS, applyExportMode, applyFontSubstitution, buildDefaultSubstitutionMap, capsFormatting, collectFontNamesFromDocx, collectFontNamesFromPptx, defaultSubstituteFor, designCanvas, designColors, detectFontFormat, documentFontRegistry, fetchGoogleFontSources, getUpstreamOverride, isAllowedFontUrl, mergeFontRegistries, mergeWithDefaults, resolveDesignColor, resolveTypeRoles, restructureNameDiscriminatedUnions, rewriteFontFamilyName, synthesizeFamilyName, themeFontRegistry, unionBranches, validateDesignColors, validateFontReferences };
|
|
1715
|
+
export { CANVASES, type ChartTypography, ChromeSchema, DEFAULT_CHART_THEME_COLORS, type DesignCanvas, DesignSpacingSchema, type DesignSystem, DesignSystemProperties, DesignSystemSchema, FONT_URL_ALLOWLIST, type FontIssueCode, FontRegistry, FontRegistryEntry, type FontRegistryInput, type FontResolutionIssue, FontRuntimeOpts, type FontSubstitution, type FontValidationInput, type FontValidationResult, MotifSchema, POINTS_PER_PIXEL_96DPI, POPULAR_GOOGLE_FONTS, PaletteSchema, type PopularGoogleFont, ROLE_SCALE_STEPS, RasterizeFontFace, ResolvedFont, ResolvedFontSource, type SynthesizedFamily, TYPE_ROLES, TextCaseSchema, type TypeRole, type TypeRoleName, TypeRoleNameSchema, TypeRoleSchema, TypeRolesSchema, TypographySchema, UPSTREAM_OVERRIDES, type UpstreamOverride, type UpstreamVariant, WEIGHT_LABELS, applyExportMode, applyFontSubstitution, buildDefaultSubstitutionMap, capsFormatting, chartFamilyResolver, chartFontFaceCss, chartPointsPerPixel, collectFontNamesFromDocx, collectFontNamesFromPptx, cssFontFamily, defaultSubstituteFor, designCanvas, designColors, detectFontFormat, documentFontRegistry, fetchGoogleFontSources, getUpstreamOverride, isAllowedFontUrl, mergeFontRegistries, mergeWithDefaults, resolveDesignColor, resolveTypeRoles, restructureNameDiscriminatedUnions, rewriteFontFamilyName, synthesizeFamilyName, themeFontRegistry, unionBranches, validateDesignColors, validateFontReferences, withChartFontFaceCss, withChartTypography };
|
package/dist/index.js
CHANGED
|
@@ -38,18 +38,6 @@ import {
|
|
|
38
38
|
transformValueError,
|
|
39
39
|
transformValueErrors
|
|
40
40
|
} from "./chunk-LLBCT7WL.js";
|
|
41
|
-
import {
|
|
42
|
-
FeatureRequirementCollector,
|
|
43
|
-
RENDERER_DEPENDENCY_MISSING,
|
|
44
|
-
RendererRegistry,
|
|
45
|
-
UnsupportedRendererFeatureError,
|
|
46
|
-
assertNever,
|
|
47
|
-
assertRendererSupports,
|
|
48
|
-
diagnoseUnsupportedFeatures,
|
|
49
|
-
partitionDiagnostics,
|
|
50
|
-
rendererError,
|
|
51
|
-
rendererWarning
|
|
52
|
-
} from "./chunk-QLZNOXT5.js";
|
|
53
41
|
import {
|
|
54
42
|
convertToJsonSchema,
|
|
55
43
|
createComponentSchema,
|
|
@@ -87,6 +75,18 @@ import {
|
|
|
87
75
|
resolveTypeRoles,
|
|
88
76
|
validateDesignColors
|
|
89
77
|
} from "./chunk-4MJFAJFW.js";
|
|
78
|
+
import {
|
|
79
|
+
FeatureRequirementCollector,
|
|
80
|
+
RENDERER_DEPENDENCY_MISSING,
|
|
81
|
+
RendererRegistry,
|
|
82
|
+
UnsupportedRendererFeatureError,
|
|
83
|
+
assertNever,
|
|
84
|
+
assertRendererSupports,
|
|
85
|
+
diagnoseUnsupportedFeatures,
|
|
86
|
+
partitionDiagnostics,
|
|
87
|
+
rendererError,
|
|
88
|
+
rendererWarning
|
|
89
|
+
} from "./chunk-QLZNOXT5.js";
|
|
90
90
|
import {
|
|
91
91
|
compareSemver,
|
|
92
92
|
isValidSemver,
|
|
@@ -240,21 +240,21 @@ var WEIGHT_LABELS = {
|
|
|
240
240
|
800: "ExtraBold",
|
|
241
241
|
900: "Black"
|
|
242
242
|
};
|
|
243
|
-
function synthesizeFamilyName(family,
|
|
244
|
-
if (
|
|
243
|
+
function synthesizeFamilyName(family, weight2, italic) {
|
|
244
|
+
if (weight2 == null) {
|
|
245
245
|
return { family, bold: false, italic, nonCanonicalWeight: false };
|
|
246
246
|
}
|
|
247
|
-
if (
|
|
247
|
+
if (weight2 === 400) {
|
|
248
248
|
return { family, bold: false, italic, nonCanonicalWeight: false };
|
|
249
249
|
}
|
|
250
|
-
if (
|
|
250
|
+
if (weight2 === 700) {
|
|
251
251
|
return { family, bold: true, italic, nonCanonicalWeight: false };
|
|
252
252
|
}
|
|
253
|
-
const label = WEIGHT_LABELS[
|
|
253
|
+
const label = WEIGHT_LABELS[weight2];
|
|
254
254
|
if (!label) {
|
|
255
255
|
return {
|
|
256
256
|
family,
|
|
257
|
-
bold:
|
|
257
|
+
bold: weight2 >= 600,
|
|
258
258
|
italic,
|
|
259
259
|
nonCanonicalWeight: true
|
|
260
260
|
};
|
|
@@ -353,8 +353,8 @@ function parseCssFaces(css) {
|
|
|
353
353
|
}
|
|
354
354
|
return out;
|
|
355
355
|
}
|
|
356
|
-
function cacheKey(family,
|
|
357
|
-
return `google|${family}|${
|
|
356
|
+
function cacheKey(family, weight2, italic) {
|
|
357
|
+
return `google|${family}|${weight2}|${italic ? "i" : "r"}`;
|
|
358
358
|
}
|
|
359
359
|
async function fetchGoogleFontSources(opts) {
|
|
360
360
|
const weights = opts.weights?.length ? opts.weights : [400, 700];
|
|
@@ -464,8 +464,8 @@ async function fetchGoogleFontSources(opts) {
|
|
|
464
464
|
}
|
|
465
465
|
|
|
466
466
|
// src/fonts/sources/url-fetcher.ts
|
|
467
|
-
function cacheKey2(url,
|
|
468
|
-
return `url|${url}|${
|
|
467
|
+
function cacheKey2(url, weight2, italic) {
|
|
468
|
+
return `url|${url}|${weight2}|${italic ? "i" : "r"}`;
|
|
469
469
|
}
|
|
470
470
|
async function fetchUrlFontSource(opts) {
|
|
471
471
|
if (!isAllowedFontUrl(opts.url)) {
|
|
@@ -592,25 +592,25 @@ function readUsWeightClass(ttf) {
|
|
|
592
592
|
if (os2.off + 6 > ttf.length) return null;
|
|
593
593
|
return ttf.readUInt16BE(os2.off + 4);
|
|
594
594
|
}
|
|
595
|
-
function validateFontMetadata(ttf,
|
|
595
|
+
function validateFontMetadata(ttf, weight2, italic, familyLabel) {
|
|
596
596
|
const diags = [];
|
|
597
597
|
const usWeight = readUsWeightClass(ttf);
|
|
598
|
-
if (usWeight != null && usWeight !==
|
|
598
|
+
if (usWeight != null && usWeight !== weight2) {
|
|
599
599
|
diags.push({
|
|
600
600
|
code: "WEIGHT_CLASS_MISMATCH",
|
|
601
|
-
message: `Font "${familyLabel}" weight ${
|
|
601
|
+
message: `Font "${familyLabel}" weight ${weight2}: OS/2.usWeightClass reports ${usWeight}. Likely a defective redistribution \u2014 consider adding an upstream override.`
|
|
602
602
|
});
|
|
603
603
|
}
|
|
604
604
|
const declaredFamilies = readFontFamilyNames(ttf);
|
|
605
605
|
if (declaredFamilies.length > 0 && !declaredFamilies.includes(familyLabel.trim())) {
|
|
606
606
|
diags.push({
|
|
607
607
|
code: "FAMILY_MISMATCH",
|
|
608
|
-
message: `Font "${familyLabel}" weight ${
|
|
608
|
+
message: `Font "${familyLabel}" weight ${weight2}${italic ? " italic" : ""}: name table declares ${declaredFamilies.map((f) => `"${f}"`).join(
|
|
609
609
|
" / "
|
|
610
610
|
)}, not "${familyLabel}". Referencing runs will not resolve this face.`
|
|
611
611
|
});
|
|
612
612
|
}
|
|
613
|
-
const std = standardSubfamilyNames(
|
|
613
|
+
const std = standardSubfamilyNames(weight2, italic);
|
|
614
614
|
if (!std) return diags;
|
|
615
615
|
const expected17 = std.typographic;
|
|
616
616
|
const expected2 = std.legacy;
|
|
@@ -619,13 +619,13 @@ function validateFontMetadata(ttf, weight, italic, familyLabel) {
|
|
|
619
619
|
if (n.nameID === 17 && n.value !== expected17) {
|
|
620
620
|
diags.push({
|
|
621
621
|
code: "SUBFAMILY_MISMATCH",
|
|
622
|
-
message: `Font "${familyLabel}" weight ${
|
|
622
|
+
message: `Font "${familyLabel}" weight ${weight2}${italic ? " italic" : ""}: name record (platform ${n.platformID}) nameID 17 = "${n.value}", expected "${expected17}".`
|
|
623
623
|
});
|
|
624
624
|
}
|
|
625
625
|
if (n.nameID === 2 && n.value !== expected2) {
|
|
626
626
|
diags.push({
|
|
627
627
|
code: "LEGACY_SUBFAMILY_MISMATCH",
|
|
628
|
-
message: `Font "${familyLabel}" weight ${
|
|
628
|
+
message: `Font "${familyLabel}" weight ${weight2}${italic ? " italic" : ""}: name record (platform ${n.platformID}) nameID 2 = "${n.value}", expected "${expected2}".`
|
|
629
629
|
});
|
|
630
630
|
}
|
|
631
631
|
}
|
|
@@ -645,8 +645,8 @@ var SFNT_VERSIONS = /* @__PURE__ */ new Set([
|
|
|
645
645
|
]);
|
|
646
646
|
var HEADER_SIZE2 = 12;
|
|
647
647
|
var TABLE_RECORD_SIZE2 = 16;
|
|
648
|
-
function validateFontStructure(ttf,
|
|
649
|
-
const face = `Font "${familyLabel}" weight ${
|
|
648
|
+
function validateFontStructure(ttf, weight2, italic, familyLabel) {
|
|
649
|
+
const face = `Font "${familyLabel}" weight ${weight2}${italic ? " italic" : ""}`;
|
|
650
650
|
const unreadable = (reason) => ({
|
|
651
651
|
code: "FONT_UNREADABLE",
|
|
652
652
|
message: `${face}: ${reason} The face will not resolve; text referencing it renders in a fallback.`
|
|
@@ -1160,16 +1160,16 @@ var INTER_VARIABLE_URL = "https://cdn.jsdelivr.net/gh/rsms/inter@v4.1/docs/font-
|
|
|
1160
1160
|
var INTER_VARIABLE_ITALIC_URL = "https://cdn.jsdelivr.net/gh/rsms/inter@v4.1/docs/font-files/InterVariable-Italic.woff2";
|
|
1161
1161
|
function interVariants() {
|
|
1162
1162
|
const weights = [100, 200, 300, 400, 500, 600, 700, 800, 900];
|
|
1163
|
-
const upright = weights.map((
|
|
1163
|
+
const upright = weights.map((weight2) => ({
|
|
1164
1164
|
kind: "variable",
|
|
1165
1165
|
url: INTER_VARIABLE_URL,
|
|
1166
|
-
weight,
|
|
1166
|
+
weight: weight2,
|
|
1167
1167
|
italic: false
|
|
1168
1168
|
}));
|
|
1169
|
-
const italic = weights.map((
|
|
1169
|
+
const italic = weights.map((weight2) => ({
|
|
1170
1170
|
kind: "variable",
|
|
1171
1171
|
url: INTER_VARIABLE_ITALIC_URL,
|
|
1172
|
-
weight,
|
|
1172
|
+
weight: weight2,
|
|
1173
1173
|
italic: true
|
|
1174
1174
|
}));
|
|
1175
1175
|
return [...upright, ...italic];
|
|
@@ -1371,6 +1371,118 @@ var DEFAULT_CHART_THEME_COLORS = [
|
|
|
1371
1371
|
"accent6"
|
|
1372
1372
|
];
|
|
1373
1373
|
|
|
1374
|
+
// src/theme/chart-typography.ts
|
|
1375
|
+
var POINTS_PER_PIXEL_96DPI = 0.75;
|
|
1376
|
+
function chartPointsPerPixel(chartWidthPx, placedWidthPt) {
|
|
1377
|
+
if (!Number.isFinite(chartWidthPx) || chartWidthPx <= 0 || placedWidthPt === void 0 || !Number.isFinite(placedWidthPt) || placedWidthPt <= 0) {
|
|
1378
|
+
return POINTS_PER_PIXEL_96DPI;
|
|
1379
|
+
}
|
|
1380
|
+
return placedWidthPt / chartWidthPx;
|
|
1381
|
+
}
|
|
1382
|
+
var SERIF_FAMILIES = /* @__PURE__ */ new Set(["georgia", "times new roman", "cambria"]);
|
|
1383
|
+
var MONO_FAMILIES = /* @__PURE__ */ new Set(["consolas", "courier new", "menlo", "monaco"]);
|
|
1384
|
+
function cssFontFamily(family, category) {
|
|
1385
|
+
const generic = category === "serif" ? "serif" : category === "mono" ? "monospace" : category === "handwriting" ? "cursive" : category === void 0 && SERIF_FAMILIES.has(family.toLowerCase()) ? "serif" : category === void 0 && MONO_FAMILIES.has(family.toLowerCase()) ? "monospace" : "sans-serif";
|
|
1386
|
+
return `"${family.replace(/["\\]/g, "\\$&")}", ${generic}`;
|
|
1387
|
+
}
|
|
1388
|
+
function chartFamilyResolver(theme) {
|
|
1389
|
+
const categories = new Map(
|
|
1390
|
+
themeFontRegistry(theme).map((entry) => [
|
|
1391
|
+
entry.family.toLowerCase(),
|
|
1392
|
+
entry.category
|
|
1393
|
+
])
|
|
1394
|
+
);
|
|
1395
|
+
return (family) => cssFontFamily(family, categories.get(family.toLowerCase()));
|
|
1396
|
+
}
|
|
1397
|
+
function isPlainObject(value) {
|
|
1398
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1399
|
+
}
|
|
1400
|
+
function fill(authored, defaults) {
|
|
1401
|
+
if (authored !== void 0 && !isPlainObject(authored)) return authored;
|
|
1402
|
+
const base = isPlainObject(authored) ? { ...authored } : {};
|
|
1403
|
+
for (const [key, value] of Object.entries(defaults)) {
|
|
1404
|
+
if (value === void 0) continue;
|
|
1405
|
+
const current = base[key];
|
|
1406
|
+
if (current === void 0) {
|
|
1407
|
+
base[key] = isPlainObject(value) ? fill(void 0, value) : value;
|
|
1408
|
+
} else if (isPlainObject(current) && isPlainObject(value)) {
|
|
1409
|
+
base[key] = fill(current, value);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
return base;
|
|
1413
|
+
}
|
|
1414
|
+
function fillAxis(authored, defaults) {
|
|
1415
|
+
if (Array.isArray(authored)) {
|
|
1416
|
+
return authored.map((axis) => fill(axis, defaults));
|
|
1417
|
+
}
|
|
1418
|
+
return fill(authored, defaults);
|
|
1419
|
+
}
|
|
1420
|
+
function weight(value) {
|
|
1421
|
+
return value === void 0 ? void 0 : String(value);
|
|
1422
|
+
}
|
|
1423
|
+
function withChartTypography(options, typography, ptPerPx) {
|
|
1424
|
+
const px = (points) => `${Math.round(points / ptPerPx * 10) / 10}px`;
|
|
1425
|
+
const labelPx = px(typography.labelPt);
|
|
1426
|
+
const sourcePx = px(typography.sourcePt);
|
|
1427
|
+
const mutedText = { fontSize: labelPx, color: typography.mutedColor };
|
|
1428
|
+
const mutedSource = { fontSize: sourcePx, color: typography.mutedColor };
|
|
1429
|
+
const labelText = {
|
|
1430
|
+
fontSize: labelPx,
|
|
1431
|
+
color: typography.textColor,
|
|
1432
|
+
fontWeight: weight(typography.labelWeight)
|
|
1433
|
+
};
|
|
1434
|
+
const axis = { labels: { style: mutedText }, title: { style: mutedText } };
|
|
1435
|
+
return {
|
|
1436
|
+
...options,
|
|
1437
|
+
chart: fill(options.chart, {
|
|
1438
|
+
style: { fontFamily: typography.bodyFamily }
|
|
1439
|
+
}),
|
|
1440
|
+
title: fill(options.title, {
|
|
1441
|
+
style: {
|
|
1442
|
+
fontFamily: typography.headingFamily,
|
|
1443
|
+
fontSize: px(typography.titlePt),
|
|
1444
|
+
fontWeight: weight(typography.titleWeight),
|
|
1445
|
+
color: typography.textColor
|
|
1446
|
+
}
|
|
1447
|
+
}),
|
|
1448
|
+
subtitle: fill(options.subtitle, { style: mutedText }),
|
|
1449
|
+
caption: fill(options.caption, { style: mutedSource }),
|
|
1450
|
+
xAxis: fillAxis(options.xAxis, axis),
|
|
1451
|
+
yAxis: fillAxis(options.yAxis, axis),
|
|
1452
|
+
legend: fill(options.legend, { itemStyle: labelText }),
|
|
1453
|
+
plotOptions: fill(options.plotOptions, {
|
|
1454
|
+
series: { dataLabels: { style: labelText } }
|
|
1455
|
+
}),
|
|
1456
|
+
credits: fill(options.credits, { style: mutedSource })
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
var FONT_FORMATS = {
|
|
1460
|
+
ttf: { mime: "font/ttf", format: "truetype" },
|
|
1461
|
+
otf: { mime: "font/otf", format: "opentype" },
|
|
1462
|
+
woff: { mime: "font/woff", format: "woff" },
|
|
1463
|
+
woff2: { mime: "font/woff2", format: "woff2" }
|
|
1464
|
+
};
|
|
1465
|
+
function chartFontFaceCss(faces, families) {
|
|
1466
|
+
const wanted = new Set(families.map((family) => family.toLowerCase()));
|
|
1467
|
+
return faces.filter((face) => wanted.has(face.family.toLowerCase())).map((face) => {
|
|
1468
|
+
const { mime, format } = FONT_FORMATS[face.format ?? "ttf"];
|
|
1469
|
+
return `@font-face{font-family:"${face.family.replace(/["\\]/g, "\\$&")}";font-weight:${face.weight};font-style:${face.italic ? "italic" : "normal"};src:url(data:${mime};base64,${face.data}) format("${format}")}`;
|
|
1470
|
+
}).join("\n");
|
|
1471
|
+
}
|
|
1472
|
+
function withChartFontFaceCss(props, faces, families) {
|
|
1473
|
+
const css = chartFontFaceCss(faces, families);
|
|
1474
|
+
if (!css) return props;
|
|
1475
|
+
const authored = props.resources?.css;
|
|
1476
|
+
return {
|
|
1477
|
+
...props,
|
|
1478
|
+
resources: {
|
|
1479
|
+
...props.resources,
|
|
1480
|
+
css: authored ? `${css}
|
|
1481
|
+
${authored}` : css
|
|
1482
|
+
}
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1374
1486
|
// src/utils/deepMerge.ts
|
|
1375
1487
|
function isObject(item) {
|
|
1376
1488
|
return item !== null && typeof item === "object" && !Array.isArray(item);
|
|
@@ -1420,6 +1532,7 @@ export {
|
|
|
1420
1532
|
MAX_VISUAL_DPI,
|
|
1421
1533
|
MIN_VISUAL_DPI,
|
|
1422
1534
|
MotifSchema,
|
|
1535
|
+
POINTS_PER_PIXEL_96DPI,
|
|
1423
1536
|
POPULAR_GOOGLE_FONTS,
|
|
1424
1537
|
PaletteSchema,
|
|
1425
1538
|
RENDERER_DEPENDENCY_MISSING,
|
|
@@ -1443,6 +1556,9 @@ export {
|
|
|
1443
1556
|
buildDefaultSubstitutionMap,
|
|
1444
1557
|
calculatePosition,
|
|
1445
1558
|
capsFormatting,
|
|
1559
|
+
chartFamilyResolver,
|
|
1560
|
+
chartFontFaceCss,
|
|
1561
|
+
chartPointsPerPixel,
|
|
1446
1562
|
clampVisualDpi,
|
|
1447
1563
|
clearComponentNamesCache,
|
|
1448
1564
|
collectFontNamesFromDocx,
|
|
@@ -1455,6 +1571,7 @@ export {
|
|
|
1455
1571
|
createErrorConfig,
|
|
1456
1572
|
createJsonParseError,
|
|
1457
1573
|
createVersion,
|
|
1574
|
+
cssFontFamily,
|
|
1458
1575
|
defaultSubstituteFor,
|
|
1459
1576
|
designCanvas,
|
|
1460
1577
|
designColors,
|
|
@@ -1499,6 +1616,8 @@ export {
|
|
|
1499
1616
|
unionBranches,
|
|
1500
1617
|
validateCustomComponentProps,
|
|
1501
1618
|
validateDesignColors,
|
|
1502
|
-
validateFontReferences
|
|
1619
|
+
validateFontReferences,
|
|
1620
|
+
withChartFontFaceCss,
|
|
1621
|
+
withChartTypography
|
|
1503
1622
|
};
|
|
1504
1623
|
//# sourceMappingURL=index.js.map
|