@jasy/pdf 1.0.0-alpha.1

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 (139) hide show
  1. package/README.md +171 -0
  2. package/dist/api/args.d.ts +10 -0
  3. package/dist/api/args.js +13 -0
  4. package/dist/api/color.d.ts +24 -0
  5. package/dist/api/color.js +215 -0
  6. package/dist/api/content.d.ts +36 -0
  7. package/dist/api/content.js +50 -0
  8. package/dist/api/descriptor.d.ts +25 -0
  9. package/dist/api/descriptor.js +71 -0
  10. package/dist/api/index.d.ts +8 -0
  11. package/dist/api/index.js +27 -0
  12. package/dist/api/insets.d.ts +16 -0
  13. package/dist/api/insets.js +21 -0
  14. package/dist/api/layout.d.ts +72 -0
  15. package/dist/api/layout.js +99 -0
  16. package/dist/api/structure.d.ts +80 -0
  17. package/dist/api/structure.js +125 -0
  18. package/dist/api/table.d.ts +37 -0
  19. package/dist/api/table.js +87 -0
  20. package/dist/api/text.d.ts +28 -0
  21. package/dist/api/text.js +61 -0
  22. package/dist/assets/Courier-Bold.afm +342 -0
  23. package/dist/assets/Courier-BoldOblique.afm +342 -0
  24. package/dist/assets/Courier-Oblique.afm +342 -0
  25. package/dist/assets/Courier.afm +342 -0
  26. package/dist/assets/Helvetica-Bold.afm +2827 -0
  27. package/dist/assets/Helvetica-BoldOblique.afm +2827 -0
  28. package/dist/assets/Helvetica-Oblique.afm +3051 -0
  29. package/dist/assets/Helvetica.afm +3051 -0
  30. package/dist/assets/Symbol.afm +213 -0
  31. package/dist/assets/Times-Bold.afm +2588 -0
  32. package/dist/assets/Times-BoldItalic.afm +2384 -0
  33. package/dist/assets/Times-Italic.afm +2667 -0
  34. package/dist/assets/Times-Roman.afm +2419 -0
  35. package/dist/assets/ZapfDingbats.afm +225 -0
  36. package/dist/assets/agl.txt +695 -0
  37. package/dist/common/color.d.ts +37 -0
  38. package/dist/common/color.js +62 -0
  39. package/dist/constants/page-sizes.d.ts +44 -0
  40. package/dist/constants/page-sizes.js +92 -0
  41. package/dist/constants/pdf-parts.d.ts +5 -0
  42. package/dist/constants/pdf-parts.js +8 -0
  43. package/dist/elements/container-element.d.ts +35 -0
  44. package/dist/elements/container-element.js +91 -0
  45. package/dist/elements/image-element.d.ts +56 -0
  46. package/dist/elements/image-element.js +151 -0
  47. package/dist/elements/index.d.ts +10 -0
  48. package/dist/elements/index.js +28 -0
  49. package/dist/elements/layout/deferred-element.d.ts +19 -0
  50. package/dist/elements/layout/deferred-element.js +33 -0
  51. package/dist/elements/layout/expanded-element.d.ts +30 -0
  52. package/dist/elements/layout/expanded-element.js +68 -0
  53. package/dist/elements/layout/padding-element.d.ts +29 -0
  54. package/dist/elements/layout/padding-element.js +76 -0
  55. package/dist/elements/layout/repeating-header-element.d.ts +29 -0
  56. package/dist/elements/layout/repeating-header-element.js +60 -0
  57. package/dist/elements/layout/sized-container-element.d.ts +14 -0
  58. package/dist/elements/layout/sized-container-element.js +37 -0
  59. package/dist/elements/line-element.d.ts +31 -0
  60. package/dist/elements/line-element.js +44 -0
  61. package/dist/elements/page-element.d.ts +58 -0
  62. package/dist/elements/page-element.js +90 -0
  63. package/dist/elements/pdf-document-element.d.ts +13 -0
  64. package/dist/elements/pdf-document-element.js +22 -0
  65. package/dist/elements/pdf-element.d.ts +67 -0
  66. package/dist/elements/pdf-element.js +55 -0
  67. package/dist/elements/rectangle-element.d.ts +55 -0
  68. package/dist/elements/rectangle-element.js +120 -0
  69. package/dist/elements/row-element.d.ts +42 -0
  70. package/dist/elements/row-element.js +54 -0
  71. package/dist/elements/text-element.d.ts +59 -0
  72. package/dist/elements/text-element.js +137 -0
  73. package/dist/index.d.ts +3 -0
  74. package/dist/index.js +21 -0
  75. package/dist/ir/display-list.d.ts +74 -0
  76. package/dist/ir/display-list.js +2 -0
  77. package/dist/layout/box-constraints.d.ts +56 -0
  78. package/dist/layout/box-constraints.js +73 -0
  79. package/dist/layout/fragmentation.d.ts +42 -0
  80. package/dist/layout/fragmentation.js +61 -0
  81. package/dist/renderer/container-renderer.d.ts +6 -0
  82. package/dist/renderer/container-renderer.js +30 -0
  83. package/dist/renderer/deferred-renderer.d.ts +6 -0
  84. package/dist/renderer/deferred-renderer.js +25 -0
  85. package/dist/renderer/expanded-renderer.d.ts +6 -0
  86. package/dist/renderer/expanded-renderer.js +23 -0
  87. package/dist/renderer/image-renderer.d.ts +6 -0
  88. package/dist/renderer/image-renderer.js +85 -0
  89. package/dist/renderer/index.d.ts +10 -0
  90. package/dist/renderer/index.js +26 -0
  91. package/dist/renderer/line-renderer.d.ts +6 -0
  92. package/dist/renderer/line-renderer.js +30 -0
  93. package/dist/renderer/padding-renderer.d.ts +6 -0
  94. package/dist/renderer/padding-renderer.js +23 -0
  95. package/dist/renderer/page-renderer.d.ts +5 -0
  96. package/dist/renderer/page-renderer.js +81 -0
  97. package/dist/renderer/pdf-backend.d.ts +45 -0
  98. package/dist/renderer/pdf-backend.js +184 -0
  99. package/dist/renderer/pdf-config.d.ts +8 -0
  100. package/dist/renderer/pdf-config.js +17 -0
  101. package/dist/renderer/pdf-document-class.d.ts +42 -0
  102. package/dist/renderer/pdf-document-class.js +118 -0
  103. package/dist/renderer/pdf-document-renderer.d.ts +20 -0
  104. package/dist/renderer/pdf-document-renderer.js +104 -0
  105. package/dist/renderer/pdf-renderer.d.ts +5 -0
  106. package/dist/renderer/pdf-renderer.js +92 -0
  107. package/dist/renderer/rectangle-renderer.d.ts +8 -0
  108. package/dist/renderer/rectangle-renderer.js +61 -0
  109. package/dist/renderer/repeating-header-renderer.d.ts +6 -0
  110. package/dist/renderer/repeating-header-renderer.js +28 -0
  111. package/dist/renderer/row-renderer.d.ts +6 -0
  112. package/dist/renderer/row-renderer.js +30 -0
  113. package/dist/renderer/text-renderer.d.ts +9 -0
  114. package/dist/renderer/text-renderer.js +125 -0
  115. package/dist/text/line-breaker.d.ts +40 -0
  116. package/dist/text/line-breaker.js +106 -0
  117. package/dist/utils/afm-parser.d.ts +12 -0
  118. package/dist/utils/afm-parser.js +91 -0
  119. package/dist/utils/flex-layout.d.ts +53 -0
  120. package/dist/utils/flex-layout.js +119 -0
  121. package/dist/utils/font-metrics.d.ts +12 -0
  122. package/dist/utils/font-metrics.js +2 -0
  123. package/dist/utils/font-path.d.ts +1 -0
  124. package/dist/utils/font-path.js +19 -0
  125. package/dist/utils/image-helper.d.ts +43 -0
  126. package/dist/utils/image-helper.js +206 -0
  127. package/dist/utils/pdf-object-manager.d.ts +97 -0
  128. package/dist/utils/pdf-object-manager.js +645 -0
  129. package/dist/utils/renderer-registry.d.ts +6 -0
  130. package/dist/utils/renderer-registry.js +19 -0
  131. package/dist/utils/ttf-parser.d.ts +29 -0
  132. package/dist/utils/ttf-parser.js +191 -0
  133. package/dist/utils/ttf-subsetter.d.ts +1 -0
  134. package/dist/utils/ttf-subsetter.js +161 -0
  135. package/dist/utils/utf8-to-windows1252-encoder.d.ts +2 -0
  136. package/dist/utils/utf8-to-windows1252-encoder.js +55 -0
  137. package/dist/validators/element-validator.d.ts +8 -0
  138. package/dist/validators/element-validator.js +61 -0
  139. package/package.json +50 -0
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DeferredElement = void 0;
4
+ const fragmentation_1 = require("../../layout/fragmentation");
5
+ const pdf_element_1 = require("../pdf-element");
6
+ /**
7
+ * Builds its subtree at layout time via `resolve(ctx)`, so the tree can depend on font
8
+ * metrics (e.g. a Table resolving `"auto"` column widths from cell content). The engine
9
+ * stays table-agnostic - the closure comes from the API layer.
10
+ */
11
+ class DeferredElement extends pdf_element_1.PDFElement {
12
+ constructor(resolve) {
13
+ super();
14
+ this.resolve = resolve;
15
+ }
16
+ build(ctx) {
17
+ this.composed = this.resolve(ctx);
18
+ return this.composed;
19
+ }
20
+ calculateLayout(constraints, offset, ctx) {
21
+ return this.build(ctx).calculateLayout(constraints, offset, ctx);
22
+ }
23
+ fragment(maxHeight, width, ctx) {
24
+ const c = this.build(ctx);
25
+ return (0, fragmentation_1.isFragmentable)(c)
26
+ ? c.fragment(maxHeight, width, ctx)
27
+ : { fitted: this, remainder: null };
28
+ }
29
+ getProps() {
30
+ return { composed: this.composed };
31
+ }
32
+ }
33
+ exports.DeferredElement = DeferredElement;
@@ -0,0 +1,30 @@
1
+ import { PDFElement, LayoutContext, FlexiblePDFElement, WithChild, FlexibleElement } from "../pdf-element";
2
+ import { BoxConstraints, Offset, Size } from "../../layout/box-constraints";
3
+ import { Fragmentable, FragmentResult } from "../../layout/fragmentation";
4
+ interface ExpandedElementParams extends FlexibleElement, WithChild {
5
+ }
6
+ export declare class ExpandedElement extends FlexiblePDFElement implements Fragmentable {
7
+ private child;
8
+ private x;
9
+ private y;
10
+ private width;
11
+ private height;
12
+ constructor({ flex, child }: ExpandedElementParams);
13
+ calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
14
+ /**
15
+ * When the column paginates, a flex region can't "fill" across pages, so it delegates to
16
+ * its child: the child splits and each half is re-wrapped in an Expanded (which then
17
+ * fills the leftover space on whichever page it lands). If the child can't split, the
18
+ * whole Expanded moves on.
19
+ */
20
+ fragment(maxHeight: number, width: number, ctx: LayoutContext): FragmentResult;
21
+ private cloneWithChild;
22
+ getProps(): {
23
+ x: number;
24
+ y: number;
25
+ width: number;
26
+ height: number;
27
+ child: PDFElement;
28
+ };
29
+ }
30
+ export {};
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExpandedElement = void 0;
4
+ const element_validator_1 = require("../../validators/element-validator");
5
+ const pdf_element_1 = require("../pdf-element");
6
+ const box_constraints_1 = require("../../layout/box-constraints");
7
+ const fragmentation_1 = require("../../layout/fragmentation");
8
+ class ExpandedElement extends pdf_element_1.FlexiblePDFElement {
9
+ constructor({ flex, child }) {
10
+ super({ flex });
11
+ this.x = 0;
12
+ this.y = 0;
13
+ this.width = 0;
14
+ this.height = 0;
15
+ this.child = child;
16
+ }
17
+ calculateLayout(constraints, offset, ctx) {
18
+ if (constraints.hasBoundedWidth)
19
+ this.width = constraints.maxWidth;
20
+ // Absolute placement from the parent; assignment (not +=) so re-layout is idempotent.
21
+ this.x = offset.x;
22
+ this.y = offset.y;
23
+ element_validator_1.Validator.validateFlexElement(this);
24
+ if (constraints.hasBoundedHeight) {
25
+ // A bounded region: fill it - the normal flex behaviour.
26
+ this.height = constraints.maxHeight;
27
+ this.child.calculateLayout(box_constraints_1.BoxConstraints.loose(this.width, this.height), { x: this.x, y: this.y }, ctx);
28
+ }
29
+ else {
30
+ // Unbounded (measuring while paginating): there's no leftover space to fill, so
31
+ // collapse to the child's natural height. This lets an overflowing column flow
32
+ // instead of the flex silently hiding the overflow.
33
+ const childSize = this.child.calculateLayout(box_constraints_1.BoxConstraints.loose(this.width, Infinity), { x: this.x, y: this.y }, ctx);
34
+ this.height = childSize.height;
35
+ }
36
+ // Top-left coordinates; the Y-flip now happens once at the IR -> backend seam.
37
+ return { width: this.width, height: this.height };
38
+ }
39
+ /**
40
+ * When the column paginates, a flex region can't "fill" across pages, so it delegates to
41
+ * its child: the child splits and each half is re-wrapped in an Expanded (which then
42
+ * fills the leftover space on whichever page it lands). If the child can't split, the
43
+ * whole Expanded moves on.
44
+ */
45
+ fragment(maxHeight, width, ctx) {
46
+ if (!(0, fragmentation_1.isFragmentable)(this.child)) {
47
+ return { fitted: null, remainder: this };
48
+ }
49
+ const split = this.child.fragment(maxHeight, width, ctx);
50
+ return {
51
+ fitted: split.fitted ? this.cloneWithChild(split.fitted) : null,
52
+ remainder: split.remainder ? this.cloneWithChild(split.remainder) : null,
53
+ };
54
+ }
55
+ cloneWithChild(child) {
56
+ return new ExpandedElement({ flex: this.flex, child });
57
+ }
58
+ getProps() {
59
+ return {
60
+ x: this.x,
61
+ y: this.y,
62
+ width: this.width,
63
+ height: this.height,
64
+ child: this.child,
65
+ };
66
+ }
67
+ }
68
+ exports.ExpandedElement = ExpandedElement;
@@ -0,0 +1,29 @@
1
+ import { PDFElement, LayoutContext, WithChild, SizedPDFElement } from "../pdf-element";
2
+ import { BoxConstraints, Offset, Size } from "../../layout/box-constraints";
3
+ import { Fragmentable, FragmentResult } from "../../layout/fragmentation";
4
+ interface PaddingElementParams extends WithChild {
5
+ margin: [number, number, number, number];
6
+ }
7
+ export declare class PaddingElement extends SizedPDFElement implements Fragmentable {
8
+ private child;
9
+ private margin;
10
+ constructor({ margin, child }: PaddingElementParams);
11
+ /**
12
+ * Splits the padded box across pages (box-decoration-break: clone - every fragment
13
+ * keeps its full top/bottom inset). The child is fragmented into the space left after
14
+ * reserving the vertical insets; each half is re-wrapped in its own padding. If the
15
+ * child can't be split, the whole padding moves on as the remainder.
16
+ */
17
+ fragment(maxHeight: number, width: number, ctx: LayoutContext): FragmentResult;
18
+ private cloneWithChild;
19
+ calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
20
+ getProps(): {
21
+ x: number;
22
+ y: number;
23
+ width: number | undefined;
24
+ height: number | undefined;
25
+ margin: [number, number, number, number];
26
+ child: PDFElement;
27
+ };
28
+ }
29
+ export {};
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PaddingElement = void 0;
4
+ const element_validator_1 = require("../../validators/element-validator");
5
+ const pdf_element_1 = require("../pdf-element");
6
+ const box_constraints_1 = require("../../layout/box-constraints");
7
+ const fragmentation_1 = require("../../layout/fragmentation");
8
+ class PaddingElement extends pdf_element_1.SizedPDFElement {
9
+ constructor({ margin, child }) {
10
+ super({ x: 0, y: 0 });
11
+ this.child = child;
12
+ this.margin = margin;
13
+ }
14
+ /**
15
+ * Splits the padded box across pages (box-decoration-break: clone - every fragment
16
+ * keeps its full top/bottom inset). The child is fragmented into the space left after
17
+ * reserving the vertical insets; each half is re-wrapped in its own padding. If the
18
+ * child can't be split, the whole padding moves on as the remainder.
19
+ */
20
+ fragment(maxHeight, width, ctx) {
21
+ if (!(0, fragmentation_1.isFragmentable)(this.child)) {
22
+ return { fitted: null, remainder: this };
23
+ }
24
+ const [marginTop, marginRight, marginBottom, marginLeft] = this.margin;
25
+ const childWidth = width - marginLeft - marginRight;
26
+ const childMaxHeight = maxHeight - marginTop - marginBottom;
27
+ const split = this.child.fragment(childMaxHeight, childWidth, ctx);
28
+ return {
29
+ fitted: split.fitted ? this.cloneWithChild(split.fitted) : null,
30
+ remainder: split.remainder ? this.cloneWithChild(split.remainder) : null,
31
+ };
32
+ }
33
+ cloneWithChild(child) {
34
+ return new PaddingElement({ margin: this.margin, child });
35
+ }
36
+ calculateLayout(constraints, offset, ctx) {
37
+ var _a;
38
+ // Padding takes the width it is offered; its height shrink-wraps the child.
39
+ if (constraints.hasBoundedWidth)
40
+ this.width = constraints.maxWidth;
41
+ this.x = offset.x;
42
+ this.y = offset.y;
43
+ const [marginTop, marginRight, marginBottom, marginLeft] = this.margin;
44
+ // The child is inset by the margins: shifted down/right, narrowed by the
45
+ // horizontal margins, and left height-unbounded so it sizes to its own content.
46
+ const childOffset = {
47
+ x: this.x + marginLeft,
48
+ y: this.y + marginTop,
49
+ };
50
+ const childWidth = constraints.hasBoundedWidth
51
+ ? Math.max(0, constraints.maxWidth - marginLeft - marginRight)
52
+ : Infinity;
53
+ const childConstraints = box_constraints_1.BoxConstraints.loose(childWidth, Infinity);
54
+ const childSize = this.child.calculateLayout(childConstraints, childOffset, ctx);
55
+ this.height = childSize.height + marginTop + marginBottom;
56
+ // Unbounded width (e.g. a fixed child in a Row): shrink-wrap to the child + insets,
57
+ // mirroring how Container/Row shrink-wrap. Bounded width was set above (unchanged).
58
+ if (!constraints.hasBoundedWidth) {
59
+ this.width = childSize.width + marginLeft + marginRight;
60
+ }
61
+ element_validator_1.Validator.validateSizedElement(this);
62
+ // Top-left coordinates; the Y-flip now happens once at the IR -> backend seam.
63
+ return { width: (_a = this.width) !== null && _a !== void 0 ? _a : 0, height: this.height };
64
+ }
65
+ getProps() {
66
+ return {
67
+ x: this.x,
68
+ y: this.y,
69
+ width: this.width,
70
+ height: this.height,
71
+ margin: this.margin,
72
+ child: this.child,
73
+ };
74
+ }
75
+ }
76
+ exports.PaddingElement = PaddingElement;
@@ -0,0 +1,29 @@
1
+ import { BoxConstraints, Offset, Size } from "../../layout/box-constraints";
2
+ import { Fragmentable, FragmentResult } from "../../layout/fragmentation";
3
+ import { LayoutContext, PDFElement } from "../pdf-element";
4
+ /**
5
+ * Stacks a `header` above a `body` and, when it paginates, **repeats the header on every
6
+ * fragment**. Used by `Table` so column headings reappear at the top of each page. Layout
7
+ * is just header-then-body (like a 2-row Column); the magic is in `fragment`: it splits the
8
+ * body and re-wraps each piece with the same header, so every physical page gets its own.
9
+ */
10
+ export declare class RepeatingHeaderElement extends PDFElement implements Fragmentable {
11
+ private header;
12
+ private body;
13
+ private gap;
14
+ private x;
15
+ private y;
16
+ private width;
17
+ private height;
18
+ constructor(header: PDFElement, body: PDFElement, gap?: number);
19
+ calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
20
+ fragment(maxHeight: number, width: number, ctx: LayoutContext): FragmentResult;
21
+ getProps(): {
22
+ x: number;
23
+ y: number;
24
+ width: number;
25
+ height: number;
26
+ header: PDFElement;
27
+ body: PDFElement;
28
+ };
29
+ }
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RepeatingHeaderElement = void 0;
4
+ const box_constraints_1 = require("../../layout/box-constraints");
5
+ const fragmentation_1 = require("../../layout/fragmentation");
6
+ const pdf_element_1 = require("../pdf-element");
7
+ /**
8
+ * Stacks a `header` above a `body` and, when it paginates, **repeats the header on every
9
+ * fragment**. Used by `Table` so column headings reappear at the top of each page. Layout
10
+ * is just header-then-body (like a 2-row Column); the magic is in `fragment`: it splits the
11
+ * body and re-wraps each piece with the same header, so every physical page gets its own.
12
+ */
13
+ class RepeatingHeaderElement extends pdf_element_1.PDFElement {
14
+ constructor(header, body, gap = 0) {
15
+ super();
16
+ this.header = header;
17
+ this.body = body;
18
+ this.gap = gap;
19
+ this.x = 0;
20
+ this.y = 0;
21
+ this.width = 0;
22
+ this.height = 0;
23
+ }
24
+ calculateLayout(constraints, offset, ctx) {
25
+ this.x = offset.x;
26
+ this.y = offset.y;
27
+ const width = constraints.hasBoundedWidth ? constraints.maxWidth : 0;
28
+ const h = this.header.calculateLayout(box_constraints_1.BoxConstraints.loose(width, Infinity), { x: this.x, y: this.y }, ctx);
29
+ const b = this.body.calculateLayout(box_constraints_1.BoxConstraints.loose(width, Infinity), { x: this.x, y: this.y + h.height + this.gap }, ctx);
30
+ this.width = width;
31
+ this.height = h.height + this.gap + b.height;
32
+ return { width: this.width, height: this.height };
33
+ }
34
+ fragment(maxHeight, width, ctx) {
35
+ // Reserve the header's height on every page; the body flows in what's left.
36
+ const headerHeight = this.header.calculateLayout(box_constraints_1.BoxConstraints.loose(width, Infinity), { x: 0, y: 0 }, ctx).height;
37
+ if (!(0, fragmentation_1.isFragmentable)(this.body))
38
+ return { fitted: this, remainder: null };
39
+ const split = this.body.fragment(Math.max(0, maxHeight - headerHeight - this.gap), width, ctx);
40
+ // Body fits whole → the whole thing fits on this page.
41
+ if (split.remainder === null)
42
+ return { fitted: this, remainder: null };
43
+ // Re-wrap each body piece with the SAME header → it reappears on the next page too.
44
+ return {
45
+ fitted: split.fitted ? new RepeatingHeaderElement(this.header, split.fitted, this.gap) : null,
46
+ remainder: new RepeatingHeaderElement(this.header, split.remainder, this.gap),
47
+ };
48
+ }
49
+ getProps() {
50
+ return {
51
+ x: this.x,
52
+ y: this.y,
53
+ width: this.width,
54
+ height: this.height,
55
+ header: this.header,
56
+ body: this.body,
57
+ };
58
+ }
59
+ }
60
+ exports.RepeatingHeaderElement = RepeatingHeaderElement;
@@ -0,0 +1,14 @@
1
+ import { SizedElement, WithChildren, SizedPDFElement, LayoutContext } from "../pdf-element";
2
+ import { BoxConstraints, Offset, Size } from "../../layout/box-constraints";
3
+ interface ContainerElementParams extends SizedElement, WithChildren {
4
+ color?: [number, number, number];
5
+ backgroundColor?: [number, number, number];
6
+ borderWidth?: number;
7
+ }
8
+ export declare class SizedContainerElement extends SizedPDFElement {
9
+ private children;
10
+ constructor({ width, height, children }: ContainerElementParams);
11
+ calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
12
+ getProps(): ContainerElementParams;
13
+ }
14
+ export {};
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SizedContainerElement = void 0;
4
+ const pdf_element_1 = require("../pdf-element");
5
+ const box_constraints_1 = require("../../layout/box-constraints");
6
+ class SizedContainerElement extends pdf_element_1.SizedPDFElement {
7
+ constructor({ width, height, children }) {
8
+ super({ x: 0, y: 0, width, height });
9
+ this.children = children;
10
+ }
11
+ calculateLayout(constraints, offset, ctx) {
12
+ var _a, _b;
13
+ if (constraints.hasBoundedWidth)
14
+ this.width = constraints.maxWidth;
15
+ if (constraints.hasBoundedHeight)
16
+ this.height = constraints.maxHeight;
17
+ // Absolute placement from the parent; assignment (not +=) so re-layout is idempotent.
18
+ this.x = offset.x;
19
+ this.y = offset.y;
20
+ const width = (_a = this.width) !== null && _a !== void 0 ? _a : 0;
21
+ const height = (_b = this.height) !== null && _b !== void 0 ? _b : 0;
22
+ if (this.children)
23
+ this.children.forEach((child) => child.calculateLayout(box_constraints_1.BoxConstraints.loose(width, height), { x: this.x, y: this.y }, ctx));
24
+ // Top-left coordinates; the Y-flip now happens once at the IR -> backend seam.
25
+ return { width, height };
26
+ }
27
+ getProps() {
28
+ return {
29
+ x: this.x,
30
+ y: this.y,
31
+ width: this.width,
32
+ height: this.height,
33
+ children: this.children,
34
+ };
35
+ }
36
+ }
37
+ exports.SizedContainerElement = SizedContainerElement;
@@ -0,0 +1,31 @@
1
+ import { Color } from "../common/color";
2
+ import { BoxConstraints, Offset, Size } from "../layout/box-constraints";
3
+ import { LayoutContext, SizedElement, SizedPDFElement } from "./pdf-element";
4
+ interface LineElementParams extends SizedElement {
5
+ color?: Color;
6
+ strokeWidth?: number;
7
+ x: number;
8
+ y: number;
9
+ xEnd: number;
10
+ yEnd: number;
11
+ }
12
+ export declare class LineElement extends SizedPDFElement {
13
+ private color?;
14
+ private strokeWidth?;
15
+ private xEnd;
16
+ private yEnd;
17
+ private sizeMemory;
18
+ constructor({ color, strokeWidth, x, y, xEnd, yEnd }: LineElementParams);
19
+ calculateLayout(constraints: BoxConstraints, offset: Offset, _ctx: LayoutContext): Size;
20
+ getProps(): {
21
+ x: number;
22
+ y: number;
23
+ xEnd: number;
24
+ yEnd: number;
25
+ height: number | undefined;
26
+ width: number | undefined;
27
+ color: Color | undefined;
28
+ strokeWidth: number | undefined;
29
+ };
30
+ }
31
+ export {};
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LineElement = void 0;
4
+ const color_1 = require("../common/color");
5
+ const pdf_element_1 = require("./pdf-element");
6
+ class LineElement extends pdf_element_1.SizedPDFElement {
7
+ constructor({ color = new color_1.Color(0, 0, 0), strokeWidth, x, y, xEnd, yEnd }) {
8
+ super({ x: x, y: y, width: xEnd, height: y + yEnd });
9
+ this.color = color;
10
+ this.strokeWidth = strokeWidth ? strokeWidth : 1;
11
+ this.x = x;
12
+ this.y = y;
13
+ this.xEnd = xEnd;
14
+ this.yEnd = yEnd;
15
+ this.sizeMemory = { x, y, width: xEnd, height: yEnd };
16
+ }
17
+ calculateLayout(constraints, offset, _ctx) {
18
+ var _a, _b;
19
+ // Set relative to parent
20
+ this.x = this.sizeMemory.x + offset.x;
21
+ this.y = this.sizeMemory.y + offset.y;
22
+ // The line spans the parent's width, so it needs a bounded width to anchor its end.
23
+ if (!constraints.hasBoundedWidth) {
24
+ throw new Error("The LineElement must be placed inside a parent container that defines its width");
25
+ }
26
+ this.xEnd = offset.x + constraints.maxWidth - this.sizeMemory.width;
27
+ this.yEnd = offset.y + this.sizeMemory.height;
28
+ // Top-left coordinates; the Y-flip happens once at the IR -> backend seam.
29
+ return { width: (_a = this.width) !== null && _a !== void 0 ? _a : 0, height: (_b = this.height) !== null && _b !== void 0 ? _b : 0 };
30
+ }
31
+ getProps() {
32
+ return {
33
+ x: this.x,
34
+ y: this.y,
35
+ xEnd: this.xEnd,
36
+ yEnd: this.yEnd,
37
+ height: this.height,
38
+ width: this.width,
39
+ color: this.color,
40
+ strokeWidth: this.strokeWidth,
41
+ };
42
+ }
43
+ }
44
+ exports.LineElement = LineElement;
@@ -0,0 +1,58 @@
1
+ import { PageSize } from "../constants/page-sizes";
2
+ import { Orientation } from "../renderer/pdf-config";
3
+ import type { ColorMode, DefaultFont, Margin } from "../renderer";
4
+ import { BoxConstraints, Offset, Size } from "../layout/box-constraints";
5
+ import { LayoutContext, PDFElement, WithChildren } from "./pdf-element";
6
+ import { TextElement } from "./text-element";
7
+ export interface PDFPageConfig {
8
+ pageSize?: PageSize;
9
+ orientation?: Orientation;
10
+ margin?: Margin;
11
+ colorMode?: ColorMode;
12
+ defaultFont?: DefaultFont;
13
+ }
14
+ interface PDFPageParams extends WithChildren {
15
+ config?: PDFPageConfig;
16
+ /** Laid out at the TOP of the content box, repeated on every physical page. */
17
+ header?: PDFElement;
18
+ /** Laid out at the BOTTOM of the content box, repeated on every physical page. */
19
+ footer?: PDFElement;
20
+ }
21
+ /**
22
+ * The content box of a page (inside the margins, orientation applied) for a fully
23
+ * resolved config. Single source of truth, shared by `PageElement` layout and the page
24
+ * driver so they can never drift.
25
+ */
26
+ export declare function resolvePageContentBox(config: PDFPageConfig): {
27
+ origin: Offset;
28
+ width: number;
29
+ height: number;
30
+ };
31
+ /** The body region of a page once the (optional) header/footer bands are subtracted. */
32
+ export interface PageBands {
33
+ bodyOrigin: Offset;
34
+ bodyWidth: number;
35
+ bodyHeight: number;
36
+ headerHeight: number;
37
+ footerHeight: number;
38
+ }
39
+ /**
40
+ * Lays out the header (top) and footer (bottom) of a page and returns the body band left
41
+ * in between. Header/footer take their natural height against the content width; the body
42
+ * gets `contentHeight - headerHeight - footerHeight`. Shared by `PageElement.calculateLayout`
43
+ * (placement) and the page driver (so its fragmentation `maxHeight` matches exactly). With
44
+ * no header/footer the bands are zero and the body equals the full content box - identical
45
+ * to a page without them.
46
+ */
47
+ export declare function layoutPageBands(config: PDFPageConfig, header: PDFElement | undefined, footer: PDFElement | undefined, ctx: LayoutContext): PageBands;
48
+ export declare class PageElement extends PDFElement {
49
+ private config;
50
+ private children;
51
+ private header?;
52
+ private footer?;
53
+ constructor({ children, config, header, footer }: PDFPageParams);
54
+ calculateLayout(_constraints: BoxConstraints, _offset: Offset, ctx: LayoutContext): Size;
55
+ getProps(): PDFPageParams;
56
+ addTextElement(element: TextElement): this;
57
+ }
58
+ export {};
@@ -0,0 +1,90 @@
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");
10
+ /**
11
+ * The content box of a page (inside the margins, orientation applied) for a fully
12
+ * resolved config. Single source of truth, shared by `PageElement` layout and the page
13
+ * driver so they can never drift.
14
+ */
15
+ function resolvePageContentBox(config) {
16
+ 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) {
20
+ [width, height] = [height, width];
21
+ }
22
+ return { origin: { x: margin.left, y: margin.top }, width, height };
23
+ }
24
+ /**
25
+ * Lays out the header (top) and footer (bottom) of a page and returns the body band left
26
+ * in between. Header/footer take their natural height against the content width; the body
27
+ * gets `contentHeight - headerHeight - footerHeight`. Shared by `PageElement.calculateLayout`
28
+ * (placement) and the page driver (so its fragmentation `maxHeight` matches exactly). With
29
+ * no header/footer the bands are zero and the body equals the full content box - identical
30
+ * to a page without them.
31
+ */
32
+ function layoutPageBands(config, header, footer, ctx) {
33
+ const { origin, width, height } = resolvePageContentBox(config);
34
+ let headerHeight = 0;
35
+ if (header) {
36
+ headerHeight = header.calculateLayout(box_constraints_1.BoxConstraints.loose(width, Infinity), origin, ctx).height;
37
+ }
38
+ let footerHeight = 0;
39
+ 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);
43
+ }
44
+ return {
45
+ bodyOrigin: { x: origin.x, y: origin.y + headerHeight },
46
+ bodyWidth: width,
47
+ bodyHeight: Math.max(0, height - headerHeight - footerHeight),
48
+ headerHeight,
49
+ footerHeight,
50
+ };
51
+ }
52
+ class PageElement extends pdf_element_1.PDFElement {
53
+ constructor({ children, config, header, footer }) {
54
+ super();
55
+ this.children = children;
56
+ this.config = config !== null && config !== void 0 ? config : {};
57
+ this.header = header;
58
+ this.footer = footer;
59
+ }
60
+ calculateLayout(_constraints, _offset, ctx) {
61
+ // Merge the document defaults (carried in the context) with this page's overrides,
62
+ // then hand descendants a context bound to THIS page's geometry. This is what
63
+ // fixes the old last-page-wins global page-config bug.
64
+ this.config = Object.assign(Object.assign({}, ctx.pageConfig), this.config);
65
+ const pageCtx = {
66
+ metrics: ctx.metrics,
67
+ pageConfig: this.config,
68
+ };
69
+ // Place the header/footer bands; the body gets the region left in between (the whole
70
+ // 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 };
76
+ }
77
+ getProps() {
78
+ return {
79
+ children: this.children,
80
+ config: this.config,
81
+ header: this.header,
82
+ footer: this.footer,
83
+ };
84
+ }
85
+ addTextElement(element) {
86
+ this.children.push(element);
87
+ return this;
88
+ }
89
+ }
90
+ exports.PageElement = PageElement;
@@ -0,0 +1,13 @@
1
+ import { PageElement } from "./page-element";
2
+ import { BoxConstraints, Offset, Size } from "../layout/box-constraints";
3
+ import { LayoutContext, PDFElement, WithChildren } from "./pdf-element";
4
+ interface PDFDocumentParams extends WithChildren {
5
+ children: PageElement[];
6
+ }
7
+ export declare class PDFDocumentElement extends PDFElement {
8
+ private children;
9
+ constructor({ children }: PDFDocumentParams);
10
+ calculateLayout(_constraints: BoxConstraints, _offset: Offset, ctx: LayoutContext): Size;
11
+ getProps(): PDFDocumentParams;
12
+ }
13
+ export {};
@@ -0,0 +1,22 @@
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 }) {
8
+ super();
9
+ this.children = children;
10
+ }
11
+ calculateLayout(_constraints, _offset, ctx) {
12
+ // The document is the root: each page derives its own geometry, so it ignores the
13
+ // incoming constraints/offset. It has no size of its own.
14
+ const origin = { x: 0, y: 0 };
15
+ this.children.forEach((child) => child.calculateLayout(new box_constraints_1.BoxConstraints(), origin, ctx));
16
+ return { width: 0, height: 0 };
17
+ }
18
+ getProps() {
19
+ return { children: this.children };
20
+ }
21
+ }
22
+ exports.PDFDocumentElement = PDFDocumentElement;