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

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 (246) hide show
  1. package/dist/api/color.d.ts +1 -24
  2. package/dist/api/color.js +2 -278
  3. package/dist/api/content.d.ts +10 -5
  4. package/dist/api/content.js +29 -13
  5. package/dist/api/descriptor.js +30 -3
  6. package/dist/api/flow.d.ts +55 -0
  7. package/dist/api/flow.js +71 -0
  8. package/dist/api/font-url.d.ts +1 -2
  9. package/dist/api/font-url.js +8 -74
  10. package/dist/api/forms.d.ts +3 -0
  11. package/dist/api/forms.js +3 -0
  12. package/dist/api/gradient.d.ts +1 -53
  13. package/dist/api/gradient.js +2 -97
  14. package/dist/api/index.d.ts +4 -0
  15. package/dist/api/index.js +3 -0
  16. package/dist/api/layout.d.ts +6 -0
  17. package/dist/api/layout.js +9 -3
  18. package/dist/api/page-builder.d.ts +4 -0
  19. package/dist/api/page-builder.js +4 -0
  20. package/dist/api/structure.d.ts +10 -2
  21. package/dist/api/structure.js +9 -6
  22. package/dist/api/table.d.ts +5 -3
  23. package/dist/api/table.js +101 -22
  24. package/dist/api/text.d.ts +8 -8
  25. package/dist/api/text.js +6 -0
  26. package/dist/canvas/painter.d.ts +2 -2
  27. package/dist/canvas/painter.js +2 -2
  28. package/dist/common/color-input.d.ts +24 -0
  29. package/dist/common/color-input.js +278 -0
  30. package/dist/common/diagnostics.d.ts +61 -0
  31. package/dist/common/diagnostics.js +77 -0
  32. package/dist/common/gradient.d.ts +53 -0
  33. package/dist/common/gradient.js +97 -0
  34. package/dist/edit/acroform-reader.js +7 -7
  35. package/dist/edit/appearance.js +3 -3
  36. package/dist/edit/attachments.d.ts +52 -0
  37. package/dist/edit/attachments.js +178 -0
  38. package/dist/edit/document.d.ts +38 -8
  39. package/dist/edit/document.js +208 -51
  40. package/dist/edit/fill.js +8 -23
  41. package/dist/edit/filters.d.ts +15 -0
  42. package/dist/edit/filters.js +157 -0
  43. package/dist/edit/index.d.ts +4 -1
  44. package/dist/edit/index.js +3 -1
  45. package/dist/edit/objects.d.ts +3 -4
  46. package/dist/edit/objects.js +5 -18
  47. package/dist/elements/canvas-element.d.ts +3 -0
  48. package/dist/elements/canvas-element.js +14 -3
  49. package/dist/elements/container-element.d.ts +8 -1
  50. package/dist/elements/container-element.js +49 -17
  51. package/dist/elements/forms/checkbox-element.d.ts +2 -0
  52. package/dist/elements/forms/checkbox-element.js +12 -3
  53. package/dist/elements/forms/choice-element.d.ts +6 -1
  54. package/dist/elements/forms/choice-element.js +17 -6
  55. package/dist/elements/forms/field-font.d.ts +8 -0
  56. package/dist/elements/forms/field-font.js +5 -0
  57. package/dist/elements/forms/push-button-element.d.ts +6 -1
  58. package/dist/elements/forms/push-button-element.js +16 -4
  59. package/dist/elements/forms/radio-element.d.ts +2 -0
  60. package/dist/elements/forms/radio-element.js +12 -3
  61. package/dist/elements/forms/signature-element.d.ts +6 -1
  62. package/dist/elements/forms/signature-element.js +16 -4
  63. package/dist/elements/forms/text-field-element.d.ts +6 -1
  64. package/dist/elements/forms/text-field-element.js +16 -4
  65. package/dist/elements/image-element.d.ts +70 -21
  66. package/dist/elements/image-element.js +173 -52
  67. package/dist/elements/index.d.ts +3 -0
  68. package/dist/elements/index.js +3 -0
  69. package/dist/elements/layout/anchor-element.d.ts +8 -10
  70. package/dist/elements/layout/anchor-element.js +10 -18
  71. package/dist/elements/layout/bookmark-element.d.ts +9 -11
  72. package/dist/elements/layout/bookmark-element.js +11 -20
  73. package/dist/elements/layout/column-break-element.d.ts +17 -0
  74. package/dist/elements/layout/column-break-element.js +26 -0
  75. package/dist/elements/layout/default-text-style-element.d.ts +11 -13
  76. package/dist/elements/layout/default-text-style-element.js +14 -31
  77. package/dist/elements/layout/deferred-element.d.ts +20 -4
  78. package/dist/elements/layout/deferred-element.js +41 -9
  79. package/dist/elements/layout/expanded-element.d.ts +11 -0
  80. package/dist/elements/layout/expanded-element.js +53 -18
  81. package/dist/elements/layout/flow-element.d.ts +109 -0
  82. package/dist/elements/layout/flow-element.js +317 -0
  83. package/dist/elements/layout/keep-together-element.d.ts +8 -18
  84. package/dist/elements/layout/keep-together-element.js +33 -45
  85. package/dist/elements/layout/link-element.d.ts +8 -13
  86. package/dist/elements/layout/link-element.js +9 -19
  87. package/dist/elements/layout/padding-element.d.ts +18 -1
  88. package/dist/elements/layout/padding-element.js +63 -28
  89. package/dist/elements/layout/page-break-element.d.ts +4 -1
  90. package/dist/elements/layout/page-break-element.js +6 -1
  91. package/dist/elements/layout/page-builder-element.d.ts +5 -0
  92. package/dist/elements/layout/page-builder-element.js +13 -2
  93. package/dist/elements/layout/positioned-element.d.ts +23 -4
  94. package/dist/elements/layout/positioned-element.js +42 -12
  95. package/dist/elements/layout/proxy-element.d.ts +47 -0
  96. package/dist/elements/layout/proxy-element.js +106 -0
  97. package/dist/elements/layout/region-element.d.ts +53 -0
  98. package/dist/elements/layout/region-element.js +108 -0
  99. package/dist/elements/layout/repeating-header-element.d.ts +9 -0
  100. package/dist/elements/layout/repeating-header-element.js +55 -13
  101. package/dist/elements/layout/rotated-box-element.d.ts +6 -0
  102. package/dist/elements/layout/rotated-box-element.js +27 -14
  103. package/dist/elements/layout/rotated-element.d.ts +9 -0
  104. package/dist/elements/layout/rotated-element.js +33 -2
  105. package/dist/elements/layout/struct-group.d.ts +9 -17
  106. package/dist/elements/layout/struct-group.js +10 -35
  107. package/dist/elements/line-element.d.ts +1 -0
  108. package/dist/elements/line-element.js +6 -0
  109. package/dist/elements/page-element.d.ts +10 -3
  110. package/dist/elements/page-element.js +39 -31
  111. package/dist/elements/pdf-document-element.d.ts +3 -0
  112. package/dist/elements/pdf-document-element.js +7 -0
  113. package/dist/elements/pdf-element.d.ts +105 -16
  114. package/dist/elements/pdf-element.js +118 -19
  115. package/dist/elements/rectangle-element.d.ts +22 -2
  116. package/dist/elements/rectangle-element.js +119 -55
  117. package/dist/elements/row-element.d.ts +17 -12
  118. package/dist/elements/row-element.js +89 -9
  119. package/dist/elements/svg-element.d.ts +30 -6
  120. package/dist/elements/svg-element.js +91 -26
  121. package/dist/elements/text-element.d.ts +48 -13
  122. package/dist/elements/text-element.js +161 -116
  123. package/dist/forms/acroform.d.ts +9 -1
  124. package/dist/forms/acroform.js +83 -34
  125. package/dist/forms/appearance.d.ts +17 -6
  126. package/dist/forms/appearance.js +18 -16
  127. package/dist/ir/display-list.d.ts +18 -3
  128. package/dist/layout/box-constraints.d.ts +17 -0
  129. package/dist/layout/box-constraints.js +31 -0
  130. package/dist/layout/collect-images.d.ts +6 -4
  131. package/dist/layout/collect-images.js +14 -8
  132. package/dist/layout/flex-item.d.ts +11 -0
  133. package/dist/layout/flex-item.js +51 -0
  134. package/dist/layout/fragmentation.d.ts +23 -9
  135. package/dist/layout/fragmentation.js +62 -19
  136. package/dist/layout/layout-cache.d.ts +53 -0
  137. package/dist/layout/layout-cache.js +110 -0
  138. package/dist/platform/browser-fs.d.ts +1 -2
  139. package/dist/platform/browser-fs.js +3 -6
  140. package/dist/platform/node-fs.d.ts +0 -1
  141. package/dist/platform/node-fs.js +0 -4
  142. package/dist/renderer/anchor-renderer.d.ts +2 -1
  143. package/dist/renderer/anchor-renderer.js +5 -4
  144. package/dist/renderer/bookmark-renderer.d.ts +2 -1
  145. package/dist/renderer/bookmark-renderer.js +5 -4
  146. package/dist/renderer/canvas-renderer.d.ts +2 -1
  147. package/dist/renderer/canvas-renderer.js +5 -2
  148. package/dist/renderer/column-break-renderer.d.ts +8 -0
  149. package/dist/renderer/column-break-renderer.js +15 -0
  150. package/dist/renderer/container-renderer.d.ts +2 -1
  151. package/dist/renderer/container-renderer.js +6 -5
  152. package/dist/renderer/default-text-style-renderer.d.ts +2 -1
  153. package/dist/renderer/default-text-style-renderer.js +2 -3
  154. package/dist/renderer/deferred-renderer.d.ts +2 -1
  155. package/dist/renderer/deferred-renderer.js +2 -3
  156. package/dist/renderer/emoji-image.js +3 -1
  157. package/dist/renderer/expanded-renderer.d.ts +2 -1
  158. package/dist/renderer/expanded-renderer.js +2 -3
  159. package/dist/renderer/flow-renderer.d.ts +16 -0
  160. package/dist/renderer/flow-renderer.js +21 -0
  161. package/dist/renderer/form-field-renderer.d.ts +2 -1
  162. package/dist/renderer/form-field-renderer.js +16 -3
  163. package/dist/renderer/image-renderer.d.ts +2 -1
  164. package/dist/renderer/image-renderer.js +33 -53
  165. package/dist/renderer/index.d.ts +2 -0
  166. package/dist/renderer/index.js +2 -0
  167. package/dist/renderer/keep-together-renderer.d.ts +2 -1
  168. package/dist/renderer/keep-together-renderer.js +2 -3
  169. package/dist/renderer/line-renderer.d.ts +2 -1
  170. package/dist/renderer/line-renderer.js +7 -2
  171. package/dist/renderer/link-renderer.d.ts +2 -1
  172. package/dist/renderer/link-renderer.js +36 -5
  173. package/dist/renderer/padding-renderer.d.ts +2 -1
  174. package/dist/renderer/padding-renderer.js +5 -5
  175. package/dist/renderer/page-break-renderer.d.ts +6 -5
  176. package/dist/renderer/page-break-renderer.js +14 -7
  177. package/dist/renderer/page-builder-renderer.d.ts +2 -1
  178. package/dist/renderer/page-builder-renderer.js +2 -3
  179. package/dist/renderer/page-renderer.js +30 -10
  180. package/dist/renderer/pdf-backend.d.ts +4 -3
  181. package/dist/renderer/pdf-backend.js +14 -7
  182. package/dist/renderer/pdf-document-renderer.d.ts +1 -8
  183. package/dist/renderer/pdf-document-renderer.js +17 -17
  184. package/dist/renderer/pdf-renderer.js +20 -11
  185. package/dist/renderer/positioned-renderer.d.ts +18 -1
  186. package/dist/renderer/positioned-renderer.js +43 -6
  187. package/dist/renderer/rectangle-renderer.d.ts +2 -1
  188. package/dist/renderer/rectangle-renderer.js +18 -8
  189. package/dist/renderer/repeating-header-renderer.d.ts +2 -1
  190. package/dist/renderer/repeating-header-renderer.js +6 -5
  191. package/dist/renderer/rotated-renderer.d.ts +2 -1
  192. package/dist/renderer/rotated-renderer.js +9 -5
  193. package/dist/renderer/row-renderer.d.ts +2 -1
  194. package/dist/renderer/row-renderer.js +6 -5
  195. package/dist/renderer/struct-group-renderer.d.ts +2 -1
  196. package/dist/renderer/struct-group-renderer.js +2 -3
  197. package/dist/renderer/svg-renderer.d.ts +2 -1
  198. package/dist/renderer/svg-renderer.js +8 -2
  199. package/dist/renderer/text-renderer.d.ts +4 -6
  200. package/dist/renderer/text-renderer.js +114 -127
  201. package/dist/svg/gradients.js +5 -0
  202. package/dist/svg/index.d.ts +1 -1
  203. package/dist/svg/index.js +36 -6
  204. package/dist/svg/style.d.ts +2 -0
  205. package/dist/svg/style.js +12 -1
  206. package/dist/text/advance.d.ts +4 -0
  207. package/dist/text/advance.js +17 -4
  208. package/dist/text/bidi.d.ts +0 -2
  209. package/dist/text/bidi.js +0 -10
  210. package/dist/text/glyph-coverage.d.ts +1 -1
  211. package/dist/text/glyph-coverage.js +7 -3
  212. package/dist/text/hyphen-breaks.d.ts +17 -0
  213. package/dist/text/hyphen-breaks.js +54 -0
  214. package/dist/text/line-breaker.d.ts +55 -29
  215. package/dist/text/line-breaker.js +453 -188
  216. package/dist/text/line-metrics.d.ts +8 -3
  217. package/dist/text/line-metrics.js +11 -2
  218. package/dist/text/pdf-text-string.d.ts +21 -0
  219. package/dist/text/pdf-text-string.js +114 -0
  220. package/dist/text/text-decoration.d.ts +5 -5
  221. package/dist/text/text-decoration.js +5 -5
  222. package/dist/text/word-splitting.d.ts +15 -2
  223. package/dist/text/word-splitting.js +45 -13
  224. package/dist/utils/afm-parser.d.ts +2 -0
  225. package/dist/utils/afm-parser.js +10 -1
  226. package/dist/utils/dest-registry.d.ts +1 -1
  227. package/dist/utils/dest-registry.js +3 -2
  228. package/dist/utils/fetch-bytes.d.ts +19 -0
  229. package/dist/utils/fetch-bytes.js +79 -0
  230. package/dist/utils/flex-layout.d.ts +16 -14
  231. package/dist/utils/flex-layout.js +427 -217
  232. package/dist/utils/font-metrics.d.ts +4 -3
  233. package/dist/utils/image-helper.d.ts +59 -11
  234. package/dist/utils/image-helper.js +209 -81
  235. package/dist/utils/pdf-object-manager.d.ts +17 -6
  236. package/dist/utils/pdf-object-manager.js +70 -20
  237. package/dist/utils/renderer-registry.d.ts +49 -2
  238. package/dist/utils/renderer-registry.js +40 -0
  239. package/dist/utils/struct-tree.d.ts +24 -0
  240. package/dist/utils/struct-tree.js +61 -9
  241. package/dist/utils/ttf-parser.d.ts +4 -0
  242. package/dist/utils/ttf-parser.js +30 -6
  243. package/dist/validators/element-validator.js +2 -1
  244. package/package.json +1 -1
  245. package/dist/elements/layout/sized-container-element.d.ts +0 -14
  246. package/dist/elements/layout/sized-container-element.js +0 -32
@@ -0,0 +1,17 @@
1
+ import { BoxConstraints, Offset, Size } from "../../layout/box-constraints.js";
2
+ import { LayoutContext, PDFElement } from "../pdf-element.js";
3
+ /**
4
+ * A forced break to the next Region of a `Flow` - from the last one, the next page's first. Always of
5
+ * the innermost Flow; several skip several. It takes no space and draws nothing: the packer of a Region
6
+ * consumes it, so reaching the render pass means it sat outside one, which is reported there.
7
+ */
8
+ export declare class ColumnBreakElement extends PDFElement {
9
+ /** The page it was last laid out on - only so an ineffective break can name it. */
10
+ page?: number;
11
+ calculateLayout(_constraints: BoxConstraints, _offset: Offset, ctx: LayoutContext): Size;
12
+ protected computeSize(): Size;
13
+ isColumnBreak(): boolean;
14
+ /** So a container around it is packed (and the break found) instead of placed whole. */
15
+ hasForcedBreak(): boolean;
16
+ getProps(): Record<string, never>;
17
+ }
@@ -0,0 +1,26 @@
1
+ import { PDFElement } from "../pdf-element.js";
2
+ import { pageOf } from "../../common/diagnostics.js";
3
+ /**
4
+ * A forced break to the next Region of a `Flow` - from the last one, the next page's first. Always of
5
+ * the innermost Flow; several skip several. It takes no space and draws nothing: the packer of a Region
6
+ * consumes it, so reaching the render pass means it sat outside one, which is reported there.
7
+ */
8
+ export class ColumnBreakElement extends PDFElement {
9
+ calculateLayout(_constraints, _offset, ctx) {
10
+ this.page = pageOf(ctx);
11
+ return { width: 0, height: 0 };
12
+ }
13
+ computeSize() {
14
+ return { width: 0, height: 0 };
15
+ }
16
+ isColumnBreak() {
17
+ return true;
18
+ }
19
+ /** So a container around it is packed (and the break found) instead of placed whole. */
20
+ hasForcedBreak() {
21
+ return true;
22
+ }
23
+ getProps() {
24
+ return {};
25
+ }
26
+ }
@@ -1,29 +1,27 @@
1
- import { PDFElement, LayoutContext, WithChild, SizedPDFElement } from "../pdf-element.js";
2
- import { BoxConstraints, Offset, Size } from "../../layout/box-constraints.js";
3
- import { Fragmentable, FragmentResult } from "../../layout/fragmentation.js";
1
+ import { PDFElement, LayoutContext, WithChild } from "../pdf-element.js";
2
+ import { ProxyElement } from "./proxy-element.js";
4
3
  import { ResolvedTextStyle } from "../../text/text-style.js";
5
4
  interface DefaultTextStyleParams extends WithChild {
6
5
  style: Partial<ResolvedTextStyle>;
7
6
  }
8
7
  /**
9
8
  * Provides default text properties (font/size/color/lineHeight/align/weight) to its whole subtree -
10
- * Flutter's `DefaultTextStyle`. Transparent to layout: the child takes the same constraints, offset
11
- * and size; only the inherited TextStyle changes. A `Text` below still wins per property, and these
9
+ * Flutter's `DefaultTextStyle`. Transparent to layout (a `ProxyElement`): only the inherited
10
+ * TextStyle changes. A `Text` below still wins per property, and these
12
11
  * overrides layer onto whatever the element already inherited from above.
13
12
  */
14
- export declare class DefaultTextStyleElement extends SizedPDFElement implements Fragmentable {
15
- private child;
13
+ export declare class DefaultTextStyleElement extends ProxyElement {
16
14
  private style;
17
15
  constructor({ child, style }: DefaultTextStyleParams);
18
- private childCtx;
19
- fragment(maxHeight: number, width: number, ctx: LayoutContext): FragmentResult;
20
- private cloneWithChild;
21
- calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
16
+ protected childContext(ctx: LayoutContext): LayoutContext;
17
+ private merged?;
18
+ private mergedOnto;
19
+ protected rewrap(child: PDFElement): PDFElement;
22
20
  getProps(): {
23
21
  x: number;
24
22
  y: number;
25
- width: number | undefined;
26
- height: number | undefined;
23
+ width: number;
24
+ height: number;
27
25
  child: PDFElement;
28
26
  };
29
27
  }
@@ -1,46 +1,29 @@
1
- import { SizedPDFElement } from "../pdf-element.js";
2
- import { isFragmentable } from "../../layout/fragmentation.js";
1
+ import { ProxyElement } from "./proxy-element.js";
3
2
  import { DEFAULT_TEXT_STYLE, mergeTextStyle } from "../../text/text-style.js";
4
3
  /**
5
4
  * Provides default text properties (font/size/color/lineHeight/align/weight) to its whole subtree -
6
- * Flutter's `DefaultTextStyle`. Transparent to layout: the child takes the same constraints, offset
7
- * and size; only the inherited TextStyle changes. A `Text` below still wins per property, and these
5
+ * Flutter's `DefaultTextStyle`. Transparent to layout (a `ProxyElement`): only the inherited
6
+ * TextStyle changes. A `Text` below still wins per property, and these
8
7
  * overrides layer onto whatever the element already inherited from above.
9
8
  */
10
- export class DefaultTextStyleElement extends SizedPDFElement {
9
+ export class DefaultTextStyleElement extends ProxyElement {
11
10
  constructor({ child, style }) {
12
- super({ x: 0, y: 0 });
13
- this.child = child;
11
+ super(child);
14
12
  this.style = style;
15
13
  }
16
- childCtx(ctx) {
17
- return {
18
- ...ctx,
19
- textStyle: mergeTextStyle(ctx.textStyle ?? DEFAULT_TEXT_STYLE, this.style),
20
- };
14
+ childContext(ctx) {
15
+ return { ...ctx, textStyle: this.mergedOnto(ctx.textStyle ?? DEFAULT_TEXT_STYLE) };
21
16
  }
22
- // Transparent to fragmentation too: split the child against the merged context, re-wrapping each
23
- // half so the remainder on the next page keeps the same defaults.
24
- fragment(maxHeight, width, ctx) {
25
- if (!isFragmentable(this.child))
26
- return { fitted: null, remainder: this };
27
- const split = this.child.fragment(maxHeight, width, this.childCtx(ctx));
28
- return {
29
- fitted: split.fitted ? this.cloneWithChild(split.fitted) : null,
30
- remainder: split.remainder ? this.cloneWithChild(split.remainder) : null,
31
- };
17
+ mergedOnto(inherited) {
18
+ if (this.merged?.onto !== inherited) {
19
+ this.merged = { onto: inherited, style: mergeTextStyle(inherited, this.style) };
20
+ }
21
+ return this.merged.style;
32
22
  }
33
- cloneWithChild(child) {
23
+ // Each piece of a split keeps the defaults, so the remainder on the next page looks the same.
24
+ rewrap(child) {
34
25
  return new DefaultTextStyleElement({ child, style: this.style });
35
26
  }
36
- calculateLayout(constraints, offset, ctx) {
37
- this.x = offset.x;
38
- this.y = offset.y;
39
- const size = this.child.calculateLayout(constraints, offset, this.childCtx(ctx));
40
- this.width = size.width;
41
- this.height = size.height;
42
- return size;
43
- }
44
27
  getProps() {
45
28
  return { x: this.x, y: this.y, width: this.width, height: this.height, child: this.child };
46
29
  }
@@ -2,18 +2,34 @@ import { BoxConstraints, Offset, Size } from "../../layout/box-constraints.js";
2
2
  import { Fragmentable, FragmentResult } from "../../layout/fragmentation.js";
3
3
  import { LayoutContext, PDFElement } from "../pdf-element.js";
4
4
  /**
5
- * Builds its subtree at layout time via `resolve(ctx)`, so the tree can depend on font
6
- * metrics (e.g. a Table resolving `"auto"` column widths from cell content). The engine
7
- * stays table-agnostic - the closure comes from the API layer.
5
+ * Builds its subtree at layout time via `resolve(ctx, width)`, so the tree can depend on font
6
+ * metrics and on the width it gets (e.g. a Table fitting its `"auto"` and `fr` columns to the cell
7
+ * content and its own width). The engine stays table-agnostic - the closure comes from the API layer.
8
8
  */
9
9
  export declare class DeferredElement extends PDFElement implements Fragmentable {
10
10
  private resolve;
11
+ private readonly sources;
11
12
  private composed?;
12
- constructor(resolve: (ctx: LayoutContext) => PDFElement);
13
+ private built;
14
+ /**
15
+ * `sources`: the elements the subtree will be built from (a table's cells), so the async pass before
16
+ * layout can reach what is not built yet - an image from a URL in a cell.
17
+ */
18
+ constructor(resolve: (ctx: LayoutContext, width: number) => PDFElement, sources?: readonly PDFElement[]);
19
+ /** A copy builds a subtree of its own, from copies, so even element cells it holds are its own. */
20
+ clone(): this;
21
+ /** The subtree for this context and width: built once, and again only when one of them changes. */
22
+ private subtree;
13
23
  private build;
14
24
  calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
25
+ /** Its subtree is unknown until built, and could hold a `PageBuilder`; until then assume it does. */
26
+ dependsOnPage(): boolean;
27
+ /** Sizes the subtree for this context - the same one a layout under it will draw. */
28
+ protected computeSize(constraints: BoxConstraints, ctx: LayoutContext): Size;
15
29
  fragment(maxHeight: number, width: number, ctx: LayoutContext): FragmentResult;
30
+ layoutChildren(): readonly PDFElement[];
16
31
  getProps(): {
17
32
  composed: PDFElement | undefined;
33
+ sources: readonly PDFElement[];
18
34
  };
19
35
  }
@@ -1,29 +1,61 @@
1
1
  import { isFragmentable } from "../../layout/fragmentation.js";
2
2
  import { PDFElement } from "../pdf-element.js";
3
+ import { BuiltFromContext } from "../../layout/layout-cache.js";
4
+ const builtOnPage = (subtree) => subtree.dependsOnPage();
3
5
  /**
4
- * Builds its subtree at layout time via `resolve(ctx)`, so the tree can depend on font
5
- * metrics (e.g. a Table resolving `"auto"` column widths from cell content). The engine
6
- * stays table-agnostic - the closure comes from the API layer.
6
+ * Builds its subtree at layout time via `resolve(ctx, width)`, so the tree can depend on font
7
+ * metrics and on the width it gets (e.g. a Table fitting its `"auto"` and `fr` columns to the cell
8
+ * content and its own width). The engine stays table-agnostic - the closure comes from the API layer.
7
9
  */
8
10
  export class DeferredElement extends PDFElement {
9
- constructor(resolve) {
11
+ /**
12
+ * `sources`: the elements the subtree will be built from (a table's cells), so the async pass before
13
+ * layout can reach what is not built yet - an image from a URL in a cell.
14
+ */
15
+ constructor(resolve, sources = []) {
10
16
  super();
11
17
  this.resolve = resolve;
18
+ this.sources = sources;
19
+ this.built = new BuiltFromContext(builtOnPage);
12
20
  }
13
- build(ctx) {
14
- this.composed = this.resolve(ctx);
21
+ /** A copy builds a subtree of its own, from copies, so even element cells it holds are its own. */
22
+ clone() {
23
+ const copy = super.clone();
24
+ const resolve = this.resolve;
25
+ copy.resolve = (ctx, width) => resolve(ctx, width).clone();
26
+ copy.built = new BuiltFromContext(builtOnPage);
27
+ copy.composed = undefined;
28
+ return copy;
29
+ }
30
+ /** The subtree for this context and width: built once, and again only when one of them changes. */
31
+ subtree(ctx, width) {
32
+ return this.built.get(ctx, width, () => this.resolve(ctx, width));
33
+ }
34
+ build(ctx, width) {
35
+ this.composed = this.subtree(ctx, width);
15
36
  return this.composed;
16
37
  }
17
38
  calculateLayout(constraints, offset, ctx) {
18
- return this.build(ctx).calculateLayout(constraints, offset, ctx);
39
+ return this.build(ctx, constraints.maxWidth).calculateLayout(constraints, offset, ctx);
40
+ }
41
+ /** Its subtree is unknown until built, and could hold a `PageBuilder`; until then assume it does. */
42
+ dependsOnPage() {
43
+ return this.composed?.dependsOnPage() ?? true;
44
+ }
45
+ /** Sizes the subtree for this context - the same one a layout under it will draw. */
46
+ computeSize(constraints, ctx) {
47
+ return this.subtree(ctx, constraints.maxWidth).measure(constraints, ctx);
19
48
  }
20
49
  fragment(maxHeight, width, ctx) {
21
- const c = this.build(ctx);
50
+ const c = this.build(ctx, width);
22
51
  return isFragmentable(c)
23
52
  ? c.fragment(maxHeight, width, ctx)
24
53
  : { fitted: this, remainder: null };
25
54
  }
55
+ layoutChildren() {
56
+ return this.composed ? [this.composed] : [];
57
+ }
26
58
  getProps() {
27
- return { composed: this.composed };
59
+ return { composed: this.composed, sources: this.sources };
28
60
  }
29
61
  }
@@ -12,7 +12,12 @@ export declare class ExpandedElement extends FlexiblePDFElement implements Fragm
12
12
  constructor({ flex, basis, basisFactor, child }: ExpandedElementParams);
13
13
  /** Layout-transparent: the floor is whatever the wrapped child needs. */
14
14
  minIntrinsicMain(horizontal: boolean, ctx: LayoutContext): number;
15
+ /** And the ceiling is the child's. */
16
+ maxExtent(horizontal: boolean): number;
15
17
  calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
18
+ protected computeSize(constraints: BoxConstraints, ctx: LayoutContext): Size;
19
+ /** The one sizing path: with an `offset` the child is laid out there, with `null` only measured. */
20
+ private sizeWith;
16
21
  /**
17
22
  * When the column paginates, a flex region can't "fill" across pages, so it delegates to
18
23
  * its child: the child splits and each half is re-wrapped in an Expanded (which then
@@ -20,7 +25,13 @@ export declare class ExpandedElement extends FlexiblePDFElement implements Fragm
20
25
  * whole Expanded moves on.
21
26
  */
22
27
  fragment(maxHeight: number, width: number, ctx: LayoutContext): FragmentResult;
28
+ needsRegion(): boolean;
29
+ needsBoundedMain(horizontal: boolean): boolean;
30
+ hasForcedBreak(): boolean;
31
+ breaksBefore(): boolean;
32
+ breaksAfter(): boolean;
23
33
  private cloneWithChild;
34
+ layoutChildren(): readonly PDFElement[];
24
35
  getProps(): {
25
36
  x: number;
26
37
  y: number;
@@ -15,27 +15,41 @@ export class ExpandedElement extends FlexiblePDFElement {
15
15
  minIntrinsicMain(horizontal, ctx) {
16
16
  return this.child.minIntrinsicMain(horizontal, ctx);
17
17
  }
18
+ /** And the ceiling is the child's. */
19
+ maxExtent(horizontal) {
20
+ return this.child.maxExtent(horizontal);
21
+ }
18
22
  calculateLayout(constraints, offset, ctx) {
19
- if (constraints.hasBoundedWidth)
20
- this.width = constraints.maxWidth;
23
+ Validator.validateFlexElement(this);
24
+ const size = this.sizeWith(constraints, ctx, offset);
21
25
  // Absolute placement from the parent; assignment (not +=) so re-layout is idempotent.
22
26
  this.x = offset.x;
23
27
  this.y = offset.y;
24
- Validator.validateFlexElement(this);
25
- if (constraints.hasBoundedHeight) {
26
- // A bounded region: fill it - the normal flex behavior.
27
- this.height = constraints.maxHeight;
28
- this.child.calculateLayout(BoxConstraints.loose(this.width, this.height), { x: this.x, y: this.y }, ctx);
29
- }
30
- else {
31
- // Unbounded (measuring while paginating): there's no leftover space to fill, so
32
- // collapse to the child's natural height. This lets an overflowing column flow
33
- // instead of the flex silently hiding the overflow.
34
- const childSize = this.child.calculateLayout(BoxConstraints.loose(this.width, Infinity), { x: this.x, y: this.y }, ctx);
35
- this.height = childSize.height;
36
- }
37
- // Top-left coordinates; the Y-flip now happens once at the IR -> backend seam.
38
- return { width: this.width, height: this.height };
28
+ this.width = size.width;
29
+ this.height = size.height;
30
+ return size;
31
+ }
32
+ computeSize(constraints, ctx) {
33
+ return this.sizeWith(constraints, ctx, null);
34
+ }
35
+ /** The one sizing path: with an `offset` the child is laid out there, with `null` only measured. */
36
+ sizeWith(constraints, ctx, offset) {
37
+ // It fills its MAIN axis only. Across it, it is an ordinary child: with no width bound it takes
38
+ // its content's width, from THIS call - never one left over from an earlier layout.
39
+ const width = constraints.hasBoundedWidth ? constraints.maxWidth : Infinity;
40
+ // A bounded region: fill it - the normal flex behavior. Unbounded (measuring while paginating):
41
+ // there's no leftover space to fill, so collapse to the child's natural height. This lets an
42
+ // overflowing column flow instead of the flex silently hiding the overflow.
43
+ // `Expanded(child)` is `flex: 1` on the child: the extent its line SET (the min) is the child's too,
44
+ // so its own width or height gives way to its share - as in CSS and Flutter.
45
+ const childConstraints = new BoxConstraints(constraints.minWidth, width, constraints.minHeight, constraints.hasBoundedHeight ? constraints.maxHeight : Infinity);
46
+ const childSize = offset
47
+ ? this.child.calculateLayout(childConstraints, { x: offset.x, y: offset.y }, ctx)
48
+ : this.child.measure(childConstraints, ctx);
49
+ return {
50
+ width: constraints.hasBoundedWidth ? width : childSize.width,
51
+ height: constraints.hasBoundedHeight ? constraints.maxHeight : childSize.height,
52
+ };
39
53
  }
40
54
  /**
41
55
  * When the column paginates, a flex region can't "fill" across pages, so it delegates to
@@ -51,8 +65,26 @@ export class ExpandedElement extends FlexiblePDFElement {
51
65
  return {
52
66
  fitted: split.fitted ? this.cloneWithChild(split.fitted) : null,
53
67
  remainder: split.remainder ? this.cloneWithChild(split.remainder) : null,
68
+ forceBreak: split.forceBreak,
54
69
  };
55
70
  }
71
+ needsRegion() {
72
+ return this.child.needsRegion();
73
+ }
74
+ // The child IS the flex item: what it needs across the line, the line must give it.
75
+ needsBoundedMain(horizontal) {
76
+ return this.child.needsBoundedMain(horizontal);
77
+ }
78
+ // A break inside the flex child is a break in the flow it sits in.
79
+ hasForcedBreak() {
80
+ return this.child.hasForcedBreak();
81
+ }
82
+ breaksBefore() {
83
+ return this.child.breaksBefore();
84
+ }
85
+ breaksAfter() {
86
+ return this.child.breaksAfter();
87
+ }
56
88
  cloneWithChild(child) {
57
89
  // The basis has to come along: a fragment that lost it would claim only its share of the leftover,
58
90
  // so a `flexBasis` slot would silently change width on the page it continues onto.
@@ -61,7 +93,10 @@ export class ExpandedElement extends FlexiblePDFElement {
61
93
  basis: this.basis,
62
94
  basisFactor: this.basisFactor,
63
95
  child,
64
- });
96
+ }).asPieceOf(this);
97
+ }
98
+ layoutChildren() {
99
+ return [this.child];
65
100
  }
66
101
  getProps() {
67
102
  return {
@@ -0,0 +1,109 @@
1
+ import { BoxConstraints, Offset, Size } from "../../layout/box-constraints.js";
2
+ import { type FragmentResult } from "../../layout/fragmentation.js";
3
+ import { ContainerElement } from "../container-element.js";
4
+ import { LayoutContext, PDFElement, SizedPDFElement } from "../pdf-element.js";
5
+ /** What a template is built from: `index` counts THIS Flow's pages (0 = where it starts). */
6
+ export interface FlowPage {
7
+ index: number;
8
+ pageNumber: number;
9
+ }
10
+ export type FlowTemplate = (page: FlowPage) => PDFElement;
11
+ /** A template with no `Region` would place nothing on every page, forever. */
12
+ export declare class FlowWithoutRegionError extends Error {
13
+ constructor();
14
+ }
15
+ /** A template that leaves every Region without room, where the Flow cannot move on to find more. */
16
+ export declare class FlowRegionsEmptyError extends Error {
17
+ constructor(width: number, height: number);
18
+ }
19
+ /** Laid out with no height to fill: every line would land in the first Region. */
20
+ export declare class FlowUnboundedError extends Error {
21
+ constructor();
22
+ }
23
+ /** A PageBuilder builds again for the page it is drawn on, so Regions inside it lose their content. */
24
+ export declare class RegionInPageBuilderError extends Error {
25
+ constructor();
26
+ }
27
+ /**
28
+ * One page of a Flow: its template, with each Region holding its content. It is drawn laid out at the
29
+ * height it was filled at - not at its own size, which a template that does not fill (`50%`) differs from.
30
+ */
31
+ export declare class FlowPieceElement extends SizedPDFElement {
32
+ private readonly template;
33
+ private readonly size;
34
+ private readonly bound;
35
+ private readonly page;
36
+ constructor(template: PDFElement, size: Size, bound: number, page: object);
37
+ calculateLayout(_constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
38
+ protected computeSize(): Size;
39
+ layoutChildren(): readonly PDFElement[];
40
+ getProps(): {
41
+ child: PDFElement;
42
+ width: number;
43
+ height: number;
44
+ x: number;
45
+ y: number;
46
+ };
47
+ }
48
+ /**
49
+ * Content flowing through the Regions of a template, page after page. It has no height of its own -
50
+ * like an `Expanded`, it takes what it is given: the rest of the page when it is fragmented, the rest
51
+ * of a box with a height otherwise. On its last page it shrinks to what the content used.
52
+ */
53
+ export declare class FlowElement extends SizedPDFElement {
54
+ private readonly content;
55
+ private readonly template;
56
+ private readonly index;
57
+ private readonly balance;
58
+ private piece?;
59
+ constructor({ content, template, index, balance, }: {
60
+ content: ContainerElement;
61
+ template: FlowTemplate;
62
+ index?: number;
63
+ balance?: boolean;
64
+ });
65
+ needsRegion(): boolean;
66
+ /** Its height comes from the parent, as an `Expanded`'s does. */
67
+ needsBoundedMain(horizontal: boolean): boolean;
68
+ /** The template is built from the page it lands on. */
69
+ dependsOnPage(): boolean;
70
+ fragment(maxHeight: number, width: number, ctx: LayoutContext): FragmentResult;
71
+ calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
72
+ /** Unbounded, it has no height of its own (a parent that bounds it asks again); bounded, it fills. */
73
+ protected computeSize(constraints: BoxConstraints, ctx: LayoutContext): Size;
74
+ /**
75
+ * The one place a page of the Flow is made: the template at `width` x `maxHeight`, the content
76
+ * fragmented into its Regions in turn, and - when the content ends here - the template shrunk to
77
+ * what it used, or balanced.
78
+ */
79
+ private fill;
80
+ /**
81
+ * The content fragmented into the laid-out Regions in turn; each holds its piece. `fresh` is how tall
82
+ * each Region is on a full page - what a block that does not fit here may wait for.
83
+ */
84
+ private pour;
85
+ /**
86
+ * The last page, balanced: the smallest height at which the content, poured again, still ends here,
87
+ * so the Regions come out about equally full. Every candidate is a real pour - breaks,
88
+ * `keepTogether` and orphans hold as on any page - and the last one poured is the one drawn. A
89
+ * candidate is silent, and one that had to clip a block it could not break is too low.
90
+ */
91
+ private balanced;
92
+ /**
93
+ * The last page: the smallest height at which every filled Region still holds what it was given.
94
+ * Only a layout knows how a template's Regions answer a height (`%`, stacked, `minHeight`, fixed), so
95
+ * each candidate is laid out and checked. The first is exact for Regions side by side (the full bound
96
+ * less the smallest slack); below it the bound is searched, down to the template's own height.
97
+ * Content that overflowed its Region at full height asks for no more than it had there.
98
+ */
99
+ private shrink;
100
+ layoutChildren(): readonly PDFElement[];
101
+ getProps(): {
102
+ x: number;
103
+ y: number;
104
+ width: number | undefined;
105
+ height: number | undefined;
106
+ child: ContainerElement;
107
+ piece: FlowPieceElement | undefined;
108
+ };
109
+ }