@jasy/pdf 1.0.0-alpha.9 → 1.0.0-beta.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.
- package/README.md +27 -9
- package/dist/api/color.js +69 -0
- package/dist/api/content.d.ts +40 -1
- package/dist/api/content.js +83 -0
- package/dist/api/descriptor.js +6 -1
- package/dist/api/dimension.d.ts +3 -2
- package/dist/api/font-url.js +3 -2
- package/dist/api/structure.d.ts +7 -0
- package/dist/api/structure.js +38 -3
- package/dist/api/text.d.ts +23 -2
- package/dist/api/text.js +9 -0
- package/dist/canvas/painter.d.ts +102 -0
- package/dist/canvas/painter.js +183 -0
- package/dist/elements/canvas-element.d.ts +38 -0
- package/dist/elements/canvas-element.js +47 -0
- package/dist/elements/container-element.d.ts +2 -0
- package/dist/elements/container-element.js +6 -0
- package/dist/elements/index.d.ts +2 -0
- package/dist/elements/index.js +2 -0
- package/dist/elements/layout/expanded-element.d.ts +2 -0
- package/dist/elements/layout/expanded-element.js +4 -0
- package/dist/elements/layout/keep-together-element.d.ts +2 -0
- package/dist/elements/layout/keep-together-element.js +4 -0
- package/dist/elements/layout/padding-element.d.ts +5 -0
- package/dist/elements/layout/padding-element.js +8 -0
- package/dist/elements/layout/struct-group.d.ts +2 -0
- package/dist/elements/layout/struct-group.js +4 -0
- package/dist/elements/pdf-element.d.ts +19 -3
- package/dist/elements/pdf-element.js +22 -4
- package/dist/elements/rectangle-element.d.ts +2 -0
- package/dist/elements/rectangle-element.js +6 -0
- package/dist/elements/row-element.d.ts +2 -0
- package/dist/elements/row-element.js +6 -0
- package/dist/elements/svg-element.d.ts +41 -0
- package/dist/elements/svg-element.js +93 -0
- package/dist/elements/text-element.d.ts +33 -1
- package/dist/elements/text-element.js +80 -4
- package/dist/ir/display-list.d.ts +44 -5
- package/dist/layout/min-intrinsic.d.ts +29 -0
- package/dist/layout/min-intrinsic.js +37 -0
- package/dist/renderer/anchor-renderer.js +1 -1
- package/dist/renderer/bookmark-renderer.js +1 -1
- package/dist/renderer/canvas-renderer.d.ts +14 -0
- package/dist/renderer/canvas-renderer.js +36 -0
- package/dist/renderer/container-renderer.js +1 -3
- package/dist/renderer/default-text-style-renderer.js +1 -1
- package/dist/renderer/deferred-renderer.js +1 -1
- package/dist/renderer/expanded-renderer.js +1 -1
- package/dist/renderer/index.d.ts +2 -0
- package/dist/renderer/index.js +2 -0
- package/dist/renderer/keep-together-renderer.js +1 -1
- package/dist/renderer/link-renderer.js +1 -1
- package/dist/renderer/padding-renderer.js +1 -1
- package/dist/renderer/page-builder-renderer.js +1 -1
- package/dist/renderer/page-renderer.js +1 -3
- package/dist/renderer/pdf-backend.d.ts +15 -4
- package/dist/renderer/pdf-backend.js +125 -35
- package/dist/renderer/pdf-renderer.js +5 -1
- package/dist/renderer/positioned-renderer.js +1 -1
- package/dist/renderer/rectangle-renderer.js +1 -3
- package/dist/renderer/repeating-header-renderer.js +1 -2
- package/dist/renderer/rotated-renderer.js +1 -1
- package/dist/renderer/row-renderer.js +1 -3
- package/dist/renderer/struct-group-renderer.js +1 -1
- package/dist/renderer/svg-renderer.d.ts +11 -0
- package/dist/renderer/svg-renderer.js +27 -0
- package/dist/renderer/text-renderer.js +30 -19
- package/dist/svg/css.d.ts +38 -0
- package/dist/svg/css.js +115 -0
- package/dist/svg/errors.d.ts +24 -0
- package/dist/svg/errors.js +30 -0
- package/dist/svg/gradients.d.ts +34 -0
- package/dist/svg/gradients.js +156 -0
- package/dist/svg/index.d.ts +22 -0
- package/dist/svg/index.js +331 -0
- package/dist/svg/shapes.d.ts +6 -0
- package/dist/svg/shapes.js +177 -0
- package/dist/svg/style.d.ts +61 -0
- package/dist/svg/style.js +144 -0
- package/dist/svg/transform.d.ts +13 -0
- package/dist/svg/transform.js +68 -0
- package/dist/svg/units.d.ts +11 -0
- package/dist/svg/units.js +73 -0
- package/dist/svg/xml.d.ts +24 -0
- package/dist/svg/xml.js +158 -0
- package/dist/text/advance.d.ts +3 -0
- package/dist/text/advance.js +4 -3
- package/dist/text/glyph-coverage.d.ts +15 -0
- package/dist/text/glyph-coverage.js +77 -0
- package/dist/text/line-breaker.d.ts +8 -0
- package/dist/text/line-breaker.js +146 -70
- package/dist/text/shape.d.ts +3 -3
- package/dist/text/shape.js +34 -10
- package/dist/text/text-style.d.ts +13 -0
- package/dist/text/text-style.js +6 -0
- package/dist/text/whitespace.d.ts +4 -0
- package/dist/text/whitespace.js +43 -0
- package/dist/text/word-splitting.d.ts +42 -0
- package/dist/text/word-splitting.js +72 -0
- package/dist/utils/flex-layout.js +67 -18
- package/dist/utils/font-metrics.d.ts +9 -3
- package/dist/utils/pdf-object-manager.d.ts +25 -5
- package/dist/utils/pdf-object-manager.js +82 -18
- package/dist/utils/renderer-registry.d.ts +13 -1
- package/dist/utils/renderer-registry.js +22 -1
- package/dist/utils/woff2.d.ts +6 -0
- package/dist/utils/woff2.js +651 -0
- package/dist/vector/path.d.ts +33 -0
- package/dist/vector/path.js +81 -0
- package/package.json +5 -2
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { toColor } from "../api/color.js";
|
|
2
|
+
import { isGradientInput, resolveGradient } from "../api/gradient.js";
|
|
3
|
+
import { pathData } from "../svg/shapes.js";
|
|
4
|
+
import { boundsOf, quadToCubic } from "../vector/path.js";
|
|
5
|
+
const DEGREES = Math.PI / 180;
|
|
6
|
+
export class CanvasPainter {
|
|
7
|
+
constructor(size) {
|
|
8
|
+
this.size = size;
|
|
9
|
+
/** The nodes drawn so far, in order. */
|
|
10
|
+
this.nodes = [];
|
|
11
|
+
/** The path being built. Emptied by every painting call, so a shape cannot leak into the next. */
|
|
12
|
+
this.current = [];
|
|
13
|
+
this.cursor = [0, 0];
|
|
14
|
+
this.start = [0, 0];
|
|
15
|
+
}
|
|
16
|
+
/** Everything drawn, for the renderer. */
|
|
17
|
+
drawn() {
|
|
18
|
+
return this.nodes;
|
|
19
|
+
}
|
|
20
|
+
// --- building a path ---------------------------------------------------------------------------
|
|
21
|
+
move(x, y) {
|
|
22
|
+
this.current.push({ op: "m", x, y });
|
|
23
|
+
this.cursor = [x, y];
|
|
24
|
+
this.start = [x, y];
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
line(x, y) {
|
|
28
|
+
this.current.push({ op: "l", x, y });
|
|
29
|
+
this.cursor = [x, y];
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
/** A cubic Bézier - the curve PDF draws natively. */
|
|
33
|
+
curve(x1, y1, x2, y2, x, y) {
|
|
34
|
+
this.current.push({ op: "c", x1, y1, x2, y2, x, y });
|
|
35
|
+
this.cursor = [x, y];
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
/** A quadratic Bézier. PDF has no operator for one, so it is raised to a cubic exactly. */
|
|
39
|
+
quad(cx, cy, x, y) {
|
|
40
|
+
this.current.push(quadToCubic(this.cursor[0], this.cursor[1], cx, cy, x, y));
|
|
41
|
+
this.cursor = [x, y];
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
close() {
|
|
45
|
+
this.current.push({ op: "z" });
|
|
46
|
+
this.cursor = [...this.start];
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
rect(x, y, width, height) {
|
|
50
|
+
return this.move(x, y)
|
|
51
|
+
.line(x + width, y)
|
|
52
|
+
.line(x + width, y + height)
|
|
53
|
+
.line(x, y + height)
|
|
54
|
+
.close();
|
|
55
|
+
}
|
|
56
|
+
circle(cx, cy, r) {
|
|
57
|
+
return this.ellipse(cx, cy, r, r);
|
|
58
|
+
}
|
|
59
|
+
/** Four cubics; the constant is the standard circle-to-Bézier one. */
|
|
60
|
+
ellipse(cx, cy, rx, ry) {
|
|
61
|
+
const k = 0.5522847498307936;
|
|
62
|
+
const [ox, oy] = [rx * k, ry * k];
|
|
63
|
+
return this.move(cx - rx, cy)
|
|
64
|
+
.curve(cx - rx, cy - oy, cx - ox, cy - ry, cx, cy - ry)
|
|
65
|
+
.curve(cx + ox, cy - ry, cx + rx, cy - oy, cx + rx, cy)
|
|
66
|
+
.curve(cx + rx, cy + oy, cx + ox, cy + ry, cx, cy + ry)
|
|
67
|
+
.curve(cx - ox, cy + ry, cx - rx, cy + oy, cx - rx, cy)
|
|
68
|
+
.close();
|
|
69
|
+
}
|
|
70
|
+
polygon(points) {
|
|
71
|
+
if (points.length === 0)
|
|
72
|
+
return this;
|
|
73
|
+
this.move(points[0][0], points[0][1]);
|
|
74
|
+
for (const [x, y] of points.slice(1))
|
|
75
|
+
this.line(x, y);
|
|
76
|
+
return this.close();
|
|
77
|
+
}
|
|
78
|
+
/** SVG path data, through the same reader `Svg()` uses - arcs and shorthands included. */
|
|
79
|
+
path(d) {
|
|
80
|
+
const parsed = pathData(d);
|
|
81
|
+
this.current.push(...parsed);
|
|
82
|
+
// Where the pen ends up: after a `z` it is back at that subpath's `m`, not at the last point.
|
|
83
|
+
for (const cmd of parsed) {
|
|
84
|
+
if (cmd.op === "m")
|
|
85
|
+
this.start = [cmd.x, cmd.y];
|
|
86
|
+
this.cursor = cmd.op === "z" ? [...this.start] : [cmd.x, cmd.y];
|
|
87
|
+
}
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
// --- painting it -------------------------------------------------------------------------------
|
|
91
|
+
/** Fills the path built so far and starts a new one. Default black, as in SVG and PDF. */
|
|
92
|
+
fill(paint = "black", options = {}) {
|
|
93
|
+
return this.paint(this.resolve(paint), undefined, options.rule);
|
|
94
|
+
}
|
|
95
|
+
/** Strokes the path built so far and starts a new one. */
|
|
96
|
+
stroke(color = "black", options = {}) {
|
|
97
|
+
return this.paint(undefined, this.strokeStyle(color, options));
|
|
98
|
+
}
|
|
99
|
+
fillAndStroke(paint, color, options = {}) {
|
|
100
|
+
return this.paint(this.resolve(paint), this.strokeStyle(color, options), options.rule);
|
|
101
|
+
}
|
|
102
|
+
// --- scopes, instead of save/restore -----------------------------------------------------------
|
|
103
|
+
/**
|
|
104
|
+
* Draws inside a transform. It closes itself, so there is no `restore()` to forget - and a nested
|
|
105
|
+
* `group` composes, exactly like a `<g transform>` in SVG.
|
|
106
|
+
*/
|
|
107
|
+
group(transform, draw) {
|
|
108
|
+
this.assertNoPendingPath("group");
|
|
109
|
+
const matrix = this.matrixOf(transform);
|
|
110
|
+
this.nodes.push({ type: "transform-push", matrix });
|
|
111
|
+
draw(this);
|
|
112
|
+
this.assertNoPendingPath("group");
|
|
113
|
+
this.nodes.push({ type: "transform-pop" });
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Draws clipped to a path. `build` draws the clip shape (nothing is painted from it), `draw` draws
|
|
118
|
+
* what the clip applies to.
|
|
119
|
+
*/
|
|
120
|
+
clipped(build, draw, options = {}) {
|
|
121
|
+
this.assertNoPendingPath("clipped");
|
|
122
|
+
build(this);
|
|
123
|
+
const commands = this.current;
|
|
124
|
+
this.current = [];
|
|
125
|
+
this.nodes.push({ type: "clip-path-push", commands, fillRule: options.rule });
|
|
126
|
+
draw(this);
|
|
127
|
+
this.assertNoPendingPath("clipped");
|
|
128
|
+
this.nodes.push({ type: "clip-pop" });
|
|
129
|
+
return this;
|
|
130
|
+
}
|
|
131
|
+
// --- internals ---------------------------------------------------------------------------------
|
|
132
|
+
paint(fill, stroke, rule) {
|
|
133
|
+
const commands = this.current;
|
|
134
|
+
this.current = [];
|
|
135
|
+
if (commands.length === 0)
|
|
136
|
+
return this;
|
|
137
|
+
this.nodes.push({ type: "path", commands, fill, fillRule: rule, stroke });
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* A path may not cross the edge of a scope. It would be built in one coordinate space and painted
|
|
142
|
+
* in another, and the version that just dropped it made a shape vanish without a word - exactly
|
|
143
|
+
* the silence this codebase refuses everywhere else.
|
|
144
|
+
*/
|
|
145
|
+
assertNoPendingPath(scope) {
|
|
146
|
+
if (this.current.length === 0)
|
|
147
|
+
return;
|
|
148
|
+
this.current = [];
|
|
149
|
+
throw new Error(`@jasy/pdf: a Canvas path was built but never filled or stroked around \`${scope}()\`. ` +
|
|
150
|
+
`Paint it before the scope opens, or move the drawing inside it.`);
|
|
151
|
+
}
|
|
152
|
+
resolve(paint) {
|
|
153
|
+
if (isGradientInput(paint)) {
|
|
154
|
+
const box = boundsOf(this.current);
|
|
155
|
+
return resolveGradient(paint, box.x, box.y, box.width, box.height);
|
|
156
|
+
}
|
|
157
|
+
return toColor(paint);
|
|
158
|
+
}
|
|
159
|
+
strokeStyle(color, options) {
|
|
160
|
+
return {
|
|
161
|
+
color: toColor(color),
|
|
162
|
+
width: options.width ?? 1,
|
|
163
|
+
cap: options.cap,
|
|
164
|
+
join: options.join,
|
|
165
|
+
miterLimit: options.miterLimit,
|
|
166
|
+
dash: options.dash,
|
|
167
|
+
dashOffset: options.dashOffset,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
matrixOf(t) {
|
|
171
|
+
const [ox, oy] = t.origin ?? [0, 0];
|
|
172
|
+
const [sx, sy] = Array.isArray(t.scale) ? t.scale : [t.scale ?? 1, t.scale ?? 1];
|
|
173
|
+
const theta = (t.rotate ?? 0) * DEGREES;
|
|
174
|
+
const [cos, sin] = [Math.cos(theta), Math.sin(theta)];
|
|
175
|
+
// T(translate) · T(origin) · R · S · T(-origin), written out as a PDF `cm` operand.
|
|
176
|
+
const [a, b, c, d] = [cos * sx, sin * sx, -sin * sy, cos * sy];
|
|
177
|
+
const [tx, ty] = t.translate ?? [0, 0];
|
|
178
|
+
// `-sin(0)` is -0, which is a surprising value to read back out of a matrix. It writes as
|
|
179
|
+
// "0.000" either way, so normalising costs nothing and keeps the number predictable.
|
|
180
|
+
const zero = (n) => (n === 0 ? 0 : n);
|
|
181
|
+
return [a, b, c, d, tx + ox - (a * ox + c * oy), ty + oy - (b * ox + d * oy)].map(zero);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { BoxConstraints, Offset, Size, SizingParams } from "../layout/box-constraints.js";
|
|
2
|
+
import type { CanvasPainter, CanvasSize } from "../canvas/painter.js";
|
|
3
|
+
import { LayoutContext, SizedPDFElement } from "./pdf-element.js";
|
|
4
|
+
/** What the user draws. Runs at RENDER time, once the box is known. */
|
|
5
|
+
export type CanvasPaint = (painter: CanvasPainter, size: CanvasSize) => void;
|
|
6
|
+
interface CanvasElementParams extends SizingParams {
|
|
7
|
+
paint: CanvasPaint;
|
|
8
|
+
widthFactor?: number;
|
|
9
|
+
heightFactor?: number;
|
|
10
|
+
/** Alternate text (tagged PDF): with it the drawing is a Figure, without it decoration. */
|
|
11
|
+
alt?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A box in the layout that the caller draws into.
|
|
15
|
+
*
|
|
16
|
+
* It has no children and no intrinsic size, so unlike an image it FILLS what it is offered when no
|
|
17
|
+
* size is given - there is nothing to derive one from. That is also why the callback is handed the
|
|
18
|
+
* resolved size: a drawing written against `size.width` works at any box.
|
|
19
|
+
*/
|
|
20
|
+
export declare class CanvasElement extends SizedPDFElement {
|
|
21
|
+
private paintFn;
|
|
22
|
+
private alt?;
|
|
23
|
+
private requested;
|
|
24
|
+
private sizing;
|
|
25
|
+
private widthFactor?;
|
|
26
|
+
private heightFactor?;
|
|
27
|
+
constructor(params: CanvasElementParams);
|
|
28
|
+
getProps(): {
|
|
29
|
+
x: number;
|
|
30
|
+
y: number;
|
|
31
|
+
width: number | undefined;
|
|
32
|
+
height: number | undefined;
|
|
33
|
+
paint: CanvasPaint;
|
|
34
|
+
alt: string | undefined;
|
|
35
|
+
};
|
|
36
|
+
calculateLayout(constraints: BoxConstraints, offset: Offset, _ctx: LayoutContext): Size;
|
|
37
|
+
}
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { extentSpecs, resolveSize, } from "../layout/box-constraints.js";
|
|
2
|
+
import { SizedPDFElement } from "./pdf-element.js";
|
|
3
|
+
/**
|
|
4
|
+
* A box in the layout that the caller draws into.
|
|
5
|
+
*
|
|
6
|
+
* It has no children and no intrinsic size, so unlike an image it FILLS what it is offered when no
|
|
7
|
+
* size is given - there is nothing to derive one from. That is also why the callback is handed the
|
|
8
|
+
* resolved size: a drawing written against `size.width` works at any box.
|
|
9
|
+
*/
|
|
10
|
+
export class CanvasElement extends SizedPDFElement {
|
|
11
|
+
constructor(params) {
|
|
12
|
+
super({ x: 0, y: 0, width: params.width, height: params.height });
|
|
13
|
+
const { paint, alt, widthFactor, heightFactor, ...sizing } = params;
|
|
14
|
+
this.paintFn = paint;
|
|
15
|
+
this.alt = alt;
|
|
16
|
+
this.widthFactor = widthFactor;
|
|
17
|
+
this.heightFactor = heightFactor;
|
|
18
|
+
this.sizing = sizing;
|
|
19
|
+
this.requested = { width: params.width, height: params.height };
|
|
20
|
+
}
|
|
21
|
+
getProps() {
|
|
22
|
+
return {
|
|
23
|
+
x: this.x,
|
|
24
|
+
y: this.y,
|
|
25
|
+
width: this.width,
|
|
26
|
+
height: this.height,
|
|
27
|
+
paint: this.paintFn,
|
|
28
|
+
alt: this.alt,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
calculateLayout(constraints, offset, _ctx) {
|
|
32
|
+
this.x = offset.x;
|
|
33
|
+
this.y = offset.y;
|
|
34
|
+
const specs = extentSpecs({
|
|
35
|
+
...this.sizing,
|
|
36
|
+
width: this.requested.width,
|
|
37
|
+
height: this.requested.height,
|
|
38
|
+
widthFactor: this.widthFactor,
|
|
39
|
+
heightFactor: this.heightFactor,
|
|
40
|
+
});
|
|
41
|
+
const resolved = resolveSize(specs.width, specs.height, this.sizing.aspectRatio, constraints);
|
|
42
|
+
const bounds = resolved.constraints;
|
|
43
|
+
this.width = resolved.width ?? (bounds.hasBoundedWidth ? bounds.maxWidth : 0);
|
|
44
|
+
this.height = resolved.height ?? (bounds.hasBoundedHeight ? bounds.maxHeight : 0);
|
|
45
|
+
return { width: this.width, height: this.height };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -74,6 +74,8 @@ export declare class ContainerElement extends SizedPDFElement implements Fragmen
|
|
|
74
74
|
* every existing layout untouched.
|
|
75
75
|
*/
|
|
76
76
|
needsBoundedMain(horizontal: boolean): boolean;
|
|
77
|
+
/** A Column stacks vertically, so widths overlap and heights add up. */
|
|
78
|
+
minIntrinsicMain(horizontal: boolean, ctx: LayoutContext): number;
|
|
77
79
|
calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
|
|
78
80
|
getProps(): ContainerElementParams;
|
|
79
81
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FlexLayoutHelper, VERTICAL_AXIS } from "../utils/flex-layout.js";
|
|
2
|
+
import { stackMinIntrinsic, withDeclaredExtent } from "../layout/min-intrinsic.js";
|
|
2
3
|
import { extentSpecs, resolveSize, } from "../layout/box-constraints.js";
|
|
3
4
|
import { childrenForceBreak, packChildren, } from "../layout/fragmentation.js";
|
|
4
5
|
import { FlexiblePDFElement, SizedPDFElement, } from "./pdf-element.js";
|
|
@@ -93,6 +94,11 @@ export class ContainerElement extends SizedPDFElement {
|
|
|
93
94
|
const ownFlexChild = !horizontal && this.children.some((c) => c instanceof FlexiblePDFElement);
|
|
94
95
|
return ownFlexChild || this.children.some((c) => c.needsBoundedMain(horizontal));
|
|
95
96
|
}
|
|
97
|
+
/** A Column stacks vertically, so widths overlap and heights add up. */
|
|
98
|
+
minIntrinsicMain(horizontal, ctx) {
|
|
99
|
+
const content = stackMinIntrinsic(this.children, this.gap, !horizontal, horizontal, ctx, this.wrap);
|
|
100
|
+
return withDeclaredExtent(content, horizontal ? this.requested.width : this.requested.height, horizontal ? this.requested.minWidth : this.requested.minHeight);
|
|
101
|
+
}
|
|
96
102
|
calculateLayout(constraints, offset, ctx) {
|
|
97
103
|
this.x = offset.x;
|
|
98
104
|
this.y = offset.y;
|
package/dist/elements/index.d.ts
CHANGED
package/dist/elements/index.js
CHANGED
|
@@ -10,6 +10,8 @@ export declare class ExpandedElement extends FlexiblePDFElement implements Fragm
|
|
|
10
10
|
private width;
|
|
11
11
|
private height;
|
|
12
12
|
constructor({ flex, basis, basisFactor, child }: ExpandedElementParams);
|
|
13
|
+
/** Layout-transparent: the floor is whatever the wrapped child needs. */
|
|
14
|
+
minIntrinsicMain(horizontal: boolean, ctx: LayoutContext): number;
|
|
13
15
|
calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
|
|
14
16
|
/**
|
|
15
17
|
* When the column paginates, a flex region can't "fill" across pages, so it delegates to
|
|
@@ -11,6 +11,10 @@ export class ExpandedElement extends FlexiblePDFElement {
|
|
|
11
11
|
this.height = 0;
|
|
12
12
|
this.child = child;
|
|
13
13
|
}
|
|
14
|
+
/** Layout-transparent: the floor is whatever the wrapped child needs. */
|
|
15
|
+
minIntrinsicMain(horizontal, ctx) {
|
|
16
|
+
return this.child.minIntrinsicMain(horizontal, ctx);
|
|
17
|
+
}
|
|
14
18
|
calculateLayout(constraints, offset, ctx) {
|
|
15
19
|
if (constraints.hasBoundedWidth)
|
|
16
20
|
this.width = constraints.maxWidth;
|
|
@@ -26,6 +26,8 @@ export declare class KeepTogetherElement extends PDFElement implements Fragmenta
|
|
|
26
26
|
constructor({ child }: {
|
|
27
27
|
child: PDFElement;
|
|
28
28
|
});
|
|
29
|
+
/** Layout-transparent: the floor is whatever the wrapped child needs. */
|
|
30
|
+
minIntrinsicMain(horizontal: boolean, ctx: LayoutContext): number;
|
|
29
31
|
calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
|
|
30
32
|
relativeSizeFactor(horizontal: boolean): number | undefined;
|
|
31
33
|
needsBoundedMain(horizontal: boolean): boolean;
|
|
@@ -26,6 +26,10 @@ export class KeepTogetherElement extends PDFElement {
|
|
|
26
26
|
this.warnedBreakConflict = false;
|
|
27
27
|
this.child = child;
|
|
28
28
|
}
|
|
29
|
+
/** Layout-transparent: the floor is whatever the wrapped child needs. */
|
|
30
|
+
minIntrinsicMain(horizontal, ctx) {
|
|
31
|
+
return this.child.minIntrinsicMain(horizontal, ctx);
|
|
32
|
+
}
|
|
29
33
|
calculateLayout(constraints, offset, ctx) {
|
|
30
34
|
return this.child.calculateLayout(constraints, offset, ctx);
|
|
31
35
|
}
|
|
@@ -21,6 +21,11 @@ export declare class PaddingElement extends SizedPDFElement implements Fragmenta
|
|
|
21
21
|
breaksBefore(): boolean;
|
|
22
22
|
breaksAfter(): boolean;
|
|
23
23
|
private cloneWithChild;
|
|
24
|
+
/**
|
|
25
|
+
* The child plus our own insets. A PERCENTAGE inset counts as zero here: it resolves against a width
|
|
26
|
+
* that is not decided yet, which is exactly how CSS treats a percentage when sizing intrinsically.
|
|
27
|
+
*/
|
|
28
|
+
minIntrinsicMain(horizontal: boolean, ctx: LayoutContext): number;
|
|
24
29
|
calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
|
|
25
30
|
getProps(): {
|
|
26
31
|
x: number;
|
|
@@ -43,6 +43,14 @@ export class PaddingElement extends SizedPDFElement {
|
|
|
43
43
|
cloneWithChild(child) {
|
|
44
44
|
return new PaddingElement({ margin: this.margin, child });
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* The child plus our own insets. A PERCENTAGE inset counts as zero here: it resolves against a width
|
|
48
|
+
* that is not decided yet, which is exactly how CSS treats a percentage when sizing intrinsically.
|
|
49
|
+
*/
|
|
50
|
+
minIntrinsicMain(horizontal, ctx) {
|
|
51
|
+
const [top, right, bottom, left] = resolveEdges(this.margin, 0);
|
|
52
|
+
return (this.child.minIntrinsicMain(horizontal, ctx) + (horizontal ? left + right : top + bottom));
|
|
53
|
+
}
|
|
46
54
|
calculateLayout(constraints, offset, ctx) {
|
|
47
55
|
// Padding takes the width it is offered; its height shrink-wraps the child.
|
|
48
56
|
if (constraints.hasBoundedWidth)
|
|
@@ -16,6 +16,8 @@ export declare class StructGroup extends PDFElement implements Fragmentable {
|
|
|
16
16
|
role: string;
|
|
17
17
|
child: PDFElement;
|
|
18
18
|
});
|
|
19
|
+
/** Layout-transparent: the floor is whatever the wrapped child needs. */
|
|
20
|
+
minIntrinsicMain(horizontal: boolean, ctx: LayoutContext): number;
|
|
19
21
|
calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
|
|
20
22
|
/** Only splittable when the wrapped child is - so a StructGroup(Row) is moved whole to the next page,
|
|
21
23
|
* not clipped at the boundary (isFragmentable honours this veto). */
|
|
@@ -14,6 +14,10 @@ export class StructGroup extends PDFElement {
|
|
|
14
14
|
this.role = role;
|
|
15
15
|
this.child = child;
|
|
16
16
|
}
|
|
17
|
+
/** Layout-transparent: the floor is whatever the wrapped child needs. */
|
|
18
|
+
minIntrinsicMain(horizontal, ctx) {
|
|
19
|
+
return this.child.minIntrinsicMain(horizontal, ctx);
|
|
20
|
+
}
|
|
17
21
|
calculateLayout(constraints, offset, ctx) {
|
|
18
22
|
return this.child.calculateLayout(constraints, offset, ctx);
|
|
19
23
|
}
|
|
@@ -95,6 +95,17 @@ export declare abstract class PDFElement {
|
|
|
95
95
|
* the parent hands it the line's bounded main extent instead of `Infinity`. Default: no.
|
|
96
96
|
*/
|
|
97
97
|
needsBoundedMain(_horizontal: boolean): boolean;
|
|
98
|
+
/**
|
|
99
|
+
* The smallest main-axis extent this element can be squeezed into and still hold its content - CSS's
|
|
100
|
+
* `min-content` size, which is what an automatic `min-width` resolves to on a flex item. The shrink
|
|
101
|
+
* pass floors every child at this value, so a line that overflows badly stops squeezing rather than
|
|
102
|
+
* grinding a paragraph down to one word per line (which would only trade a wide box for a tall one).
|
|
103
|
+
*
|
|
104
|
+
* `horizontal` = the width. Default 0: an element with no opinion may be squeezed to nothing, which
|
|
105
|
+
* is what an empty box or a plain drawing can do without harm. Text and the layout containers
|
|
106
|
+
* override it; containers recurse, since a Row is only as unsqueezable as its children.
|
|
107
|
+
*/
|
|
108
|
+
minIntrinsicMain(_horizontal: boolean, _ctx: LayoutContext): number;
|
|
98
109
|
/**
|
|
99
110
|
* Whether this element IS a forced page break (a `PageBreak`). The pagination packer cuts the flow
|
|
100
111
|
* at such a marker: everything before it stays, everything after starts a new page. Default: no.
|
|
@@ -135,9 +146,14 @@ export declare abstract class PDFElement {
|
|
|
135
146
|
order: number;
|
|
136
147
|
/**
|
|
137
148
|
* 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.
|
|
139
|
-
*
|
|
140
|
-
*
|
|
149
|
+
* weighted by its own size as CSS does. Default 1, as in CSS: an overflowing line squeezes its
|
|
150
|
+
* children back inside instead of letting them run past the edge of the box. `0` opts out and keeps
|
|
151
|
+
* a child at its natural size.
|
|
152
|
+
*
|
|
153
|
+
* This was 0 until the alpha, so that adding flexShrink could not move an existing layout. That
|
|
154
|
+
* caution outlived its use: four of the gallery cases turned out to be overflowing their page
|
|
155
|
+
* margin without anyone noticing, because nothing ever pulled them back. Shrinking is bounded from
|
|
156
|
+
* below by `minIntrinsicMain`, so a child is never squeezed past what it can actually hold.
|
|
141
157
|
*/
|
|
142
158
|
flexShrink: number;
|
|
143
159
|
/** Sets `flexShrink` and returns the element. */
|
|
@@ -13,11 +13,16 @@ export class PDFElement {
|
|
|
13
13
|
this.order = 0;
|
|
14
14
|
/**
|
|
15
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.
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* weighted by its own size as CSS does. Default 1, as in CSS: an overflowing line squeezes its
|
|
17
|
+
* children back inside instead of letting them run past the edge of the box. `0` opts out and keeps
|
|
18
|
+
* a child at its natural size.
|
|
19
|
+
*
|
|
20
|
+
* This was 0 until the alpha, so that adding flexShrink could not move an existing layout. That
|
|
21
|
+
* caution outlived its use: four of the gallery cases turned out to be overflowing their page
|
|
22
|
+
* margin without anyone noticing, because nothing ever pulled them back. Shrinking is bounded from
|
|
23
|
+
* below by `minIntrinsicMain`, so a child is never squeezed past what it can actually hold.
|
|
19
24
|
*/
|
|
20
|
-
this.flexShrink =
|
|
25
|
+
this.flexShrink = 1;
|
|
21
26
|
}
|
|
22
27
|
get structId() {
|
|
23
28
|
return this._structId;
|
|
@@ -46,6 +51,19 @@ export class PDFElement {
|
|
|
46
51
|
needsBoundedMain(_horizontal) {
|
|
47
52
|
return false;
|
|
48
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* The smallest main-axis extent this element can be squeezed into and still hold its content - CSS's
|
|
56
|
+
* `min-content` size, which is what an automatic `min-width` resolves to on a flex item. The shrink
|
|
57
|
+
* pass floors every child at this value, so a line that overflows badly stops squeezing rather than
|
|
58
|
+
* grinding a paragraph down to one word per line (which would only trade a wide box for a tall one).
|
|
59
|
+
*
|
|
60
|
+
* `horizontal` = the width. Default 0: an element with no opinion may be squeezed to nothing, which
|
|
61
|
+
* is what an empty box or a plain drawing can do without harm. Text and the layout containers
|
|
62
|
+
* override it; containers recurse, since a Row is only as unsqueezable as its children.
|
|
63
|
+
*/
|
|
64
|
+
minIntrinsicMain(_horizontal, _ctx) {
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
49
67
|
/**
|
|
50
68
|
* Whether this element IS a forced page break (a `PageBreak`). The pagination packer cuts the flow
|
|
51
69
|
* at such a marker: everything before it stays, everything after starts a new page. Default: no.
|
|
@@ -78,6 +78,8 @@ export declare class RectangleElement extends SizedPDFElement implements Fragmen
|
|
|
78
78
|
* `Spacer` in a box can only resolve if the box itself was given a bounded extent to pass down. With an
|
|
79
79
|
* explicit size on an axis the box already knows its extent there and asks for nothing. */
|
|
80
80
|
needsBoundedMain(horizontal: boolean): boolean;
|
|
81
|
+
/** A Box stacks its children vertically inside its border, which is on both sides of the axis. */
|
|
82
|
+
minIntrinsicMain(horizontal: boolean, ctx: LayoutContext): number;
|
|
81
83
|
calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
|
|
82
84
|
getProps(): {
|
|
83
85
|
x: number;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Color } from "../common/color.js";
|
|
2
2
|
import { BoxConstraints, extentSpecs, resolveSize, } from "../layout/box-constraints.js";
|
|
3
|
+
import { stackMinIntrinsic, withDeclaredExtent } from "../layout/min-intrinsic.js";
|
|
3
4
|
import { childrenForceBreak, packChildren, } from "../layout/fragmentation.js";
|
|
4
5
|
import { SizedPDFElement, } from "./pdf-element.js";
|
|
5
6
|
export class RectangleElement extends SizedPDFElement {
|
|
@@ -99,6 +100,11 @@ export class RectangleElement extends SizedPDFElement {
|
|
|
99
100
|
return false;
|
|
100
101
|
return this.children.some((c) => c.needsBoundedMain(horizontal));
|
|
101
102
|
}
|
|
103
|
+
/** A Box stacks its children vertically inside its border, which is on both sides of the axis. */
|
|
104
|
+
minIntrinsicMain(horizontal, ctx) {
|
|
105
|
+
const content = stackMinIntrinsic(this.children, 0, !horizontal, horizontal, ctx) + 2 * this.borderWidth;
|
|
106
|
+
return withDeclaredExtent(content, horizontal ? this.sizeMemory.width : this.sizeMemory.height, horizontal ? this.sizeMemory.minWidth : this.sizeMemory.minHeight);
|
|
107
|
+
}
|
|
102
108
|
calculateLayout(constraints, offset, ctx) {
|
|
103
109
|
// An explicit extent is a fixed point size or a fraction of the offered box (relative sizing);
|
|
104
110
|
// the fraction only resolves in a bounded region. `undefined` = fill / shrink-wrap below.
|
|
@@ -65,6 +65,8 @@ export declare class RowElement extends SizedPDFElement {
|
|
|
65
65
|
/** The mirror of `ContainerElement`: a Row's OWN flex child needs a bounded width, and the need of any
|
|
66
66
|
* descendant propagates on both axes (see the Column for the reasoning). */
|
|
67
67
|
needsBoundedMain(horizontal: boolean): boolean;
|
|
68
|
+
/** A Row lays out side by side, so widths add up (gaps included) and heights overlap. */
|
|
69
|
+
minIntrinsicMain(horizontal: boolean, ctx: LayoutContext): number;
|
|
68
70
|
calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
|
|
69
71
|
getProps(): {
|
|
70
72
|
x: number;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { extentSpecs, resolveSize, } from "../layout/box-constraints.js";
|
|
2
2
|
import { FlexLayoutHelper, HORIZONTAL_AXIS } from "../utils/flex-layout.js";
|
|
3
|
+
import { stackMinIntrinsic, withDeclaredExtent } from "../layout/min-intrinsic.js";
|
|
3
4
|
import { FlexiblePDFElement, SizedPDFElement, } from "./pdf-element.js";
|
|
4
5
|
/**
|
|
5
6
|
* Horizontal stack: the mirror of `ContainerElement` (Column). Children are laid out
|
|
@@ -47,6 +48,11 @@ export class RowElement extends SizedPDFElement {
|
|
|
47
48
|
const ownFlexChild = horizontal && this.children.some((c) => c instanceof FlexiblePDFElement);
|
|
48
49
|
return ownFlexChild || this.children.some((c) => c.needsBoundedMain(horizontal));
|
|
49
50
|
}
|
|
51
|
+
/** A Row lays out side by side, so widths add up (gaps included) and heights overlap. */
|
|
52
|
+
minIntrinsicMain(horizontal, ctx) {
|
|
53
|
+
const content = stackMinIntrinsic(this.children, this.gap, horizontal, horizontal, ctx, this.wrap);
|
|
54
|
+
return withDeclaredExtent(content, horizontal ? this.requested.width : this.requested.height, horizontal ? this.requested.minWidth : this.requested.minHeight);
|
|
55
|
+
}
|
|
50
56
|
calculateLayout(constraints, offset, ctx) {
|
|
51
57
|
this.x = offset.x;
|
|
52
58
|
this.y = offset.y;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type SvgSize } from "../svg/index.js";
|
|
2
|
+
import { BoxConstraints, Offset, Size, SizingParams } from "../layout/box-constraints.js";
|
|
3
|
+
import { LayoutContext, SizedPDFElement } from "./pdf-element.js";
|
|
4
|
+
/** Markup, a file path (Node only), or the file's bytes. */
|
|
5
|
+
export type SvgSource = string | Uint8Array;
|
|
6
|
+
interface SvgElementParams extends SizingParams {
|
|
7
|
+
source: SvgSource;
|
|
8
|
+
/** Width/height as a fraction (0..1) of the offered box, for `"50%"`. */
|
|
9
|
+
widthFactor?: number;
|
|
10
|
+
heightFactor?: number;
|
|
11
|
+
/** Alternate text for the tagged structure tree; makes the drawing a Figure rather than decoration. */
|
|
12
|
+
alt?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* An SVG drawing placed in the layout like an image.
|
|
16
|
+
*
|
|
17
|
+
* Unlike a bitmap it needs no decoder, so its intrinsic size is known SYNCHRONOUSLY, in the
|
|
18
|
+
* constructor - which also means a broken file fails on the line that asked for it rather than deep
|
|
19
|
+
* inside a later render, the same bargain `addFont` makes.
|
|
20
|
+
*/
|
|
21
|
+
export declare class SvgElement extends SizedPDFElement {
|
|
22
|
+
private source;
|
|
23
|
+
private intrinsic;
|
|
24
|
+
private alt?;
|
|
25
|
+
private requested;
|
|
26
|
+
private sizing;
|
|
27
|
+
private widthFactor?;
|
|
28
|
+
private heightFactor?;
|
|
29
|
+
constructor(params: SvgElementParams);
|
|
30
|
+
getProps(): {
|
|
31
|
+
x: number;
|
|
32
|
+
y: number;
|
|
33
|
+
width: number | undefined;
|
|
34
|
+
height: number | undefined;
|
|
35
|
+
source: string;
|
|
36
|
+
intrinsic: SvgSize;
|
|
37
|
+
alt: string | undefined;
|
|
38
|
+
};
|
|
39
|
+
calculateLayout(constraints: BoxConstraints, offset: Offset, _ctx: LayoutContext): Size;
|
|
40
|
+
}
|
|
41
|
+
export {};
|