postext-pdf 1.4.2 → 1.5.1

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 (208) hide show
  1. package/README.md +3 -3
  2. package/dist/__tests__/accessibility.test.js +66 -0
  3. package/dist/__tests__/accessibility.test.js.map +1 -1
  4. package/dist/__tests__/alpha.test.d.ts +2 -0
  5. package/dist/__tests__/alpha.test.d.ts.map +1 -0
  6. package/dist/__tests__/alpha.test.js +164 -0
  7. package/dist/__tests__/alpha.test.js.map +1 -0
  8. package/dist/__tests__/bleedClip.test.d.ts +2 -0
  9. package/dist/__tests__/bleedClip.test.d.ts.map +1 -0
  10. package/dist/__tests__/bleedClip.test.js +83 -0
  11. package/dist/__tests__/bleedClip.test.js.map +1 -0
  12. package/dist/__tests__/boxBorderInside.test.d.ts +2 -0
  13. package/dist/__tests__/boxBorderInside.test.d.ts.map +1 -0
  14. package/dist/__tests__/boxBorderInside.test.js +63 -0
  15. package/dist/__tests__/boxBorderInside.test.js.map +1 -0
  16. package/dist/__tests__/calloutRuntTracking.test.d.ts +2 -0
  17. package/dist/__tests__/calloutRuntTracking.test.d.ts.map +1 -0
  18. package/dist/__tests__/calloutRuntTracking.test.js +72 -0
  19. package/dist/__tests__/calloutRuntTracking.test.js.map +1 -0
  20. package/dist/__tests__/calloutStripeRadius.test.d.ts +2 -0
  21. package/dist/__tests__/calloutStripeRadius.test.d.ts.map +1 -0
  22. package/dist/__tests__/calloutStripeRadius.test.js +90 -0
  23. package/dist/__tests__/calloutStripeRadius.test.js.map +1 -0
  24. package/dist/__tests__/chipVerticalPadding.test.d.ts +2 -0
  25. package/dist/__tests__/chipVerticalPadding.test.d.ts.map +1 -0
  26. package/dist/__tests__/chipVerticalPadding.test.js +71 -0
  27. package/dist/__tests__/chipVerticalPadding.test.js.map +1 -0
  28. package/dist/__tests__/chips.test.js +20 -3
  29. package/dist/__tests__/chips.test.js.map +1 -1
  30. package/dist/__tests__/documentLanguage.test.d.ts +2 -0
  31. package/dist/__tests__/documentLanguage.test.d.ts.map +1 -0
  32. package/dist/__tests__/documentLanguage.test.js +44 -0
  33. package/dist/__tests__/documentLanguage.test.js.map +1 -0
  34. package/dist/__tests__/fallbackSpaces.test.d.ts +2 -0
  35. package/dist/__tests__/fallbackSpaces.test.d.ts.map +1 -0
  36. package/dist/__tests__/fallbackSpaces.test.js +156 -0
  37. package/dist/__tests__/fallbackSpaces.test.js.map +1 -0
  38. package/dist/__tests__/floatReadingOrder.test.d.ts +2 -0
  39. package/dist/__tests__/floatReadingOrder.test.d.ts.map +1 -0
  40. package/dist/__tests__/floatReadingOrder.test.js +208 -0
  41. package/dist/__tests__/floatReadingOrder.test.js.map +1 -0
  42. package/dist/__tests__/fontEmbedding.test.d.ts +2 -0
  43. package/dist/__tests__/fontEmbedding.test.d.ts.map +1 -0
  44. package/dist/__tests__/fontEmbedding.test.js +118 -0
  45. package/dist/__tests__/fontEmbedding.test.js.map +1 -0
  46. package/dist/__tests__/fontFaces.test.d.ts +2 -0
  47. package/dist/__tests__/fontFaces.test.d.ts.map +1 -0
  48. package/dist/__tests__/fontFaces.test.js +193 -0
  49. package/dist/__tests__/fontFaces.test.js.map +1 -0
  50. package/dist/__tests__/fontStack.test.d.ts +2 -0
  51. package/dist/__tests__/fontStack.test.d.ts.map +1 -0
  52. package/dist/__tests__/fontStack.test.js +48 -0
  53. package/dist/__tests__/fontStack.test.js.map +1 -0
  54. package/dist/__tests__/headerFooter.test.js +83 -1
  55. package/dist/__tests__/headerFooter.test.js.map +1 -1
  56. package/dist/__tests__/headingAlign.test.d.ts +2 -0
  57. package/dist/__tests__/headingAlign.test.d.ts.map +1 -0
  58. package/dist/__tests__/headingAlign.test.js +66 -0
  59. package/dist/__tests__/headingAlign.test.js.map +1 -0
  60. package/dist/__tests__/hiddenHeadings.test.d.ts +2 -0
  61. package/dist/__tests__/hiddenHeadings.test.d.ts.map +1 -0
  62. package/dist/__tests__/hiddenHeadings.test.js +96 -0
  63. package/dist/__tests__/hiddenHeadings.test.js.map +1 -0
  64. package/dist/__tests__/lineTracking.test.d.ts +2 -0
  65. package/dist/__tests__/lineTracking.test.d.ts.map +1 -0
  66. package/dist/__tests__/lineTracking.test.js +80 -0
  67. package/dist/__tests__/lineTracking.test.js.map +1 -0
  68. package/dist/__tests__/measuredLines.test.d.ts +2 -0
  69. package/dist/__tests__/measuredLines.test.d.ts.map +1 -0
  70. package/dist/__tests__/measuredLines.test.js +255 -0
  71. package/dist/__tests__/measuredLines.test.js.map +1 -0
  72. package/dist/__tests__/metadata.test.d.ts +2 -0
  73. package/dist/__tests__/metadata.test.d.ts.map +1 -0
  74. package/dist/__tests__/metadata.test.js +51 -0
  75. package/dist/__tests__/metadata.test.js.map +1 -0
  76. package/dist/__tests__/outlineTitles.test.d.ts +2 -0
  77. package/dist/__tests__/outlineTitles.test.d.ts.map +1 -0
  78. package/dist/__tests__/outlineTitles.test.js +59 -0
  79. package/dist/__tests__/outlineTitles.test.js.map +1 -0
  80. package/dist/__tests__/pdfGenerationConfig.test.d.ts +2 -0
  81. package/dist/__tests__/pdfGenerationConfig.test.d.ts.map +1 -0
  82. package/dist/__tests__/pdfGenerationConfig.test.js +76 -0
  83. package/dist/__tests__/pdfGenerationConfig.test.js.map +1 -0
  84. package/dist/__tests__/pdfWorker.test.d.ts +2 -0
  85. package/dist/__tests__/pdfWorker.test.d.ts.map +1 -0
  86. package/dist/__tests__/pdfWorker.test.js +123 -0
  87. package/dist/__tests__/pdfWorker.test.js.map +1 -0
  88. package/dist/__tests__/printMarks.test.d.ts +2 -0
  89. package/dist/__tests__/printMarks.test.d.ts.map +1 -0
  90. package/dist/__tests__/printMarks.test.js +170 -0
  91. package/dist/__tests__/printMarks.test.js.map +1 -0
  92. package/dist/__tests__/printMaster.test.d.ts +2 -0
  93. package/dist/__tests__/printMaster.test.d.ts.map +1 -0
  94. package/dist/__tests__/printMaster.test.js +161 -0
  95. package/dist/__tests__/printMaster.test.js.map +1 -0
  96. package/dist/__tests__/renderWarnings.test.d.ts +2 -0
  97. package/dist/__tests__/renderWarnings.test.d.ts.map +1 -0
  98. package/dist/__tests__/renderWarnings.test.js +89 -0
  99. package/dist/__tests__/renderWarnings.test.js.map +1 -0
  100. package/dist/__tests__/repeatedHyphen.test.d.ts +2 -0
  101. package/dist/__tests__/repeatedHyphen.test.d.ts.map +1 -0
  102. package/dist/__tests__/repeatedHyphen.test.js +125 -0
  103. package/dist/__tests__/repeatedHyphen.test.js.map +1 -0
  104. package/dist/__tests__/scriptStacking.test.d.ts +2 -0
  105. package/dist/__tests__/scriptStacking.test.d.ts.map +1 -0
  106. package/dist/__tests__/scriptStacking.test.js +66 -0
  107. package/dist/__tests__/scriptStacking.test.js.map +1 -0
  108. package/dist/__tests__/sectionColumnRule.test.d.ts +2 -0
  109. package/dist/__tests__/sectionColumnRule.test.d.ts.map +1 -0
  110. package/dist/__tests__/sectionColumnRule.test.js +144 -0
  111. package/dist/__tests__/sectionColumnRule.test.js.map +1 -0
  112. package/dist/__tests__/svgFormReuse.test.d.ts +2 -0
  113. package/dist/__tests__/svgFormReuse.test.d.ts.map +1 -0
  114. package/dist/__tests__/svgFormReuse.test.js +102 -0
  115. package/dist/__tests__/svgFormReuse.test.js.map +1 -0
  116. package/dist/__tests__/svgVector.test.js +85 -11
  117. package/dist/__tests__/svgVector.test.js.map +1 -1
  118. package/dist/__tests__/tableFillSeams.test.d.ts +2 -0
  119. package/dist/__tests__/tableFillSeams.test.d.ts.map +1 -0
  120. package/dist/__tests__/tableFillSeams.test.js +72 -0
  121. package/dist/__tests__/tableFillSeams.test.js.map +1 -0
  122. package/dist/__tests__/tocLeaderKerning.test.d.ts +2 -0
  123. package/dist/__tests__/tocLeaderKerning.test.d.ts.map +1 -0
  124. package/dist/__tests__/tocLeaderKerning.test.js +84 -0
  125. package/dist/__tests__/tocLeaderKerning.test.js.map +1 -0
  126. package/dist/__tests__/tocNumberBaseline.test.d.ts +2 -0
  127. package/dist/__tests__/tocNumberBaseline.test.d.ts.map +1 -0
  128. package/dist/__tests__/tocNumberBaseline.test.js +72 -0
  129. package/dist/__tests__/tocNumberBaseline.test.js.map +1 -0
  130. package/dist/__tests__/tocTagging.test.d.ts +2 -0
  131. package/dist/__tests__/tocTagging.test.d.ts.map +1 -0
  132. package/dist/__tests__/tocTagging.test.js +120 -0
  133. package/dist/__tests__/tocTagging.test.js.map +1 -0
  134. package/dist/__tests__/trackedLineAlign.test.d.ts +2 -0
  135. package/dist/__tests__/trackedLineAlign.test.d.ts.map +1 -0
  136. package/dist/__tests__/trackedLineAlign.test.js +73 -0
  137. package/dist/__tests__/trackedLineAlign.test.js.map +1 -0
  138. package/dist/__tests__/uriLinks.test.d.ts +2 -0
  139. package/dist/__tests__/uriLinks.test.d.ts.map +1 -0
  140. package/dist/__tests__/uriLinks.test.js +103 -0
  141. package/dist/__tests__/uriLinks.test.js.map +1 -0
  142. package/dist/colors.d.ts +18 -3
  143. package/dist/colors.d.ts.map +1 -1
  144. package/dist/colors.js +77 -25
  145. package/dist/colors.js.map +1 -1
  146. package/dist/fontCache.d.ts +67 -4
  147. package/dist/fontCache.d.ts.map +1 -1
  148. package/dist/fontCache.js +200 -23
  149. package/dist/fontCache.js.map +1 -1
  150. package/dist/index.d.ts +1 -1
  151. package/dist/index.d.ts.map +1 -1
  152. package/dist/pdf-backend/blockRender.d.ts.map +1 -1
  153. package/dist/pdf-backend/blockRender.js +127 -28
  154. package/dist/pdf-backend/blockRender.js.map +1 -1
  155. package/dist/pdf-backend/fallbackSpaces.d.ts +37 -0
  156. package/dist/pdf-backend/fallbackSpaces.d.ts.map +1 -0
  157. package/dist/pdf-backend/fallbackSpaces.js +119 -0
  158. package/dist/pdf-backend/fallbackSpaces.js.map +1 -0
  159. package/dist/pdf-backend/fontHelpers.d.ts +10 -1
  160. package/dist/pdf-backend/fontHelpers.d.ts.map +1 -1
  161. package/dist/pdf-backend/fontHelpers.js +111 -65
  162. package/dist/pdf-backend/fontHelpers.js.map +1 -1
  163. package/dist/pdf-backend/headerFooter.d.ts +4 -2
  164. package/dist/pdf-backend/headerFooter.d.ts.map +1 -1
  165. package/dist/pdf-backend/headerFooter.js +105 -48
  166. package/dist/pdf-backend/headerFooter.js.map +1 -1
  167. package/dist/pdf-backend/index.d.ts +55 -6
  168. package/dist/pdf-backend/index.d.ts.map +1 -1
  169. package/dist/pdf-backend/index.js +135 -35
  170. package/dist/pdf-backend/index.js.map +1 -1
  171. package/dist/pdf-backend/links.d.ts +82 -1
  172. package/dist/pdf-backend/links.d.ts.map +1 -1
  173. package/dist/pdf-backend/links.js +141 -3
  174. package/dist/pdf-backend/links.js.map +1 -1
  175. package/dist/pdf-backend/outlines.d.ts.map +1 -1
  176. package/dist/pdf-backend/outlines.js +7 -0
  177. package/dist/pdf-backend/outlines.js.map +1 -1
  178. package/dist/pdf-backend/pageDecorations.d.ts.map +1 -1
  179. package/dist/pdf-backend/pageDecorations.js +42 -26
  180. package/dist/pdf-backend/pageDecorations.js.map +1 -1
  181. package/dist/pdf-backend/primitives.d.ts +61 -1
  182. package/dist/pdf-backend/primitives.d.ts.map +1 -1
  183. package/dist/pdf-backend/primitives.js +318 -18
  184. package/dist/pdf-backend/primitives.js.map +1 -1
  185. package/dist/pdf-backend/renderResourceBlock.d.ts +3 -1
  186. package/dist/pdf-backend/renderResourceBlock.d.ts.map +1 -1
  187. package/dist/pdf-backend/renderResourceBlock.js +97 -19
  188. package/dist/pdf-backend/renderResourceBlock.js.map +1 -1
  189. package/dist/pdf-backend/structureFlow.d.ts +70 -3
  190. package/dist/pdf-backend/structureFlow.d.ts.map +1 -1
  191. package/dist/pdf-backend/structureFlow.js +209 -27
  192. package/dist/pdf-backend/structureFlow.js.map +1 -1
  193. package/dist/pdf-backend/svgVector.d.ts +7 -2
  194. package/dist/pdf-backend/svgVector.d.ts.map +1 -1
  195. package/dist/pdf-backend/svgVector.js +157 -72
  196. package/dist/pdf-backend/svgVector.js.map +1 -1
  197. package/dist/pdf-backend/tagging.d.ts +1 -1
  198. package/dist/pdf-backend/tagging.d.ts.map +1 -1
  199. package/dist/pdf-backend/tagging.js.map +1 -1
  200. package/dist/worker/client.d.ts +1 -1
  201. package/dist/worker/client.d.ts.map +1 -1
  202. package/dist/worker/client.js +8 -0
  203. package/dist/worker/client.js.map +1 -1
  204. package/dist/worker/pdf.worker.js +1 -0
  205. package/dist/worker/pdf.worker.js.map +1 -1
  206. package/dist/worker/protocol.d.ts +8 -1
  207. package/dist/worker/protocol.d.ts.map +1 -1
  208. package/package.json +3 -3
@@ -0,0 +1,123 @@
1
+ import { describe, it, expect, beforeAll, vi } from 'vitest';
2
+ import fs from 'node:fs';
3
+ import fontkit from '@pdf-lib/fontkit';
4
+ import { buildDocument } from 'postext';
5
+ import { parseFontString } from '../fontString';
6
+ // EF-52: `postext-pdf/worker` end to end, with the worker run in-process
7
+ // behind a message channel that clones every message the way a real
8
+ // worker boundary does. The host answers the worker's font questions and
9
+ // receives its progress and warnings.
10
+ const fontBytes = fs.readFileSync(new URL('../../../../apps/web/public/fonts/Lora-Regular.ttf', import.meta.url));
11
+ const face = fontkit.create(fontBytes);
12
+ class StubCtx {
13
+ font = '';
14
+ measureText(s) {
15
+ const sizePx = parseFontString(this.font)?.sizePx ?? 16;
16
+ return { width: (face.layout(s).advanceWidth / face.unitsPerEm) * sizePx };
17
+ }
18
+ }
19
+ globalThis.OffscreenCanvas = class {
20
+ getContext() {
21
+ return new StubCtx();
22
+ }
23
+ };
24
+ /** One end of a message channel: messages posted here reach the other end
25
+ * on a later task, structured-cloned. */
26
+ class Port {
27
+ other;
28
+ listeners = [];
29
+ addEventListener(type, fn) {
30
+ if (type === 'message')
31
+ this.listeners.push(fn);
32
+ }
33
+ postMessage(data) {
34
+ const copy = structuredClone(data);
35
+ setTimeout(() => {
36
+ for (const fn of this.other.listeners)
37
+ fn({ data: copy });
38
+ }, 0);
39
+ }
40
+ terminate() { }
41
+ close() { }
42
+ }
43
+ /** One handle for the one in-process worker (a handle listens to every
44
+ * message of its worker, so two on one channel would both answer). */
45
+ let pdf;
46
+ beforeAll(async () => {
47
+ const host = new Port();
48
+ const scope = new Port();
49
+ host.other = scope;
50
+ scope.other = host;
51
+ // The worker module binds `self` when it loads.
52
+ globalThis.self = scope;
53
+ await import('../worker/pdf.worker');
54
+ const { createPdfWorker } = await import('../worker/client');
55
+ pdf = createPdfWorker({ worker: host });
56
+ });
57
+ const pt = (value) => ({ value, unit: 'pt' });
58
+ const config = {
59
+ page: { width: pt(300), height: pt(300), margins: { top: pt(20), bottom: pt(20), left: pt(20), right: pt(20) } },
60
+ bodyText: { fontFamily: 'Lora' },
61
+ headings: { fontFamily: 'Lora', levels: [{ level: 1, breakBefore: { enabled: false } }] },
62
+ };
63
+ describe('createPdfWorker (EF-52)', () => {
64
+ it('renders on the worker, asking the host for fonts and reporting progress', async () => {
65
+ const doc = buildDocument({ markdown: '# Title\n\nBody text.' }, config);
66
+ const asked = [];
67
+ const progress = [];
68
+ const bytes = await pdf.render(doc, {
69
+ fontProvider: async (family, weight, style) => {
70
+ asked.push(`${family}|${weight}|${style}`);
71
+ return new Uint8Array(fontBytes);
72
+ },
73
+ onProgress: (p) => progress.push(p.phase),
74
+ });
75
+ expect(new TextDecoder().decode(bytes.slice(0, 5))).toBe('%PDF-');
76
+ expect(asked).toContain('Lora|400|normal');
77
+ expect(progress).toEqual(expect.arrayContaining(['prepare', 'pages', 'save']));
78
+ });
79
+ it('forwards font fallback warnings to the host', async () => {
80
+ const doc = buildDocument({ markdown: 'Some *italic* words.' }, config);
81
+ const warnings = [];
82
+ const bytes = await pdf.render(doc, {
83
+ fontProvider: async (_family, _weight, style) => {
84
+ if (style === 'italic')
85
+ throw new Error('no italic cut');
86
+ return new Uint8Array(fontBytes);
87
+ },
88
+ onWarning: (w) => { if (w.kind === 'fontFallback')
89
+ warnings.push(w); },
90
+ });
91
+ expect(bytes.byteLength).toBeGreaterThan(0);
92
+ expect(warnings).toHaveLength(1);
93
+ expect(warnings[0]).toMatchObject({ kind: 'fontFallback', family: 'Lora', style: 'italic', fallback: { style: 'normal' } });
94
+ expect(warnings[0].reason).toContain('no italic cut');
95
+ });
96
+ it('forwards a missing image to the host, and logs only font fallbacks without a handler', async () => {
97
+ const photo = {
98
+ id: 'photo', typeId: 'figure', kind: 'bitmap', caption: 'A photo.', createdAt: 0, updatedAt: 0,
99
+ bitmap: { fileId: 'file-photo', format: 'png', width: 400, height: 300 }, placement: { position: 'here' },
100
+ };
101
+ const doc = buildDocument({ markdown: 'Text.\n\n::resource{id="photo"}\n', resources: [photo] }, config);
102
+ const fontProvider = async () => new Uint8Array(fontBytes);
103
+ const warnings = [];
104
+ await pdf.render(doc, { fontProvider, onWarning: (w) => warnings.push(w) });
105
+ expect(warnings).toEqual([{ kind: 'missingImage', fileId: 'file-photo', resourceId: 'photo', pageIndex: 0, documentIndex: 0 }]);
106
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
107
+ try {
108
+ await pdf.render(doc, { fontProvider });
109
+ expect(warn).not.toHaveBeenCalled();
110
+ }
111
+ finally {
112
+ warn.mockRestore();
113
+ }
114
+ });
115
+ it('rejects when a family cannot be had at all', async () => {
116
+ const doc = buildDocument({ markdown: 'Body.' }, config);
117
+ await expect(pdf.render(doc, {
118
+ fontProvider: async () => { throw new Error('offline'); },
119
+ onWarning: () => undefined,
120
+ })).rejects.toThrow(/failed to load font\(s\): Lora 400/);
121
+ });
122
+ });
123
+ //# sourceMappingURL=pdfWorker.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdfWorker.test.js","sourceRoot":"","sources":["../../src/__tests__/pdfWorker.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=printMarks.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"printMarks.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/printMarks.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,170 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import fs from 'node:fs';
3
+ import { PDFArray, PDFDict, PDFDocument, PDFName, PDFRawStream, decodePDFRawStream } from 'pdf-lib';
4
+ import fontkit from '@pdf-lib/fontkit';
5
+ import { buildDocument, dimensionToPx } from 'postext';
6
+ import { renderToPdf } from '../pdf-backend';
7
+ import { parseFontString } from '../fontString';
8
+ const fontBytes = fs.readFileSync(new URL('../../../../apps/web/public/fonts/Lora-Regular.ttf', import.meta.url));
9
+ const fontProvider = async () => new Uint8Array(fontBytes);
10
+ const face = fontkit.create(fontBytes);
11
+ class StubCtx {
12
+ font = '';
13
+ measureText(s) {
14
+ const sizePx = parseFontString(this.font)?.sizePx ?? 16;
15
+ return { width: (face.layout(s).advanceWidth / face.unitsPerEm) * sizePx };
16
+ }
17
+ }
18
+ globalThis.OffscreenCanvas = class {
19
+ getContext() {
20
+ return new StubCtx();
21
+ }
22
+ };
23
+ const mm = (value) => ({ value, unit: 'mm' });
24
+ const MM = 72 / 25.4;
25
+ /** A 170 × 227 mm page (the EF-120 report), no running heads. */
26
+ function config(cutLines) {
27
+ return {
28
+ page: { sizePreset: 'custom', width: mm(170), height: mm(227), dpi: 150, ...(cutLines ? { cutLines } : {}) },
29
+ header: { elements: [] },
30
+ footer: { elements: [] },
31
+ };
32
+ }
33
+ async function render(cutLines, colorSpace) {
34
+ const doc = buildDocument({ markdown: 'A paragraph of text.' }, config(cutLines));
35
+ const bytes = await renderToPdf(doc, { fontProvider, ...(colorSpace ? { colorSpace } : {}) });
36
+ const pdf = await PDFDocument.load(bytes);
37
+ const page = pdf.getPage(0);
38
+ const contents = page.node.Contents();
39
+ const streams = contents instanceof PDFArray ? contents.asArray().map((r) => pdf.context.lookup(r)) : [contents];
40
+ const content = streams
41
+ .map((s) => (s instanceof PDFRawStream ? Buffer.from(decodePDFRawStream(s).decode()).toString('latin1') : ''))
42
+ .join('\n');
43
+ const box = (name) => {
44
+ const arr = page.node.lookupMaybe(PDFName.of(name), PDFArray);
45
+ return arr?.asArray().map((n) => n.asNumber());
46
+ };
47
+ return { pdf, page, content, box, doc };
48
+ }
49
+ /** Every `x1 y1 m x2 y2 l S` line of a content stream, in points. */
50
+ function strokedLines(content) {
51
+ const num = '(-?[\\d.]+)';
52
+ const re = new RegExp(`((?:[^\\n]*\\n){0,4}?)${num} ${num} m\\s+${num} ${num} l\\s+S`, 'g');
53
+ return [...content.matchAll(re)].map((m) => ({ ops: m[1], x1: +m[2], y1: +m[3], x2: +m[4], y2: +m[5] }));
54
+ }
55
+ const close = (a, b) => {
56
+ expect(a).toBeDefined();
57
+ expect(a.length).toBe(4);
58
+ a.forEach((v, i) => expect(v).toBeCloseTo(b[i], 3));
59
+ };
60
+ // EF-125. With cut lines on, the MediaBox is the whole sheet, marks
61
+ // included; a print workflow needs the trimmed page (TrimBox) and the art
62
+ // that runs past it (BleedBox) on every page.
63
+ describe('print boxes (EF-125)', () => {
64
+ it('sets TrimBox to the trimmed page and BleedBox to the trim plus the bleed', async () => {
65
+ const { pdf, box } = await render({ enabled: true, bleed: mm(5) });
66
+ // Sheet: 170 + 2 × (5 + 3 + 5) mm = 196 mm wide, 253 mm tall.
67
+ const [, , w, h] = box('MediaBox');
68
+ expect(w / MM).toBeCloseTo(196, 3);
69
+ expect(h / MM).toBeCloseTo(253, 3);
70
+ close(box('TrimBox'), [13 * MM, 13 * MM, 183 * MM, 240 * MM]);
71
+ close(box('BleedBox'), [8 * MM, 8 * MM, 188 * MM, 245 * MM]);
72
+ for (const page of pdf.getPages()) {
73
+ expect(page.getTrimBox().width / MM).toBeCloseTo(170, 3);
74
+ expect(page.getBleedBox().width / MM).toBeCloseTo(180, 3);
75
+ }
76
+ });
77
+ it('adds no boxes when cut lines are off (the page is the trim)', async () => {
78
+ const { box } = await render(undefined);
79
+ expect(box('TrimBox')).toBeUndefined();
80
+ expect(box('BleedBox')).toBeUndefined();
81
+ });
82
+ });
83
+ // EF-120. The marks started markOffset outside the trim even when the bleed
84
+ // was wider, so their inner ends lay over art running into the bleed.
85
+ describe('crop marks in the PDF (EF-120)', () => {
86
+ it('keeps every mark outside the BleedBox', async () => {
87
+ const { content } = await render({ enabled: true, bleed: mm(5) });
88
+ // The trim plus 5 mm of bleed, 8 mm inside the 196 × 253 mm sheet.
89
+ const [bx0, by0, bx1, by1] = [8 * MM, 8 * MM, 188 * MM, 245 * MM];
90
+ const lines = strokedLines(content);
91
+ expect(lines).toHaveLength(8);
92
+ const eps = 1e-3;
93
+ for (const l of lines) {
94
+ for (const [x, y] of [[l.x1, l.y1], [l.x2, l.y2]]) {
95
+ const inside = x > bx0 + eps && x < bx1 - eps && y > by0 + eps && y < by1 - eps;
96
+ expect(inside).toBe(false);
97
+ }
98
+ // 5 mm long, from the bleed edge outwards.
99
+ expect(Math.hypot(l.x2 - l.x1, l.y2 - l.y1) / MM).toBeCloseTo(5, 3);
100
+ }
101
+ });
102
+ it('draws the marks 3 to 8 mm outside the trim with the defaults, as before', async () => {
103
+ const { content, doc } = await render({ enabled: true });
104
+ const trim = (doc.trimOffset * 72) / 150 / MM;
105
+ expect(trim).toBeCloseTo(dimensionToPx(mm(11), 150) * 72 / 150 / MM, 6);
106
+ const lines = strokedLines(content);
107
+ expect(lines).toHaveLength(8);
108
+ // The top-left horizontal mark runs from 8 mm to 3 mm off the sheet's left edge.
109
+ const xs = lines.flatMap((l) => (Math.abs(l.y1 - l.y2) < 1e-6 ? [l.x1, l.x2] : [])).map((v) => v / MM).sort((a, b) => a - b);
110
+ expect(xs[0]).toBeCloseTo(trim - 8, 3);
111
+ expect(xs[1]).toBeCloseTo(trim - 8, 3);
112
+ expect(xs[2]).toBeCloseTo(trim - 3, 3);
113
+ });
114
+ it('sets the marks round the TrimBox it writes, both read from doc.trimOffset', async () => {
115
+ const built = buildDocument({ markdown: 'A paragraph of text.' }, config({ enabled: true }));
116
+ // A trim offset the cut-line settings alone would not give.
117
+ const doc = { ...built, trimOffset: built.trimOffset + dimensionToPx(mm(4), 150) };
118
+ const pdf = await PDFDocument.load(await renderToPdf(doc, { fontProvider }));
119
+ const page = pdf.getPage(0);
120
+ const contents = page.node.Contents();
121
+ const streams = contents instanceof PDFArray ? contents.asArray().map((r) => pdf.context.lookup(r)) : [contents];
122
+ const content = streams
123
+ .map((s) => (s instanceof PDFRawStream ? Buffer.from(decodePDFRawStream(s).decode()).toString('latin1') : ''))
124
+ .join('\n');
125
+ const trimLeft = page.getTrimBox().x / MM;
126
+ expect(trimLeft).toBeCloseTo(15, 3);
127
+ const xs = strokedLines(content)
128
+ .flatMap((l) => (Math.abs(l.y1 - l.y2) < 1e-6 ? [l.x1, l.x2] : []))
129
+ .map((v) => v / MM)
130
+ .sort((a, b) => a - b);
131
+ // The left marks run 3 to 8 mm outside the TrimBox.
132
+ expect(xs[0]).toBeCloseTo(trimLeft - 8, 3);
133
+ expect(xs[2]).toBeCloseTo(trimLeft - 3, 3);
134
+ });
135
+ });
136
+ // EF-125. A CMYK file printed the marks '0 0 0 1 K', on the black plate
137
+ // alone; crop marks belong on every plate, in registration colour.
138
+ describe('crop mark colour (EF-125)', () => {
139
+ it('paints the marks in the /All separation in CMYK output', async () => {
140
+ const { content, page, pdf } = await render({ enabled: true, bleed: mm(5) }, 'cmyk');
141
+ const lines = strokedLines(content);
142
+ expect(lines).toHaveLength(8);
143
+ for (const l of lines) {
144
+ expect(l.ops).toMatch(/\/(\S+) CS\s+1 SCN/);
145
+ expect(l.ops).not.toMatch(/\bK\b/);
146
+ }
147
+ const name = /\/(\S+) CS\s+1 SCN/.exec(lines[0].ops)[1];
148
+ const resources = page.node.Resources();
149
+ const spaces = resources.lookup(PDFName.of('ColorSpace'), PDFDict);
150
+ const space = spaces.lookup(PDFName.of(name), PDFArray);
151
+ const [family, colorant, alternate, fn] = space.asArray().map((o) => pdf.context.lookup(o));
152
+ expect(String(family)).toBe('/Separation');
153
+ expect(String(colorant)).toBe('/All');
154
+ expect(String(alternate)).toBe('/DeviceCMYK');
155
+ const c1 = fn.lookup(PDFName.of('C1'), PDFArray).asArray().map((n) => n.asNumber());
156
+ expect(c1).toEqual([1, 1, 1, 1]);
157
+ });
158
+ it('keeps the configured colour in RGB and grayscale output', async () => {
159
+ for (const [space, ink] of [['rgb', /0 0 0 RG/], ['grayscale', /0 G/]]) {
160
+ const { content } = await render({ enabled: true, color: { hex: '#000000', model: 'hex' } }, space);
161
+ const lines = strokedLines(content);
162
+ expect(lines).toHaveLength(8);
163
+ for (const l of lines) {
164
+ expect(l.ops).toMatch(ink);
165
+ expect(l.ops).not.toMatch(/SCN/);
166
+ }
167
+ }
168
+ });
169
+ });
170
+ //# sourceMappingURL=printMarks.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"printMarks.test.js","sourceRoot":"","sources":["../../src/__tests__/printMarks.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAa,YAAY,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAC/G,OAAO,OAAO,MAAM,kBAAkB,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAEvD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,SAAS,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,oDAAoD,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAClH,MAAM,YAAY,GAAG,KAAK,IAAI,EAAE,CAAC,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC;AAC3D,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AAEvC,MAAM,OAAO;IACX,IAAI,GAAG,EAAE,CAAC;IACV,WAAW,CAAC,CAAS;QACnB,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,IAAI,EAAE,CAAC;QACxD,OAAO,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,MAAM,EAAE,CAAC;IAC7E,CAAC;CACF;AACA,UAAsD,CAAC,eAAe,GAAG;IACxE,UAAU;QACR,OAAO,IAAI,OAAO,EAAE,CAAC;IACvB,CAAC;CACF,CAAC;AAEF,MAAM,EAAE,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAa,EAAE,CAAC,CAAC;AAC/D,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAIrB,iEAAiE;AACjE,SAAS,MAAM,CAAC,QAAmB;IACjC,OAAO;QACL,IAAI,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;QAC5G,MAAM,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;QACxB,MAAM,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;KACzB,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,QAA8B,EAAE,UAA0B;IAC9E,MAAM,GAAG,GAAG,aAAa,CAAC,EAAE,QAAQ,EAAE,sBAAsB,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;IAClF,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,EAAE,YAAY,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAC9F,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;IACtC,MAAM,OAAO,GAAG,QAAQ,YAAY,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACjH,MAAM,OAAO,GAAG,OAAO;SACpB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SAC7G,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,GAAG,GAAG,CAAC,IAAY,EAAwB,EAAE;QACjD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC9D,OAAO,GAAG,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAE,CAAe,CAAC,QAAQ,EAAE,CAAC,CAAC;IAChE,CAAC,CAAC;IACF,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAC1C,CAAC;AAED,qEAAqE;AACrE,SAAS,YAAY,CAAC,OAAe;IACnC,MAAM,GAAG,GAAG,aAAa,CAAC;IAC1B,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,yBAAyB,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,GAAG,SAAS,EAAE,GAAG,CAAC,CAAC;IAC5F,OAAO,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,CAAC;AAChH,CAAC;AAED,MAAM,KAAK,GAAG,CAAC,CAAuB,EAAE,CAAW,EAAE,EAAE;IACrD,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACxB,MAAM,CAAC,CAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC,CAAC;AAEF,oEAAoE;AACpE,0EAA0E;AAC1E,8CAA8C;AAC9C,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,0EAA0E,EAAE,KAAK,IAAI,EAAE;QACxF,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACnE,8DAA8D;QAC9D,MAAM,CAAC,EAAE,AAAD,EAAG,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,UAAU,CAAE,CAAC;QACpC,MAAM,CAAC,CAAE,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,CAAE,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACpC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC;QAC9D,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC;QAC7D,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACzD,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;QACxC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACvC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,4EAA4E;AAC5E,sEAAsE;AACtE,QAAQ,CAAC,gCAAgC,EAAE,GAAG,EAAE;IAC9C,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAClE,mEAAmE;QACnE,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC;QAClE,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC;QACjB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAU,EAAE,CAAC;gBAC3D,MAAM,MAAM,GAAG,CAAC,GAAG,GAAI,GAAG,GAAG,IAAI,CAAC,GAAG,GAAI,GAAG,GAAG,IAAI,CAAC,GAAG,GAAI,GAAG,GAAG,IAAI,CAAC,GAAG,GAAI,GAAG,GAAG,CAAC;gBACpF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC7B,CAAC;YACD,2CAA2C;YAC3C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACtE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;QACvF,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,UAAU,GAAG,EAAE,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC;QAC9C,MAAM,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,aAAa,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;QACxE,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,iFAAiF;QACjF,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7H,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACvC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACvC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,KAAK,IAAI,EAAE;QACzF,MAAM,KAAK,GAAG,aAAa,CAAC,EAAE,QAAQ,EAAE,sBAAsB,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC7F,4DAA4D;QAC5D,MAAM,GAAG,GAAG,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;QACnF,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,MAAM,WAAW,CAAC,GAAG,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;QAC7E,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,QAAQ,YAAY,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACjH,MAAM,OAAO,GAAG,OAAO;aACpB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;aAC7G,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;QAC1C,MAAM,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACpC,MAAM,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC;aAC7B,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;aAClE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;aAClB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACzB,oDAAoD;QACpD,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,wEAAwE;AACxE,mEAAmE;AACnE,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACrF,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;YAC5C,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC;QACD,MAAM,IAAI,GAAG,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,GAAG,CAAE,CAAC,CAAC,CAAE,CAAC;QAC3D,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAG,CAAC;QACzC,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;QACnE,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC;QACxD,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5F,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC9C,MAAM,EAAE,GAAI,EAAc,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAE,CAAe,CAAC,QAAQ,EAAE,CAAC,CAAC;QAChH,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;QACvE,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,CAAU,EAAE,CAAC;YAChF,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;YACpG,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;YACpC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC9B,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;gBACtB,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAC3B,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=printMaster.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"printMaster.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/printMaster.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,161 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { PDFArray, PDFDocument, PDFRawStream, decodePDFRawStream } from 'pdf-lib';
3
+ import fs from 'node:fs';
4
+ import fontkit from '@pdf-lib/fontkit';
5
+ import { buildDocument } from 'postext';
6
+ import { renderToPdf } from '../pdf-backend';
7
+ import { preloadResourceImages } from '../pdf-backend/renderResourceBlock';
8
+ import { parseFontString } from '../fontString';
9
+ // EF-06: a figure's vector print master (`svg.pdfFileId`) is what
10
+ // `renderToPdf` embeds — the host hands over bytes by id, it does not have
11
+ // to swap the master in for the SVG's own id (only the bundle adapter did).
12
+ const fontBytes = fs.readFileSync(new URL('../../../../apps/web/public/fonts/Lora-Regular.ttf', import.meta.url));
13
+ const face = fontkit.create(fontBytes);
14
+ class StubCtx {
15
+ font = '';
16
+ measureText(s) {
17
+ const sizePx = parseFontString(this.font)?.sizePx ?? 16;
18
+ return { width: (face.layout(s).advanceWidth / face.unitsPerEm) * sizePx };
19
+ }
20
+ }
21
+ globalThis.OffscreenCanvas = class {
22
+ getContext() {
23
+ return new StubCtx();
24
+ }
25
+ };
26
+ const SVG = new TextEncoder().encode('<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100" viewBox="0 0 200 100"><rect width="200" height="100" fill="#000000"/></svg>');
27
+ async function masterPdf() {
28
+ const master = await PDFDocument.create();
29
+ master.addPage([200, 100]).drawRectangle({ x: 0, y: 0, width: 200, height: 100 });
30
+ return master.save();
31
+ }
32
+ function vdt(resource, singleInk = false) {
33
+ return {
34
+ config: { page: { dpi: 72 }, diagramStyle: { singleInk, inkColor: { hex: '#295aa3' } } },
35
+ blocks: [{
36
+ bbox: { x: 0, y: 0, width: 100, height: 80 },
37
+ resourceBlock: {
38
+ kind: 'svg',
39
+ fileId: 'fig.svg',
40
+ resource: { id: 'fig', kind: 'svg', ...resource },
41
+ bodyRect: { x: 0, y: 0, width: 100, height: 50 },
42
+ },
43
+ }],
44
+ pages: [],
45
+ };
46
+ }
47
+ describe('preloadResourceImages — print masters (EF-06)', () => {
48
+ it('asks for the master by its own id and embeds it in place of the SVG', async () => {
49
+ const master = await masterPdf();
50
+ const asked = [];
51
+ const bytes = (id) => {
52
+ asked.push(id);
53
+ return id === 'fig.pdf' ? master : id === 'fig.svg' ? SVG : undefined;
54
+ };
55
+ const pdfDoc = await PDFDocument.create();
56
+ const images = await preloadResourceImages(pdfDoc, vdt({ svg: { fileId: 'fig.svg', pdfFileId: 'fig.pdf' } }), bytes);
57
+ expect(asked[0]).toBe('fig.pdf');
58
+ expect(images.get('fig.svg')?.kind).toBe('page');
59
+ });
60
+ it('falls back to the SVG when the master is missing or does not decode', async () => {
61
+ const pdfDoc = await PDFDocument.create();
62
+ const missing = await preloadResourceImages(pdfDoc, vdt({ svg: { fileId: 'fig.svg', pdfFileId: 'fig.pdf' } }), (id) => (id === 'fig.svg' ? SVG : undefined));
63
+ expect(missing.get('fig.svg')?.kind).toBe('vector');
64
+ const broken = await preloadResourceImages(pdfDoc, vdt({ svg: { fileId: 'fig.svg', pdfFileId: 'fig.pdf' } }), (id) => (id === 'fig.pdf' ? new TextEncoder().encode('%PDF-1.7 truncated') : id === 'fig.svg' ? SVG : undefined));
65
+ expect(broken.get('fig.svg')?.kind).toBe('vector');
66
+ });
67
+ it('ignores the master in single-ink mode, which recolours SVG markup only', async () => {
68
+ const master = await masterPdf();
69
+ const asked = [];
70
+ const pdfDoc = await PDFDocument.create();
71
+ const images = await preloadResourceImages(pdfDoc, vdt({ svg: { fileId: 'fig.svg', pdfFileId: 'fig.pdf' } }, true), (id) => {
72
+ asked.push(id);
73
+ return id === 'fig.pdf' ? master : SVG;
74
+ });
75
+ expect(asked).not.toContain('fig.pdf');
76
+ expect(images.get('fig.svg')?.kind).toBe('vector');
77
+ });
78
+ it('still takes a master a host swapped in under the SVG id (bundleResourceBytes)', async () => {
79
+ const master = await masterPdf();
80
+ const pdfDoc = await PDFDocument.create();
81
+ const images = await preloadResourceImages(pdfDoc, vdt({ svg: { fileId: 'fig.svg', pdfFileId: 'fig.pdf' } }), (id) => (id === 'fig.svg' ? master : undefined));
82
+ expect(images.get('fig.svg')?.kind).toBe('page');
83
+ });
84
+ });
85
+ describe('renderToPdf — print masters (EF-06)', () => {
86
+ const pt = (value) => ({ value, unit: 'pt' });
87
+ const config = {
88
+ page: { width: pt(300), height: pt(300), margins: { top: pt(20), bottom: pt(20), left: pt(20), right: pt(20) } },
89
+ headings: { levels: [{ level: 1, breakBefore: { enabled: false } }] },
90
+ };
91
+ const resources = [{
92
+ id: 'fig',
93
+ typeId: 'figure',
94
+ kind: 'svg',
95
+ createdAt: 0,
96
+ updatedAt: 0,
97
+ svg: { fileId: 'fig.svg', width: 200, height: 100, pdfFileId: 'fig.pdf' },
98
+ placement: { position: 'here' },
99
+ }];
100
+ it('embeds the master page as a form XObject from plain resourceBytes', async () => {
101
+ const master = await masterPdf();
102
+ const doc = buildDocument({ markdown: 'Text.\n\n::resource{id="fig"}', resources }, config);
103
+ const bytes = await renderToPdf(doc, {
104
+ fontProvider: async () => new Uint8Array(fontBytes),
105
+ resourceBytes: (id) => (id === 'fig.pdf' ? master : id === 'fig.svg' ? SVG : undefined),
106
+ });
107
+ const pdf = await PDFDocument.load(bytes);
108
+ const contents = pdf.getPage(0).node.Contents();
109
+ const refs = contents instanceof PDFArray ? contents.asArray() : [contents];
110
+ const content = refs
111
+ .map((ref) => {
112
+ const s = pdf.context.lookup(ref);
113
+ return s instanceof PDFRawStream ? new TextDecoder('latin1').decode(decodePDFRawStream(s).decode()) : '';
114
+ })
115
+ .join('\n');
116
+ expect(content).toMatch(/\/EmbeddedPdfPage[^\s]* Do/);
117
+ });
118
+ it('embeds the master wherever the SVG is drawn: a table cell, a design image, a box icon', async () => {
119
+ const master = await masterPdf();
120
+ const table = {
121
+ id: 'tab', typeId: 'table', kind: 'table', createdAt: 0, updatedAt: 0,
122
+ table: { model: { rows: [[{ content: 'Cell', image: { resourceId: 'fig' } }]] } },
123
+ placement: { position: 'here' },
124
+ };
125
+ const uses = [
126
+ ['cell', config, 'Text.\n\n::resource{id="tab"}'],
127
+ ['design image', {
128
+ ...config,
129
+ header: { elements: [{ kind: 'image', id: 'logo', resourceId: 'fig', placement: { anchor: { to: 'container', edge: 'top-left' }, size: { width: pt(40) } } }] },
130
+ }, 'Text.'],
131
+ ['box icon', { ...config, calloutStyles: [{ id: 'note', icon: { kind: 'resource', resourceId: 'fig' } }] }, ':::callout{type="note"}\nBoxed.\n:::'],
132
+ ];
133
+ for (const [use, cfg, markdown] of uses) {
134
+ const doc = buildDocument({ markdown, resources: [...resources, table] }, cfg);
135
+ const asked = [];
136
+ const pdfDoc = await PDFDocument.create();
137
+ const images = await preloadResourceImages(pdfDoc, doc, (id) => {
138
+ asked.push(id);
139
+ return id === 'fig.pdf' ? master : id === 'fig.svg' ? SVG : undefined;
140
+ });
141
+ expect(asked[0], use).toBe('fig.pdf');
142
+ expect(images.get('fig.svg')?.kind, use).toBe('page');
143
+ }
144
+ });
145
+ it('gives every chapter of a book the master, whichever chapter draws the SVG first', async () => {
146
+ const master = await masterPdf();
147
+ const table = {
148
+ id: 'tab', typeId: 'table', kind: 'table', createdAt: 0, updatedAt: 0,
149
+ table: { model: { rows: [[{ content: 'Cell', image: { resourceId: 'fig' } }]] } },
150
+ placement: { position: 'here' },
151
+ };
152
+ const chapter1 = buildDocument({ markdown: 'One.\n\n::resource{id="tab"}', resources: [...resources, table] }, config);
153
+ const chapter2 = buildDocument({ markdown: 'Two.\n\n::resource{id="fig"}', resources: [...resources, table] }, config);
154
+ const pdfDoc = await PDFDocument.create();
155
+ const bytes = (id) => (id === 'fig.pdf' ? master : id === 'fig.svg' ? SVG : undefined);
156
+ const shared = await preloadResourceImages(pdfDoc, chapter1, bytes);
157
+ await preloadResourceImages(pdfDoc, chapter2, bytes, undefined, undefined, shared);
158
+ expect(shared.get('fig.svg')?.kind).toBe('page');
159
+ });
160
+ });
161
+ //# sourceMappingURL=printMaster.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"printMaster.test.js","sourceRoot":"","sources":["../../src/__tests__/printMaster.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAClF,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,OAAO,MAAM,kBAAkB,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,kEAAkE;AAClE,2EAA2E;AAC3E,4EAA4E;AAE5E,MAAM,SAAS,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,oDAAoD,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAClH,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AAEvC,MAAM,OAAO;IACX,IAAI,GAAG,EAAE,CAAC;IACV,WAAW,CAAC,CAAS;QACnB,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,IAAI,EAAE,CAAC;QACxD,OAAO,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,MAAM,EAAE,CAAC;IAC7E,CAAC;CACF;AACA,UAAsD,CAAC,eAAe,GAAG;IACxE,UAAU;QACR,OAAO,IAAI,OAAO,EAAE,CAAC;IACvB,CAAC;CACF,CAAC;AAEF,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAClC,8IAA8I,CAC/I,CAAC;AAEF,KAAK,UAAU,SAAS;IACtB,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,CAAC;IAC1C,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAClF,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AACvB,CAAC;AAED,SAAS,GAAG,CAAC,QAA2B,EAAE,SAAS,GAAG,KAAK;IACzD,OAAO;QACL,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE;QACxF,MAAM,EAAE,CAAC;gBACP,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE;gBAC5C,aAAa,EAAE;oBACb,IAAI,EAAE,KAAK;oBACX,MAAM,EAAE,SAAS;oBACjB,QAAQ,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE;oBACjD,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE;iBACjD;aACF,CAAC;QACF,KAAK,EAAE,EAAE;KACgB,CAAC;AAC9B,CAAC;AAED,QAAQ,CAAC,+CAA+C,EAAE,GAAG,EAAE;IAC7D,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;QACnF,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,EAAE;YAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACf,OAAO,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;QACxE,CAAC,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;QACrH,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACjC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;QACnF,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAG,MAAM,qBAAqB,CACzC,MAAM,EACN,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,CAAC,EACzD,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAC7C,CAAC;QACF,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,MAAM,qBAAqB,CACxC,MAAM,EACN,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,CAAC,EACzD,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CACjH,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;QACtF,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,qBAAqB,CACxC,MAAM,EACN,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,CAAC,EAC/D,CAAC,EAAE,EAAE,EAAE;YACL,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACf,OAAO,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC;QACzC,CAAC,CACF,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+EAA+E,EAAE,KAAK,IAAI,EAAE;QAC7F,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,qBAAqB,CACxC,MAAM,EACN,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,CAAC,EACzD,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAChD,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,qCAAqC,EAAE,GAAG,EAAE;IACnD,MAAM,EAAE,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAa,EAAE,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAkB;QAC5B,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;QAChH,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE;KACtE,CAAC;IACF,MAAM,SAAS,GAAe,CAAC;YAC7B,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,QAAQ;YAChB,IAAI,EAAE,KAAK;YACX,SAAS,EAAE,CAAC;YACZ,SAAS,EAAE,CAAC;YACZ,GAAG,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE;YACzE,SAAS,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE;SAChC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,aAAa,CAAC,EAAE,QAAQ,EAAE,+BAA+B,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;QAC5F,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE;YACnC,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,UAAU,CAAC,SAAS,CAAC;YACnD,aAAa,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;SACxF,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChD,MAAM,IAAI,GAAG,QAAQ,YAAY,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC5E,MAAM,OAAO,GAAG,IAAI;aACjB,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YACX,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAClC,OAAO,CAAC,YAAY,YAAY,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3G,CAAC,CAAC;aACD,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uFAAuF,EAAE,KAAK,IAAI,EAAE;QACrG,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,KAAK,GAAa;YACtB,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC;YACrE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE;YACjF,SAAS,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE;SAChC,CAAC;QACF,MAAM,IAAI,GAA2C;YACnD,CAAC,MAAM,EAAE,MAAM,EAAE,+BAA+B,CAAC;YACjD,CAAC,cAAc,EAAE;oBACf,GAAG,MAAM;oBACT,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE;iBAChK,EAAE,OAAO,CAAC;YACX,CAAC,UAAU,EAAE,EAAE,GAAG,MAAM,EAAE,aAAa,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE,sCAAsC,CAAC;SACpJ,CAAC;QACF,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC;YACxC,MAAM,GAAG,GAAG,aAAa,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;YAC/E,MAAM,KAAK,GAAa,EAAE,CAAC;YAC3B,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,CAAC;YAC1C,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE;gBAC7D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACf,OAAO,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;YACxE,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACtC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iFAAiF,EAAE,KAAK,IAAI,EAAE;QAC/F,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,KAAK,GAAa;YACtB,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC;YACrE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE;YACjF,SAAS,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE;SAChC,CAAC;QACF,MAAM,QAAQ,GAAG,aAAa,CAAC,EAAE,QAAQ,EAAE,8BAA8B,EAAE,SAAS,EAAE,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACvH,MAAM,QAAQ,GAAG,aAAa,CAAC,EAAE,QAAQ,EAAE,8BAA8B,EAAE,SAAS,EAAE,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACvH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC/F,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;QACpE,MAAM,qBAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QACnF,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=renderWarnings.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderWarnings.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/renderWarnings.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,89 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import fs from 'node:fs';
3
+ import { PDFArray, PDFDocument, PDFRawStream, decodePDFRawStream } from 'pdf-lib';
4
+ import fontkit from '@pdf-lib/fontkit';
5
+ import { buildDocument } from 'postext';
6
+ import { renderToPdf } from '../pdf-backend';
7
+ import { parseFontString } from '../fontString';
8
+ const fontBytes = fs.readFileSync(new URL('../../../../apps/web/public/fonts/Lora-Regular.ttf', import.meta.url));
9
+ const fontProvider = async () => new Uint8Array(fontBytes);
10
+ const face = fontkit.create(fontBytes);
11
+ class StubCtx {
12
+ font = '';
13
+ measureText(s) {
14
+ const sizePx = parseFontString(this.font)?.sizePx ?? 16;
15
+ const run = face.layout(s);
16
+ return { width: (run.advanceWidth / face.unitsPerEm) * sizePx };
17
+ }
18
+ }
19
+ globalThis.OffscreenCanvas = class {
20
+ getContext() {
21
+ return new StubCtx();
22
+ }
23
+ };
24
+ const pt = (value) => ({ value, unit: 'pt' });
25
+ const hex = (h) => ({ hex: h, model: 'hex' });
26
+ const cell = (content, extra = {}) => ({ content, ...extra });
27
+ const PAGE = {
28
+ page: { width: pt(400), height: pt(500), margins: { top: pt(20), bottom: pt(20), left: pt(20), right: pt(20) } },
29
+ headings: { balancing: { enabled: false }, levels: [{ level: 1, breakBefore: { enabled: false } }] },
30
+ };
31
+ const table = {
32
+ id: 'parts',
33
+ typeId: 'table',
34
+ kind: 'table',
35
+ caption: 'Parts.',
36
+ createdAt: 0,
37
+ updatedAt: 0,
38
+ table: {
39
+ model: {
40
+ headerRowCount: 1,
41
+ rows: [
42
+ [cell('Part', { isHeader: true }), cell('Qty', { isHeader: true })],
43
+ ...Array.from({ length: 4 }, (_, i) => [cell(`Part ${i + 1}`), cell(String(i + 2))]),
44
+ ],
45
+ },
46
+ },
47
+ placement: { position: 'here' },
48
+ };
49
+ const photo = {
50
+ id: 'photo',
51
+ typeId: 'figure',
52
+ kind: 'bitmap',
53
+ caption: 'A photo.',
54
+ createdAt: 0,
55
+ updatedAt: 0,
56
+ bitmap: { fileId: 'file-photo', format: 'png', width: 400, height: 300 },
57
+ placement: { position: 'here' },
58
+ };
59
+ async function pageText(bytes, index = 0) {
60
+ const pdf = await PDFDocument.load(bytes);
61
+ const contents = pdf.getPage(index).node.Contents();
62
+ const streams = contents instanceof PDFArray ? contents.asArray().map((r) => pdf.context.lookup(r)) : [contents];
63
+ return streams
64
+ .map((s) => (s instanceof PDFRawStream ? Buffer.from(decodePDFRawStream(s).decode()).toString('latin1') : ''))
65
+ .join('\n');
66
+ }
67
+ /** Non-stroking RGB fills set on a page (`r g b rg`). */
68
+ const fills = (text) => [...text.matchAll(/([\d.]+) ([\d.]+) ([\d.]+) rg/g)].map((m) => m.slice(1, 4).map((v) => Number(v).toFixed(3)).join(' '));
69
+ describe('zebra rows in the PDF', () => {
70
+ it('fills the alternate body rows, and only when enabled', async () => {
71
+ const md = '::resource{id="parts"}\n';
72
+ const zebra = { ...PAGE, tableStyle: { bodyAlternateBackgroundEnabled: true, bodyAlternateBackground: hex('#eef3fa') } };
73
+ const stripe = [0xee, 0xf3, 0xfa].map((v) => (v / 255).toFixed(3)).join(' ');
74
+ const on = fills(await pageText(await renderToPdf(buildDocument({ markdown: md, resources: [table] }, zebra), { fontProvider })));
75
+ const off = fills(await pageText(await renderToPdf(buildDocument({ markdown: md, resources: [table] }, PAGE), { fontProvider })));
76
+ // Body rows 2 and 4 × two columns.
77
+ expect(on.filter((f) => f === stripe)).toHaveLength(4);
78
+ expect(off).not.toContain(stripe);
79
+ }, 60_000);
80
+ });
81
+ describe('render warnings in the PDF', () => {
82
+ it('reports an image with no bytes once, with its document and page', async () => {
83
+ const doc = buildDocument({ markdown: 'Text.\n\n::resource{id="photo"}\n\nMore :ref{id="photo"}.\n', resources: [photo] }, PAGE);
84
+ const warnings = [];
85
+ await renderToPdf([doc, doc], { fontProvider, onWarning: (w) => warnings.push(w) });
86
+ expect(warnings).toEqual([{ kind: 'missingImage', fileId: 'file-photo', resourceId: 'photo', pageIndex: 0, documentIndex: 0 }]);
87
+ }, 60_000);
88
+ });
89
+ //# sourceMappingURL=renderWarnings.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderWarnings.test.js","sourceRoot":"","sources":["../../src/__tests__/renderWarnings.test.ts"],"names":[],"mappings":"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"}