@jasy/pdf 1.0.0-beta.3 → 1.0.0-beta.4

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 (210) hide show
  1. package/dist/api/content.d.ts +5 -5
  2. package/dist/api/content.js +12 -6
  3. package/dist/api/font-url.d.ts +1 -2
  4. package/dist/api/font-url.js +8 -74
  5. package/dist/api/forms.d.ts +3 -0
  6. package/dist/api/forms.js +3 -0
  7. package/dist/api/index.d.ts +2 -0
  8. package/dist/api/index.js +1 -0
  9. package/dist/api/layout.d.ts +6 -0
  10. package/dist/api/layout.js +7 -2
  11. package/dist/api/page-builder.d.ts +4 -0
  12. package/dist/api/page-builder.js +4 -0
  13. package/dist/api/structure.d.ts +10 -2
  14. package/dist/api/structure.js +9 -6
  15. package/dist/api/table.d.ts +5 -3
  16. package/dist/api/table.js +99 -22
  17. package/dist/common/diagnostics.d.ts +53 -0
  18. package/dist/common/diagnostics.js +77 -0
  19. package/dist/edit/acroform-reader.js +7 -7
  20. package/dist/edit/appearance.js +3 -3
  21. package/dist/edit/attachments.d.ts +52 -0
  22. package/dist/edit/attachments.js +178 -0
  23. package/dist/edit/document.d.ts +38 -8
  24. package/dist/edit/document.js +208 -51
  25. package/dist/edit/fill.js +8 -23
  26. package/dist/edit/filters.d.ts +15 -0
  27. package/dist/edit/filters.js +157 -0
  28. package/dist/edit/index.d.ts +4 -1
  29. package/dist/edit/index.js +3 -1
  30. package/dist/edit/objects.d.ts +3 -4
  31. package/dist/edit/objects.js +5 -18
  32. package/dist/elements/canvas-element.d.ts +3 -0
  33. package/dist/elements/canvas-element.js +14 -3
  34. package/dist/elements/container-element.d.ts +5 -1
  35. package/dist/elements/container-element.js +35 -15
  36. package/dist/elements/forms/checkbox-element.d.ts +2 -0
  37. package/dist/elements/forms/checkbox-element.js +12 -3
  38. package/dist/elements/forms/choice-element.d.ts +6 -1
  39. package/dist/elements/forms/choice-element.js +17 -6
  40. package/dist/elements/forms/field-font.d.ts +8 -0
  41. package/dist/elements/forms/field-font.js +5 -0
  42. package/dist/elements/forms/push-button-element.d.ts +6 -1
  43. package/dist/elements/forms/push-button-element.js +16 -4
  44. package/dist/elements/forms/radio-element.d.ts +2 -0
  45. package/dist/elements/forms/radio-element.js +12 -3
  46. package/dist/elements/forms/signature-element.d.ts +6 -1
  47. package/dist/elements/forms/signature-element.js +16 -4
  48. package/dist/elements/forms/text-field-element.d.ts +6 -1
  49. package/dist/elements/forms/text-field-element.js +16 -4
  50. package/dist/elements/image-element.d.ts +39 -1
  51. package/dist/elements/image-element.js +95 -12
  52. package/dist/elements/layout/anchor-element.d.ts +8 -10
  53. package/dist/elements/layout/anchor-element.js +10 -18
  54. package/dist/elements/layout/bookmark-element.d.ts +9 -11
  55. package/dist/elements/layout/bookmark-element.js +11 -20
  56. package/dist/elements/layout/default-text-style-element.d.ts +11 -13
  57. package/dist/elements/layout/default-text-style-element.js +14 -31
  58. package/dist/elements/layout/deferred-element.d.ts +12 -4
  59. package/dist/elements/layout/deferred-element.js +24 -7
  60. package/dist/elements/layout/expanded-element.d.ts +7 -0
  61. package/dist/elements/layout/expanded-element.js +40 -18
  62. package/dist/elements/layout/keep-together-element.d.ts +8 -18
  63. package/dist/elements/layout/keep-together-element.js +21 -45
  64. package/dist/elements/layout/link-element.d.ts +8 -13
  65. package/dist/elements/layout/link-element.js +9 -19
  66. package/dist/elements/layout/padding-element.d.ts +15 -1
  67. package/dist/elements/layout/padding-element.js +55 -27
  68. package/dist/elements/layout/page-break-element.d.ts +4 -1
  69. package/dist/elements/layout/page-break-element.js +6 -1
  70. package/dist/elements/layout/page-builder-element.d.ts +5 -0
  71. package/dist/elements/layout/page-builder-element.js +11 -0
  72. package/dist/elements/layout/positioned-element.d.ts +14 -2
  73. package/dist/elements/layout/positioned-element.js +36 -10
  74. package/dist/elements/layout/proxy-element.d.ts +43 -0
  75. package/dist/elements/layout/proxy-element.js +94 -0
  76. package/dist/elements/layout/repeating-header-element.d.ts +6 -0
  77. package/dist/elements/layout/repeating-header-element.js +40 -13
  78. package/dist/elements/layout/rotated-box-element.d.ts +6 -0
  79. package/dist/elements/layout/rotated-box-element.js +27 -14
  80. package/dist/elements/layout/rotated-element.d.ts +7 -0
  81. package/dist/elements/layout/rotated-element.js +25 -2
  82. package/dist/elements/layout/struct-group.d.ts +9 -17
  83. package/dist/elements/layout/struct-group.js +10 -35
  84. package/dist/elements/line-element.d.ts +1 -0
  85. package/dist/elements/line-element.js +6 -0
  86. package/dist/elements/page-element.d.ts +10 -3
  87. package/dist/elements/page-element.js +39 -31
  88. package/dist/elements/pdf-document-element.d.ts +3 -0
  89. package/dist/elements/pdf-document-element.js +7 -0
  90. package/dist/elements/pdf-element.d.ts +75 -15
  91. package/dist/elements/pdf-element.js +68 -18
  92. package/dist/elements/rectangle-element.d.ts +14 -0
  93. package/dist/elements/rectangle-element.js +97 -52
  94. package/dist/elements/row-element.d.ts +11 -0
  95. package/dist/elements/row-element.js +58 -8
  96. package/dist/elements/svg-element.d.ts +30 -6
  97. package/dist/elements/svg-element.js +88 -17
  98. package/dist/elements/text-element.d.ts +32 -13
  99. package/dist/elements/text-element.js +119 -97
  100. package/dist/forms/acroform.d.ts +9 -1
  101. package/dist/forms/acroform.js +83 -34
  102. package/dist/forms/appearance.d.ts +17 -6
  103. package/dist/forms/appearance.js +18 -16
  104. package/dist/ir/display-list.d.ts +12 -2
  105. package/dist/layout/box-constraints.d.ts +5 -0
  106. package/dist/layout/box-constraints.js +14 -0
  107. package/dist/layout/collect-images.d.ts +6 -4
  108. package/dist/layout/collect-images.js +6 -4
  109. package/dist/layout/flex-item.d.ts +9 -0
  110. package/dist/layout/flex-item.js +39 -0
  111. package/dist/layout/fragmentation.d.ts +12 -4
  112. package/dist/layout/fragmentation.js +37 -14
  113. package/dist/layout/layout-cache.d.ts +53 -0
  114. package/dist/layout/layout-cache.js +107 -0
  115. package/dist/renderer/anchor-renderer.d.ts +2 -1
  116. package/dist/renderer/anchor-renderer.js +5 -4
  117. package/dist/renderer/bookmark-renderer.d.ts +2 -1
  118. package/dist/renderer/bookmark-renderer.js +5 -4
  119. package/dist/renderer/canvas-renderer.d.ts +2 -1
  120. package/dist/renderer/canvas-renderer.js +5 -2
  121. package/dist/renderer/container-renderer.d.ts +2 -1
  122. package/dist/renderer/container-renderer.js +6 -5
  123. package/dist/renderer/default-text-style-renderer.d.ts +2 -1
  124. package/dist/renderer/default-text-style-renderer.js +2 -3
  125. package/dist/renderer/deferred-renderer.d.ts +2 -1
  126. package/dist/renderer/deferred-renderer.js +2 -3
  127. package/dist/renderer/expanded-renderer.d.ts +2 -1
  128. package/dist/renderer/expanded-renderer.js +2 -3
  129. package/dist/renderer/form-field-renderer.d.ts +2 -1
  130. package/dist/renderer/form-field-renderer.js +16 -3
  131. package/dist/renderer/image-renderer.d.ts +2 -1
  132. package/dist/renderer/image-renderer.js +5 -2
  133. package/dist/renderer/keep-together-renderer.d.ts +2 -1
  134. package/dist/renderer/keep-together-renderer.js +2 -3
  135. package/dist/renderer/line-renderer.d.ts +2 -1
  136. package/dist/renderer/line-renderer.js +7 -2
  137. package/dist/renderer/link-renderer.d.ts +2 -1
  138. package/dist/renderer/link-renderer.js +36 -5
  139. package/dist/renderer/padding-renderer.d.ts +2 -1
  140. package/dist/renderer/padding-renderer.js +5 -5
  141. package/dist/renderer/page-break-renderer.d.ts +6 -5
  142. package/dist/renderer/page-break-renderer.js +14 -7
  143. package/dist/renderer/page-builder-renderer.d.ts +2 -1
  144. package/dist/renderer/page-builder-renderer.js +2 -3
  145. package/dist/renderer/page-renderer.js +28 -7
  146. package/dist/renderer/pdf-backend.d.ts +4 -3
  147. package/dist/renderer/pdf-backend.js +5 -5
  148. package/dist/renderer/pdf-document-renderer.d.ts +1 -8
  149. package/dist/renderer/pdf-document-renderer.js +15 -16
  150. package/dist/renderer/pdf-renderer.js +11 -11
  151. package/dist/renderer/positioned-renderer.d.ts +6 -1
  152. package/dist/renderer/positioned-renderer.js +6 -5
  153. package/dist/renderer/rectangle-renderer.d.ts +2 -1
  154. package/dist/renderer/rectangle-renderer.js +9 -4
  155. package/dist/renderer/repeating-header-renderer.d.ts +2 -1
  156. package/dist/renderer/repeating-header-renderer.js +6 -5
  157. package/dist/renderer/rotated-renderer.d.ts +2 -1
  158. package/dist/renderer/rotated-renderer.js +8 -5
  159. package/dist/renderer/row-renderer.d.ts +2 -1
  160. package/dist/renderer/row-renderer.js +6 -5
  161. package/dist/renderer/struct-group-renderer.d.ts +2 -1
  162. package/dist/renderer/struct-group-renderer.js +2 -3
  163. package/dist/renderer/svg-renderer.d.ts +2 -1
  164. package/dist/renderer/svg-renderer.js +8 -2
  165. package/dist/renderer/text-renderer.d.ts +4 -6
  166. package/dist/renderer/text-renderer.js +101 -111
  167. package/dist/svg/gradients.js +5 -0
  168. package/dist/svg/index.js +10 -1
  169. package/dist/svg/style.d.ts +2 -0
  170. package/dist/svg/style.js +11 -0
  171. package/dist/text/advance.d.ts +4 -0
  172. package/dist/text/advance.js +17 -4
  173. package/dist/text/bidi.d.ts +0 -2
  174. package/dist/text/bidi.js +0 -10
  175. package/dist/text/glyph-coverage.d.ts +1 -1
  176. package/dist/text/glyph-coverage.js +7 -3
  177. package/dist/text/hyphen-breaks.d.ts +17 -0
  178. package/dist/text/hyphen-breaks.js +54 -0
  179. package/dist/text/line-breaker.d.ts +34 -31
  180. package/dist/text/line-breaker.js +364 -176
  181. package/dist/text/line-metrics.d.ts +6 -3
  182. package/dist/text/line-metrics.js +7 -2
  183. package/dist/text/pdf-text-string.d.ts +21 -0
  184. package/dist/text/pdf-text-string.js +114 -0
  185. package/dist/text/text-decoration.d.ts +5 -5
  186. package/dist/text/text-decoration.js +5 -5
  187. package/dist/text/word-splitting.d.ts +15 -2
  188. package/dist/text/word-splitting.js +34 -8
  189. package/dist/utils/afm-parser.d.ts +2 -0
  190. package/dist/utils/afm-parser.js +10 -1
  191. package/dist/utils/dest-registry.d.ts +1 -1
  192. package/dist/utils/dest-registry.js +3 -2
  193. package/dist/utils/fetch-bytes.d.ts +19 -0
  194. package/dist/utils/fetch-bytes.js +79 -0
  195. package/dist/utils/flex-layout.d.ts +13 -13
  196. package/dist/utils/flex-layout.js +318 -216
  197. package/dist/utils/font-metrics.d.ts +4 -3
  198. package/dist/utils/image-helper.js +2 -1
  199. package/dist/utils/pdf-object-manager.d.ts +11 -3
  200. package/dist/utils/pdf-object-manager.js +39 -13
  201. package/dist/utils/renderer-registry.d.ts +43 -2
  202. package/dist/utils/renderer-registry.js +40 -0
  203. package/dist/utils/struct-tree.d.ts +24 -0
  204. package/dist/utils/struct-tree.js +61 -9
  205. package/dist/utils/ttf-parser.d.ts +4 -0
  206. package/dist/utils/ttf-parser.js +27 -4
  207. package/dist/validators/element-validator.js +2 -1
  208. package/package.json +1 -1
  209. package/dist/elements/layout/sized-container-element.d.ts +0 -14
  210. package/dist/elements/layout/sized-container-element.js +0 -32
@@ -18,7 +18,7 @@ export class PDFDocumentRenderer {
18
18
  // `Page` element produces one run of sheets, so the run's length is that section's total.
19
19
  const section = [];
20
20
  for (const page of document.getProps().children) {
21
- const sheets = PDFDocumentRenderer.paginateLogicalPage(page, ctx);
21
+ const sheets = PDFDocumentRenderer.paginateLogicalPage(page, ctx, physicalPages.length + 1);
22
22
  sheets.forEach((_, i) => section.push({ number: i + 1, total: sheets.length }));
23
23
  physicalPages.push(...sheets);
24
24
  }
@@ -47,17 +47,11 @@ export class PDFDocumentRenderer {
47
47
  objectManager.replaceObject(pagesObjectNumber, updatedPagesObject);
48
48
  return pagesObjectNumber;
49
49
  }
50
- /**
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
- */
50
+ /** The page geometry a physical page will use: resolved, never read back from an earlier layout. */
58
51
  static configOf(physical, ctx) {
59
- const config = physical.kind === "whole" ? physical.page.getProps().config : physical.config;
60
- return { ...ctx.pageConfig, ...config };
52
+ return physical.kind === "whole"
53
+ ? physical.page.resolveConfig(ctx.pageConfig)
54
+ : physical.config;
61
55
  }
62
56
  /**
63
57
  * Splits one logical page into the physical pages it will occupy, without rendering.
@@ -66,13 +60,15 @@ export class PDFDocumentRenderer {
66
60
  * (a Container). Anything else stays a single page on the unchanged path, so non-overflowing
67
61
  * documents keep producing byte-identical output.
68
62
  */
69
- static paginateLogicalPage(page, ctx) {
70
- const { children, config, header, footer } = page.getProps();
63
+ static paginateLogicalPage(page, ctx, firstPage) {
64
+ const { children, header, footer } = page.getProps();
65
+ // Resolved here, not read back from an earlier layout pass that happened to merge it.
66
+ const config = page.resolveConfig(ctx.pageConfig);
71
67
  if (children.length !== 1 || !isFragmentable(children[0])) {
72
68
  return [{ kind: "whole", page }];
73
69
  }
74
70
  // 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).
71
+ // band between them. Resolve that band once.
76
72
  // The frame is a throwaway: this pass only wants the band HEIGHTS, and an out-of-flow child
77
73
  // contributes none. It exists because a `Positioned` in a band demands one; pass 2 builds the
78
74
  // real frame (in `PageElement`) and drains it.
@@ -81,7 +77,9 @@ export class PDFDocumentRenderer {
81
77
  pageConfig: config,
82
78
  textStyle: ctx.textStyle,
83
79
  onOverflow: ctx.onOverflow,
80
+ diagnostics: ctx.diagnostics,
84
81
  frame: pageFrame(config),
82
+ paginatingPage: firstPage, // the bands are laid out here once, for the first sheet
85
83
  };
86
84
  const { bodyWidth: width, bodyHeight: height } = layoutPageBands(config, header, footer, pageCtx);
87
85
  // A `keepTogether` group compares its height against a FULL fresh page (this band), so thread it in.
@@ -97,6 +95,7 @@ export class PDFDocumentRenderer {
97
95
  }
98
96
  // pageCtx, not the document ctx: a descendant that reads `pageConfig` while being measured (a
99
97
  // `PageBuilder` sizing its provisional build) must see THIS page's geometry, not the document default.
98
+ pageCtx.paginatingPage = firstPage + pages.length;
100
99
  const { fitted, remainder, forceBreak } = region.fragment(height, width, pageCtx);
101
100
  // Everything fits on one page: keep the ORIGINAL page so output is unchanged. Measuring inside
102
101
  // fragment() left its children at the measuring origin; pass B lays the page out again, which
@@ -115,8 +114,8 @@ export class PDFDocumentRenderer {
115
114
  if (fitted === null) {
116
115
  // Measure the region's true height for the message (rare path, so the extra pass is free);
117
116
  // 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");
117
+ const needed = region.measure(BoxConstraints.loose(width, Infinity), pageCtx).height;
118
+ reportOverflow(region, needed, height, pageCtx);
120
119
  pages.push({ kind: "fragment", config, content: region, header, footer });
121
120
  break;
122
121
  }
@@ -1,3 +1,4 @@
1
+ import { comparePdfText } from "../text/pdf-text-string.js";
1
2
  import { PDFDocumentRenderer } from "./pdf-document-renderer.js";
2
3
  import { RendererRegistry } from "../utils/renderer-registry.js";
3
4
  import { DefaultTextStyleElement, ExpandedElement, ImageElement, LineElement, CanvasElement, PaddingElement, SvgElement, TextElement, } from "../elements/index.js";
@@ -45,8 +46,6 @@ import { PageBuilderRenderer } from "./page-builder-renderer.js";
45
46
  import { RotatedElement } from "../elements/layout/rotated-element.js";
46
47
  import { RotatedBoxElement } from "../elements/layout/rotated-box-element.js";
47
48
  import { RotatedRenderer } from "./rotated-renderer.js";
48
- import { PdfBackend } from "./pdf-backend.js";
49
- import { BoxConstraints } from "../layout/box-constraints.js";
50
49
  import { collectImageElements } from "../layout/collect-images.js";
51
50
  import { DEFAULT_TEXT_STYLE, mergeTextStyle } from "../text/text-style.js";
52
51
  export class PDFRenderer {
@@ -85,21 +84,21 @@ export class PDFRenderer {
85
84
  // Header: version line + the PDF/A binary marker (the object manager owns it so its length
86
85
  // matches the xref offset calculation).
87
86
  pdfContent += objectManager.getHeader();
88
- // Layout pass: thread the context explicitly. The seed page config is the document
89
- // default; each PageElement overrides it for its own subtree.
87
+ // The layout context, threaded explicitly. The seed page config is the document default; each
88
+ // PageElement overrides it for its own subtree.
90
89
  const ctx = {
91
90
  metrics: objectManager,
92
91
  pageConfig: objectManager.getPDFConfig(),
93
92
  textStyle: mergeTextStyle(DEFAULT_TEXT_STYLE, document.getDefaultTextStyle()),
94
93
  onOverflow: objectManager.getOverflowPolicy(),
94
+ diagnostics: objectManager.diagnostics,
95
95
  };
96
96
  // Pre-layout: resolve every image's intrinsic pixel size (async), so the synchronous layout can
97
97
  // give a width-only image a proportional height. init() is idempotent, so the renderer reuses it.
98
98
  const images = collectImageElements(document);
99
99
  if (images.length > 0)
100
100
  await Promise.all(images.map((img) => img.resolveIntrinsicSize()));
101
- document.calculateLayout(new BoxConstraints(), { x: 0, y: 0 }, ctx);
102
- // Render pages and contents (the driver paginates overflowing pages).
101
+ // Paginate, then lay out and render page by page (the driver does both; nothing is laid out before).
103
102
  await PDFDocumentRenderer.render(document, objectManager, ctx);
104
103
  // Add the catalog. XMP metadata (/Metadata) and embedded files (/AF + /Names/EmbeddedFiles)
105
104
  // are added only when present; with neither, the catalog is byte-identical to before.
@@ -118,11 +117,12 @@ export class PDFRenderer {
118
117
  const namesParts = [];
119
118
  const attachments = objectManager.getAttachments();
120
119
  if (attachments.length > 0) {
121
- // The name-tree key is a PDF literal string, so it needs the same escaping the /Filespec's /F and
122
- // /UF get in attachFile(). Unescaped, a ")" or "\" in a file name closes the string early and the
123
- // rest of it leaks out as raw operators.
124
- const names = attachments
125
- .map((a) => `(${PdfBackend.escapePdfString(a.name)}) ${a.filespec} 0 R`)
120
+ // The name-tree key is a text string like the /Filespec's /F and /UF, so it goes through the same
121
+ // choke point - encoded and, in an encrypted file, enciphered. A name tree is SORTED by its keys
122
+ // (a reader may search it), so the keys are ordered by their bytes first.
123
+ const names = [...attachments]
124
+ .sort((a, b) => comparePdfText(a.name, b.name))
125
+ .map((a) => `${objectManager.pdfString(a.name)} ${a.filespec} 0 R`)
126
126
  .join(" ");
127
127
  const af = attachments.map((a) => `${a.filespec} 0 R`).join(" ");
128
128
  catalogParts.push(`/AF [${af}]`);
@@ -1,6 +1,11 @@
1
1
  import { PDFObjectManager } from "../utils/pdf-object-manager.js";
2
2
  import { PositionedElement } from "../elements/layout/positioned-element.js";
3
+ import { type RenderScope } from "../utils/renderer-registry.js";
3
4
  import { IRNode } from "../ir/display-list.js";
4
5
  export declare class PositionedRenderer {
5
- static render(positionedElement: PositionedElement, objectManager: PDFObjectManager): Promise<IRNode[]>;
6
+ /**
7
+ * Drawn at its own place in the tree (tree order is paint order), from its frame: the child was laid
8
+ * out relative to the frame's top-left, not to the flow it sits in.
9
+ */
10
+ static render(positionedElement: PositionedElement, objectManager: PDFObjectManager, scope: RenderScope): Promise<IRNode[]>;
6
11
  }
@@ -1,10 +1,11 @@
1
1
  import { RendererRegistry } from "../utils/renderer-registry.js";
2
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.
3
+ /**
4
+ * Drawn at its own place in the tree (tree order is paint order), from its frame: the child was laid
5
+ * out relative to the frame's top-left, not to the flow it sits in.
6
+ */
7
+ static async render(positionedElement, objectManager, scope) {
6
8
  const { child } = positionedElement.getProps();
7
- const renderer = RendererRegistry.getRenderer(child);
8
- return renderer(child, objectManager);
9
+ return RendererRegistry.render(child, objectManager, { ...scope, origin: scope.frame });
9
10
  }
10
11
  }
@@ -1,8 +1,9 @@
1
1
  import { PDFObjectManager } from "../utils/pdf-object-manager.js";
2
2
  import { RectangleElement } from "../elements/rectangle-element.js";
3
+ import { type RenderScope } from "../utils/renderer-registry.js";
3
4
  import { IRNode } from "../ir/display-list.js";
4
5
  export declare class RectangleRenderer {
5
- static render(rectangleElement: RectangleElement, objectManager: PDFObjectManager): Promise<IRNode[]>;
6
+ static render(rectangleElement: RectangleElement, objectManager: PDFObjectManager, scope: RenderScope): Promise<IRNode[]>;
6
7
  /** One `line` node per present side, along the box edges (top-left coordinates). */
7
8
  private static sideLines;
8
9
  }
@@ -1,8 +1,11 @@
1
1
  import { RendererRegistry } from "../utils/renderer-registry.js";
2
2
  import { isGradientInput, resolveGradient } from "../api/gradient.js";
3
3
  export class RectangleRenderer {
4
- static async render(rectangleElement, objectManager) {
5
- const { x, y, width, height, children, color, backgroundColor, borderWidth, radius, sideBorders, overflow, } = rectangleElement.getProps();
4
+ static async render(rectangleElement, objectManager, scope) {
5
+ const { x: ownX, y: ownY, width, height, children, color, backgroundColor, borderWidth, radius, sideBorders, overflow, relative, } = rectangleElement.getProps();
6
+ // Its own position is relative to `scope.origin`; the IR wants page coordinates.
7
+ const x = scope.origin.x + (ownX ?? 0);
8
+ const y = scope.origin.y + (ownY ?? 0);
6
9
  const h = height;
7
10
  const nodes = [];
8
11
  // A gradient is written box-relative (an angle and stops); this is the first place the box is
@@ -41,10 +44,12 @@ export class RectangleRenderer {
41
44
  if (clip) {
42
45
  nodes.push({ type: "clip-push", x, y, width, height: h, ...(radius ? { radius } : {}) });
43
46
  }
47
+ // Its children are placed relative to its own top-left; a `relative` box is also the frame its
48
+ // `Positioned` descendants were placed against.
49
+ const childScope = { origin: { x, y }, frame: relative ? { x, y } : scope.frame };
44
50
  if (children)
45
51
  for (const child of children) {
46
- const renderer = RendererRegistry.getRenderer(child);
47
- nodes.push(...(await renderer(child, objectManager)));
52
+ nodes.push(...(await RendererRegistry.render(child, objectManager, childScope)));
48
53
  }
49
54
  if (clip)
50
55
  nodes.push({ type: "clip-pop" });
@@ -1,6 +1,7 @@
1
1
  import { PDFObjectManager } from "../utils/pdf-object-manager.js";
2
2
  import { RepeatingHeaderElement } from "../elements/layout/repeating-header-element.js";
3
+ import { type RenderScope } from "../utils/renderer-registry.js";
3
4
  import { IRNode } from "../ir/display-list.js";
4
5
  export declare class RepeatingHeaderRenderer {
5
- static render(element: RepeatingHeaderElement, objectManager: PDFObjectManager): Promise<IRNode[]>;
6
+ static render(element: RepeatingHeaderElement, objectManager: PDFObjectManager, scope: RenderScope): Promise<IRNode[]>;
6
7
  }
@@ -1,11 +1,12 @@
1
- import { RendererRegistry } from "../utils/renderer-registry.js";
1
+ import { RendererRegistry, within } from "../utils/renderer-registry.js";
2
2
  export class RepeatingHeaderRenderer {
3
- static async render(element, objectManager) {
4
- const { header, body } = element.getProps();
3
+ static async render(element, objectManager, scope) {
4
+ const { x, y, header, body } = element.getProps();
5
5
  const nodes = [];
6
+ // Header and body are placed relative to its own top-left.
7
+ const inner = within(scope, { x, y });
6
8
  for (const child of [header, body]) {
7
- const renderer = RendererRegistry.getRenderer(child);
8
- nodes.push(...(await renderer(child, objectManager)));
9
+ nodes.push(...(await RendererRegistry.render(child, objectManager, inner)));
9
10
  }
10
11
  return nodes;
11
12
  }
@@ -1,7 +1,8 @@
1
1
  import { PDFObjectManager } from "../utils/pdf-object-manager.js";
2
2
  import { RotatedElement } from "../elements/layout/rotated-element.js";
3
3
  import { RotatedBoxElement } from "../elements/layout/rotated-box-element.js";
4
+ import { type RenderScope } from "../utils/renderer-registry.js";
4
5
  import { IRNode } from "../ir/display-list.js";
5
6
  export declare class RotatedRenderer {
6
- static render(element: RotatedElement | RotatedBoxElement, objectManager: PDFObjectManager): Promise<IRNode[]>;
7
+ static render(element: RotatedElement | RotatedBoxElement, objectManager: PDFObjectManager, scope: RenderScope): Promise<IRNode[]>;
7
8
  }
@@ -1,9 +1,12 @@
1
- import { RendererRegistry } from "../utils/renderer-registry.js";
1
+ import { RendererRegistry, within } from "../utils/renderer-registry.js";
2
2
  export class RotatedRenderer {
3
3
  // Both `Rotated` (paint-only) and `RotatedBox` (layout-aware quarter-turns) expose the same
4
4
  // `{ angle, child, x, y, width, height }` and rotate the child around the box center - shared here.
5
- static async render(element, objectManager) {
6
- const { angle, child, x, y, width, height } = element.getProps();
5
+ static async render(element, objectManager, scope) {
6
+ const { angle, child, x: ownX, y: ownY, width, height } = element.getProps();
7
+ // Its own position is relative to `scope.origin`; the IR wants page coordinates.
8
+ const x = scope.origin.x + (ownX ?? 0);
9
+ const y = scope.origin.y + (ownY ?? 0);
7
10
  // Rotate around the box CENTER, in the engine's top-left coordinates (the IR->backend seam flips
8
11
  // the matrix to PDF space per page). A positive angle reads clockwise on the page.
9
12
  const cx = x + width / 2;
@@ -21,8 +24,8 @@ export class RotatedRenderer {
21
24
  cx - cos * cx + sin * cy,
22
25
  cy - sin * cx - cos * cy,
23
26
  ];
24
- const renderer = RendererRegistry.getRenderer(child);
25
- const childNodes = await renderer(child, objectManager);
27
+ // The child is placed relative to the box top-left.
28
+ const childNodes = await RendererRegistry.render(child, objectManager, within(scope, { x: ownX, y: ownY }));
26
29
  // Wrap the child's drawing between a push (apply the matrix) and a pop (restore).
27
30
  return [{ type: "transform-push", matrix }, ...childNodes, { type: "transform-pop" }];
28
31
  }
@@ -1,6 +1,7 @@
1
1
  import { PDFObjectManager } from "../utils/pdf-object-manager.js";
2
2
  import { RowElement } from "../elements/row-element.js";
3
+ import { type RenderScope } from "../utils/renderer-registry.js";
3
4
  import { IRNode } from "../ir/display-list.js";
4
5
  export declare class RowRenderer {
5
- static render(rowElement: RowElement, objectManager: PDFObjectManager): Promise<IRNode[]>;
6
+ static render(rowElement: RowElement, objectManager: PDFObjectManager, scope: RenderScope): Promise<IRNode[]>;
6
7
  }
@@ -1,12 +1,13 @@
1
- import { RendererRegistry } from "../utils/renderer-registry.js";
1
+ import { RendererRegistry, within } from "../utils/renderer-registry.js";
2
2
  export class RowRenderer {
3
- static async render(rowElement, objectManager) {
4
- const { children } = rowElement.getProps();
3
+ static async render(rowElement, objectManager, scope) {
4
+ const { x, y, children } = rowElement.getProps();
5
5
  const nodes = [];
6
+ // Its children are placed relative to its own top-left.
7
+ const inner = within(scope, { x, y });
6
8
  if (children)
7
9
  for (const child of children) {
8
- const renderer = RendererRegistry.getRenderer(child);
9
- nodes.push(...(await renderer(child, objectManager)));
10
+ nodes.push(...(await RendererRegistry.render(child, objectManager, inner)));
10
11
  }
11
12
  return nodes;
12
13
  }
@@ -1,10 +1,11 @@
1
1
  import { StructGroup } from "../elements/layout/struct-group.js";
2
2
  import { PDFObjectManager } from "../utils/pdf-object-manager.js";
3
3
  import { IRNode } from "../ir/display-list.js";
4
+ import { type RenderScope } from "../utils/renderer-registry.js";
4
5
  /**
5
6
  * Renders a StructGroup: opens its structure element, renders the child under it, closes it. With accessible
6
7
  * tagging off it is fully transparent - just the child's IR.
7
8
  */
8
9
  export declare class StructGroupRenderer {
9
- static render(group: StructGroup, objectManager: PDFObjectManager): Promise<IRNode[]>;
10
+ static render(group: StructGroup, objectManager: PDFObjectManager, scope: RenderScope): Promise<IRNode[]>;
10
11
  }
@@ -4,11 +4,10 @@ import { RendererRegistry } from "../utils/renderer-registry.js";
4
4
  * tagging off it is fully transparent - just the child's IR.
5
5
  */
6
6
  export class StructGroupRenderer {
7
- static async render(group, objectManager) {
7
+ static async render(group, objectManager, scope) {
8
8
  const { role, child } = group.getProps();
9
9
  const renderChild = async () => {
10
- const renderer = RendererRegistry.getRenderer(child);
11
- return renderer(child, objectManager);
10
+ return RendererRegistry.render(child, objectManager, scope);
12
11
  };
13
12
  if (!objectManager.struct.enabled)
14
13
  return renderChild();
@@ -1,3 +1,4 @@
1
+ import type { RenderScope } from "../utils/renderer-registry.js";
1
2
  import { PDFObjectManager } from "../utils/pdf-object-manager.js";
2
3
  import { SvgElement } from "../elements/svg-element.js";
3
4
  import type { IRNode } from "../ir/display-list.js";
@@ -7,5 +8,5 @@ import type { IRNode } from "../ir/display-list.js";
7
8
  * that colour emoji already draw through carries it.
8
9
  */
9
10
  export declare class SvgRenderer {
10
- static render(element: SvgElement, objectManager: PDFObjectManager): Promise<IRNode[]>;
11
+ static render(element: SvgElement, objectManager: PDFObjectManager, scope: RenderScope): Promise<IRNode[]>;
11
12
  }
@@ -5,8 +5,14 @@ import { svgToIr } from "../svg/index.js";
5
5
  * that colour emoji already draw through carries it.
6
6
  */
7
7
  export class SvgRenderer {
8
- static async render(element, objectManager) {
9
- const { x, y, width, height, source, alt } = element.getProps();
8
+ static async render(element, objectManager, scope) {
9
+ await element.load(); // an SVG from a URL; already done when the pre-layout pass reached it
10
+ const { x: ownX, y: ownY, width, height, source, alt } = element.getProps();
11
+ if (source === undefined)
12
+ throw new Error("@jasy/pdf: an SVG was drawn before it was loaded");
13
+ // Its own position is relative to `scope.origin`; the IR wants page coordinates.
14
+ const x = scope.origin.x + (ownX ?? 0);
15
+ const y = scope.origin.y + (ownY ?? 0);
10
16
  const nodes = svgToIr(source, {
11
17
  x: x ?? 0,
12
18
  y: y ?? 0,
@@ -1,11 +1,9 @@
1
- import { TextElement, TextSegment } from "../elements/text-element.js";
2
- import { FontStyle, PDFObjectManager } from "../utils/pdf-object-manager.js";
3
- import type { FontMetrics } from "../utils/font-metrics.js";
1
+ import type { RenderScope } from "../utils/renderer-registry.js";
2
+ import { TextElement } from "../elements/text-element.js";
3
+ import { PDFObjectManager } from "../utils/pdf-object-manager.js";
4
4
  import { IRNode } from "../ir/display-list.js";
5
- import { type LineOptions, TextOverflow } from "../text/line-breaker.js";
6
5
  export declare class TextRenderer {
7
- static calculateTextHeight(content: string | TextSegment[], fontSize: number, fontFamily: string, fontStyle: FontStyle, objectManager: FontMetrics, maxWidth: number, maxLines?: number, overflow?: TextOverflow, lineHeight?: number, letterSpacing?: number, lineOptions?: LineOptions): number;
8
- static render(textElement: TextElement, objectManager: PDFObjectManager): Promise<IRNode[]>;
6
+ static render(textElement: TextElement, objectManager: PDFObjectManager, scope: RenderScope): Promise<IRNode[]>;
9
7
  private static _expandColorGlyphs;
10
8
  private static _glyphToPath;
11
9
  private static _toGradient;