@jasy/pdf 1.0.0-alpha.1 → 1.0.0-alpha.10

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 (275) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +35 -21
  3. package/dist/api/args.d.ts +1 -1
  4. package/dist/api/args.js +2 -5
  5. package/dist/api/color.d.ts +4 -4
  6. package/dist/api/color.js +11 -17
  7. package/dist/api/content.d.ts +20 -13
  8. package/dist/api/content.js +44 -29
  9. package/dist/api/descriptor.d.ts +2 -2
  10. package/dist/api/descriptor.js +121 -31
  11. package/dist/api/dimension.d.ts +76 -0
  12. package/dist/api/dimension.js +43 -0
  13. package/dist/api/font-url.d.ts +28 -0
  14. package/dist/api/font-url.js +134 -0
  15. package/dist/api/forms.d.ts +347 -0
  16. package/dist/api/forms.js +189 -0
  17. package/dist/api/gradient.d.ts +53 -0
  18. package/dist/api/gradient.js +97 -0
  19. package/dist/api/index.d.ts +13 -8
  20. package/dist/api/index.js +13 -24
  21. package/dist/api/insets.d.ts +19 -12
  22. package/dist/api/insets.js +19 -12
  23. package/dist/api/layout.d.ts +149 -25
  24. package/dist/api/layout.js +182 -51
  25. package/dist/api/page-builder.d.ts +40 -0
  26. package/dist/api/page-builder.js +44 -0
  27. package/dist/api/structure.d.ts +100 -13
  28. package/dist/api/structure.js +179 -88
  29. package/dist/api/table.d.ts +5 -5
  30. package/dist/api/table.js +38 -26
  31. package/dist/api/text.d.ts +90 -4
  32. package/dist/api/text.js +116 -31
  33. package/dist/assets/font-data.d.ts +2 -0
  34. package/dist/assets/font-data.js +21 -0
  35. package/dist/assets/font-data.ts +37 -0
  36. package/dist/common/color.js +1 -5
  37. package/dist/constants/page-sizes.js +3 -6
  38. package/dist/constants/pdf-parts.js +1 -4
  39. package/dist/crypto/legacy-handler.d.ts +43 -0
  40. package/dist/crypto/legacy-handler.js +145 -0
  41. package/dist/crypto/security-handler.d.ts +62 -0
  42. package/dist/crypto/security-handler.js +155 -0
  43. package/dist/crypto/webcrypto.d.ts +11 -0
  44. package/dist/crypto/webcrypto.js +62 -0
  45. package/dist/edit/acroform-reader.d.ts +70 -0
  46. package/dist/edit/acroform-reader.js +126 -0
  47. package/dist/edit/appearance.d.ts +47 -0
  48. package/dist/edit/appearance.js +171 -0
  49. package/dist/edit/document.d.ts +145 -0
  50. package/dist/edit/document.js +642 -0
  51. package/dist/edit/fill.d.ts +65 -0
  52. package/dist/edit/fill.js +424 -0
  53. package/dist/edit/flatten.d.ts +54 -0
  54. package/dist/edit/flatten.js +462 -0
  55. package/dist/edit/index.d.ts +20 -0
  56. package/dist/edit/index.js +18 -0
  57. package/dist/edit/lexer.d.ts +30 -0
  58. package/dist/edit/lexer.js +285 -0
  59. package/dist/edit/objects.d.ts +68 -0
  60. package/dist/edit/objects.js +83 -0
  61. package/dist/edit/writer.d.ts +60 -0
  62. package/dist/edit/writer.js +283 -0
  63. package/dist/elements/container-element.d.ts +50 -5
  64. package/dist/elements/container-element.js +100 -39
  65. package/dist/elements/forms/checkbox-element.d.ts +51 -0
  66. package/dist/elements/forms/checkbox-element.js +48 -0
  67. package/dist/elements/forms/choice-element.d.ts +53 -0
  68. package/dist/elements/forms/choice-element.js +59 -0
  69. package/dist/elements/forms/push-button-element.d.ts +56 -0
  70. package/dist/elements/forms/push-button-element.js +49 -0
  71. package/dist/elements/forms/radio-element.d.ts +52 -0
  72. package/dist/elements/forms/radio-element.js +49 -0
  73. package/dist/elements/forms/signature-element.d.ts +54 -0
  74. package/dist/elements/forms/signature-element.js +47 -0
  75. package/dist/elements/forms/text-field-element.d.ts +61 -0
  76. package/dist/elements/forms/text-field-element.js +54 -0
  77. package/dist/elements/image-element.d.ts +58 -6
  78. package/dist/elements/image-element.js +145 -111
  79. package/dist/elements/index.d.ts +26 -10
  80. package/dist/elements/index.js +26 -28
  81. package/dist/elements/layout/anchor-element.d.ts +23 -0
  82. package/dist/elements/layout/anchor-element.js +27 -0
  83. package/dist/elements/layout/bookmark-element.d.ts +27 -0
  84. package/dist/elements/layout/bookmark-element.js +30 -0
  85. package/dist/elements/layout/default-text-style-element.d.ts +30 -0
  86. package/dist/elements/layout/default-text-style-element.js +47 -0
  87. package/dist/elements/layout/deferred-element.d.ts +3 -3
  88. package/dist/elements/layout/deferred-element.js +4 -8
  89. package/dist/elements/layout/expanded-element.d.ts +6 -4
  90. package/dist/elements/layout/expanded-element.js +22 -17
  91. package/dist/elements/layout/keep-together-element.d.ts +42 -0
  92. package/dist/elements/layout/keep-together-element.js +85 -0
  93. package/dist/elements/layout/link-element.d.ts +33 -0
  94. package/dist/elements/layout/link-element.js +39 -0
  95. package/dist/elements/layout/padding-element.d.ts +10 -5
  96. package/dist/elements/layout/padding-element.js +25 -16
  97. package/dist/elements/layout/page-break-element.d.ts +17 -0
  98. package/dist/elements/layout/page-break-element.js +24 -0
  99. package/dist/elements/layout/page-builder-element.d.ts +32 -0
  100. package/dist/elements/layout/page-builder-element.js +45 -0
  101. package/dist/elements/layout/positioned-element.d.ts +47 -0
  102. package/dist/elements/layout/positioned-element.js +73 -0
  103. package/dist/elements/layout/repeating-header-element.d.ts +3 -3
  104. package/dist/elements/layout/repeating-header-element.js +8 -12
  105. package/dist/elements/layout/rotated-box-element.d.ts +35 -0
  106. package/dist/elements/layout/rotated-box-element.js +54 -0
  107. package/dist/elements/layout/rotated-element.d.ts +32 -0
  108. package/dist/elements/layout/rotated-element.js +41 -0
  109. package/dist/elements/layout/sized-container-element.d.ts +2 -2
  110. package/dist/elements/layout/sized-container-element.js +6 -11
  111. package/dist/elements/layout/struct-group.d.ts +28 -0
  112. package/dist/elements/layout/struct-group.js +42 -0
  113. package/dist/elements/line-element.d.ts +3 -3
  114. package/dist/elements/line-element.js +5 -10
  115. package/dist/elements/page-element.d.ts +26 -6
  116. package/dist/elements/page-element.js +72 -27
  117. package/dist/elements/pdf-document-element.d.ts +10 -4
  118. package/dist/elements/pdf-document-element.js +11 -10
  119. package/dist/elements/pdf-element.d.ts +150 -4
  120. package/dist/elements/pdf-element.js +138 -20
  121. package/dist/elements/rectangle-element.d.ts +52 -11
  122. package/dist/elements/rectangle-element.js +129 -36
  123. package/dist/elements/row-element.d.ts +45 -4
  124. package/dist/elements/row-element.js +66 -18
  125. package/dist/elements/text-element.d.ts +132 -11
  126. package/dist/elements/text-element.js +206 -56
  127. package/dist/forms/acroform.d.ts +63 -0
  128. package/dist/forms/acroform.js +427 -0
  129. package/dist/forms/appearance.d.ts +58 -0
  130. package/dist/forms/appearance.js +204 -0
  131. package/dist/forms/field.d.ts +164 -0
  132. package/dist/forms/field.js +1 -0
  133. package/dist/forms/pdf.d.ts +19 -0
  134. package/dist/forms/pdf.js +28 -0
  135. package/dist/index.d.ts +3 -3
  136. package/dist/index.js +3 -19
  137. package/dist/ir/display-list.d.ts +197 -8
  138. package/dist/ir/display-list.js +8 -2
  139. package/dist/layout/alignment.d.ts +10 -0
  140. package/dist/layout/alignment.js +6 -0
  141. package/dist/layout/box-constraints.d.ts +61 -0
  142. package/dist/layout/box-constraints.js +88 -6
  143. package/dist/layout/collect-images.d.ts +9 -0
  144. package/dist/layout/collect-images.js +34 -0
  145. package/dist/layout/fragmentation.d.ts +20 -1
  146. package/dist/layout/fragmentation.js +74 -18
  147. package/dist/layout/insets.d.ts +28 -0
  148. package/dist/layout/insets.js +20 -0
  149. package/dist/platform/browser-fs.d.ts +2 -0
  150. package/dist/platform/browser-fs.js +9 -0
  151. package/dist/platform/browser-image.d.ts +5 -0
  152. package/dist/platform/browser-image.js +13 -0
  153. package/dist/platform/node-fs.d.ts +2 -0
  154. package/dist/platform/node-fs.js +10 -0
  155. package/dist/platform/node-image.d.ts +5 -0
  156. package/dist/platform/node-image.js +9 -0
  157. package/dist/renderer/anchor-renderer.d.ts +6 -0
  158. package/dist/renderer/anchor-renderer.js +11 -0
  159. package/dist/renderer/bookmark-renderer.d.ts +6 -0
  160. package/dist/renderer/bookmark-renderer.js +11 -0
  161. package/dist/renderer/container-renderer.d.ts +3 -3
  162. package/dist/renderer/container-renderer.js +11 -28
  163. package/dist/renderer/default-text-style-renderer.d.ts +6 -0
  164. package/dist/renderer/default-text-style-renderer.js +10 -0
  165. package/dist/renderer/deferred-renderer.d.ts +3 -3
  166. package/dist/renderer/deferred-renderer.js +8 -23
  167. package/dist/renderer/emoji-image.d.ts +9 -0
  168. package/dist/renderer/emoji-image.js +68 -0
  169. package/dist/renderer/expanded-renderer.d.ts +3 -3
  170. package/dist/renderer/expanded-renderer.js +6 -21
  171. package/dist/renderer/form-field-renderer.d.ts +20 -0
  172. package/dist/renderer/form-field-renderer.js +12 -0
  173. package/dist/renderer/image-renderer.d.ts +4 -4
  174. package/dist/renderer/image-renderer.js +86 -75
  175. package/dist/renderer/index.d.ts +10 -10
  176. package/dist/renderer/index.js +10 -26
  177. package/dist/renderer/keep-together-renderer.d.ts +10 -0
  178. package/dist/renderer/keep-together-renderer.js +12 -0
  179. package/dist/renderer/line-renderer.d.ts +3 -3
  180. package/dist/renderer/line-renderer.js +13 -28
  181. package/dist/renderer/link-renderer.d.ts +6 -0
  182. package/dist/renderer/link-renderer.js +11 -0
  183. package/dist/renderer/padding-renderer.d.ts +3 -3
  184. package/dist/renderer/padding-renderer.js +6 -21
  185. package/dist/renderer/page-break-renderer.d.ts +14 -0
  186. package/dist/renderer/page-break-renderer.js +15 -0
  187. package/dist/renderer/page-builder-renderer.d.ts +6 -0
  188. package/dist/renderer/page-builder-renderer.js +11 -0
  189. package/dist/renderer/page-renderer.d.ts +2 -2
  190. package/dist/renderer/page-renderer.js +115 -77
  191. package/dist/renderer/pdf-backend.d.ts +31 -4
  192. package/dist/renderer/pdf-backend.js +287 -49
  193. package/dist/renderer/pdf-config.js +4 -7
  194. package/dist/renderer/pdf-document-class.d.ts +5 -5
  195. package/dist/renderer/pdf-document-class.js +27 -41
  196. package/dist/renderer/pdf-document-renderer.d.ts +19 -10
  197. package/dist/renderer/pdf-document-renderer.js +135 -91
  198. package/dist/renderer/pdf-renderer.d.ts +2 -2
  199. package/dist/renderer/pdf-renderer.js +165 -90
  200. package/dist/renderer/positioned-renderer.d.ts +6 -0
  201. package/dist/renderer/positioned-renderer.js +10 -0
  202. package/dist/renderer/rectangle-renderer.d.ts +3 -3
  203. package/dist/renderer/rectangle-renderer.js +49 -44
  204. package/dist/renderer/repeating-header-renderer.d.ts +3 -3
  205. package/dist/renderer/repeating-header-renderer.js +10 -26
  206. package/dist/renderer/rotated-renderer.d.ts +7 -0
  207. package/dist/renderer/rotated-renderer.js +29 -0
  208. package/dist/renderer/row-renderer.d.ts +3 -3
  209. package/dist/renderer/row-renderer.js +11 -28
  210. package/dist/renderer/struct-group-renderer.d.ts +10 -0
  211. package/dist/renderer/struct-group-renderer.js +24 -0
  212. package/dist/renderer/text-renderer.d.ts +9 -5
  213. package/dist/renderer/text-renderer.js +437 -89
  214. package/dist/text/advance.d.ts +34 -0
  215. package/dist/text/advance.js +45 -0
  216. package/dist/text/arabic.d.ts +20 -0
  217. package/dist/text/arabic.js +101 -0
  218. package/dist/text/bidi.d.ts +33 -0
  219. package/dist/text/bidi.js +94 -0
  220. package/dist/text/emoji-codepoints.d.ts +1 -0
  221. package/dist/text/emoji-codepoints.js +29 -0
  222. package/dist/text/font-fallback.d.ts +25 -0
  223. package/dist/text/font-fallback.js +38 -0
  224. package/dist/text/line-breaker.d.ts +50 -12
  225. package/dist/text/line-breaker.js +158 -37
  226. package/dist/text/line-metrics.d.ts +49 -0
  227. package/dist/text/line-metrics.js +57 -0
  228. package/dist/text/orphans-widows.d.ts +27 -0
  229. package/dist/text/orphans-widows.js +53 -0
  230. package/dist/text/shape.d.ts +29 -0
  231. package/dist/text/shape.js +67 -0
  232. package/dist/text/text-decoration.d.ts +50 -0
  233. package/dist/text/text-decoration.js +62 -0
  234. package/dist/text/text-style.d.ts +60 -0
  235. package/dist/text/text-style.js +79 -0
  236. package/dist/utils/afm-parser.d.ts +34 -0
  237. package/dist/utils/afm-parser.js +76 -17
  238. package/dist/utils/bytes.d.ts +24 -0
  239. package/dist/utils/bytes.js +76 -0
  240. package/dist/utils/dest-registry.d.ts +22 -0
  241. package/dist/utils/dest-registry.js +32 -0
  242. package/dist/utils/flex-layout.d.ts +36 -9
  243. package/dist/utils/flex-layout.js +207 -30
  244. package/dist/utils/font-metrics.d.ts +20 -1
  245. package/dist/utils/font-metrics.js +1 -2
  246. package/dist/utils/font-path.js +3 -6
  247. package/dist/utils/gsub.d.ts +36 -0
  248. package/dist/utils/gsub.js +178 -0
  249. package/dist/utils/image-helper.d.ts +6 -5
  250. package/dist/utils/image-helper.js +101 -111
  251. package/dist/utils/inflate.d.ts +16 -0
  252. package/dist/utils/inflate.js +52 -0
  253. package/dist/utils/md5.d.ts +4 -0
  254. package/dist/utils/md5.js +79 -0
  255. package/dist/utils/outline.d.ts +21 -0
  256. package/dist/utils/outline.js +79 -0
  257. package/dist/utils/pdf-object-manager.d.ts +110 -8
  258. package/dist/utils/pdf-object-manager.js +583 -141
  259. package/dist/utils/renderer-registry.d.ts +13 -1
  260. package/dist/utils/renderer-registry.js +23 -6
  261. package/dist/utils/struct-tree.d.ts +44 -0
  262. package/dist/utils/struct-tree.js +129 -0
  263. package/dist/utils/ttf-parser.d.ts +134 -2
  264. package/dist/utils/ttf-parser.js +873 -50
  265. package/dist/utils/ttf-subsetter.d.ts +1 -1
  266. package/dist/utils/ttf-subsetter.js +40 -42
  267. package/dist/utils/ua-xmp.d.ts +9 -0
  268. package/dist/utils/ua-xmp.js +32 -0
  269. package/dist/utils/utf8-to-windows1252-encoder.d.ts +6 -0
  270. package/dist/utils/utf8-to-windows1252-encoder.js +11 -4
  271. package/dist/utils/woff.d.ts +10 -0
  272. package/dist/utils/woff.js +116 -0
  273. package/dist/validators/element-validator.d.ts +2 -2
  274. package/dist/validators/element-validator.js +31 -26
  275. package/package.json +22 -2
@@ -1,19 +1,28 @@
1
- import { PDFDocumentElement } from "../elements/pdf-document-element";
2
- import { LayoutContext } from "../elements/pdf-element";
3
- import { PDFObjectManager } from "../utils/pdf-object-manager";
1
+ import { PDFDocumentElement } from "../elements/pdf-document-element.js";
2
+ import { LayoutContext } from "../elements/pdf-element.js";
3
+ import { PDFObjectManager } from "../utils/pdf-object-manager.js";
4
4
  export declare class PDFDocumentRenderer {
5
5
  static render(document: PDFDocumentElement, objectManager: PDFObjectManager, ctx: LayoutContext): Promise<number>;
6
6
  /**
7
- * Renders one logical page, paginating it into one or more physical pages.
7
+ * The page geometry a physical page will use. `PageElement.calculateLayout` has already folded the
8
+ * document defaults into every page's config by the time we get here (`PDFRenderer` lays the document
9
+ * out before it calls us, and it is our only caller). Merging again is therefore a no-op today - it is
10
+ * here so this stays correct BY CONSTRUCTION rather than by call order: were that order ever changed,
11
+ * an unmerged config would otherwise make `resolvePageSize` fall back to A4 and quietly report the
12
+ * wrong page size.
13
+ */
14
+ private static configOf;
15
+ /**
16
+ * Splits one logical page into the physical pages it will occupy, without rendering.
8
17
  *
9
- * Slice 0 only auto-paginates the common shape - a page whose single child is a
10
- * fragmentation context (a Container). Anything else renders as a single page on the
11
- * unchanged path, so non-overflowing documents stay byte-identical to pre-Slice-0.
18
+ * Only the common shape auto-paginates - a page whose single child is a fragmentation context
19
+ * (a Container). Anything else stays a single page on the unchanged path, so non-overflowing
20
+ * documents keep producing byte-identical output.
12
21
  */
13
- private static renderLogicalPage;
22
+ private static paginateLogicalPage;
14
23
  /**
15
- * Lays out one fragment on a fresh page of the same geometry and renders it. The
16
- * header/footer are attached to every physical page so they repeat; `PageElement`
24
+ * Lays out one physical page and renders it. A fragment gets a fresh `PageElement` of the same
25
+ * geometry; the header/footer are attached to every physical page so they repeat, and `PageElement`
17
26
  * re-places them and the body in the band between.
18
27
  */
19
28
  private static renderPhysicalPage;
@@ -1,104 +1,148 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.PDFDocumentRenderer = void 0;
13
- const page_element_1 = require("../elements/page-element");
14
- const box_constraints_1 = require("../layout/box-constraints");
15
- const fragmentation_1 = require("../layout/fragmentation");
16
- const page_renderer_1 = require("./page-renderer");
17
- class PDFDocumentRenderer {
18
- static render(document, objectManager, ctx) {
19
- return __awaiter(this, void 0, void 0, function* () {
20
- const pageNumbers = [];
21
- // Add the pages object first... we need its object number (resources). The count is
22
- // a placeholder; it is replaced below with the real (post-pagination) page count.
23
- const pagesObject = `<< /Type /Pages /Kids [] /Count ${document.getProps().children.length} >>`;
24
- const pagesObjectNumber = objectManager.addObject(pagesObject);
25
- // Now set the given object number all its childs
26
- objectManager.setParentObjectNumber(pagesObjectNumber);
27
- // The page driver: each logical PageElement may produce SEVERAL physical PDF pages
28
- // when its content overflows (Slice 0: whole children reflow to the next page).
29
- for (let page of document.getProps().children) {
30
- const numbers = yield PDFDocumentRenderer.renderLogicalPage(page, objectManager, ctx);
31
- pageNumbers.push(...numbers);
32
- }
33
- // We must update the pages object with the real physical page numbers and count.
34
- const updatedPagesObject = `<< /Type /Pages /Kids [${pageNumbers
35
- .map((num) => `${num} 0 R`)
36
- .join(" ")}] /Count ${pageNumbers.length} >>`;
37
- // Now we must replace it in the object manager
38
- objectManager.replaceObject(pagesObjectNumber, updatedPagesObject);
39
- return pagesObjectNumber;
40
- });
1
+ import { layoutPageBands, pageFrame, PageElement, resolvePageSize, } from "../elements/page-element.js";
2
+ import { BoxConstraints } from "../layout/box-constraints.js";
3
+ import { isFragmentable, reportOverflow } from "../layout/fragmentation.js";
4
+ import { PageRenderer } from "./page-renderer.js";
5
+ export class PDFDocumentRenderer {
6
+ static async render(document, objectManager, ctx) {
7
+ // Add the pages object first... we need its object number (resources). The count is
8
+ // a placeholder; it is replaced below with the real (post-pagination) page count.
9
+ const pagesObject = `<< /Type /Pages /Kids [] /Count ${document.getProps().children.length} >>`;
10
+ const pagesObjectNumber = objectManager.addObject(pagesObject);
11
+ // Now set the given object number all its childs
12
+ objectManager.setParentObjectNumber(pagesObjectNumber);
13
+ // Pass A - paginate the whole document WITHOUT drawing anything. Fragmentation is synchronous and
14
+ // pure (`{ fitted, remainder }`), so the full list of physical pages can be settled up front. Only
15
+ // then is the total page count known, which is what page numbers in a header/footer need.
16
+ const physicalPages = [];
17
+ // Where each sheet sits WITHIN the logical page it came from. Pass A already knows: one logical
18
+ // `Page` element produces one run of sheets, so the run's length is that section's total.
19
+ const section = [];
20
+ for (const page of document.getProps().children) {
21
+ const sheets = PDFDocumentRenderer.paginateLogicalPage(page, ctx);
22
+ sheets.forEach((_, i) => section.push({ number: i + 1, total: sheets.length }));
23
+ physicalPages.push(...sheets);
24
+ }
25
+ // Pass B - draw them, in order, so the emitted page objects keep their previous numbering. Each page
26
+ // is laid out again with its own PageInfo, which is how a `PageBuilder` anywhere in the tree (header,
27
+ // footer or body) learns its final page number and the now-known document total.
28
+ const pageNumbers = [];
29
+ for (const [index, physical] of physicalPages.entries()) {
30
+ const pageInfo = {
31
+ pageNumber: index + 1,
32
+ pageCount: physicalPages.length,
33
+ subPageNumber: section[index].number,
34
+ subPageTotalPages: section[index].total,
35
+ pageSize: resolvePageSize(PDFDocumentRenderer.configOf(physical, ctx)),
36
+ };
37
+ pageNumbers.push(await PDFDocumentRenderer.renderPhysicalPage(physical, objectManager, {
38
+ ...ctx,
39
+ pageInfo,
40
+ }));
41
+ }
42
+ // We must update the pages object with the real physical page numbers and count.
43
+ const updatedPagesObject = `<< /Type /Pages /Kids [${pageNumbers
44
+ .map((num) => `${num} 0 R`)
45
+ .join(" ")}] /Count ${pageNumbers.length} >>`;
46
+ // Now we must replace it in the object manager
47
+ objectManager.replaceObject(pagesObjectNumber, updatedPagesObject);
48
+ return pagesObjectNumber;
41
49
  }
42
50
  /**
43
- * Renders one logical page, paginating it into one or more physical pages.
51
+ * The page geometry a physical page will use. `PageElement.calculateLayout` has already folded the
52
+ * document defaults into every page's config by the time we get here (`PDFRenderer` lays the document
53
+ * out before it calls us, and it is our only caller). Merging again is therefore a no-op today - it is
54
+ * here so this stays correct BY CONSTRUCTION rather than by call order: were that order ever changed,
55
+ * an unmerged config would otherwise make `resolvePageSize` fall back to A4 and quietly report the
56
+ * wrong page size.
57
+ */
58
+ static configOf(physical, ctx) {
59
+ const config = physical.kind === "whole" ? physical.page.getProps().config : physical.config;
60
+ return { ...ctx.pageConfig, ...config };
61
+ }
62
+ /**
63
+ * Splits one logical page into the physical pages it will occupy, without rendering.
44
64
  *
45
- * Slice 0 only auto-paginates the common shape - a page whose single child is a
46
- * fragmentation context (a Container). Anything else renders as a single page on the
47
- * unchanged path, so non-overflowing documents stay byte-identical to pre-Slice-0.
65
+ * Only the common shape auto-paginates - a page whose single child is a fragmentation context
66
+ * (a Container). Anything else stays a single page on the unchanged path, so non-overflowing
67
+ * documents keep producing byte-identical output.
48
68
  */
49
- static renderLogicalPage(page, objectManager, ctx) {
50
- return __awaiter(this, void 0, void 0, function* () {
51
- const { children, config, header, footer } = page.getProps();
52
- if (children.length !== 1 || !(0, fragmentation_1.isFragmentable)(children[0])) {
53
- return [yield page_renderer_1.PageRenderer.render(page, objectManager)];
69
+ static paginateLogicalPage(page, ctx) {
70
+ const { children, config, header, footer } = page.getProps();
71
+ if (children.length !== 1 || !isFragmentable(children[0])) {
72
+ return [{ kind: "whole", page }];
73
+ }
74
+ // Header/footer repeat on every physical page, so the body only ever flows into the
75
+ // band between them. Resolve that band once (config is already merged by pass 1).
76
+ // The frame is a throwaway: this pass only wants the band HEIGHTS, and an out-of-flow child
77
+ // contributes none. It exists because a `Positioned` in a band demands one; pass 2 builds the
78
+ // real frame (in `PageElement`) and drains it.
79
+ const pageCtx = {
80
+ metrics: ctx.metrics,
81
+ pageConfig: config,
82
+ textStyle: ctx.textStyle,
83
+ onOverflow: ctx.onOverflow,
84
+ frame: pageFrame(config),
85
+ };
86
+ const { bodyWidth: width, bodyHeight: height } = layoutPageBands(config, header, footer, pageCtx);
87
+ // A `keepTogether` group compares its height against a FULL fresh page (this band), so thread it in.
88
+ pageCtx.pageBodyHeight = height;
89
+ const pages = [];
90
+ let region = children[0];
91
+ let isFirstRegion = true;
92
+ while (region) {
93
+ if (!isFragmentable(region)) {
94
+ // A non-fragmentable remainder is placed whole on its own page.
95
+ pages.push({ kind: "fragment", config, content: region, header, footer });
96
+ break;
54
97
  }
55
- // Header/footer repeat on every physical page, so the body only ever flows into the
56
- // band between them. Resolve that band once (config is already merged by pass 1).
57
- const pageCtx = { metrics: ctx.metrics, pageConfig: config };
58
- const { bodyWidth: width, bodyHeight: height } = (0, page_element_1.layoutPageBands)(config, header, footer, pageCtx);
59
- const numbers = [];
60
- let region = children[0];
61
- let isFirstRegion = true;
62
- while (region) {
63
- if (!(0, fragmentation_1.isFragmentable)(region)) {
64
- // A non-fragmentable remainder is placed whole on its own page.
65
- numbers.push(yield PDFDocumentRenderer.renderPhysicalPage(config, region, header, footer, objectManager, ctx));
66
- break;
67
- }
68
- const { fitted, remainder } = region.fragment(height, width, ctx);
69
- // Everything fits on one page: render the ORIGINAL page so output is unchanged.
70
- // Measuring inside fragment() laid the children out at the origin to size them, so
71
- // re-run the page layout first to restore their real positions (deterministic).
72
- if (isFirstRegion && remainder === null) {
73
- page.calculateLayout(new box_constraints_1.BoxConstraints(), { x: 0, y: 0 }, ctx);
74
- numbers.push(yield page_renderer_1.PageRenderer.render(page, objectManager));
75
- break;
76
- }
77
- if (fitted) {
78
- numbers.push(yield PDFDocumentRenderer.renderPhysicalPage(config, fitted, header, footer, objectManager, ctx));
79
- }
80
- region = remainder;
81
- isFirstRegion = false;
98
+ // pageCtx, not the document ctx: a descendant that reads `pageConfig` while being measured (a
99
+ // `PageBuilder` sizing its provisional build) must see THIS page's geometry, not the document default.
100
+ const { fitted, remainder, forceBreak } = region.fragment(height, width, pageCtx);
101
+ // Everything fits on one page: keep the ORIGINAL page so output is unchanged. Measuring inside
102
+ // fragment() left its children at the measuring origin; pass B lays the page out again, which
103
+ // restores their real positions (layout is deterministic). A consumed trailing `PageBreak`
104
+ // (forceBreak with no remainder) is the exception: render the packed `fitted` instead, which no
105
+ // longer holds the break marker, so the original with the break does not leak into the render pass.
106
+ if (isFirstRegion && remainder === null && !forceBreak) {
107
+ pages.push({ kind: "whole", page });
108
+ break;
82
109
  }
83
- return numbers;
84
- });
110
+ // TERMINATION GUARANTEE. Every page here has the full body height, so `fitted === null` means
111
+ // nothing fit even on a whole page - the region did not get smaller. Advancing to the
112
+ // (identical) remainder would loop forever. So we stop: place the region whole (clipped to the
113
+ // page) and surface it per the overflow policy. A step that shrinks nothing ends the loop, for
114
+ // ANY region - an oversized unbreakable block, a future keep-together group, or an engine bug.
115
+ if (fitted === null) {
116
+ // Measure the region's true height for the message (rare path, so the extra pass is free);
117
+ // pass B lays it out again, so this mutation does not leak.
118
+ const needed = region.calculateLayout(BoxConstraints.loose(width, Infinity), { x: 0, y: 0 }, pageCtx).height;
119
+ reportOverflow(region, needed, height, pageCtx.onOverflow ?? "ignore");
120
+ pages.push({ kind: "fragment", config, content: region, header, footer });
121
+ break;
122
+ }
123
+ pages.push({ kind: "fragment", config, content: fitted, header, footer });
124
+ region = remainder;
125
+ isFirstRegion = false;
126
+ }
127
+ return pages;
85
128
  }
86
129
  /**
87
- * Lays out one fragment on a fresh page of the same geometry and renders it. The
88
- * header/footer are attached to every physical page so they repeat; `PageElement`
130
+ * Lays out one physical page and renders it. A fragment gets a fresh `PageElement` of the same
131
+ * geometry; the header/footer are attached to every physical page so they repeat, and `PageElement`
89
132
  * re-places them and the body in the band between.
90
133
  */
91
- static renderPhysicalPage(config, content, header, footer, objectManager, ctx) {
92
- return __awaiter(this, void 0, void 0, function* () {
93
- const physicalPage = new page_element_1.PageElement({
94
- config,
95
- header,
96
- footer,
97
- children: [content],
98
- });
99
- physicalPage.calculateLayout(new box_constraints_1.BoxConstraints(), { x: 0, y: 0 }, ctx);
100
- return page_renderer_1.PageRenderer.render(physicalPage, objectManager);
134
+ static async renderPhysicalPage(physical, objectManager, ctx) {
135
+ if (physical.kind === "whole") {
136
+ physical.page.calculateLayout(new BoxConstraints(), { x: 0, y: 0 }, ctx);
137
+ return PageRenderer.render(physical.page, objectManager);
138
+ }
139
+ const page = new PageElement({
140
+ config: physical.config,
141
+ header: physical.header,
142
+ footer: physical.footer,
143
+ children: [physical.content],
101
144
  });
145
+ page.calculateLayout(new BoxConstraints(), { x: 0, y: 0 }, ctx);
146
+ return PageRenderer.render(page, objectManager);
102
147
  }
103
148
  }
104
- exports.PDFDocumentRenderer = PDFDocumentRenderer;
@@ -1,5 +1,5 @@
1
- import { PDFDocumentElement } from "../elements/pdf-document-element";
2
- import { PDFObjectManager } from "../utils/pdf-object-manager";
1
+ import { PDFDocumentElement } from "../elements/pdf-document-element.js";
2
+ import { PDFObjectManager } from "../utils/pdf-object-manager.js";
3
3
  export declare class PDFRenderer {
4
4
  static render(document: PDFDocumentElement, objectManager: PDFObjectManager): Promise<string>;
5
5
  }
@@ -1,92 +1,167 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.PDFRenderer = void 0;
13
- const pdf_document_renderer_1 = require("./pdf-document-renderer");
14
- const renderer_registry_1 = require("../utils/renderer-registry");
15
- const elements_1 = require("../elements");
16
- const text_renderer_1 = require("./text-renderer");
17
- const container_element_1 = require("../elements/container-element");
18
- const rectangle_element_1 = require("../elements/rectangle-element");
19
- const row_element_1 = require("../elements/row-element");
20
- const container_renderer_1 = require("./container-renderer");
21
- const rectangle_renderer_1 = require("./rectangle-renderer");
22
- const row_renderer_1 = require("./row-renderer");
23
- const expanded_renderer_1 = require("./expanded-renderer");
24
- const padding_renderer_1 = require("./padding-renderer");
25
- const image_renderer_1 = require("./image-renderer");
26
- const line_renderer_1 = require("./line-renderer");
27
- const repeating_header_element_1 = require("../elements/layout/repeating-header-element");
28
- const repeating_header_renderer_1 = require("./repeating-header-renderer");
29
- const deferred_element_1 = require("../elements/layout/deferred-element");
30
- const deferred_renderer_1 = require("./deferred-renderer");
31
- const box_constraints_1 = require("../layout/box-constraints");
32
- class PDFRenderer {
33
- static render(document, objectManager) {
34
- return __awaiter(this, void 0, void 0, function* () {
35
- // Register all Renderer
36
- renderer_registry_1.RendererRegistry.register(elements_1.TextElement, text_renderer_1.TextRenderer.render);
37
- renderer_registry_1.RendererRegistry.register(container_element_1.ContainerElement, container_renderer_1.ContainerRenderer.render);
38
- renderer_registry_1.RendererRegistry.register(row_element_1.RowElement, row_renderer_1.RowRenderer.render);
39
- renderer_registry_1.RendererRegistry.register(rectangle_element_1.RectangleElement, rectangle_renderer_1.RectangleRenderer.render);
40
- renderer_registry_1.RendererRegistry.register(elements_1.ExpandedElement, expanded_renderer_1.ExpandedRenderer.render);
41
- renderer_registry_1.RendererRegistry.register(elements_1.PaddingElement, padding_renderer_1.PaddingRenderer.render);
42
- renderer_registry_1.RendererRegistry.register(elements_1.ImageElement, image_renderer_1.ImageRenderer.render);
43
- renderer_registry_1.RendererRegistry.register(elements_1.LineElement, line_renderer_1.LineRenderer.render);
44
- renderer_registry_1.RendererRegistry.register(repeating_header_element_1.RepeatingHeaderElement, repeating_header_renderer_1.RepeatingHeaderRenderer.render);
45
- renderer_registry_1.RendererRegistry.register(deferred_element_1.DeferredElement, deferred_renderer_1.DeferredRenderer.render);
46
- let pdfContent = "";
47
- // Header: version line + the PDF/A binary marker (the object manager owns it so its length
48
- // matches the xref offset calculation).
49
- pdfContent += objectManager.getHeader();
50
- // Layout pass: thread the context explicitly. The seed page config is the document
51
- // default; each PageElement overrides it for its own subtree.
52
- const ctx = {
53
- metrics: objectManager,
54
- pageConfig: objectManager.getPDFConfig(),
55
- };
56
- document.calculateLayout(new box_constraints_1.BoxConstraints(), { x: 0, y: 0 }, ctx);
57
- // Render pages and contents (the driver paginates overflowing pages).
58
- yield pdf_document_renderer_1.PDFDocumentRenderer.render(document, objectManager, ctx);
59
- // Add the catalog. XMP metadata (/Metadata) and embedded files (/AF + /Names/EmbeddedFiles)
60
- // are added only when present; with neither, the catalog is byte-identical to before.
61
- const catalogParts = [`/Type /Catalog /Pages ${objectManager.getParentObjectNumber()} 0 R`];
62
- const xmp = objectManager.getXmpMetadata();
63
- if (xmp) {
64
- const metadataObject = objectManager.addObject(`<< /Type /Metadata /Subtype /XML /Length ${xmp.length} >>\nstream\n${xmp}\nendstream`);
65
- catalogParts.push(`/Metadata ${metadataObject} 0 R`);
66
- }
67
- const outputIntent = objectManager.getOutputIntent();
68
- if (outputIntent) {
69
- catalogParts.push(`/OutputIntents [${outputIntent} 0 R]`);
70
- }
71
- const attachments = objectManager.getAttachments();
72
- if (attachments.length > 0) {
73
- const names = attachments.map((a) => `(${a.name}) ${a.filespec} 0 R`).join(" ");
74
- const af = attachments.map((a) => `${a.filespec} 0 R`).join(" ");
75
- catalogParts.push(`/AF [${af}]`, `/Names << /EmbeddedFiles << /Names [${names}] >> >>`);
76
- }
77
- const catalogObject = `<< ${catalogParts.join(" ")} >>`;
78
- objectManager.addObject(catalogObject);
79
- // Now that the render pass has revealed which glyphs each embedded font uses, fill the reserved
80
- // font objects with the subset font program (must happen before the objects are serialized).
81
- objectManager.finalizeCustomFonts();
82
- // Add rendered objects
83
- pdfContent += objectManager.getRenderedObjects();
84
- // Add XRef table and trailer
85
- const startxref = pdfContent.length;
86
- pdfContent += objectManager.getXRefTable();
87
- pdfContent += objectManager.getTrailerAndXRef(startxref);
88
- return pdfContent;
89
- });
1
+ import { PDFDocumentRenderer } from "./pdf-document-renderer.js";
2
+ import { RendererRegistry } from "../utils/renderer-registry.js";
3
+ import { DefaultTextStyleElement, ExpandedElement, ImageElement, LineElement, PaddingElement, TextElement, } from "../elements/index.js";
4
+ import { TextRenderer } from "./text-renderer.js";
5
+ import { ContainerElement } from "../elements/container-element.js";
6
+ import { RectangleElement } from "../elements/rectangle-element.js";
7
+ import { RowElement } from "../elements/row-element.js";
8
+ import { ContainerRenderer } from "./container-renderer.js";
9
+ import { RectangleRenderer } from "./rectangle-renderer.js";
10
+ import { RowRenderer } from "./row-renderer.js";
11
+ import { ExpandedRenderer } from "./expanded-renderer.js";
12
+ import { PaddingRenderer } from "./padding-renderer.js";
13
+ import { DefaultTextStyleRenderer } from "./default-text-style-renderer.js";
14
+ import { ImageRenderer } from "./image-renderer.js";
15
+ import { LineRenderer } from "./line-renderer.js";
16
+ import { RepeatingHeaderElement } from "../elements/layout/repeating-header-element.js";
17
+ import { RepeatingHeaderRenderer } from "./repeating-header-renderer.js";
18
+ import { DeferredElement } from "../elements/layout/deferred-element.js";
19
+ import { DeferredRenderer } from "./deferred-renderer.js";
20
+ import { PositionedElement } from "../elements/layout/positioned-element.js";
21
+ import { PositionedRenderer } from "./positioned-renderer.js";
22
+ import { StructGroup } from "../elements/layout/struct-group.js";
23
+ import { StructGroupRenderer } from "./struct-group-renderer.js";
24
+ import { LinkElement } from "../elements/layout/link-element.js";
25
+ import { LinkRenderer } from "./link-renderer.js";
26
+ import { BookmarkElement } from "../elements/layout/bookmark-element.js";
27
+ import { BookmarkRenderer } from "./bookmark-renderer.js";
28
+ import { AnchorElement } from "../elements/layout/anchor-element.js";
29
+ import { PageBreakElement } from "../elements/layout/page-break-element.js";
30
+ import { AnchorRenderer } from "./anchor-renderer.js";
31
+ import { PageBreakRenderer } from "./page-break-renderer.js";
32
+ import { KeepTogetherElement } from "../elements/layout/keep-together-element.js";
33
+ import { KeepTogetherRenderer } from "./keep-together-renderer.js";
34
+ import { TextFieldElement } from "../elements/forms/text-field-element.js";
35
+ import { CheckboxElement } from "../elements/forms/checkbox-element.js";
36
+ import { RadioElement } from "../elements/forms/radio-element.js";
37
+ import { ChoiceElement } from "../elements/forms/choice-element.js";
38
+ import { PushButtonElement } from "../elements/forms/push-button-element.js";
39
+ import { SignatureFieldElement } from "../elements/forms/signature-element.js";
40
+ import { FormFieldRenderer } from "./form-field-renderer.js";
41
+ import { PageBuilderElement } from "../elements/layout/page-builder-element.js";
42
+ import { PageBuilderRenderer } from "./page-builder-renderer.js";
43
+ import { RotatedElement } from "../elements/layout/rotated-element.js";
44
+ import { RotatedBoxElement } from "../elements/layout/rotated-box-element.js";
45
+ import { RotatedRenderer } from "./rotated-renderer.js";
46
+ import { PdfBackend } from "./pdf-backend.js";
47
+ import { BoxConstraints } from "../layout/box-constraints.js";
48
+ import { collectImageElements } from "../layout/collect-images.js";
49
+ import { DEFAULT_TEXT_STYLE, mergeTextStyle } from "../text/text-style.js";
50
+ export class PDFRenderer {
51
+ static async render(document, objectManager) {
52
+ // Register all Renderer
53
+ RendererRegistry.register(TextElement, TextRenderer.render);
54
+ RendererRegistry.register(ContainerElement, ContainerRenderer.render);
55
+ RendererRegistry.register(RowElement, RowRenderer.render);
56
+ RendererRegistry.register(RectangleElement, RectangleRenderer.render);
57
+ RendererRegistry.register(ExpandedElement, ExpandedRenderer.render);
58
+ RendererRegistry.register(PaddingElement, PaddingRenderer.render);
59
+ RendererRegistry.register(PageBreakElement, PageBreakRenderer.render);
60
+ RendererRegistry.register(KeepTogetherElement, KeepTogetherRenderer.render);
61
+ RendererRegistry.register(TextFieldElement, FormFieldRenderer.render);
62
+ RendererRegistry.register(CheckboxElement, FormFieldRenderer.render);
63
+ RendererRegistry.register(RadioElement, FormFieldRenderer.render);
64
+ RendererRegistry.register(ChoiceElement, FormFieldRenderer.render);
65
+ RendererRegistry.register(PushButtonElement, FormFieldRenderer.render);
66
+ RendererRegistry.register(SignatureFieldElement, FormFieldRenderer.render);
67
+ RendererRegistry.register(DefaultTextStyleElement, DefaultTextStyleRenderer.render);
68
+ RendererRegistry.register(ImageElement, ImageRenderer.render);
69
+ RendererRegistry.register(LineElement, LineRenderer.render);
70
+ RendererRegistry.register(RepeatingHeaderElement, RepeatingHeaderRenderer.render);
71
+ RendererRegistry.register(DeferredElement, DeferredRenderer.render);
72
+ RendererRegistry.register(PositionedElement, PositionedRenderer.render);
73
+ RendererRegistry.register(StructGroup, StructGroupRenderer.render);
74
+ RendererRegistry.register(LinkElement, LinkRenderer.render);
75
+ RendererRegistry.register(BookmarkElement, BookmarkRenderer.render);
76
+ RendererRegistry.register(AnchorElement, AnchorRenderer.render);
77
+ RendererRegistry.register(PageBuilderElement, PageBuilderRenderer.render);
78
+ RendererRegistry.register(RotatedElement, RotatedRenderer.render);
79
+ RendererRegistry.register(RotatedBoxElement, RotatedRenderer.render);
80
+ let pdfContent = "";
81
+ // Header: version line + the PDF/A binary marker (the object manager owns it so its length
82
+ // matches the xref offset calculation).
83
+ pdfContent += objectManager.getHeader();
84
+ // Layout pass: thread the context explicitly. The seed page config is the document
85
+ // default; each PageElement overrides it for its own subtree.
86
+ const ctx = {
87
+ metrics: objectManager,
88
+ pageConfig: objectManager.getPDFConfig(),
89
+ textStyle: mergeTextStyle(DEFAULT_TEXT_STYLE, document.getDefaultTextStyle()),
90
+ onOverflow: objectManager.getOverflowPolicy(),
91
+ };
92
+ // Pre-layout: resolve every image's intrinsic pixel size (async), so the synchronous layout can
93
+ // give a width-only image a proportional height. init() is idempotent, so the renderer reuses it.
94
+ const images = collectImageElements(document);
95
+ if (images.length > 0)
96
+ await Promise.all(images.map((img) => img.resolveIntrinsicSize()));
97
+ document.calculateLayout(new BoxConstraints(), { x: 0, y: 0 }, ctx);
98
+ // Render pages and contents (the driver paginates overflowing pages).
99
+ await PDFDocumentRenderer.render(document, objectManager, ctx);
100
+ // Add the catalog. XMP metadata (/Metadata) and embedded files (/AF + /Names/EmbeddedFiles)
101
+ // are added only when present; with neither, the catalog is byte-identical to before.
102
+ const catalogParts = [`/Type /Catalog /Pages ${objectManager.getParentObjectNumber()} 0 R`];
103
+ const xmp = objectManager.getXmpMetadata();
104
+ if (xmp) {
105
+ const metadataObject = objectManager.addObject(`<< /Type /Metadata /Subtype /XML /Length ${xmp.length} >>\nstream\n${xmp}\nendstream`);
106
+ catalogParts.push(`/Metadata ${metadataObject} 0 R`);
107
+ }
108
+ const outputIntent = objectManager.getOutputIntent();
109
+ if (outputIntent) {
110
+ catalogParts.push(`/OutputIntents [${outputIntent} 0 R]`);
111
+ }
112
+ // The catalog has exactly one /Names dict; embedded files and named destinations are two entries in
113
+ // it, so collect both and emit the /Names once (below).
114
+ const namesParts = [];
115
+ const attachments = objectManager.getAttachments();
116
+ if (attachments.length > 0) {
117
+ // The name-tree key is a PDF literal string, so it needs the same escaping the /Filespec's /F and
118
+ // /UF get in attachFile(). Unescaped, a ")" or "\" in a file name closes the string early and the
119
+ // rest of it leaks out as raw operators.
120
+ const names = attachments
121
+ .map((a) => `(${PdfBackend.escapePdfString(a.name)}) ${a.filespec} 0 R`)
122
+ .join(" ");
123
+ const af = attachments.map((a) => `${a.filespec} 0 R`).join(" ");
124
+ catalogParts.push(`/AF [${af}]`);
125
+ namesParts.push(`/EmbeddedFiles << /Names [${names}] >>`);
126
+ }
127
+ // Accessible tagging: finalize the structure tree (emits StructTreeRoot + StructElems + ParentTree) and
128
+ // fold /MarkInfo + /StructTreeRoot + /Lang into the catalog. Returns "" (no-op) when tagging is off.
129
+ const structCatalog = objectManager.struct.finalize(objectManager);
130
+ if (structCatalog) {
131
+ catalogParts.push(structCatalog);
132
+ // PDF/UA requires the viewer to show the document title (not the file name).
133
+ catalogParts.push("/ViewerPreferences << /DisplayDocTitle true >>");
134
+ }
135
+ // Document outline (bookmarks): emit the /Outlines tree collected during page rendering. Returns
136
+ // "" (no-op) when no Bookmark was placed, so a plain document's catalog is unchanged.
137
+ const outlineCatalog = objectManager.outline.finalize(objectManager);
138
+ if (outlineCatalog)
139
+ catalogParts.push(outlineCatalog);
140
+ // Interactive form fields: emit the catalog /AcroForm dict collected as pages rendered. Returns ""
141
+ // (no-op) when no field was placed, so a plain document's catalog is unchanged.
142
+ const acroformCatalog = objectManager.acroform.finalize(objectManager);
143
+ if (acroformCatalog)
144
+ catalogParts.push(acroformCatalog);
145
+ // Named destinations (internal-link targets): a /Dests entry in the shared /Names dict.
146
+ const destsNames = objectManager.dests.finalize(objectManager);
147
+ if (destsNames)
148
+ namesParts.push(destsNames);
149
+ // Emit the single /Names dict if either embedded files or destinations contributed to it.
150
+ if (namesParts.length > 0)
151
+ catalogParts.push(`/Names << ${namesParts.join(" ")} >>`);
152
+ const catalogObject = `<< ${catalogParts.join(" ")} >>`;
153
+ objectManager.addObject(catalogObject);
154
+ // Now that the render pass has revealed which glyphs each embedded font uses, fill the reserved
155
+ // font objects with the subset font program (must happen before the objects are serialized).
156
+ objectManager.finalizeCustomFonts();
157
+ // Encrypt every registered stream + add the /Encrypt object (no-op unless a security handler was set).
158
+ await objectManager.finalizeEncryption();
159
+ // Add rendered objects
160
+ pdfContent += objectManager.getRenderedObjects();
161
+ // Add XRef table and trailer
162
+ const startxref = pdfContent.length;
163
+ pdfContent += objectManager.getXRefTable();
164
+ pdfContent += objectManager.getTrailerAndXRef(startxref);
165
+ return pdfContent;
90
166
  }
91
167
  }
92
- exports.PDFRenderer = PDFRenderer;
@@ -0,0 +1,6 @@
1
+ import { PDFObjectManager } from "../utils/pdf-object-manager.js";
2
+ import { PositionedElement } from "../elements/layout/positioned-element.js";
3
+ import { IRNode } from "../ir/display-list.js";
4
+ export declare class PositionedRenderer {
5
+ static render(positionedElement: PositionedElement, objectManager: PDFObjectManager): Promise<IRNode[]>;
6
+ }
@@ -0,0 +1,10 @@
1
+ import { RendererRegistry } from "../utils/renderer-registry.js";
2
+ export class PositionedRenderer {
3
+ static async render(positionedElement, objectManager) {
4
+ // The child was already placed (by the frame) during layout; just emit its IR. It is drawn
5
+ // after the frame's flow content, so it paints on top.
6
+ const { child } = positionedElement.getProps();
7
+ const renderer = RendererRegistry.getRenderer(child);
8
+ return renderer(child, objectManager);
9
+ }
10
+ }
@@ -1,6 +1,6 @@
1
- import { PDFObjectManager } from "../utils/pdf-object-manager";
2
- import { RectangleElement } from "../elements/rectangle-element";
3
- import { IRNode } from "../ir/display-list";
1
+ import { PDFObjectManager } from "../utils/pdf-object-manager.js";
2
+ import { RectangleElement } from "../elements/rectangle-element.js";
3
+ import { IRNode } from "../ir/display-list.js";
4
4
  export declare class RectangleRenderer {
5
5
  static render(rectangleElement: RectangleElement, objectManager: PDFObjectManager): Promise<IRNode[]>;
6
6
  /** One `line` node per present side, along the box edges (top-left coordinates). */