pretext-pdf 0.9.2 → 1.0.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 (105) hide show
  1. package/CHANGELOG.md +126 -110
  2. package/README.md +122 -1
  3. package/dist/allowed-props.d.ts +40 -0
  4. package/dist/allowed-props.d.ts.map +1 -0
  5. package/dist/allowed-props.js +130 -0
  6. package/dist/allowed-props.js.map +1 -0
  7. package/dist/assets.d.ts +4 -2
  8. package/dist/assets.d.ts.map +1 -1
  9. package/dist/assets.js +22 -1
  10. package/dist/assets.js.map +1 -1
  11. package/dist/benchmarks/corpora.d.ts +11 -0
  12. package/dist/benchmarks/corpora.d.ts.map +1 -0
  13. package/dist/benchmarks/corpora.js +228 -0
  14. package/dist/benchmarks/corpora.js.map +1 -0
  15. package/dist/builder.d.ts +10 -2
  16. package/dist/builder.d.ts.map +1 -1
  17. package/dist/builder.js +12 -4
  18. package/dist/builder.js.map +1 -1
  19. package/dist/element-types.d.ts +16 -0
  20. package/dist/element-types.d.ts.map +1 -0
  21. package/dist/element-types.js +16 -0
  22. package/dist/element-types.js.map +1 -0
  23. package/dist/errors.d.ts +8 -1
  24. package/dist/errors.d.ts.map +1 -1
  25. package/dist/errors.js +4 -0
  26. package/dist/errors.js.map +1 -1
  27. package/dist/fonts.d.ts +2 -1
  28. package/dist/fonts.d.ts.map +1 -1
  29. package/dist/fonts.js.map +1 -1
  30. package/dist/index.d.ts +23 -14
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +27 -310
  33. package/dist/index.js.map +1 -1
  34. package/dist/layout-state.d.ts +39 -0
  35. package/dist/layout-state.d.ts.map +1 -0
  36. package/dist/layout-state.js +46 -0
  37. package/dist/layout-state.js.map +1 -0
  38. package/dist/measure-blocks.d.ts +2 -1
  39. package/dist/measure-blocks.d.ts.map +1 -1
  40. package/dist/measure-blocks.js.map +1 -1
  41. package/dist/measure.d.ts +4 -2
  42. package/dist/measure.d.ts.map +1 -1
  43. package/dist/measure.js +15 -5
  44. package/dist/measure.js.map +1 -1
  45. package/dist/page-sizes.d.ts +1 -0
  46. package/dist/page-sizes.d.ts.map +1 -1
  47. package/dist/page-sizes.js.map +1 -1
  48. package/dist/paginate.d.ts +1 -1
  49. package/dist/paginate.d.ts.map +1 -1
  50. package/dist/paginate.js +11 -2
  51. package/dist/paginate.js.map +1 -1
  52. package/dist/pipeline-footnotes.d.ts +18 -0
  53. package/dist/pipeline-footnotes.d.ts.map +1 -0
  54. package/dist/pipeline-footnotes.js +98 -0
  55. package/dist/pipeline-footnotes.js.map +1 -0
  56. package/dist/pipeline-toc.d.ts +11 -0
  57. package/dist/pipeline-toc.d.ts.map +1 -0
  58. package/dist/pipeline-toc.js +28 -0
  59. package/dist/pipeline-toc.js.map +1 -0
  60. package/dist/pipeline.d.ts +24 -0
  61. package/dist/pipeline.d.ts.map +1 -0
  62. package/dist/pipeline.js +116 -0
  63. package/dist/pipeline.js.map +1 -0
  64. package/dist/plugin-registry.d.ts +40 -0
  65. package/dist/plugin-registry.d.ts.map +1 -0
  66. package/dist/plugin-registry.js +104 -0
  67. package/dist/plugin-registry.js.map +1 -0
  68. package/dist/plugin-types.d.ts +185 -0
  69. package/dist/plugin-types.d.ts.map +1 -0
  70. package/dist/plugin-types.js +27 -0
  71. package/dist/plugin-types.js.map +1 -0
  72. package/dist/post-process.d.ts +16 -0
  73. package/dist/post-process.d.ts.map +1 -0
  74. package/dist/post-process.js +80 -0
  75. package/dist/post-process.js.map +1 -0
  76. package/dist/render-blocks.d.ts +2 -1
  77. package/dist/render-blocks.d.ts.map +1 -1
  78. package/dist/render-blocks.js.map +1 -1
  79. package/dist/render-extras.d.ts +2 -2
  80. package/dist/render-extras.d.ts.map +1 -1
  81. package/dist/render-extras.js.map +1 -1
  82. package/dist/render.d.ts +4 -2
  83. package/dist/render.d.ts.map +1 -1
  84. package/dist/render.js +23 -4
  85. package/dist/render.js.map +1 -1
  86. package/dist/types-internal.d.ts +302 -0
  87. package/dist/types-internal.d.ts.map +1 -0
  88. package/dist/types-internal.js +9 -0
  89. package/dist/types-internal.js.map +1 -0
  90. package/dist/types-public.d.ts +1031 -0
  91. package/dist/types-public.d.ts.map +1 -0
  92. package/dist/types-public.js +2 -0
  93. package/dist/types-public.js.map +1 -0
  94. package/dist/types.d.ts +6 -1224
  95. package/dist/types.d.ts.map +1 -1
  96. package/dist/types.js +7 -0
  97. package/dist/types.js.map +1 -1
  98. package/dist/validate.d.ts +6 -2
  99. package/dist/validate.d.ts.map +1 -1
  100. package/dist/validate.js +148 -5
  101. package/dist/validate.js.map +1 -1
  102. package/package.json +31 -23
  103. package/docs/screenshots/showcase-invoice.png +0 -0
  104. package/docs/screenshots/showcase-report.png +0 -0
  105. package/docs/screenshots/showcase-resume.png +0 -0
@@ -0,0 +1,18 @@
1
+ import type { ContentElement } from './types-public.js';
2
+ import type { MeasuredBlock, PaginatedDocument } from './types-internal.js';
3
+ /** Build document-order footnote numbering by scanning rich-paragraphs in content order. */
4
+ export declare function buildFootnoteNumbering(content: ContentElement[]): Map<string, number>;
5
+ /**
6
+ * Two-pass footnote pagination.
7
+ *
8
+ * Pass 1: paginate without zone reservation to find which refs land on which page.
9
+ * Pass 2: paginate with per-page zones reserved at the bottom.
10
+ * Returns the final PaginatedDocument with footnoteItems and footnoteZoneHeight annotated.
11
+ */
12
+ export declare function runFootnoteTwoPass(measuredBlocks: MeasuredBlock[], contentHeight: number, paginateConfig: {
13
+ minOrphanLines: number;
14
+ minWidowLines: number;
15
+ }, doc: {
16
+ content: ContentElement[];
17
+ }): PaginatedDocument;
18
+ //# sourceMappingURL=pipeline-footnotes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline-footnotes.d.ts","sourceRoot":"","sources":["../src/pipeline-footnotes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAsB,MAAM,mBAAmB,CAAA;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAM3E,4FAA4F;AAC5F,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAarF;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,cAAc,EAAE,aAAa,EAAE,EAC/B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,EACjE,GAAG,EAAE;IAAE,OAAO,EAAE,cAAc,EAAE,CAAA;CAAE,GACjC,iBAAiB,CAiFnB"}
@@ -0,0 +1,98 @@
1
+ import { PretextPdfError } from './errors.js';
2
+ import { paginate } from './paginate.js';
3
+ const SEPARATOR_HEIGHT = 16; // separator line + padding above/below
4
+ /** Build document-order footnote numbering by scanning rich-paragraphs in content order. */
5
+ export function buildFootnoteNumbering(content) {
6
+ const numbering = new Map();
7
+ let counter = 1;
8
+ for (const el of content) {
9
+ if (el.type === 'rich-paragraph') {
10
+ for (const span of el.spans) {
11
+ if (span.footnoteRef && !numbering.has(span.footnoteRef)) {
12
+ numbering.set(span.footnoteRef, counter++);
13
+ }
14
+ }
15
+ }
16
+ }
17
+ return numbering;
18
+ }
19
+ /**
20
+ * Two-pass footnote pagination.
21
+ *
22
+ * Pass 1: paginate without zone reservation to find which refs land on which page.
23
+ * Pass 2: paginate with per-page zones reserved at the bottom.
24
+ * Returns the final PaginatedDocument with footnoteItems and footnoteZoneHeight annotated.
25
+ */
26
+ export function runFootnoteTwoPass(measuredBlocks, contentHeight, paginateConfig, doc) {
27
+ const footnoteDefElements = doc.content.filter(el => el.type === 'footnote-def');
28
+ if (footnoteDefElements.length === 0) {
29
+ return paginate(measuredBlocks, contentHeight, paginateConfig);
30
+ }
31
+ // Build a map of def id → measured height (already measured in measuredBlocks)
32
+ const footnoteDefHeightMap = new Map();
33
+ for (const block of measuredBlocks) {
34
+ if (block.element.type === 'footnote-def') {
35
+ const def = block.element;
36
+ footnoteDefHeightMap.set(def.id, block.height + (block.spaceAfter ?? 0));
37
+ }
38
+ }
39
+ // Strip footnote-def blocks from the block stream (defs are not placed in flow)
40
+ const flowBlocks = measuredBlocks.filter(b => b.element.type !== 'footnote-def');
41
+ // Build document-order footnote numbering before pagination (controls rendering order)
42
+ const footnoteNumbering = buildFootnoteNumbering(doc.content);
43
+ // PASS 1: Paginate without any footnote zone reservation
44
+ const pass1 = paginate(flowBlocks, contentHeight, paginateConfig);
45
+ // Determine which footnote refs land on which page
46
+ const pageFootnoteRefs = new Map();
47
+ for (let pageIdx = 0; pageIdx < pass1.pages.length; pageIdx++) {
48
+ const page = pass1.pages[pageIdx];
49
+ const refsOnPageSet = new Set();
50
+ for (const pagedBlock of page.blocks) {
51
+ const el = pagedBlock.measuredBlock.element;
52
+ if (el.type === 'rich-paragraph') {
53
+ for (const span of el.spans) {
54
+ if (span.footnoteRef)
55
+ refsOnPageSet.add(span.footnoteRef);
56
+ }
57
+ }
58
+ }
59
+ if (refsOnPageSet.size > 0) {
60
+ pageFootnoteRefs.set(pageIdx, [...refsOnPageSet]);
61
+ }
62
+ }
63
+ // Build per-page footnote zone heights
64
+ const footnoteZones = new Map();
65
+ for (const [pageIdx, refIds] of pageFootnoteRefs) {
66
+ let zoneHeight = SEPARATOR_HEIGHT;
67
+ for (const refId of refIds) {
68
+ zoneHeight += footnoteDefHeightMap.get(refId) ?? 0;
69
+ }
70
+ footnoteZones.set(pageIdx, zoneHeight);
71
+ }
72
+ // PASS 2: Paginate with zones reserved
73
+ const pass2Config = { ...paginateConfig, footnoteZones };
74
+ const paginatedDoc = paginate(flowBlocks, contentHeight, pass2Config);
75
+ paginatedDoc.footnoteNumbering = footnoteNumbering;
76
+ // Annotate each RenderedPage with its footnote items
77
+ for (const [pageIdx, refIds] of pageFootnoteRefs) {
78
+ if (pageIdx >= paginatedDoc.pages.length) {
79
+ throw new PretextPdfError('PAGINATION_FAILED', `Footnote zone computed for page ${pageIdx} but document only has ${paginatedDoc.pages.length} pages. This is a pagination bug.`);
80
+ }
81
+ const page = paginatedDoc.pages[pageIdx];
82
+ if (!page) {
83
+ throw new PretextPdfError('PAGINATION_FAILED', `Footnote zone computed for page ${pageIdx} but document only has ${paginatedDoc.pages.length} pages. This is a pagination bug.`);
84
+ }
85
+ page.footnoteItems = refIds
86
+ .map(id => {
87
+ const def = footnoteDefElements.find(d => d.id === id);
88
+ const num = footnoteNumbering.get(id) ?? 0;
89
+ return def ? { def, number: num } : null;
90
+ })
91
+ .filter(Boolean);
92
+ const zoneHeight = footnoteZones.get(pageIdx);
93
+ if (zoneHeight !== undefined)
94
+ page.footnoteZoneHeight = zoneHeight;
95
+ }
96
+ return paginatedDoc;
97
+ }
98
+ //# sourceMappingURL=pipeline-footnotes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline-footnotes.js","sourceRoot":"","sources":["../src/pipeline-footnotes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { PdfDocument } from './types-public.js';
2
+ import type { MeasuredBlock } from './types-internal.js';
3
+ /**
4
+ * Two-pass TOC entry building.
5
+ *
6
+ * Pass 1: draft pagination to collect heading page numbers.
7
+ * Splices real TOC entry blocks into measuredBlocks at the placeholder index.
8
+ * Returns the updated measuredBlocks array (new reference, original untouched).
9
+ */
10
+ export declare function runTocTwoPass(measuredBlocks: MeasuredBlock[], doc: Pick<PdfDocument, 'content' | 'defaultFont'> & Partial<PdfDocument>, contentWidth: number, contentHeight: number): Promise<MeasuredBlock[]>;
11
+ //# sourceMappingURL=pipeline-toc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline-toc.d.ts","sourceRoot":"","sources":["../src/pipeline-toc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAGxD;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,cAAc,EAAE,aAAa,EAAE,EAC/B,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,GAAG,aAAa,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,EACxE,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,aAAa,EAAE,CAAC,CA0B1B"}
@@ -0,0 +1,28 @@
1
+ import { paginate } from './paginate.js';
2
+ /**
3
+ * Two-pass TOC entry building.
4
+ *
5
+ * Pass 1: draft pagination to collect heading page numbers.
6
+ * Splices real TOC entry blocks into measuredBlocks at the placeholder index.
7
+ * Returns the updated measuredBlocks array (new reference, original untouched).
8
+ */
9
+ export async function runTocTwoPass(measuredBlocks, doc, contentWidth, contentHeight) {
10
+ const tocIndex = doc.content.findIndex(el => el.type === 'toc');
11
+ if (tocIndex === -1)
12
+ return measuredBlocks;
13
+ const tocElement = doc.content[tocIndex];
14
+ if (!tocElement || tocElement.type !== 'toc')
15
+ throw new Error('TOC element type mismatch');
16
+ // Pass 1: paginate without real TOC content to collect heading page numbers
17
+ const draftPaginatedDoc = paginate(measuredBlocks, contentHeight);
18
+ // Dynamic import preserves lazy-load semantics (measure.ts can be heavy)
19
+ const { buildTocEntryBlocks } = await import('./measure.js');
20
+ const tocEntryBlocks = await buildTocEntryBlocks(draftPaginatedDoc.headings, tocElement, contentWidth, doc);
21
+ // Splice TOC entries in place of the placeholder (zero-height) block at tocIndex
22
+ return [
23
+ ...measuredBlocks.slice(0, tocIndex),
24
+ ...tocEntryBlocks,
25
+ ...measuredBlocks.slice(tocIndex + 1),
26
+ ];
27
+ }
28
+ //# sourceMappingURL=pipeline-toc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline-toc.js","sourceRoot":"","sources":["../src/pipeline-toc.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,cAA+B,EAC/B,GAAwE,EACxE,YAAoB,EACpB,aAAqB;IAErB,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,KAAK,CAAC,CAAA;IAC/D,IAAI,QAAQ,KAAK,CAAC,CAAC;QAAE,OAAO,cAAc,CAAA;IAE1C,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IACxC,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,IAAI,KAAK,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAA;IAE1F,4EAA4E;IAC5E,MAAM,iBAAiB,GAAG,QAAQ,CAAC,cAAc,EAAE,aAAa,CAAC,CAAA;IAEjE,yEAAyE;IACzE,MAAM,EAAE,mBAAmB,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAA;IAE5D,MAAM,cAAc,GAAG,MAAM,mBAAmB,CAC9C,iBAAiB,CAAC,QAAQ,EAC1B,UAAU,EACV,YAAY,EACZ,GAAkB,CACnB,CAAA;IAED,iFAAiF;IACjF,OAAO;QACL,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC;QACpC,GAAG,cAAc;QACjB,GAAG,cAAc,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC;KACtC,CAAA;AACH,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { PDFDocument } from '@cantoo/pdf-lib';
2
+ import type { PdfDocument, RenderOptions } from './types-public.js';
3
+ import type { PageGeometry, FontMap, ImageMap, MeasuredBlock } from './types-internal.js';
4
+ import { runFootnoteTwoPass } from './pipeline-footnotes.js';
5
+ export declare function stageValidate(doc: PdfDocument, options?: RenderOptions): void;
6
+ export declare function stageInit(doc: PdfDocument): Promise<{
7
+ pdfDoc: PDFDocument;
8
+ geo: Omit<PageGeometry, 'contentHeight' | 'headerHeight' | 'footerHeight'>;
9
+ defaultFont: string;
10
+ }>;
11
+ export declare function stageLoadAssets(doc: PdfDocument, pdfDoc: PDFDocument, contentWidth: number, plugins?: import('./plugin-types.js').PluginDefinition[]): Promise<{
12
+ fontMap: FontMap;
13
+ imageMap: ImageMap;
14
+ }>;
15
+ export declare function stageFinalizeGeo(doc: PdfDocument, partialGeo: Omit<PageGeometry, 'contentHeight' | 'headerHeight' | 'footerHeight'>, defaultFont: string): Promise<PageGeometry>;
16
+ export declare function stageMeasure(doc: PdfDocument, contentWidth: number, imageMap: ImageMap, contentHeight: number, plugins?: import('./plugin-types.js').PluginDefinition[]): Promise<MeasuredBlock[]>;
17
+ export declare function stagePaginate(measuredBlocks: MeasuredBlock[], contentHeight: number, doc: PdfDocument): ReturnType<typeof runFootnoteTwoPass>;
18
+ export declare function stageRender(paginatedDoc: ReturnType<typeof stagePaginate>, doc: PdfDocument, fontMap: FontMap, imageMap: ImageMap, pdfDoc: PDFDocument, geo: PageGeometry, plugins?: import('./plugin-types.js').PluginDefinition[]): Promise<Uint8Array>;
19
+ /**
20
+ * Run all 5 stages in sequence and return raw PDF bytes.
21
+ * Post-processing (signature, encryption) is applied by the public render() in index.ts.
22
+ */
23
+ export declare function runPipeline(doc: PdfDocument, options?: RenderOptions): Promise<Uint8Array>;
24
+ //# sourceMappingURL=pipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAsB,MAAM,iBAAiB,CAAA;AACjE,OAAO,KAAK,EAAE,WAAW,EAAW,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAC5E,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAUzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAI5D,wBAAgB,aAAa,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,CAE7E;AAID,wBAAsB,SAAS,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC;IACzD,MAAM,EAAE,WAAW,CAAA;IACnB,GAAG,EAAE,IAAI,CAAC,YAAY,EAAE,eAAe,GAAG,cAAc,GAAG,cAAc,CAAC,CAAA;IAC1E,WAAW,EAAE,MAAM,CAAA;CACpB,CAAC,CAwCD;AAID,wBAAsB,eAAe,CACnC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,WAAW,EACnB,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,OAAO,mBAAmB,EAAE,gBAAgB,EAAE,GACvD,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC,CAMnD;AAID,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,WAAW,EAChB,UAAU,EAAE,IAAI,CAAC,YAAY,EAAE,eAAe,GAAG,cAAc,GAAG,cAAc,CAAC,EACjF,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC,CAiCvB;AAID,wBAAsB,YAAY,CAChC,GAAG,EAAE,WAAW,EAChB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,OAAO,mBAAmB,EAAE,gBAAgB,EAAE,GACvD,OAAO,CAAC,aAAa,EAAE,CAAC,CAI1B;AAID,wBAAgB,aAAa,CAC3B,cAAc,EAAE,aAAa,EAAE,EAC/B,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,WAAW,GACf,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAGvC;AAID,wBAAsB,WAAW,CAC/B,YAAY,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,EAC9C,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,YAAY,EACjB,OAAO,CAAC,EAAE,OAAO,mBAAmB,EAAE,gBAAgB,EAAE,GACvD,OAAO,CAAC,UAAU,CAAC,CAErB;AAID;;;GAGG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAiBhG"}
@@ -0,0 +1,116 @@
1
+ import { PDFDocument, PDFName, PDFString } from '@cantoo/pdf-lib';
2
+ import { PretextPdfError } from './errors.js';
3
+ import { resolvePageDimensions } from './page-sizes.js';
4
+ import { validate } from './validate.js';
5
+ import { loadFonts } from './fonts.js';
6
+ import { loadImages } from './assets.js';
7
+ import { measureAllBlocks, measureHeaderFooterHeight } from './measure.js';
8
+ import { LINE_HEIGHT_COMPACT } from './render-utils.js';
9
+ import { renderDocument } from './render.js';
10
+ import { runTocTwoPass } from './pipeline-toc.js';
11
+ import { runFootnoteTwoPass } from './pipeline-footnotes.js';
12
+ // ─── Stage 1: Validate ────────────────────────────────────────────────────────
13
+ export function stageValidate(doc, options) {
14
+ validate(doc, options);
15
+ }
16
+ // ─── Stage 2a: Init (geometry + pdfDoc + metadata) ───────────────────────────
17
+ export async function stageInit(doc) {
18
+ const [pageWidth, pageHeight] = resolvePageDimensions(doc.pageSize);
19
+ const margins = {
20
+ top: doc.margins?.top ?? 72,
21
+ bottom: doc.margins?.bottom ?? 72,
22
+ left: doc.margins?.left ?? 72,
23
+ right: doc.margins?.right ?? 72,
24
+ };
25
+ const contentWidth = pageWidth - margins.left - margins.right;
26
+ if (contentWidth <= 0) {
27
+ throw new PretextPdfError('PAGE_TOO_SMALL', `Content width is ${contentWidth}pt after applying margins. Reduce margins or increase page size.`);
28
+ }
29
+ const pdfDoc = await PDFDocument.create();
30
+ if (doc.metadata) {
31
+ const m = doc.metadata;
32
+ if (m.title)
33
+ pdfDoc.setTitle(m.title);
34
+ if (m.author)
35
+ pdfDoc.setAuthor(m.author);
36
+ if (m.subject)
37
+ pdfDoc.setSubject(m.subject);
38
+ if (m.keywords)
39
+ pdfDoc.setKeywords(m.keywords);
40
+ pdfDoc.setCreator(m.creator ?? 'pretext-pdf');
41
+ if (m.producer)
42
+ pdfDoc.setProducer(m.producer);
43
+ if (m.language) {
44
+ pdfDoc.catalog.set(PDFName.of('Lang'), PDFString.of(m.language));
45
+ }
46
+ }
47
+ const creationDate = doc.renderDate
48
+ ? (doc.renderDate instanceof Date ? doc.renderDate : new Date(doc.renderDate))
49
+ : new Date();
50
+ pdfDoc.setCreationDate(creationDate);
51
+ pdfDoc.setModificationDate(creationDate);
52
+ return {
53
+ pdfDoc,
54
+ geo: { pageWidth, pageHeight, margins, contentWidth },
55
+ defaultFont: doc.defaultFont ?? 'Inter',
56
+ };
57
+ }
58
+ // ─── Stage 2b: Load assets (fonts + images in parallel) ──────────────────────
59
+ export async function stageLoadAssets(doc, pdfDoc, contentWidth, plugins) {
60
+ const [fontMap, imageMap] = await Promise.all([
61
+ loadFonts(doc, pdfDoc),
62
+ loadImages(doc, pdfDoc, contentWidth, plugins),
63
+ ]);
64
+ return { fontMap, imageMap };
65
+ }
66
+ // ─── Stage 2c: Finalize geometry (header/footer heights) ─────────────────────
67
+ export async function stageFinalizeGeo(doc, partialGeo, defaultFont) {
68
+ const { pageHeight, margins, contentWidth } = partialGeo;
69
+ const headerHeight = doc.header
70
+ ? await measureHeaderFooterHeight(doc.header.text, doc.header.fontSize ?? 10, doc.header.fontFamily ?? defaultFont, contentWidth, (doc.header.fontSize ?? 10) * LINE_HEIGHT_COMPACT) + 8
71
+ : 0;
72
+ const footerHeight = doc.footer
73
+ ? await measureHeaderFooterHeight(doc.footer.text, doc.footer.fontSize ?? 10, doc.footer.fontFamily ?? defaultFont, contentWidth, (doc.footer.fontSize ?? 10) * LINE_HEIGHT_COMPACT) + 8
74
+ : 0;
75
+ const contentHeight = pageHeight - margins.top - margins.bottom - headerHeight - footerHeight;
76
+ if (contentHeight <= 0) {
77
+ throw new PretextPdfError('PAGE_TOO_SMALL', `Content height is ${contentHeight}pt after applying margins + header/footer. Try reducing margins, header/footer font size, or increasing page size.`);
78
+ }
79
+ return { ...partialGeo, headerHeight, footerHeight, contentHeight };
80
+ }
81
+ // ─── Stage 3: Measure ─────────────────────────────────────────────────────────
82
+ export async function stageMeasure(doc, contentWidth, imageMap, contentHeight, plugins) {
83
+ let measuredBlocks = await measureAllBlocks(doc, contentWidth, imageMap, contentHeight, plugins);
84
+ measuredBlocks = await runTocTwoPass(measuredBlocks, doc, contentWidth, contentHeight);
85
+ return measuredBlocks;
86
+ }
87
+ // ─── Stage 4: Paginate ────────────────────────────────────────────────────────
88
+ export function stagePaginate(measuredBlocks, contentHeight, doc) {
89
+ const paginateConfig = { minOrphanLines: 2, minWidowLines: 2 };
90
+ return runFootnoteTwoPass(measuredBlocks, contentHeight, paginateConfig, doc);
91
+ }
92
+ // ─── Stage 5: Render ──────────────────────────────────────────────────────────
93
+ export async function stageRender(paginatedDoc, doc, fontMap, imageMap, pdfDoc, geo, plugins) {
94
+ return renderDocument(paginatedDoc, doc, fontMap, imageMap, pdfDoc, geo, plugins);
95
+ }
96
+ // ─── Composed pipeline ────────────────────────────────────────────────────────
97
+ /**
98
+ * Run all 5 stages in sequence and return raw PDF bytes.
99
+ * Post-processing (signature, encryption) is applied by the public render() in index.ts.
100
+ */
101
+ export async function runPipeline(doc, options) {
102
+ // Node canvas polyfill MUST be installed before any Pretext import (measure.ts uses it)
103
+ if (typeof OffscreenCanvas === 'undefined' && typeof window === 'undefined') {
104
+ const { installNodePolyfill } = await import('./node-polyfill.js');
105
+ await installNodePolyfill();
106
+ }
107
+ const plugins = options?.plugins;
108
+ stageValidate(doc, options);
109
+ const { pdfDoc, geo: partialGeo, defaultFont } = await stageInit(doc);
110
+ const { fontMap, imageMap } = await stageLoadAssets(doc, pdfDoc, partialGeo.contentWidth, plugins);
111
+ const geo = await stageFinalizeGeo(doc, partialGeo, defaultFont);
112
+ const measuredBlocks = await stageMeasure(doc, geo.contentWidth, imageMap, geo.contentHeight, plugins);
113
+ const paginatedDoc = stagePaginate(measuredBlocks, geo.contentHeight, doc);
114
+ return stageRender(paginatedDoc, doc, fontMap, imageMap, pdfDoc, geo, plugins);
115
+ }
116
+ //# sourceMappingURL=pipeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Plugin registry — orchestration helpers for the 4 injection points.
3
+ *
4
+ * All functions are pure (no module-level state). The plugin list is
5
+ * threaded through the pipeline via RenderOptions and passed explicitly.
6
+ *
7
+ * @internal Not part of the public API. Import PluginDefinition from plugin-types.ts.
8
+ */
9
+ import type { PluginDefinition, PluginMeasureContext } from './plugin-types.js';
10
+ import type { MeasuredBlock, PageGeometry } from './types-internal.js';
11
+ /**
12
+ * Return the first plugin whose `type` matches `elementType`, or `undefined`.
13
+ * @internal
14
+ */
15
+ export declare function findPlugin(plugins: PluginDefinition[], elementType: string): PluginDefinition | undefined;
16
+ /**
17
+ * Run a plugin's optional `validate` hook.
18
+ * Returns the rejection message if the hook rejects, or `undefined` if accepted.
19
+ */
20
+ export declare function runPluginValidate(plugin: PluginDefinition, element: Record<string, unknown>): string | undefined;
21
+ /**
22
+ * Run a plugin's optional `loadAsset` hook and return the resulting PDFImage key
23
+ * if an image was embedded.
24
+ *
25
+ * Callers are responsible for setting `imageMap.set(key, image)` with the returned image.
26
+ */
27
+ export declare function runPluginLoadAsset(plugin: PluginDefinition, element: Record<string, unknown>, pdfDoc: import('@cantoo/pdf-lib').PDFDocument, contentWidth: number, contentIndex: number): Promise<{
28
+ key: string;
29
+ image: import('@cantoo/pdf-lib').PDFImage;
30
+ } | undefined>;
31
+ /**
32
+ * Run a plugin's `measure` hook and wrap the result into a `MeasuredBlock`.
33
+ */
34
+ export declare function runPluginMeasure(plugin: PluginDefinition, element: Record<string, unknown>, context: PluginMeasureContext, pluginImageKey?: string): Promise<MeasuredBlock>;
35
+ /**
36
+ * Run a plugin's `render` hook with the fully-assembled render context.
37
+ * Wraps thrown errors in RENDER_FAILED so callers see a consistent error type.
38
+ */
39
+ export declare function runPluginRender(plugin: PluginDefinition, block: MeasuredBlock, pdfPage: import('@cantoo/pdf-lib').PDFPage, pdfDoc: import('@cantoo/pdf-lib').PDFDocument, x: number, y: number, geo: PageGeometry, imageMap: import('./types-internal.js').ImageMap): void;
40
+ //# sourceMappingURL=plugin-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-registry.d.ts","sourceRoot":"","sources":["../src/plugin-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAA4C,MAAM,mBAAmB,CAAA;AACzH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAKtE;;;GAGG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,WAAW,EAAE,MAAM,GAClB,gBAAgB,GAAG,SAAS,CAE9B;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,MAAM,GAAG,SAAS,CAKpB;AAID;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,MAAM,EAAE,OAAO,iBAAiB,EAAE,WAAW,EAC7C,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,iBAAiB,EAAE,QAAQ,CAAA;CAAE,GAAG,SAAS,CAAC,CAMjF;AAID;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,OAAO,EAAE,oBAAoB,EAC7B,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,aAAa,CAAC,CA+BxB;AAID;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,OAAO,iBAAiB,EAAE,OAAO,EAC1C,MAAM,EAAE,OAAO,iBAAiB,EAAE,WAAW,EAC7C,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,GAAG,EAAE,YAAY,EACjB,QAAQ,EAAE,OAAO,qBAAqB,EAAE,QAAQ,GAC/C,IAAI,CAwBN"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Plugin registry — orchestration helpers for the 4 injection points.
3
+ *
4
+ * All functions are pure (no module-level state). The plugin list is
5
+ * threaded through the pipeline via RenderOptions and passed explicitly.
6
+ *
7
+ * @internal Not part of the public API. Import PluginDefinition from plugin-types.ts.
8
+ */
9
+ import { PretextPdfError } from './errors.js';
10
+ // ─── Lookup ───────────────────────────────────────────────────────────────────
11
+ /**
12
+ * Return the first plugin whose `type` matches `elementType`, or `undefined`.
13
+ * @internal
14
+ */
15
+ export function findPlugin(plugins, elementType) {
16
+ return plugins.find(p => p.type === elementType);
17
+ }
18
+ // ─── Stage 1: validate ────────────────────────────────────────────────────────
19
+ /**
20
+ * Run a plugin's optional `validate` hook.
21
+ * Returns the rejection message if the hook rejects, or `undefined` if accepted.
22
+ */
23
+ export function runPluginValidate(plugin, element) {
24
+ if (!plugin.validate)
25
+ return undefined;
26
+ const result = plugin.validate(element);
27
+ // Normalize: only non-empty strings are rejections; '' and void are acceptance
28
+ return typeof result === 'string' && result.length > 0 ? result : undefined;
29
+ }
30
+ // ─── Stage 2b: load assets ────────────────────────────────────────────────────
31
+ /**
32
+ * Run a plugin's optional `loadAsset` hook and return the resulting PDFImage key
33
+ * if an image was embedded.
34
+ *
35
+ * Callers are responsible for setting `imageMap.set(key, image)` with the returned image.
36
+ */
37
+ export async function runPluginLoadAsset(plugin, element, pdfDoc, contentWidth, contentIndex) {
38
+ if (!plugin.loadAsset)
39
+ return undefined;
40
+ const image = await plugin.loadAsset(element, pdfDoc, contentWidth);
41
+ if (!image)
42
+ return undefined;
43
+ const key = `${plugin.type}-${contentIndex}`;
44
+ return { key, image };
45
+ }
46
+ // ─── Stage 3: measure ─────────────────────────────────────────────────────────
47
+ /**
48
+ * Run a plugin's `measure` hook and wrap the result into a `MeasuredBlock`.
49
+ */
50
+ export async function runPluginMeasure(plugin, element, context, pluginImageKey) {
51
+ let result;
52
+ try {
53
+ result = await plugin.measure(element, context);
54
+ }
55
+ catch (err) {
56
+ throw new PretextPdfError('RENDER_FAILED', `Plugin '${plugin.type}' measure hook threw: ${err instanceof Error ? err.message : String(err)}`);
57
+ }
58
+ if (typeof result.height !== 'number' || !isFinite(result.height) || result.height < 0) {
59
+ throw new PretextPdfError('RENDER_FAILED', `Plugin '${plugin.type}' measure hook returned invalid height: ${result.height}. Must be a finite non-negative number.`);
60
+ }
61
+ const block = {
62
+ element: element,
63
+ height: result.height,
64
+ lines: [],
65
+ fontSize: 0,
66
+ lineHeight: 0,
67
+ fontKey: '',
68
+ spaceAfter: result.spaceAfter ?? 0,
69
+ spaceBefore: result.spaceBefore ?? 0,
70
+ pluginData: result.pluginData,
71
+ };
72
+ if (pluginImageKey !== undefined)
73
+ block.pluginImageKey = pluginImageKey;
74
+ return block;
75
+ }
76
+ // ─── Stage 5: render ──────────────────────────────────────────────────────────
77
+ /**
78
+ * Run a plugin's `render` hook with the fully-assembled render context.
79
+ * Wraps thrown errors in RENDER_FAILED so callers see a consistent error type.
80
+ */
81
+ export function runPluginRender(plugin, block, pdfPage, pdfDoc, x, y, geo, imageMap) {
82
+ const pdfImage = block.pluginImageKey ? imageMap.get(block.pluginImageKey) : undefined;
83
+ const ctx = {
84
+ element: block.element,
85
+ pdfPage,
86
+ pdfDoc,
87
+ pageWidth: geo.pageWidth,
88
+ pageHeight: geo.pageHeight,
89
+ margins: geo.margins,
90
+ x,
91
+ y,
92
+ width: geo.contentWidth,
93
+ height: block.height,
94
+ pluginData: block.pluginData,
95
+ ...(pdfImage !== undefined ? { pdfImage } : {}),
96
+ };
97
+ try {
98
+ plugin.render(ctx);
99
+ }
100
+ catch (err) {
101
+ throw new PretextPdfError('RENDER_FAILED', `Plugin '${plugin.type}' render hook threw: ${err instanceof Error ? err.message : String(err)}`);
102
+ }
103
+ }
104
+ //# sourceMappingURL=plugin-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-registry.js","sourceRoot":"","sources":["../src/plugin-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE7C,iFAAiF;AAEjF;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,OAA2B,EAC3B,WAAmB;IAEnB,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAA;AAClD,CAAC;AAED,iFAAiF;AAEjF;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAwB,EACxB,OAAgC;IAEhC,IAAI,CAAC,MAAM,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAA;IACtC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;IACvC,+EAA+E;IAC/E,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAA;AAC7E,CAAC;AAED,iFAAiF;AAEjF;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAwB,EACxB,OAAgC,EAChC,MAA6C,EAC7C,YAAoB,EACpB,YAAoB;IAEpB,IAAI,CAAC,MAAM,CAAC,SAAS;QAAE,OAAO,SAAS,CAAA;IACvC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,CAAA;IACnE,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAA;IAC5B,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,IAAI,IAAI,YAAY,EAAE,CAAA;IAC5C,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAA;AACvB,CAAC;AAED,iFAAiF;AAEjF;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAwB,EACxB,OAAgC,EAChC,OAA6B,EAC7B,cAAuB;IAEvB,IAAI,MAA2B,CAAA;IAC/B,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IACjD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,eAAe,CACvB,eAAe,EACf,WAAW,MAAM,CAAC,IAAI,yBAAyB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAClG,CAAA;IACH,CAAC;IAED,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvF,MAAM,IAAI,eAAe,CACvB,eAAe,EACf,WAAW,MAAM,CAAC,IAAI,2CAA2C,MAAM,CAAC,MAAM,yCAAyC,CACxH,CAAA;IACH,CAAC;IAED,MAAM,KAAK,GAAkB;QAC3B,OAAO,EAAE,OAA8C;QACvD,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,CAAC;QACX,UAAU,EAAE,CAAC;QACb,OAAO,EAAE,EAAE;QACX,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,CAAC;QAClC,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,CAAC;QACpC,UAAU,EAAE,MAAM,CAAC,UAAU;KAC9B,CAAA;IACD,IAAI,cAAc,KAAK,SAAS;QAAE,KAAK,CAAC,cAAc,GAAG,cAAc,CAAA;IACvE,OAAO,KAAK,CAAA;AACd,CAAC;AAED,iFAAiF;AAEjF;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAwB,EACxB,KAAoB,EACpB,OAA0C,EAC1C,MAA6C,EAC7C,CAAS,EACT,CAAS,EACT,GAAiB,EACjB,QAAgD;IAEhD,MAAM,QAAQ,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACtF,MAAM,GAAG,GAAwB;QAC/B,OAAO,EAAE,KAAK,CAAC,OAA6C;QAC5D,OAAO;QACP,MAAM;QACN,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,CAAC;QACD,CAAC;QACD,KAAK,EAAE,GAAG,CAAC,YAAY;QACvB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChD,CAAA;IACD,IAAI,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACpB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,eAAe,CACvB,eAAe,EACf,WAAW,MAAM,CAAC,IAAI,wBAAwB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACjG,CAAA;IACH,CAAC;AACH,CAAC"}