@nowline/export-core 0.5.0 → 0.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/browser.d.ts +10 -0
- package/dist/fonts/browser.d.ts.map +1 -0
- package/dist/fonts/browser.js +43 -0
- package/dist/fonts/browser.js.map +1 -0
- package/dist/fonts/bundled.d.ts +8 -0
- package/dist/fonts/bundled.d.ts.map +1 -1
- package/dist/fonts/bundled.js +28 -4
- package/dist/fonts/bundled.js.map +1 -1
- package/dist/fonts/index.d.ts +1 -0
- package/dist/fonts/index.d.ts.map +1 -1
- package/dist/fonts/index.js +1 -0
- package/dist/fonts/index.js.map +1 -1
- package/dist/fonts/resolve.d.ts +22 -4
- package/dist/fonts/resolve.d.ts.map +1 -1
- package/dist/fonts/resolve.js +72 -25
- package/dist/fonts/resolve.js.map +1 -1
- package/dist/fonts/sfns.d.ts.map +1 -1
- package/dist/fonts/sfns.js +8 -6
- package/dist/fonts/sfns.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/fonts/browser.ts +47 -0
- package/src/fonts/bundled.ts +28 -4
- package/src/fonts/index.ts +1 -0
- package/src/fonts/resolve.ts +108 -30
- package/src/fonts/sfns.ts +8 -6
- package/src/index.ts +3 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ResolvedFontPair } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Return the canonical bundled DejaVu font pair decoded with browser-compatible
|
|
4
|
+
* APIs (`atob`). Result is cached after the first call so the ~1 MB decode only
|
|
5
|
+
* runs once per page. Safe to call before wasm initialization.
|
|
6
|
+
*/
|
|
7
|
+
export declare function loadBundledFontsForBrowser(): ResolvedFontPair;
|
|
8
|
+
/** Test seam: drop the cached pair so isolated tests start fresh. */
|
|
9
|
+
export declare function _clearBrowserFontsCache(): void;
|
|
10
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/fonts/browser.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAWpD;;;;GAIG;AACH,wBAAgB,0BAA0B,IAAI,gBAAgB,CAY7D;AAED,qEAAqE;AACrE,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Browser-compatible bundled font loader.
|
|
2
|
+
//
|
|
3
|
+
// Same bundled DejaVu bytes as loadBundledSans/loadBundledMono (Node), but
|
|
4
|
+
// decoded with `atob()` instead of Node's `Buffer.from()` so the module
|
|
5
|
+
// bundles cleanly for the browser (the Free/Pro web apps, Playwright legs).
|
|
6
|
+
//
|
|
7
|
+
// Usage: `loadBundledFontsForBrowser()` returns a cached `ResolvedFontPair`
|
|
8
|
+
// synchronously after the first call. Call it once at module init or lazy-once
|
|
9
|
+
// inside your `loadWasm` / font-loading step.
|
|
10
|
+
//
|
|
11
|
+
// Node callers: use `loadBundledSans()` / `loadBundledMono()` from bundled.ts.
|
|
12
|
+
import { MONO_BASE64, SANS_BASE64 } from '../generated/bundled-fonts.js';
|
|
13
|
+
function b64ToBytes(b64) {
|
|
14
|
+
const binary = atob(b64);
|
|
15
|
+
const out = new Uint8Array(binary.length);
|
|
16
|
+
for (let i = 0; i < binary.length; i++)
|
|
17
|
+
out[i] = binary.charCodeAt(i);
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
let cachedFonts;
|
|
21
|
+
/**
|
|
22
|
+
* Return the canonical bundled DejaVu font pair decoded with browser-compatible
|
|
23
|
+
* APIs (`atob`). Result is cached after the first call so the ~1 MB decode only
|
|
24
|
+
* runs once per page. Safe to call before wasm initialization.
|
|
25
|
+
*/
|
|
26
|
+
export function loadBundledFontsForBrowser() {
|
|
27
|
+
if (!cachedFonts) {
|
|
28
|
+
cachedFonts = {
|
|
29
|
+
sans: { name: 'DejaVu Sans', bytes: b64ToBytes(SANS_BASE64), source: 'bundled' },
|
|
30
|
+
mono: {
|
|
31
|
+
name: 'DejaVu Sans Mono',
|
|
32
|
+
bytes: b64ToBytes(MONO_BASE64),
|
|
33
|
+
source: 'bundled',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return cachedFonts;
|
|
38
|
+
}
|
|
39
|
+
/** Test seam: drop the cached pair so isolated tests start fresh. */
|
|
40
|
+
export function _clearBrowserFontsCache() {
|
|
41
|
+
cachedFonts = undefined;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/fonts/browser.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,EAAE;AACF,2EAA2E;AAC3E,wEAAwE;AACxE,4EAA4E;AAC5E,EAAE;AACF,4EAA4E;AAC5E,+EAA+E;AAC/E,8CAA8C;AAC9C,EAAE;AACF,+EAA+E;AAE/E,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAGzE,SAAS,UAAU,CAAC,GAAW;IAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACtE,OAAO,GAAG,CAAC;AACf,CAAC;AAED,IAAI,WAAyC,CAAC;AAE9C;;;;GAIG;AACH,MAAM,UAAU,0BAA0B;IACtC,IAAI,CAAC,WAAW,EAAE,CAAC;QACf,WAAW,GAAG;YACV,IAAI,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE;YAChF,IAAI,EAAE;gBACF,IAAI,EAAE,kBAAkB;gBACxB,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC;gBAC9B,MAAM,EAAE,SAAS;aACpB;SACJ,CAAC;IACN,CAAC;IACD,OAAO,WAAW,CAAC;AACvB,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,uBAAuB;IACnC,WAAW,GAAG,SAAS,CAAC;AAC5B,CAAC"}
|
package/dist/fonts/bundled.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
export declare const BUNDLED_SANS_PATH: string;
|
|
2
2
|
export declare const BUNDLED_MONO_PATH: string;
|
|
3
|
+
/**
|
|
4
|
+
* Family names stamped on the bundled DejaVu faces. The resolver's
|
|
5
|
+
* `ResolvedFont.name`, the renderer's pinned `font-family`, the resvg family
|
|
6
|
+
* hints, and the live-preview `@font-face` must all use these exact strings
|
|
7
|
+
* so preview and raster export name the same face (the WYSIWYG contract).
|
|
8
|
+
*/
|
|
9
|
+
export declare const BUNDLED_SANS_FAMILY = "DejaVu Sans";
|
|
10
|
+
export declare const BUNDLED_MONO_FAMILY = "DejaVu Sans Mono";
|
|
3
11
|
export declare function loadBundledSans(): Promise<Uint8Array>;
|
|
4
12
|
export declare function loadBundledMono(): Promise<Uint8Array>;
|
|
5
13
|
/** Test seam: drop cached bytes so platform-mocked tests start fresh. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundled.d.ts","sourceRoot":"","sources":["../../src/fonts/bundled.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"bundled.d.ts","sourceRoot":"","sources":["../../src/fonts/bundled.ts"],"names":[],"mappings":"AA8CA,eAAO,MAAM,iBAAiB,QAA0C,CAAC;AACzE,eAAO,MAAM,iBAAiB,QAA8C,CAAC;AAE7E;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AACjD,eAAO,MAAM,mBAAmB,qBAAqB,CAAC;AAUtD,wBAAsB,eAAe,IAAI,OAAO,CAAC,UAAU,CAAC,CAI3D;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,UAAU,CAAC,CAI3D;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC"}
|
package/dist/fonts/bundled.js
CHANGED
|
@@ -16,16 +16,40 @@
|
|
|
16
16
|
import * as path from 'node:path';
|
|
17
17
|
import { fileURLToPath } from 'node:url';
|
|
18
18
|
import { MONO_BASE64, SANS_BASE64 } from '../generated/bundled-fonts.js';
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
|
|
19
|
+
// Resolve the on-disk assets/fonts directory from this module's location.
|
|
20
|
+
// `import.meta.url` is rewritten to `undefined` when this module is bundled
|
|
21
|
+
// into a CommonJS context — esbuild collapses `import.meta` to `{}` for the VS
|
|
22
|
+
// Code extension's `dist/extension.cjs`. The two exported paths below are
|
|
23
|
+
// informational only (the runtime decodes the embedded base64 above, never
|
|
24
|
+
// reads these files), so degrade to a bare relative directory instead of
|
|
25
|
+
// throwing `ERR_INVALID_ARG_TYPE` at module load — which would crash the
|
|
26
|
+
// extension before it can activate. See packages/vscode-extension.
|
|
27
|
+
function bundledFontsDir() {
|
|
28
|
+
try {
|
|
29
|
+
// dist/fonts/bundled.js → ../../assets/fonts/
|
|
30
|
+
// src/fonts/bundled.ts → ../../assets/fonts/
|
|
31
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
32
|
+
return path.resolve(here, '..', '..', 'assets', 'fonts');
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return path.join('assets', 'fonts');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const ASSETS_DIR = bundledFontsDir();
|
|
23
39
|
// Informational: where the source-of-truth TTFs live on disk for users
|
|
24
40
|
// running under Node. Not the runtime load source — `loadBundledSans` and
|
|
25
41
|
// `loadBundledMono` decode from the embedded base64 instead. Inside a bun
|
|
26
42
|
// --compile binary these paths are phantom (the assets are not on disk).
|
|
27
43
|
export const BUNDLED_SANS_PATH = path.join(ASSETS_DIR, 'DejaVuSans.ttf');
|
|
28
44
|
export const BUNDLED_MONO_PATH = path.join(ASSETS_DIR, 'DejaVuSansMono.ttf');
|
|
45
|
+
/**
|
|
46
|
+
* Family names stamped on the bundled DejaVu faces. The resolver's
|
|
47
|
+
* `ResolvedFont.name`, the renderer's pinned `font-family`, the resvg family
|
|
48
|
+
* hints, and the live-preview `@font-face` must all use these exact strings
|
|
49
|
+
* so preview and raster export name the same face (the WYSIWYG contract).
|
|
50
|
+
*/
|
|
51
|
+
export const BUNDLED_SANS_FAMILY = 'DejaVu Sans';
|
|
52
|
+
export const BUNDLED_MONO_FAMILY = 'DejaVu Sans Mono';
|
|
29
53
|
let cachedSans;
|
|
30
54
|
let cachedMono;
|
|
31
55
|
function decodeBase64(b64) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundled.js","sourceRoot":"","sources":["../../src/fonts/bundled.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,EAAE;AACF,uDAAuD;AACvD,EAAE;AACF,6EAA6E;AAC7E,0EAA0E;AAC1E,6EAA6E;AAC7E,SAAS;AACT,EAAE;AACF,2DAA2D;AAC3D,2EAA2E;AAC3E,6EAA6E;AAC7E,2EAA2E;AAC3E,qEAAqE;AACrE,2BAA2B;AAE3B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAEzE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"bundled.js","sourceRoot":"","sources":["../../src/fonts/bundled.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,EAAE;AACF,uDAAuD;AACvD,EAAE;AACF,6EAA6E;AAC7E,0EAA0E;AAC1E,6EAA6E;AAC7E,SAAS;AACT,EAAE;AACF,2DAA2D;AAC3D,2EAA2E;AAC3E,6EAA6E;AAC7E,2EAA2E;AAC3E,qEAAqE;AACrE,2BAA2B;AAE3B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAEzE,0EAA0E;AAC1E,4EAA4E;AAC5E,+EAA+E;AAC/E,0EAA0E;AAC1E,2EAA2E;AAC3E,yEAAyE;AACzE,yEAAyE;AACzE,mEAAmE;AACnE,SAAS,eAAe;IACpB,IAAI,CAAC;QACD,8CAA8C;QAC9C,+CAA+C;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,GAAG,eAAe,EAAE,CAAC;AAErC,uEAAuE;AACvE,0EAA0E;AAC1E,0EAA0E;AAC1E,yEAAyE;AACzE,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC;AACzE,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,oBAAoB,CAAC,CAAC;AAE7E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,aAAa,CAAC;AACjD,MAAM,CAAC,MAAM,mBAAmB,GAAG,kBAAkB,CAAC;AAEtD,IAAI,UAAkC,CAAC;AACvC,IAAI,UAAkC,CAAC;AAEvC,SAAS,YAAY,CAAC,GAAW;IAC7B,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACvC,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe;IACjC,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IAClC,UAAU,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IACvC,OAAO,UAAU,CAAC;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe;IACjC,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IAClC,UAAU,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IACvC,OAAO,UAAU,CAAC;AACtB,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,iBAAiB;IAC7B,UAAU,GAAG,SAAS,CAAC;IACvB,UAAU,GAAG,SAAS,CAAC;AAC3B,CAAC"}
|
package/dist/fonts/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { _clearBrowserFontsCache, loadBundledFontsForBrowser } from './browser.js';
|
|
1
2
|
export { BUNDLED_MONO_PATH, BUNDLED_SANS_PATH, clearBundledCache, loadBundledMono, loadBundledSans, } from './bundled.js';
|
|
2
3
|
export { ALIASES, aliasCandidate, type FontCandidate, isAlias, type PlatformProbe, probeListFor, } from './probe-list.js';
|
|
3
4
|
export type { ResolveOptions, ResolveResult } from './resolve.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fonts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,eAAe,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EACH,OAAO,EACP,cAAc,EACd,KAAK,aAAa,EAClB,OAAO,EACP,KAAK,aAAa,EAClB,YAAY,GACf,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fonts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EACH,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,eAAe,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EACH,OAAO,EACP,cAAc,EACd,KAAK,aAAa,EAClB,OAAO,EACP,KAAK,aAAa,EAClB,YAAY,GACf,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/fonts/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { _clearBrowserFontsCache, loadBundledFontsForBrowser } from './browser.js';
|
|
1
2
|
export { BUNDLED_MONO_PATH, BUNDLED_SANS_PATH, clearBundledCache, loadBundledMono, loadBundledSans, } from './bundled.js';
|
|
2
3
|
export { ALIASES, aliasCandidate, isAlias, probeListFor, } from './probe-list.js';
|
|
3
4
|
export { FontResolveError, resolveFonts } from './resolve.js';
|
package/dist/fonts/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fonts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,eAAe,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EACH,OAAO,EACP,cAAc,EAEd,OAAO,EAEP,YAAY,GACf,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fonts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EACH,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,eAAe,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EACH,OAAO,EACP,cAAc,EAEd,OAAO,EAEP,YAAY,GACf,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/fonts/resolve.d.ts
CHANGED
|
@@ -5,9 +5,18 @@ export interface ResolveOptions {
|
|
|
5
5
|
/** Path or alias for the mono role. */
|
|
6
6
|
fontMono?: string;
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Opt in to the platform font probe (step 4). Off by default: the
|
|
9
|
+
* resolver is bundled-first, so the default (no flag/env) is the bundled
|
|
10
|
+
* DejaVu pair on every OS. With this set the probe runs and the first
|
|
11
|
+
* STATIC system font wins (variable fonts are skipped), bundled if none.
|
|
12
|
+
*/
|
|
13
|
+
useSystemFonts?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Skip the probe; go straight to the bundled DejaVu pair. Implied by
|
|
9
16
|
* `NOWLINE_HEADLESS=1` and by `CI=true` without a TTY (unless
|
|
10
|
-
* `disableAutoHeadless` is set).
|
|
17
|
+
* `disableAutoHeadless` is set). Largely redundant now that bundled is the
|
|
18
|
+
* default, but retained so an explicit `--headless` still forces bundled
|
|
19
|
+
* even alongside `useSystemFonts`.
|
|
11
20
|
*/
|
|
12
21
|
headless?: boolean;
|
|
13
22
|
/** Disable the CI-no-TTY auto-headless heuristic. Defaults to false. */
|
|
@@ -22,11 +31,20 @@ export interface ResolveResult {
|
|
|
22
31
|
sans: ResolvedFont;
|
|
23
32
|
mono: ResolvedFont;
|
|
24
33
|
/**
|
|
25
|
-
* True when
|
|
26
|
-
*
|
|
34
|
+
* True when a role landed on bundled DejaVu *after an opted-in probe found
|
|
35
|
+
* nothing usable* (`useSystemFonts` set, no static system font present).
|
|
36
|
+
* The bundled-first default is the intended path, not a fallback, so it
|
|
37
|
+
* does NOT set this. Callers (CLI) emit a `--strict` warning on this.
|
|
27
38
|
*/
|
|
28
39
|
sansFellBackToBundled: boolean;
|
|
29
40
|
monoFellBackToBundled: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* True when an explicitly requested font (flag / env / alias) was a
|
|
43
|
+
* variable font and was replaced by the bundled DejaVu (raster cannot
|
|
44
|
+
* render a VF). Callers warn; the CLI errors under `--strict`.
|
|
45
|
+
*/
|
|
46
|
+
sansVariableFontSubstituted: boolean;
|
|
47
|
+
monoVariableFontSubstituted: boolean;
|
|
30
48
|
}
|
|
31
49
|
export declare function resolveFonts(options?: ResolveOptions): Promise<ResolveResult>;
|
|
32
50
|
export declare class FontResolveError extends Error {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/fonts/resolve.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/fonts/resolve.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAwB,YAAY,EAAE,MAAM,aAAa,CAAC;AAUtE,MAAM,WAAW,cAAc;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wEAAwE;IACxE,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IACpC,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACnD,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,YAAY,CAAC;IACnB;;;;;OAKG;IACH,qBAAqB,EAAE,OAAO,CAAC;IAC/B,qBAAqB,EAAE,OAAO,CAAC;IAC/B;;;;OAIG;IACH,2BAA2B,EAAE,OAAO,CAAC;IACrC,2BAA2B,EAAE,OAAO,CAAC;CACxC;AAED,wBAAsB,YAAY,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CAqCvF;AA6KD,qBAAa,gBAAiB,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI9B"}
|
package/dist/fonts/resolve.js
CHANGED
|
@@ -1,18 +1,32 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Bundled-first font resolver shared by PDF and PNG.
|
|
2
2
|
//
|
|
3
|
-
// Spec: specs/handoffs/m2c.md § 10 "Font strategy —
|
|
4
|
-
//
|
|
3
|
+
// Spec: specs/handoffs/m2c.md § 10 "Font strategy — bundled first, system
|
|
4
|
+
// fonts opt-in".
|
|
5
|
+
//
|
|
6
|
+
// The resolver defaults to the bundled static DejaVu pair on every OS so that
|
|
7
|
+
// preview and raster export look identical everywhere and we never hand
|
|
8
|
+
// `@resvg/resvg-wasm` a variable font (which it cannot rasterize — see
|
|
9
|
+
// `sfns.ts`). System fonts are an explicit opt-in.
|
|
5
10
|
//
|
|
6
11
|
// Resolution order (run independently for sans / mono — first hit wins):
|
|
7
12
|
// 1. Explicit flag — `--font-sans <path|alias>` / `--font-mono <path|alias>`
|
|
8
13
|
// 2. Environment — NOWLINE_FONT_SANS / NOWLINE_FONT_MONO
|
|
9
|
-
// 3. Headless
|
|
10
|
-
// without a TTY
|
|
11
|
-
//
|
|
12
|
-
//
|
|
14
|
+
// 3. Headless / default — `--headless`, NOWLINE_HEADLESS=1, auto in CI
|
|
15
|
+
// without a TTY, OR the plain default (no
|
|
16
|
+
// `useSystemFonts`): bundled DejaVu pair, no probe.
|
|
17
|
+
// 4. Platform probe — opt-in via `useSystemFonts`; first existing STATIC
|
|
18
|
+
// entry from `probe-list.ts` (variable fonts skipped).
|
|
19
|
+
// 5. Bundled fallback — DejaVuSans.ttf / DejaVuSansMono.ttf (after an
|
|
20
|
+
// opted-in probe found nothing usable).
|
|
21
|
+
//
|
|
22
|
+
// Variable-font guard: an explicitly requested font (flag/env/alias) that
|
|
23
|
+
// turns out to be a variable font is substituted with the bundled DejaVu and
|
|
24
|
+
// flagged (`*VariableFontSubstituted`), because raster export cannot render a
|
|
25
|
+
// VF and there is no runtime instancer. On the opt-in probe path, variable
|
|
26
|
+
// candidates are skipped in favor of the next static system font.
|
|
13
27
|
import { existsSync as defaultExistsSync, promises as fs } from 'node:fs';
|
|
14
28
|
import * as path from 'node:path';
|
|
15
|
-
import { loadBundledMono, loadBundledSans } from './bundled.js';
|
|
29
|
+
import { BUNDLED_MONO_FAMILY, BUNDLED_SANS_FAMILY, loadBundledMono, loadBundledSans, } from './bundled.js';
|
|
16
30
|
import { aliasCandidate, isAlias, probeListFor } from './probe-list.js';
|
|
17
31
|
import { isVariableFontBytes } from './sfns.js';
|
|
18
32
|
export async function resolveFonts(options = {}) {
|
|
@@ -22,11 +36,13 @@ export async function resolveFonts(options = {}) {
|
|
|
22
36
|
const readFileBytes = options.readFileBytes ?? defaultReadFileBytes;
|
|
23
37
|
const probe = probeListFor(platform, env);
|
|
24
38
|
const headlessRequested = isHeadlessRequested(options, env);
|
|
39
|
+
const useSystemFonts = options.useSystemFonts ?? false;
|
|
25
40
|
const sans = await resolveRole({
|
|
26
41
|
role: 'sans',
|
|
27
42
|
flag: options.fontSans,
|
|
28
43
|
envValue: env.NOWLINE_FONT_SANS,
|
|
29
44
|
headless: headlessRequested,
|
|
45
|
+
useSystemFonts,
|
|
30
46
|
probe,
|
|
31
47
|
fileExists,
|
|
32
48
|
readFileBytes,
|
|
@@ -36,15 +52,18 @@ export async function resolveFonts(options = {}) {
|
|
|
36
52
|
flag: options.fontMono,
|
|
37
53
|
envValue: env.NOWLINE_FONT_MONO,
|
|
38
54
|
headless: headlessRequested,
|
|
55
|
+
useSystemFonts,
|
|
39
56
|
probe,
|
|
40
57
|
fileExists,
|
|
41
58
|
readFileBytes,
|
|
42
59
|
});
|
|
43
60
|
return {
|
|
44
|
-
sans,
|
|
45
|
-
mono,
|
|
46
|
-
sansFellBackToBundled: sans.
|
|
47
|
-
monoFellBackToBundled: mono.
|
|
61
|
+
sans: sans.font,
|
|
62
|
+
mono: mono.font,
|
|
63
|
+
sansFellBackToBundled: sans.fellBack,
|
|
64
|
+
monoFellBackToBundled: mono.fellBack,
|
|
65
|
+
sansVariableFontSubstituted: sans.variableSubstituted,
|
|
66
|
+
monoVariableFontSubstituted: mono.variableSubstituted,
|
|
48
67
|
};
|
|
49
68
|
}
|
|
50
69
|
function isHeadlessRequested(options, env) {
|
|
@@ -61,30 +80,58 @@ function isHeadlessRequested(options, env) {
|
|
|
61
80
|
async function resolveRole(args) {
|
|
62
81
|
// Step 1 — flag (path or alias)
|
|
63
82
|
if (args.flag) {
|
|
64
|
-
return loadFlag(args.flag, args.role, 'flag', args);
|
|
83
|
+
return guardExplicit(await loadFlag(args.flag, args.role, 'flag', args), args);
|
|
65
84
|
}
|
|
66
85
|
// Step 2 — environment
|
|
67
86
|
if (args.envValue) {
|
|
68
|
-
return loadFlag(args.envValue, args.role, 'env', args);
|
|
87
|
+
return guardExplicit(await loadFlag(args.envValue, args.role, 'env', args), args);
|
|
69
88
|
}
|
|
70
|
-
// Step 3 — headless: skip probe, go to bundled
|
|
89
|
+
// Step 3 — explicit headless: skip probe, go to bundled.
|
|
71
90
|
if (args.headless) {
|
|
72
|
-
|
|
91
|
+
const font = await loadBundled(args.role, 'headless');
|
|
92
|
+
return { font, fellBack: false, variableSubstituted: false };
|
|
73
93
|
}
|
|
74
|
-
// Step
|
|
94
|
+
// Step 3 (default) — bundled-first: with no explicit system opt-in the
|
|
95
|
+
// default is the bundled DejaVu pair on every OS. This is the intended
|
|
96
|
+
// path (not a fallback), so it does not set `fellBack`.
|
|
97
|
+
if (!args.useSystemFonts) {
|
|
98
|
+
const font = await loadBundled(args.role, 'bundled');
|
|
99
|
+
return { font, fellBack: false, variableSubstituted: false };
|
|
100
|
+
}
|
|
101
|
+
// Step 4 — platform probe (opt-in). Skip variable candidates so we land on
|
|
102
|
+
// the next STATIC system font (resvg/raster cannot render a VF).
|
|
75
103
|
for (const candidate of args.probe[args.role]) {
|
|
76
|
-
if (args.fileExists(candidate.path))
|
|
77
|
-
|
|
78
|
-
|
|
104
|
+
if (!args.fileExists(candidate.path))
|
|
105
|
+
continue;
|
|
106
|
+
const bytes = await args.readFileBytes(candidate.path);
|
|
107
|
+
if (isVariableFontBytes(bytes))
|
|
108
|
+
continue;
|
|
109
|
+
return {
|
|
110
|
+
font: decorate(bytes, {
|
|
79
111
|
name: candidate.name,
|
|
80
112
|
source: 'probe',
|
|
81
113
|
path: candidate.path,
|
|
82
114
|
face: candidate.face,
|
|
83
|
-
})
|
|
84
|
-
|
|
115
|
+
}),
|
|
116
|
+
fellBack: false,
|
|
117
|
+
variableSubstituted: false,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
// Step 5 — bundled fallback after an opted-in probe found nothing usable.
|
|
121
|
+
const font = await loadBundled(args.role, 'bundled');
|
|
122
|
+
return { font, fellBack: true, variableSubstituted: false };
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Guard an explicitly resolved font (flag / env / alias). A variable font
|
|
126
|
+
* cannot be rasterized and we have no runtime instancer, so substitute the
|
|
127
|
+
* bundled DejaVu and flag it; the caller decides whether to warn or error.
|
|
128
|
+
*/
|
|
129
|
+
async function guardExplicit(font, args) {
|
|
130
|
+
if (font.isVariableFont) {
|
|
131
|
+
const bundled = await loadBundled(args.role, 'bundled');
|
|
132
|
+
return { font: bundled, fellBack: false, variableSubstituted: true };
|
|
85
133
|
}
|
|
86
|
-
|
|
87
|
-
return loadBundled(args.role, 'bundled');
|
|
134
|
+
return { font, fellBack: false, variableSubstituted: false };
|
|
88
135
|
}
|
|
89
136
|
async function loadFlag(raw, role, source, args) {
|
|
90
137
|
if (path.isAbsolute(raw) ||
|
|
@@ -128,7 +175,7 @@ async function loadFlag(raw, role, source, args) {
|
|
|
128
175
|
async function loadBundled(role, source) {
|
|
129
176
|
const bytes = role === 'sans' ? await loadBundledSans() : await loadBundledMono();
|
|
130
177
|
return decorate(bytes, {
|
|
131
|
-
name: role === 'sans' ?
|
|
178
|
+
name: role === 'sans' ? BUNDLED_SANS_FAMILY : BUNDLED_MONO_FAMILY,
|
|
132
179
|
source,
|
|
133
180
|
});
|
|
134
181
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/fonts/resolve.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/fonts/resolve.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,EAAE;AACF,0EAA0E;AAC1E,iBAAiB;AACjB,EAAE;AACF,8EAA8E;AAC9E,wEAAwE;AACxE,uEAAuE;AACvE,mDAAmD;AACnD,EAAE;AACF,yEAAyE;AACzE,kFAAkF;AAClF,gEAAgE;AAChE,yEAAyE;AACzE,mEAAmE;AACnE,6EAA6E;AAC7E,6EAA6E;AAC7E,gFAAgF;AAChF,wEAAwE;AACxE,iEAAiE;AACjE,EAAE;AACF,0EAA0E;AAC1E,6EAA6E;AAC7E,8EAA8E;AAC9E,2EAA2E;AAC3E,kEAAkE;AAElE,OAAO,EAAE,UAAU,IAAI,iBAAiB,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAGlC,OAAO,EACH,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,EACf,eAAe,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,OAAO,EAAsB,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC5F,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAoDhD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,UAA0B,EAAE;IAC3D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;IACtD,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,iBAAiB,CAAC;IAC3D,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,oBAAoB,CAAC;IACpE,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAE1C,MAAM,iBAAiB,GAAG,mBAAmB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC5D,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,KAAK,CAAC;IACvD,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC;QAC3B,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,OAAO,CAAC,QAAQ;QACtB,QAAQ,EAAE,GAAG,CAAC,iBAAiB;QAC/B,QAAQ,EAAE,iBAAiB;QAC3B,cAAc;QACd,KAAK;QACL,UAAU;QACV,aAAa;KAChB,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC;QAC3B,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,OAAO,CAAC,QAAQ;QACtB,QAAQ,EAAE,GAAG,CAAC,iBAAiB;QAC/B,QAAQ,EAAE,iBAAiB;QAC3B,cAAc;QACd,KAAK;QACL,UAAU;QACV,aAAa;KAChB,CAAC,CAAC;IACH,OAAO;QACH,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,qBAAqB,EAAE,IAAI,CAAC,QAAQ;QACpC,qBAAqB,EAAE,IAAI,CAAC,QAAQ;QACpC,2BAA2B,EAAE,IAAI,CAAC,mBAAmB;QACrD,2BAA2B,EAAE,IAAI,CAAC,mBAAmB;KACxD,CAAC;AACN,CAAC;AAED,SAAS,mBAAmB,CAAC,OAAuB,EAAE,GAAsB;IACxE,IAAI,OAAO,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAClC,IAAI,GAAG,CAAC,gBAAgB,KAAK,GAAG,IAAI,GAAG,CAAC,gBAAgB,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACjF,IAAI,OAAO,CAAC,mBAAmB;QAAE,OAAO,KAAK,CAAC;IAC9C,MAAM,IAAI,GAAG,GAAG,CAAC,EAAE,KAAK,MAAM,IAAI,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC;IACjD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzE,OAAO,IAAI,IAAI,CAAC,WAAW,CAAC;AAChC,CAAC;AAqBD,KAAK,UAAU,WAAW,CAAC,IAAc;IACrC,gCAAgC;IAChC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,OAAO,aAAa,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IACnF,CAAC;IACD,uBAAuB;IACvB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,OAAO,aAAa,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IACtF,CAAC;IACD,yDAAyD;IACzD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChB,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QACtD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,CAAC;IACjE,CAAC;IACD,uEAAuE;IACvE,uEAAuE;IACvE,wDAAwD;IACxD,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACrD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,CAAC;IACjE,CAAC;IACD,2EAA2E;IAC3E,iEAAiE;IACjE,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC;YAAE,SAAS;QAC/C,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACvD,IAAI,mBAAmB,CAAC,KAAK,CAAC;YAAE,SAAS;QACzC,OAAO;YACH,IAAI,EAAE,QAAQ,CAAC,KAAK,EAAE;gBAClB,IAAI,EAAE,SAAS,CAAC,IAAI;gBACpB,MAAM,EAAE,OAAO;gBACf,IAAI,EAAE,SAAS,CAAC,IAAI;gBACpB,IAAI,EAAE,SAAS,CAAC,IAAI;aACvB,CAAC;YACF,QAAQ,EAAE,KAAK;YACf,mBAAmB,EAAE,KAAK;SAC7B,CAAC;IACN,CAAC;IACD,0EAA0E;IAC1E,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACrD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,CAAC;AAChE,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,aAAa,CAAC,IAAkB,EAAE,IAAc;IAC3D,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACxD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,mBAAmB,EAAE,IAAI,EAAE,CAAC;IACzE,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,CAAC;AACjE,CAAC;AAED,KAAK,UAAU,QAAQ,CACnB,GAAW,EACX,IAAc,EACd,MAAsB,EACtB,IAAc;IAEd,IACI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QACpB,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;QACnB,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;QACtB,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC;QACpB,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC;QACpB,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,EACtB,CAAC;QACC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,gBAAgB,CAAC,6BAA6B,GAAG,iBAAiB,GAAG,GAAG,CAAC,CAAC;QACxF,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QAC5C,OAAO,QAAQ,CAAC,KAAK,EAAE;YACnB,IAAI,EAAE,iBAAiB,CAAC,GAAG,CAAC;YAC5B,MAAM;YACN,IAAI,EAAE,GAAG;SACZ,CAAC,CAAC;IACP,CAAC;IAED,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACf,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACxD,IAAI,CAAC,SAAS,EAAE,CAAC;YACb,MAAM,IAAI,gBAAgB,CAAC,UAAU,GAAG,YAAY,IAAI,2BAA2B,CAAC,CAAC;QACzF,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,mEAAmE;YACnE,kEAAkE;YAClE,oEAAoE;YACpE,MAAM,IAAI,gBAAgB,CACtB,UAAU,GAAG,aAAa,SAAS,CAAC,IAAI,sCAAsC,CACjF,CAAC;QACN,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACvD,OAAO,QAAQ,CAAC,KAAK,EAAE;YACnB,IAAI,EAAE,SAAS,CAAC,IAAI;YACpB,MAAM;YACN,IAAI,EAAE,SAAS,CAAC,IAAI;YACpB,IAAI,EAAE,SAAS,CAAC,IAAI;SACvB,CAAC,CAAC;IACP,CAAC;IAED,MAAM,IAAI,gBAAgB,CAAC,eAAe,GAAG,uCAAuC,CAAC,CAAC;AAC1F,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,IAAc,EAAE,MAAkB;IACzD,MAAM,KAAK,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,eAAe,EAAE,CAAC,CAAC,CAAC,MAAM,eAAe,EAAE,CAAC;IAClF,OAAO,QAAQ,CAAC,KAAK,EAAE;QACnB,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,mBAAmB;QACjE,MAAM;KACT,CAAC,CAAC;AACP,CAAC;AASD,SAAS,QAAQ,CAAC,KAAiB,EAAE,IAAkB;IACnD,OAAO;QACH,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK;QACL,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,cAAc,EAAE,mBAAmB,CAAC,KAAK,CAAC;KAC7C,CAAC;AACN,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAe;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3D,OAAO,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AAC/D,CAAC;AAED,KAAK,UAAU,oBAAoB,CAAC,CAAS;IACzC,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACjC,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACvC,YAAY,OAAe;QACvB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACnC,CAAC;CACJ"}
|
package/dist/fonts/sfns.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sfns.d.ts","sourceRoot":"","sources":["../../src/fonts/sfns.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sfns.d.ts","sourceRoot":"","sources":["../../src/fonts/sfns.ts"],"names":[],"mappings":"AAaA;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAkB9D"}
|
package/dist/fonts/sfns.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Variable-font detection.
|
|
2
2
|
//
|
|
3
|
-
// Spec: specs/handoffs/m2c.md § 10 "
|
|
3
|
+
// Spec: specs/handoffs/m2c.md § 10 "Font strategy — bundled first, system
|
|
4
|
+
// fonts opt-in".
|
|
4
5
|
//
|
|
5
|
-
// Detection only —
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// Detection only — the resolver uses this to guard explicit VF requests
|
|
7
|
+
// (substituting the bundled DejaVu and setting `variableSubstituted`) and to
|
|
8
|
+
// skip VF candidates on the opt-in system-font probe. There is no runtime
|
|
9
|
+
// instancer; any VF that reaches export would rasterize as blank in
|
|
10
|
+
// `@resvg/resvg-wasm`.
|
|
9
11
|
const FVAR_TAG = 0x66766172; // 'fvar'
|
|
10
12
|
/**
|
|
11
13
|
* Returns true when the given TTF/OTF byte buffer carries an `fvar` table —
|
package/dist/fonts/sfns.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sfns.js","sourceRoot":"","sources":["../../src/fonts/sfns.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"sfns.js","sourceRoot":"","sources":["../../src/fonts/sfns.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,EAAE;AACF,0EAA0E;AAC1E,iBAAiB;AACjB,EAAE;AACF,wEAAwE;AACxE,6EAA6E;AAC7E,0EAA0E;AAC1E,oEAAoE;AACpE,uBAAuB;AAEvB,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,SAAS;AAEtC;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAiB;IACjD,IAAI,KAAK,CAAC,UAAU,GAAG,EAAE;QAAE,OAAO,KAAK,CAAC;IACxC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC5E,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACtC,uEAAuE;IACvE,MAAM,MAAM,GAAG,WAAW,KAAK,UAAU,IAAI,WAAW,KAAK,UAAU,CAAC;IACxE,MAAM,YAAY,GAAG,WAAW,KAAK,UAAU,CAAC,CAAC,8BAA8B;IAC/E,IAAI,CAAC,MAAM,IAAI,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAC3C,IAAI,YAAY;QAAE,OAAO,KAAK,CAAC,CAAC,gDAAgD;IAEhF,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;QACjC,MAAM,YAAY,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC;QACjC,IAAI,YAAY,GAAG,EAAE,GAAG,KAAK,CAAC,UAAU;YAAE,OAAO,KAAK,CAAC;QACvD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACzC,IAAI,GAAG,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;IACtC,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { displayLabel, getProp, getProps, hasProp, type PropertyHost, roadmapTitle, } from './ast-helpers.js';
|
|
2
|
-
export {
|
|
2
|
+
export { _clearBrowserFontsCache, loadBundledFontsForBrowser } from './fonts/browser.js';
|
|
3
|
+
export { BUNDLED_MONO_FAMILY, BUNDLED_MONO_PATH, BUNDLED_SANS_FAMILY, BUNDLED_SANS_PATH, clearBundledCache, loadBundledMono, loadBundledSans, } from './fonts/bundled.js';
|
|
3
4
|
export { ALIASES, aliasCandidate, type FontCandidate, isAlias, type PlatformProbe, probeListFor, } from './fonts/probe-list.js';
|
|
4
5
|
export { FontResolveError, type ResolveOptions, type ResolveResult, resolveFonts, } from './fonts/resolve.js';
|
|
5
6
|
export { isVariableFontBytes } from './fonts/sfns.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,YAAY,EACZ,OAAO,EACP,QAAQ,EACR,OAAO,EACP,KAAK,YAAY,EACjB,YAAY,GACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACH,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,eAAe,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACH,OAAO,EACP,cAAc,EACd,KAAK,aAAa,EAClB,OAAO,EACP,KAAK,aAAa,EAClB,YAAY,GACf,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACH,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,YAAY,GACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EACH,KAAK,YAAY,EACjB,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,KAAK,YAAY,EACjB,WAAW,EACX,cAAc,GACjB,MAAM,eAAe,CAAC;AACvB,YAAY,EACR,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,SAAS,EACT,aAAa,EACb,cAAc,EACd,WAAW,EACX,aAAa,EACb,YAAY,EACZ,gBAAgB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACH,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,cAAc,GACjB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,YAAY,EACZ,OAAO,EACP,QAAQ,EACR,OAAO,EACP,KAAK,YAAY,EACjB,YAAY,GACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AACzF,OAAO,EACH,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,eAAe,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACH,OAAO,EACP,cAAc,EACd,KAAK,aAAa,EAClB,OAAO,EACP,KAAK,aAAa,EAClB,YAAY,GACf,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACH,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,YAAY,GACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EACH,KAAK,YAAY,EACjB,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,KAAK,YAAY,EACjB,WAAW,EACX,cAAc,GACjB,MAAM,eAAe,CAAC;AACvB,YAAY,EACR,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,SAAS,EACT,aAAa,EACb,cAAc,EACd,WAAW,EACX,aAAa,EACb,YAAY,EACZ,gBAAgB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACH,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,cAAc,GACjB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { displayLabel, getProp, getProps, hasProp, roadmapTitle, } from './ast-helpers.js';
|
|
2
|
-
export {
|
|
2
|
+
export { _clearBrowserFontsCache, loadBundledFontsForBrowser } from './fonts/browser.js';
|
|
3
|
+
export { BUNDLED_MONO_FAMILY, BUNDLED_MONO_PATH, BUNDLED_SANS_FAMILY, BUNDLED_SANS_PATH, clearBundledCache, loadBundledMono, loadBundledSans, } from './fonts/bundled.js';
|
|
3
4
|
export { ALIASES, aliasCandidate, isAlias, probeListFor, } from './fonts/probe-list.js';
|
|
4
5
|
export { FontResolveError, resolveFonts, } from './fonts/resolve.js';
|
|
5
6
|
export { isVariableFontBytes } from './fonts/sfns.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,YAAY,EACZ,OAAO,EACP,QAAQ,EACR,OAAO,EAEP,YAAY,GACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACH,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,eAAe,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACH,OAAO,EACP,cAAc,EAEd,OAAO,EAEP,YAAY,GACf,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACH,gBAAgB,EAGhB,YAAY,GACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAEH,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,WAAW,EAEX,WAAW,EACX,cAAc,GACjB,MAAM,eAAe,CAAC;AAavB,OAAO,EACH,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,cAAc,GACjB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,YAAY,EACZ,OAAO,EACP,QAAQ,EACR,OAAO,EAEP,YAAY,GACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AACzF,OAAO,EACH,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,eAAe,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACH,OAAO,EACP,cAAc,EAEd,OAAO,EAEP,YAAY,GACf,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACH,gBAAgB,EAGhB,YAAY,GACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAEH,UAAU,EACV,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,WAAW,EAEX,WAAW,EACX,cAAc,GACjB,MAAM,eAAe,CAAC;AAavB,OAAO,EACH,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,cAAc,GACjB,MAAM,YAAY,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nowline/export-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Nowline export-core — shared types, font resolver, unit converter, PDF page-size types",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"engines": {
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"assets/"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@nowline/core": "0.
|
|
38
|
-
"@nowline/layout": "0.
|
|
37
|
+
"@nowline/core": "0.6.0",
|
|
38
|
+
"@nowline/layout": "0.6.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^25.9.1",
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Browser-compatible bundled font loader.
|
|
2
|
+
//
|
|
3
|
+
// Same bundled DejaVu bytes as loadBundledSans/loadBundledMono (Node), but
|
|
4
|
+
// decoded with `atob()` instead of Node's `Buffer.from()` so the module
|
|
5
|
+
// bundles cleanly for the browser (the Free/Pro web apps, Playwright legs).
|
|
6
|
+
//
|
|
7
|
+
// Usage: `loadBundledFontsForBrowser()` returns a cached `ResolvedFontPair`
|
|
8
|
+
// synchronously after the first call. Call it once at module init or lazy-once
|
|
9
|
+
// inside your `loadWasm` / font-loading step.
|
|
10
|
+
//
|
|
11
|
+
// Node callers: use `loadBundledSans()` / `loadBundledMono()` from bundled.ts.
|
|
12
|
+
|
|
13
|
+
import { MONO_BASE64, SANS_BASE64 } from '../generated/bundled-fonts.js';
|
|
14
|
+
import type { ResolvedFontPair } from '../types.js';
|
|
15
|
+
|
|
16
|
+
function b64ToBytes(b64: string): Uint8Array {
|
|
17
|
+
const binary = atob(b64);
|
|
18
|
+
const out = new Uint8Array(binary.length);
|
|
19
|
+
for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let cachedFonts: ResolvedFontPair | undefined;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Return the canonical bundled DejaVu font pair decoded with browser-compatible
|
|
27
|
+
* APIs (`atob`). Result is cached after the first call so the ~1 MB decode only
|
|
28
|
+
* runs once per page. Safe to call before wasm initialization.
|
|
29
|
+
*/
|
|
30
|
+
export function loadBundledFontsForBrowser(): ResolvedFontPair {
|
|
31
|
+
if (!cachedFonts) {
|
|
32
|
+
cachedFonts = {
|
|
33
|
+
sans: { name: 'DejaVu Sans', bytes: b64ToBytes(SANS_BASE64), source: 'bundled' },
|
|
34
|
+
mono: {
|
|
35
|
+
name: 'DejaVu Sans Mono',
|
|
36
|
+
bytes: b64ToBytes(MONO_BASE64),
|
|
37
|
+
source: 'bundled',
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return cachedFonts;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Test seam: drop the cached pair so isolated tests start fresh. */
|
|
45
|
+
export function _clearBrowserFontsCache(): void {
|
|
46
|
+
cachedFonts = undefined;
|
|
47
|
+
}
|
package/src/fonts/bundled.ts
CHANGED
|
@@ -19,11 +19,26 @@ import { fileURLToPath } from 'node:url';
|
|
|
19
19
|
|
|
20
20
|
import { MONO_BASE64, SANS_BASE64 } from '../generated/bundled-fonts.js';
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
// Resolve the on-disk assets/fonts directory from this module's location.
|
|
23
|
+
// `import.meta.url` is rewritten to `undefined` when this module is bundled
|
|
24
|
+
// into a CommonJS context — esbuild collapses `import.meta` to `{}` for the VS
|
|
25
|
+
// Code extension's `dist/extension.cjs`. The two exported paths below are
|
|
26
|
+
// informational only (the runtime decodes the embedded base64 above, never
|
|
27
|
+
// reads these files), so degrade to a bare relative directory instead of
|
|
28
|
+
// throwing `ERR_INVALID_ARG_TYPE` at module load — which would crash the
|
|
29
|
+
// extension before it can activate. See packages/vscode-extension.
|
|
30
|
+
function bundledFontsDir(): string {
|
|
31
|
+
try {
|
|
32
|
+
// dist/fonts/bundled.js → ../../assets/fonts/
|
|
33
|
+
// src/fonts/bundled.ts → ../../assets/fonts/
|
|
34
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
35
|
+
return path.resolve(here, '..', '..', 'assets', 'fonts');
|
|
36
|
+
} catch {
|
|
37
|
+
return path.join('assets', 'fonts');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
23
40
|
|
|
24
|
-
|
|
25
|
-
// src/fonts/bundled.ts → ../../assets/fonts/
|
|
26
|
-
const ASSETS_DIR = path.resolve(HERE, '..', '..', 'assets', 'fonts');
|
|
41
|
+
const ASSETS_DIR = bundledFontsDir();
|
|
27
42
|
|
|
28
43
|
// Informational: where the source-of-truth TTFs live on disk for users
|
|
29
44
|
// running under Node. Not the runtime load source — `loadBundledSans` and
|
|
@@ -32,6 +47,15 @@ const ASSETS_DIR = path.resolve(HERE, '..', '..', 'assets', 'fonts');
|
|
|
32
47
|
export const BUNDLED_SANS_PATH = path.join(ASSETS_DIR, 'DejaVuSans.ttf');
|
|
33
48
|
export const BUNDLED_MONO_PATH = path.join(ASSETS_DIR, 'DejaVuSansMono.ttf');
|
|
34
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Family names stamped on the bundled DejaVu faces. The resolver's
|
|
52
|
+
* `ResolvedFont.name`, the renderer's pinned `font-family`, the resvg family
|
|
53
|
+
* hints, and the live-preview `@font-face` must all use these exact strings
|
|
54
|
+
* so preview and raster export name the same face (the WYSIWYG contract).
|
|
55
|
+
*/
|
|
56
|
+
export const BUNDLED_SANS_FAMILY = 'DejaVu Sans';
|
|
57
|
+
export const BUNDLED_MONO_FAMILY = 'DejaVu Sans Mono';
|
|
58
|
+
|
|
35
59
|
let cachedSans: Uint8Array | undefined;
|
|
36
60
|
let cachedMono: Uint8Array | undefined;
|
|
37
61
|
|
package/src/fonts/index.ts
CHANGED
package/src/fonts/resolve.ts
CHANGED
|
@@ -1,21 +1,40 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Bundled-first font resolver shared by PDF and PNG.
|
|
2
2
|
//
|
|
3
|
-
// Spec: specs/handoffs/m2c.md § 10 "Font strategy —
|
|
4
|
-
//
|
|
3
|
+
// Spec: specs/handoffs/m2c.md § 10 "Font strategy — bundled first, system
|
|
4
|
+
// fonts opt-in".
|
|
5
|
+
//
|
|
6
|
+
// The resolver defaults to the bundled static DejaVu pair on every OS so that
|
|
7
|
+
// preview and raster export look identical everywhere and we never hand
|
|
8
|
+
// `@resvg/resvg-wasm` a variable font (which it cannot rasterize — see
|
|
9
|
+
// `sfns.ts`). System fonts are an explicit opt-in.
|
|
5
10
|
//
|
|
6
11
|
// Resolution order (run independently for sans / mono — first hit wins):
|
|
7
12
|
// 1. Explicit flag — `--font-sans <path|alias>` / `--font-mono <path|alias>`
|
|
8
13
|
// 2. Environment — NOWLINE_FONT_SANS / NOWLINE_FONT_MONO
|
|
9
|
-
// 3. Headless
|
|
10
|
-
// without a TTY
|
|
11
|
-
//
|
|
12
|
-
//
|
|
14
|
+
// 3. Headless / default — `--headless`, NOWLINE_HEADLESS=1, auto in CI
|
|
15
|
+
// without a TTY, OR the plain default (no
|
|
16
|
+
// `useSystemFonts`): bundled DejaVu pair, no probe.
|
|
17
|
+
// 4. Platform probe — opt-in via `useSystemFonts`; first existing STATIC
|
|
18
|
+
// entry from `probe-list.ts` (variable fonts skipped).
|
|
19
|
+
// 5. Bundled fallback — DejaVuSans.ttf / DejaVuSansMono.ttf (after an
|
|
20
|
+
// opted-in probe found nothing usable).
|
|
21
|
+
//
|
|
22
|
+
// Variable-font guard: an explicitly requested font (flag/env/alias) that
|
|
23
|
+
// turns out to be a variable font is substituted with the bundled DejaVu and
|
|
24
|
+
// flagged (`*VariableFontSubstituted`), because raster export cannot render a
|
|
25
|
+
// VF and there is no runtime instancer. On the opt-in probe path, variable
|
|
26
|
+
// candidates are skipped in favor of the next static system font.
|
|
13
27
|
|
|
14
28
|
import { existsSync as defaultExistsSync, promises as fs } from 'node:fs';
|
|
15
29
|
import * as path from 'node:path';
|
|
16
30
|
|
|
17
31
|
import type { FontRole, FontSource, ResolvedFont } from '../types.js';
|
|
18
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
BUNDLED_MONO_FAMILY,
|
|
34
|
+
BUNDLED_SANS_FAMILY,
|
|
35
|
+
loadBundledMono,
|
|
36
|
+
loadBundledSans,
|
|
37
|
+
} from './bundled.js';
|
|
19
38
|
import { aliasCandidate, isAlias, type PlatformProbe, probeListFor } from './probe-list.js';
|
|
20
39
|
import { isVariableFontBytes } from './sfns.js';
|
|
21
40
|
|
|
@@ -25,9 +44,18 @@ export interface ResolveOptions {
|
|
|
25
44
|
/** Path or alias for the mono role. */
|
|
26
45
|
fontMono?: string;
|
|
27
46
|
/**
|
|
28
|
-
*
|
|
47
|
+
* Opt in to the platform font probe (step 4). Off by default: the
|
|
48
|
+
* resolver is bundled-first, so the default (no flag/env) is the bundled
|
|
49
|
+
* DejaVu pair on every OS. With this set the probe runs and the first
|
|
50
|
+
* STATIC system font wins (variable fonts are skipped), bundled if none.
|
|
51
|
+
*/
|
|
52
|
+
useSystemFonts?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Skip the probe; go straight to the bundled DejaVu pair. Implied by
|
|
29
55
|
* `NOWLINE_HEADLESS=1` and by `CI=true` without a TTY (unless
|
|
30
|
-
* `disableAutoHeadless` is set).
|
|
56
|
+
* `disableAutoHeadless` is set). Largely redundant now that bundled is the
|
|
57
|
+
* default, but retained so an explicit `--headless` still forces bundled
|
|
58
|
+
* even alongside `useSystemFonts`.
|
|
31
59
|
*/
|
|
32
60
|
headless?: boolean;
|
|
33
61
|
/** Disable the CI-no-TTY auto-headless heuristic. Defaults to false. */
|
|
@@ -44,11 +72,20 @@ export interface ResolveResult {
|
|
|
44
72
|
sans: ResolvedFont;
|
|
45
73
|
mono: ResolvedFont;
|
|
46
74
|
/**
|
|
47
|
-
* True when
|
|
48
|
-
*
|
|
75
|
+
* True when a role landed on bundled DejaVu *after an opted-in probe found
|
|
76
|
+
* nothing usable* (`useSystemFonts` set, no static system font present).
|
|
77
|
+
* The bundled-first default is the intended path, not a fallback, so it
|
|
78
|
+
* does NOT set this. Callers (CLI) emit a `--strict` warning on this.
|
|
49
79
|
*/
|
|
50
80
|
sansFellBackToBundled: boolean;
|
|
51
81
|
monoFellBackToBundled: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* True when an explicitly requested font (flag / env / alias) was a
|
|
84
|
+
* variable font and was replaced by the bundled DejaVu (raster cannot
|
|
85
|
+
* render a VF). Callers warn; the CLI errors under `--strict`.
|
|
86
|
+
*/
|
|
87
|
+
sansVariableFontSubstituted: boolean;
|
|
88
|
+
monoVariableFontSubstituted: boolean;
|
|
52
89
|
}
|
|
53
90
|
|
|
54
91
|
export async function resolveFonts(options: ResolveOptions = {}): Promise<ResolveResult> {
|
|
@@ -59,11 +96,13 @@ export async function resolveFonts(options: ResolveOptions = {}): Promise<Resolv
|
|
|
59
96
|
const probe = probeListFor(platform, env);
|
|
60
97
|
|
|
61
98
|
const headlessRequested = isHeadlessRequested(options, env);
|
|
99
|
+
const useSystemFonts = options.useSystemFonts ?? false;
|
|
62
100
|
const sans = await resolveRole({
|
|
63
101
|
role: 'sans',
|
|
64
102
|
flag: options.fontSans,
|
|
65
103
|
envValue: env.NOWLINE_FONT_SANS,
|
|
66
104
|
headless: headlessRequested,
|
|
105
|
+
useSystemFonts,
|
|
67
106
|
probe,
|
|
68
107
|
fileExists,
|
|
69
108
|
readFileBytes,
|
|
@@ -73,15 +112,18 @@ export async function resolveFonts(options: ResolveOptions = {}): Promise<Resolv
|
|
|
73
112
|
flag: options.fontMono,
|
|
74
113
|
envValue: env.NOWLINE_FONT_MONO,
|
|
75
114
|
headless: headlessRequested,
|
|
115
|
+
useSystemFonts,
|
|
76
116
|
probe,
|
|
77
117
|
fileExists,
|
|
78
118
|
readFileBytes,
|
|
79
119
|
});
|
|
80
120
|
return {
|
|
81
|
-
sans,
|
|
82
|
-
mono,
|
|
83
|
-
sansFellBackToBundled: sans.
|
|
84
|
-
monoFellBackToBundled: mono.
|
|
121
|
+
sans: sans.font,
|
|
122
|
+
mono: mono.font,
|
|
123
|
+
sansFellBackToBundled: sans.fellBack,
|
|
124
|
+
monoFellBackToBundled: mono.fellBack,
|
|
125
|
+
sansVariableFontSubstituted: sans.variableSubstituted,
|
|
126
|
+
monoVariableFontSubstituted: mono.variableSubstituted,
|
|
85
127
|
};
|
|
86
128
|
}
|
|
87
129
|
|
|
@@ -99,38 +141,74 @@ interface RoleArgs {
|
|
|
99
141
|
flag?: string;
|
|
100
142
|
envValue?: string;
|
|
101
143
|
headless: boolean;
|
|
144
|
+
useSystemFonts: boolean;
|
|
102
145
|
probe: PlatformProbe;
|
|
103
146
|
fileExists: (p: string) => boolean;
|
|
104
147
|
readFileBytes: (p: string) => Promise<Uint8Array>;
|
|
105
148
|
}
|
|
106
149
|
|
|
107
|
-
|
|
150
|
+
interface RoleResolution {
|
|
151
|
+
font: ResolvedFont;
|
|
152
|
+
/** Bundled because an opted-in probe found no usable system font. */
|
|
153
|
+
fellBack: boolean;
|
|
154
|
+
/** An explicit VF request was replaced by the bundled DejaVu. */
|
|
155
|
+
variableSubstituted: boolean;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function resolveRole(args: RoleArgs): Promise<RoleResolution> {
|
|
108
159
|
// Step 1 — flag (path or alias)
|
|
109
160
|
if (args.flag) {
|
|
110
|
-
return loadFlag(args.flag, args.role, 'flag', args);
|
|
161
|
+
return guardExplicit(await loadFlag(args.flag, args.role, 'flag', args), args);
|
|
111
162
|
}
|
|
112
163
|
// Step 2 — environment
|
|
113
164
|
if (args.envValue) {
|
|
114
|
-
return loadFlag(args.envValue, args.role, 'env', args);
|
|
165
|
+
return guardExplicit(await loadFlag(args.envValue, args.role, 'env', args), args);
|
|
115
166
|
}
|
|
116
|
-
// Step 3 — headless: skip probe, go to bundled
|
|
167
|
+
// Step 3 — explicit headless: skip probe, go to bundled.
|
|
117
168
|
if (args.headless) {
|
|
118
|
-
|
|
169
|
+
const font = await loadBundled(args.role, 'headless');
|
|
170
|
+
return { font, fellBack: false, variableSubstituted: false };
|
|
171
|
+
}
|
|
172
|
+
// Step 3 (default) — bundled-first: with no explicit system opt-in the
|
|
173
|
+
// default is the bundled DejaVu pair on every OS. This is the intended
|
|
174
|
+
// path (not a fallback), so it does not set `fellBack`.
|
|
175
|
+
if (!args.useSystemFonts) {
|
|
176
|
+
const font = await loadBundled(args.role, 'bundled');
|
|
177
|
+
return { font, fellBack: false, variableSubstituted: false };
|
|
119
178
|
}
|
|
120
|
-
// Step 4 — platform probe
|
|
179
|
+
// Step 4 — platform probe (opt-in). Skip variable candidates so we land on
|
|
180
|
+
// the next STATIC system font (resvg/raster cannot render a VF).
|
|
121
181
|
for (const candidate of args.probe[args.role]) {
|
|
122
|
-
if (args.fileExists(candidate.path))
|
|
123
|
-
|
|
124
|
-
|
|
182
|
+
if (!args.fileExists(candidate.path)) continue;
|
|
183
|
+
const bytes = await args.readFileBytes(candidate.path);
|
|
184
|
+
if (isVariableFontBytes(bytes)) continue;
|
|
185
|
+
return {
|
|
186
|
+
font: decorate(bytes, {
|
|
125
187
|
name: candidate.name,
|
|
126
188
|
source: 'probe',
|
|
127
189
|
path: candidate.path,
|
|
128
190
|
face: candidate.face,
|
|
129
|
-
})
|
|
130
|
-
|
|
191
|
+
}),
|
|
192
|
+
fellBack: false,
|
|
193
|
+
variableSubstituted: false,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
// Step 5 — bundled fallback after an opted-in probe found nothing usable.
|
|
197
|
+
const font = await loadBundled(args.role, 'bundled');
|
|
198
|
+
return { font, fellBack: true, variableSubstituted: false };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Guard an explicitly resolved font (flag / env / alias). A variable font
|
|
203
|
+
* cannot be rasterized and we have no runtime instancer, so substitute the
|
|
204
|
+
* bundled DejaVu and flag it; the caller decides whether to warn or error.
|
|
205
|
+
*/
|
|
206
|
+
async function guardExplicit(font: ResolvedFont, args: RoleArgs): Promise<RoleResolution> {
|
|
207
|
+
if (font.isVariableFont) {
|
|
208
|
+
const bundled = await loadBundled(args.role, 'bundled');
|
|
209
|
+
return { font: bundled, fellBack: false, variableSubstituted: true };
|
|
131
210
|
}
|
|
132
|
-
|
|
133
|
-
return loadBundled(args.role, 'bundled');
|
|
211
|
+
return { font, fellBack: false, variableSubstituted: false };
|
|
134
212
|
}
|
|
135
213
|
|
|
136
214
|
async function loadFlag(
|
|
@@ -187,7 +265,7 @@ async function loadFlag(
|
|
|
187
265
|
async function loadBundled(role: FontRole, source: FontSource): Promise<ResolvedFont> {
|
|
188
266
|
const bytes = role === 'sans' ? await loadBundledSans() : await loadBundledMono();
|
|
189
267
|
return decorate(bytes, {
|
|
190
|
-
name: role === 'sans' ?
|
|
268
|
+
name: role === 'sans' ? BUNDLED_SANS_FAMILY : BUNDLED_MONO_FAMILY,
|
|
191
269
|
source,
|
|
192
270
|
});
|
|
193
271
|
}
|
package/src/fonts/sfns.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Variable-font detection.
|
|
2
2
|
//
|
|
3
|
-
// Spec: specs/handoffs/m2c.md § 10 "
|
|
3
|
+
// Spec: specs/handoffs/m2c.md § 10 "Font strategy — bundled first, system
|
|
4
|
+
// fonts opt-in".
|
|
4
5
|
//
|
|
5
|
-
// Detection only —
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// Detection only — the resolver uses this to guard explicit VF requests
|
|
7
|
+
// (substituting the bundled DejaVu and setting `variableSubstituted`) and to
|
|
8
|
+
// skip VF candidates on the opt-in system-font probe. There is no runtime
|
|
9
|
+
// instancer; any VF that reaches export would rasterize as blank in
|
|
10
|
+
// `@resvg/resvg-wasm`.
|
|
9
11
|
|
|
10
12
|
const FVAR_TAG = 0x66766172; // 'fvar'
|
|
11
13
|
|
package/src/index.ts
CHANGED
|
@@ -6,8 +6,11 @@ export {
|
|
|
6
6
|
type PropertyHost,
|
|
7
7
|
roadmapTitle,
|
|
8
8
|
} from './ast-helpers.js';
|
|
9
|
+
export { _clearBrowserFontsCache, loadBundledFontsForBrowser } from './fonts/browser.js';
|
|
9
10
|
export {
|
|
11
|
+
BUNDLED_MONO_FAMILY,
|
|
10
12
|
BUNDLED_MONO_PATH,
|
|
13
|
+
BUNDLED_SANS_FAMILY,
|
|
11
14
|
BUNDLED_SANS_PATH,
|
|
12
15
|
clearBundledCache,
|
|
13
16
|
loadBundledMono,
|