@jasy/pdf 1.0.0-alpha.2 → 1.0.0-alpha.4
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 +24 -14
- 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 +8 -8
- package/dist/api/content.js +23 -24
- package/dist/api/descriptor.d.ts +2 -2
- package/dist/api/descriptor.js +75 -31
- package/dist/api/index.d.ts +8 -8
- package/dist/api/index.js +8 -24
- package/dist/api/insets.js +4 -8
- package/dist/api/layout.d.ts +18 -16
- package/dist/api/layout.js +41 -52
- package/dist/api/structure.d.ts +65 -13
- package/dist/api/structure.js +140 -88
- package/dist/api/table.d.ts +5 -5
- package/dist/api/table.js +28 -24
- package/dist/api/text.d.ts +27 -2
- package/dist/api/text.js +45 -27
- 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/security-handler.d.ts +46 -0
- package/dist/crypto/security-handler.js +129 -0
- package/dist/crypto/webcrypto.d.ts +11 -0
- package/dist/crypto/webcrypto.js +62 -0
- package/dist/elements/container-element.d.ts +4 -4
- package/dist/elements/container-element.js +9 -13
- package/dist/elements/image-element.d.ts +18 -2
- package/dist/elements/image-element.js +81 -105
- package/dist/elements/index.d.ts +12 -11
- package/dist/elements/index.js +12 -29
- 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 +3 -3
- package/dist/elements/layout/expanded-element.js +10 -14
- package/dist/elements/layout/padding-element.d.ts +3 -3
- package/dist/elements/layout/padding-element.js +9 -14
- package/dist/elements/layout/positioned-element.d.ts +17 -4
- package/dist/elements/layout/positioned-element.js +29 -25
- 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/sized-container-element.d.ts +2 -2
- package/dist/elements/layout/sized-container-element.js +6 -11
- package/dist/elements/line-element.d.ts +3 -3
- package/dist/elements/line-element.js +5 -10
- package/dist/elements/page-element.d.ts +8 -6
- package/dist/elements/page-element.js +20 -23
- 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 +12 -3
- package/dist/elements/pdf-element.js +10 -19
- package/dist/elements/rectangle-element.d.ts +5 -5
- package/dist/elements/rectangle-element.js +19 -25
- package/dist/elements/row-element.d.ts +3 -3
- package/dist/elements/row-element.js +7 -11
- package/dist/elements/text-element.d.ts +37 -11
- package/dist/elements/text-element.js +64 -39
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -19
- package/dist/ir/display-list.d.ts +4 -2
- package/dist/ir/display-list.js +1 -2
- package/dist/layout/box-constraints.js +2 -6
- package/dist/layout/fragmentation.d.ts +8 -1
- package/dist/layout/fragmentation.js +22 -10
- 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/container-renderer.d.ts +3 -3
- package/dist/renderer/container-renderer.js +12 -27
- 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/expanded-renderer.d.ts +3 -3
- package/dist/renderer/expanded-renderer.js +6 -21
- package/dist/renderer/image-renderer.d.ts +3 -3
- package/dist/renderer/image-renderer.js +77 -75
- package/dist/renderer/index.d.ts +10 -10
- package/dist/renderer/index.js +10 -26
- package/dist/renderer/line-renderer.d.ts +3 -3
- package/dist/renderer/line-renderer.js +13 -28
- package/dist/renderer/padding-renderer.d.ts +3 -3
- package/dist/renderer/padding-renderer.js +6 -21
- package/dist/renderer/page-renderer.d.ts +2 -2
- package/dist/renderer/page-renderer.js +61 -77
- package/dist/renderer/pdf-backend.d.ts +2 -2
- package/dist/renderer/pdf-backend.js +21 -19
- 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 +24 -41
- package/dist/renderer/pdf-document-renderer.d.ts +3 -3
- package/dist/renderer/pdf-document-renderer.js +71 -85
- package/dist/renderer/pdf-renderer.d.ts +2 -2
- package/dist/renderer/pdf-renderer.js +85 -93
- package/dist/renderer/positioned-renderer.d.ts +3 -3
- package/dist/renderer/positioned-renderer.js +8 -23
- package/dist/renderer/rectangle-renderer.d.ts +3 -3
- package/dist/renderer/rectangle-renderer.js +45 -52
- package/dist/renderer/repeating-header-renderer.d.ts +3 -3
- package/dist/renderer/repeating-header-renderer.js +11 -26
- package/dist/renderer/row-renderer.d.ts +3 -3
- package/dist/renderer/row-renderer.js +12 -27
- package/dist/renderer/text-renderer.d.ts +6 -5
- package/dist/renderer/text-renderer.js +33 -42
- package/dist/text/line-breaker.d.ts +8 -5
- package/dist/text/line-breaker.js +67 -16
- package/dist/text/text-style.d.ts +25 -0
- package/dist/text/text-style.js +29 -0
- package/dist/utils/afm-parser.js +3 -13
- package/dist/utils/bytes.d.ts +24 -0
- package/dist/utils/bytes.js +76 -0
- package/dist/utils/flex-layout.d.ts +2 -2
- package/dist/utils/flex-layout.js +15 -20
- package/dist/utils/font-metrics.d.ts +1 -1
- package/dist/utils/font-metrics.js +1 -2
- package/dist/utils/font-path.js +3 -6
- package/dist/utils/image-helper.d.ts +6 -5
- package/dist/utils/image-helper.js +101 -111
- package/dist/utils/md5.d.ts +4 -0
- package/dist/utils/md5.js +79 -0
- package/dist/utils/pdf-object-manager.d.ts +18 -6
- package/dist/utils/pdf-object-manager.js +0 -0
- package/dist/utils/renderer-registry.js +1 -5
- package/dist/utils/ttf-parser.d.ts +2 -2
- package/dist/utils/ttf-parser.js +32 -36
- package/dist/utils/ttf-subsetter.d.ts +1 -1
- package/dist/utils/ttf-subsetter.js +40 -42
- package/dist/utils/utf8-to-windows1252-encoder.js +1 -4
- package/dist/validators/element-validator.d.ts +2 -2
- package/dist/validators/element-validator.js +9 -13
- package/package.json +14 -2
|
@@ -1,24 +1,36 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TextElement = void 0;
|
|
4
|
-
const color_1 = require("../common/color");
|
|
5
1
|
// Import the renderer DIRECTLY, not via the "../renderer" barrel: the barrel pulls in
|
|
6
2
|
// pdf-renderer (and every element) while this element module is still loading, which under
|
|
7
3
|
// ESM (Vite/vitest, and the future framework bindings) duplicates the element classes and
|
|
8
4
|
// breaks the constructor-keyed RendererRegistry. A direct import keeps the graph acyclic.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class TextElement extends
|
|
14
|
-
constructor({ fontSize, content, fontFamily
|
|
5
|
+
import { TextRenderer } from "../renderer/text-renderer.js";
|
|
6
|
+
import { DEFAULT_TEXT_STYLE } from "../text/text-style.js";
|
|
7
|
+
import { wrapStringIntoLines, breakSegmentsIntoLines, segmentLinesToSegments, } from "../text/line-breaker.js";
|
|
8
|
+
import { SizedPDFElement } from "./pdf-element.js";
|
|
9
|
+
export class TextElement extends SizedPDFElement {
|
|
10
|
+
constructor({ fontSize, content, fontFamily, fontStyle, color, textAlignment, maxLines, overflow = "clip", lineHeight, }) {
|
|
15
11
|
super({ x: 0, y: 0 });
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
12
|
+
this.rawFontSize = fontSize;
|
|
13
|
+
this.rawFontFamily = fontFamily;
|
|
14
|
+
this.rawFontStyle = fontStyle;
|
|
15
|
+
this.rawColor = color;
|
|
16
|
+
this.rawTextAlignment = textAlignment;
|
|
17
|
+
this.rawLineHeight = lineHeight;
|
|
20
18
|
this.content = content;
|
|
21
|
-
this.
|
|
19
|
+
this.maxLines = maxLines;
|
|
20
|
+
this.overflow = overflow;
|
|
21
|
+
this.applyStyle(DEFAULT_TEXT_STYLE);
|
|
22
|
+
}
|
|
23
|
+
// Resolve the author-set values against the cascade: explicit > inherited (ctx) > built-in default.
|
|
24
|
+
resolveStyle(ctx) {
|
|
25
|
+
this.applyStyle(ctx.textStyle ?? DEFAULT_TEXT_STYLE);
|
|
26
|
+
}
|
|
27
|
+
applyStyle(ts) {
|
|
28
|
+
this.fontSize = this.rawFontSize ?? ts.fontSize;
|
|
29
|
+
this.fontFamily = this.rawFontFamily ?? ts.fontFamily;
|
|
30
|
+
this.fontStyle = this.rawFontStyle ?? ts.fontStyle;
|
|
31
|
+
this.color = this.rawColor ?? ts.color;
|
|
32
|
+
this.textAlignment = this.rawTextAlignment ?? ts.textAlignment;
|
|
33
|
+
this.lineHeight = this.rawLineHeight ?? ts.lineHeight;
|
|
22
34
|
}
|
|
23
35
|
/**
|
|
24
36
|
* Splits the paragraph at line boxes (Slice 1). The lines that fit in `maxHeight` stay;
|
|
@@ -27,6 +39,7 @@ class TextElement extends pdf_element_1.SizedPDFElement {
|
|
|
27
39
|
* the whole element on for progress. Handles both plain strings and styled segments.
|
|
28
40
|
*/
|
|
29
41
|
fragment(maxHeight, width, ctx) {
|
|
42
|
+
this.resolveStyle(ctx);
|
|
30
43
|
return typeof this.content === "string"
|
|
31
44
|
? this.fragmentString(this.content, maxHeight, width, ctx)
|
|
32
45
|
: this.fragmentSegments(this.content, maxHeight, width, ctx);
|
|
@@ -34,8 +47,8 @@ class TextElement extends pdf_element_1.SizedPDFElement {
|
|
|
34
47
|
// Plain string: every wrapped line is `fontSize` tall (matches calculateTextHeight),
|
|
35
48
|
// so `floor(maxHeight / fontSize)` lines fit.
|
|
36
49
|
fragmentString(content, maxHeight, width, ctx) {
|
|
37
|
-
const lines =
|
|
38
|
-
const fittedLineCount = Math.floor(maxHeight / this.fontSize);
|
|
50
|
+
const lines = wrapStringIntoLines(content, this.fontFamily, this.fontSize, this.fontStyle, width, ctx.metrics, this.maxLines, this.overflow);
|
|
51
|
+
const fittedLineCount = Math.floor(maxHeight / (this.fontSize * this.lineHeight));
|
|
39
52
|
if (fittedLineCount <= 0)
|
|
40
53
|
return { fitted: null, remainder: this };
|
|
41
54
|
if (fittedLineCount >= lines.length)
|
|
@@ -48,17 +61,18 @@ class TextElement extends pdf_element_1.SizedPDFElement {
|
|
|
48
61
|
// Styled segments: each line's height is its tallest font (maxFontSize), so pack lines
|
|
49
62
|
// by cumulative leading. Rebuild the fitted/remainder halves back into TextSegment[].
|
|
50
63
|
fragmentSegments(content, maxHeight, width, ctx) {
|
|
51
|
-
const lines =
|
|
64
|
+
const lines = breakSegmentsIntoLines(content, {
|
|
52
65
|
fontFamily: this.fontFamily,
|
|
53
66
|
fontSize: this.fontSize,
|
|
54
67
|
fontStyle: this.fontStyle,
|
|
55
|
-
}, width, ctx.metrics);
|
|
68
|
+
}, width, ctx.metrics, this.maxLines, this.overflow);
|
|
56
69
|
let used = 0;
|
|
57
70
|
let fittedLineCount = 0;
|
|
58
71
|
for (const line of lines) {
|
|
59
|
-
|
|
72
|
+
const lineBox = line.maxFontSize * this.lineHeight;
|
|
73
|
+
if (used + lineBox > maxHeight)
|
|
60
74
|
break;
|
|
61
|
-
used +=
|
|
75
|
+
used += lineBox;
|
|
62
76
|
fittedLineCount++;
|
|
63
77
|
}
|
|
64
78
|
if (fittedLineCount <= 0)
|
|
@@ -66,8 +80,8 @@ class TextElement extends pdf_element_1.SizedPDFElement {
|
|
|
66
80
|
if (fittedLineCount >= lines.length)
|
|
67
81
|
return { fitted: this, remainder: null };
|
|
68
82
|
return {
|
|
69
|
-
fitted: this.cloneWithContent(
|
|
70
|
-
remainder: this.cloneWithContent(
|
|
83
|
+
fitted: this.cloneWithContent(segmentLinesToSegments(lines.slice(0, fittedLineCount))),
|
|
84
|
+
remainder: this.cloneWithContent(segmentLinesToSegments(lines.slice(fittedLineCount))),
|
|
71
85
|
};
|
|
72
86
|
}
|
|
73
87
|
// A copy carrying the same style but different (already-wrapped) content. Re-wrapping at
|
|
@@ -80,10 +94,13 @@ class TextElement extends pdf_element_1.SizedPDFElement {
|
|
|
80
94
|
fontStyle: this.fontStyle,
|
|
81
95
|
color: this.color,
|
|
82
96
|
textAlignment: this.textAlignment,
|
|
97
|
+
maxLines: this.maxLines,
|
|
98
|
+
overflow: this.overflow,
|
|
99
|
+
lineHeight: this.lineHeight,
|
|
83
100
|
});
|
|
84
101
|
}
|
|
85
102
|
calculateLayout(constraints, offset, ctx) {
|
|
86
|
-
|
|
103
|
+
this.resolveStyle(ctx);
|
|
87
104
|
this.x = offset.x;
|
|
88
105
|
this.y = offset.y;
|
|
89
106
|
// Bounded width (e.g. inside a Column) wraps to that width; an unbounded width
|
|
@@ -92,32 +109,38 @@ class TextElement extends pdf_element_1.SizedPDFElement {
|
|
|
92
109
|
this.width = constraints.hasBoundedWidth
|
|
93
110
|
? constraints.maxWidth
|
|
94
111
|
: this.naturalWidth(ctx.metrics);
|
|
95
|
-
const wrapWidth =
|
|
96
|
-
this.height =
|
|
112
|
+
const wrapWidth = this.width ?? 0;
|
|
113
|
+
this.height = TextRenderer.calculateTextHeight(this.content, this.fontSize, this.fontFamily, this.fontStyle, ctx.metrics, wrapWidth, this.maxLines, this.overflow, this.lineHeight);
|
|
97
114
|
// Top-left coordinates (y = top of the text box). The baseline offset and the
|
|
98
115
|
// Y-flip are applied downstream (the line-builder positions baselines, the seam
|
|
99
116
|
// flips to PDF), so the element stays coordinate-system-blind.
|
|
100
117
|
return { width: wrapWidth, height: this.height };
|
|
101
118
|
}
|
|
102
119
|
/** The unwrapped single-line width of the content (used when width is unbounded, e.g. inside a Row).
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
120
|
+
* Must match the LINE-BREAKER's one-line measure EXACTLY - not just algebraically but BIT-for-bit,
|
|
121
|
+
* so a text laid out at this width never re-wraps inside its own natural-width box. The breaker
|
|
122
|
+
* accumulates `currentWidth += wordWidth + spaceWidth`, grouping the word and its trailing space
|
|
123
|
+
* into one term; we must group the same way. Adding word and space as two separate steps is
|
|
124
|
+
* algebraically equal but, because floating-point addition is not associative, drifts by a sub-ULP
|
|
125
|
+
* - enough to tip a borderline string (e.g. "20 Jun 2026", wider than "04 Jul 2026" only because
|
|
126
|
+
* 'n' beats 'l') one bit over its own width, dropping the last word onto a second line. */
|
|
106
127
|
naturalWidth(metrics) {
|
|
107
|
-
const
|
|
128
|
+
const oneLine = (text, family, size, style) => {
|
|
129
|
+
const words = text.split(" ");
|
|
130
|
+
const space = metrics.getCharWidth(" ", size, undefined, family, style);
|
|
108
131
|
let width = 0;
|
|
109
|
-
|
|
110
|
-
|
|
132
|
+
words.forEach((word, i) => {
|
|
133
|
+
const w = metrics.getStringWidth(word, family, size, style);
|
|
134
|
+
// Group (word + space) as one term, exactly like the breaker - see the note above.
|
|
135
|
+
width += i < words.length - 1 ? w + space : w;
|
|
136
|
+
});
|
|
111
137
|
return width;
|
|
112
138
|
};
|
|
113
139
|
if (typeof this.content === "string") {
|
|
114
|
-
return
|
|
140
|
+
return oneLine(this.content, this.fontFamily, this.fontSize, this.fontStyle);
|
|
115
141
|
}
|
|
116
|
-
return this.content.reduce((sum, seg) =>
|
|
117
|
-
|
|
118
|
-
return sum +
|
|
119
|
-
advance(seg.content, (_a = seg.fontFamily) !== null && _a !== void 0 ? _a : this.fontFamily, (_b = seg.fontSize) !== null && _b !== void 0 ? _b : this.fontSize, (_c = seg.fontStyle) !== null && _c !== void 0 ? _c : this.fontStyle);
|
|
120
|
-
}, 0);
|
|
142
|
+
return this.content.reduce((sum, seg) => sum +
|
|
143
|
+
oneLine(seg.content, seg.fontFamily ?? this.fontFamily, seg.fontSize ?? this.fontSize, seg.fontStyle ?? this.fontStyle), 0);
|
|
121
144
|
}
|
|
122
145
|
getProps() {
|
|
123
146
|
return {
|
|
@@ -131,7 +154,9 @@ class TextElement extends pdf_element_1.SizedPDFElement {
|
|
|
131
154
|
color: this.color,
|
|
132
155
|
content: this.content,
|
|
133
156
|
textAlignment: this.textAlignment,
|
|
157
|
+
maxLines: this.maxLines,
|
|
158
|
+
overflow: this.overflow,
|
|
159
|
+
lineHeight: this.lineHeight,
|
|
134
160
|
};
|
|
135
161
|
}
|
|
136
162
|
}
|
|
137
|
-
exports.TextElement = TextElement;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from "./elements";
|
|
2
|
-
export * from "./renderer";
|
|
3
|
-
export * from "./api";
|
|
1
|
+
export * from "./elements/index.js";
|
|
2
|
+
export * from "./renderer/index.js";
|
|
3
|
+
export * from "./api/index.js";
|
package/dist/index.js
CHANGED
|
@@ -1,21 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./elements"), exports);
|
|
18
|
-
__exportStar(require("./renderer"), exports);
|
|
19
|
-
__exportStar(require("./api"), exports);
|
|
1
|
+
export * from "./elements/index.js";
|
|
2
|
+
export * from "./renderer/index.js";
|
|
3
|
+
export * from "./api/index.js";
|
|
20
4
|
// export * from './validator';
|
|
21
5
|
// export * from './utils';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Color } from "../common/color";
|
|
2
|
-
import { FontStyle } from "../utils/pdf-object-manager";
|
|
1
|
+
import { Color } from "../common/color.js";
|
|
2
|
+
import { FontStyle } from "../utils/pdf-object-manager.js";
|
|
3
3
|
/**
|
|
4
4
|
* Display list - the seam between layout and the PDF backend.
|
|
5
5
|
*
|
|
@@ -60,6 +60,8 @@ export interface Image {
|
|
|
60
60
|
intrinsicHeight: number;
|
|
61
61
|
data: string;
|
|
62
62
|
imageType: string;
|
|
63
|
+
/** Flate-compressed DeviceGray alpha channel for a transparent PNG, embedded as the XObject's /SMask. */
|
|
64
|
+
smask?: string;
|
|
63
65
|
/** cover/contain fits clip the placement to the element's original frame. */
|
|
64
66
|
clip?: {
|
|
65
67
|
x: number;
|
package/dist/ir/display-list.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.BoxConstraints = void 0;
|
|
4
1
|
/**
|
|
5
2
|
* The size constraints handed DOWN the tree during layout (Flutter's BoxConstraints
|
|
6
3
|
* model). Pure geometry: a min/max range per axis, no position. An element receives a
|
|
@@ -15,7 +12,7 @@ exports.BoxConstraints = void 0;
|
|
|
15
12
|
* remaining vertical space becomes a `maxHeight`, and "does this fit?" is a constraint
|
|
16
13
|
* check, not a special case.
|
|
17
14
|
*/
|
|
18
|
-
class BoxConstraints {
|
|
15
|
+
export class BoxConstraints {
|
|
19
16
|
constructor(minWidth = 0, maxWidth = Infinity, minHeight = 0, maxHeight = Infinity) {
|
|
20
17
|
this.minWidth = minWidth;
|
|
21
18
|
this.maxWidth = maxWidth;
|
|
@@ -28,7 +25,7 @@ class BoxConstraints {
|
|
|
28
25
|
}
|
|
29
26
|
/** Tight only on the axes given; the others stay unbounded (0..Infinity). */
|
|
30
27
|
static tightFor({ width, height }) {
|
|
31
|
-
return new BoxConstraints(width
|
|
28
|
+
return new BoxConstraints(width ?? 0, width ?? Infinity, height ?? 0, height ?? Infinity);
|
|
32
29
|
}
|
|
33
30
|
/** Caps each axis at `max` but allows anything down to zero (shrink-wrap). */
|
|
34
31
|
static loose(maxWidth, maxHeight) {
|
|
@@ -70,4 +67,3 @@ class BoxConstraints {
|
|
|
70
67
|
return new BoxConstraints(Math.max(parent.minWidth, Math.min(this.minWidth, parent.maxWidth)), Math.max(parent.minWidth, Math.min(this.maxWidth, parent.maxWidth)), Math.max(parent.minHeight, Math.min(this.minHeight, parent.maxHeight)), Math.max(parent.minHeight, Math.min(this.maxHeight, parent.maxHeight)));
|
|
71
68
|
}
|
|
72
69
|
}
|
|
73
|
-
exports.BoxConstraints = BoxConstraints;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LayoutContext, PDFElement } from "../elements/pdf-element";
|
|
1
|
+
import { LayoutContext, PDFElement } from "../elements/pdf-element.js";
|
|
2
2
|
/**
|
|
3
3
|
* The result of splitting an element against a fragmentation region (a page, later a
|
|
4
4
|
* column). `fitted` is the part that fits in the region; `remainder` describes what did
|
|
@@ -22,6 +22,13 @@ export interface Fragmentable {
|
|
|
22
22
|
fragment(maxHeight: number, width: number, ctx: LayoutContext): FragmentResult;
|
|
23
23
|
}
|
|
24
24
|
export declare function isFragmentable(element: PDFElement): element is PDFElement & Fragmentable;
|
|
25
|
+
/**
|
|
26
|
+
* What to do when an element is taller than the whole page region and cannot be broken. We always
|
|
27
|
+
* still place it (clipped) so pagination terminates - this only controls how loud we are about it:
|
|
28
|
+
* `"error"` throws (the default; an unbreakable overflow is almost always a layout bug), `"warn"`
|
|
29
|
+
* logs and clips, `"ignore"` clips silently.
|
|
30
|
+
*/
|
|
31
|
+
export type OverflowPolicy = "error" | "warn" | "ignore";
|
|
25
32
|
/**
|
|
26
33
|
* Packs a vertical stack of children into `maxHeight`, in order. Children are measured
|
|
27
34
|
* against `width` (unbounded height) and added until one would overflow; that straddling
|
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.isFragmentable = isFragmentable;
|
|
4
|
-
exports.packChildren = packChildren;
|
|
5
|
-
const box_constraints_1 = require("./box-constraints");
|
|
6
|
-
function isFragmentable(element) {
|
|
1
|
+
import { BoxConstraints } from "./box-constraints.js";
|
|
2
|
+
export function isFragmentable(element) {
|
|
7
3
|
return ("fragment" in element &&
|
|
8
4
|
typeof element.fragment === "function");
|
|
9
5
|
}
|
|
6
|
+
function reportOverflow(child, childHeight, maxHeight, policy) {
|
|
7
|
+
if (policy === "ignore")
|
|
8
|
+
return;
|
|
9
|
+
const name = child.constructor.name.replace(/Element$/, "");
|
|
10
|
+
const detail = `${name} is ${Math.round(childHeight)}pt tall but the page region is only ` +
|
|
11
|
+
`${Math.round(maxHeight)}pt and it cannot be broken - reduce its size, give it a bounded ` +
|
|
12
|
+
`height, or let it split across pages.`;
|
|
13
|
+
if (policy === "error")
|
|
14
|
+
throw new Error(`Layout overflow: ${detail}`);
|
|
15
|
+
console.warn(`Layout overflow (clipped): ${detail}`);
|
|
16
|
+
}
|
|
10
17
|
/**
|
|
11
18
|
* Packs a vertical stack of children into `maxHeight`, in order. Children are measured
|
|
12
19
|
* against `width` (unbounded height) and added until one would overflow; that straddling
|
|
@@ -21,13 +28,13 @@ function isFragmentable(element) {
|
|
|
21
28
|
* Shared by every element that lays out a vertical stack and can split it across regions
|
|
22
29
|
* (Container, and the decorated boxes Padding/Rectangle).
|
|
23
30
|
*/
|
|
24
|
-
function packChildren(children, maxHeight, width, ctx, gap = 0) {
|
|
31
|
+
export function packChildren(children, maxHeight, width, ctx, gap = 0) {
|
|
25
32
|
const fitted = [];
|
|
26
33
|
const remainder = [];
|
|
27
34
|
let usedHeight = 0;
|
|
28
35
|
for (let i = 0; i < children.length; i++) {
|
|
29
36
|
const child = children[i];
|
|
30
|
-
const childHeight = child.calculateLayout(
|
|
37
|
+
const childHeight = child.calculateLayout(BoxConstraints.loose(width, Infinity), { x: 0, y: 0 }, ctx).height;
|
|
31
38
|
// A gap precedes every child except the first one placed in this region.
|
|
32
39
|
const lead = fitted.length > 0 ? gap : 0;
|
|
33
40
|
if (usedHeight + lead + childHeight <= maxHeight) {
|
|
@@ -48,10 +55,15 @@ function packChildren(children, maxHeight, width, ctx, gap = 0) {
|
|
|
48
55
|
}
|
|
49
56
|
}
|
|
50
57
|
if (!placedPart) {
|
|
51
|
-
if (fitted.length === 0)
|
|
58
|
+
if (fitted.length === 0) {
|
|
59
|
+
// Taller than the whole region and unsplittable: force it on (it overflows and is clipped)
|
|
60
|
+
// so the next region still advances - and surface it per the overflow policy.
|
|
61
|
+
reportOverflow(child, childHeight, maxHeight, ctx.onOverflow ?? "ignore");
|
|
52
62
|
fitted.push(child);
|
|
53
|
-
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
54
65
|
remainder.push(child);
|
|
66
|
+
}
|
|
55
67
|
}
|
|
56
68
|
for (let j = i + 1; j < children.length; j++)
|
|
57
69
|
remainder.push(children[j]);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Browser stand-in for platform/node-fs.ts (selected by the package.json "browser" field). The browser has
|
|
2
|
+
// no file paths, so loading from one throws a clear, actionable error - pass bytes instead.
|
|
3
|
+
const hint = "needs Node. In the browser pass bytes (Uint8Array / ArrayBuffer) instead - e.g. import the file as a URL and fetch() it, or register a font from its bytes.";
|
|
4
|
+
export function readFileBytes(_path) {
|
|
5
|
+
throw new Error(`Loading a font from a file path ${hint}`);
|
|
6
|
+
}
|
|
7
|
+
export function readFileBytesAsync(_path) {
|
|
8
|
+
throw new Error(`Loading an image from a file path ${hint}`);
|
|
9
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// PNG decode in the browser: the platform's own image decoder, via an OffscreenCanvas, to raw RGBA. No
|
|
2
|
+
// jimp, no Buffer - keeps the browser bundle lean and works wherever Canvas does. The package `browser`
|
|
3
|
+
// field swaps `node-image.ts` for this module at bundle time.
|
|
4
|
+
export async function pngToRgba(bytes) {
|
|
5
|
+
const bitmap = await createImageBitmap(new Blob([new Uint8Array(bytes)], { type: "image/png" }));
|
|
6
|
+
const { width, height } = bitmap;
|
|
7
|
+
const ctx = new OffscreenCanvas(width, height).getContext("2d");
|
|
8
|
+
if (!ctx)
|
|
9
|
+
throw new Error("@jasy/pdf: no 2D canvas context available to decode the PNG.");
|
|
10
|
+
ctx.drawImage(bitmap, 0, 0);
|
|
11
|
+
bitmap.close();
|
|
12
|
+
return { width, height, rgba: new Uint8Array(ctx.getImageData(0, 0, width, height).data.buffer) };
|
|
13
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Node implementation of the platform file reads. In a browser bundle this whole module is swapped for
|
|
2
|
+
// platform/browser-fs.ts via the package.json "browser" field, so `node:fs` never reaches the browser.
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
|
+
export function readFileBytes(path) {
|
|
6
|
+
return readFileSync(path);
|
|
7
|
+
}
|
|
8
|
+
export function readFileBytesAsync(path) {
|
|
9
|
+
return readFile(path);
|
|
10
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// PNG decode on Node: jimp → raw RGBA pixels. jimp is lazy-imported (it pulls Node-ish bits in) so a
|
|
2
|
+
// text-only render never loads it. The browser swaps this whole module for `browser-image.ts` (a Canvas
|
|
3
|
+
// decode) via the package `browser` field, so neither jimp nor Buffer reach the browser bundle.
|
|
4
|
+
export async function pngToRgba(bytes) {
|
|
5
|
+
const { Jimp } = await import("jimp");
|
|
6
|
+
const image = await Jimp.fromBuffer(Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength));
|
|
7
|
+
const { width, height, data } = image.bitmap;
|
|
8
|
+
return { width, height, rgba: new Uint8Array(data.buffer, data.byteOffset, data.byteLength) };
|
|
9
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { PDFObjectManager } from "../utils/pdf-object-manager";
|
|
2
|
-
import { ContainerElement } from "../elements/container-element";
|
|
3
|
-
import { IRNode } from "../ir/display-list";
|
|
1
|
+
import { PDFObjectManager } from "../utils/pdf-object-manager.js";
|
|
2
|
+
import { ContainerElement } from "../elements/container-element.js";
|
|
3
|
+
import { IRNode } from "../ir/display-list.js";
|
|
4
4
|
export declare class ContainerRenderer {
|
|
5
5
|
static render(containerElement: ContainerElement, objectManager: PDFObjectManager): Promise<IRNode[]>;
|
|
6
6
|
}
|
|
@@ -1,30 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.ContainerRenderer = void 0;
|
|
13
|
-
const renderer_registry_1 = require("../utils/renderer-registry");
|
|
14
|
-
class ContainerRenderer {
|
|
15
|
-
static render(containerElement, objectManager) {
|
|
16
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
-
const { children } = containerElement.getProps();
|
|
18
|
-
const nodes = [];
|
|
19
|
-
if (children)
|
|
20
|
-
for (const child of children) {
|
|
21
|
-
const renderer = renderer_registry_1.RendererRegistry.getRenderer(child);
|
|
22
|
-
if (renderer) {
|
|
23
|
-
nodes.push(...(yield renderer(child, objectManager)));
|
|
24
|
-
}
|
|
1
|
+
import { RendererRegistry } from "../utils/renderer-registry.js";
|
|
2
|
+
export class ContainerRenderer {
|
|
3
|
+
static async render(containerElement, objectManager) {
|
|
4
|
+
const { children } = containerElement.getProps();
|
|
5
|
+
const nodes = [];
|
|
6
|
+
if (children)
|
|
7
|
+
for (const child of children) {
|
|
8
|
+
const renderer = RendererRegistry.getRenderer(child);
|
|
9
|
+
if (renderer) {
|
|
10
|
+
nodes.push(...(await renderer(child, objectManager)));
|
|
25
11
|
}
|
|
26
|
-
|
|
27
|
-
|
|
12
|
+
}
|
|
13
|
+
return nodes;
|
|
28
14
|
}
|
|
29
15
|
}
|
|
30
|
-
exports.ContainerRenderer = ContainerRenderer;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { PDFObjectManager } from "../utils/pdf-object-manager.js";
|
|
2
|
+
import { DefaultTextStyleElement } from "../elements/layout/default-text-style-element.js";
|
|
3
|
+
import { IRNode } from "../ir/display-list.js";
|
|
4
|
+
export declare class DefaultTextStyleRenderer {
|
|
5
|
+
static render(element: DefaultTextStyleElement, objectManager: PDFObjectManager): Promise<IRNode[]>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { RendererRegistry } from "../utils/renderer-registry.js";
|
|
2
|
+
// Transparent wrapper: the text style was already resolved onto the descendants at layout time, so
|
|
3
|
+
// the renderer just emits the child's display list (like PaddingRenderer).
|
|
4
|
+
export class DefaultTextStyleRenderer {
|
|
5
|
+
static async render(element, objectManager) {
|
|
6
|
+
const { child } = element.getProps();
|
|
7
|
+
const renderer = RendererRegistry.getRenderer(child);
|
|
8
|
+
return renderer ? await renderer(child, objectManager) : [];
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { PDFObjectManager } from "../utils/pdf-object-manager";
|
|
2
|
-
import { DeferredElement } from "../elements/layout/deferred-element";
|
|
3
|
-
import { IRNode } from "../ir/display-list";
|
|
1
|
+
import { PDFObjectManager } from "../utils/pdf-object-manager.js";
|
|
2
|
+
import { DeferredElement } from "../elements/layout/deferred-element.js";
|
|
3
|
+
import { IRNode } from "../ir/display-list.js";
|
|
4
4
|
export declare class DeferredRenderer {
|
|
5
5
|
static render(element: DeferredElement, objectManager: PDFObjectManager): Promise<IRNode[]>;
|
|
6
6
|
}
|
|
@@ -1,25 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.DeferredRenderer = void 0;
|
|
13
|
-
const renderer_registry_1 = require("../utils/renderer-registry");
|
|
14
|
-
class DeferredRenderer {
|
|
15
|
-
static render(element, objectManager) {
|
|
16
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
-
const { composed } = element.getProps();
|
|
18
|
-
if (!composed)
|
|
19
|
-
return [];
|
|
20
|
-
const renderer = renderer_registry_1.RendererRegistry.getRenderer(composed);
|
|
21
|
-
return renderer ? renderer(composed, objectManager) : [];
|
|
22
|
-
});
|
|
1
|
+
import { RendererRegistry } from "../utils/renderer-registry.js";
|
|
2
|
+
export class DeferredRenderer {
|
|
3
|
+
static async render(element, objectManager) {
|
|
4
|
+
const { composed } = element.getProps();
|
|
5
|
+
if (!composed)
|
|
6
|
+
return [];
|
|
7
|
+
const renderer = RendererRegistry.getRenderer(composed);
|
|
8
|
+
return renderer ? renderer(composed, objectManager) : [];
|
|
23
9
|
}
|
|
24
10
|
}
|
|
25
|
-
exports.DeferredRenderer = DeferredRenderer;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { PDFObjectManager } from "../utils/pdf-object-manager";
|
|
2
|
-
import { ExpandedElement } from "../elements";
|
|
3
|
-
import { IRNode } from "../ir/display-list";
|
|
1
|
+
import { PDFObjectManager } from "../utils/pdf-object-manager.js";
|
|
2
|
+
import { ExpandedElement } from "../elements/index.js";
|
|
3
|
+
import { IRNode } from "../ir/display-list.js";
|
|
4
4
|
export declare class ExpandedRenderer {
|
|
5
5
|
static render(expandedElement: ExpandedElement, objectManager: PDFObjectManager): Promise<IRNode[]>;
|
|
6
6
|
}
|
|
@@ -1,23 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.ExpandedRenderer = void 0;
|
|
13
|
-
const renderer_registry_1 = require("../utils/renderer-registry");
|
|
14
|
-
class ExpandedRenderer {
|
|
15
|
-
static render(expandedElement, objectManager) {
|
|
16
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
-
const { child } = expandedElement.getProps();
|
|
18
|
-
const renderer = renderer_registry_1.RendererRegistry.getRenderer(child);
|
|
19
|
-
return renderer ? yield renderer(child, objectManager) : [];
|
|
20
|
-
});
|
|
1
|
+
import { RendererRegistry } from "../utils/renderer-registry.js";
|
|
2
|
+
export class ExpandedRenderer {
|
|
3
|
+
static async render(expandedElement, objectManager) {
|
|
4
|
+
const { child } = expandedElement.getProps();
|
|
5
|
+
const renderer = RendererRegistry.getRenderer(child);
|
|
6
|
+
return renderer ? await renderer(child, objectManager) : [];
|
|
21
7
|
}
|
|
22
8
|
}
|
|
23
|
-
exports.ExpandedRenderer = ExpandedRenderer;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ImageElement } from "../elements/image-element";
|
|
2
|
-
import { PDFObjectManager } from "../utils/pdf-object-manager";
|
|
3
|
-
import { IRNode } from "../ir/display-list";
|
|
1
|
+
import { ImageElement } from "../elements/image-element.js";
|
|
2
|
+
import { PDFObjectManager } from "../utils/pdf-object-manager.js";
|
|
3
|
+
import { IRNode } from "../ir/display-list.js";
|
|
4
4
|
export declare class ImageRenderer {
|
|
5
5
|
static render(imageElement: ImageElement, _objectManager: PDFObjectManager): Promise<IRNode[]>;
|
|
6
6
|
}
|