@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.
- package/LICENSE +21 -0
- package/README.md +35 -21
- package/dist/api/args.d.ts +1 -1
- package/dist/api/args.js +2 -5
- package/dist/api/color.d.ts +4 -4
- package/dist/api/color.js +11 -17
- package/dist/api/content.d.ts +20 -13
- package/dist/api/content.js +44 -29
- package/dist/api/descriptor.d.ts +2 -2
- package/dist/api/descriptor.js +121 -31
- package/dist/api/dimension.d.ts +76 -0
- package/dist/api/dimension.js +43 -0
- package/dist/api/font-url.d.ts +28 -0
- package/dist/api/font-url.js +134 -0
- package/dist/api/forms.d.ts +347 -0
- package/dist/api/forms.js +189 -0
- package/dist/api/gradient.d.ts +53 -0
- package/dist/api/gradient.js +97 -0
- package/dist/api/index.d.ts +13 -8
- package/dist/api/index.js +13 -24
- package/dist/api/insets.d.ts +19 -12
- package/dist/api/insets.js +19 -12
- package/dist/api/layout.d.ts +149 -25
- package/dist/api/layout.js +182 -51
- package/dist/api/page-builder.d.ts +40 -0
- package/dist/api/page-builder.js +44 -0
- package/dist/api/structure.d.ts +100 -13
- package/dist/api/structure.js +179 -88
- package/dist/api/table.d.ts +5 -5
- package/dist/api/table.js +38 -26
- package/dist/api/text.d.ts +90 -4
- package/dist/api/text.js +116 -31
- package/dist/assets/font-data.d.ts +2 -0
- package/dist/assets/font-data.js +21 -0
- package/dist/assets/font-data.ts +37 -0
- package/dist/common/color.js +1 -5
- package/dist/constants/page-sizes.js +3 -6
- package/dist/constants/pdf-parts.js +1 -4
- package/dist/crypto/legacy-handler.d.ts +43 -0
- package/dist/crypto/legacy-handler.js +145 -0
- package/dist/crypto/security-handler.d.ts +62 -0
- package/dist/crypto/security-handler.js +155 -0
- package/dist/crypto/webcrypto.d.ts +11 -0
- package/dist/crypto/webcrypto.js +62 -0
- package/dist/edit/acroform-reader.d.ts +70 -0
- package/dist/edit/acroform-reader.js +126 -0
- package/dist/edit/appearance.d.ts +47 -0
- package/dist/edit/appearance.js +171 -0
- package/dist/edit/document.d.ts +145 -0
- package/dist/edit/document.js +642 -0
- package/dist/edit/fill.d.ts +65 -0
- package/dist/edit/fill.js +424 -0
- package/dist/edit/flatten.d.ts +54 -0
- package/dist/edit/flatten.js +462 -0
- package/dist/edit/index.d.ts +20 -0
- package/dist/edit/index.js +18 -0
- package/dist/edit/lexer.d.ts +30 -0
- package/dist/edit/lexer.js +285 -0
- package/dist/edit/objects.d.ts +68 -0
- package/dist/edit/objects.js +83 -0
- package/dist/edit/writer.d.ts +60 -0
- package/dist/edit/writer.js +283 -0
- package/dist/elements/container-element.d.ts +50 -5
- package/dist/elements/container-element.js +100 -39
- package/dist/elements/forms/checkbox-element.d.ts +51 -0
- package/dist/elements/forms/checkbox-element.js +48 -0
- package/dist/elements/forms/choice-element.d.ts +53 -0
- package/dist/elements/forms/choice-element.js +59 -0
- package/dist/elements/forms/push-button-element.d.ts +56 -0
- package/dist/elements/forms/push-button-element.js +49 -0
- package/dist/elements/forms/radio-element.d.ts +52 -0
- package/dist/elements/forms/radio-element.js +49 -0
- package/dist/elements/forms/signature-element.d.ts +54 -0
- package/dist/elements/forms/signature-element.js +47 -0
- package/dist/elements/forms/text-field-element.d.ts +61 -0
- package/dist/elements/forms/text-field-element.js +54 -0
- package/dist/elements/image-element.d.ts +58 -6
- package/dist/elements/image-element.js +145 -111
- package/dist/elements/index.d.ts +26 -10
- package/dist/elements/index.js +26 -28
- package/dist/elements/layout/anchor-element.d.ts +23 -0
- package/dist/elements/layout/anchor-element.js +27 -0
- package/dist/elements/layout/bookmark-element.d.ts +27 -0
- package/dist/elements/layout/bookmark-element.js +30 -0
- package/dist/elements/layout/default-text-style-element.d.ts +30 -0
- package/dist/elements/layout/default-text-style-element.js +47 -0
- package/dist/elements/layout/deferred-element.d.ts +3 -3
- package/dist/elements/layout/deferred-element.js +4 -8
- package/dist/elements/layout/expanded-element.d.ts +6 -4
- package/dist/elements/layout/expanded-element.js +22 -17
- package/dist/elements/layout/keep-together-element.d.ts +42 -0
- package/dist/elements/layout/keep-together-element.js +85 -0
- package/dist/elements/layout/link-element.d.ts +33 -0
- package/dist/elements/layout/link-element.js +39 -0
- package/dist/elements/layout/padding-element.d.ts +10 -5
- package/dist/elements/layout/padding-element.js +25 -16
- package/dist/elements/layout/page-break-element.d.ts +17 -0
- package/dist/elements/layout/page-break-element.js +24 -0
- package/dist/elements/layout/page-builder-element.d.ts +32 -0
- package/dist/elements/layout/page-builder-element.js +45 -0
- package/dist/elements/layout/positioned-element.d.ts +47 -0
- package/dist/elements/layout/positioned-element.js +73 -0
- package/dist/elements/layout/repeating-header-element.d.ts +3 -3
- package/dist/elements/layout/repeating-header-element.js +8 -12
- package/dist/elements/layout/rotated-box-element.d.ts +35 -0
- package/dist/elements/layout/rotated-box-element.js +54 -0
- package/dist/elements/layout/rotated-element.d.ts +32 -0
- package/dist/elements/layout/rotated-element.js +41 -0
- package/dist/elements/layout/sized-container-element.d.ts +2 -2
- package/dist/elements/layout/sized-container-element.js +6 -11
- package/dist/elements/layout/struct-group.d.ts +28 -0
- package/dist/elements/layout/struct-group.js +42 -0
- package/dist/elements/line-element.d.ts +3 -3
- package/dist/elements/line-element.js +5 -10
- package/dist/elements/page-element.d.ts +26 -6
- package/dist/elements/page-element.js +72 -27
- package/dist/elements/pdf-document-element.d.ts +10 -4
- package/dist/elements/pdf-document-element.js +11 -10
- package/dist/elements/pdf-element.d.ts +150 -4
- package/dist/elements/pdf-element.js +138 -20
- package/dist/elements/rectangle-element.d.ts +52 -11
- package/dist/elements/rectangle-element.js +129 -36
- package/dist/elements/row-element.d.ts +45 -4
- package/dist/elements/row-element.js +66 -18
- package/dist/elements/text-element.d.ts +132 -11
- package/dist/elements/text-element.js +206 -56
- package/dist/forms/acroform.d.ts +63 -0
- package/dist/forms/acroform.js +427 -0
- package/dist/forms/appearance.d.ts +58 -0
- package/dist/forms/appearance.js +204 -0
- package/dist/forms/field.d.ts +164 -0
- package/dist/forms/field.js +1 -0
- package/dist/forms/pdf.d.ts +19 -0
- package/dist/forms/pdf.js +28 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -19
- package/dist/ir/display-list.d.ts +197 -8
- package/dist/ir/display-list.js +8 -2
- package/dist/layout/alignment.d.ts +10 -0
- package/dist/layout/alignment.js +6 -0
- package/dist/layout/box-constraints.d.ts +61 -0
- package/dist/layout/box-constraints.js +88 -6
- package/dist/layout/collect-images.d.ts +9 -0
- package/dist/layout/collect-images.js +34 -0
- package/dist/layout/fragmentation.d.ts +20 -1
- package/dist/layout/fragmentation.js +74 -18
- package/dist/layout/insets.d.ts +28 -0
- package/dist/layout/insets.js +20 -0
- package/dist/platform/browser-fs.d.ts +2 -0
- package/dist/platform/browser-fs.js +9 -0
- package/dist/platform/browser-image.d.ts +5 -0
- package/dist/platform/browser-image.js +13 -0
- package/dist/platform/node-fs.d.ts +2 -0
- package/dist/platform/node-fs.js +10 -0
- package/dist/platform/node-image.d.ts +5 -0
- package/dist/platform/node-image.js +9 -0
- package/dist/renderer/anchor-renderer.d.ts +6 -0
- package/dist/renderer/anchor-renderer.js +11 -0
- package/dist/renderer/bookmark-renderer.d.ts +6 -0
- package/dist/renderer/bookmark-renderer.js +11 -0
- package/dist/renderer/container-renderer.d.ts +3 -3
- package/dist/renderer/container-renderer.js +11 -28
- package/dist/renderer/default-text-style-renderer.d.ts +6 -0
- package/dist/renderer/default-text-style-renderer.js +10 -0
- package/dist/renderer/deferred-renderer.d.ts +3 -3
- package/dist/renderer/deferred-renderer.js +8 -23
- package/dist/renderer/emoji-image.d.ts +9 -0
- package/dist/renderer/emoji-image.js +68 -0
- package/dist/renderer/expanded-renderer.d.ts +3 -3
- package/dist/renderer/expanded-renderer.js +6 -21
- package/dist/renderer/form-field-renderer.d.ts +20 -0
- package/dist/renderer/form-field-renderer.js +12 -0
- package/dist/renderer/image-renderer.d.ts +4 -4
- package/dist/renderer/image-renderer.js +86 -75
- package/dist/renderer/index.d.ts +10 -10
- package/dist/renderer/index.js +10 -26
- package/dist/renderer/keep-together-renderer.d.ts +10 -0
- package/dist/renderer/keep-together-renderer.js +12 -0
- package/dist/renderer/line-renderer.d.ts +3 -3
- package/dist/renderer/line-renderer.js +13 -28
- package/dist/renderer/link-renderer.d.ts +6 -0
- package/dist/renderer/link-renderer.js +11 -0
- package/dist/renderer/padding-renderer.d.ts +3 -3
- package/dist/renderer/padding-renderer.js +6 -21
- package/dist/renderer/page-break-renderer.d.ts +14 -0
- package/dist/renderer/page-break-renderer.js +15 -0
- package/dist/renderer/page-builder-renderer.d.ts +6 -0
- package/dist/renderer/page-builder-renderer.js +11 -0
- package/dist/renderer/page-renderer.d.ts +2 -2
- package/dist/renderer/page-renderer.js +115 -77
- package/dist/renderer/pdf-backend.d.ts +31 -4
- package/dist/renderer/pdf-backend.js +287 -49
- package/dist/renderer/pdf-config.js +4 -7
- package/dist/renderer/pdf-document-class.d.ts +5 -5
- package/dist/renderer/pdf-document-class.js +27 -41
- package/dist/renderer/pdf-document-renderer.d.ts +19 -10
- package/dist/renderer/pdf-document-renderer.js +135 -91
- package/dist/renderer/pdf-renderer.d.ts +2 -2
- package/dist/renderer/pdf-renderer.js +165 -90
- package/dist/renderer/positioned-renderer.d.ts +6 -0
- package/dist/renderer/positioned-renderer.js +10 -0
- package/dist/renderer/rectangle-renderer.d.ts +3 -3
- package/dist/renderer/rectangle-renderer.js +49 -44
- package/dist/renderer/repeating-header-renderer.d.ts +3 -3
- package/dist/renderer/repeating-header-renderer.js +10 -26
- package/dist/renderer/rotated-renderer.d.ts +7 -0
- package/dist/renderer/rotated-renderer.js +29 -0
- package/dist/renderer/row-renderer.d.ts +3 -3
- package/dist/renderer/row-renderer.js +11 -28
- package/dist/renderer/struct-group-renderer.d.ts +10 -0
- package/dist/renderer/struct-group-renderer.js +24 -0
- package/dist/renderer/text-renderer.d.ts +9 -5
- package/dist/renderer/text-renderer.js +437 -89
- package/dist/text/advance.d.ts +34 -0
- package/dist/text/advance.js +45 -0
- package/dist/text/arabic.d.ts +20 -0
- package/dist/text/arabic.js +101 -0
- package/dist/text/bidi.d.ts +33 -0
- package/dist/text/bidi.js +94 -0
- package/dist/text/emoji-codepoints.d.ts +1 -0
- package/dist/text/emoji-codepoints.js +29 -0
- package/dist/text/font-fallback.d.ts +25 -0
- package/dist/text/font-fallback.js +38 -0
- package/dist/text/line-breaker.d.ts +50 -12
- package/dist/text/line-breaker.js +158 -37
- package/dist/text/line-metrics.d.ts +49 -0
- package/dist/text/line-metrics.js +57 -0
- package/dist/text/orphans-widows.d.ts +27 -0
- package/dist/text/orphans-widows.js +53 -0
- package/dist/text/shape.d.ts +29 -0
- package/dist/text/shape.js +67 -0
- package/dist/text/text-decoration.d.ts +50 -0
- package/dist/text/text-decoration.js +62 -0
- package/dist/text/text-style.d.ts +60 -0
- package/dist/text/text-style.js +79 -0
- package/dist/utils/afm-parser.d.ts +34 -0
- package/dist/utils/afm-parser.js +76 -17
- package/dist/utils/bytes.d.ts +24 -0
- package/dist/utils/bytes.js +76 -0
- package/dist/utils/dest-registry.d.ts +22 -0
- package/dist/utils/dest-registry.js +32 -0
- package/dist/utils/flex-layout.d.ts +36 -9
- package/dist/utils/flex-layout.js +207 -30
- package/dist/utils/font-metrics.d.ts +20 -1
- package/dist/utils/font-metrics.js +1 -2
- package/dist/utils/font-path.js +3 -6
- package/dist/utils/gsub.d.ts +36 -0
- package/dist/utils/gsub.js +178 -0
- package/dist/utils/image-helper.d.ts +6 -5
- package/dist/utils/image-helper.js +101 -111
- package/dist/utils/inflate.d.ts +16 -0
- package/dist/utils/inflate.js +52 -0
- package/dist/utils/md5.d.ts +4 -0
- package/dist/utils/md5.js +79 -0
- package/dist/utils/outline.d.ts +21 -0
- package/dist/utils/outline.js +79 -0
- package/dist/utils/pdf-object-manager.d.ts +110 -8
- package/dist/utils/pdf-object-manager.js +583 -141
- package/dist/utils/renderer-registry.d.ts +13 -1
- package/dist/utils/renderer-registry.js +23 -6
- package/dist/utils/struct-tree.d.ts +44 -0
- package/dist/utils/struct-tree.js +129 -0
- package/dist/utils/ttf-parser.d.ts +134 -2
- package/dist/utils/ttf-parser.js +873 -50
- package/dist/utils/ttf-subsetter.d.ts +1 -1
- package/dist/utils/ttf-subsetter.js +40 -42
- package/dist/utils/ua-xmp.d.ts +9 -0
- package/dist/utils/ua-xmp.js +32 -0
- package/dist/utils/utf8-to-windows1252-encoder.d.ts +6 -0
- package/dist/utils/utf8-to-windows1252-encoder.js +11 -4
- package/dist/utils/woff.d.ts +10 -0
- package/dist/utils/woff.js +116 -0
- package/dist/validators/element-validator.d.ts +2 -2
- package/dist/validators/element-validator.js +31 -26
- package/package.json +22 -2
|
@@ -1,6 +1,6 @@
|
|
|
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";
|
|
1
|
+
import { PDFElement, LayoutContext, FlexiblePDFElement, WithChild, FlexibleElement } from "../pdf-element.js";
|
|
2
|
+
import { BoxConstraints, Offset, Size } from "../../layout/box-constraints.js";
|
|
3
|
+
import { Fragmentable, FragmentResult } from "../../layout/fragmentation.js";
|
|
4
4
|
interface ExpandedElementParams extends FlexibleElement, WithChild {
|
|
5
5
|
}
|
|
6
6
|
export declare class ExpandedElement extends FlexiblePDFElement implements Fragmentable {
|
|
@@ -9,7 +9,7 @@ export declare class ExpandedElement extends FlexiblePDFElement implements Fragm
|
|
|
9
9
|
private y;
|
|
10
10
|
private width;
|
|
11
11
|
private height;
|
|
12
|
-
constructor({ flex, child }: ExpandedElementParams);
|
|
12
|
+
constructor({ flex, basis, basisFactor, child }: ExpandedElementParams);
|
|
13
13
|
calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
|
|
14
14
|
/**
|
|
15
15
|
* When the column paginates, a flex region can't "fill" across pages, so it delegates to
|
|
@@ -24,6 +24,8 @@ export declare class ExpandedElement extends FlexiblePDFElement implements Fragm
|
|
|
24
24
|
y: number;
|
|
25
25
|
width: number;
|
|
26
26
|
height: number;
|
|
27
|
+
basis: number;
|
|
28
|
+
basisFactor: number | undefined;
|
|
27
29
|
child: PDFElement;
|
|
28
30
|
};
|
|
29
31
|
}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class ExpandedElement extends pdf_element_1.FlexiblePDFElement {
|
|
9
|
-
constructor({ flex, child }) {
|
|
10
|
-
super({ flex });
|
|
1
|
+
import { Validator } from "../../validators/element-validator.js";
|
|
2
|
+
import { FlexiblePDFElement, } from "../pdf-element.js";
|
|
3
|
+
import { BoxConstraints } from "../../layout/box-constraints.js";
|
|
4
|
+
import { isFragmentable } from "../../layout/fragmentation.js";
|
|
5
|
+
export class ExpandedElement extends FlexiblePDFElement {
|
|
6
|
+
constructor({ flex, basis, basisFactor, child }) {
|
|
7
|
+
super({ flex, basis, basisFactor });
|
|
11
8
|
this.x = 0;
|
|
12
9
|
this.y = 0;
|
|
13
10
|
this.width = 0;
|
|
@@ -20,17 +17,17 @@ class ExpandedElement extends pdf_element_1.FlexiblePDFElement {
|
|
|
20
17
|
// Absolute placement from the parent; assignment (not +=) so re-layout is idempotent.
|
|
21
18
|
this.x = offset.x;
|
|
22
19
|
this.y = offset.y;
|
|
23
|
-
|
|
20
|
+
Validator.validateFlexElement(this);
|
|
24
21
|
if (constraints.hasBoundedHeight) {
|
|
25
|
-
// A bounded region: fill it - the normal flex
|
|
22
|
+
// A bounded region: fill it - the normal flex behavior.
|
|
26
23
|
this.height = constraints.maxHeight;
|
|
27
|
-
this.child.calculateLayout(
|
|
24
|
+
this.child.calculateLayout(BoxConstraints.loose(this.width, this.height), { x: this.x, y: this.y }, ctx);
|
|
28
25
|
}
|
|
29
26
|
else {
|
|
30
27
|
// Unbounded (measuring while paginating): there's no leftover space to fill, so
|
|
31
28
|
// collapse to the child's natural height. This lets an overflowing column flow
|
|
32
29
|
// instead of the flex silently hiding the overflow.
|
|
33
|
-
const childSize = this.child.calculateLayout(
|
|
30
|
+
const childSize = this.child.calculateLayout(BoxConstraints.loose(this.width, Infinity), { x: this.x, y: this.y }, ctx);
|
|
34
31
|
this.height = childSize.height;
|
|
35
32
|
}
|
|
36
33
|
// Top-left coordinates; the Y-flip now happens once at the IR -> backend seam.
|
|
@@ -43,7 +40,7 @@ class ExpandedElement extends pdf_element_1.FlexiblePDFElement {
|
|
|
43
40
|
* whole Expanded moves on.
|
|
44
41
|
*/
|
|
45
42
|
fragment(maxHeight, width, ctx) {
|
|
46
|
-
if (!
|
|
43
|
+
if (!isFragmentable(this.child)) {
|
|
47
44
|
return { fitted: null, remainder: this };
|
|
48
45
|
}
|
|
49
46
|
const split = this.child.fragment(maxHeight, width, ctx);
|
|
@@ -53,7 +50,14 @@ class ExpandedElement extends pdf_element_1.FlexiblePDFElement {
|
|
|
53
50
|
};
|
|
54
51
|
}
|
|
55
52
|
cloneWithChild(child) {
|
|
56
|
-
|
|
53
|
+
// The basis has to come along: a fragment that lost it would claim only its share of the leftover,
|
|
54
|
+
// so a `flexBasis` slot would silently change width on the page it continues onto.
|
|
55
|
+
return new ExpandedElement({
|
|
56
|
+
flex: this.flex,
|
|
57
|
+
basis: this.basis,
|
|
58
|
+
basisFactor: this.basisFactor,
|
|
59
|
+
child,
|
|
60
|
+
});
|
|
57
61
|
}
|
|
58
62
|
getProps() {
|
|
59
63
|
return {
|
|
@@ -61,8 +65,9 @@ class ExpandedElement extends pdf_element_1.FlexiblePDFElement {
|
|
|
61
65
|
y: this.y,
|
|
62
66
|
width: this.width,
|
|
63
67
|
height: this.height,
|
|
68
|
+
basis: this.basis,
|
|
69
|
+
basisFactor: this.basisFactor,
|
|
64
70
|
child: this.child,
|
|
65
71
|
};
|
|
66
72
|
}
|
|
67
73
|
}
|
|
68
|
-
exports.ExpandedElement = ExpandedElement;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { BoxConstraints, Offset, Size } from "../../layout/box-constraints.js";
|
|
2
|
+
import { Fragmentable, FragmentResult } from "../../layout/fragmentation.js";
|
|
3
|
+
import { LayoutContext, PDFElement } from "../pdf-element.js";
|
|
4
|
+
/**
|
|
5
|
+
* A layout-TRANSPARENT wrapper that tries to keep its content on ONE page (CSS `break-inside: avoid`).
|
|
6
|
+
* It delegates all layout and rendering to its child, so the visual result is byte-identical to the
|
|
7
|
+
* unwrapped content; only pagination behaves differently.
|
|
8
|
+
*
|
|
9
|
+
* The rule, decided in `fragment`:
|
|
10
|
+
* 1. Fits in the space offered here -> place it whole.
|
|
11
|
+
* 2. Does not fit here but fits on a FRESH -> veto the split: defer the whole group to the next
|
|
12
|
+
* full page page (`fitted: null`), so it lands together there.
|
|
13
|
+
* 3. Taller than a whole page -> drop the veto and split normally. A group that can
|
|
14
|
+
* never fit on one page must break, or pagination
|
|
15
|
+
* could not terminate. Inner `keepTogether`s survive
|
|
16
|
+
* this (they are separate elements, re-evaluated when
|
|
17
|
+
* the child splits) - graceful degrade, outer->inner.
|
|
18
|
+
*
|
|
19
|
+
* A forced page break inside the group (a `PageBreak`, or a `breakBefore`/`breakAfter`) contradicts
|
|
20
|
+
* keeping it together: the break wins (CSS behaves the same), and we WARN once rather than silently
|
|
21
|
+
* swallowing either intent.
|
|
22
|
+
*/
|
|
23
|
+
export declare class KeepTogetherElement extends PDFElement implements Fragmentable {
|
|
24
|
+
private child;
|
|
25
|
+
private warnedBreakConflict;
|
|
26
|
+
constructor({ child }: {
|
|
27
|
+
child: PDFElement;
|
|
28
|
+
});
|
|
29
|
+
calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
|
|
30
|
+
relativeSizeFactor(horizontal: boolean): number | undefined;
|
|
31
|
+
needsBoundedMain(horizontal: boolean): boolean;
|
|
32
|
+
hasForcedBreak(): boolean;
|
|
33
|
+
breaksBefore(): boolean;
|
|
34
|
+
breaksAfter(): boolean;
|
|
35
|
+
fragment(maxHeight: number, width: number, ctx: LayoutContext): FragmentResult;
|
|
36
|
+
/** Delegate the actual split to the child, unwrapping - once we have decided to break the group, the
|
|
37
|
+
* pieces just flow (any INNER keepTogether lives in the child subtree and still vetoes there). */
|
|
38
|
+
private splitChild;
|
|
39
|
+
getProps(): {
|
|
40
|
+
child: PDFElement;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { BoxConstraints } from "../../layout/box-constraints.js";
|
|
2
|
+
import { isFragmentable } from "../../layout/fragmentation.js";
|
|
3
|
+
import { PDFElement } from "../pdf-element.js";
|
|
4
|
+
/**
|
|
5
|
+
* A layout-TRANSPARENT wrapper that tries to keep its content on ONE page (CSS `break-inside: avoid`).
|
|
6
|
+
* It delegates all layout and rendering to its child, so the visual result is byte-identical to the
|
|
7
|
+
* unwrapped content; only pagination behaves differently.
|
|
8
|
+
*
|
|
9
|
+
* The rule, decided in `fragment`:
|
|
10
|
+
* 1. Fits in the space offered here -> place it whole.
|
|
11
|
+
* 2. Does not fit here but fits on a FRESH -> veto the split: defer the whole group to the next
|
|
12
|
+
* full page page (`fitted: null`), so it lands together there.
|
|
13
|
+
* 3. Taller than a whole page -> drop the veto and split normally. A group that can
|
|
14
|
+
* never fit on one page must break, or pagination
|
|
15
|
+
* could not terminate. Inner `keepTogether`s survive
|
|
16
|
+
* this (they are separate elements, re-evaluated when
|
|
17
|
+
* the child splits) - graceful degrade, outer->inner.
|
|
18
|
+
*
|
|
19
|
+
* A forced page break inside the group (a `PageBreak`, or a `breakBefore`/`breakAfter`) contradicts
|
|
20
|
+
* keeping it together: the break wins (CSS behaves the same), and we WARN once rather than silently
|
|
21
|
+
* swallowing either intent.
|
|
22
|
+
*/
|
|
23
|
+
export class KeepTogetherElement extends PDFElement {
|
|
24
|
+
constructor({ child }) {
|
|
25
|
+
super();
|
|
26
|
+
this.warnedBreakConflict = false;
|
|
27
|
+
this.child = child;
|
|
28
|
+
}
|
|
29
|
+
calculateLayout(constraints, offset, ctx) {
|
|
30
|
+
return this.child.calculateLayout(constraints, offset, ctx);
|
|
31
|
+
}
|
|
32
|
+
// Transparent: forward the sizing hints the flex parent reads, so a %-sized or Spacer-holding child
|
|
33
|
+
// keeps working when it is wrapped (the wrapper must not swallow these).
|
|
34
|
+
relativeSizeFactor(horizontal) {
|
|
35
|
+
return this.child.relativeSizeFactor(horizontal);
|
|
36
|
+
}
|
|
37
|
+
needsBoundedMain(horizontal) {
|
|
38
|
+
return this.child.needsBoundedMain(horizontal);
|
|
39
|
+
}
|
|
40
|
+
// Transparent to break control: a break-before/after on the wrapped element still applies to the group
|
|
41
|
+
// as a whole, and an inner forced break still forces the parent to fragment us (so `fragment` runs).
|
|
42
|
+
hasForcedBreak() {
|
|
43
|
+
return this.child.hasForcedBreak();
|
|
44
|
+
}
|
|
45
|
+
breaksBefore() {
|
|
46
|
+
return this.child.breaksBefore();
|
|
47
|
+
}
|
|
48
|
+
breaksAfter() {
|
|
49
|
+
return this.child.breaksAfter();
|
|
50
|
+
}
|
|
51
|
+
fragment(maxHeight, width, ctx) {
|
|
52
|
+
// Contradiction: a forced break inside the group beats keeping it together. Split at the break by
|
|
53
|
+
// delegating to the child (which honours it), and say so once - never keep such a group whole.
|
|
54
|
+
if (this.child.hasForcedBreak()) {
|
|
55
|
+
if (!this.warnedBreakConflict) {
|
|
56
|
+
this.warnedBreakConflict = true;
|
|
57
|
+
console.warn("keepTogether contains a forced page break (PageBreak / breakBefore / breakAfter). The break " +
|
|
58
|
+
"wins and the group is split. Remove the break, or the keepTogether, to resolve this.");
|
|
59
|
+
}
|
|
60
|
+
return this.splitChild(maxHeight, width, ctx);
|
|
61
|
+
}
|
|
62
|
+
const natural = this.child.calculateLayout(BoxConstraints.loose(width, Infinity), { x: 0, y: 0 }, ctx).height;
|
|
63
|
+
// 1. Fits in the offered space: keep whole.
|
|
64
|
+
if (natural <= maxHeight)
|
|
65
|
+
return { fitted: this, remainder: null };
|
|
66
|
+
// 2. Would fit on a fresh full page: veto the split, defer the whole group to the next page. Only
|
|
67
|
+
// reachable in a PARTIAL region (something is already on this page); alone on a full page,
|
|
68
|
+
// `maxHeight` IS the page body, so `natural > maxHeight` means case 3, never this.
|
|
69
|
+
const pageBody = ctx.pageBodyHeight ?? maxHeight;
|
|
70
|
+
if (natural <= pageBody)
|
|
71
|
+
return { fitted: null, remainder: this };
|
|
72
|
+
// 3. Bigger than a whole page: it can never be kept together, so split it.
|
|
73
|
+
return this.splitChild(maxHeight, width, ctx);
|
|
74
|
+
}
|
|
75
|
+
/** Delegate the actual split to the child, unwrapping - once we have decided to break the group, the
|
|
76
|
+
* pieces just flow (any INNER keepTogether lives in the child subtree and still vetoes there). */
|
|
77
|
+
splitChild(maxHeight, width, ctx) {
|
|
78
|
+
if (!isFragmentable(this.child))
|
|
79
|
+
return { fitted: null, remainder: this };
|
|
80
|
+
return this.child.fragment(maxHeight, width, ctx);
|
|
81
|
+
}
|
|
82
|
+
getProps() {
|
|
83
|
+
return { child: this.child };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { PDFElement, LayoutContext } from "../pdf-element.js";
|
|
2
|
+
import { BoxConstraints, Offset, Size } from "../../layout/box-constraints.js";
|
|
3
|
+
/**
|
|
4
|
+
* Makes its child a clickable hyperlink. `href` is an external URL; `dest` is an internal named
|
|
5
|
+
* destination (an `Anchor` elsewhere in the document) - exactly one is set. Layout-transparent: it
|
|
6
|
+
* delegates `calculateLayout` to the child and records the resulting box, which the renderer turns into
|
|
7
|
+
* a `Link` IR node (and the page renderer into a /Link annotation). The link draws nothing itself -
|
|
8
|
+
* style the child (e.g. a blue Text) if you want it to look like a link.
|
|
9
|
+
*/
|
|
10
|
+
export declare class LinkElement extends PDFElement {
|
|
11
|
+
private href?;
|
|
12
|
+
private dest?;
|
|
13
|
+
private child;
|
|
14
|
+
private x;
|
|
15
|
+
private y;
|
|
16
|
+
private width;
|
|
17
|
+
private height;
|
|
18
|
+
constructor({ href, dest, child }: {
|
|
19
|
+
href?: string;
|
|
20
|
+
dest?: string;
|
|
21
|
+
child: PDFElement;
|
|
22
|
+
});
|
|
23
|
+
calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
|
|
24
|
+
getProps(): {
|
|
25
|
+
href: string | undefined;
|
|
26
|
+
dest: string | undefined;
|
|
27
|
+
child: PDFElement;
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { PDFElement } from "../pdf-element.js";
|
|
2
|
+
/**
|
|
3
|
+
* Makes its child a clickable hyperlink. `href` is an external URL; `dest` is an internal named
|
|
4
|
+
* destination (an `Anchor` elsewhere in the document) - exactly one is set. Layout-transparent: it
|
|
5
|
+
* delegates `calculateLayout` to the child and records the resulting box, which the renderer turns into
|
|
6
|
+
* a `Link` IR node (and the page renderer into a /Link annotation). The link draws nothing itself -
|
|
7
|
+
* style the child (e.g. a blue Text) if you want it to look like a link.
|
|
8
|
+
*/
|
|
9
|
+
export class LinkElement extends PDFElement {
|
|
10
|
+
constructor({ href, dest, child }) {
|
|
11
|
+
super();
|
|
12
|
+
this.x = 0;
|
|
13
|
+
this.y = 0;
|
|
14
|
+
this.width = 0;
|
|
15
|
+
this.height = 0;
|
|
16
|
+
this.href = href;
|
|
17
|
+
this.dest = dest;
|
|
18
|
+
this.child = child;
|
|
19
|
+
}
|
|
20
|
+
calculateLayout(constraints, offset, ctx) {
|
|
21
|
+
const size = this.child.calculateLayout(constraints, offset, ctx);
|
|
22
|
+
this.x = offset.x;
|
|
23
|
+
this.y = offset.y;
|
|
24
|
+
this.width = size.width;
|
|
25
|
+
this.height = size.height;
|
|
26
|
+
return size;
|
|
27
|
+
}
|
|
28
|
+
getProps() {
|
|
29
|
+
return {
|
|
30
|
+
href: this.href,
|
|
31
|
+
dest: this.dest,
|
|
32
|
+
child: this.child,
|
|
33
|
+
x: this.x,
|
|
34
|
+
y: this.y,
|
|
35
|
+
width: this.width,
|
|
36
|
+
height: this.height,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
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";
|
|
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";
|
|
4
|
+
import { type EdgeSpecs } from "../../layout/insets.js";
|
|
4
5
|
interface PaddingElementParams extends WithChild {
|
|
5
|
-
|
|
6
|
+
/** Still unresolved: a side may be a percentage of the offered WIDTH (see `Insets`). */
|
|
7
|
+
margin: EdgeSpecs;
|
|
6
8
|
}
|
|
7
9
|
export declare class PaddingElement extends SizedPDFElement implements Fragmentable {
|
|
8
10
|
private child;
|
|
@@ -15,6 +17,9 @@ export declare class PaddingElement extends SizedPDFElement implements Fragmenta
|
|
|
15
17
|
* child can't be split, the whole padding moves on as the remainder.
|
|
16
18
|
*/
|
|
17
19
|
fragment(maxHeight: number, width: number, ctx: LayoutContext): FragmentResult;
|
|
20
|
+
hasForcedBreak(): boolean;
|
|
21
|
+
breaksBefore(): boolean;
|
|
22
|
+
breaksAfter(): boolean;
|
|
18
23
|
private cloneWithChild;
|
|
19
24
|
calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
|
|
20
25
|
getProps(): {
|
|
@@ -22,7 +27,7 @@ export declare class PaddingElement extends SizedPDFElement implements Fragmenta
|
|
|
22
27
|
y: number;
|
|
23
28
|
width: number | undefined;
|
|
24
29
|
height: number | undefined;
|
|
25
|
-
margin:
|
|
30
|
+
margin: EdgeSpecs;
|
|
26
31
|
child: PDFElement;
|
|
27
32
|
};
|
|
28
33
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const fragmentation_1 = require("../../layout/fragmentation");
|
|
8
|
-
class PaddingElement extends pdf_element_1.SizedPDFElement {
|
|
1
|
+
import { Validator } from "../../validators/element-validator.js";
|
|
2
|
+
import { SizedPDFElement } from "../pdf-element.js";
|
|
3
|
+
import { BoxConstraints } from "../../layout/box-constraints.js";
|
|
4
|
+
import { isFragmentable } from "../../layout/fragmentation.js";
|
|
5
|
+
import { resolveEdges } from "../../layout/insets.js";
|
|
6
|
+
export class PaddingElement extends SizedPDFElement {
|
|
9
7
|
constructor({ margin, child }) {
|
|
10
8
|
super({ x: 0, y: 0 });
|
|
11
9
|
this.child = child;
|
|
@@ -18,29 +16,41 @@ class PaddingElement extends pdf_element_1.SizedPDFElement {
|
|
|
18
16
|
* child can't be split, the whole padding moves on as the remainder.
|
|
19
17
|
*/
|
|
20
18
|
fragment(maxHeight, width, ctx) {
|
|
21
|
-
if (!
|
|
19
|
+
if (!isFragmentable(this.child)) {
|
|
22
20
|
return { fitted: null, remainder: this };
|
|
23
21
|
}
|
|
24
|
-
const [marginTop, marginRight, marginBottom, marginLeft] = this.margin;
|
|
22
|
+
const [marginTop, marginRight, marginBottom, marginLeft] = resolveEdges(this.margin, width);
|
|
25
23
|
const childWidth = width - marginLeft - marginRight;
|
|
26
24
|
const childMaxHeight = maxHeight - marginTop - marginBottom;
|
|
27
25
|
const split = this.child.fragment(childMaxHeight, childWidth, ctx);
|
|
28
26
|
return {
|
|
29
27
|
fitted: split.fitted ? this.cloneWithChild(split.fitted) : null,
|
|
30
28
|
remainder: split.remainder ? this.cloneWithChild(split.remainder) : null,
|
|
29
|
+
forceBreak: split.forceBreak,
|
|
31
30
|
};
|
|
32
31
|
}
|
|
32
|
+
hasForcedBreak() {
|
|
33
|
+
return this.child.hasForcedBreak();
|
|
34
|
+
}
|
|
35
|
+
// Padding is a transparent single-child wrapper, so a break-before/after on the wrapped element must
|
|
36
|
+
// still be seen by the parent packer at this boundary (otherwise it is swallowed).
|
|
37
|
+
breaksBefore() {
|
|
38
|
+
return this.child.breaksBefore();
|
|
39
|
+
}
|
|
40
|
+
breaksAfter() {
|
|
41
|
+
return this.child.breaksAfter();
|
|
42
|
+
}
|
|
33
43
|
cloneWithChild(child) {
|
|
34
44
|
return new PaddingElement({ margin: this.margin, child });
|
|
35
45
|
}
|
|
36
46
|
calculateLayout(constraints, offset, ctx) {
|
|
37
|
-
var _a;
|
|
38
47
|
// Padding takes the width it is offered; its height shrink-wraps the child.
|
|
39
48
|
if (constraints.hasBoundedWidth)
|
|
40
49
|
this.width = constraints.maxWidth;
|
|
41
50
|
this.x = offset.x;
|
|
42
51
|
this.y = offset.y;
|
|
43
|
-
|
|
52
|
+
// Percentages resolve against the width we were OFFERED, so every pass gets the same answer.
|
|
53
|
+
const [marginTop, marginRight, marginBottom, marginLeft] = resolveEdges(this.margin, constraints.maxWidth);
|
|
44
54
|
// The child is inset by the margins: shifted down/right, narrowed by the
|
|
45
55
|
// horizontal margins, and left height-unbounded so it sizes to its own content.
|
|
46
56
|
const childOffset = {
|
|
@@ -50,7 +60,7 @@ class PaddingElement extends pdf_element_1.SizedPDFElement {
|
|
|
50
60
|
const childWidth = constraints.hasBoundedWidth
|
|
51
61
|
? Math.max(0, constraints.maxWidth - marginLeft - marginRight)
|
|
52
62
|
: Infinity;
|
|
53
|
-
const childConstraints =
|
|
63
|
+
const childConstraints = BoxConstraints.loose(childWidth, Infinity);
|
|
54
64
|
const childSize = this.child.calculateLayout(childConstraints, childOffset, ctx);
|
|
55
65
|
this.height = childSize.height + marginTop + marginBottom;
|
|
56
66
|
// Unbounded width (e.g. a fixed child in a Row): shrink-wrap to the child + insets,
|
|
@@ -58,9 +68,9 @@ class PaddingElement extends pdf_element_1.SizedPDFElement {
|
|
|
58
68
|
if (!constraints.hasBoundedWidth) {
|
|
59
69
|
this.width = childSize.width + marginLeft + marginRight;
|
|
60
70
|
}
|
|
61
|
-
|
|
71
|
+
Validator.validateSizedElement(this);
|
|
62
72
|
// Top-left coordinates; the Y-flip now happens once at the IR -> backend seam.
|
|
63
|
-
return { width:
|
|
73
|
+
return { width: this.width ?? 0, height: this.height };
|
|
64
74
|
}
|
|
65
75
|
getProps() {
|
|
66
76
|
return {
|
|
@@ -73,4 +83,3 @@ class PaddingElement extends pdf_element_1.SizedPDFElement {
|
|
|
73
83
|
};
|
|
74
84
|
}
|
|
75
85
|
}
|
|
76
|
-
exports.PaddingElement = PaddingElement;
|
|
@@ -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 page break. Placed in a flow (a `Column`/`Row`/`Box`), it makes everything AFTER it start
|
|
5
|
+
* on a fresh page, no matter how much space is left. It takes zero space and draws nothing - the
|
|
6
|
+
* pagination packer consumes it at the cut, so it never reaches the render pass.
|
|
7
|
+
*
|
|
8
|
+
* Nesting works: a break deep inside a container is honoured because the packer fragments any child
|
|
9
|
+
* whose subtree `hasForcedBreak()`, and the resulting `forceBreak` bubbles up so the outer flow stops
|
|
10
|
+
* too. A break wins over a `keepTogether` (a forced break cannot be avoided) - CSS behaves the same.
|
|
11
|
+
*/
|
|
12
|
+
export declare class PageBreakElement extends PDFElement {
|
|
13
|
+
calculateLayout(_constraints: BoxConstraints, _offset: Offset, _ctx: LayoutContext): Size;
|
|
14
|
+
isPageBreak(): boolean;
|
|
15
|
+
hasForcedBreak(): boolean;
|
|
16
|
+
getProps(): Record<string, never>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { PDFElement } from "../pdf-element.js";
|
|
2
|
+
/**
|
|
3
|
+
* A forced page break. Placed in a flow (a `Column`/`Row`/`Box`), it makes everything AFTER it start
|
|
4
|
+
* on a fresh page, no matter how much space is left. It takes zero space and draws nothing - the
|
|
5
|
+
* pagination packer consumes it at the cut, so it never reaches the render pass.
|
|
6
|
+
*
|
|
7
|
+
* Nesting works: a break deep inside a container is honoured because the packer fragments any child
|
|
8
|
+
* whose subtree `hasForcedBreak()`, and the resulting `forceBreak` bubbles up so the outer flow stops
|
|
9
|
+
* too. A break wins over a `keepTogether` (a forced break cannot be avoided) - CSS behaves the same.
|
|
10
|
+
*/
|
|
11
|
+
export class PageBreakElement extends PDFElement {
|
|
12
|
+
calculateLayout(_constraints, _offset, _ctx) {
|
|
13
|
+
return { width: 0, height: 0 };
|
|
14
|
+
}
|
|
15
|
+
isPageBreak() {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
hasForcedBreak() {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
getProps() {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { BoxConstraints, Offset, Size } from "../../layout/box-constraints.js";
|
|
2
|
+
import { LayoutContext, PageInfo, PDFElement } from "../pdf-element.js";
|
|
3
|
+
/**
|
|
4
|
+
* Builds its subtree from the page it lands on, so a header, a footer or plain body content can show
|
|
5
|
+
* `pageNumber` / `pageCount`. The closure runs on EVERY layout, because the numbers differ between the
|
|
6
|
+
* two driver passes: pagination has to size the element before the page total exists, so it builds
|
|
7
|
+
* against a provisional "1 of 1"; the render pass then rebuilds it with the real `PageInfo`.
|
|
8
|
+
*
|
|
9
|
+
* Two consequences, and neither is magic - both come from the same chicken-and-egg: the content decides
|
|
10
|
+
* the page count that the content displays.
|
|
11
|
+
*
|
|
12
|
+
* 1. In the flowing BODY the box is reserved by the provisional build, so a much wider final string
|
|
13
|
+
* (`"9 of 10"` vs `"1 of 1"`) can paint slightly past it. Keep dynamic body content roughly constant
|
|
14
|
+
* in width.
|
|
15
|
+
* 2. A header/footer is re-laid out per page, so its real height is honoured - but the BODY band was
|
|
16
|
+
* already sized against the provisional build. A later page whose header is SHORTER simply gains
|
|
17
|
+
* room (harmless). A later page whose header is TALLER shrinks the band its body was measured for,
|
|
18
|
+
* and that body can overflow. So: a conditional header may shrink on later pages, never grow.
|
|
19
|
+
*/
|
|
20
|
+
export declare class PageBuilderElement extends PDFElement {
|
|
21
|
+
private composed?;
|
|
22
|
+
private readonly build;
|
|
23
|
+
constructor({ build }: {
|
|
24
|
+
build: (info: PageInfo) => PDFElement;
|
|
25
|
+
});
|
|
26
|
+
/** The real page info during the render pass; a provisional "1 of 1" while paginating. */
|
|
27
|
+
private infoFrom;
|
|
28
|
+
calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
|
|
29
|
+
getProps(): {
|
|
30
|
+
composed: PDFElement | undefined;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { PDFElement } from "../pdf-element.js";
|
|
2
|
+
import { resolvePageSize } from "../page-element.js";
|
|
3
|
+
/**
|
|
4
|
+
* Builds its subtree from the page it lands on, so a header, a footer or plain body content can show
|
|
5
|
+
* `pageNumber` / `pageCount`. The closure runs on EVERY layout, because the numbers differ between the
|
|
6
|
+
* two driver passes: pagination has to size the element before the page total exists, so it builds
|
|
7
|
+
* against a provisional "1 of 1"; the render pass then rebuilds it with the real `PageInfo`.
|
|
8
|
+
*
|
|
9
|
+
* Two consequences, and neither is magic - both come from the same chicken-and-egg: the content decides
|
|
10
|
+
* the page count that the content displays.
|
|
11
|
+
*
|
|
12
|
+
* 1. In the flowing BODY the box is reserved by the provisional build, so a much wider final string
|
|
13
|
+
* (`"9 of 10"` vs `"1 of 1"`) can paint slightly past it. Keep dynamic body content roughly constant
|
|
14
|
+
* in width.
|
|
15
|
+
* 2. A header/footer is re-laid out per page, so its real height is honoured - but the BODY band was
|
|
16
|
+
* already sized against the provisional build. A later page whose header is SHORTER simply gains
|
|
17
|
+
* room (harmless). A later page whose header is TALLER shrinks the band its body was measured for,
|
|
18
|
+
* and that body can overflow. So: a conditional header may shrink on later pages, never grow.
|
|
19
|
+
*/
|
|
20
|
+
export class PageBuilderElement extends PDFElement {
|
|
21
|
+
constructor({ build }) {
|
|
22
|
+
super();
|
|
23
|
+
this.build = build;
|
|
24
|
+
}
|
|
25
|
+
/** The real page info during the render pass; a provisional "1 of 1" while paginating. */
|
|
26
|
+
infoFrom(ctx) {
|
|
27
|
+
return (ctx.pageInfo ?? {
|
|
28
|
+
pageNumber: 1,
|
|
29
|
+
pageCount: 1,
|
|
30
|
+
subPageNumber: 1,
|
|
31
|
+
subPageTotalPages: 1,
|
|
32
|
+
pageSize: resolvePageSize(ctx.pageConfig),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
calculateLayout(constraints, offset, ctx) {
|
|
36
|
+
this.composed = this.build(this.infoFrom(ctx));
|
|
37
|
+
return this.composed.calculateLayout(constraints, offset, ctx);
|
|
38
|
+
}
|
|
39
|
+
// Named `composed` (not `child`) like DeferredElement: the pre-layout image walk keys on `child`, and
|
|
40
|
+
// this subtree does not exist yet when it runs. So an Image inside a PageBuilder skips aspect-ratio
|
|
41
|
+
// pre-resolution - give it both width and height, exactly as inside a Deferred/Table subtree.
|
|
42
|
+
getProps() {
|
|
43
|
+
return { composed: this.composed };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { BoxConstraints, Offset, Size } from "../../layout/box-constraints.js";
|
|
2
|
+
import { LayoutContext, PDFElement, WithChild } from "../pdf-element.js";
|
|
3
|
+
/** A position anchor along one axis: `start` (left/top), `center`, or `end` (right/bottom). */
|
|
4
|
+
export type PositionAnchor = "start" | "center" | "end";
|
|
5
|
+
/**
|
|
6
|
+
* How a `Positioned` child sits in its frame. Two ways, pick per axis:
|
|
7
|
+
* - EDGE pinning: `top`/`right`/`bottom`/`left` (points from that edge; negative pokes outside).
|
|
8
|
+
* Pinning BOTH sides of an axis stretches the child to fill between them.
|
|
9
|
+
* - ANCHOR + nudge: `h`/`v` anchor the child (start/center/end) and `x`/`y` nudge it from there
|
|
10
|
+
* (e.g. `{ h: "center", x: -10 }` = centered minus 10pt). The child shrink-wraps to its content.
|
|
11
|
+
* An edge wins over an anchor on the same axis. With nothing set the child sits at the top-left.
|
|
12
|
+
*/
|
|
13
|
+
export interface PositionedInsets {
|
|
14
|
+
top?: number;
|
|
15
|
+
right?: number;
|
|
16
|
+
bottom?: number;
|
|
17
|
+
left?: number;
|
|
18
|
+
h?: PositionAnchor;
|
|
19
|
+
v?: PositionAnchor;
|
|
20
|
+
x?: number;
|
|
21
|
+
y?: number;
|
|
22
|
+
}
|
|
23
|
+
interface PositionedElementParams extends WithChild, PositionedInsets {
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* An out-of-flow child, placed relative to the nearest enclosing positioning frame: a `relative`
|
|
27
|
+
* Box, or failing that the page's content box. It takes ZERO space in the normal flow -
|
|
28
|
+
* `calculateLayout` returns `Size(0,0)` and instead registers a placement closure on the frame. The
|
|
29
|
+
* frame runs that closure once it knows its own size, so `right`/`bottom` resolve against the final
|
|
30
|
+
* box and the child can overflow it (a negative `top`/`left` makes it poke into / out of the
|
|
31
|
+
* corner, e.g. a badge or tab).
|
|
32
|
+
*
|
|
33
|
+
* Every page supplies a frame - header, footer and body alike - so within a document there is
|
|
34
|
+
* always one. Outside of one we REFUSE rather than draw: the element used to fall through to its
|
|
35
|
+
* child's default (0, 0), which is how a header watermark landed silently in the page corner
|
|
36
|
+
* (ISSUE-4). Content that lands somewhere unasked is worse than content that does not render.
|
|
37
|
+
*/
|
|
38
|
+
export declare class PositionedElement extends PDFElement {
|
|
39
|
+
private child;
|
|
40
|
+
private insets;
|
|
41
|
+
constructor({ child, top, right, bottom, left, h, v, x, y }: PositionedElementParams);
|
|
42
|
+
calculateLayout(_constraints: BoxConstraints, _offset: Offset, ctx: LayoutContext): Size;
|
|
43
|
+
/** Lays the child out at the resolved position inside (or overflowing) the frame box. */
|
|
44
|
+
private placeInFrame;
|
|
45
|
+
getProps(): WithChild;
|
|
46
|
+
}
|
|
47
|
+
export {};
|