@jasy/pdf 1.0.0-beta.3 → 1.0.0-beta.5
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/color.d.ts +1 -24
- package/dist/api/color.js +2 -278
- package/dist/api/content.d.ts +10 -5
- package/dist/api/content.js +29 -13
- package/dist/api/descriptor.js +30 -3
- package/dist/api/flow.d.ts +55 -0
- package/dist/api/flow.js +71 -0
- 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/gradient.d.ts +1 -53
- package/dist/api/gradient.js +2 -97
- package/dist/api/index.d.ts +4 -0
- package/dist/api/index.js +3 -0
- package/dist/api/layout.d.ts +6 -0
- package/dist/api/layout.js +9 -3
- 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 +101 -22
- package/dist/api/text.d.ts +8 -8
- package/dist/api/text.js +6 -0
- package/dist/canvas/painter.d.ts +2 -2
- package/dist/canvas/painter.js +2 -2
- package/dist/common/color-input.d.ts +24 -0
- package/dist/common/color-input.js +278 -0
- package/dist/common/diagnostics.d.ts +61 -0
- package/dist/common/diagnostics.js +77 -0
- package/dist/common/gradient.d.ts +53 -0
- package/dist/common/gradient.js +97 -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 +8 -1
- package/dist/elements/container-element.js +49 -17
- 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 +70 -21
- package/dist/elements/image-element.js +173 -52
- package/dist/elements/index.d.ts +3 -0
- package/dist/elements/index.js +3 -0
- 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/column-break-element.d.ts +17 -0
- package/dist/elements/layout/column-break-element.js +26 -0
- 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 +20 -4
- package/dist/elements/layout/deferred-element.js +41 -9
- package/dist/elements/layout/expanded-element.d.ts +11 -0
- package/dist/elements/layout/expanded-element.js +53 -18
- package/dist/elements/layout/flow-element.d.ts +109 -0
- package/dist/elements/layout/flow-element.js +317 -0
- package/dist/elements/layout/keep-together-element.d.ts +8 -18
- package/dist/elements/layout/keep-together-element.js +33 -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 +18 -1
- package/dist/elements/layout/padding-element.js +63 -28
- 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 +13 -2
- package/dist/elements/layout/positioned-element.d.ts +23 -4
- package/dist/elements/layout/positioned-element.js +42 -12
- package/dist/elements/layout/proxy-element.d.ts +47 -0
- package/dist/elements/layout/proxy-element.js +106 -0
- package/dist/elements/layout/region-element.d.ts +53 -0
- package/dist/elements/layout/region-element.js +108 -0
- package/dist/elements/layout/repeating-header-element.d.ts +9 -0
- package/dist/elements/layout/repeating-header-element.js +55 -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 +9 -0
- package/dist/elements/layout/rotated-element.js +33 -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 +105 -16
- package/dist/elements/pdf-element.js +118 -19
- package/dist/elements/rectangle-element.d.ts +22 -2
- package/dist/elements/rectangle-element.js +119 -55
- package/dist/elements/row-element.d.ts +17 -12
- package/dist/elements/row-element.js +89 -9
- package/dist/elements/svg-element.d.ts +30 -6
- package/dist/elements/svg-element.js +91 -26
- package/dist/elements/text-element.d.ts +48 -13
- package/dist/elements/text-element.js +161 -116
- 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 +18 -3
- package/dist/layout/box-constraints.d.ts +17 -0
- package/dist/layout/box-constraints.js +31 -0
- package/dist/layout/collect-images.d.ts +6 -4
- package/dist/layout/collect-images.js +14 -8
- package/dist/layout/flex-item.d.ts +11 -0
- package/dist/layout/flex-item.js +51 -0
- package/dist/layout/fragmentation.d.ts +23 -9
- package/dist/layout/fragmentation.js +62 -19
- package/dist/layout/layout-cache.d.ts +53 -0
- package/dist/layout/layout-cache.js +110 -0
- package/dist/platform/browser-fs.d.ts +1 -2
- package/dist/platform/browser-fs.js +3 -6
- package/dist/platform/node-fs.d.ts +0 -1
- package/dist/platform/node-fs.js +0 -4
- 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/column-break-renderer.d.ts +8 -0
- package/dist/renderer/column-break-renderer.js +15 -0
- 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/emoji-image.js +3 -1
- package/dist/renderer/expanded-renderer.d.ts +2 -1
- package/dist/renderer/expanded-renderer.js +2 -3
- package/dist/renderer/flow-renderer.d.ts +16 -0
- package/dist/renderer/flow-renderer.js +21 -0
- 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 +33 -53
- package/dist/renderer/index.d.ts +2 -0
- package/dist/renderer/index.js +2 -0
- 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 +30 -10
- package/dist/renderer/pdf-backend.d.ts +4 -3
- package/dist/renderer/pdf-backend.js +14 -7
- package/dist/renderer/pdf-document-renderer.d.ts +1 -8
- package/dist/renderer/pdf-document-renderer.js +17 -17
- package/dist/renderer/pdf-renderer.js +20 -11
- package/dist/renderer/positioned-renderer.d.ts +18 -1
- package/dist/renderer/positioned-renderer.js +43 -6
- package/dist/renderer/rectangle-renderer.d.ts +2 -1
- package/dist/renderer/rectangle-renderer.js +18 -8
- 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 +9 -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 +114 -127
- package/dist/svg/gradients.js +5 -0
- package/dist/svg/index.d.ts +1 -1
- package/dist/svg/index.js +36 -6
- package/dist/svg/style.d.ts +2 -0
- package/dist/svg/style.js +12 -1
- 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 +55 -29
- package/dist/text/line-breaker.js +453 -188
- package/dist/text/line-metrics.d.ts +8 -3
- package/dist/text/line-metrics.js +11 -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 +45 -13
- 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 +16 -14
- package/dist/utils/flex-layout.js +427 -217
- package/dist/utils/font-metrics.d.ts +4 -3
- package/dist/utils/image-helper.d.ts +59 -11
- package/dist/utils/image-helper.js +209 -81
- package/dist/utils/pdf-object-manager.d.ts +17 -6
- package/dist/utils/pdf-object-manager.js +70 -20
- package/dist/utils/renderer-registry.d.ts +49 -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 +30 -6
- 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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Radii } from "../ir/display-list.js";
|
|
2
|
+
import { type FitAlign, type ImageDimensions } from "../utils/image-helper.js";
|
|
2
3
|
import { BoxConstraints, Offset, Size } from "../layout/box-constraints.js";
|
|
3
4
|
import { LayoutContext, SizedPDFElement } from "./pdf-element.js";
|
|
4
5
|
export declare enum BoxFit {
|
|
@@ -8,28 +9,33 @@ export declare enum BoxFit {
|
|
|
8
9
|
fill = "FILL"
|
|
9
10
|
}
|
|
10
11
|
export declare abstract class CustomImage {
|
|
11
|
-
|
|
12
|
+
/** Loads the image. `fresh`: a new render begins - a load that failed in an earlier one is tried again. */
|
|
13
|
+
abstract init(fresh?: boolean): void | Promise<void>;
|
|
12
14
|
abstract getImageType(): string | Promise<string>;
|
|
13
15
|
abstract getFileData(): string | Promise<string>;
|
|
14
|
-
abstract getImageDimensions(): Promise<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
abstract getImageDimensions(): Promise<ImageDimensions>;
|
|
17
|
+
/**
|
|
18
|
+
* Its dimensions if its bytes are already in hand (bytes, a local file), else `undefined` - then the
|
|
19
|
+
* async pass before layout fetches them. Reading a header from memory is all this may do: layout
|
|
20
|
+
* asks it, so an image built during layout (in a `PageBuilder`, a table cell) knows its size too.
|
|
21
|
+
*/
|
|
22
|
+
dimensionsNow(): ImageDimensions | undefined;
|
|
18
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* An image from a file path (Node). The file is read HERE, when the image is named - like an SVG or a
|
|
26
|
+
* font file - so a wrong path fails on the line that named it, and layout never waits on a disk.
|
|
27
|
+
*/
|
|
19
28
|
export declare class CustomLocalImage extends CustomImage {
|
|
20
29
|
private imagePath;
|
|
21
30
|
private fileBuffer;
|
|
22
31
|
private fileRawData;
|
|
23
32
|
private dims?;
|
|
24
33
|
constructor(imagePath: string);
|
|
25
|
-
init():
|
|
34
|
+
init(): void;
|
|
26
35
|
getImageType(): Promise<string>;
|
|
27
|
-
|
|
36
|
+
dimensionsNow(): ImageDimensions | undefined;
|
|
28
37
|
getFileData(): string;
|
|
29
|
-
getImageDimensions(): Promise<
|
|
30
|
-
width: number;
|
|
31
|
-
height: number;
|
|
32
|
-
}>;
|
|
38
|
+
getImageDimensions(): Promise<ImageDimensions>;
|
|
33
39
|
}
|
|
34
40
|
/**
|
|
35
41
|
* An image straight from raw bytes (a browser upload / fetch, no filesystem). The PDF filter is sniffed
|
|
@@ -43,10 +49,38 @@ export declare class CustomBytesImage extends CustomImage {
|
|
|
43
49
|
init(): Promise<void>;
|
|
44
50
|
getImageType(): Promise<string>;
|
|
45
51
|
getFileData(): string;
|
|
46
|
-
getImageDimensions(): Promise<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
getImageDimensions(): Promise<ImageDimensions>;
|
|
53
|
+
dimensionsNow(): ImageDimensions | undefined;
|
|
54
|
+
}
|
|
55
|
+
/** Thrown when an image could not be fetched or is not one we can embed. Always says which URL. */
|
|
56
|
+
export declare class ImageUrlError extends Error {
|
|
57
|
+
constructor(message: string);
|
|
58
|
+
}
|
|
59
|
+
/** How long an image from a URL may take, and how large it may be - a raster image and an SVG alike. */
|
|
60
|
+
export declare const IMAGE_FETCH_LIMITS: {
|
|
61
|
+
timeoutMs: number;
|
|
62
|
+
maxBytes: number;
|
|
63
|
+
};
|
|
64
|
+
/** Whether an image source string is an address to fetch rather than a file path. */
|
|
65
|
+
export declare const isImageUrl: (src: string) => boolean;
|
|
66
|
+
/**
|
|
67
|
+
* An image from a URL - `http(s):` or a `data:` URL - fetched in the async pass before layout, with
|
|
68
|
+
* the same timeout and size ceiling as a font. What it IS comes from its bytes, never from the
|
|
69
|
+
* address, so `.../photo?id=7` works. A dead link stops the render with an `ImageUrlError` naming it.
|
|
70
|
+
*/
|
|
71
|
+
export declare class CustomUrlImage extends CustomImage {
|
|
72
|
+
private readonly url;
|
|
73
|
+
private loading?;
|
|
74
|
+
private loaded?;
|
|
75
|
+
private failed;
|
|
76
|
+
constructor(url: string);
|
|
77
|
+
init(fresh?: boolean): Promise<void>;
|
|
78
|
+
private load;
|
|
79
|
+
private image;
|
|
80
|
+
getImageType(): Promise<string>;
|
|
81
|
+
getFileData(): string;
|
|
82
|
+
getImageDimensions(): Promise<ImageDimensions>;
|
|
83
|
+
dimensionsNow(): ImageDimensions | undefined;
|
|
50
84
|
}
|
|
51
85
|
interface ImageElementParams {
|
|
52
86
|
image: CustomImage;
|
|
@@ -67,9 +101,12 @@ interface ImageElementParams {
|
|
|
67
101
|
maxHeightFactor?: number;
|
|
68
102
|
/** width / height; overrides the image's OWN ratio (CSS `aspect-ratio`), `fit` then places it. */
|
|
69
103
|
aspectRatio?: number;
|
|
104
|
+
/** How the image fills its box (CSS `object-fit`); `fill` by default, as in CSS. */
|
|
70
105
|
fit?: BoxFit;
|
|
71
106
|
/** Corner radius in points - one number or per corner; rounds the image box (0 = sharp, default). */
|
|
72
107
|
radius?: number | Radii;
|
|
108
|
+
/** Where the image sits in its box when `fit` leaves space or cuts it: 0 = start, 1 = end per axis. */
|
|
109
|
+
align?: FitAlign;
|
|
73
110
|
/** Alternate text for accessibility (tagged PDF). With `alt` the image is a Figure; without, decoration. */
|
|
74
111
|
alt?: string;
|
|
75
112
|
}
|
|
@@ -79,21 +116,32 @@ export declare class ImageElement extends SizedPDFElement {
|
|
|
79
116
|
private heightFactor?;
|
|
80
117
|
private fit;
|
|
81
118
|
private radius;
|
|
119
|
+
private align?;
|
|
82
120
|
private readonly alt?;
|
|
83
121
|
private requested;
|
|
84
122
|
/** min/max/aspectRatio, kept as given so every layout pass re-resolves them. */
|
|
85
123
|
private sizing;
|
|
86
124
|
private intrinsic?;
|
|
87
|
-
constructor({ image, width, height, widthFactor, heightFactor, fit, radius, alt, ...sizing }: ImageElementParams);
|
|
125
|
+
constructor({ image, width, height, widthFactor, heightFactor, fit, radius, align, alt, ...sizing }: ImageElementParams);
|
|
88
126
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
127
|
+
* Fetches what layout cannot wait for (an image from a URL) and records its own size. Runs in the
|
|
128
|
+
* async pass before layout; an image whose bytes are in hand has nothing to fetch. Best-effort: a
|
|
129
|
+
* failed fetch leaves the size unresolved (the renderer surfaces the real error) and the image keeps
|
|
130
|
+
* whatever explicit size it was given.
|
|
93
131
|
*/
|
|
94
132
|
resolveIntrinsicSize(): Promise<void>;
|
|
133
|
+
/** Its own size in points: known now, or resolved by the pass before layout; `undefined` if neither. */
|
|
134
|
+
private ownSize;
|
|
95
135
|
relativeSizeFactor(horizontal: boolean): number | undefined;
|
|
96
|
-
calculateLayout(constraints: BoxConstraints, offset: Offset,
|
|
136
|
+
calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
|
|
137
|
+
protected computeSize(constraints: BoxConstraints): Size;
|
|
138
|
+
/** Its size was needed from the file and the file had not been read: say so, rather than draw it wrong. */
|
|
139
|
+
private reportUnsized;
|
|
140
|
+
/** Its size under these constraints - the one computation both `measure` and layout use. `guessed`:
|
|
141
|
+
* a side that would have followed the file's own size did not, because that size was unknown. */
|
|
142
|
+
private sizeFor;
|
|
143
|
+
/** The image's own extent on one axis, clamped by its min/max; unknown before the pre-pass. */
|
|
144
|
+
private ownExtent;
|
|
97
145
|
getProps(): {
|
|
98
146
|
x: number;
|
|
99
147
|
y: number;
|
|
@@ -102,6 +150,7 @@ export declare class ImageElement extends SizedPDFElement {
|
|
|
102
150
|
image: CustomImage;
|
|
103
151
|
fit: BoxFit;
|
|
104
152
|
radius: number | Radii;
|
|
153
|
+
align: FitAlign | undefined;
|
|
105
154
|
alt: string | undefined;
|
|
106
155
|
};
|
|
107
156
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readImageDimensions, naturalSize, } from "../utils/image-helper.js";
|
|
2
2
|
import { latin1FromBytes } from "../utils/bytes.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { readFileBytes } from "../platform/node-fs.js";
|
|
4
|
+
import { fetchBytes } from "../utils/fetch-bytes.js";
|
|
5
|
+
import { extentSpecs, resolveSize, sizeAtOwnRatio, } from "../layout/box-constraints.js";
|
|
5
6
|
import { SizedPDFElement } from "./pdf-element.js";
|
|
7
|
+
import { diagnose, pageOf } from "../common/diagnostics.js";
|
|
6
8
|
// path.extname without node:path (browser-safe): the substring from the last dot, if it sits after the
|
|
7
9
|
// last slash (so a dot in a directory name does not count). Enough for image file extensions.
|
|
8
10
|
function extname(p) {
|
|
@@ -18,23 +20,41 @@ export var BoxFit;
|
|
|
18
20
|
BoxFit["fill"] = "FILL";
|
|
19
21
|
})(BoxFit || (BoxFit = {}));
|
|
20
22
|
export class CustomImage {
|
|
23
|
+
/**
|
|
24
|
+
* Its dimensions if its bytes are already in hand (bytes, a local file), else `undefined` - then the
|
|
25
|
+
* async pass before layout fetches them. Reading a header from memory is all this may do: layout
|
|
26
|
+
* asks it, so an image built during layout (in a `PageBuilder`, a table cell) knows its size too.
|
|
27
|
+
*/
|
|
28
|
+
dimensionsNow() {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
21
31
|
}
|
|
32
|
+
/** Dimensions read from bytes in hand; a format we cannot read is `undefined` here - the render names it. */
|
|
33
|
+
function headerOf(bytes) {
|
|
34
|
+
try {
|
|
35
|
+
return readImageDimensions(bytes);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* An image from a file path (Node). The file is read HERE, when the image is named - like an SVG or a
|
|
43
|
+
* font file - so a wrong path fails on the line that named it, and layout never waits on a disk.
|
|
44
|
+
*/
|
|
22
45
|
export class CustomLocalImage extends CustomImage {
|
|
23
46
|
constructor(imagePath) {
|
|
24
47
|
super();
|
|
25
48
|
this.imagePath = imagePath;
|
|
26
|
-
}
|
|
27
|
-
async init() {
|
|
28
|
-
if (this.fileBuffer)
|
|
29
|
-
return; // idempotent: the pre-layout size pass and the renderer both call init()
|
|
30
49
|
try {
|
|
31
|
-
|
|
32
|
-
await this.loadImage(this.imagePath);
|
|
50
|
+
this.fileBuffer = readFileBytes(imagePath);
|
|
33
51
|
}
|
|
34
|
-
catch (
|
|
35
|
-
|
|
52
|
+
catch (e) {
|
|
53
|
+
throw new Error(`@jasy/pdf: could not read the image at ${imagePath}: ${String(e?.message ?? e)}`);
|
|
36
54
|
}
|
|
55
|
+
this.fileRawData = latin1FromBytes(this.fileBuffer);
|
|
37
56
|
}
|
|
57
|
+
init() { }
|
|
38
58
|
async getImageType() {
|
|
39
59
|
const ext = extname(this.imagePath).toLowerCase();
|
|
40
60
|
switch (ext) {
|
|
@@ -51,23 +71,15 @@ export class CustomLocalImage extends CustomImage {
|
|
|
51
71
|
throw new Error(`Unsupported image format: ${ext}`);
|
|
52
72
|
}
|
|
53
73
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
this.fileBuffer = result;
|
|
58
|
-
this.fileRawData = latin1FromBytes(result);
|
|
59
|
-
return result;
|
|
74
|
+
dimensionsNow() {
|
|
75
|
+
this.dims ?? (this.dims = headerOf(this.fileBuffer));
|
|
76
|
+
return this.dims;
|
|
60
77
|
}
|
|
61
78
|
getFileData() {
|
|
62
79
|
return this.fileRawData;
|
|
63
80
|
}
|
|
64
81
|
async getImageDimensions() {
|
|
65
|
-
|
|
66
|
-
return this.dims; // reuse: the pre-layout size pass and the renderer both ask
|
|
67
|
-
if (!this.fileBuffer) {
|
|
68
|
-
throw new Error("You must first call the `loadAndConvertImage` method");
|
|
69
|
-
}
|
|
70
|
-
this.dims = await getImageDimensions(this.fileBuffer);
|
|
82
|
+
this.dims ?? (this.dims = readImageDimensions(this.fileBuffer)); // reuse across layout + render
|
|
71
83
|
return this.dims;
|
|
72
84
|
}
|
|
73
85
|
}
|
|
@@ -94,13 +106,77 @@ export class CustomBytesImage extends CustomImage {
|
|
|
94
106
|
return this.fileRawData;
|
|
95
107
|
}
|
|
96
108
|
async getImageDimensions() {
|
|
97
|
-
|
|
98
|
-
this.dims = await getImageDimensions(this.bytes); // reuse across pre-pass + render
|
|
109
|
+
this.dims ?? (this.dims = readImageDimensions(this.bytes)); // reuse across layout + render
|
|
99
110
|
return this.dims;
|
|
100
111
|
}
|
|
112
|
+
dimensionsNow() {
|
|
113
|
+
this.dims ?? (this.dims = headerOf(this.bytes));
|
|
114
|
+
return this.dims;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** Thrown when an image could not be fetched or is not one we can embed. Always says which URL. */
|
|
118
|
+
export class ImageUrlError extends Error {
|
|
119
|
+
constructor(message) {
|
|
120
|
+
super(`@jasy/pdf: ${message}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/** How long an image from a URL may take, and how large it may be - a raster image and an SVG alike. */
|
|
124
|
+
export const IMAGE_FETCH_LIMITS = { timeoutMs: 15000, maxBytes: 32 * 1024 * 1024 };
|
|
125
|
+
/** Whether an image source string is an address to fetch rather than a file path. */
|
|
126
|
+
export const isImageUrl = (src) => /^(https?:|data:)/i.test(src.trim());
|
|
127
|
+
/**
|
|
128
|
+
* An image from a URL - `http(s):` or a `data:` URL - fetched in the async pass before layout, with
|
|
129
|
+
* the same timeout and size ceiling as a font. What it IS comes from its bytes, never from the
|
|
130
|
+
* address, so `.../photo?id=7` works. A dead link stops the render with an `ImageUrlError` naming it.
|
|
131
|
+
*/
|
|
132
|
+
export class CustomUrlImage extends CustomImage {
|
|
133
|
+
constructor(url) {
|
|
134
|
+
super();
|
|
135
|
+
this.url = url;
|
|
136
|
+
this.failed = false;
|
|
137
|
+
}
|
|
138
|
+
// One fetch per render, however often it is asked: the pre-layout size pass and the renderer both
|
|
139
|
+
// call init(). A failure sticks for that render only - the next one asks again (`fresh`).
|
|
140
|
+
async init(fresh = false) {
|
|
141
|
+
if (fresh && this.failed)
|
|
142
|
+
this.loading = undefined;
|
|
143
|
+
this.loading ?? (this.loading = this.load().then((image) => ((this.failed = false), image), (e) => {
|
|
144
|
+
this.failed = true;
|
|
145
|
+
throw e;
|
|
146
|
+
}));
|
|
147
|
+
this.loaded = await this.loading;
|
|
148
|
+
}
|
|
149
|
+
async load() {
|
|
150
|
+
const bytes = await fetchBytes(this.url, "image", ImageUrlError, IMAGE_FETCH_LIMITS);
|
|
151
|
+
const jpeg = bytes[0] === 0xff && bytes[1] === 0xd8;
|
|
152
|
+
const png = bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4e && bytes[3] === 0x47;
|
|
153
|
+
if (!jpeg && !png) {
|
|
154
|
+
// An error page answers with a 200 often enough to name it rather than fail deep inside.
|
|
155
|
+
const looksLikeMarkup = new TextDecoder().decode(bytes.subarray(0, 64)).trimStart()[0] === "<";
|
|
156
|
+
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`);
|
|
157
|
+
}
|
|
158
|
+
return new CustomBytesImage(bytes);
|
|
159
|
+
}
|
|
160
|
+
image() {
|
|
161
|
+
if (!this.loaded)
|
|
162
|
+
throw new Error("@jasy/pdf: an image was measured before it was loaded");
|
|
163
|
+
return this.loaded;
|
|
164
|
+
}
|
|
165
|
+
getImageType() {
|
|
166
|
+
return this.image().getImageType();
|
|
167
|
+
}
|
|
168
|
+
getFileData() {
|
|
169
|
+
return this.image().getFileData();
|
|
170
|
+
}
|
|
171
|
+
getImageDimensions() {
|
|
172
|
+
return this.image().getImageDimensions();
|
|
173
|
+
}
|
|
174
|
+
dimensionsNow() {
|
|
175
|
+
return this.loaded?.dimensionsNow();
|
|
176
|
+
}
|
|
101
177
|
}
|
|
102
178
|
export class ImageElement extends SizedPDFElement {
|
|
103
|
-
constructor({ image, width, height, widthFactor, heightFactor, fit = BoxFit.
|
|
179
|
+
constructor({ image, width, height, widthFactor, heightFactor, fit = BoxFit.fill, radius, align, alt, ...sizing }) {
|
|
104
180
|
// Seed this.width/height from the request so getProps() reflects it before layout; calculateLayout
|
|
105
181
|
// then reads `requested` (never the mutated fields) and overwrites these with the laid-out size.
|
|
106
182
|
super({ x: 0, y: 0, width });
|
|
@@ -112,29 +188,68 @@ export class ImageElement extends SizedPDFElement {
|
|
|
112
188
|
this.sizing = sizing;
|
|
113
189
|
this.fit = fit;
|
|
114
190
|
this.radius = radius ?? 0;
|
|
191
|
+
this.align = align;
|
|
115
192
|
this.alt = alt;
|
|
116
193
|
}
|
|
117
194
|
/**
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
195
|
+
* Fetches what layout cannot wait for (an image from a URL) and records its own size. Runs in the
|
|
196
|
+
* async pass before layout; an image whose bytes are in hand has nothing to fetch. Best-effort: a
|
|
197
|
+
* failed fetch leaves the size unresolved (the renderer surfaces the real error) and the image keeps
|
|
198
|
+
* whatever explicit size it was given.
|
|
122
199
|
*/
|
|
123
200
|
async resolveIntrinsicSize() {
|
|
201
|
+
if (this.ownSize())
|
|
202
|
+
return;
|
|
124
203
|
try {
|
|
125
|
-
await this.image.init();
|
|
126
|
-
this.intrinsic = await this.image.getImageDimensions();
|
|
204
|
+
await this.image.init(true); // a render begins here
|
|
205
|
+
this.intrinsic = naturalSize(await this.image.getImageDimensions());
|
|
127
206
|
}
|
|
128
207
|
catch {
|
|
129
208
|
// leave intrinsic undefined - aspect derivation simply doesn't fire
|
|
130
209
|
}
|
|
131
210
|
}
|
|
211
|
+
/** Its own size in points: known now, or resolved by the pass before layout; `undefined` if neither. */
|
|
212
|
+
ownSize() {
|
|
213
|
+
if (!this.intrinsic) {
|
|
214
|
+
const dims = this.image.dimensionsNow();
|
|
215
|
+
if (dims)
|
|
216
|
+
this.intrinsic = naturalSize(dims);
|
|
217
|
+
}
|
|
218
|
+
return this.intrinsic;
|
|
219
|
+
}
|
|
132
220
|
relativeSizeFactor(horizontal) {
|
|
133
221
|
return horizontal ? this.widthFactor : this.heightFactor;
|
|
134
222
|
}
|
|
135
|
-
calculateLayout(constraints, offset,
|
|
223
|
+
calculateLayout(constraints, offset, ctx) {
|
|
224
|
+
const size = this.sizeFor(constraints);
|
|
225
|
+
if (size.guessed)
|
|
226
|
+
this.reportUnsized(ctx);
|
|
136
227
|
this.x = offset.x;
|
|
137
228
|
this.y = offset.y;
|
|
229
|
+
// Kept as resolved, `undefined` included: the renderer reads an unset axis differently from 0.
|
|
230
|
+
this.width = size.width;
|
|
231
|
+
this.height = size.height;
|
|
232
|
+
return { width: size.width ?? 0, height: size.height ?? 0 };
|
|
233
|
+
}
|
|
234
|
+
computeSize(constraints) {
|
|
235
|
+
const size = this.sizeFor(constraints);
|
|
236
|
+
return { width: size.width ?? 0, height: size.height ?? 0 };
|
|
237
|
+
}
|
|
238
|
+
/** Its size was needed from the file and the file had not been read: say so, rather than draw it wrong. */
|
|
239
|
+
reportUnsized(ctx) {
|
|
240
|
+
diagnose(ctx.diagnostics, {
|
|
241
|
+
code: "image-unsized",
|
|
242
|
+
severity: "warning",
|
|
243
|
+
message: "An image's own size was not known when it was laid out - an image from a URL that is first " +
|
|
244
|
+
"built in a PageBuilder cannot be fetched before layout. Its open side fills what it is " +
|
|
245
|
+
"offered. Give it a width and a height, or create it outside the PageBuilder.",
|
|
246
|
+
about: this,
|
|
247
|
+
page: pageOf(ctx),
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
/** Its size under these constraints - the one computation both `measure` and layout use. `guessed`:
|
|
251
|
+
* a side that would have followed the file's own size did not, because that size was unknown. */
|
|
252
|
+
sizeFor(constraints) {
|
|
138
253
|
// Relative sizing: a fixed size or a fraction of the offered box (fraction only in a bounded axis).
|
|
139
254
|
// Read from `requested` (not this.width/height, which we overwrite below) so every pass re-resolves.
|
|
140
255
|
const specs = extentSpecs({
|
|
@@ -144,31 +259,36 @@ export class ImageElement extends SizedPDFElement {
|
|
|
144
259
|
widthFactor: this.widthFactor,
|
|
145
260
|
heightFactor: this.heightFactor,
|
|
146
261
|
});
|
|
147
|
-
// An explicit `aspectRatio` wins over the image's own
|
|
148
|
-
// box with NEITHER axis pinned. Left to the intrinsic ratio that would change what an unsized image
|
|
149
|
-
// has always done here (fill both axes and let `fit` sort it out).
|
|
262
|
+
// An explicit `aspectRatio` wins over the image's own.
|
|
150
263
|
const resolved = resolveSize(specs.width, specs.height, this.sizing.aspectRatio, constraints);
|
|
151
264
|
let w = resolved.width;
|
|
152
265
|
let h = resolved.height;
|
|
153
266
|
const bounds = resolved.constraints;
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
if (this.sizing.aspectRatio === undefined &&
|
|
157
|
-
|
|
158
|
-
this.intrinsic.width > 0 &&
|
|
159
|
-
this.intrinsic.height > 0) {
|
|
160
|
-
const ratio = this.intrinsic.width / this.intrinsic.height;
|
|
161
|
-
if (w !== undefined && h === undefined)
|
|
162
|
-
h = bounds.constrainHeight(w / ratio);
|
|
163
|
-
else if (h !== undefined && w === undefined)
|
|
164
|
-
w = bounds.constrainWidth(h * ratio);
|
|
267
|
+
// Its own ratio fills an open axis, once the pre-pass resolved its own size.
|
|
268
|
+
const own = this.ownSize();
|
|
269
|
+
if (this.sizing.aspectRatio === undefined && own && own.width > 0 && own.height > 0) {
|
|
270
|
+
({ width: w, height: h } = sizeAtOwnRatio(w, h, own, bounds));
|
|
165
271
|
}
|
|
166
|
-
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
272
|
+
const guessed = own === undefined && (w === undefined || h === undefined);
|
|
273
|
+
// Still unpinned (no intrinsic size known): a bounded axis fills; an unbounded one takes the image's
|
|
274
|
+
// own extent (`naturalSize`). Read from `requested`, never the mutated this.width/height.
|
|
275
|
+
const width = w ??
|
|
276
|
+
(bounds.hasBoundedWidth
|
|
277
|
+
? bounds.maxWidth
|
|
278
|
+
: (this.requested.width ?? this.ownExtent(bounds, "width")));
|
|
279
|
+
const height = h ??
|
|
280
|
+
(bounds.hasBoundedHeight
|
|
281
|
+
? bounds.maxHeight
|
|
282
|
+
: (this.requested.height ?? this.ownExtent(bounds, "height")));
|
|
170
283
|
// Top-left coordinates; the fit logic (renderer) and the Y-flip (seam) run later.
|
|
171
|
-
return { width
|
|
284
|
+
return { width, height, guessed };
|
|
285
|
+
}
|
|
286
|
+
/** The image's own extent on one axis, clamped by its min/max; unknown before the pre-pass. */
|
|
287
|
+
ownExtent(bounds, axis) {
|
|
288
|
+
const own = this.ownSize()?.[axis];
|
|
289
|
+
if (own === undefined)
|
|
290
|
+
return undefined;
|
|
291
|
+
return axis === "width" ? bounds.constrainWidth(own) : bounds.constrainHeight(own);
|
|
172
292
|
}
|
|
173
293
|
getProps() {
|
|
174
294
|
return {
|
|
@@ -179,6 +299,7 @@ export class ImageElement extends SizedPDFElement {
|
|
|
179
299
|
image: this.image,
|
|
180
300
|
fit: this.fit,
|
|
181
301
|
radius: this.radius,
|
|
302
|
+
align: this.align,
|
|
182
303
|
alt: this.alt,
|
|
183
304
|
};
|
|
184
305
|
}
|
package/dist/elements/index.d.ts
CHANGED
|
@@ -20,6 +20,9 @@ export * from "./layout/link-element.js";
|
|
|
20
20
|
export * from "./layout/bookmark-element.js";
|
|
21
21
|
export * from "./layout/anchor-element.js";
|
|
22
22
|
export * from "./layout/page-builder-element.js";
|
|
23
|
+
export * from "./layout/flow-element.js";
|
|
24
|
+
export * from "./layout/region-element.js";
|
|
25
|
+
export * from "./layout/column-break-element.js";
|
|
23
26
|
export * from "./layout/rotated-element.js";
|
|
24
27
|
export * from "./layout/rotated-box-element.js";
|
|
25
28
|
export * from "./line-element.js";
|
package/dist/elements/index.js
CHANGED
|
@@ -20,6 +20,9 @@ export * from "./layout/link-element.js";
|
|
|
20
20
|
export * from "./layout/bookmark-element.js";
|
|
21
21
|
export * from "./layout/anchor-element.js";
|
|
22
22
|
export * from "./layout/page-builder-element.js";
|
|
23
|
+
export * from "./layout/flow-element.js";
|
|
24
|
+
export * from "./layout/region-element.js";
|
|
25
|
+
export * from "./layout/column-break-element.js";
|
|
23
26
|
export * from "./layout/rotated-element.js";
|
|
24
27
|
export * from "./layout/rotated-box-element.js";
|
|
25
28
|
export * from "./line-element.js";
|
|
@@ -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
|
}
|