@jasy/pdf 1.0.0-alpha.7 → 1.0.0-alpha.9
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/README.md +11 -10
- package/dist/api/content.d.ts +5 -4
- package/dist/api/content.js +13 -7
- package/dist/api/descriptor.js +23 -0
- package/dist/api/dimension.d.ts +56 -0
- package/dist/api/dimension.js +27 -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 +3 -0
- package/dist/api/index.js +3 -0
- package/dist/api/insets.d.ts +19 -12
- package/dist/api/insets.js +18 -7
- package/dist/api/layout.d.ts +20 -6
- package/dist/api/layout.js +31 -6
- package/dist/api/page-builder.d.ts +8 -0
- package/dist/api/page-builder.js +12 -0
- package/dist/api/structure.d.ts +11 -0
- package/dist/api/structure.js +17 -4
- package/dist/api/text.d.ts +42 -6
- package/dist/api/text.js +29 -5
- package/dist/crypto/legacy-handler.d.ts +43 -0
- package/dist/crypto/legacy-handler.js +145 -0
- package/dist/crypto/security-handler.d.ts +16 -0
- package/dist/crypto/security-handler.js +26 -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 +21 -1
- package/dist/elements/container-element.js +25 -10
- 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 +18 -4
- package/dist/elements/image-element.js +27 -12
- package/dist/elements/index.d.ts +6 -0
- package/dist/elements/index.js +6 -0
- package/dist/elements/layout/expanded-element.d.ts +3 -1
- package/dist/elements/layout/expanded-element.js +12 -3
- package/dist/elements/layout/padding-element.d.ts +4 -2
- package/dist/elements/layout/padding-element.js +4 -2
- package/dist/elements/layout/page-builder-element.js +2 -0
- package/dist/elements/pdf-element.d.ts +59 -1
- package/dist/elements/pdf-element.js +62 -1
- package/dist/elements/rectangle-element.d.ts +20 -6
- package/dist/elements/rectangle-element.js +25 -12
- package/dist/elements/row-element.d.ts +24 -1
- package/dist/elements/row-element.js +25 -10
- package/dist/elements/text-element.d.ts +60 -1
- package/dist/elements/text-element.js +132 -33
- 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/ir/display-list.d.ts +40 -7
- package/dist/ir/display-list.js +8 -1
- package/dist/layout/alignment.d.ts +10 -0
- package/dist/layout/alignment.js +6 -0
- package/dist/layout/box-constraints.d.ts +53 -0
- package/dist/layout/box-constraints.js +72 -0
- package/dist/layout/insets.d.ts +28 -0
- package/dist/layout/insets.js +20 -0
- package/dist/renderer/form-field-renderer.d.ts +20 -0
- package/dist/renderer/form-field-renderer.js +12 -0
- package/dist/renderer/image-renderer.js +2 -1
- package/dist/renderer/page-renderer.js +8 -2
- package/dist/renderer/pdf-backend.d.ts +6 -0
- package/dist/renderer/pdf-backend.js +86 -31
- package/dist/renderer/pdf-document-renderer.js +8 -1
- package/dist/renderer/pdf-renderer.js +19 -1
- package/dist/renderer/rectangle-renderer.js +8 -2
- package/dist/renderer/text-renderer.d.ts +2 -2
- package/dist/renderer/text-renderer.js +150 -42
- package/dist/text/advance.js +5 -2
- 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/font-fallback.d.ts +25 -0
- package/dist/text/font-fallback.js +38 -0
- package/dist/text/line-breaker.d.ts +35 -1
- package/dist/text/line-breaker.js +76 -16
- 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-style.d.ts +25 -0
- package/dist/text/text-style.js +44 -1
- package/dist/utils/dest-registry.d.ts +5 -2
- package/dist/utils/dest-registry.js +5 -5
- package/dist/utils/flex-layout.d.ts +24 -4
- package/dist/utils/flex-layout.js +169 -7
- package/dist/utils/font-metrics.d.ts +5 -0
- package/dist/utils/gsub.d.ts +36 -0
- package/dist/utils/gsub.js +178 -0
- package/dist/utils/inflate.d.ts +16 -0
- package/dist/utils/inflate.js +52 -0
- package/dist/utils/outline.js +1 -2
- package/dist/utils/pdf-object-manager.d.ts +39 -0
- package/dist/utils/pdf-object-manager.js +166 -15
- package/dist/utils/struct-tree.js +2 -3
- package/dist/utils/ttf-parser.d.ts +6 -0
- package/dist/utils/ttf-parser.js +19 -0
- package/dist/utils/utf8-to-windows1252-encoder.d.ts +6 -0
- package/dist/utils/utf8-to-windows1252-encoder.js +10 -0
- package/dist/utils/woff.d.ts +10 -0
- package/dist/utils/woff.js +116 -0
- package/dist/validators/element-validator.js +14 -3
- package/package.json +10 -2
|
@@ -3,6 +3,7 @@ import type { PDFPageConfig } from "./page-element.js";
|
|
|
3
3
|
import type { ResolvedTextStyle } from "../text/text-style.js";
|
|
4
4
|
import type { OverflowPolicy } from "../layout/fragmentation.js";
|
|
5
5
|
import type { BoxConstraints, Offset, Size } from "../layout/box-constraints.js";
|
|
6
|
+
import type { CrossAlign } from "../layout/alignment.js";
|
|
6
7
|
/**
|
|
7
8
|
* Everything the layout pass needs, threaded explicitly (no global singleton):
|
|
8
9
|
* font metrics for measuring, and the geometry of the page currently being laid out.
|
|
@@ -29,8 +30,18 @@ export interface PositioningFrame {
|
|
|
29
30
|
* the document's final physical page total. `pageSize` is the media box in points, orientation applied.
|
|
30
31
|
*/
|
|
31
32
|
export interface PageInfo {
|
|
33
|
+
/** 1-based, counting PHYSICAL pages across the whole document. */
|
|
32
34
|
pageNumber: number;
|
|
35
|
+
/** Physical pages in the whole document. */
|
|
33
36
|
pageCount: number;
|
|
37
|
+
/**
|
|
38
|
+
* 1-based within the LOGICAL page this sheet came from - the `Page(...)` element the author wrote.
|
|
39
|
+
* A document of one `Page` has `subPageNumber === pageNumber`; a document of several (an invoice
|
|
40
|
+
* plus its attachment, say) restarts the count at each one.
|
|
41
|
+
*/
|
|
42
|
+
subPageNumber: number;
|
|
43
|
+
/** Physical pages this logical page produced. */
|
|
44
|
+
subPageTotalPages: number;
|
|
34
45
|
pageSize: {
|
|
35
46
|
width: number;
|
|
36
47
|
height: number;
|
|
@@ -108,6 +119,31 @@ export declare abstract class PDFElement {
|
|
|
108
119
|
* parent packer once this element has been placed whole. Default: no.
|
|
109
120
|
*/
|
|
110
121
|
breaksAfter(): boolean;
|
|
122
|
+
/**
|
|
123
|
+
* This child's own cross-axis alignment inside its Column / Row (CSS `align-self`), overriding the
|
|
124
|
+
* container's `align`. Lives on the base element because any child of a stack may set it, not just
|
|
125
|
+
* the sized ones. `undefined` means "follow the container".
|
|
126
|
+
*/
|
|
127
|
+
alignSelf?: CrossAlign;
|
|
128
|
+
/** Sets `alignSelf` and returns the element, so a factory can thread it through in one expression. */
|
|
129
|
+
withAlignSelf(align: CrossAlign | undefined): this;
|
|
130
|
+
/**
|
|
131
|
+
* Where this child sits among its siblings (CSS `order`), lowest first, source order deciding ties.
|
|
132
|
+
* It moves the child in the LAYOUT only - the element tree, and therefore the reading order a tagged
|
|
133
|
+
* PDF exposes, is untouched. Default 0.
|
|
134
|
+
*/
|
|
135
|
+
order: number;
|
|
136
|
+
/**
|
|
137
|
+
* How willingly this child gives up main-axis space when the line overflows (CSS `flex-shrink`),
|
|
138
|
+
* weighted by its own size as CSS does. **Default 0, deliberately unlike CSS's 1**: shrinking is a
|
|
139
|
+
* change of what a document LOOKS like, and every document written before this existed must keep
|
|
140
|
+
* laying out exactly as it did. Opt in per child.
|
|
141
|
+
*/
|
|
142
|
+
flexShrink: number;
|
|
143
|
+
/** Sets `flexShrink` and returns the element. */
|
|
144
|
+
withFlexShrink(shrink: number | undefined): this;
|
|
145
|
+
/** Sets `order` and returns the element, so a factory can thread it through in one expression. */
|
|
146
|
+
withOrder(order: number | undefined): this;
|
|
111
147
|
}
|
|
112
148
|
export declare abstract class SizedPDFElement extends PDFElement {
|
|
113
149
|
protected x: number;
|
|
@@ -120,14 +156,32 @@ export declare abstract class SizedPDFElement extends PDFElement {
|
|
|
120
156
|
export declare abstract class FlexiblePDFElement extends PDFElement {
|
|
121
157
|
protected flex: number;
|
|
122
158
|
protected verticalChildAlignment: VerticalAlignment;
|
|
159
|
+
/** Points reserved before any leftover is shared out (CSS `flex-basis`); default 0. */
|
|
160
|
+
protected basis: number;
|
|
161
|
+
/** The same as a fraction of the line, for `flexBasis: "25%"`. */
|
|
162
|
+
protected basisFactor?: number;
|
|
123
163
|
constructor(data: FlexibleElement);
|
|
124
164
|
getFlex(): number;
|
|
165
|
+
/**
|
|
166
|
+
* The main extent this child starts from, before the leftover is shared out. `lineMain` is what the
|
|
167
|
+
* line offers its items (its extent minus the gaps), so a percentage basis resolves against the same
|
|
168
|
+
* base a percentage WIDTH already does - and falls back to 0 on an unbounded axis, where a fraction
|
|
169
|
+
* has no meaning.
|
|
170
|
+
*/
|
|
171
|
+
/** Whether a PERCENTAGE basis was given - it resolves to 0 on an unbounded axis, so asking for the
|
|
172
|
+
* number alone cannot tell "no basis" from "a basis that does not apply here". */
|
|
173
|
+
hasBasisFactor(): boolean;
|
|
174
|
+
getBasis(lineMain: number): number;
|
|
125
175
|
}
|
|
126
176
|
export declare enum HorizontalAlignment {
|
|
177
|
+
/** CSS `text-align: start` - the side the writing STARTS on: left in `ltr`, right in `rtl`. The
|
|
178
|
+
* default, so a right-to-left paragraph begins on the right without anyone asking for it. */
|
|
179
|
+
start = "START",
|
|
127
180
|
left = "LEFT",
|
|
128
181
|
right = "RIGHT",
|
|
129
182
|
center = "CENTER",
|
|
130
|
-
|
|
183
|
+
/** CSS `text-align: justify` - both edges line up, the last line of a paragraph excepted. */
|
|
184
|
+
justify = "JUSTIFY"
|
|
131
185
|
}
|
|
132
186
|
export declare enum VerticalAlignment {
|
|
133
187
|
top = "TOP",
|
|
@@ -142,6 +196,10 @@ export interface WithChild {
|
|
|
142
196
|
}
|
|
143
197
|
export interface FlexibleElement {
|
|
144
198
|
flex: number;
|
|
199
|
+
/** Points reserved before the leftover is shared out (CSS `flex-basis`). */
|
|
200
|
+
basis?: number;
|
|
201
|
+
/** The same as a fraction of the line, for `flexBasis: "25%"`. */
|
|
202
|
+
basisFactor?: number;
|
|
145
203
|
verticalChildAlignment?: VerticalAlignment;
|
|
146
204
|
}
|
|
147
205
|
export interface SizedElement {
|
|
@@ -5,6 +5,19 @@ export class PDFElement {
|
|
|
5
5
|
// fragmentation copies (adoptStructId), so a paragraph or table split across pages stays ONE structure
|
|
6
6
|
// element. Only the elements that actually tag (Text, Image, StructGroup) read it.
|
|
7
7
|
this._structId = _nextStructId++;
|
|
8
|
+
/**
|
|
9
|
+
* Where this child sits among its siblings (CSS `order`), lowest first, source order deciding ties.
|
|
10
|
+
* It moves the child in the LAYOUT only - the element tree, and therefore the reading order a tagged
|
|
11
|
+
* PDF exposes, is untouched. Default 0.
|
|
12
|
+
*/
|
|
13
|
+
this.order = 0;
|
|
14
|
+
/**
|
|
15
|
+
* How willingly this child gives up main-axis space when the line overflows (CSS `flex-shrink`),
|
|
16
|
+
* weighted by its own size as CSS does. **Default 0, deliberately unlike CSS's 1**: shrinking is a
|
|
17
|
+
* change of what a document LOOKS like, and every document written before this existed must keep
|
|
18
|
+
* laying out exactly as it did. Opt in per child.
|
|
19
|
+
*/
|
|
20
|
+
this.flexShrink = 0;
|
|
8
21
|
}
|
|
9
22
|
get structId() {
|
|
10
23
|
return this._structId;
|
|
@@ -65,6 +78,31 @@ export class PDFElement {
|
|
|
65
78
|
breaksAfter() {
|
|
66
79
|
return false;
|
|
67
80
|
}
|
|
81
|
+
/** Sets `alignSelf` and returns the element, so a factory can thread it through in one expression. */
|
|
82
|
+
withAlignSelf(align) {
|
|
83
|
+
if (align !== undefined)
|
|
84
|
+
this.alignSelf = align;
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
/** Sets `flexShrink` and returns the element. */
|
|
88
|
+
withFlexShrink(shrink) {
|
|
89
|
+
if (shrink !== undefined) {
|
|
90
|
+
if (!Number.isFinite(shrink) || shrink < 0) {
|
|
91
|
+
throw new Error(`Invalid flexShrink ${shrink}: it must be a number of at least 0.`);
|
|
92
|
+
}
|
|
93
|
+
this.flexShrink = shrink;
|
|
94
|
+
}
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
/** Sets `order` and returns the element, so a factory can thread it through in one expression. */
|
|
98
|
+
withOrder(order) {
|
|
99
|
+
if (order !== undefined) {
|
|
100
|
+
if (!Number.isFinite(order))
|
|
101
|
+
throw new Error(`Invalid order ${order}: it must be a number.`);
|
|
102
|
+
this.order = order;
|
|
103
|
+
}
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
68
106
|
}
|
|
69
107
|
export class SizedPDFElement extends PDFElement {
|
|
70
108
|
constructor(data) {
|
|
@@ -83,17 +121,40 @@ export class FlexiblePDFElement extends PDFElement {
|
|
|
83
121
|
super();
|
|
84
122
|
this.flex = data.flex;
|
|
85
123
|
this.verticalChildAlignment = data.verticalChildAlignment || VerticalAlignment.middle;
|
|
124
|
+
this.basis = data.basis ?? 0;
|
|
125
|
+
this.basisFactor = data.basisFactor;
|
|
86
126
|
}
|
|
87
127
|
getFlex() {
|
|
88
128
|
return this.flex;
|
|
89
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* The main extent this child starts from, before the leftover is shared out. `lineMain` is what the
|
|
132
|
+
* line offers its items (its extent minus the gaps), so a percentage basis resolves against the same
|
|
133
|
+
* base a percentage WIDTH already does - and falls back to 0 on an unbounded axis, where a fraction
|
|
134
|
+
* has no meaning.
|
|
135
|
+
*/
|
|
136
|
+
/** Whether a PERCENTAGE basis was given - it resolves to 0 on an unbounded axis, so asking for the
|
|
137
|
+
* number alone cannot tell "no basis" from "a basis that does not apply here". */
|
|
138
|
+
hasBasisFactor() {
|
|
139
|
+
return this.basisFactor !== undefined;
|
|
140
|
+
}
|
|
141
|
+
getBasis(lineMain) {
|
|
142
|
+
if (this.basisFactor !== undefined) {
|
|
143
|
+
return Number.isFinite(lineMain) ? lineMain * this.basisFactor : 0;
|
|
144
|
+
}
|
|
145
|
+
return this.basis;
|
|
146
|
+
}
|
|
90
147
|
}
|
|
91
148
|
export var HorizontalAlignment;
|
|
92
149
|
(function (HorizontalAlignment) {
|
|
150
|
+
/** CSS `text-align: start` - the side the writing STARTS on: left in `ltr`, right in `rtl`. The
|
|
151
|
+
* default, so a right-to-left paragraph begins on the right without anyone asking for it. */
|
|
152
|
+
HorizontalAlignment["start"] = "START";
|
|
93
153
|
HorizontalAlignment["left"] = "LEFT";
|
|
94
154
|
HorizontalAlignment["right"] = "RIGHT";
|
|
95
155
|
HorizontalAlignment["center"] = "CENTER";
|
|
96
|
-
|
|
156
|
+
/** CSS `text-align: justify` - both edges line up, the last line of a paragraph excepted. */
|
|
157
|
+
HorizontalAlignment["justify"] = "JUSTIFY";
|
|
97
158
|
})(HorizontalAlignment || (HorizontalAlignment = {}));
|
|
98
159
|
export var VerticalAlignment;
|
|
99
160
|
(function (VerticalAlignment) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Radii } from "../ir/display-list.js";
|
|
2
|
+
import type { GradientInput } from "../api/gradient.js";
|
|
1
3
|
import { Color } from "../common/color.js";
|
|
2
4
|
import { BoxConstraints, Offset, Size } from "../layout/box-constraints.js";
|
|
3
5
|
import { Fragmentable, FragmentResult } from "../layout/fragmentation.js";
|
|
@@ -12,10 +14,11 @@ export interface SideBorders {
|
|
|
12
14
|
}
|
|
13
15
|
interface RectangleElementParams extends SizedElement, WithChildren {
|
|
14
16
|
color?: Color;
|
|
15
|
-
|
|
17
|
+
/** Solid colour, or a box-relative gradient the renderer resolves against this box. */
|
|
18
|
+
backgroundColor?: Color | GradientInput;
|
|
16
19
|
borderWidth?: number;
|
|
17
|
-
/** Corner radius in points
|
|
18
|
-
radius?: number;
|
|
20
|
+
/** Corner radius in points - one number for all four corners, or per corner. 0 = sharp (default). */
|
|
21
|
+
radius?: number | Radii;
|
|
19
22
|
/** Individual side borders; overrides the uniform `color` border when present. */
|
|
20
23
|
sideBorders?: SideBorders;
|
|
21
24
|
/** When true, this box is a positioning frame for `Positioned` descendants (CSS `relative`). */
|
|
@@ -28,6 +31,17 @@ interface RectangleElementParams extends SizedElement, WithChildren {
|
|
|
28
31
|
widthFactor?: number;
|
|
29
32
|
/** Height as a fraction (0..1) of the offered height; see `widthFactor`. */
|
|
30
33
|
heightFactor?: number;
|
|
34
|
+
/** Lower / upper bounds per axis, in points or as a fraction; see `SizingParams`. */
|
|
35
|
+
minWidth?: number;
|
|
36
|
+
maxWidth?: number;
|
|
37
|
+
minHeight?: number;
|
|
38
|
+
maxHeight?: number;
|
|
39
|
+
minWidthFactor?: number;
|
|
40
|
+
maxWidthFactor?: number;
|
|
41
|
+
minHeightFactor?: number;
|
|
42
|
+
maxHeightFactor?: number;
|
|
43
|
+
/** width / height; derives whichever axis is left open (CSS `aspect-ratio`). */
|
|
44
|
+
aspectRatio?: number;
|
|
31
45
|
/** Start this box on a fresh page (CSS `break-before: page`). */
|
|
32
46
|
breakBefore?: boolean;
|
|
33
47
|
/** Start everything after this box on a fresh page (CSS `break-after: page`). */
|
|
@@ -45,7 +59,7 @@ export declare class RectangleElement extends SizedPDFElement implements Fragmen
|
|
|
45
59
|
private breakBefore;
|
|
46
60
|
private breakAfter;
|
|
47
61
|
private sizeMemory;
|
|
48
|
-
constructor({ children, color, backgroundColor, borderWidth, width, height, radius, sideBorders, relative, overflow,
|
|
62
|
+
constructor({ children, color, backgroundColor, borderWidth, width, height, radius, sideBorders, relative, overflow, breakBefore, breakAfter, ...sizing }: RectangleElementParams);
|
|
49
63
|
breaksBefore(): boolean;
|
|
50
64
|
breaksAfter(): boolean;
|
|
51
65
|
/**
|
|
@@ -72,9 +86,9 @@ export declare class RectangleElement extends SizedPDFElement implements Fragmen
|
|
|
72
86
|
height: number | undefined;
|
|
73
87
|
children: PDFElement[];
|
|
74
88
|
color: Color;
|
|
75
|
-
backgroundColor: Color | undefined;
|
|
89
|
+
backgroundColor: Color | GradientInput | undefined;
|
|
76
90
|
borderWidth: number;
|
|
77
|
-
radius: number;
|
|
91
|
+
radius: number | Radii;
|
|
78
92
|
sideBorders: SideBorders | undefined;
|
|
79
93
|
overflow: "hidden" | "visible";
|
|
80
94
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Color } from "../common/color.js";
|
|
2
|
-
import { BoxConstraints,
|
|
2
|
+
import { BoxConstraints, extentSpecs, resolveSize, } from "../layout/box-constraints.js";
|
|
3
3
|
import { childrenForceBreak, packChildren, } from "../layout/fragmentation.js";
|
|
4
4
|
import { SizedPDFElement, } from "./pdf-element.js";
|
|
5
5
|
export class RectangleElement extends SizedPDFElement {
|
|
6
|
-
constructor({ children = [], color = new Color(0, 0, 0), backgroundColor, borderWidth, width, height, radius, sideBorders, relative, overflow,
|
|
6
|
+
constructor({ children = [], color = new Color(0, 0, 0), backgroundColor, borderWidth, width, height, radius, sideBorders, relative, overflow, breakBefore, breakAfter, ...sizing }) {
|
|
7
7
|
super({ x: 0, y: 0, width, height });
|
|
8
8
|
this.children = [];
|
|
9
9
|
this.children = children;
|
|
@@ -17,7 +17,7 @@ export class RectangleElement extends SizedPDFElement {
|
|
|
17
17
|
this.overflow = overflow ?? "visible";
|
|
18
18
|
this.breakBefore = breakBefore ?? false;
|
|
19
19
|
this.breakAfter = breakAfter ?? false;
|
|
20
|
-
this.sizeMemory = { x: 0, y: 0, width, height
|
|
20
|
+
this.sizeMemory = { ...sizing, x: 0, y: 0, width, height };
|
|
21
21
|
}
|
|
22
22
|
breaksBefore() {
|
|
23
23
|
return this.breakBefore;
|
|
@@ -102,15 +102,21 @@ export class RectangleElement extends SizedPDFElement {
|
|
|
102
102
|
calculateLayout(constraints, offset, ctx) {
|
|
103
103
|
// An explicit extent is a fixed point size or a fraction of the offered box (relative sizing);
|
|
104
104
|
// the fraction only resolves in a bounded region. `undefined` = fill / shrink-wrap below.
|
|
105
|
-
const
|
|
106
|
-
const
|
|
105
|
+
const specs = extentSpecs(this.sizeMemory);
|
|
106
|
+
const resolved = resolveSize(specs.width, specs.height, this.sizeMemory.aspectRatio, constraints);
|
|
107
|
+
const explicitWidth = resolved.width;
|
|
108
|
+
const explicitHeight = resolved.height;
|
|
109
|
+
// min/max come back as narrowed constraints, so the fill and shrink-wrap paths below obey them too.
|
|
110
|
+
const bounds = resolved.constraints;
|
|
107
111
|
// Width: an explicit size wins (clamped), else fill the offered box. (Without this a
|
|
108
112
|
// fixed box would balloon to the parent's size.)
|
|
113
|
+
// Whether there is a region to fill is decided by the constraints we were GIVEN: a `maxWidth` caps
|
|
114
|
+
// a box, it does not turn a shrink-wrapping one into a filling one (CSS `max-width` does not either).
|
|
109
115
|
this.width =
|
|
110
116
|
explicitWidth !== undefined
|
|
111
|
-
?
|
|
117
|
+
? bounds.constrainWidth(explicitWidth)
|
|
112
118
|
: constraints.hasBoundedWidth
|
|
113
|
-
?
|
|
119
|
+
? bounds.maxWidth
|
|
114
120
|
: this.width;
|
|
115
121
|
// Width shrink-wrap: no explicit width AND an unbounded region (e.g. a `Box` badge inside a
|
|
116
122
|
// `Positioned`). Resolved after the children are measured, just below.
|
|
@@ -121,9 +127,9 @@ export class RectangleElement extends SizedPDFElement {
|
|
|
121
127
|
const shrinkWrapHeight = explicitHeight === undefined && !constraints.hasBoundedHeight;
|
|
122
128
|
this.height =
|
|
123
129
|
explicitHeight !== undefined
|
|
124
|
-
?
|
|
130
|
+
? bounds.constrainHeight(explicitHeight)
|
|
125
131
|
: constraints.hasBoundedHeight
|
|
126
|
-
?
|
|
132
|
+
? bounds.maxHeight
|
|
127
133
|
: this.height;
|
|
128
134
|
this.x = this.sizeMemory.x + offset.x;
|
|
129
135
|
this.y = this.sizeMemory.y + offset.y;
|
|
@@ -161,11 +167,18 @@ export class RectangleElement extends SizedPDFElement {
|
|
|
161
167
|
contentWidth = Math.max(contentWidth, childSize.width);
|
|
162
168
|
}
|
|
163
169
|
// No explicit width and no bounded region: shrink-wrap to the widest child.
|
|
170
|
+
// Clamped, so a min/max still applies to a box that sized itself to its content.
|
|
164
171
|
if (shrinkWrapWidth)
|
|
165
|
-
this.width = contentWidth + 2 * this.borderWidth;
|
|
172
|
+
this.width = bounds.constrainWidth(contentWidth + 2 * this.borderWidth);
|
|
173
|
+
// A ratio on a shrink-wrapping box can only be applied HERE: before the children were measured
|
|
174
|
+
// there was no width to derive the height from.
|
|
175
|
+
const ratio = this.sizeMemory.aspectRatio;
|
|
176
|
+
const ratioSizesHeight = ratio !== undefined && ratio > 0 && shrinkWrapWidth && shrinkWrapHeight;
|
|
166
177
|
// No explicit height and no bounded region: the border hugs its content.
|
|
167
|
-
if (
|
|
168
|
-
this.height =
|
|
178
|
+
if (ratioSizesHeight)
|
|
179
|
+
this.height = bounds.constrainHeight((this.width ?? 0) / ratio);
|
|
180
|
+
else if (shrinkWrapHeight)
|
|
181
|
+
this.height = bounds.constrainHeight(contentHeight + 2 * this.borderWidth);
|
|
169
182
|
// The box is sized now: place any out-of-flow `Positioned` descendants against its box.
|
|
170
183
|
if (frame) {
|
|
171
184
|
frame.origin = { x: this.x, y: this.y };
|
|
@@ -8,11 +8,28 @@ interface RowElementParams extends WithChildren {
|
|
|
8
8
|
main?: MainAlign;
|
|
9
9
|
/** Vertical alignment of each child (cross axis); defaults to `stretch`. */
|
|
10
10
|
cross?: CrossAlign;
|
|
11
|
+
/** Lay the children out backwards along the main axis (CSS `row-reverse`). */
|
|
12
|
+
reverse?: boolean;
|
|
13
|
+
/** Let the children flow onto further lines when they do not fit (CSS `flex-wrap`). */
|
|
14
|
+
wrap?: boolean;
|
|
15
|
+
/** How the block of wrapped lines sits across the axis (CSS `align-content`). */
|
|
16
|
+
alignContent?: MainAlign;
|
|
11
17
|
/** Width/height as points (fixed) or a fraction (0..1) of the offered box (relative sizing). */
|
|
12
18
|
width?: number;
|
|
13
19
|
height?: number;
|
|
14
20
|
widthFactor?: number;
|
|
15
21
|
heightFactor?: number;
|
|
22
|
+
/** Lower / upper bounds per axis, in points or as a fraction; see `SizingParams`. */
|
|
23
|
+
minWidth?: number;
|
|
24
|
+
maxWidth?: number;
|
|
25
|
+
minHeight?: number;
|
|
26
|
+
maxHeight?: number;
|
|
27
|
+
minWidthFactor?: number;
|
|
28
|
+
maxWidthFactor?: number;
|
|
29
|
+
minHeightFactor?: number;
|
|
30
|
+
maxHeightFactor?: number;
|
|
31
|
+
/** width / height; derives whichever axis is left open (CSS `aspect-ratio`). */
|
|
32
|
+
aspectRatio?: number;
|
|
16
33
|
/** Start this row on a fresh page (CSS `break-before: page`). */
|
|
17
34
|
breakBefore?: boolean;
|
|
18
35
|
/** Start everything after this row on a fresh page (CSS `break-after: page`). */
|
|
@@ -35,10 +52,13 @@ export declare class RowElement extends SizedPDFElement {
|
|
|
35
52
|
private gap;
|
|
36
53
|
private main;
|
|
37
54
|
private cross;
|
|
55
|
+
private reverse;
|
|
56
|
+
private wrap;
|
|
57
|
+
private alignContent?;
|
|
38
58
|
private breakBefore;
|
|
39
59
|
private breakAfter;
|
|
40
60
|
private requested;
|
|
41
|
-
constructor({ children, gap, main, cross,
|
|
61
|
+
constructor({ children, gap, main, cross, reverse, wrap, alignContent, width, height, breakBefore, breakAfter, ...sizing }: RowElementParams);
|
|
42
62
|
breaksBefore(): boolean;
|
|
43
63
|
breaksAfter(): boolean;
|
|
44
64
|
relativeSizeFactor(horizontal: boolean): number | undefined;
|
|
@@ -55,6 +75,9 @@ export declare class RowElement extends SizedPDFElement {
|
|
|
55
75
|
gap: number;
|
|
56
76
|
main: MainAlign;
|
|
57
77
|
cross: CrossAlign;
|
|
78
|
+
reverse: boolean;
|
|
79
|
+
wrap: boolean;
|
|
80
|
+
alignContent: MainAlign | undefined;
|
|
58
81
|
};
|
|
59
82
|
}
|
|
60
83
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { extentSpecs, resolveSize, } from "../layout/box-constraints.js";
|
|
2
2
|
import { FlexLayoutHelper, HORIZONTAL_AXIS } from "../utils/flex-layout.js";
|
|
3
3
|
import { FlexiblePDFElement, SizedPDFElement, } from "./pdf-element.js";
|
|
4
4
|
/**
|
|
@@ -14,13 +14,16 @@ import { FlexiblePDFElement, SizedPDFElement, } from "./pdf-element.js";
|
|
|
14
14
|
* Grid/Table concern.
|
|
15
15
|
*/
|
|
16
16
|
export class RowElement extends SizedPDFElement {
|
|
17
|
-
constructor({ children, gap, main, cross,
|
|
17
|
+
constructor({ children, gap, main, cross, reverse, wrap, alignContent, width, height, breakBefore, breakAfter, ...sizing }) {
|
|
18
18
|
super({ x: 0, y: 0 });
|
|
19
19
|
this.children = children;
|
|
20
20
|
this.gap = gap ?? 0;
|
|
21
21
|
this.main = main ?? "start";
|
|
22
22
|
this.cross = cross ?? "stretch";
|
|
23
|
-
this.
|
|
23
|
+
this.reverse = reverse ?? false;
|
|
24
|
+
this.wrap = wrap ?? false;
|
|
25
|
+
this.alignContent = alignContent;
|
|
26
|
+
this.requested = { ...sizing, width, height };
|
|
24
27
|
this.breakBefore = breakBefore ?? false;
|
|
25
28
|
this.breakAfter = breakAfter ?? false;
|
|
26
29
|
}
|
|
@@ -49,24 +52,33 @@ export class RowElement extends SizedPDFElement {
|
|
|
49
52
|
this.y = offset.y;
|
|
50
53
|
// Relative sizing: a pinned extent (fixed or a fraction of the offered box, clamped) wins; else
|
|
51
54
|
// width fills the offered space (flex children split the leftover) and height is the tallest child.
|
|
52
|
-
const
|
|
53
|
-
const
|
|
55
|
+
const specs = extentSpecs(this.requested);
|
|
56
|
+
const resolved = resolveSize(specs.width, specs.height, this.requested.aspectRatio, constraints);
|
|
57
|
+
const { width: explicitWidth, height: explicitHeight, constraints: bounds } = resolved;
|
|
58
|
+
// Fill-or-shrink-wrap is decided by the constraints we were GIVEN; a min/max only caps the result.
|
|
54
59
|
const boundedWidth = explicitWidth !== undefined
|
|
55
|
-
?
|
|
60
|
+
? bounds.constrainWidth(explicitWidth)
|
|
56
61
|
: constraints.hasBoundedWidth
|
|
57
|
-
?
|
|
62
|
+
? bounds.maxWidth
|
|
58
63
|
: undefined;
|
|
59
64
|
const boundedHeight = explicitHeight !== undefined
|
|
60
|
-
?
|
|
65
|
+
? bounds.constrainHeight(explicitHeight)
|
|
61
66
|
: constraints.hasBoundedHeight
|
|
62
|
-
?
|
|
67
|
+
? bounds.maxHeight
|
|
63
68
|
: undefined;
|
|
64
69
|
// Horizontal stack: main = width (children fill it), cross = height (children can stretch to it).
|
|
65
70
|
const mainAvail = boundedWidth ?? Infinity;
|
|
66
71
|
const crossAvail = boundedHeight ?? Infinity;
|
|
67
72
|
let result = { mainUsed: 0, crossUsed: 0 };
|
|
68
73
|
if (this.children.length > 0) {
|
|
69
|
-
result = FlexLayoutHelper.layout(this.children, HORIZONTAL_AXIS, mainAvail, crossAvail, this.x, this.y, {
|
|
74
|
+
result = FlexLayoutHelper.layout(this.children, HORIZONTAL_AXIS, mainAvail, crossAvail, this.x, this.y, {
|
|
75
|
+
gap: this.gap,
|
|
76
|
+
main: this.main,
|
|
77
|
+
cross: this.cross,
|
|
78
|
+
reverse: this.reverse,
|
|
79
|
+
wrap: this.wrap,
|
|
80
|
+
alignContent: this.alignContent,
|
|
81
|
+
}, ctx);
|
|
70
82
|
}
|
|
71
83
|
this.width = boundedWidth ?? result.mainUsed;
|
|
72
84
|
this.height = boundedHeight ?? result.crossUsed;
|
|
@@ -82,6 +94,9 @@ export class RowElement extends SizedPDFElement {
|
|
|
82
94
|
gap: this.gap,
|
|
83
95
|
main: this.main,
|
|
84
96
|
cross: this.cross,
|
|
97
|
+
reverse: this.reverse,
|
|
98
|
+
wrap: this.wrap,
|
|
99
|
+
alignContent: this.alignContent,
|
|
85
100
|
};
|
|
86
101
|
}
|
|
87
102
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Color } from "../common/color.js";
|
|
2
2
|
import { FontStyle } from "../utils/pdf-object-manager.js";
|
|
3
3
|
import { BoxConstraints, Offset, Size } from "../layout/box-constraints.js";
|
|
4
|
+
import type { Direction } from "../text/bidi.js";
|
|
5
|
+
import { type TextTransform } from "../text/text-style.js";
|
|
4
6
|
import { Fragmentable, FragmentResult } from "../layout/fragmentation.js";
|
|
5
7
|
import { TextOverflow } from "../text/line-breaker.js";
|
|
6
8
|
import { HorizontalAlignment, LayoutContext, SizedPDFElement } from "./pdf-element.js";
|
|
@@ -9,6 +11,8 @@ export interface TextSegment {
|
|
|
9
11
|
fontStyle?: FontStyle;
|
|
10
12
|
fontColor?: Color;
|
|
11
13
|
fontFamily?: string;
|
|
14
|
+
/** The rest of the family stack for THIS run; unset inherits the Text's own. */
|
|
15
|
+
fontFallback?: string[];
|
|
12
16
|
fontSize?: number;
|
|
13
17
|
/** External URL: this segment becomes an inline hyperlink (a /Link annotation over its glyphs). */
|
|
14
18
|
href?: string;
|
|
@@ -19,7 +23,17 @@ export interface TextSegment {
|
|
|
19
23
|
strikethrough?: boolean;
|
|
20
24
|
/** Extra space after every glyph, in points; unset inherits the Text's own value. */
|
|
21
25
|
letterSpacing?: number;
|
|
26
|
+
/**
|
|
27
|
+
* CSS `vertical-align`, for a footnote marker or a formula index. Shifts THIS run's baseline;
|
|
28
|
+
* it does not resize the text, and it does not change the line's height - set `fontSize` yourself
|
|
29
|
+
* for the smaller look a browser's `<sup>` gets from its default stylesheet.
|
|
30
|
+
*/
|
|
31
|
+
verticalAlign?: VerticalTextAlign;
|
|
22
32
|
}
|
|
33
|
+
/** How far a run sits off the baseline, as a fraction of its font size (up is positive). */
|
|
34
|
+
export declare const VERTICAL_TEXT_SHIFT: Record<VerticalTextAlign, number>;
|
|
35
|
+
/** CSS `vertical-align`, the three values that mean something for a run of text. */
|
|
36
|
+
export type VerticalTextAlign = "baseline" | "super" | "sub";
|
|
23
37
|
/** Accessibility role for the tagged structure tree: a heading level or a paragraph (the default). */
|
|
24
38
|
export type TextRole = "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p";
|
|
25
39
|
interface TextElementParams {
|
|
@@ -27,12 +41,18 @@ interface TextElementParams {
|
|
|
27
41
|
/** Unset (undefined) inherits the cascaded size; see ResolvedTextStyle. */
|
|
28
42
|
fontSize?: number;
|
|
29
43
|
fontFamily?: string;
|
|
44
|
+
/** The rest of the family stack, for code points `fontFamily` cannot draw. */
|
|
45
|
+
fontFallback?: string[];
|
|
30
46
|
fontStyle?: FontStyle;
|
|
31
47
|
content: string | TextSegment[];
|
|
32
48
|
color?: Color;
|
|
33
49
|
textAlignment?: HorizontalAlignment;
|
|
34
50
|
/** Cap the wrapped lines (default: unlimited / open-end). */
|
|
35
51
|
maxLines?: number;
|
|
52
|
+
/** Minimum lines that must stay behind at a page break (CSS `orphans`, default 2). */
|
|
53
|
+
orphans?: number;
|
|
54
|
+
/** Minimum lines that must carry over to the next page (CSS `widows`, default 2). */
|
|
55
|
+
widows?: number;
|
|
36
56
|
/** What to do past `maxLines`: `"clip"` (default) drops them, `"ellipsis"` ends with "…". */
|
|
37
57
|
overflow?: TextOverflow;
|
|
38
58
|
/** Line-height multiplier: each line is `fontSize * lineHeight` tall. Unset means the font's
|
|
@@ -46,6 +66,14 @@ interface TextElementParams {
|
|
|
46
66
|
skipInk?: boolean;
|
|
47
67
|
/** Extra space after every glyph, in points (CSS `letter-spacing`). Default 0. */
|
|
48
68
|
letterSpacing?: number;
|
|
69
|
+
/** Base writing direction (CSS `direction`); decides where a line starts. Default `"ltr"`. */
|
|
70
|
+
direction?: Direction;
|
|
71
|
+
/** CSS `text-transform`; applied before the text is measured or drawn. */
|
|
72
|
+
textTransform?: TextTransform;
|
|
73
|
+
/** CSS `word-spacing`, in points: extra advance at every space. */
|
|
74
|
+
wordSpacing?: number;
|
|
75
|
+
/** CSS `text-indent`, in points: how far the first line starts in. */
|
|
76
|
+
textIndent?: number;
|
|
49
77
|
/** Accessibility role for the tagged structure tree (heading level or paragraph; default `"p"`). */
|
|
50
78
|
role?: TextRole;
|
|
51
79
|
}
|
|
@@ -60,6 +88,11 @@ export declare class TextElement extends SizedPDFElement implements Fragmentable
|
|
|
60
88
|
private readonly rawStrikethrough?;
|
|
61
89
|
private readonly rawSkipInk?;
|
|
62
90
|
private readonly rawLetterSpacing?;
|
|
91
|
+
private readonly rawDirection?;
|
|
92
|
+
private readonly rawFontFallback?;
|
|
93
|
+
private readonly rawTextTransform?;
|
|
94
|
+
private readonly rawWordSpacing?;
|
|
95
|
+
private readonly rawTextIndent?;
|
|
63
96
|
private fontSize;
|
|
64
97
|
private fontFamily;
|
|
65
98
|
private fontStyle;
|
|
@@ -70,12 +103,31 @@ export declare class TextElement extends SizedPDFElement implements Fragmentable
|
|
|
70
103
|
private strikethrough;
|
|
71
104
|
private skipInk;
|
|
72
105
|
private letterSpacing;
|
|
106
|
+
private direction;
|
|
107
|
+
private fontFallback;
|
|
108
|
+
private resolved?;
|
|
109
|
+
private textTransform;
|
|
110
|
+
private wordSpacing;
|
|
111
|
+
private textIndent;
|
|
73
112
|
private content;
|
|
74
113
|
private maxLines?;
|
|
114
|
+
private orphans?;
|
|
115
|
+
private widows?;
|
|
75
116
|
private overflow;
|
|
76
117
|
private readonly role?;
|
|
77
|
-
constructor({ fontSize, content, fontFamily, fontStyle, color, textAlignment, maxLines, overflow, lineHeight, underline, strikethrough, skipInk, letterSpacing, role, }: TextElementParams);
|
|
118
|
+
constructor({ fontSize, content, fontFamily, fontStyle, color, textAlignment, maxLines, orphans, widows, overflow, lineHeight, underline, strikethrough, skipInk, letterSpacing, direction, fontFallback, textTransform, wordSpacing, textIndent, role, }: TextElementParams);
|
|
78
119
|
private resolveStyle;
|
|
120
|
+
/** Justified lines may be squeezed to keep a word; every other alignment gets no slack. Read by the
|
|
121
|
+
* breaker in BOTH passes, or a measured line and a drawn one would disagree about where it ends. */
|
|
122
|
+
/**
|
|
123
|
+
* The content as it is measured AND drawn - `text-transform` applied. One place, so a recased
|
|
124
|
+
* paragraph can never be measured in one casing and drawn in another (`ABC` and `abc` are not the
|
|
125
|
+
* same width in most fonts).
|
|
126
|
+
*/
|
|
127
|
+
private display;
|
|
128
|
+
/** The content with `text-transform` applied, and nothing else. */
|
|
129
|
+
private recased;
|
|
130
|
+
private lineOptions;
|
|
79
131
|
private applyStyle;
|
|
80
132
|
/**
|
|
81
133
|
* Splits the paragraph at line boxes (Slice 1). The lines that fit in `maxHeight` stay;
|
|
@@ -86,6 +138,8 @@ export declare class TextElement extends SizedPDFElement implements Fragmentable
|
|
|
86
138
|
fragment(maxHeight: number, width: number, ctx: LayoutContext): FragmentResult;
|
|
87
139
|
private fragmentString;
|
|
88
140
|
private fragmentSegments;
|
|
141
|
+
/** The orphan/widow minimums for this paragraph; unset means the CSS default of 2 lines each. */
|
|
142
|
+
private breakRule;
|
|
89
143
|
private cloneWithContent;
|
|
90
144
|
calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
|
|
91
145
|
/** The unwrapped single-line width of the content (used when width is unbounded, e.g. inside a Row).
|
|
@@ -109,12 +163,17 @@ export declare class TextElement extends SizedPDFElement implements Fragmentable
|
|
|
109
163
|
content: string | TextSegment[];
|
|
110
164
|
textAlignment: HorizontalAlignment;
|
|
111
165
|
maxLines: number | undefined;
|
|
166
|
+
orphans: number | undefined;
|
|
167
|
+
widows: number | undefined;
|
|
112
168
|
overflow: TextOverflow;
|
|
113
169
|
lineHeight: number | undefined;
|
|
114
170
|
underline: boolean;
|
|
115
171
|
strikethrough: boolean;
|
|
116
172
|
skipInk: boolean;
|
|
117
173
|
letterSpacing: number;
|
|
174
|
+
direction: Direction;
|
|
175
|
+
wordSpacing: number;
|
|
176
|
+
textIndent: number;
|
|
118
177
|
role: TextRole | undefined;
|
|
119
178
|
};
|
|
120
179
|
}
|