@nowline/export-core 0.2.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.
Files changed (60) hide show
  1. package/README.md +131 -0
  2. package/assets/fonts/DejaVuSans.ttf +0 -0
  3. package/assets/fonts/DejaVuSansMono.ttf +0 -0
  4. package/assets/fonts/LICENSE-DejaVu.txt +187 -0
  5. package/dist/ast-helpers.d.ts +16 -0
  6. package/dist/ast-helpers.d.ts.map +1 -0
  7. package/dist/ast-helpers.js +44 -0
  8. package/dist/ast-helpers.js.map +1 -0
  9. package/dist/fonts/bundled.d.ts +7 -0
  10. package/dist/fonts/bundled.d.ts.map +1 -0
  11. package/dist/fonts/bundled.js +52 -0
  12. package/dist/fonts/bundled.js.map +1 -0
  13. package/dist/fonts/index.d.ts +6 -0
  14. package/dist/fonts/index.d.ts.map +1 -0
  15. package/dist/fonts/index.js +5 -0
  16. package/dist/fonts/index.js.map +1 -0
  17. package/dist/fonts/probe-list.d.ts +29 -0
  18. package/dist/fonts/probe-list.d.ts.map +1 -0
  19. package/dist/fonts/probe-list.js +119 -0
  20. package/dist/fonts/probe-list.js.map +1 -0
  21. package/dist/fonts/resolve.d.ts +35 -0
  22. package/dist/fonts/resolve.d.ts.map +1 -0
  23. package/dist/fonts/resolve.js +159 -0
  24. package/dist/fonts/resolve.js.map +1 -0
  25. package/dist/fonts/sfns.d.ts +6 -0
  26. package/dist/fonts/sfns.d.ts.map +1 -0
  27. package/dist/fonts/sfns.js +37 -0
  28. package/dist/fonts/sfns.js.map +1 -0
  29. package/dist/generated/bundled-fonts.d.ts +3 -0
  30. package/dist/generated/bundled-fonts.d.ts.map +1 -0
  31. package/dist/generated/bundled-fonts.js +9 -0
  32. package/dist/generated/bundled-fonts.js.map +1 -0
  33. package/dist/index.d.ts +9 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +8 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/pdf-page.d.ts +66 -0
  38. package/dist/pdf-page.d.ts.map +1 -0
  39. package/dist/pdf-page.js +170 -0
  40. package/dist/pdf-page.js.map +1 -0
  41. package/dist/types.d.ts +61 -0
  42. package/dist/types.d.ts.map +1 -0
  43. package/dist/types.js +6 -0
  44. package/dist/types.js.map +1 -0
  45. package/dist/units.d.ts +16 -0
  46. package/dist/units.d.ts.map +1 -0
  47. package/dist/units.js +56 -0
  48. package/dist/units.js.map +1 -0
  49. package/package.json +50 -0
  50. package/src/ast-helpers.ts +47 -0
  51. package/src/fonts/bundled.ts +59 -0
  52. package/src/fonts/index.ts +18 -0
  53. package/src/fonts/probe-list.ts +143 -0
  54. package/src/fonts/resolve.ts +228 -0
  55. package/src/fonts/sfns.ts +34 -0
  56. package/src/generated/bundled-fonts.ts +10 -0
  57. package/src/index.ts +62 -0
  58. package/src/pdf-page.ts +224 -0
  59. package/src/types.ts +88 -0
  60. package/src/units.ts +60 -0
@@ -0,0 +1,35 @@
1
+ import type { ResolvedFont } from '../types.js';
2
+ export interface ResolveOptions {
3
+ /** Path or alias for the sans role. Wins over env / probe / bundled. */
4
+ fontSans?: string;
5
+ /** Path or alias for the mono role. */
6
+ fontMono?: string;
7
+ /**
8
+ * Skip steps 4–5; go straight to step 5 (bundled DejaVu pair). Implied by
9
+ * `NOWLINE_HEADLESS=1` and by `CI=true` without a TTY (unless
10
+ * `disableAutoHeadless` is set).
11
+ */
12
+ headless?: boolean;
13
+ /** Disable the CI-no-TTY auto-headless heuristic. Defaults to false. */
14
+ disableAutoHeadless?: boolean;
15
+ platform?: NodeJS.Platform;
16
+ fileExists?: (p: string) => boolean;
17
+ readFileBytes?: (p: string) => Promise<Uint8Array>;
18
+ env?: NodeJS.ProcessEnv;
19
+ isStdoutTty?: boolean;
20
+ }
21
+ export interface ResolveResult {
22
+ sans: ResolvedFont;
23
+ mono: ResolvedFont;
24
+ /**
25
+ * True when EITHER role landed at step 5 without explicit `--headless`.
26
+ * Callers (CLI) emit a `--strict` warning on this.
27
+ */
28
+ sansFellBackToBundled: boolean;
29
+ monoFellBackToBundled: boolean;
30
+ }
31
+ export declare function resolveFonts(options?: ResolveOptions): Promise<ResolveResult>;
32
+ export declare class FontResolveError extends Error {
33
+ constructor(message: string);
34
+ }
35
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/fonts/resolve.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAwB,YAAY,EAAE,MAAM,aAAa,CAAC;AAKtE,MAAM,WAAW,cAAc;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;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;;;OAGG;IACH,qBAAqB,EAAE,OAAO,CAAC;IAC/B,qBAAqB,EAAE,OAAO,CAAC;CAClC;AAED,wBAAsB,YAAY,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CAgCvF;AAyID,qBAAa,gBAAiB,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI9B"}
@@ -0,0 +1,159 @@
1
+ // 5-step font resolver shared by PDF and PNG.
2
+ //
3
+ // Spec: specs/handoffs/m2c.md § 10 "Font strategy — system first, one
4
+ // bundled headless fallback".
5
+ //
6
+ // Resolution order (run independently for sans / mono — first hit wins):
7
+ // 1. Explicit flag — `--font-sans <path|alias>` / `--font-mono <path|alias>`
8
+ // 2. Environment — NOWLINE_FONT_SANS / NOWLINE_FONT_MONO
9
+ // 3. Headless override — `--headless`, NOWLINE_HEADLESS=1, or auto in CI
10
+ // without a TTY
11
+ // 4. Platform probe — first existing entry from `probe-list.ts`
12
+ // 5. Bundled fallback — DejaVuSans.ttf / DejaVuSansMono.ttf
13
+ import { existsSync as defaultExistsSync, promises as fs } from 'node:fs';
14
+ import * as path from 'node:path';
15
+ import { loadBundledMono, loadBundledSans } from './bundled.js';
16
+ import { aliasCandidate, isAlias, probeListFor } from './probe-list.js';
17
+ import { isVariableFontBytes } from './sfns.js';
18
+ export async function resolveFonts(options = {}) {
19
+ const env = options.env ?? process.env;
20
+ const platform = options.platform ?? process.platform;
21
+ const fileExists = options.fileExists ?? defaultExistsSync;
22
+ const readFileBytes = options.readFileBytes ?? defaultReadFileBytes;
23
+ const probe = probeListFor(platform, env);
24
+ const headlessRequested = isHeadlessRequested(options, env);
25
+ const sans = await resolveRole({
26
+ role: 'sans',
27
+ flag: options.fontSans,
28
+ envValue: env.NOWLINE_FONT_SANS,
29
+ headless: headlessRequested,
30
+ probe,
31
+ fileExists,
32
+ readFileBytes,
33
+ });
34
+ const mono = await resolveRole({
35
+ role: 'mono',
36
+ flag: options.fontMono,
37
+ envValue: env.NOWLINE_FONT_MONO,
38
+ headless: headlessRequested,
39
+ probe,
40
+ fileExists,
41
+ readFileBytes,
42
+ });
43
+ return {
44
+ sans,
45
+ mono,
46
+ sansFellBackToBundled: sans.source === 'bundled' && !headlessRequested,
47
+ monoFellBackToBundled: mono.source === 'bundled' && !headlessRequested,
48
+ };
49
+ }
50
+ function isHeadlessRequested(options, env) {
51
+ if (options.headless)
52
+ return true;
53
+ if (env.NOWLINE_HEADLESS === '1' || env.NOWLINE_HEADLESS === 'true')
54
+ return true;
55
+ if (options.disableAutoHeadless)
56
+ return false;
57
+ const inCI = env.CI === 'true' || env.CI === '1';
58
+ const stdoutIsTty = options.isStdoutTty ?? Boolean(process.stdout.isTTY);
59
+ return inCI && !stdoutIsTty;
60
+ }
61
+ async function resolveRole(args) {
62
+ // Step 1 — flag (path or alias)
63
+ if (args.flag) {
64
+ return loadFlag(args.flag, args.role, 'flag', args);
65
+ }
66
+ // Step 2 — environment
67
+ if (args.envValue) {
68
+ return loadFlag(args.envValue, args.role, 'env', args);
69
+ }
70
+ // Step 3 — headless: skip probe, go to bundled
71
+ if (args.headless) {
72
+ return loadBundled(args.role, 'headless');
73
+ }
74
+ // Step 4 — platform probe
75
+ for (const candidate of args.probe[args.role]) {
76
+ if (args.fileExists(candidate.path)) {
77
+ const bytes = await args.readFileBytes(candidate.path);
78
+ return decorate(bytes, {
79
+ name: candidate.name,
80
+ source: 'probe',
81
+ path: candidate.path,
82
+ face: candidate.face,
83
+ });
84
+ }
85
+ }
86
+ // Step 5 — bundled fallback
87
+ return loadBundled(args.role, 'bundled');
88
+ }
89
+ async function loadFlag(raw, role, source, args) {
90
+ if (path.isAbsolute(raw) ||
91
+ raw.startsWith('.') ||
92
+ raw.includes(path.sep) ||
93
+ raw.endsWith('.ttf') ||
94
+ raw.endsWith('.otf') ||
95
+ raw.endsWith('.ttc')) {
96
+ const abs = path.resolve(raw);
97
+ if (!args.fileExists(abs)) {
98
+ throw new FontResolveError(`font path does not exist: ${raw} (resolved to ${abs})`);
99
+ }
100
+ const bytes = await args.readFileBytes(abs);
101
+ return decorate(bytes, {
102
+ name: deriveDisplayName(abs),
103
+ source,
104
+ path: abs,
105
+ });
106
+ }
107
+ if (isAlias(raw)) {
108
+ const candidate = aliasCandidate(raw, role, args.probe);
109
+ if (!candidate) {
110
+ throw new FontResolveError(`alias "${raw}" has no ${role} mapping on this platform`);
111
+ }
112
+ if (!args.fileExists(candidate.path)) {
113
+ // Alias is known but the underlying file is missing — fall through
114
+ // to bundled with the original source preserved is wrong; surface
115
+ // the missing-file error so the user knows their alias didn't land.
116
+ throw new FontResolveError(`alias "${raw}" maps to ${candidate.path} which does not exist on this system`);
117
+ }
118
+ const bytes = await args.readFileBytes(candidate.path);
119
+ return decorate(bytes, {
120
+ name: candidate.name,
121
+ source,
122
+ path: candidate.path,
123
+ face: candidate.face,
124
+ });
125
+ }
126
+ throw new FontResolveError(`font value "${raw}" is neither a path nor a known alias`);
127
+ }
128
+ async function loadBundled(role, source) {
129
+ const bytes = role === 'sans' ? await loadBundledSans() : await loadBundledMono();
130
+ return decorate(bytes, {
131
+ name: role === 'sans' ? 'DejaVu Sans' : 'DejaVu Sans Mono',
132
+ source,
133
+ });
134
+ }
135
+ function decorate(bytes, meta) {
136
+ return {
137
+ name: meta.name,
138
+ bytes,
139
+ source: meta.source,
140
+ path: meta.path,
141
+ face: meta.face,
142
+ isVariableFont: isVariableFontBytes(bytes),
143
+ };
144
+ }
145
+ function deriveDisplayName(absPath) {
146
+ const base = path.basename(absPath, path.extname(absPath));
147
+ return base.replace(/-Regular$/, '').replace(/[_-]/g, ' ');
148
+ }
149
+ async function defaultReadFileBytes(p) {
150
+ const buf = await fs.readFile(p);
151
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
152
+ }
153
+ export class FontResolveError extends Error {
154
+ constructor(message) {
155
+ super(message);
156
+ this.name = 'FontResolveError';
157
+ }
158
+ }
159
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/fonts/resolve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Returns true when the given TTF/OTF byte buffer carries an `fvar` table —
3
+ * i.e. it is an OpenType variable font with continuous axes.
4
+ */
5
+ export declare function isVariableFontBytes(bytes: Uint8Array): boolean;
6
+ //# sourceMappingURL=sfns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sfns.d.ts","sourceRoot":"","sources":["../../src/fonts/sfns.ts"],"names":[],"mappings":"AAWA;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAkB9D"}
@@ -0,0 +1,37 @@
1
+ // SF Pro variable-font handling.
2
+ //
3
+ // Spec: specs/handoffs/m2c.md § 10 "Variable-font handling (SFNS.ttf)".
4
+ //
5
+ // Detection only — actual VF instancing for PDF embedding lives in
6
+ // @nowline/export-pdf, which depends on fontkit directly. The resolver
7
+ // surfaces `isVariableFont: true` so consumers know they may need to
8
+ // pre-instance.
9
+ const FVAR_TAG = 0x66766172; // 'fvar'
10
+ /**
11
+ * Returns true when the given TTF/OTF byte buffer carries an `fvar` table —
12
+ * i.e. it is an OpenType variable font with continuous axes.
13
+ */
14
+ export function isVariableFontBytes(bytes) {
15
+ if (bytes.byteLength < 12)
16
+ return false;
17
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
18
+ const sfntVersion = view.getUint32(0);
19
+ // Accept both TrueType ('\x00\x01\x00\x00') and OpenType/CFF ('OTTO').
20
+ const isSfnt = sfntVersion === 0x00010000 || sfntVersion === 0x4f54544f;
21
+ const isCollection = sfntVersion === 0x74746366; // 'ttcf' — handled separately
22
+ if (!isSfnt && !isCollection)
23
+ return false;
24
+ if (isCollection)
25
+ return false; // collection: caller should index by face first
26
+ const numTables = view.getUint16(4);
27
+ for (let i = 0; i < numTables; i++) {
28
+ const recordOffset = 12 + i * 16;
29
+ if (recordOffset + 16 > bytes.byteLength)
30
+ return false;
31
+ const tag = view.getUint32(recordOffset);
32
+ if (tag === FVAR_TAG)
33
+ return true;
34
+ }
35
+ return false;
36
+ }
37
+ //# sourceMappingURL=sfns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sfns.js","sourceRoot":"","sources":["../../src/fonts/sfns.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,EAAE;AACF,wEAAwE;AACxE,EAAE;AACF,mEAAmE;AACnE,uEAAuE;AACvE,qEAAqE;AACrE,gBAAgB;AAEhB,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"}