@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.
- package/README.md +131 -0
- package/assets/fonts/DejaVuSans.ttf +0 -0
- package/assets/fonts/DejaVuSansMono.ttf +0 -0
- package/assets/fonts/LICENSE-DejaVu.txt +187 -0
- package/dist/ast-helpers.d.ts +16 -0
- package/dist/ast-helpers.d.ts.map +1 -0
- package/dist/ast-helpers.js +44 -0
- package/dist/ast-helpers.js.map +1 -0
- package/dist/fonts/bundled.d.ts +7 -0
- package/dist/fonts/bundled.d.ts.map +1 -0
- package/dist/fonts/bundled.js +52 -0
- package/dist/fonts/bundled.js.map +1 -0
- package/dist/fonts/index.d.ts +6 -0
- package/dist/fonts/index.d.ts.map +1 -0
- package/dist/fonts/index.js +5 -0
- package/dist/fonts/index.js.map +1 -0
- package/dist/fonts/probe-list.d.ts +29 -0
- package/dist/fonts/probe-list.d.ts.map +1 -0
- package/dist/fonts/probe-list.js +119 -0
- package/dist/fonts/probe-list.js.map +1 -0
- package/dist/fonts/resolve.d.ts +35 -0
- package/dist/fonts/resolve.d.ts.map +1 -0
- package/dist/fonts/resolve.js +159 -0
- package/dist/fonts/resolve.js.map +1 -0
- package/dist/fonts/sfns.d.ts +6 -0
- package/dist/fonts/sfns.d.ts.map +1 -0
- package/dist/fonts/sfns.js +37 -0
- package/dist/fonts/sfns.js.map +1 -0
- package/dist/generated/bundled-fonts.d.ts +3 -0
- package/dist/generated/bundled-fonts.d.ts.map +1 -0
- package/dist/generated/bundled-fonts.js +9 -0
- package/dist/generated/bundled-fonts.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/pdf-page.d.ts +66 -0
- package/dist/pdf-page.d.ts.map +1 -0
- package/dist/pdf-page.js +170 -0
- package/dist/pdf-page.js.map +1 -0
- package/dist/types.d.ts +61 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/units.d.ts +16 -0
- package/dist/units.d.ts.map +1 -0
- package/dist/units.js +56 -0
- package/dist/units.js.map +1 -0
- package/package.json +50 -0
- package/src/ast-helpers.ts +47 -0
- package/src/fonts/bundled.ts +59 -0
- package/src/fonts/index.ts +18 -0
- package/src/fonts/probe-list.ts +143 -0
- package/src/fonts/resolve.ts +228 -0
- package/src/fonts/sfns.ts +34 -0
- package/src/generated/bundled-fonts.ts +10 -0
- package/src/index.ts +62 -0
- package/src/pdf-page.ts +224 -0
- package/src/types.ts +88 -0
- package/src/units.ts +60 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundled-fonts.js","sourceRoot":"","sources":["../../src/generated/bundled-fonts.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,6CAA6C;AAC7C,EAAE;AACF,qEAAqE;AACrE,sEAAsE;AACtE,YAAY;AAEZ,MAAM,CAAC,MAAM,WAAW,GAAG,8xz9BAA8xz9B,CAAC;AAE1zz9B,MAAM,CAAC,MAAM,WAAW,GAAG,8o3bAA8o3b,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { displayLabel, getProp, getProps, hasProp, type PropertyHost, roadmapTitle, } from './ast-helpers.js';
|
|
2
|
+
export { BUNDLED_MONO_PATH, BUNDLED_SANS_PATH, clearBundledCache, loadBundledMono, loadBundledSans, } from './fonts/bundled.js';
|
|
3
|
+
export { ALIASES, aliasCandidate, type FontCandidate, isAlias, type PlatformProbe, probeListFor, } from './fonts/probe-list.js';
|
|
4
|
+
export { FontResolveError, type ResolveOptions, type ResolveResult, resolveFonts, } from './fonts/resolve.js';
|
|
5
|
+
export { isVariableFontBytes } from './fonts/sfns.js';
|
|
6
|
+
export { type ContentScale, fitContent, isPdfPresetName, PageSizeParseError, parsePageSize, presetDimensions, presetNames, type ResolvedPage, resolvePage, validateMargin, } from './pdf-page.js';
|
|
7
|
+
export type { ExportInputs, FontRole, FontSource, PdfLength, PdfLengthUnit, PdfOrientation, PdfPageSize, PdfPresetName, ResolvedFont, ResolvedFontPair, } from './types.js';
|
|
8
|
+
export { LengthParseError, lengthToPoints, parseLength, pointsToLength, } from './units.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { displayLabel, getProp, getProps, hasProp, roadmapTitle, } from './ast-helpers.js';
|
|
2
|
+
export { BUNDLED_MONO_PATH, BUNDLED_SANS_PATH, clearBundledCache, loadBundledMono, loadBundledSans, } from './fonts/bundled.js';
|
|
3
|
+
export { ALIASES, aliasCandidate, isAlias, probeListFor, } from './fonts/probe-list.js';
|
|
4
|
+
export { FontResolveError, resolveFonts, } from './fonts/resolve.js';
|
|
5
|
+
export { isVariableFontBytes } from './fonts/sfns.js';
|
|
6
|
+
export { fitContent, isPdfPresetName, PageSizeParseError, parsePageSize, presetDimensions, presetNames, resolvePage, validateMargin, } from './pdf-page.js';
|
|
7
|
+
export { LengthParseError, lengthToPoints, parseLength, pointsToLength, } from './units.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { PdfOrientation, PdfPageSize, PdfPresetName } from './types.js';
|
|
2
|
+
interface PresetDimensions {
|
|
3
|
+
widthPt: number;
|
|
4
|
+
heightPt: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class PageSizeParseError extends Error {
|
|
7
|
+
constructor(message: string);
|
|
8
|
+
}
|
|
9
|
+
export declare function isPdfPresetName(value: string): value is PdfPresetName;
|
|
10
|
+
export declare function presetNames(): readonly PdfPresetName[];
|
|
11
|
+
export declare function presetDimensions(name: PdfPresetName): PresetDimensions;
|
|
12
|
+
/**
|
|
13
|
+
* Parse a `--page-size` value: preset name, custom `WxHunit`, or `content`.
|
|
14
|
+
* Case-insensitive.
|
|
15
|
+
*/
|
|
16
|
+
export declare function parsePageSize(input: string): PdfPageSize;
|
|
17
|
+
export interface ResolvedPage {
|
|
18
|
+
widthPt: number;
|
|
19
|
+
heightPt: number;
|
|
20
|
+
orientation: 'portrait' | 'landscape';
|
|
21
|
+
isContentSized: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the final page rectangle (in points) from page size, orientation,
|
|
25
|
+
* and content dimensions.
|
|
26
|
+
*
|
|
27
|
+
* Rules per § 4 "Orientation" / "Scaling":
|
|
28
|
+
* - `--page-size content` → page = content + 2 × margin; orientation derived from
|
|
29
|
+
* the resulting aspect; `orientation` argument is ignored.
|
|
30
|
+
* - Fixed page (preset or custom):
|
|
31
|
+
* `auto` → portrait if content taller-than-wide, landscape otherwise.
|
|
32
|
+
* `portrait` / `landscape` → swap the preset W/H if the preset is in the
|
|
33
|
+
* opposite orientation.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolvePage(args: {
|
|
36
|
+
pageSize: PdfPageSize;
|
|
37
|
+
orientation: PdfOrientation;
|
|
38
|
+
contentWidthPt: number;
|
|
39
|
+
contentHeightPt: number;
|
|
40
|
+
marginPt: number;
|
|
41
|
+
}): ResolvedPage;
|
|
42
|
+
/**
|
|
43
|
+
* Validate a margin against the resolved page. `margin × 2 ≥ either dim`
|
|
44
|
+
* means the printable area collapses; throw with a pointer at `--margin`.
|
|
45
|
+
*/
|
|
46
|
+
export declare function validateMargin(marginPt: number, page: ResolvedPage): void;
|
|
47
|
+
export interface ContentScale {
|
|
48
|
+
/** Factor applied to content; 1 = native, < 1 = shrink to fit. */
|
|
49
|
+
factor: number;
|
|
50
|
+
/** Top-left of where the (scaled) content begins inside the page. */
|
|
51
|
+
offsetX: number;
|
|
52
|
+
offsetY: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* For fixed-page mode, fit (centered, never up-scaled) the content rectangle
|
|
56
|
+
* inside the printable area `(page − 2 × margin)`. For content-sized pages,
|
|
57
|
+
* the factor is always 1 and the offset is just the margin.
|
|
58
|
+
*/
|
|
59
|
+
export declare function fitContent(args: {
|
|
60
|
+
page: ResolvedPage;
|
|
61
|
+
contentWidthPt: number;
|
|
62
|
+
contentHeightPt: number;
|
|
63
|
+
marginPt: number;
|
|
64
|
+
}): ContentScale;
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=pdf-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-page.d.ts","sourceRoot":"","sources":["../src/pdf-page.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAa,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGxF,UAAU,gBAAgB;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CACpB;AA+BD,qBAAa,kBAAmB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI9B;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,aAAa,CAErE;AAED,wBAAgB,WAAW,IAAI,SAAS,aAAa,EAAE,CAEtD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,gBAAgB,CAEtE;AAQD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAiCxD;AAED,MAAM,WAAW,YAAY;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,UAAU,GAAG,WAAW,CAAC;IACtC,cAAc,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE;IAC9B,QAAQ,EAAE,WAAW,CAAC;IACtB,WAAW,EAAE,cAAc,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CACpB,GAAG,YAAY,CAqCf;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CASzE;AAED,MAAM,WAAW,YAAY;IACzB,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CACpB,GAAG,YAAY,CAiBf"}
|
package/dist/pdf-page.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// PDF page-size parsing, preset table, and orientation/margin resolution.
|
|
2
|
+
//
|
|
3
|
+
// Spec: specs/handoffs/m2c.md § 4 "PDF — vector PDF via PDFKit"
|
|
4
|
+
// - Default: US Letter (8.5 × 11 in) portrait.
|
|
5
|
+
// - Presets: imperial (letter, legal, tabloid, ledger) + ISO 216 (a5–a1, b5–b3).
|
|
6
|
+
// - Custom: WxHunit (mixed units rejected).
|
|
7
|
+
// - "content": page = content dimensions, no scaling, no upper bound.
|
|
8
|
+
import { LengthParseError, lengthToPoints, parseLength } from './units.js';
|
|
9
|
+
function fromMm(widthMm, heightMm) {
|
|
10
|
+
const factor = 72 / 25.4;
|
|
11
|
+
return {
|
|
12
|
+
widthPt: roundToMicropoint(widthMm * factor),
|
|
13
|
+
heightPt: roundToMicropoint(heightMm * factor),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function roundToMicropoint(n) {
|
|
17
|
+
return Math.round(n * 1000) / 1000;
|
|
18
|
+
}
|
|
19
|
+
const PRESETS = {
|
|
20
|
+
letter: { widthPt: 612, heightPt: 792 },
|
|
21
|
+
legal: { widthPt: 612, heightPt: 1008 },
|
|
22
|
+
tabloid: { widthPt: 792, heightPt: 1224 },
|
|
23
|
+
ledger: { widthPt: 1224, heightPt: 792 },
|
|
24
|
+
a1: fromMm(594, 841),
|
|
25
|
+
a2: fromMm(420, 594),
|
|
26
|
+
a3: fromMm(297, 420),
|
|
27
|
+
a4: fromMm(210, 297),
|
|
28
|
+
a5: fromMm(148, 210),
|
|
29
|
+
b3: fromMm(353, 500),
|
|
30
|
+
b4: fromMm(250, 353),
|
|
31
|
+
b5: fromMm(176, 250),
|
|
32
|
+
};
|
|
33
|
+
const PRESET_NAMES = Object.keys(PRESETS);
|
|
34
|
+
export class PageSizeParseError extends Error {
|
|
35
|
+
constructor(message) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = 'PageSizeParseError';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function isPdfPresetName(value) {
|
|
41
|
+
return PRESET_NAMES.includes(value.toLowerCase());
|
|
42
|
+
}
|
|
43
|
+
export function presetNames() {
|
|
44
|
+
return PRESET_NAMES;
|
|
45
|
+
}
|
|
46
|
+
export function presetDimensions(name) {
|
|
47
|
+
return PRESETS[name];
|
|
48
|
+
}
|
|
49
|
+
// Canonical form: `<W>x<H><unit>` — single trailing unit applies to both
|
|
50
|
+
// (e.g. `8.5x11in`).
|
|
51
|
+
// Explicit form: `<W><unit>x<H><unit>` — both dimensions tagged. Same unit
|
|
52
|
+
// required; mismatch is rejected as "mixed units".
|
|
53
|
+
const CUSTOM_RE = /^(\d+(?:\.\d+)?)([a-z]+)?x(\d+(?:\.\d+)?)([a-z]+)$/i;
|
|
54
|
+
/**
|
|
55
|
+
* Parse a `--page-size` value: preset name, custom `WxHunit`, or `content`.
|
|
56
|
+
* Case-insensitive.
|
|
57
|
+
*/
|
|
58
|
+
export function parsePageSize(input) {
|
|
59
|
+
const lower = input.trim().toLowerCase();
|
|
60
|
+
if (!lower)
|
|
61
|
+
throw new PageSizeParseError('empty page size');
|
|
62
|
+
if (lower === 'content')
|
|
63
|
+
return { kind: 'content' };
|
|
64
|
+
if (isPdfPresetName(lower)) {
|
|
65
|
+
return { kind: 'preset', name: lower };
|
|
66
|
+
}
|
|
67
|
+
const match = CUSTOM_RE.exec(lower);
|
|
68
|
+
if (!match) {
|
|
69
|
+
throw new PageSizeParseError(`invalid page size "${input}": expected preset (${PRESET_NAMES.join(', ')}), "content", or <W>x<H><unit> (e.g. 8.5x11in)`);
|
|
70
|
+
}
|
|
71
|
+
const [, wRaw, wUnitMaybe, hRaw, hUnit] = match;
|
|
72
|
+
if (wUnitMaybe !== undefined && wUnitMaybe !== hUnit) {
|
|
73
|
+
throw new PageSizeParseError(`invalid page size "${input}": mixed units (${wUnitMaybe} vs ${hUnit}); use the same unit for width and height`);
|
|
74
|
+
}
|
|
75
|
+
let width;
|
|
76
|
+
let height;
|
|
77
|
+
try {
|
|
78
|
+
width = parseLength(`${wRaw}${hUnit}`);
|
|
79
|
+
height = parseLength(`${hRaw}${hUnit}`);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
if (err instanceof LengthParseError) {
|
|
83
|
+
throw new PageSizeParseError(`invalid page size "${input}": ${err.message}`);
|
|
84
|
+
}
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
87
|
+
return { kind: 'custom', width, height };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Resolve the final page rectangle (in points) from page size, orientation,
|
|
91
|
+
* and content dimensions.
|
|
92
|
+
*
|
|
93
|
+
* Rules per § 4 "Orientation" / "Scaling":
|
|
94
|
+
* - `--page-size content` → page = content + 2 × margin; orientation derived from
|
|
95
|
+
* the resulting aspect; `orientation` argument is ignored.
|
|
96
|
+
* - Fixed page (preset or custom):
|
|
97
|
+
* `auto` → portrait if content taller-than-wide, landscape otherwise.
|
|
98
|
+
* `portrait` / `landscape` → swap the preset W/H if the preset is in the
|
|
99
|
+
* opposite orientation.
|
|
100
|
+
*/
|
|
101
|
+
export function resolvePage(args) {
|
|
102
|
+
const { pageSize, orientation, contentWidthPt, contentHeightPt, marginPt } = args;
|
|
103
|
+
if (pageSize.kind === 'content') {
|
|
104
|
+
const widthPt = contentWidthPt + 2 * marginPt;
|
|
105
|
+
const heightPt = contentHeightPt + 2 * marginPt;
|
|
106
|
+
return {
|
|
107
|
+
widthPt,
|
|
108
|
+
heightPt,
|
|
109
|
+
orientation: widthPt >= heightPt ? 'landscape' : 'portrait',
|
|
110
|
+
isContentSized: true,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
let widthPt;
|
|
114
|
+
let heightPt;
|
|
115
|
+
if (pageSize.kind === 'preset') {
|
|
116
|
+
({ widthPt, heightPt } = PRESETS[pageSize.name]);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
widthPt = lengthToPoints(pageSize.width);
|
|
120
|
+
heightPt = lengthToPoints(pageSize.height);
|
|
121
|
+
}
|
|
122
|
+
const resolvedOrientation = orientation === 'auto'
|
|
123
|
+
? contentWidthPt > contentHeightPt
|
|
124
|
+
? 'landscape'
|
|
125
|
+
: 'portrait'
|
|
126
|
+
: orientation;
|
|
127
|
+
if (resolvedOrientation === 'landscape' && widthPt < heightPt) {
|
|
128
|
+
[widthPt, heightPt] = [heightPt, widthPt];
|
|
129
|
+
}
|
|
130
|
+
else if (resolvedOrientation === 'portrait' && widthPt > heightPt) {
|
|
131
|
+
[widthPt, heightPt] = [heightPt, widthPt];
|
|
132
|
+
}
|
|
133
|
+
return { widthPt, heightPt, orientation: resolvedOrientation, isContentSized: false };
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Validate a margin against the resolved page. `margin × 2 ≥ either dim`
|
|
137
|
+
* means the printable area collapses; throw with a pointer at `--margin`.
|
|
138
|
+
*/
|
|
139
|
+
export function validateMargin(marginPt, page) {
|
|
140
|
+
if (!Number.isFinite(marginPt) || marginPt < 0) {
|
|
141
|
+
throw new PageSizeParseError('invalid margin: must be a non-negative number of points');
|
|
142
|
+
}
|
|
143
|
+
if (marginPt * 2 >= page.widthPt || marginPt * 2 >= page.heightPt) {
|
|
144
|
+
throw new PageSizeParseError(`margin ${marginPt}pt consumes the entire ${page.widthPt}x${page.heightPt}pt page`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* For fixed-page mode, fit (centered, never up-scaled) the content rectangle
|
|
149
|
+
* inside the printable area `(page − 2 × margin)`. For content-sized pages,
|
|
150
|
+
* the factor is always 1 and the offset is just the margin.
|
|
151
|
+
*/
|
|
152
|
+
export function fitContent(args) {
|
|
153
|
+
const { page, contentWidthPt, contentHeightPt, marginPt } = args;
|
|
154
|
+
if (page.isContentSized) {
|
|
155
|
+
return { factor: 1, offsetX: marginPt, offsetY: marginPt };
|
|
156
|
+
}
|
|
157
|
+
const printableW = page.widthPt - 2 * marginPt;
|
|
158
|
+
const printableH = page.heightPt - 2 * marginPt;
|
|
159
|
+
const scaleX = printableW / contentWidthPt;
|
|
160
|
+
const scaleY = printableH / contentHeightPt;
|
|
161
|
+
const factor = Math.min(scaleX, scaleY, 1);
|
|
162
|
+
const scaledW = contentWidthPt * factor;
|
|
163
|
+
const scaledH = contentHeightPt * factor;
|
|
164
|
+
return {
|
|
165
|
+
factor,
|
|
166
|
+
offsetX: marginPt + (printableW - scaledW) / 2,
|
|
167
|
+
offsetY: marginPt + (printableH - scaledH) / 2,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=pdf-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-page.js","sourceRoot":"","sources":["../src/pdf-page.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { NowlineFile, ResolveResult } from '@nowline/core';
|
|
2
|
+
import type { PositionedRoadmap } from '@nowline/layout';
|
|
3
|
+
/** Bundle of inputs every export function consumes. */
|
|
4
|
+
export interface ExportInputs {
|
|
5
|
+
/** Positioned model produced by `layoutRoadmap()`. */
|
|
6
|
+
model: PositionedRoadmap;
|
|
7
|
+
/** Original AST — needed for XLSX / Mermaid / MS Project. */
|
|
8
|
+
ast: NowlineFile;
|
|
9
|
+
/** Include-resolved data — needed for XLSX joins. */
|
|
10
|
+
resolved: ResolveResult;
|
|
11
|
+
/**
|
|
12
|
+
* Display path for the source. Use `'<stdin>'` when piped. Used in
|
|
13
|
+
* footers / metadata; never for filesystem reads.
|
|
14
|
+
*/
|
|
15
|
+
sourcePath: string;
|
|
16
|
+
/**
|
|
17
|
+
* Optional pinned timestamp. Exporters that would otherwise call
|
|
18
|
+
* `new Date()` (PDF CreationDate, XLSX Created) MUST take this from
|
|
19
|
+
* `ast.generated` first, then `today`, never `new Date()`.
|
|
20
|
+
*/
|
|
21
|
+
today?: Date;
|
|
22
|
+
}
|
|
23
|
+
export type PdfPresetName = 'letter' | 'legal' | 'tabloid' | 'ledger' | 'a5' | 'a4' | 'a3' | 'a2' | 'a1' | 'b5' | 'b4' | 'b3';
|
|
24
|
+
export type PdfLengthUnit = 'pt' | 'in' | 'mm' | 'cm';
|
|
25
|
+
export interface PdfLength {
|
|
26
|
+
value: number;
|
|
27
|
+
unit: PdfLengthUnit;
|
|
28
|
+
}
|
|
29
|
+
export type PdfPageSize = {
|
|
30
|
+
kind: 'preset';
|
|
31
|
+
name: PdfPresetName;
|
|
32
|
+
} | {
|
|
33
|
+
kind: 'custom';
|
|
34
|
+
width: PdfLength;
|
|
35
|
+
height: PdfLength;
|
|
36
|
+
} | {
|
|
37
|
+
kind: 'content';
|
|
38
|
+
};
|
|
39
|
+
export type PdfOrientation = 'portrait' | 'landscape' | 'auto';
|
|
40
|
+
/** Where the resolver landed when it stopped. */
|
|
41
|
+
export type FontSource = 'flag' | 'env' | 'headless' | 'probe' | 'bundled';
|
|
42
|
+
export type FontRole = 'sans' | 'mono';
|
|
43
|
+
export interface ResolvedFont {
|
|
44
|
+
/** Friendly family name, e.g. 'DejaVu Sans', 'SF Pro'. */
|
|
45
|
+
name: string;
|
|
46
|
+
/** Full TTF/OTF bytes, ready for PDFKit / resvg consumption. */
|
|
47
|
+
bytes: Uint8Array;
|
|
48
|
+
/** Where in the resolver chain this font came from. */
|
|
49
|
+
source: FontSource;
|
|
50
|
+
/** Filesystem path; undefined for bundled / synthesized bytes. */
|
|
51
|
+
path?: string;
|
|
52
|
+
/** Face inside a `.ttc` collection, if applicable. */
|
|
53
|
+
face?: string;
|
|
54
|
+
/** True when the loaded font is an OpenType variable font (axes, no fixed instance). */
|
|
55
|
+
isVariableFont?: boolean;
|
|
56
|
+
}
|
|
57
|
+
export interface ResolvedFontPair {
|
|
58
|
+
sans: ResolvedFont;
|
|
59
|
+
mono: ResolvedFont;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,uDAAuD;AACvD,MAAM,WAAW,YAAY;IACzB,sDAAsD;IACtD,KAAK,EAAE,iBAAiB,CAAC;IACzB,6DAA6D;IAC7D,GAAG,EAAE,WAAW,CAAC;IACjB,qDAAqD;IACrD,QAAQ,EAAE,aAAa,CAAC;IACxB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,KAAK,CAAC,EAAE,IAAI,CAAC;CAChB;AAID,MAAM,MAAM,aAAa,GAEnB,QAAQ,GACR,OAAO,GACP,SAAS,GACT,QAAQ,GAER,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,IAAI,GAEJ,IAAI,GACJ,IAAI,GACJ,IAAI,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEtD,MAAM,WAAW,SAAS;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,aAAa,CAAC;CACvB;AAED,MAAM,MAAM,WAAW,GACjB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,aAAa,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAE1B,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,WAAW,GAAG,MAAM,CAAC;AAI/D,iDAAiD;AACjD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;AAE3E,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvC,MAAM,WAAW,YAAY;IACzB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,KAAK,EAAE,UAAU,CAAC;IAClB,uDAAuD;IACvD,MAAM,EAAE,UAAU,CAAC;IACnB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wFAAwF;IACxF,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,YAAY,CAAC;CACtB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,EAAE;AACF,yEAAyE;AACzE,wCAAwC"}
|
package/dist/units.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PdfLength, PdfLengthUnit } from './types.js';
|
|
2
|
+
/** Convert a tagged length to PDF points. */
|
|
3
|
+
export declare function lengthToPoints(length: PdfLength): number;
|
|
4
|
+
/** Convert a raw point count back to a tagged length in the requested unit. */
|
|
5
|
+
export declare function pointsToLength(points: number, unit: PdfLengthUnit): PdfLength;
|
|
6
|
+
export declare class LengthParseError extends Error {
|
|
7
|
+
constructor(input: string, reason: string);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Parse a unit-tagged length like `36pt`, `0.5in`, `10mm`, `1cm`.
|
|
11
|
+
*
|
|
12
|
+
* Throws `LengthParseError` on missing unit, unknown unit, non-numeric, zero,
|
|
13
|
+
* or negative values.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseLength(input: string): PdfLength;
|
|
16
|
+
//# sourceMappingURL=units.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"units.d.ts","sourceRoot":"","sources":["../src/units.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAS3D,6CAA6C;AAC7C,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAExD;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,SAAS,CAE7E;AAED,qBAAa,gBAAiB,SAAQ,KAAK;gBAC3B,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAI5C;AAKD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAkBpD"}
|
package/dist/units.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Length conversions and parsing for PDF page sizing.
|
|
2
|
+
//
|
|
3
|
+
// PDF native unit is the "PostScript point": 1 pt = 1/72 in. Every length
|
|
4
|
+
// resolves to points before the page is laid out — see specs/handoffs/m2c.md
|
|
5
|
+
// § 4 "Unit conversion".
|
|
6
|
+
const POINTS_PER_UNIT = {
|
|
7
|
+
pt: 1,
|
|
8
|
+
in: 72,
|
|
9
|
+
mm: 72 / 25.4, // ≈ 2.83464567
|
|
10
|
+
cm: 72 / 2.54, // ≈ 28.3464567
|
|
11
|
+
};
|
|
12
|
+
/** Convert a tagged length to PDF points. */
|
|
13
|
+
export function lengthToPoints(length) {
|
|
14
|
+
return length.value * POINTS_PER_UNIT[length.unit];
|
|
15
|
+
}
|
|
16
|
+
/** Convert a raw point count back to a tagged length in the requested unit. */
|
|
17
|
+
export function pointsToLength(points, unit) {
|
|
18
|
+
return { value: points / POINTS_PER_UNIT[unit], unit };
|
|
19
|
+
}
|
|
20
|
+
export class LengthParseError extends Error {
|
|
21
|
+
constructor(input, reason) {
|
|
22
|
+
super(`invalid length "${input}": ${reason}`);
|
|
23
|
+
this.name = 'LengthParseError';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const LENGTH_RE = /^(-?\d+(?:\.\d+)?)([a-z]+)$/i;
|
|
27
|
+
const KNOWN_UNITS = new Set(['pt', 'in', 'mm', 'cm']);
|
|
28
|
+
/**
|
|
29
|
+
* Parse a unit-tagged length like `36pt`, `0.5in`, `10mm`, `1cm`.
|
|
30
|
+
*
|
|
31
|
+
* Throws `LengthParseError` on missing unit, unknown unit, non-numeric, zero,
|
|
32
|
+
* or negative values.
|
|
33
|
+
*/
|
|
34
|
+
export function parseLength(input) {
|
|
35
|
+
const trimmed = input.trim();
|
|
36
|
+
if (!trimmed)
|
|
37
|
+
throw new LengthParseError(input, 'empty');
|
|
38
|
+
const match = LENGTH_RE.exec(trimmed);
|
|
39
|
+
if (!match) {
|
|
40
|
+
if (/^-?\d+(?:\.\d+)?$/.test(trimmed)) {
|
|
41
|
+
throw new LengthParseError(input, 'missing unit (expected pt, in, mm, or cm)');
|
|
42
|
+
}
|
|
43
|
+
throw new LengthParseError(input, 'expected <number><unit>');
|
|
44
|
+
}
|
|
45
|
+
const unit = match[2].toLowerCase();
|
|
46
|
+
if (!KNOWN_UNITS.has(unit)) {
|
|
47
|
+
throw new LengthParseError(input, `unknown unit "${unit}"; expected one of pt, in, mm, cm`);
|
|
48
|
+
}
|
|
49
|
+
const value = Number(match[1]);
|
|
50
|
+
if (!Number.isFinite(value))
|
|
51
|
+
throw new LengthParseError(input, 'non-numeric value');
|
|
52
|
+
if (value <= 0)
|
|
53
|
+
throw new LengthParseError(input, 'must be positive');
|
|
54
|
+
return { value, unit: unit };
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=units.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"units.js","sourceRoot":"","sources":["../src/units.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,EAAE;AACF,0EAA0E;AAC1E,6EAA6E;AAC7E,yBAAyB;AAIzB,MAAM,eAAe,GAA4C;IAC7D,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,EAAE;IACN,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,eAAe;IAC9B,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,eAAe;CACjC,CAAC;AAEF,6CAA6C;AAC7C,MAAM,UAAU,cAAc,CAAC,MAAiB;IAC5C,OAAO,MAAM,CAAC,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AACvD,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,IAAmB;IAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;AAC3D,CAAC;AAED,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACvC,YAAY,KAAa,EAAE,MAAc;QACrC,KAAK,CAAC,mBAAmB,KAAK,MAAM,MAAM,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACnC,CAAC;CACJ;AAED,MAAM,SAAS,GAAG,8BAA8B,CAAC;AACjD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AAErE;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa;IACrC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK,EAAE,CAAC;QACT,IAAI,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,gBAAgB,CAAC,KAAK,EAAE,2CAA2C,CAAC,CAAC;QACnF,CAAC;QACD,MAAM,IAAI,gBAAgB,CAAC,KAAK,EAAE,yBAAyB,CAAC,CAAC;IACjE,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACpC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAqB,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,IAAI,mCAAmC,CAAC,CAAC;IAChG,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,gBAAgB,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;IACpF,IAAI,KAAK,IAAI,CAAC;QAAE,MAAM,IAAI,gBAAgB,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAC;IACtE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAqB,EAAE,CAAC;AAClD,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nowline/export-core",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Nowline export-core — shared types, font resolver, unit converter, PDF page-size types",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./fonts": {
|
|
15
|
+
"types": "./dist/fonts/index.d.ts",
|
|
16
|
+
"import": "./dist/fonts/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./units": {
|
|
19
|
+
"types": "./dist/units.d.ts",
|
|
20
|
+
"import": "./dist/units.js"
|
|
21
|
+
},
|
|
22
|
+
"./pdf-page": {
|
|
23
|
+
"types": "./dist/pdf-page.d.ts",
|
|
24
|
+
"import": "./dist/pdf-page.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist/",
|
|
29
|
+
"src/",
|
|
30
|
+
"assets/"
|
|
31
|
+
],
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@nowline/layout": "0.2.0",
|
|
34
|
+
"@nowline/core": "0.2.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^22.0.0",
|
|
38
|
+
"typescript": "~5.7.0",
|
|
39
|
+
"vitest": "^3.1.0"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"bundle-fonts": "node scripts/bundle-fonts.mjs",
|
|
43
|
+
"prebuild": "node scripts/bundle-fonts.mjs",
|
|
44
|
+
"build": "tsc -b tsconfig.json",
|
|
45
|
+
"watch": "tsc -b tsconfig.json --watch",
|
|
46
|
+
"pretest": "node scripts/bundle-fonts.mjs",
|
|
47
|
+
"test": "vitest run",
|
|
48
|
+
"test:watch": "vitest"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Tiny AST property helpers shared by lossy exporters (Mermaid, MS Project,
|
|
2
|
+
// XLSX). The Langium-generated AST stores DSL properties as
|
|
3
|
+
// `EntityProperty[]` with `{ key, value?, values[] }`. Walking that shape
|
|
4
|
+
// directly is verbose; these helpers keep the exporters readable.
|
|
5
|
+
|
|
6
|
+
import type { EntityProperty, RoadmapDeclaration } from '@nowline/core';
|
|
7
|
+
|
|
8
|
+
export type PropertyHost = {
|
|
9
|
+
properties?: EntityProperty[];
|
|
10
|
+
title?: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/** Return the single-value `value` for `key`, or undefined. */
|
|
15
|
+
export function getProp(host: PropertyHost, key: string): string | undefined {
|
|
16
|
+
const prop = host.properties?.find((p) => p.key === key);
|
|
17
|
+
if (!prop) return undefined;
|
|
18
|
+
if (prop.value !== undefined) return prop.value;
|
|
19
|
+
if (prop.values && prop.values.length > 0) return prop.values[0];
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Return the multi-value list for `key` (e.g. `after:[a, b]`), or empty. */
|
|
24
|
+
export function getProps(host: PropertyHost, key: string): readonly string[] {
|
|
25
|
+
const prop = host.properties?.find((p) => p.key === key);
|
|
26
|
+
if (!prop) return [];
|
|
27
|
+
if (prop.values && prop.values.length > 0) return prop.values;
|
|
28
|
+
if (prop.value !== undefined) return [prop.value];
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function hasProp(host: PropertyHost, key: string): boolean {
|
|
33
|
+
return host.properties?.some((p) => p.key === key) ?? false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Display label: title if present, otherwise name (id), otherwise '<unnamed>'. */
|
|
37
|
+
export function displayLabel(host: PropertyHost): string {
|
|
38
|
+
if (host.title?.trim()) return host.title;
|
|
39
|
+
if (host.name?.trim()) return host.name;
|
|
40
|
+
return '<unnamed>';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Roadmap title falls back to declaration name. */
|
|
44
|
+
export function roadmapTitle(decl: RoadmapDeclaration | undefined): string {
|
|
45
|
+
if (!decl) return 'Roadmap';
|
|
46
|
+
return decl.title?.trim() || decl.name?.trim() || 'Roadmap';
|
|
47
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Bundled-fallback font loader.
|
|
2
|
+
//
|
|
3
|
+
// Spec: specs/handoffs/m2c.md § 10 "Bundled fallback".
|
|
4
|
+
//
|
|
5
|
+
// Ships exactly two TTFs under `assets/fonts/`: DejaVuSans.ttf (~740 KB) and
|
|
6
|
+
// DejaVuSansMono.ttf (~330 KB). Bold / italic are synthesized by PDFKit's
|
|
7
|
+
// faux-bold and skew transforms. License: see LICENSE-DejaVu.txt next to the
|
|
8
|
+
// fonts.
|
|
9
|
+
//
|
|
10
|
+
// The TTF bytes are embedded as base64 string literals via
|
|
11
|
+
// `scripts/bundle-fonts.mjs` (run as `prebuild` / `pretest`) so the loader
|
|
12
|
+
// works under both Node and a bun --compile binary. `bun --compile` does not
|
|
13
|
+
// virtualize fs.readFile of import.meta.url paths, so the on-disk asset is
|
|
14
|
+
// invisible inside the compiled binary; the embedded strings are the
|
|
15
|
+
// runtime source of truth.
|
|
16
|
+
|
|
17
|
+
import * as path from 'node:path';
|
|
18
|
+
import { fileURLToPath } from 'node:url';
|
|
19
|
+
|
|
20
|
+
import { MONO_BASE64, SANS_BASE64 } from '../generated/bundled-fonts.js';
|
|
21
|
+
|
|
22
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
|
|
24
|
+
// dist/fonts/bundled.js → ../../assets/fonts/
|
|
25
|
+
// src/fonts/bundled.ts → ../../assets/fonts/
|
|
26
|
+
const ASSETS_DIR = path.resolve(HERE, '..', '..', 'assets', 'fonts');
|
|
27
|
+
|
|
28
|
+
// Informational: where the source-of-truth TTFs live on disk for users
|
|
29
|
+
// running under Node. Not the runtime load source — `loadBundledSans` and
|
|
30
|
+
// `loadBundledMono` decode from the embedded base64 instead. Inside a bun
|
|
31
|
+
// --compile binary these paths are phantom (the assets are not on disk).
|
|
32
|
+
export const BUNDLED_SANS_PATH = path.join(ASSETS_DIR, 'DejaVuSans.ttf');
|
|
33
|
+
export const BUNDLED_MONO_PATH = path.join(ASSETS_DIR, 'DejaVuSansMono.ttf');
|
|
34
|
+
|
|
35
|
+
let cachedSans: Uint8Array | undefined;
|
|
36
|
+
let cachedMono: Uint8Array | undefined;
|
|
37
|
+
|
|
38
|
+
function decodeBase64(b64: string): Uint8Array {
|
|
39
|
+
const buf = Buffer.from(b64, 'base64');
|
|
40
|
+
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function loadBundledSans(): Promise<Uint8Array> {
|
|
44
|
+
if (cachedSans) return cachedSans;
|
|
45
|
+
cachedSans = decodeBase64(SANS_BASE64);
|
|
46
|
+
return cachedSans;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function loadBundledMono(): Promise<Uint8Array> {
|
|
50
|
+
if (cachedMono) return cachedMono;
|
|
51
|
+
cachedMono = decodeBase64(MONO_BASE64);
|
|
52
|
+
return cachedMono;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Test seam: drop cached bytes so platform-mocked tests start fresh. */
|
|
56
|
+
export function clearBundledCache(): void {
|
|
57
|
+
cachedSans = undefined;
|
|
58
|
+
cachedMono = undefined;
|
|
59
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export {
|
|
2
|
+
BUNDLED_MONO_PATH,
|
|
3
|
+
BUNDLED_SANS_PATH,
|
|
4
|
+
clearBundledCache,
|
|
5
|
+
loadBundledMono,
|
|
6
|
+
loadBundledSans,
|
|
7
|
+
} from './bundled.js';
|
|
8
|
+
export {
|
|
9
|
+
ALIASES,
|
|
10
|
+
aliasCandidate,
|
|
11
|
+
type FontCandidate,
|
|
12
|
+
isAlias,
|
|
13
|
+
type PlatformProbe,
|
|
14
|
+
probeListFor,
|
|
15
|
+
} from './probe-list.js';
|
|
16
|
+
export type { ResolveOptions, ResolveResult } from './resolve.js';
|
|
17
|
+
export { FontResolveError, resolveFonts } from './resolve.js';
|
|
18
|
+
export { isVariableFontBytes } from './sfns.js';
|