@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 @@
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"}
@@ -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"}
@@ -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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"}
@@ -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,6 @@
1
+ // Shared types consumed by every @nowline/export-* package.
2
+ //
3
+ // Heavy deps (resvg, pdfkit, exceljs) live in the format packages, never
4
+ // here — see specs/handoffs/m2c.md § 1.
5
+ export {};
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,EAAE;AACF,yEAAyE;AACzE,wCAAwC"}
@@ -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';