postext-pdf 0.1.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 (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +67 -0
  3. package/dist/__bench__/render.bench.test.d.ts +2 -0
  4. package/dist/__bench__/render.bench.test.d.ts.map +1 -0
  5. package/dist/__bench__/render.bench.test.js +86 -0
  6. package/dist/__bench__/render.bench.test.js.map +1 -0
  7. package/dist/__tests__/accessibility.test.d.ts +2 -0
  8. package/dist/__tests__/accessibility.test.d.ts.map +1 -0
  9. package/dist/__tests__/accessibility.test.js +364 -0
  10. package/dist/__tests__/accessibility.test.js.map +1 -0
  11. package/dist/__tests__/contentsLinks.test.d.ts +2 -0
  12. package/dist/__tests__/contentsLinks.test.d.ts.map +1 -0
  13. package/dist/__tests__/contentsLinks.test.js +131 -0
  14. package/dist/__tests__/contentsLinks.test.js.map +1 -0
  15. package/dist/__tests__/fontCache.test.d.ts +2 -0
  16. package/dist/__tests__/fontCache.test.d.ts.map +1 -0
  17. package/dist/__tests__/fontCache.test.js +42 -0
  18. package/dist/__tests__/fontCache.test.js.map +1 -0
  19. package/dist/__tests__/headerFooter.test.d.ts +2 -0
  20. package/dist/__tests__/headerFooter.test.d.ts.map +1 -0
  21. package/dist/__tests__/headerFooter.test.js +68 -0
  22. package/dist/__tests__/headerFooter.test.js.map +1 -0
  23. package/dist/__tests__/rotatedTable.test.d.ts +2 -0
  24. package/dist/__tests__/rotatedTable.test.d.ts.map +1 -0
  25. package/dist/__tests__/rotatedTable.test.js +112 -0
  26. package/dist/__tests__/rotatedTable.test.js.map +1 -0
  27. package/dist/__tests__/svgVector.test.d.ts +2 -0
  28. package/dist/__tests__/svgVector.test.d.ts.map +1 -0
  29. package/dist/__tests__/svgVector.test.js +322 -0
  30. package/dist/__tests__/svgVector.test.js.map +1 -0
  31. package/dist/__tests__/toUnicode.test.d.ts +2 -0
  32. package/dist/__tests__/toUnicode.test.d.ts.map +1 -0
  33. package/dist/__tests__/toUnicode.test.js +57 -0
  34. package/dist/__tests__/toUnicode.test.js.map +1 -0
  35. package/dist/colors.d.ts +23 -0
  36. package/dist/colors.d.ts.map +1 -0
  37. package/dist/colors.js +47 -0
  38. package/dist/colors.js.map +1 -0
  39. package/dist/fontCache.d.ts +41 -0
  40. package/dist/fontCache.d.ts.map +1 -0
  41. package/dist/fontCache.js +154 -0
  42. package/dist/fontCache.js.map +1 -0
  43. package/dist/fontString.d.ts +14 -0
  44. package/dist/fontString.d.ts.map +1 -0
  45. package/dist/fontString.js +43 -0
  46. package/dist/fontString.js.map +1 -0
  47. package/dist/index.d.ts +6 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +5 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/pdf-backend/blockRender.d.ts +15 -0
  52. package/dist/pdf-backend/blockRender.d.ts.map +1 -0
  53. package/dist/pdf-backend/blockRender.js +244 -0
  54. package/dist/pdf-backend/blockRender.js.map +1 -0
  55. package/dist/pdf-backend/fontHelpers.d.ts +6 -0
  56. package/dist/pdf-backend/fontHelpers.d.ts.map +1 -0
  57. package/dist/pdf-backend/fontHelpers.js +81 -0
  58. package/dist/pdf-backend/fontHelpers.js.map +1 -0
  59. package/dist/pdf-backend/headerFooter.d.ts +15 -0
  60. package/dist/pdf-backend/headerFooter.d.ts.map +1 -0
  61. package/dist/pdf-backend/headerFooter.js +145 -0
  62. package/dist/pdf-backend/headerFooter.js.map +1 -0
  63. package/dist/pdf-backend/index.d.ts +47 -0
  64. package/dist/pdf-backend/index.d.ts.map +1 -0
  65. package/dist/pdf-backend/index.js +227 -0
  66. package/dist/pdf-backend/index.js.map +1 -0
  67. package/dist/pdf-backend/links.d.ts +44 -0
  68. package/dist/pdf-backend/links.d.ts.map +1 -0
  69. package/dist/pdf-backend/links.js +110 -0
  70. package/dist/pdf-backend/links.js.map +1 -0
  71. package/dist/pdf-backend/outlines.d.ts +8 -0
  72. package/dist/pdf-backend/outlines.d.ts.map +1 -0
  73. package/dist/pdf-backend/outlines.js +145 -0
  74. package/dist/pdf-backend/outlines.js.map +1 -0
  75. package/dist/pdf-backend/pageDecorations.d.ts +13 -0
  76. package/dist/pdf-backend/pageDecorations.d.ts.map +1 -0
  77. package/dist/pdf-backend/pageDecorations.js +88 -0
  78. package/dist/pdf-backend/pageDecorations.js.map +1 -0
  79. package/dist/pdf-backend/pageLabels.d.ts +8 -0
  80. package/dist/pdf-backend/pageLabels.d.ts.map +1 -0
  81. package/dist/pdf-backend/pageLabels.js +61 -0
  82. package/dist/pdf-backend/pageLabels.js.map +1 -0
  83. package/dist/pdf-backend/primitives.d.ts +46 -0
  84. package/dist/pdf-backend/primitives.d.ts.map +1 -0
  85. package/dist/pdf-backend/primitives.js +162 -0
  86. package/dist/pdf-backend/primitives.js.map +1 -0
  87. package/dist/pdf-backend/renderResourceBlock.d.ts +87 -0
  88. package/dist/pdf-backend/renderResourceBlock.d.ts.map +1 -0
  89. package/dist/pdf-backend/renderResourceBlock.js +753 -0
  90. package/dist/pdf-backend/renderResourceBlock.js.map +1 -0
  91. package/dist/pdf-backend/structureFlow.d.ts +55 -0
  92. package/dist/pdf-backend/structureFlow.d.ts.map +1 -0
  93. package/dist/pdf-backend/structureFlow.js +180 -0
  94. package/dist/pdf-backend/structureFlow.js.map +1 -0
  95. package/dist/pdf-backend/svgVector.d.ts +144 -0
  96. package/dist/pdf-backend/svgVector.d.ts.map +1 -0
  97. package/dist/pdf-backend/svgVector.js +1455 -0
  98. package/dist/pdf-backend/svgVector.js.map +1 -0
  99. package/dist/pdf-backend/tagging.d.ts +136 -0
  100. package/dist/pdf-backend/tagging.d.ts.map +1 -0
  101. package/dist/pdf-backend/tagging.js +296 -0
  102. package/dist/pdf-backend/tagging.js.map +1 -0
  103. package/dist/woff2.d.ts +11 -0
  104. package/dist/woff2.d.ts.map +1 -0
  105. package/dist/woff2.js +47 -0
  106. package/dist/woff2.js.map +1 -0
  107. package/dist/worker/client.d.ts +30 -0
  108. package/dist/worker/client.d.ts.map +1 -0
  109. package/dist/worker/client.js +112 -0
  110. package/dist/worker/client.js.map +1 -0
  111. package/dist/worker/pdf.worker.d.ts +2 -0
  112. package/dist/worker/pdf.worker.d.ts.map +1 -0
  113. package/dist/worker/pdf.worker.js +67 -0
  114. package/dist/worker/pdf.worker.js.map +1 -0
  115. package/dist/worker/protocol.d.ts +49 -0
  116. package/dist/worker/protocol.d.ts.map +1 -0
  117. package/dist/worker/protocol.js +2 -0
  118. package/dist/worker/protocol.js.map +1 -0
  119. package/package.json +67 -0
@@ -0,0 +1,131 @@
1
+ import { describe, it, expect, beforeAll } from 'vitest';
2
+ import fs from 'node:fs';
3
+ import { PDFArray, PDFDict, PDFDocument, PDFHexString, PDFName, PDFNumber, PDFRef, PDFString } from 'pdf-lib';
4
+ import fontkit from '@pdf-lib/fontkit';
5
+ import { buildDocument, computeOutlineFor, parseMarkdown } 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 filler = (n) => Array.from({ length: n }, (_, i) => `Paragraph ${i} with enough words to consume vertical space and force the page to overflow.`).join('\n\n');
26
+ const config = {
27
+ page: {
28
+ width: pt(360), height: pt(300),
29
+ margins: { top: pt(18), bottom: pt(18), left: pt(18), right: pt(18) },
30
+ pageNumbering: { format: 'lower-roman', startAt: 1 },
31
+ },
32
+ layout: { layoutType: 'single' },
33
+ locale: 'en-us',
34
+ headings: { levels: [{ level: 1, breakBefore: { enabled: true, parity: 'odd' } }] },
35
+ headingStyles: [{ id: 'front', numbered: false }],
36
+ toc: { subtitle: { enabled: true, attr: 'author' }, parts: { enabled: true } },
37
+ };
38
+ const book = `# Contents {style="front" toc="false"}
39
+
40
+ :::toc
41
+
42
+ # Preface {style="front"}
43
+
44
+ ${filler(2)}
45
+
46
+ :::numbering{format="decimal" startAt=1}
47
+
48
+ :::part{number="I" title="Foundations"}
49
+ :::
50
+
51
+ # The lantern {author="A. Author"}
52
+
53
+ ${filler(3)}
54
+
55
+ # Trimming {author="B. Author"}
56
+
57
+ ${filler(1)}`;
58
+ const textOf = (v) => (v instanceof PDFHexString || v instanceof PDFString ? v.decodeText() : undefined);
59
+ let pdf;
60
+ let doc;
61
+ beforeAll(async () => {
62
+ doc = buildDocument({ markdown: book, metadata: { title: 'Linked book' } }, config);
63
+ const bytes = await renderToPdf(doc, { fontProvider });
64
+ const out = process.env.POSTEXT_TOC_PDF_OUT;
65
+ if (out)
66
+ fs.writeFileSync(out, bytes);
67
+ pdf = await PDFDocument.load(bytes);
68
+ }, 60_000);
69
+ describe('contents rows as links', () => {
70
+ it('links every row to the top of the page it lists', () => {
71
+ const rows = doc.blocks.filter((b) => (b.tocEntry ?? b.tocPart) && b.pageIndex === 0);
72
+ expect(rows.length).toBe(4); // Preface, part I, The lantern, Trimming
73
+ const annots = pdf.getPage(0).node.Annots().asArray().map((r) => pdf.context.lookup(r, PDFDict));
74
+ expect(annots).toHaveLength(4);
75
+ const pages = pdf.getPages();
76
+ // Rows by the text the annotation describes (a part row is drawn after
77
+ // the column's other blocks, so annotation order is not row order).
78
+ const rowText = (b) => (b.tocPart ? `${b.tocPart.number} ${b.tocPart.title}` : b.lines.map((l) => l.text).join(' '));
79
+ const seen = new Set();
80
+ for (const annot of annots) {
81
+ expect(annot.get(PDFName.of('Subtype'))).toEqual(PDFName.of('Link'));
82
+ const contents = textOf(annot.get(PDFName.of('Contents')));
83
+ const row = rows.find((b) => rowText(b) === contents);
84
+ expect(row).toBeDefined();
85
+ seen.add(contents);
86
+ const target = pages[row.tocEntry?.pageIndex ?? row.tocPart.pageIndex];
87
+ const dest = annot.lookup(PDFName.of('Dest'), PDFArray).asArray();
88
+ expect(dest[0]).toEqual(target.ref);
89
+ expect(dest[1]).toEqual(PDFName.of('XYZ'));
90
+ expect(dest[3].asNumber()).toBeCloseTo(target.getHeight(), 3);
91
+ // Tagged: the annotation joins a `Link` element and describes its target.
92
+ expect(annot.get(PDFName.of('StructParent'))).toBeInstanceOf(PDFNumber);
93
+ }
94
+ expect(seen.has('I Foundations')).toBe(true);
95
+ expect([...seen].some((t) => t.startsWith('The lantern'))).toBe(true);
96
+ });
97
+ it('wraps each row\'s text in a Link structure element with its annotation', () => {
98
+ const root = pdf.catalog.lookup(PDFName.of('StructTreeRoot'), PDFDict);
99
+ const links = [];
100
+ const visit = (ref) => {
101
+ const dict = ref instanceof PDFRef ? pdf.context.lookup(ref, PDFDict) : ref;
102
+ if (dict.get(PDFName.of('S')) === PDFName.of('Link'))
103
+ links.push(dict);
104
+ const k = dict.get(PDFName.of('K'));
105
+ const kids = k instanceof PDFArray ? k.asArray() : k ? [k] : [];
106
+ for (const kid of kids) {
107
+ if (kid instanceof PDFRef)
108
+ visit(kid);
109
+ else if (kid instanceof PDFDict && kid.get(PDFName.of('S')))
110
+ visit(kid);
111
+ }
112
+ };
113
+ visit(root);
114
+ expect(links).toHaveLength(4);
115
+ for (const link of links) {
116
+ const k = link.lookup(PDFName.of('K'), PDFArray).asArray().map((x) => (x instanceof PDFRef ? pdf.context.lookup(x) : x));
117
+ const types = k.map((x) => (x instanceof PDFDict ? textOf(x.get(PDFName.of('Type'))) ?? x.get(PDFName.of('Type'))?.decodeText() : 'mcid'));
118
+ expect(types.filter((t) => t === 'OBJR')).toHaveLength(1);
119
+ expect(types.filter((t) => t === 'MCR' || t === 'mcid').length).toBeGreaterThan(0);
120
+ }
121
+ });
122
+ it('skips rows whose page lies outside a chapter rendered on its own', async () => {
123
+ // The chapter with the contents, laid out after ten pages of another
124
+ // chapter, listing a host outline that points into the book.
125
+ const outline = computeOutlineFor(parseMarkdown(book), config).map((e, i) => ({ ...e, pageLabel: String(i + 1), pageIndex: 100 + i }));
126
+ const alone = buildDocument({ markdown: book, outline, continuation: { pageIndexOffset: 10 } }, config);
127
+ const out = await PDFDocument.load(await renderToPdf(alone, { fontProvider }));
128
+ expect(out.getPage(0).node.Annots()?.asArray() ?? []).toHaveLength(0);
129
+ });
130
+ });
131
+ //# sourceMappingURL=contentsLinks.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentsLinks.test.js","sourceRoot":"","sources":["../../src/__tests__/contentsLinks.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=fontCache.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fontCache.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/fontCache.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,42 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { readFileSync } from 'node:fs';
3
+ import { PDFDocument } from 'pdf-lib';
4
+ import fontkit from '@pdf-lib/fontkit';
5
+ import { coverAllGlyphWidths } from '../fontCache';
6
+ // A TrueType face from the demo app's static fonts (OFL). The width fix is
7
+ // applied to CFF faces in production, but the mechanics are format-agnostic.
8
+ const FONT_PATH = new URL('../../../../apps/web/public/fonts/Fraunces-Regular.ttf', import.meta.url);
9
+ /** The `/W` array of the first CID font in `bytes`, as numbers. */
10
+ function widthsArray(bytes) {
11
+ const text = new TextDecoder('latin1').decode(bytes);
12
+ const match = /\/W \[([\s\S]*?)\]\s*\]/.exec(text);
13
+ if (!match)
14
+ return [];
15
+ return (match[1] + ']').replace(/[[\]]/g, ' ').trim().split(/\s+/).map(Number);
16
+ }
17
+ describe('coverAllGlyphWidths', () => {
18
+ it('lists an advance for every glyph in the face, not only the cmap-reachable ones', async () => {
19
+ const bytes = readFileSync(FONT_PATH);
20
+ const face = fontkit.create(bytes);
21
+ const plainDoc = await PDFDocument.create();
22
+ plainDoc.registerFontkit(fontkit);
23
+ const plain = await plainDoc.embedFont(bytes, { subset: false });
24
+ plainDoc.addPage().drawText('fi fl', { font: plain, size: 12 });
25
+ const plainWidths = widthsArray(await plainDoc.save({ useObjectStreams: false }));
26
+ const fixedDoc = await PDFDocument.create();
27
+ fixedDoc.registerFontkit(fontkit);
28
+ const fixed = await fixedDoc.embedFont(bytes, { subset: false });
29
+ coverAllGlyphWidths(fixed);
30
+ fixedDoc.addPage().drawText('fi fl', { font: fixed, size: 12 });
31
+ const fixedWidths = widthsArray(await fixedDoc.save({ useObjectStreams: false }));
32
+ // `[0 [w0 w1 … w(n-1)]]`: the leading CID plus one width per glyph.
33
+ expect(fixedWidths[0]).toBe(0);
34
+ expect(fixedWidths.length).toBe(face.numGlyphs + 1);
35
+ // pdf-lib's own table stops at the glyphs the cmap can reach.
36
+ expect(plainWidths.length).toBeLessThan(fixedWidths.length);
37
+ // Widths are in 1000-unit glyph space.
38
+ const space = face.glyphForCodePoint(0x20);
39
+ expect(fixedWidths[1 + space.id]).toBeCloseTo(space.advanceWidth * 1000 / face.unitsPerEm, 6);
40
+ });
41
+ });
42
+ //# sourceMappingURL=fontCache.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fontCache.test.js","sourceRoot":"","sources":["../../src/__tests__/fontCache.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,OAAO,MAAM,kBAAkB,CAAC;AACvC,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEnD,2EAA2E;AAC3E,6EAA6E;AAC7E,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,wDAAwD,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAErG,mEAAmE;AACnE,SAAS,WAAW,CAAC,KAAiB;IACpC,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnD,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AACjF,CAAC;AAED,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,gFAAgF,EAAE,KAAK,IAAI,EAAE;QAC9F,MAAM,KAAK,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAEnC,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,CAAC;QAC5C,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAClC,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QACjE,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAChE,MAAM,WAAW,GAAG,WAAW,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QAElF,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,CAAC;QAC5C,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAClC,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QACjE,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAC3B,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAChE,MAAM,WAAW,GAAG,WAAW,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QAElF,oEAAoE;QACpE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;QACpD,8DAA8D;QAC9D,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAC5D,uCAAuC;QACvC,MAAM,KAAK,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IAChG,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=headerFooter.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headerFooter.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/headerFooter.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,68 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { PDFDocument } from 'pdf-lib';
3
+ import { FontCache } from '../fontCache';
4
+ import { renderHeaderFooterSlot } from '../pdf-backend/headerFooter';
5
+ import { makeScale } from '../pdf-backend/primitives';
6
+ /** The page's content stream operators, before pdf-lib deflates them. */
7
+ function pageContent(page) {
8
+ const stream = page.getContentStream();
9
+ return new TextDecoder('latin1').decode(stream.getUnencodedContents());
10
+ }
11
+ describe('design boxes in the PDF backend', () => {
12
+ it('draws a rounded box at its page position (path anchored at the page top, y down)', async () => {
13
+ const pdfDoc = await PDFDocument.create();
14
+ const page = pdfDoc.addPage([612, 792]);
15
+ // 72 dpi: 1 px = 1 pt, so the numbers below read directly as points.
16
+ const ctx = { page, pageHeightPt: 792, scale: makeScale(72), colorSpace: 'rgb' };
17
+ const slot = {
18
+ blocks: [{
19
+ kind: 'box',
20
+ id: 'dot',
21
+ bbox: { x: 72, y: 144, width: 36, height: 36 },
22
+ box: {
23
+ backgroundColor: '#9bcdbf',
24
+ borderWidthPx: 0,
25
+ borderRadiusPx: 18,
26
+ padding: { top: 0, right: 0, bottom: 0, left: 0 },
27
+ },
28
+ }],
29
+ };
30
+ const fontCache = new FontCache(pdfDoc, async () => new Uint8Array());
31
+ renderHeaderFooterSlot(ctx, slot, fontCache);
32
+ const content = pageContent(page);
33
+ // pdf-lib's drawSvgPath translates to the given origin then flips y, so
34
+ // the path must be anchored at the top-left of the page…
35
+ expect(content).toContain('1 0 0 1 0 792 cm');
36
+ expect(content).toContain('1 0 0 -1 0 0 cm');
37
+ // …and start at the box's top edge in top-down points (x + r, y).
38
+ expect(content).toMatch(/(^|\n)90 144 m\n/);
39
+ // The old absolute-coordinate path (bottom edge at 792 - 180 = 612)
40
+ // was mirrored below the page and never painted.
41
+ expect(content).not.toMatch(/(^|\n)90 612 m\n/);
42
+ });
43
+ it('draws a square box with drawRectangle at its page position', async () => {
44
+ const pdfDoc = await PDFDocument.create();
45
+ const page = pdfDoc.addPage([612, 792]);
46
+ const ctx = { page, pageHeightPt: 792, scale: makeScale(72), colorSpace: 'rgb' };
47
+ const slot = {
48
+ blocks: [{
49
+ kind: 'box',
50
+ id: 'tab',
51
+ bbox: { x: 0, y: 0, width: 50, height: 20 },
52
+ box: {
53
+ backgroundColor: '#9bcdbf',
54
+ borderWidthPx: 0,
55
+ borderRadiusPx: 0,
56
+ padding: { top: 0, right: 0, bottom: 0, left: 0 },
57
+ },
58
+ }],
59
+ };
60
+ renderHeaderFooterSlot(ctx, slot, new FontCache(pdfDoc, async () => new Uint8Array()));
61
+ const content = pageContent(page);
62
+ // pdf-lib translates to the bottom-left corner (y = 792 - 20) and
63
+ // traces the rectangle from the origin.
64
+ expect(content).toContain('1 0 0 1 0 772 cm');
65
+ expect(content).toContain('50 20 l');
66
+ });
67
+ });
68
+ //# sourceMappingURL=headerFooter.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headerFooter.test.js","sourceRoot":"","sources":["../../src/__tests__/headerFooter.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rotatedTable.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rotatedTable.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/rotatedTable.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,112 @@
1
+ import { describe, it, expect, beforeAll } 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 } 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 cell = (content, extra = {}) => ({ content, ...extra });
26
+ const config = {
27
+ page: { width: pt(400), height: pt(500), margins: { top: pt(20), bottom: pt(20), left: pt(20), right: pt(20) } },
28
+ headings: { balancing: { enabled: false }, levels: [{ level: 1, breakBefore: { enabled: false } }] },
29
+ colorPalette: [{ id: 'ok', name: 'Compatible', value: { hex: '#c1dfd6', model: 'hex' } }],
30
+ };
31
+ const table = {
32
+ id: 'tab',
33
+ typeId: 'table',
34
+ kind: 'table',
35
+ caption: 'Services and benefits.',
36
+ note: ':swatch{color="ok"} compatible; :swatch{color="#e04148"} incompatible. See :ref{id="fig"}.',
37
+ createdAt: 0,
38
+ updatedAt: 0,
39
+ table: {
40
+ model: {
41
+ headerRowCount: 1,
42
+ rows: [
43
+ [cell('Service', { isHeader: true }), cell('Grade I', { isHeader: true }), cell('Grade II', { isHeader: true })],
44
+ [cell('Home care'), cell('Yes', { background: { hex: '#000000', model: 'hex', paletteId: 'ok' } }), cell('No', { background: { hex: '#e04148', model: 'hex' } })],
45
+ [cell('Day centre'), cell('See :ref{id="fig"}'), cell('Yes')],
46
+ ],
47
+ },
48
+ },
49
+ placement: { rotate: 'ccw' },
50
+ };
51
+ const figure = {
52
+ id: 'fig',
53
+ typeId: 'figure',
54
+ kind: 'bitmap',
55
+ caption: 'A picture.',
56
+ createdAt: 0,
57
+ updatedAt: 0,
58
+ bitmap: { fileId: 'missing', format: 'png', width: 200, height: 100 },
59
+ };
60
+ const filler = (n) => Array.from({ length: n }, (_, i) => `Paragraph ${i} carries enough words to take a few lines of the narrow column so the flow advances.`).join('\n\n');
61
+ const markdown = `Intro :ref{id="tab"} and :ref{id="fig"} text.\n\n${filler(12)}`;
62
+ let pdf;
63
+ let doc;
64
+ let bytes;
65
+ beforeAll(async () => {
66
+ doc = buildDocument({ markdown, resources: [table, figure] }, config);
67
+ bytes = await renderToPdf(doc, { fontProvider });
68
+ pdf = await PDFDocument.load(bytes);
69
+ }, 60_000);
70
+ describe('a rotated table in the PDF', () => {
71
+ it('is placed turned on a page of its own', () => {
72
+ const placed = doc.pages.flatMap((p) => (p.floats ?? []).filter((b) => b.resourceBlock.resource.id === 'tab').map((b) => ({ page: p, block: b })));
73
+ expect(placed).toHaveLength(1);
74
+ expect(placed[0].block.resourceBlock.rotation?.direction).toBe('ccw');
75
+ });
76
+ it('draws the block through a quarter-turn matrix', () => {
77
+ const { page } = doc.pages.flatMap((p) => (p.floats ?? []).filter((b) => b.resourceBlock.resource.id === 'tab').map(() => ({ page: p })))[0];
78
+ const pdfPage = pdf.getPage(page.index);
79
+ const contents = pdfPage.node.Contents();
80
+ const streams = contents instanceof PDFArray
81
+ ? contents.asArray().map((r) => pdf.context.lookup(r))
82
+ : [contents];
83
+ const text = streams
84
+ .map((s) => (s instanceof PDFRawStream ? Buffer.from(decodePDFRawStream(s).decode()).toString('latin1') : ''))
85
+ .join('\n');
86
+ // `0 1 -1 0 e f cm`: the counter-clockwise turn.
87
+ expect(/(^|\s)0 1 -1 0 [-\d.]+ [-\d.]+ cm/.test(text)).toBe(true);
88
+ // Balanced graphics state around it.
89
+ expect((text.match(/\bq\b/g) ?? []).length).toBe((text.match(/\bQ\b/g) ?? []).length);
90
+ });
91
+ it('turns the link annotation of a ref inside a cell with the block', () => {
92
+ const { page, block } = doc.pages.flatMap((p) => (p.floats ?? []).filter((b) => b.resourceBlock.resource.id === 'tab').map((b) => ({ page: p, block: b })))[0];
93
+ const pdfPage = pdf.getPage(page.index);
94
+ const annots = (pdfPage.node.Annots()?.asArray() ?? []).map((r) => pdf.context.lookup(r, PDFDict));
95
+ const links = annots.filter((a) => a.get(PDFName.of('Subtype')) === PDFName.of('Link'));
96
+ expect(links.length).toBeGreaterThanOrEqual(2); // the cell ref and the note ref
97
+ const scale = 72 / doc.config.page.dpi;
98
+ const h = pdfPage.getHeight();
99
+ for (const link of links) {
100
+ const rect = link.lookup(PDFName.of('Rect'), PDFArray).asArray().map((n) => n.asNumber());
101
+ const [x1, y1, x2, y2] = rect;
102
+ // Inside the band the block takes, in page space.
103
+ expect(x1).toBeGreaterThanOrEqual(block.bbox.x * scale - 0.5);
104
+ expect(x2).toBeLessThanOrEqual((block.bbox.x + block.bbox.width) * scale + 0.5);
105
+ expect(y1).toBeGreaterThanOrEqual(h - (block.bbox.y + block.bbox.height) * scale - 0.5);
106
+ expect(y2).toBeLessThanOrEqual(h - block.bbox.y * scale + 0.5);
107
+ // A turned label: taller than wide.
108
+ expect(y2 - y1).toBeGreaterThan(x2 - x1);
109
+ }
110
+ });
111
+ });
112
+ //# sourceMappingURL=rotatedTable.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rotatedTable.test.js","sourceRoot":"","sources":["../../src/__tests__/rotatedTable.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzD,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,MAAM,SAAS,CAAC;AAExC,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,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC3B,OAAO,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,YAAY,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,MAAM,EAAE,CAAC;IAClE,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,IAAI,GAAG,CAAC,OAAe,EAAE,QAA4B,EAAE,EAAa,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;AAErG,MAAM,MAAM,GAAkB;IAC5B,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;IAChH,QAAQ,EAAE,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE;IACpG,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;CAC1F,CAAC;AAEF,MAAM,KAAK,GAAa;IACtB,EAAE,EAAE,KAAK;IACT,MAAM,EAAE,OAAO;IACf,IAAI,EAAE,OAAO;IACb,OAAO,EAAE,wBAAwB;IACjC,IAAI,EAAE,4FAA4F;IAClG,SAAS,EAAE,CAAC;IACZ,SAAS,EAAE,CAAC;IACZ,KAAK,EAAE;QACL,KAAK,EAAE;YACL,cAAc,EAAE,CAAC;YACjB,IAAI,EAAE;gBACJ,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;gBAChH,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;gBACjK,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,oBAAoB,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;aAC9D;SACF;KACF;IACD,SAAS,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;CAC7B,CAAC;AACF,MAAM,MAAM,GAAa;IACvB,EAAE,EAAE,KAAK;IACT,MAAM,EAAE,QAAQ;IAChB,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,YAAY;IACrB,SAAS,EAAE,CAAC;IACZ,SAAS,EAAE,CAAC;IACZ,MAAM,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE;CACtE,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,EAAE,CAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,sFAAsF,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACzJ,MAAM,QAAQ,GAAG,oDAAoD,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;AAElF,IAAI,GAAgB,CAAC;AACrB,IAAI,GAAqC,CAAC;AAC1C,IAAI,KAAiB,CAAC;AAEtB,SAAS,CAAC,KAAK,IAAI,EAAE;IACnB,GAAG,GAAG,aAAa,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IACtE,KAAK,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;IACjD,GAAG,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACtC,CAAC,EAAE,MAAM,CAAC,CAAC;AAEX,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAc,CAAC,QAAQ,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACpJ,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,aAAc,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAc,CAAC,QAAQ,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC;QAC/I,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,QAAQ,YAAY,QAAQ;YAC1C,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACtD,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACf,MAAM,IAAI,GAAG,OAAO;aACjB,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,iDAAiD;QACjD,MAAM,CAAC,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClE,qCAAqC;QACrC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IACxF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAc,CAAC,QAAQ,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC;QACjK,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QACnG,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,KAAK,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;QACxF,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC,gCAAgC;QAChF,MAAM,KAAK,GAAG,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QACvC,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;QAC9B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAE,CAAe,CAAC,QAAQ,EAAE,CAAC,CAAC;YACzG,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,IAAwC,CAAC;YAClE,kDAAkD;YAClD,MAAM,CAAC,EAAE,CAAC,CAAC,sBAAsB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,GAAG,CAAC,CAAC;YAC9D,MAAM,CAAC,EAAE,CAAC,CAAC,mBAAmB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,GAAG,CAAC,CAAC;YAChF,MAAM,CAAC,EAAE,CAAC,CAAC,sBAAsB,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,GAAG,CAAC,CAAC;YACxF,MAAM,CAAC,EAAE,CAAC,CAAC,mBAAmB,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,GAAG,CAAC,CAAC;YAC/D,oCAAoC;YACpC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,eAAe,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=svgVector.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svgVector.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/svgVector.test.ts"],"names":[],"mappings":""}