@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,22 +1,29 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PageElement = void 0;
4
- exports.resolvePageContentBox = resolvePageContentBox;
5
- exports.layoutPageBands = layoutPageBands;
6
- const page_sizes_1 = require("../constants/page-sizes");
7
- const pdf_config_1 = require("../renderer/pdf-config");
8
- const box_constraints_1 = require("../layout/box-constraints");
9
- const pdf_element_1 = require("./pdf-element");
1
+ import { pageFormats, PageSize } from "../constants/page-sizes.js";
2
+ import { Orientation } from "../renderer/pdf-config.js";
3
+ import { BoxConstraints } from "../layout/box-constraints.js";
4
+ import { PDFElement } from "./pdf-element.js";
5
+ /**
6
+ * The full page (media box) size in points, orientation applied. This is what a `PageBuilder` sees as
7
+ * `pageSize`; `resolvePageContentBox` below then subtracts the margins.
8
+ */
9
+ export function resolvePageSize(config) {
10
+ // Defaults to A4 when the config is not fully resolved, matching what PageRenderer does for the MediaBox.
11
+ const [pageW, pageH] = config.customSize ?? pageFormats[config.pageSize ?? PageSize.A4];
12
+ return config.orientation === Orientation.landscape
13
+ ? { width: pageH, height: pageW }
14
+ : { width: pageW, height: pageH };
15
+ }
10
16
  /**
11
17
  * The content box of a page (inside the margins, orientation applied) for a fully
12
18
  * resolved config. Single source of truth, shared by `PageElement` layout and the page
13
19
  * driver so they can never drift.
14
20
  */
15
- function resolvePageContentBox(config) {
21
+ export function resolvePageContentBox(config) {
16
22
  const margin = config.margin;
17
- let width = page_sizes_1.pageFormats[config.pageSize][0] - margin.left - margin.right;
18
- let height = page_sizes_1.pageFormats[config.pageSize][1] - margin.top - margin.bottom;
19
- if (config.orientation === pdf_config_1.Orientation.landscape) {
23
+ const [pageW, pageH] = config.customSize ?? pageFormats[config.pageSize];
24
+ let width = pageW - margin.left - margin.right;
25
+ let height = pageH - margin.top - margin.bottom;
26
+ if (config.orientation === Orientation.landscape) {
20
27
  [width, height] = [height, width];
21
28
  }
22
29
  return { origin: { x: margin.left, y: margin.top }, width, height };
@@ -29,17 +36,35 @@ function resolvePageContentBox(config) {
29
36
  * no header/footer the bands are zero and the body equals the full content box - identical
30
37
  * to a page without them.
31
38
  */
32
- function layoutPageBands(config, header, footer, ctx) {
39
+ /**
40
+ * The page's positioning frame: its CONTENT box (paper minus margins). Header, footer and body all
41
+ * resolve a `Positioned` against this same box, so `top`/`bottom` mean the same thing in each.
42
+ *
43
+ * `layoutPageBands` lays the bands out, so its caller must hand it a context carrying this frame -
44
+ * otherwise a `Positioned` in a band has nothing to resolve against (ISSUE-4). A caller that only
45
+ * wants the band HEIGHTS may throw the frame away without draining it: an out-of-flow child
46
+ * contributes no height.
47
+ */
48
+ export function pageFrame(config) {
49
+ const { origin, width, height } = resolvePageContentBox(config);
50
+ return { origin, size: { width, height }, place: [] };
51
+ }
52
+ export function layoutPageBands(config, header, footer, ctx) {
33
53
  const { origin, width, height } = resolvePageContentBox(config);
34
54
  let headerHeight = 0;
35
55
  if (header) {
36
- headerHeight = header.calculateLayout(box_constraints_1.BoxConstraints.loose(width, Infinity), origin, ctx).height;
56
+ headerHeight = header.calculateLayout(BoxConstraints.loose(width, Infinity), origin, ctx).height;
37
57
  }
38
58
  let footerHeight = 0;
39
59
  if (footer) {
40
- // Measure first to learn its height, then place it flush against the bottom edge.
41
- footerHeight = footer.calculateLayout(box_constraints_1.BoxConstraints.loose(width, Infinity), origin, ctx).height;
42
- footer.calculateLayout(box_constraints_1.BoxConstraints.loose(width, Infinity), { x: origin.x, y: origin.y + height - footerHeight }, ctx);
60
+ // Measure first to learn its height, then place it flush against the bottom edge. The MEASURING
61
+ // pass gets a throwaway `place` list: an out-of-flow child registers a placement every time it is
62
+ // laid out, and this subtree is laid out twice. Only the second, real pass may register.
63
+ const measureCtx = ctx.frame
64
+ ? { ...ctx, frame: { ...ctx.frame, place: [] } }
65
+ : ctx;
66
+ footerHeight = footer.calculateLayout(BoxConstraints.loose(width, Infinity), origin, measureCtx).height;
67
+ footer.calculateLayout(BoxConstraints.loose(width, Infinity), { x: origin.x, y: origin.y + height - footerHeight }, ctx);
43
68
  }
44
69
  return {
45
70
  bodyOrigin: { x: origin.x, y: origin.y + headerHeight },
@@ -49,11 +74,11 @@ function layoutPageBands(config, header, footer, ctx) {
49
74
  footerHeight,
50
75
  };
51
76
  }
52
- class PageElement extends pdf_element_1.PDFElement {
77
+ export class PageElement extends PDFElement {
53
78
  constructor({ children, config, header, footer }) {
54
79
  super();
55
80
  this.children = children;
56
- this.config = config !== null && config !== void 0 ? config : {};
81
+ this.config = config ?? {};
57
82
  this.header = header;
58
83
  this.footer = footer;
59
84
  }
@@ -61,18 +86,39 @@ class PageElement extends pdf_element_1.PDFElement {
61
86
  // Merge the document defaults (carried in the context) with this page's overrides,
62
87
  // then hand descendants a context bound to THIS page's geometry. This is what
63
88
  // fixes the old last-page-wins global page-config bug.
64
- this.config = Object.assign(Object.assign({}, ctx.pageConfig), this.config);
89
+ this.config = { ...ctx.pageConfig, ...this.config };
65
90
  const pageCtx = {
66
91
  metrics: ctx.metrics,
67
92
  pageConfig: this.config,
93
+ textStyle: ctx.textStyle,
94
+ onOverflow: ctx.onOverflow,
95
+ pageInfo: ctx.pageInfo, // the render pass supplies it; absent during pagination
68
96
  };
97
+ // The PAGE is a positioning frame, and the frame is the content box - not the body region left
98
+ // over between the bands. So `Positioned` means the same thing in the header, in the footer and
99
+ // in the body, `bottom: 0` is the foot of the page rather than the top of the footer, and adding
100
+ // a header no longer shifts a page-level `Positioned` that nobody touched. A `relative` Box
101
+ // still overrides it for its own subtree.
102
+ //
103
+ // Built BEFORE the bands are laid out: they need to see it, and their heights are not known yet
104
+ // anyway. (That is the whole of ISSUE-4 - a `Positioned` in a band had no frame to resolve
105
+ // against, so it silently stayed at the page's top-left corner.)
106
+ const frame = pageFrame(this.config);
107
+ const frameCtx = { ...pageCtx, frame };
69
108
  // Place the header/footer bands; the body gets the region left in between (the whole
70
109
  // content box when there is neither - byte-identical to a plain page).
71
- const bands = layoutPageBands(this.config, this.header, this.footer, pageCtx);
72
- const childConstraints = box_constraints_1.BoxConstraints.loose(bands.bodyWidth, bands.bodyHeight);
73
- this.children.forEach((child) => child.calculateLayout(childConstraints, bands.bodyOrigin, pageCtx));
74
- const { width, height } = resolvePageContentBox(this.config);
75
- return { width, height };
110
+ const bands = layoutPageBands(this.config, this.header, this.footer, frameCtx);
111
+ const childConstraints = BoxConstraints.loose(bands.bodyWidth, bands.bodyHeight);
112
+ this.children.forEach((child) => child.calculateLayout(childConstraints, bands.bodyOrigin, frameCtx));
113
+ // Drained last, once every band and the body have registered: an out-of-flow child is placed
114
+ // against the final frame box and paints on top of the flow content.
115
+ //
116
+ // The callback gets `frameCtx`, not `pageCtx`: a `Positioned` NESTED inside an out-of-flow child
117
+ // must still see a frame. It appends to `frame.place` while we walk it, and a `for..of` over an
118
+ // array visits what was appended, so nested placements drain in the same loop.
119
+ for (const place of frame.place)
120
+ place(frame, frameCtx);
121
+ return { width: frame.size.width, height: frame.size.height };
76
122
  }
77
123
  getProps() {
78
124
  return {
@@ -87,4 +133,3 @@ class PageElement extends pdf_element_1.PDFElement {
87
133
  return this;
88
134
  }
89
135
  }
90
- exports.PageElement = PageElement;
@@ -1,12 +1,18 @@
1
- import { PageElement } from "./page-element";
2
- import { BoxConstraints, Offset, Size } from "../layout/box-constraints";
3
- import { LayoutContext, PDFElement, WithChildren } from "./pdf-element";
1
+ import { PageElement } from "./page-element.js";
2
+ import { BoxConstraints, Offset, Size } from "../layout/box-constraints.js";
3
+ import { LayoutContext, PDFElement, WithChildren } from "./pdf-element.js";
4
+ import type { ResolvedTextStyle } from "../text/text-style.js";
4
5
  interface PDFDocumentParams extends WithChildren {
5
6
  children: PageElement[];
7
+ /** Document-level text defaults descendants inherit (seeded into the layout-context root). */
8
+ defaultTextStyle?: Partial<ResolvedTextStyle>;
6
9
  }
7
10
  export declare class PDFDocumentElement extends PDFElement {
8
11
  private children;
9
- constructor({ children }: PDFDocumentParams);
12
+ private defaultTextStyle?;
13
+ constructor({ children, defaultTextStyle }: PDFDocumentParams);
14
+ /** The document-level text defaults; the renderer merges them into the cascade root. */
15
+ getDefaultTextStyle(): Partial<ResolvedTextStyle> | undefined;
10
16
  calculateLayout(_constraints: BoxConstraints, _offset: Offset, ctx: LayoutContext): Size;
11
17
  getProps(): PDFDocumentParams;
12
18
  }
@@ -1,22 +1,23 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PDFDocumentElement = void 0;
4
- const box_constraints_1 = require("../layout/box-constraints");
5
- const pdf_element_1 = require("./pdf-element");
6
- class PDFDocumentElement extends pdf_element_1.PDFElement {
7
- constructor({ children }) {
1
+ import { BoxConstraints } from "../layout/box-constraints.js";
2
+ import { PDFElement } from "./pdf-element.js";
3
+ export class PDFDocumentElement extends PDFElement {
4
+ constructor({ children, defaultTextStyle }) {
8
5
  super();
9
6
  this.children = children;
7
+ this.defaultTextStyle = defaultTextStyle;
8
+ }
9
+ /** The document-level text defaults; the renderer merges them into the cascade root. */
10
+ getDefaultTextStyle() {
11
+ return this.defaultTextStyle;
10
12
  }
11
13
  calculateLayout(_constraints, _offset, ctx) {
12
14
  // The document is the root: each page derives its own geometry, so it ignores the
13
15
  // incoming constraints/offset. It has no size of its own.
14
16
  const origin = { x: 0, y: 0 };
15
- this.children.forEach((child) => child.calculateLayout(new box_constraints_1.BoxConstraints(), origin, ctx));
17
+ this.children.forEach((child) => child.calculateLayout(new BoxConstraints(), origin, ctx));
16
18
  return { width: 0, height: 0 };
17
19
  }
18
20
  getProps() {
19
- return { children: this.children };
21
+ return { children: this.children, defaultTextStyle: this.defaultTextStyle };
20
22
  }
21
23
  }
22
- exports.PDFDocumentElement = PDFDocumentElement;
@@ -1,17 +1,77 @@
1
- import type { FontMetrics } from "../utils/font-metrics";
2
- import type { PDFPageConfig } from "./page-element";
3
- import type { BoxConstraints, Offset, Size } from "../layout/box-constraints";
1
+ import type { FontMetrics } from "../utils/font-metrics.js";
2
+ import type { PDFPageConfig } from "./page-element.js";
3
+ import type { ResolvedTextStyle } from "../text/text-style.js";
4
+ import type { OverflowPolicy } from "../layout/fragmentation.js";
5
+ import type { BoxConstraints, Offset, Size } from "../layout/box-constraints.js";
6
+ import type { CrossAlign } from "../layout/alignment.js";
4
7
  /**
5
8
  * Everything the layout pass needs, threaded explicitly (no global singleton):
6
9
  * font metrics for measuring, and the geometry of the page currently being laid out.
7
10
  * `PageElement` sets `pageConfig` for its subtree, so each page flips against its own
8
11
  * height. The PDF byte writer is deliberately absent - layout must not touch it.
9
12
  */
13
+ /**
14
+ * A positioning frame - the CSS "containing block" that `Positioned` children resolve their
15
+ * offsets against. A `relative` Box (later the page) creates one and threads it to its subtree;
16
+ * `Positioned` descendants register a placement closure in `place`, which the frame drains once it
17
+ * has finished sizing itself (so `right`/`bottom` can resolve against the final box).
18
+ */
19
+ export interface PositioningFrame {
20
+ origin: Offset;
21
+ size: Size;
22
+ place: Array<(frame: {
23
+ origin: Offset;
24
+ size: Size;
25
+ }, ctx: LayoutContext) => void>;
26
+ }
27
+ /**
28
+ * What a `PageBuilder` learns about the page it landed on. `pageNumber` is 1-based and counts PHYSICAL
29
+ * pages across the whole document (a logical `Page` that overflows contributes several); `pageCount` is
30
+ * the document's final physical page total. `pageSize` is the media box in points, orientation applied.
31
+ */
32
+ export interface PageInfo {
33
+ /** 1-based, counting PHYSICAL pages across the whole document. */
34
+ pageNumber: number;
35
+ /** Physical pages in the whole document. */
36
+ pageCount: number;
37
+ /**
38
+ * 1-based within the LOGICAL page this sheet came from - the `Page(...)` element the author wrote.
39
+ * A document of one `Page` has `subPageNumber === pageNumber`; a document of several (an invoice
40
+ * plus its attachment, say) restarts the count at each one.
41
+ */
42
+ subPageNumber: number;
43
+ /** Physical pages this logical page produced. */
44
+ subPageTotalPages: number;
45
+ pageSize: {
46
+ width: number;
47
+ height: number;
48
+ };
49
+ }
10
50
  export interface LayoutContext {
11
51
  metrics: FontMetrics;
12
52
  pageConfig: PDFPageConfig;
53
+ /** The page a `PageBuilder` is being laid out on. Only the render pass knows the real numbers, so
54
+ * during pagination this is absent and a `PageBuilder` builds against a provisional "1 of 1". */
55
+ pageInfo?: PageInfo;
56
+ /** The nearest enclosing positioning frame, if any (set by a `relative` Box). */
57
+ frame?: PositioningFrame;
58
+ /** The cascaded text style descendants inherit (CSS/Flutter-style). Seeded at the document root
59
+ * from the built-in defaults + the `Document` defaults, and carried through page contexts; a
60
+ * `Text` resolves its own unset properties against it. Absent falls back to the built-in defaults. */
61
+ textStyle?: ResolvedTextStyle;
62
+ /** What to do when an element overflows a page region and cannot break (set from the render option;
63
+ * absent = clip silently). Evaluated in `packChildren` where the forced placement happens. */
64
+ onOverflow?: OverflowPolicy;
65
+ /** The FULL body height of the current page (between header and footer), set by the page driver. A
66
+ * `keepTogether` group reads it to decide the degrade rule: veto splitting only when the group would
67
+ * fit on a fresh page; a group taller than a whole page drops the veto and splits. */
68
+ pageBodyHeight?: number;
13
69
  }
14
70
  export declare abstract class PDFElement {
71
+ private _structId;
72
+ get structId(): number;
73
+ /** Make this element share `from`'s tagging identity - used by fragmentation clones. Returns this. */
74
+ adoptStructId(from: PDFElement): this;
15
75
  abstract getProps(): unknown;
16
76
  /**
17
77
  * Lays the element out: `constraints` bound its size (flows DOWN), `offset` is the
@@ -20,6 +80,70 @@ export declare abstract class PDFElement {
20
80
  * happens once at the IR -> backend seam.
21
81
  */
22
82
  abstract calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
83
+ /**
84
+ * The relative-size fraction (0..1) this element asked for on the given axis, if any (`horizontal` =
85
+ * width). A flex parent reads it to resolve a percentage child against its OWN main size - e.g. a
86
+ * `Box({ width: "50%" })` inside a Row, where the child otherwise gets an unbounded main axis and the
87
+ * fraction would have nothing to resolve against. Default: the element is not relatively sized.
88
+ */
89
+ relativeSizeFactor(_horizontal: boolean): number | undefined;
90
+ /**
91
+ * Whether this element needs a BOUNDED extent on the given axis to lay itself out (`true` for a
92
+ * horizontal axis = width). A flex parent measures its fixed children with an unbounded main axis, so
93
+ * they keep their natural size; but a stack holding an `Expanded`/`Spacer` has no leftover space to
94
+ * distribute unless it is told how much main axis it may occupy. Such a stack answers `true` here and
95
+ * the parent hands it the line's bounded main extent instead of `Infinity`. Default: no.
96
+ */
97
+ needsBoundedMain(_horizontal: boolean): boolean;
98
+ /**
99
+ * Whether this element IS a forced page break (a `PageBreak`). The pagination packer cuts the flow
100
+ * at such a marker: everything before it stays, everything after starts a new page. Default: no.
101
+ */
102
+ isPageBreak(): boolean;
103
+ /**
104
+ * Whether this element is, or CONTAINS anywhere in its subtree, a forced page break. A container
105
+ * that merely fits by height must still be fragmented when this is true, so a break nested inside
106
+ * it is honoured rather than swallowed. Containers override this to recurse into their children.
107
+ * Default: no.
108
+ */
109
+ hasForcedBreak(): boolean;
110
+ /**
111
+ * Whether this element asks to START on a fresh page (CSS `break-before: page`, react-pdf's `break`
112
+ * prop). Read by the parent packer at the child boundary, not by the element itself; ignored when the
113
+ * element is already at the top of a region (nothing before it), so it never makes an empty page.
114
+ * Default: no.
115
+ */
116
+ breaksBefore(): boolean;
117
+ /**
118
+ * Whether everything AFTER this element must start a fresh page (CSS `break-after: page`). Read by the
119
+ * parent packer once this element has been placed whole. Default: no.
120
+ */
121
+ breaksAfter(): boolean;
122
+ /**
123
+ * This child's own cross-axis alignment inside its Column / Row (CSS `align-self`), overriding the
124
+ * container's `align`. Lives on the base element because any child of a stack may set it, not just
125
+ * the sized ones. `undefined` means "follow the container".
126
+ */
127
+ alignSelf?: CrossAlign;
128
+ /** Sets `alignSelf` and returns the element, so a factory can thread it through in one expression. */
129
+ withAlignSelf(align: CrossAlign | undefined): this;
130
+ /**
131
+ * Where this child sits among its siblings (CSS `order`), lowest first, source order deciding ties.
132
+ * It moves the child in the LAYOUT only - the element tree, and therefore the reading order a tagged
133
+ * PDF exposes, is untouched. Default 0.
134
+ */
135
+ order: number;
136
+ /**
137
+ * How willingly this child gives up main-axis space when the line overflows (CSS `flex-shrink`),
138
+ * weighted by its own size as CSS does. **Default 0, deliberately unlike CSS's 1**: shrinking is a
139
+ * change of what a document LOOKS like, and every document written before this existed must keep
140
+ * laying out exactly as it did. Opt in per child.
141
+ */
142
+ flexShrink: number;
143
+ /** Sets `flexShrink` and returns the element. */
144
+ withFlexShrink(shrink: number | undefined): this;
145
+ /** Sets `order` and returns the element, so a factory can thread it through in one expression. */
146
+ withOrder(order: number | undefined): this;
23
147
  }
24
148
  export declare abstract class SizedPDFElement extends PDFElement {
25
149
  protected x: number;
@@ -32,14 +156,32 @@ export declare abstract class SizedPDFElement extends PDFElement {
32
156
  export declare abstract class FlexiblePDFElement extends PDFElement {
33
157
  protected flex: number;
34
158
  protected verticalChildAlignment: VerticalAlignment;
159
+ /** Points reserved before any leftover is shared out (CSS `flex-basis`); default 0. */
160
+ protected basis: number;
161
+ /** The same as a fraction of the line, for `flexBasis: "25%"`. */
162
+ protected basisFactor?: number;
35
163
  constructor(data: FlexibleElement);
36
164
  getFlex(): number;
165
+ /**
166
+ * The main extent this child starts from, before the leftover is shared out. `lineMain` is what the
167
+ * line offers its items (its extent minus the gaps), so a percentage basis resolves against the same
168
+ * base a percentage WIDTH already does - and falls back to 0 on an unbounded axis, where a fraction
169
+ * has no meaning.
170
+ */
171
+ /** Whether a PERCENTAGE basis was given - it resolves to 0 on an unbounded axis, so asking for the
172
+ * number alone cannot tell "no basis" from "a basis that does not apply here". */
173
+ hasBasisFactor(): boolean;
174
+ getBasis(lineMain: number): number;
37
175
  }
38
176
  export declare enum HorizontalAlignment {
177
+ /** CSS `text-align: start` - the side the writing STARTS on: left in `ltr`, right in `rtl`. The
178
+ * default, so a right-to-left paragraph begins on the right without anyone asking for it. */
179
+ start = "START",
39
180
  left = "LEFT",
40
181
  right = "RIGHT",
41
182
  center = "CENTER",
42
- block = "BLOCK"
183
+ /** CSS `text-align: justify` - both edges line up, the last line of a paragraph excepted. */
184
+ justify = "JUSTIFY"
43
185
  }
44
186
  export declare enum VerticalAlignment {
45
187
  top = "TOP",
@@ -54,6 +196,10 @@ export interface WithChild {
54
196
  }
55
197
  export interface FlexibleElement {
56
198
  flex: number;
199
+ /** Points reserved before the leftover is shared out (CSS `flex-basis`). */
200
+ basis?: number;
201
+ /** The same as a fraction of the line, for `flexBasis: "25%"`. */
202
+ basisFactor?: number;
57
203
  verticalChildAlignment?: VerticalAlignment;
58
204
  }
59
205
  export interface SizedElement {
@@ -1,13 +1,110 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VerticalAlignment = exports.HorizontalAlignment = exports.FlexiblePDFElement = exports.SizedPDFElement = exports.PDFElement = void 0;
4
- exports.isSizedElement = isSizedElement;
5
- exports.hasChildrenProp = hasChildrenProp;
6
- exports.hasChildProp = hasChildProp;
7
- class PDFElement {
1
+ let _nextStructId = 0;
2
+ export class PDFElement {
3
+ constructor() {
4
+ // Stable identity for accessible (PDF/UA) tagging: assigned once at construction and carried through
5
+ // fragmentation copies (adoptStructId), so a paragraph or table split across pages stays ONE structure
6
+ // element. Only the elements that actually tag (Text, Image, StructGroup) read it.
7
+ this._structId = _nextStructId++;
8
+ /**
9
+ * Where this child sits among its siblings (CSS `order`), lowest first, source order deciding ties.
10
+ * It moves the child in the LAYOUT only - the element tree, and therefore the reading order a tagged
11
+ * PDF exposes, is untouched. Default 0.
12
+ */
13
+ this.order = 0;
14
+ /**
15
+ * How willingly this child gives up main-axis space when the line overflows (CSS `flex-shrink`),
16
+ * weighted by its own size as CSS does. **Default 0, deliberately unlike CSS's 1**: shrinking is a
17
+ * change of what a document LOOKS like, and every document written before this existed must keep
18
+ * laying out exactly as it did. Opt in per child.
19
+ */
20
+ this.flexShrink = 0;
21
+ }
22
+ get structId() {
23
+ return this._structId;
24
+ }
25
+ /** Make this element share `from`'s tagging identity - used by fragmentation clones. Returns this. */
26
+ adoptStructId(from) {
27
+ this._structId = from._structId;
28
+ return this;
29
+ }
30
+ /**
31
+ * The relative-size fraction (0..1) this element asked for on the given axis, if any (`horizontal` =
32
+ * width). A flex parent reads it to resolve a percentage child against its OWN main size - e.g. a
33
+ * `Box({ width: "50%" })` inside a Row, where the child otherwise gets an unbounded main axis and the
34
+ * fraction would have nothing to resolve against. Default: the element is not relatively sized.
35
+ */
36
+ relativeSizeFactor(_horizontal) {
37
+ return undefined;
38
+ }
39
+ /**
40
+ * Whether this element needs a BOUNDED extent on the given axis to lay itself out (`true` for a
41
+ * horizontal axis = width). A flex parent measures its fixed children with an unbounded main axis, so
42
+ * they keep their natural size; but a stack holding an `Expanded`/`Spacer` has no leftover space to
43
+ * distribute unless it is told how much main axis it may occupy. Such a stack answers `true` here and
44
+ * the parent hands it the line's bounded main extent instead of `Infinity`. Default: no.
45
+ */
46
+ needsBoundedMain(_horizontal) {
47
+ return false;
48
+ }
49
+ /**
50
+ * Whether this element IS a forced page break (a `PageBreak`). The pagination packer cuts the flow
51
+ * at such a marker: everything before it stays, everything after starts a new page. Default: no.
52
+ */
53
+ isPageBreak() {
54
+ return false;
55
+ }
56
+ /**
57
+ * Whether this element is, or CONTAINS anywhere in its subtree, a forced page break. A container
58
+ * that merely fits by height must still be fragmented when this is true, so a break nested inside
59
+ * it is honoured rather than swallowed. Containers override this to recurse into their children.
60
+ * Default: no.
61
+ */
62
+ hasForcedBreak() {
63
+ return false;
64
+ }
65
+ /**
66
+ * Whether this element asks to START on a fresh page (CSS `break-before: page`, react-pdf's `break`
67
+ * prop). Read by the parent packer at the child boundary, not by the element itself; ignored when the
68
+ * element is already at the top of a region (nothing before it), so it never makes an empty page.
69
+ * Default: no.
70
+ */
71
+ breaksBefore() {
72
+ return false;
73
+ }
74
+ /**
75
+ * Whether everything AFTER this element must start a fresh page (CSS `break-after: page`). Read by the
76
+ * parent packer once this element has been placed whole. Default: no.
77
+ */
78
+ breaksAfter() {
79
+ return false;
80
+ }
81
+ /** Sets `alignSelf` and returns the element, so a factory can thread it through in one expression. */
82
+ withAlignSelf(align) {
83
+ if (align !== undefined)
84
+ this.alignSelf = align;
85
+ return this;
86
+ }
87
+ /** Sets `flexShrink` and returns the element. */
88
+ withFlexShrink(shrink) {
89
+ if (shrink !== undefined) {
90
+ if (!Number.isFinite(shrink) || shrink < 0) {
91
+ throw new Error(`Invalid flexShrink ${shrink}: it must be a number of at least 0.`);
92
+ }
93
+ this.flexShrink = shrink;
94
+ }
95
+ return this;
96
+ }
97
+ /** Sets `order` and returns the element, so a factory can thread it through in one expression. */
98
+ withOrder(order) {
99
+ if (order !== undefined) {
100
+ if (!Number.isFinite(order))
101
+ throw new Error(`Invalid order ${order}: it must be a number.`);
102
+ this.order = order;
103
+ }
104
+ return this;
105
+ }
8
106
  }
9
- exports.PDFElement = PDFElement;
10
- class SizedPDFElement extends PDFElement {
107
+ export class SizedPDFElement extends PDFElement {
11
108
  constructor(data) {
12
109
  super();
13
110
  this.x = data.x;
@@ -19,37 +116,58 @@ class SizedPDFElement extends PDFElement {
19
116
  return { x: this.x, y: this.y, width: this.width, height: this.height };
20
117
  }
21
118
  }
22
- exports.SizedPDFElement = SizedPDFElement;
23
- class FlexiblePDFElement extends PDFElement {
119
+ export class FlexiblePDFElement extends PDFElement {
24
120
  constructor(data) {
25
121
  super();
26
122
  this.flex = data.flex;
27
123
  this.verticalChildAlignment = data.verticalChildAlignment || VerticalAlignment.middle;
124
+ this.basis = data.basis ?? 0;
125
+ this.basisFactor = data.basisFactor;
28
126
  }
29
127
  getFlex() {
30
128
  return this.flex;
31
129
  }
130
+ /**
131
+ * The main extent this child starts from, before the leftover is shared out. `lineMain` is what the
132
+ * line offers its items (its extent minus the gaps), so a percentage basis resolves against the same
133
+ * base a percentage WIDTH already does - and falls back to 0 on an unbounded axis, where a fraction
134
+ * has no meaning.
135
+ */
136
+ /** Whether a PERCENTAGE basis was given - it resolves to 0 on an unbounded axis, so asking for the
137
+ * number alone cannot tell "no basis" from "a basis that does not apply here". */
138
+ hasBasisFactor() {
139
+ return this.basisFactor !== undefined;
140
+ }
141
+ getBasis(lineMain) {
142
+ if (this.basisFactor !== undefined) {
143
+ return Number.isFinite(lineMain) ? lineMain * this.basisFactor : 0;
144
+ }
145
+ return this.basis;
146
+ }
32
147
  }
33
- exports.FlexiblePDFElement = FlexiblePDFElement;
34
- var HorizontalAlignment;
148
+ export var HorizontalAlignment;
35
149
  (function (HorizontalAlignment) {
150
+ /** CSS `text-align: start` - the side the writing STARTS on: left in `ltr`, right in `rtl`. The
151
+ * default, so a right-to-left paragraph begins on the right without anyone asking for it. */
152
+ HorizontalAlignment["start"] = "START";
36
153
  HorizontalAlignment["left"] = "LEFT";
37
154
  HorizontalAlignment["right"] = "RIGHT";
38
155
  HorizontalAlignment["center"] = "CENTER";
39
- HorizontalAlignment["block"] = "BLOCK";
40
- })(HorizontalAlignment || (exports.HorizontalAlignment = HorizontalAlignment = {}));
41
- var VerticalAlignment;
156
+ /** CSS `text-align: justify` - both edges line up, the last line of a paragraph excepted. */
157
+ HorizontalAlignment["justify"] = "JUSTIFY";
158
+ })(HorizontalAlignment || (HorizontalAlignment = {}));
159
+ export var VerticalAlignment;
42
160
  (function (VerticalAlignment) {
43
161
  VerticalAlignment["top"] = "TOP";
44
162
  VerticalAlignment["middle"] = "MIDDLE";
45
163
  VerticalAlignment["bottom"] = "BOTTOM";
46
- })(VerticalAlignment || (exports.VerticalAlignment = VerticalAlignment = {}));
47
- function isSizedElement(obj) {
164
+ })(VerticalAlignment || (VerticalAlignment = {}));
165
+ export function isSizedElement(obj) {
48
166
  return typeof obj === "object" && obj !== null && "x" in obj && "y" in obj;
49
167
  }
50
- function hasChildrenProp(obj) {
168
+ export function hasChildrenProp(obj) {
51
169
  return "children" in obj;
52
170
  }
53
- function hasChildProp(obj) {
171
+ export function hasChildProp(obj) {
54
172
  return "child" in obj;
55
173
  }