@jasy/pdf 1.0.0-beta.3 → 1.0.0-beta.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/dist/api/content.d.ts +5 -5
- package/dist/api/content.js +12 -6
- package/dist/api/font-url.d.ts +1 -2
- package/dist/api/font-url.js +8 -74
- package/dist/api/forms.d.ts +3 -0
- package/dist/api/forms.js +3 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +1 -0
- package/dist/api/layout.d.ts +6 -0
- package/dist/api/layout.js +7 -2
- package/dist/api/page-builder.d.ts +4 -0
- package/dist/api/page-builder.js +4 -0
- package/dist/api/structure.d.ts +10 -2
- package/dist/api/structure.js +9 -6
- package/dist/api/table.d.ts +5 -3
- package/dist/api/table.js +99 -22
- package/dist/common/diagnostics.d.ts +53 -0
- package/dist/common/diagnostics.js +77 -0
- package/dist/edit/acroform-reader.js +7 -7
- package/dist/edit/appearance.js +3 -3
- package/dist/edit/attachments.d.ts +52 -0
- package/dist/edit/attachments.js +178 -0
- package/dist/edit/document.d.ts +38 -8
- package/dist/edit/document.js +208 -51
- package/dist/edit/fill.js +8 -23
- package/dist/edit/filters.d.ts +15 -0
- package/dist/edit/filters.js +157 -0
- package/dist/edit/index.d.ts +4 -1
- package/dist/edit/index.js +3 -1
- package/dist/edit/objects.d.ts +3 -4
- package/dist/edit/objects.js +5 -18
- package/dist/elements/canvas-element.d.ts +3 -0
- package/dist/elements/canvas-element.js +14 -3
- package/dist/elements/container-element.d.ts +5 -1
- package/dist/elements/container-element.js +35 -15
- package/dist/elements/forms/checkbox-element.d.ts +2 -0
- package/dist/elements/forms/checkbox-element.js +12 -3
- package/dist/elements/forms/choice-element.d.ts +6 -1
- package/dist/elements/forms/choice-element.js +17 -6
- package/dist/elements/forms/field-font.d.ts +8 -0
- package/dist/elements/forms/field-font.js +5 -0
- package/dist/elements/forms/push-button-element.d.ts +6 -1
- package/dist/elements/forms/push-button-element.js +16 -4
- package/dist/elements/forms/radio-element.d.ts +2 -0
- package/dist/elements/forms/radio-element.js +12 -3
- package/dist/elements/forms/signature-element.d.ts +6 -1
- package/dist/elements/forms/signature-element.js +16 -4
- package/dist/elements/forms/text-field-element.d.ts +6 -1
- package/dist/elements/forms/text-field-element.js +16 -4
- package/dist/elements/image-element.d.ts +39 -1
- package/dist/elements/image-element.js +95 -12
- package/dist/elements/layout/anchor-element.d.ts +8 -10
- package/dist/elements/layout/anchor-element.js +10 -18
- package/dist/elements/layout/bookmark-element.d.ts +9 -11
- package/dist/elements/layout/bookmark-element.js +11 -20
- package/dist/elements/layout/default-text-style-element.d.ts +11 -13
- package/dist/elements/layout/default-text-style-element.js +14 -31
- package/dist/elements/layout/deferred-element.d.ts +12 -4
- package/dist/elements/layout/deferred-element.js +24 -7
- package/dist/elements/layout/expanded-element.d.ts +7 -0
- package/dist/elements/layout/expanded-element.js +40 -18
- package/dist/elements/layout/keep-together-element.d.ts +8 -18
- package/dist/elements/layout/keep-together-element.js +21 -45
- package/dist/elements/layout/link-element.d.ts +8 -13
- package/dist/elements/layout/link-element.js +9 -19
- package/dist/elements/layout/padding-element.d.ts +15 -1
- package/dist/elements/layout/padding-element.js +55 -27
- package/dist/elements/layout/page-break-element.d.ts +4 -1
- package/dist/elements/layout/page-break-element.js +6 -1
- package/dist/elements/layout/page-builder-element.d.ts +5 -0
- package/dist/elements/layout/page-builder-element.js +11 -0
- package/dist/elements/layout/positioned-element.d.ts +14 -2
- package/dist/elements/layout/positioned-element.js +36 -10
- package/dist/elements/layout/proxy-element.d.ts +43 -0
- package/dist/elements/layout/proxy-element.js +94 -0
- package/dist/elements/layout/repeating-header-element.d.ts +6 -0
- package/dist/elements/layout/repeating-header-element.js +40 -13
- package/dist/elements/layout/rotated-box-element.d.ts +6 -0
- package/dist/elements/layout/rotated-box-element.js +27 -14
- package/dist/elements/layout/rotated-element.d.ts +7 -0
- package/dist/elements/layout/rotated-element.js +25 -2
- package/dist/elements/layout/struct-group.d.ts +9 -17
- package/dist/elements/layout/struct-group.js +10 -35
- package/dist/elements/line-element.d.ts +1 -0
- package/dist/elements/line-element.js +6 -0
- package/dist/elements/page-element.d.ts +10 -3
- package/dist/elements/page-element.js +39 -31
- package/dist/elements/pdf-document-element.d.ts +3 -0
- package/dist/elements/pdf-document-element.js +7 -0
- package/dist/elements/pdf-element.d.ts +75 -15
- package/dist/elements/pdf-element.js +68 -18
- package/dist/elements/rectangle-element.d.ts +14 -0
- package/dist/elements/rectangle-element.js +97 -52
- package/dist/elements/row-element.d.ts +11 -0
- package/dist/elements/row-element.js +58 -8
- package/dist/elements/svg-element.d.ts +30 -6
- package/dist/elements/svg-element.js +88 -17
- package/dist/elements/text-element.d.ts +32 -13
- package/dist/elements/text-element.js +119 -97
- package/dist/forms/acroform.d.ts +9 -1
- package/dist/forms/acroform.js +83 -34
- package/dist/forms/appearance.d.ts +17 -6
- package/dist/forms/appearance.js +18 -16
- package/dist/ir/display-list.d.ts +12 -2
- package/dist/layout/box-constraints.d.ts +5 -0
- package/dist/layout/box-constraints.js +14 -0
- package/dist/layout/collect-images.d.ts +6 -4
- package/dist/layout/collect-images.js +6 -4
- package/dist/layout/flex-item.d.ts +9 -0
- package/dist/layout/flex-item.js +39 -0
- package/dist/layout/fragmentation.d.ts +12 -4
- package/dist/layout/fragmentation.js +37 -14
- package/dist/layout/layout-cache.d.ts +53 -0
- package/dist/layout/layout-cache.js +107 -0
- package/dist/renderer/anchor-renderer.d.ts +2 -1
- package/dist/renderer/anchor-renderer.js +5 -4
- package/dist/renderer/bookmark-renderer.d.ts +2 -1
- package/dist/renderer/bookmark-renderer.js +5 -4
- package/dist/renderer/canvas-renderer.d.ts +2 -1
- package/dist/renderer/canvas-renderer.js +5 -2
- package/dist/renderer/container-renderer.d.ts +2 -1
- package/dist/renderer/container-renderer.js +6 -5
- package/dist/renderer/default-text-style-renderer.d.ts +2 -1
- package/dist/renderer/default-text-style-renderer.js +2 -3
- package/dist/renderer/deferred-renderer.d.ts +2 -1
- package/dist/renderer/deferred-renderer.js +2 -3
- package/dist/renderer/expanded-renderer.d.ts +2 -1
- package/dist/renderer/expanded-renderer.js +2 -3
- package/dist/renderer/form-field-renderer.d.ts +2 -1
- package/dist/renderer/form-field-renderer.js +16 -3
- package/dist/renderer/image-renderer.d.ts +2 -1
- package/dist/renderer/image-renderer.js +5 -2
- package/dist/renderer/keep-together-renderer.d.ts +2 -1
- package/dist/renderer/keep-together-renderer.js +2 -3
- package/dist/renderer/line-renderer.d.ts +2 -1
- package/dist/renderer/line-renderer.js +7 -2
- package/dist/renderer/link-renderer.d.ts +2 -1
- package/dist/renderer/link-renderer.js +36 -5
- package/dist/renderer/padding-renderer.d.ts +2 -1
- package/dist/renderer/padding-renderer.js +5 -5
- package/dist/renderer/page-break-renderer.d.ts +6 -5
- package/dist/renderer/page-break-renderer.js +14 -7
- package/dist/renderer/page-builder-renderer.d.ts +2 -1
- package/dist/renderer/page-builder-renderer.js +2 -3
- package/dist/renderer/page-renderer.js +28 -7
- package/dist/renderer/pdf-backend.d.ts +4 -3
- package/dist/renderer/pdf-backend.js +5 -5
- package/dist/renderer/pdf-document-renderer.d.ts +1 -8
- package/dist/renderer/pdf-document-renderer.js +15 -16
- package/dist/renderer/pdf-renderer.js +11 -11
- package/dist/renderer/positioned-renderer.d.ts +6 -1
- package/dist/renderer/positioned-renderer.js +6 -5
- package/dist/renderer/rectangle-renderer.d.ts +2 -1
- package/dist/renderer/rectangle-renderer.js +9 -4
- package/dist/renderer/repeating-header-renderer.d.ts +2 -1
- package/dist/renderer/repeating-header-renderer.js +6 -5
- package/dist/renderer/rotated-renderer.d.ts +2 -1
- package/dist/renderer/rotated-renderer.js +8 -5
- package/dist/renderer/row-renderer.d.ts +2 -1
- package/dist/renderer/row-renderer.js +6 -5
- package/dist/renderer/struct-group-renderer.d.ts +2 -1
- package/dist/renderer/struct-group-renderer.js +2 -3
- package/dist/renderer/svg-renderer.d.ts +2 -1
- package/dist/renderer/svg-renderer.js +8 -2
- package/dist/renderer/text-renderer.d.ts +4 -6
- package/dist/renderer/text-renderer.js +101 -111
- package/dist/svg/gradients.js +5 -0
- package/dist/svg/index.js +10 -1
- package/dist/svg/style.d.ts +2 -0
- package/dist/svg/style.js +11 -0
- package/dist/text/advance.d.ts +4 -0
- package/dist/text/advance.js +17 -4
- package/dist/text/bidi.d.ts +0 -2
- package/dist/text/bidi.js +0 -10
- package/dist/text/glyph-coverage.d.ts +1 -1
- package/dist/text/glyph-coverage.js +7 -3
- package/dist/text/hyphen-breaks.d.ts +17 -0
- package/dist/text/hyphen-breaks.js +54 -0
- package/dist/text/line-breaker.d.ts +34 -31
- package/dist/text/line-breaker.js +364 -176
- package/dist/text/line-metrics.d.ts +6 -3
- package/dist/text/line-metrics.js +7 -2
- package/dist/text/pdf-text-string.d.ts +21 -0
- package/dist/text/pdf-text-string.js +114 -0
- package/dist/text/text-decoration.d.ts +5 -5
- package/dist/text/text-decoration.js +5 -5
- package/dist/text/word-splitting.d.ts +15 -2
- package/dist/text/word-splitting.js +34 -8
- package/dist/utils/afm-parser.d.ts +2 -0
- package/dist/utils/afm-parser.js +10 -1
- package/dist/utils/dest-registry.d.ts +1 -1
- package/dist/utils/dest-registry.js +3 -2
- package/dist/utils/fetch-bytes.d.ts +19 -0
- package/dist/utils/fetch-bytes.js +79 -0
- package/dist/utils/flex-layout.d.ts +13 -13
- package/dist/utils/flex-layout.js +318 -216
- package/dist/utils/font-metrics.d.ts +4 -3
- package/dist/utils/image-helper.js +2 -1
- package/dist/utils/pdf-object-manager.d.ts +11 -3
- package/dist/utils/pdf-object-manager.js +39 -13
- package/dist/utils/renderer-registry.d.ts +43 -2
- package/dist/utils/renderer-registry.js +40 -0
- package/dist/utils/struct-tree.d.ts +24 -0
- package/dist/utils/struct-tree.js +61 -9
- package/dist/utils/ttf-parser.d.ts +4 -0
- package/dist/utils/ttf-parser.js +27 -4
- package/dist/validators/element-validator.js +2 -1
- package/package.json +1 -1
- package/dist/elements/layout/sized-container-element.d.ts +0 -14
- package/dist/elements/layout/sized-container-element.js +0 -32
|
@@ -8,7 +8,8 @@ export declare enum BoxFit {
|
|
|
8
8
|
fill = "FILL"
|
|
9
9
|
}
|
|
10
10
|
export declare abstract class CustomImage {
|
|
11
|
-
|
|
11
|
+
/** Loads the image. `fresh`: a new render begins - a load that failed in an earlier one is tried again. */
|
|
12
|
+
abstract init(fresh?: boolean): void | Promise<void>;
|
|
12
13
|
abstract getImageType(): string | Promise<string>;
|
|
13
14
|
abstract getFileData(): string | Promise<string>;
|
|
14
15
|
abstract getImageDimensions(): Promise<{
|
|
@@ -48,6 +49,38 @@ export declare class CustomBytesImage extends CustomImage {
|
|
|
48
49
|
height: number;
|
|
49
50
|
}>;
|
|
50
51
|
}
|
|
52
|
+
/** Thrown when an image could not be fetched or is not one we can embed. Always says which URL. */
|
|
53
|
+
export declare class ImageUrlError extends Error {
|
|
54
|
+
constructor(message: string);
|
|
55
|
+
}
|
|
56
|
+
/** How long an image from a URL may take, and how large it may be - a raster image and an SVG alike. */
|
|
57
|
+
export declare const IMAGE_FETCH_LIMITS: {
|
|
58
|
+
timeoutMs: number;
|
|
59
|
+
maxBytes: number;
|
|
60
|
+
};
|
|
61
|
+
/** Whether an image source string is an address to fetch rather than a file path. */
|
|
62
|
+
export declare const isImageUrl: (src: string) => boolean;
|
|
63
|
+
/**
|
|
64
|
+
* An image from a URL - `http(s):` or a `data:` URL - fetched in the async pass before layout, with
|
|
65
|
+
* the same timeout and size ceiling as a font. What it IS comes from its bytes, never from the
|
|
66
|
+
* address, so `.../photo?id=7` works. A dead link stops the render with an `ImageUrlError` naming it.
|
|
67
|
+
*/
|
|
68
|
+
export declare class CustomUrlImage extends CustomImage {
|
|
69
|
+
private readonly url;
|
|
70
|
+
private loading?;
|
|
71
|
+
private loaded?;
|
|
72
|
+
private failed;
|
|
73
|
+
constructor(url: string);
|
|
74
|
+
init(fresh?: boolean): Promise<void>;
|
|
75
|
+
private load;
|
|
76
|
+
private image;
|
|
77
|
+
getImageType(): Promise<string>;
|
|
78
|
+
getFileData(): string;
|
|
79
|
+
getImageDimensions(): Promise<{
|
|
80
|
+
width: number;
|
|
81
|
+
height: number;
|
|
82
|
+
}>;
|
|
83
|
+
}
|
|
51
84
|
interface ImageElementParams {
|
|
52
85
|
image: CustomImage;
|
|
53
86
|
width?: number;
|
|
@@ -94,6 +127,11 @@ export declare class ImageElement extends SizedPDFElement {
|
|
|
94
127
|
resolveIntrinsicSize(): Promise<void>;
|
|
95
128
|
relativeSizeFactor(horizontal: boolean): number | undefined;
|
|
96
129
|
calculateLayout(constraints: BoxConstraints, offset: Offset, _ctx: LayoutContext): Size;
|
|
130
|
+
protected computeSize(constraints: BoxConstraints): Size;
|
|
131
|
+
/** Its size under these constraints - the one computation both `measure` and layout use. */
|
|
132
|
+
private sizeFor;
|
|
133
|
+
/** The image's own pixel extent on one axis, clamped by its min/max; unknown before the pre-pass. */
|
|
134
|
+
private ownExtent;
|
|
97
135
|
getProps(): {
|
|
98
136
|
x: number;
|
|
99
137
|
y: number;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getImageDimensions } from "../utils/image-helper.js";
|
|
2
2
|
import { latin1FromBytes } from "../utils/bytes.js";
|
|
3
3
|
import { readFileBytesAsync } from "../platform/node-fs.js";
|
|
4
|
+
import { fetchBytes } from "../utils/fetch-bytes.js";
|
|
4
5
|
import { extentSpecs, resolveSize, } from "../layout/box-constraints.js";
|
|
5
6
|
import { SizedPDFElement } from "./pdf-element.js";
|
|
6
7
|
// path.extname without node:path (browser-safe): the substring from the last dot, if it sits after the
|
|
@@ -28,11 +29,10 @@ export class CustomLocalImage extends CustomImage {
|
|
|
28
29
|
if (this.fileBuffer)
|
|
29
30
|
return; // idempotent: the pre-layout size pass and the renderer both call init()
|
|
30
31
|
try {
|
|
31
|
-
// Loading image and convert it to base64
|
|
32
32
|
await this.loadImage(this.imagePath);
|
|
33
33
|
}
|
|
34
|
-
catch (
|
|
35
|
-
|
|
34
|
+
catch (e) {
|
|
35
|
+
throw new Error(`@jasy/pdf: could not read the image at ${this.imagePath}: ${String(e?.message ?? e)}`);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
async getImageType() {
|
|
@@ -64,9 +64,8 @@ export class CustomLocalImage extends CustomImage {
|
|
|
64
64
|
async getImageDimensions() {
|
|
65
65
|
if (this.dims)
|
|
66
66
|
return this.dims; // reuse: the pre-layout size pass and the renderer both ask
|
|
67
|
-
if (!this.fileBuffer)
|
|
68
|
-
throw new Error("
|
|
69
|
-
}
|
|
67
|
+
if (!this.fileBuffer)
|
|
68
|
+
throw new Error("@jasy/pdf: an image was measured before it was loaded");
|
|
70
69
|
this.dims = await getImageDimensions(this.fileBuffer);
|
|
71
70
|
return this.dims;
|
|
72
71
|
}
|
|
@@ -99,6 +98,64 @@ export class CustomBytesImage extends CustomImage {
|
|
|
99
98
|
return this.dims;
|
|
100
99
|
}
|
|
101
100
|
}
|
|
101
|
+
/** Thrown when an image could not be fetched or is not one we can embed. Always says which URL. */
|
|
102
|
+
export class ImageUrlError extends Error {
|
|
103
|
+
constructor(message) {
|
|
104
|
+
super(`@jasy/pdf: ${message}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/** How long an image from a URL may take, and how large it may be - a raster image and an SVG alike. */
|
|
108
|
+
export const IMAGE_FETCH_LIMITS = { timeoutMs: 15000, maxBytes: 32 * 1024 * 1024 };
|
|
109
|
+
/** Whether an image source string is an address to fetch rather than a file path. */
|
|
110
|
+
export const isImageUrl = (src) => /^(https?:|data:)/i.test(src.trim());
|
|
111
|
+
/**
|
|
112
|
+
* An image from a URL - `http(s):` or a `data:` URL - fetched in the async pass before layout, with
|
|
113
|
+
* the same timeout and size ceiling as a font. What it IS comes from its bytes, never from the
|
|
114
|
+
* address, so `.../photo?id=7` works. A dead link stops the render with an `ImageUrlError` naming it.
|
|
115
|
+
*/
|
|
116
|
+
export class CustomUrlImage extends CustomImage {
|
|
117
|
+
constructor(url) {
|
|
118
|
+
super();
|
|
119
|
+
this.url = url;
|
|
120
|
+
this.failed = false;
|
|
121
|
+
}
|
|
122
|
+
// One fetch per render, however often it is asked: the pre-layout size pass and the renderer both
|
|
123
|
+
// call init(). A failure sticks for that render only - the next one asks again (`fresh`).
|
|
124
|
+
async init(fresh = false) {
|
|
125
|
+
if (fresh && this.failed)
|
|
126
|
+
this.loading = undefined;
|
|
127
|
+
this.loading ?? (this.loading = this.load().then((image) => ((this.failed = false), image), (e) => {
|
|
128
|
+
this.failed = true;
|
|
129
|
+
throw e;
|
|
130
|
+
}));
|
|
131
|
+
this.loaded = await this.loading;
|
|
132
|
+
}
|
|
133
|
+
async load() {
|
|
134
|
+
const bytes = await fetchBytes(this.url, "image", ImageUrlError, IMAGE_FETCH_LIMITS);
|
|
135
|
+
const jpeg = bytes[0] === 0xff && bytes[1] === 0xd8;
|
|
136
|
+
const png = bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4e && bytes[3] === 0x47;
|
|
137
|
+
if (!jpeg && !png) {
|
|
138
|
+
// An error page answers with a 200 often enough to name it rather than fail deep inside.
|
|
139
|
+
const looksLikeMarkup = new TextDecoder().decode(bytes.subarray(0, 64)).trimStart()[0] === "<";
|
|
140
|
+
throw new ImageUrlError(`the file at ${this.url} is ${looksLikeMarkup ? "markup (an HTML page, or an SVG - pass that to Svg(url))" : "not an image we can embed"} - jasy embeds PNG and JPEG`);
|
|
141
|
+
}
|
|
142
|
+
return new CustomBytesImage(bytes);
|
|
143
|
+
}
|
|
144
|
+
image() {
|
|
145
|
+
if (!this.loaded)
|
|
146
|
+
throw new Error("@jasy/pdf: an image was measured before it was loaded");
|
|
147
|
+
return this.loaded;
|
|
148
|
+
}
|
|
149
|
+
getImageType() {
|
|
150
|
+
return this.image().getImageType();
|
|
151
|
+
}
|
|
152
|
+
getFileData() {
|
|
153
|
+
return this.image().getFileData();
|
|
154
|
+
}
|
|
155
|
+
getImageDimensions() {
|
|
156
|
+
return this.image().getImageDimensions();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
102
159
|
export class ImageElement extends SizedPDFElement {
|
|
103
160
|
constructor({ image, width, height, widthFactor, heightFactor, fit = BoxFit.none, radius, alt, ...sizing }) {
|
|
104
161
|
// Seed this.width/height from the request so getProps() reflects it before layout; calculateLayout
|
|
@@ -122,7 +179,7 @@ export class ImageElement extends SizedPDFElement {
|
|
|
122
179
|
*/
|
|
123
180
|
async resolveIntrinsicSize() {
|
|
124
181
|
try {
|
|
125
|
-
await this.image.init();
|
|
182
|
+
await this.image.init(true); // a render begins here
|
|
126
183
|
this.intrinsic = await this.image.getImageDimensions();
|
|
127
184
|
}
|
|
128
185
|
catch {
|
|
@@ -133,8 +190,20 @@ export class ImageElement extends SizedPDFElement {
|
|
|
133
190
|
return horizontal ? this.widthFactor : this.heightFactor;
|
|
134
191
|
}
|
|
135
192
|
calculateLayout(constraints, offset, _ctx) {
|
|
193
|
+
const size = this.sizeFor(constraints);
|
|
136
194
|
this.x = offset.x;
|
|
137
195
|
this.y = offset.y;
|
|
196
|
+
// Kept as resolved, `undefined` included: the renderer reads an unset axis differently from 0.
|
|
197
|
+
this.width = size.width;
|
|
198
|
+
this.height = size.height;
|
|
199
|
+
return { width: size.width ?? 0, height: size.height ?? 0 };
|
|
200
|
+
}
|
|
201
|
+
computeSize(constraints) {
|
|
202
|
+
const size = this.sizeFor(constraints);
|
|
203
|
+
return { width: size.width ?? 0, height: size.height ?? 0 };
|
|
204
|
+
}
|
|
205
|
+
/** Its size under these constraints - the one computation both `measure` and layout use. */
|
|
206
|
+
sizeFor(constraints) {
|
|
138
207
|
// Relative sizing: a fixed size or a fraction of the offered box (fraction only in a bounded axis).
|
|
139
208
|
// Read from `requested` (not this.width/height, which we overwrite below) so every pass re-resolves.
|
|
140
209
|
const specs = extentSpecs({
|
|
@@ -163,12 +232,26 @@ export class ImageElement extends SizedPDFElement {
|
|
|
163
232
|
else if (h !== undefined && w === undefined)
|
|
164
233
|
w = bounds.constrainWidth(h * ratio);
|
|
165
234
|
}
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
235
|
+
// An unpinned axis: a bounded one fills; an unbounded one takes the image's own extent (1px = 1pt) -
|
|
236
|
+
// what `fit: none` (the default) draws there, so the layout reserves exactly what is drawn. Read from
|
|
237
|
+
// `requested`, never the mutated this.width/height.
|
|
238
|
+
const width = w ??
|
|
239
|
+
(bounds.hasBoundedWidth
|
|
240
|
+
? bounds.maxWidth
|
|
241
|
+
: (this.requested.width ?? this.ownExtent(bounds, "width")));
|
|
242
|
+
const height = h ??
|
|
243
|
+
(bounds.hasBoundedHeight
|
|
244
|
+
? bounds.maxHeight
|
|
245
|
+
: (this.requested.height ?? this.ownExtent(bounds, "height")));
|
|
170
246
|
// Top-left coordinates; the fit logic (renderer) and the Y-flip (seam) run later.
|
|
171
|
-
return { width
|
|
247
|
+
return { width, height };
|
|
248
|
+
}
|
|
249
|
+
/** The image's own pixel extent on one axis, clamped by its min/max; unknown before the pre-pass. */
|
|
250
|
+
ownExtent(bounds, axis) {
|
|
251
|
+
const own = this.intrinsic?.[axis];
|
|
252
|
+
if (own === undefined)
|
|
253
|
+
return undefined;
|
|
254
|
+
return axis === "width" ? bounds.constrainWidth(own) : bounds.constrainHeight(own);
|
|
172
255
|
}
|
|
173
256
|
getProps() {
|
|
174
257
|
return {
|
|
@@ -1,20 +1,18 @@
|
|
|
1
|
-
import { PDFElement
|
|
2
|
-
import {
|
|
1
|
+
import { PDFElement } from "../pdf-element.js";
|
|
2
|
+
import { ProxyElement } from "./proxy-element.js";
|
|
3
3
|
/**
|
|
4
|
-
* Marks its child as a named jump target for an internal `Link({ to: name })`. Layout-transparent
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Marks its child as a named jump target for an internal `Link({ to: name })`. Layout-transparent (a
|
|
5
|
+
* `ProxyElement`); the renderer turns the child's top into an `Anchor` IR node (and PDFRenderer into a
|
|
6
|
+
* /Names /Dests entry). It draws nothing. Split across pages, only the first piece is the target - a
|
|
7
|
+
* name must lead to one place.
|
|
8
8
|
*/
|
|
9
|
-
export declare class AnchorElement extends
|
|
9
|
+
export declare class AnchorElement extends ProxyElement {
|
|
10
10
|
private name;
|
|
11
|
-
private child;
|
|
12
|
-
private y;
|
|
13
11
|
constructor({ name, child }: {
|
|
14
12
|
name: string;
|
|
15
13
|
child: PDFElement;
|
|
16
14
|
});
|
|
17
|
-
|
|
15
|
+
protected rewrap(child: PDFElement, continued: boolean): PDFElement;
|
|
18
16
|
getProps(): {
|
|
19
17
|
name: string;
|
|
20
18
|
child: PDFElement;
|
|
@@ -1,27 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ProxyElement } from "./proxy-element.js";
|
|
2
2
|
/**
|
|
3
|
-
* Marks its child as a named jump target for an internal `Link({ to: name })`. Layout-transparent
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* Marks its child as a named jump target for an internal `Link({ to: name })`. Layout-transparent (a
|
|
4
|
+
* `ProxyElement`); the renderer turns the child's top into an `Anchor` IR node (and PDFRenderer into a
|
|
5
|
+
* /Names /Dests entry). It draws nothing. Split across pages, only the first piece is the target - a
|
|
6
|
+
* name must lead to one place.
|
|
7
7
|
*/
|
|
8
|
-
export class AnchorElement extends
|
|
8
|
+
export class AnchorElement extends ProxyElement {
|
|
9
9
|
constructor({ name, child }) {
|
|
10
|
-
super();
|
|
11
|
-
this.y = 0;
|
|
10
|
+
super(child);
|
|
12
11
|
this.name = name;
|
|
13
|
-
this.child = child;
|
|
14
12
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
this.y = offset.y; // the child's top - the scroll target for a link that jumps here
|
|
18
|
-
return size;
|
|
13
|
+
rewrap(child, continued) {
|
|
14
|
+
return continued ? child : new AnchorElement({ name: this.name, child });
|
|
19
15
|
}
|
|
20
16
|
getProps() {
|
|
21
|
-
return {
|
|
22
|
-
name: this.name,
|
|
23
|
-
child: this.child,
|
|
24
|
-
y: this.y,
|
|
25
|
-
};
|
|
17
|
+
return { name: this.name, child: this.child, y: this.y };
|
|
26
18
|
}
|
|
27
19
|
}
|
|
@@ -1,23 +1,21 @@
|
|
|
1
|
-
import { PDFElement
|
|
2
|
-
import {
|
|
1
|
+
import { PDFElement } from "../pdf-element.js";
|
|
2
|
+
import { ProxyElement } from "./proxy-element.js";
|
|
3
3
|
/**
|
|
4
|
-
* Adds a document-outline entry (a bookmark) that jumps to its child. Layout-transparent
|
|
5
|
-
* `
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Adds a document-outline entry (a bookmark) that jumps to its child. Layout-transparent (a
|
|
5
|
+
* `ProxyElement`); the renderer turns the child's top into an `Outline` IR node (and PDFRenderer into a
|
|
6
|
+
* /Outlines tree entry). `title` is the label shown in the viewer's bookmark panel; `level` (1-based,
|
|
7
|
+
* default 1) nests it under the nearest preceding smaller level. It draws nothing. Split across pages,
|
|
8
|
+
* only the first piece carries the entry - a chapter is listed once.
|
|
9
9
|
*/
|
|
10
|
-
export declare class BookmarkElement extends
|
|
10
|
+
export declare class BookmarkElement extends ProxyElement {
|
|
11
11
|
private title;
|
|
12
12
|
private level;
|
|
13
|
-
private child;
|
|
14
|
-
private y;
|
|
15
13
|
constructor({ title, level, child }: {
|
|
16
14
|
title: string;
|
|
17
15
|
level?: number;
|
|
18
16
|
child: PDFElement;
|
|
19
17
|
});
|
|
20
|
-
|
|
18
|
+
protected rewrap(child: PDFElement, continued: boolean): PDFElement;
|
|
21
19
|
getProps(): {
|
|
22
20
|
title: string;
|
|
23
21
|
level: number;
|
|
@@ -1,30 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ProxyElement } from "./proxy-element.js";
|
|
2
2
|
/**
|
|
3
|
-
* Adds a document-outline entry (a bookmark) that jumps to its child. Layout-transparent
|
|
4
|
-
* `
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* Adds a document-outline entry (a bookmark) that jumps to its child. Layout-transparent (a
|
|
4
|
+
* `ProxyElement`); the renderer turns the child's top into an `Outline` IR node (and PDFRenderer into a
|
|
5
|
+
* /Outlines tree entry). `title` is the label shown in the viewer's bookmark panel; `level` (1-based,
|
|
6
|
+
* default 1) nests it under the nearest preceding smaller level. It draws nothing. Split across pages,
|
|
7
|
+
* only the first piece carries the entry - a chapter is listed once.
|
|
8
8
|
*/
|
|
9
|
-
export class BookmarkElement extends
|
|
9
|
+
export class BookmarkElement extends ProxyElement {
|
|
10
10
|
constructor({ title, level = 1, child }) {
|
|
11
|
-
super();
|
|
12
|
-
this.y = 0;
|
|
11
|
+
super(child);
|
|
13
12
|
this.title = title;
|
|
14
13
|
this.level = level;
|
|
15
|
-
this.child = child;
|
|
16
14
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
this.y = offset.y; // the child's top - the scroll target for the bookmark
|
|
20
|
-
return size;
|
|
15
|
+
rewrap(child, continued) {
|
|
16
|
+
return continued ? child : new BookmarkElement({ title: this.title, level: this.level, child });
|
|
21
17
|
}
|
|
22
18
|
getProps() {
|
|
23
|
-
return {
|
|
24
|
-
title: this.title,
|
|
25
|
-
level: this.level,
|
|
26
|
-
child: this.child,
|
|
27
|
-
y: this.y,
|
|
28
|
-
};
|
|
19
|
+
return { title: this.title, level: this.level, child: this.child, y: this.y };
|
|
29
20
|
}
|
|
30
21
|
}
|
|
@@ -1,29 +1,27 @@
|
|
|
1
|
-
import { PDFElement, LayoutContext, WithChild
|
|
2
|
-
import {
|
|
3
|
-
import { Fragmentable, FragmentResult } from "../../layout/fragmentation.js";
|
|
1
|
+
import { PDFElement, LayoutContext, WithChild } from "../pdf-element.js";
|
|
2
|
+
import { ProxyElement } from "./proxy-element.js";
|
|
4
3
|
import { ResolvedTextStyle } from "../../text/text-style.js";
|
|
5
4
|
interface DefaultTextStyleParams extends WithChild {
|
|
6
5
|
style: Partial<ResolvedTextStyle>;
|
|
7
6
|
}
|
|
8
7
|
/**
|
|
9
8
|
* Provides default text properties (font/size/color/lineHeight/align/weight) to its whole subtree -
|
|
10
|
-
* Flutter's `DefaultTextStyle`. Transparent to layout
|
|
11
|
-
*
|
|
9
|
+
* Flutter's `DefaultTextStyle`. Transparent to layout (a `ProxyElement`): only the inherited
|
|
10
|
+
* TextStyle changes. A `Text` below still wins per property, and these
|
|
12
11
|
* overrides layer onto whatever the element already inherited from above.
|
|
13
12
|
*/
|
|
14
|
-
export declare class DefaultTextStyleElement extends
|
|
15
|
-
private child;
|
|
13
|
+
export declare class DefaultTextStyleElement extends ProxyElement {
|
|
16
14
|
private style;
|
|
17
15
|
constructor({ child, style }: DefaultTextStyleParams);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
|
|
16
|
+
protected childContext(ctx: LayoutContext): LayoutContext;
|
|
17
|
+
private merged?;
|
|
18
|
+
private mergedOnto;
|
|
19
|
+
protected rewrap(child: PDFElement): PDFElement;
|
|
22
20
|
getProps(): {
|
|
23
21
|
x: number;
|
|
24
22
|
y: number;
|
|
25
|
-
width: number
|
|
26
|
-
height: number
|
|
23
|
+
width: number;
|
|
24
|
+
height: number;
|
|
27
25
|
child: PDFElement;
|
|
28
26
|
};
|
|
29
27
|
}
|
|
@@ -1,46 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isFragmentable } from "../../layout/fragmentation.js";
|
|
1
|
+
import { ProxyElement } from "./proxy-element.js";
|
|
3
2
|
import { DEFAULT_TEXT_STYLE, mergeTextStyle } from "../../text/text-style.js";
|
|
4
3
|
/**
|
|
5
4
|
* Provides default text properties (font/size/color/lineHeight/align/weight) to its whole subtree -
|
|
6
|
-
* Flutter's `DefaultTextStyle`. Transparent to layout
|
|
7
|
-
*
|
|
5
|
+
* Flutter's `DefaultTextStyle`. Transparent to layout (a `ProxyElement`): only the inherited
|
|
6
|
+
* TextStyle changes. A `Text` below still wins per property, and these
|
|
8
7
|
* overrides layer onto whatever the element already inherited from above.
|
|
9
8
|
*/
|
|
10
|
-
export class DefaultTextStyleElement extends
|
|
9
|
+
export class DefaultTextStyleElement extends ProxyElement {
|
|
11
10
|
constructor({ child, style }) {
|
|
12
|
-
super(
|
|
13
|
-
this.child = child;
|
|
11
|
+
super(child);
|
|
14
12
|
this.style = style;
|
|
15
13
|
}
|
|
16
|
-
|
|
17
|
-
return {
|
|
18
|
-
...ctx,
|
|
19
|
-
textStyle: mergeTextStyle(ctx.textStyle ?? DEFAULT_TEXT_STYLE, this.style),
|
|
20
|
-
};
|
|
14
|
+
childContext(ctx) {
|
|
15
|
+
return { ...ctx, textStyle: this.mergedOnto(ctx.textStyle ?? DEFAULT_TEXT_STYLE) };
|
|
21
16
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const split = this.child.fragment(maxHeight, width, this.childCtx(ctx));
|
|
28
|
-
return {
|
|
29
|
-
fitted: split.fitted ? this.cloneWithChild(split.fitted) : null,
|
|
30
|
-
remainder: split.remainder ? this.cloneWithChild(split.remainder) : null,
|
|
31
|
-
};
|
|
17
|
+
mergedOnto(inherited) {
|
|
18
|
+
if (this.merged?.onto !== inherited) {
|
|
19
|
+
this.merged = { onto: inherited, style: mergeTextStyle(inherited, this.style) };
|
|
20
|
+
}
|
|
21
|
+
return this.merged.style;
|
|
32
22
|
}
|
|
33
|
-
|
|
23
|
+
// Each piece of a split keeps the defaults, so the remainder on the next page looks the same.
|
|
24
|
+
rewrap(child) {
|
|
34
25
|
return new DefaultTextStyleElement({ child, style: this.style });
|
|
35
26
|
}
|
|
36
|
-
calculateLayout(constraints, offset, ctx) {
|
|
37
|
-
this.x = offset.x;
|
|
38
|
-
this.y = offset.y;
|
|
39
|
-
const size = this.child.calculateLayout(constraints, offset, this.childCtx(ctx));
|
|
40
|
-
this.width = size.width;
|
|
41
|
-
this.height = size.height;
|
|
42
|
-
return size;
|
|
43
|
-
}
|
|
44
27
|
getProps() {
|
|
45
28
|
return { x: this.x, y: this.y, width: this.width, height: this.height, child: this.child };
|
|
46
29
|
}
|
|
@@ -2,17 +2,25 @@ import { BoxConstraints, Offset, Size } from "../../layout/box-constraints.js";
|
|
|
2
2
|
import { Fragmentable, FragmentResult } from "../../layout/fragmentation.js";
|
|
3
3
|
import { LayoutContext, PDFElement } from "../pdf-element.js";
|
|
4
4
|
/**
|
|
5
|
-
* Builds its subtree at layout time via `resolve(ctx)`, so the tree can depend on font
|
|
6
|
-
* metrics (e.g. a Table
|
|
7
|
-
* stays table-agnostic - the closure comes from the API layer.
|
|
5
|
+
* Builds its subtree at layout time via `resolve(ctx, width)`, so the tree can depend on font
|
|
6
|
+
* metrics and on the width it gets (e.g. a Table fitting its `"auto"` and `fr` columns to the cell
|
|
7
|
+
* content and its own width). The engine stays table-agnostic - the closure comes from the API layer.
|
|
8
8
|
*/
|
|
9
9
|
export declare class DeferredElement extends PDFElement implements Fragmentable {
|
|
10
10
|
private resolve;
|
|
11
11
|
private composed?;
|
|
12
|
-
|
|
12
|
+
private built;
|
|
13
|
+
constructor(resolve: (ctx: LayoutContext, width: number) => PDFElement);
|
|
14
|
+
/** The subtree for this context and width: built once, and again only when one of them changes. */
|
|
15
|
+
private subtree;
|
|
13
16
|
private build;
|
|
14
17
|
calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
|
|
18
|
+
/** Its subtree is unknown until built, and could hold a `PageBuilder`; until then assume it does. */
|
|
19
|
+
dependsOnPage(): boolean;
|
|
20
|
+
/** Sizes the subtree for this context - the same one a layout under it will draw. */
|
|
21
|
+
protected computeSize(constraints: BoxConstraints, ctx: LayoutContext): Size;
|
|
15
22
|
fragment(maxHeight: number, width: number, ctx: LayoutContext): FragmentResult;
|
|
23
|
+
layoutChildren(): readonly PDFElement[];
|
|
16
24
|
getProps(): {
|
|
17
25
|
composed: PDFElement | undefined;
|
|
18
26
|
};
|
|
@@ -1,28 +1,45 @@
|
|
|
1
1
|
import { isFragmentable } from "../../layout/fragmentation.js";
|
|
2
2
|
import { PDFElement } from "../pdf-element.js";
|
|
3
|
+
import { BuiltFromContext } from "../../layout/layout-cache.js";
|
|
3
4
|
/**
|
|
4
|
-
* Builds its subtree at layout time via `resolve(ctx)`, so the tree can depend on font
|
|
5
|
-
* metrics (e.g. a Table
|
|
6
|
-
* stays table-agnostic - the closure comes from the API layer.
|
|
5
|
+
* Builds its subtree at layout time via `resolve(ctx, width)`, so the tree can depend on font
|
|
6
|
+
* metrics and on the width it gets (e.g. a Table fitting its `"auto"` and `fr` columns to the cell
|
|
7
|
+
* content and its own width). The engine stays table-agnostic - the closure comes from the API layer.
|
|
7
8
|
*/
|
|
8
9
|
export class DeferredElement extends PDFElement {
|
|
9
10
|
constructor(resolve) {
|
|
10
11
|
super();
|
|
11
12
|
this.resolve = resolve;
|
|
13
|
+
this.built = new BuiltFromContext();
|
|
12
14
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
/** The subtree for this context and width: built once, and again only when one of them changes. */
|
|
16
|
+
subtree(ctx, width) {
|
|
17
|
+
return this.built.get(ctx, width, () => this.resolve(ctx, width));
|
|
18
|
+
}
|
|
19
|
+
build(ctx, width) {
|
|
20
|
+
this.composed = this.subtree(ctx, width);
|
|
15
21
|
return this.composed;
|
|
16
22
|
}
|
|
17
23
|
calculateLayout(constraints, offset, ctx) {
|
|
18
|
-
return this.build(ctx).calculateLayout(constraints, offset, ctx);
|
|
24
|
+
return this.build(ctx, constraints.maxWidth).calculateLayout(constraints, offset, ctx);
|
|
25
|
+
}
|
|
26
|
+
/** Its subtree is unknown until built, and could hold a `PageBuilder`; until then assume it does. */
|
|
27
|
+
dependsOnPage() {
|
|
28
|
+
return this.composed?.dependsOnPage() ?? true;
|
|
29
|
+
}
|
|
30
|
+
/** Sizes the subtree for this context - the same one a layout under it will draw. */
|
|
31
|
+
computeSize(constraints, ctx) {
|
|
32
|
+
return this.subtree(ctx, constraints.maxWidth).measure(constraints, ctx);
|
|
19
33
|
}
|
|
20
34
|
fragment(maxHeight, width, ctx) {
|
|
21
|
-
const c = this.build(ctx);
|
|
35
|
+
const c = this.build(ctx, width);
|
|
22
36
|
return isFragmentable(c)
|
|
23
37
|
? c.fragment(maxHeight, width, ctx)
|
|
24
38
|
: { fitted: this, remainder: null };
|
|
25
39
|
}
|
|
40
|
+
layoutChildren() {
|
|
41
|
+
return this.composed ? [this.composed] : [];
|
|
42
|
+
}
|
|
26
43
|
getProps() {
|
|
27
44
|
return { composed: this.composed };
|
|
28
45
|
}
|
|
@@ -13,6 +13,9 @@ export declare class ExpandedElement extends FlexiblePDFElement implements Fragm
|
|
|
13
13
|
/** Layout-transparent: the floor is whatever the wrapped child needs. */
|
|
14
14
|
minIntrinsicMain(horizontal: boolean, ctx: LayoutContext): number;
|
|
15
15
|
calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
|
|
16
|
+
protected computeSize(constraints: BoxConstraints, ctx: LayoutContext): Size;
|
|
17
|
+
/** The one sizing path: with an `offset` the child is laid out there, with `null` only measured. */
|
|
18
|
+
private sizeWith;
|
|
16
19
|
/**
|
|
17
20
|
* When the column paginates, a flex region can't "fill" across pages, so it delegates to
|
|
18
21
|
* its child: the child splits and each half is re-wrapped in an Expanded (which then
|
|
@@ -20,7 +23,11 @@ export declare class ExpandedElement extends FlexiblePDFElement implements Fragm
|
|
|
20
23
|
* whole Expanded moves on.
|
|
21
24
|
*/
|
|
22
25
|
fragment(maxHeight: number, width: number, ctx: LayoutContext): FragmentResult;
|
|
26
|
+
hasForcedBreak(): boolean;
|
|
27
|
+
breaksBefore(): boolean;
|
|
28
|
+
breaksAfter(): boolean;
|
|
23
29
|
private cloneWithChild;
|
|
30
|
+
layoutChildren(): readonly PDFElement[];
|
|
24
31
|
getProps(): {
|
|
25
32
|
x: number;
|
|
26
33
|
y: number;
|