@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
|
@@ -31,9 +31,10 @@ export interface FontMetrics {
|
|
|
31
31
|
/** Whether the document draws this code point as colour emoji rather than from the text font - it
|
|
32
32
|
* is missing from that font on purpose, so glyph coverage must not touch it. */
|
|
33
33
|
rendersAsEmoji?(codePoint: number, fontFamily: string, fontStyle: FontStyle): boolean;
|
|
34
|
-
/** Told when a code point had to be removed because no font could draw it
|
|
35
|
-
* object used only for measuring (a test double) has
|
|
36
|
-
|
|
34
|
+
/** Told when a code point had to be removed because no font could draw it - `about` is the element
|
|
35
|
+
* it was removed from. Optional: a metrics object used only for measuring (a test double) has
|
|
36
|
+
* nothing to report to. */
|
|
37
|
+
reportMissingGlyph?(codePoints: number[], about?: object, page?: number): void;
|
|
37
38
|
/** How many glyphs the run will DRAW, when a ligature made that differ from its code-point count.
|
|
38
39
|
* Absent or `undefined` means "the same", which is every Latin run. */
|
|
39
40
|
shapedGlyphCount?(text: string, fontFamily?: string, fontStyle?: FontStyle, ligatures?: boolean): number | undefined;
|
|
@@ -3,11 +3,56 @@ declare global {
|
|
|
3
3
|
getUint24(byteOffset: number, littleEndian?: boolean): number;
|
|
4
4
|
}
|
|
5
5
|
}
|
|
6
|
-
|
|
6
|
+
/** Dots per inch, per axis. */
|
|
7
|
+
export interface Resolution {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
}
|
|
11
|
+
export interface ImageDimensions {
|
|
12
|
+
/** In pixels - what the XObject declares. */
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
/** The resolution the file declares; absent when it declares none. */
|
|
16
|
+
dpi?: Resolution;
|
|
17
|
+
/** How the stored pixels turn upright (EXIF, a JPEG only); absent when they already are. */
|
|
18
|
+
orientation?: Orientation;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* EXIF orientation: 2 mirrored, 3 turned half, 4 flipped, 5 transposed, 6 turned a quarter clockwise,
|
|
22
|
+
* 7 transversed, 8 turned a quarter counterclockwise. 5-8 swap width and height.
|
|
23
|
+
*/
|
|
24
|
+
export type Orientation = 2 | 3 | 4 | 5 | 6 | 7 | 8;
|
|
25
|
+
/**
|
|
26
|
+
* The matrix that turns the image's unit square upright, inside it - PDF's image space, y up, the first
|
|
27
|
+
* stored row at the top. Applied before the placement scales it, so the box stays the box.
|
|
28
|
+
*/
|
|
29
|
+
export declare const ORIENTATION_MATRIX: Record<Orientation, readonly number[]>;
|
|
30
|
+
/** The resolution of a file that declares none: CSS's reference pixel, 1/96 inch. */
|
|
31
|
+
export declare const DEFAULT_DPI = 96;
|
|
32
|
+
/**
|
|
33
|
+
* An image's own size in points, upright: its pixels at the resolution the file declares, else 96 dpi,
|
|
34
|
+
* turned by its orientation.
|
|
35
|
+
*/
|
|
36
|
+
export declare function naturalSize(dims: ImageDimensions): {
|
|
7
37
|
width: number;
|
|
8
38
|
height: number;
|
|
39
|
+
};
|
|
40
|
+
/** Thrown for a JPEG we cannot embed as it is. Says what it is and what to do. */
|
|
41
|
+
export declare class JpegFormatError extends Error {
|
|
42
|
+
constructor(message: string);
|
|
43
|
+
}
|
|
44
|
+
/** The colors an image's samples are in. */
|
|
45
|
+
export type ImageColorSpace = "gray" | "rgb" | "cmyk";
|
|
46
|
+
/** What an image's samples mean: their color space, and whether they are stored inverted. */
|
|
47
|
+
export interface ImageColor {
|
|
48
|
+
space: ImageColorSpace;
|
|
49
|
+
/** Photoshop stores CMYK inverted (an Adobe `APP14` segment). */
|
|
50
|
+
inverted?: boolean;
|
|
9
51
|
}
|
|
10
|
-
|
|
52
|
+
/** The color of a JPEG as it is embedded (`DCTDecode`): the space it was saved in. */
|
|
53
|
+
export declare function jpegColor(bytes: Uint8Array): ImageColor;
|
|
54
|
+
/** Read from the header alone, synchronously - layout can ask it. */
|
|
55
|
+
export declare function readImageDimensions(buffer: Uint8Array): ImageDimensions;
|
|
11
56
|
/**
|
|
12
57
|
* Converts the given image to grayscale and returns its binary data.
|
|
13
58
|
* @param imagePath Path to the input image file.
|
|
@@ -26,19 +71,22 @@ export declare function decodePngToRgbFlate(pngBuffer: Uint8Array): Promise<{
|
|
|
26
71
|
height: number;
|
|
27
72
|
}>;
|
|
28
73
|
export declare function convertImageToGrayscaleBuffer(imagePath: string): Promise<Uint8Array>;
|
|
74
|
+
/** The drawn size of an image in its box, and where it sits there. */
|
|
29
75
|
export interface FitResult {
|
|
30
76
|
width: number;
|
|
31
77
|
height: number;
|
|
32
78
|
offsetX: number;
|
|
33
79
|
offsetY: number;
|
|
34
80
|
}
|
|
35
|
-
|
|
36
|
-
export
|
|
81
|
+
/** Where an image smaller or larger than its box sits in it, per axis: 0 = start, 0.5 = center, 1 = end. */
|
|
82
|
+
export interface FitAlign {
|
|
83
|
+
x: number;
|
|
84
|
+
y: number;
|
|
85
|
+
}
|
|
86
|
+
/** Scaled to fit inside the box, whole image visible. */
|
|
87
|
+
export declare function applyContainFit(imageWidth: number, imageHeight: number, containerWidth: number, containerHeight: number, align?: FitAlign): FitResult;
|
|
88
|
+
/** Scaled to cover the box, the overhang cut off. */
|
|
89
|
+
export declare function applyCoverFit(imageWidth: number, imageHeight: number, containerWidth: number, containerHeight: number, align?: FitAlign): FitResult;
|
|
37
90
|
export declare function applyFillFit(containerWidth: number, containerHeight: number): FitResult;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
height: number;
|
|
41
|
-
offsetX: number;
|
|
42
|
-
offsetY: number;
|
|
43
|
-
};
|
|
44
|
-
export {};
|
|
91
|
+
/** At its own size (`naturalSize`), the overhang cut off. */
|
|
92
|
+
export declare function applyFitNone(imageWidth: number, imageHeight: number, containerWidth: number, containerHeight: number, align?: FitAlign): FitResult;
|
|
@@ -8,31 +8,198 @@ DataView.prototype.getUint24 = function (byteOffset, littleEndian = false) {
|
|
|
8
8
|
const b3 = this.getUint8(byteOffset + 2);
|
|
9
9
|
return littleEndian ? (b3 << 16) | (b2 << 8) | b1 : (b1 << 16) | (b2 << 8) | b3;
|
|
10
10
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
11
|
+
/**
|
|
12
|
+
* The matrix that turns the image's unit square upright, inside it - PDF's image space, y up, the first
|
|
13
|
+
* stored row at the top. Applied before the placement scales it, so the box stays the box.
|
|
14
|
+
*/
|
|
15
|
+
export const ORIENTATION_MATRIX = {
|
|
16
|
+
2: [-1, 0, 0, 1, 1, 0],
|
|
17
|
+
3: [-1, 0, 0, -1, 1, 1],
|
|
18
|
+
4: [1, 0, 0, -1, 0, 1],
|
|
19
|
+
5: [0, -1, -1, 0, 1, 1],
|
|
20
|
+
6: [0, -1, 1, 0, 0, 1],
|
|
21
|
+
7: [0, 1, 1, 0, 0, 0],
|
|
22
|
+
8: [0, 1, -1, 0, 1, 0],
|
|
23
|
+
};
|
|
24
|
+
/** The resolution of a file that declares none: CSS's reference pixel, 1/96 inch. */
|
|
25
|
+
export const DEFAULT_DPI = 96;
|
|
26
|
+
/**
|
|
27
|
+
* An image's own size in points, upright: its pixels at the resolution the file declares, else 96 dpi,
|
|
28
|
+
* turned by its orientation.
|
|
29
|
+
*/
|
|
30
|
+
export function naturalSize(dims) {
|
|
31
|
+
const width = (dims.width * 72) / (dims.dpi?.x ?? DEFAULT_DPI);
|
|
32
|
+
const height = (dims.height * 72) / (dims.dpi?.y ?? DEFAULT_DPI);
|
|
33
|
+
return (dims.orientation ?? 1) >= 5 ? { width: height, height: width } : { width, height };
|
|
34
|
+
}
|
|
35
|
+
/** A resolution only counts if both axes are real numbers above zero; anything else is no declaration. */
|
|
36
|
+
function resolution(x, y) {
|
|
37
|
+
return Number.isFinite(x) && Number.isFinite(y) && x > 0 && y > 0 ? { x, y } : undefined;
|
|
38
|
+
}
|
|
39
|
+
/** PNG `pHYs`: pixels per unit, and only unit 1 (meter) is a physical size - 0 is an aspect ratio. */
|
|
40
|
+
function pngResolution(view) {
|
|
41
|
+
for (let offset = 8; offset + 8 <= view.byteLength;) {
|
|
42
|
+
const length = view.getUint32(offset);
|
|
43
|
+
const type = view.getUint32(offset + 4);
|
|
44
|
+
if (type === 0x49444154)
|
|
45
|
+
return undefined; // IDAT: pHYs must come before it
|
|
46
|
+
if (type === 0x70485973 && length >= 9 && offset + 17 <= view.byteLength) {
|
|
47
|
+
if (view.getUint8(offset + 16) !== 1)
|
|
48
|
+
return undefined;
|
|
49
|
+
return resolution(view.getUint32(offset + 8) * 0.0254, view.getUint32(offset + 12) * 0.0254);
|
|
50
|
+
}
|
|
51
|
+
offset += 12 + length;
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
/** JFIF `APP0`: units 1 = dots per inch, 2 = per centimeter, 0 = an aspect ratio only. */
|
|
56
|
+
function jfifResolution(view, data) {
|
|
57
|
+
if (view.getUint32(data) !== 0x4a464946 || view.getUint8(data + 4) !== 0)
|
|
58
|
+
return undefined; // "JFIF\0"
|
|
59
|
+
const units = view.getUint8(data + 7);
|
|
60
|
+
const scale = units === 1 ? 1 : units === 2 ? 2.54 : 0;
|
|
61
|
+
if (scale === 0)
|
|
62
|
+
return undefined;
|
|
63
|
+
return resolution(view.getUint16(data + 8) * scale, view.getUint16(data + 10) * scale);
|
|
64
|
+
}
|
|
65
|
+
/** The first IFD of a TIFF block (EXIF), as tag → the entry's offset in `view`. */
|
|
66
|
+
function tiffEntries(view, tiff) {
|
|
67
|
+
const le = view.getUint16(tiff) === 0x4949; // "II"
|
|
68
|
+
const ifd = tiff + view.getUint32(tiff + 4, le);
|
|
69
|
+
const entries = new Map();
|
|
70
|
+
const count = view.getUint16(ifd, le);
|
|
71
|
+
for (let i = 0; i < count; i++) {
|
|
72
|
+
const entry = ifd + 2 + i * 12;
|
|
73
|
+
entries.set(view.getUint16(entry, le), entry);
|
|
74
|
+
}
|
|
75
|
+
return { entries, le };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* EXIF `APP1`: XResolution / YResolution (rationals) in ResolutionUnit, 2 = inch (the default), 3 = cm;
|
|
79
|
+
* Orientation 1-8.
|
|
80
|
+
*/
|
|
81
|
+
function exifInfo(view, data) {
|
|
82
|
+
if (view.getUint32(data) !== 0x45786966 || view.getUint16(data + 4) !== 0)
|
|
83
|
+
return undefined; // "Exif\0\0"
|
|
84
|
+
const tiff = data + 6;
|
|
85
|
+
const { entries, le } = tiffEntries(view, tiff);
|
|
86
|
+
const rational = (tag) => {
|
|
87
|
+
const entry = entries.get(tag);
|
|
88
|
+
if (entry === undefined)
|
|
89
|
+
return NaN;
|
|
90
|
+
const at = tiff + view.getUint32(entry + 8, le);
|
|
91
|
+
return view.getUint32(at, le) / view.getUint32(at + 4, le);
|
|
92
|
+
};
|
|
93
|
+
const short = (tag) => {
|
|
94
|
+
const entry = entries.get(tag);
|
|
95
|
+
return entry === undefined ? undefined : view.getUint16(entry + 8, le);
|
|
96
|
+
};
|
|
97
|
+
const unit = short(0x0128) ?? 2;
|
|
98
|
+
const scale = unit === 2 ? 1 : unit === 3 ? 2.54 : 0;
|
|
99
|
+
const dpi = scale === 0 ? undefined : resolution(rational(0x011a) * scale, rational(0x011b) * scale);
|
|
100
|
+
const turn = short(0x0112);
|
|
101
|
+
const orientation = turn !== undefined && turn >= 2 && turn <= 8 ? turn : undefined;
|
|
102
|
+
return { ...(dpi ? { dpi } : {}), ...(orientation ? { orientation } : {}) };
|
|
103
|
+
}
|
|
104
|
+
/** What a header declares is metadata: a malformed declaration is none, never a failed image. */
|
|
105
|
+
function tryRead(read) {
|
|
106
|
+
try {
|
|
107
|
+
return read();
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/** Start-of-frame markers; the rest of 0xFFC0..0xFFCF are tables (DHT C4, JPG C8, DAC CC). */
|
|
114
|
+
const isSof = (marker) => marker >= 0xffc0 &&
|
|
115
|
+
marker <= 0xffcf &&
|
|
116
|
+
marker !== 0xffc4 &&
|
|
117
|
+
marker !== 0xffc8 &&
|
|
118
|
+
marker !== 0xffcc;
|
|
119
|
+
/** Markers without a length: TEM and the restart markers. */
|
|
120
|
+
const isStandalone = (marker) => marker === 0xff01 || (marker >= 0xffd0 && marker <= 0xffd7);
|
|
121
|
+
function jpegHeader(view) {
|
|
122
|
+
// JFIF is the container's own declaration and wins; EXIF is what a camera writes.
|
|
123
|
+
let jfif;
|
|
124
|
+
let exif;
|
|
125
|
+
let adobe = false;
|
|
126
|
+
let offset = 2;
|
|
127
|
+
while (offset + 4 <= view.byteLength) {
|
|
128
|
+
if (view.getUint8(offset + 1) === 0xff) {
|
|
129
|
+
offset++; // a fill byte before the marker
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const marker = view.getUint16(offset);
|
|
133
|
+
offset += 2;
|
|
134
|
+
if (isStandalone(marker))
|
|
135
|
+
continue;
|
|
136
|
+
if (isSof(marker)) {
|
|
137
|
+
const dpi = jfif ?? exif?.dpi;
|
|
138
|
+
const orientation = exif?.orientation;
|
|
139
|
+
return {
|
|
140
|
+
height: view.getUint16(offset + 3),
|
|
141
|
+
width: view.getUint16(offset + 5),
|
|
142
|
+
components: view.getUint8(offset + 7),
|
|
143
|
+
sof: marker,
|
|
144
|
+
adobe,
|
|
145
|
+
...(dpi ? { dpi } : {}),
|
|
146
|
+
...(orientation ? { orientation } : {}),
|
|
147
|
+
};
|
|
29
148
|
}
|
|
149
|
+
if (marker === 0xffe0)
|
|
150
|
+
jfif ?? (jfif = tryRead(() => jfifResolution(view, offset + 2)));
|
|
151
|
+
if (marker === 0xffe1)
|
|
152
|
+
exif ?? (exif = tryRead(() => exifInfo(view, offset + 2)));
|
|
153
|
+
// "Adobe"
|
|
154
|
+
if (marker === 0xffee &&
|
|
155
|
+
view.getUint32(offset + 2) === 0x41646f62 &&
|
|
156
|
+
view.getUint8(offset + 6) === 0x65)
|
|
157
|
+
adobe = true;
|
|
158
|
+
offset += view.getUint16(offset);
|
|
159
|
+
}
|
|
160
|
+
throw new Error("Unsupported image format");
|
|
161
|
+
}
|
|
162
|
+
/** Thrown for a JPEG we cannot embed as it is. Says what it is and what to do. */
|
|
163
|
+
export class JpegFormatError extends Error {
|
|
164
|
+
constructor(message) {
|
|
165
|
+
super(`@jasy/pdf: ${message}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/** Baseline, extended sequential and progressive: the codings a PDF viewer's DCTDecode reads. */
|
|
169
|
+
const DCT_CODINGS = new Set([0xffc0, 0xffc1, 0xffc2]);
|
|
170
|
+
/** The color of a JPEG as it is embedded (`DCTDecode`): the space it was saved in. */
|
|
171
|
+
export function jpegColor(bytes) {
|
|
172
|
+
const header = jpegHeader(new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength));
|
|
173
|
+
if (!DCT_CODINGS.has(header.sof)) {
|
|
174
|
+
throw new JpegFormatError("this JPEG is lossless, hierarchical or arithmetic-coded, which PDF viewers cannot decode. " +
|
|
175
|
+
"Save it again as a baseline or progressive JPEG, or as a PNG.");
|
|
176
|
+
}
|
|
177
|
+
switch (header.components) {
|
|
178
|
+
case 1:
|
|
179
|
+
return { space: "gray" };
|
|
180
|
+
case 3:
|
|
181
|
+
return { space: "rgb" };
|
|
182
|
+
case 4:
|
|
183
|
+
return header.adobe ? { space: "cmyk", inverted: true } : { space: "cmyk" };
|
|
184
|
+
default:
|
|
185
|
+
throw new JpegFormatError(`this JPEG has ${header.components} color channels; only gray, RGB and CMYK are embedded.`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/** Read from the header alone, synchronously - layout can ask it. */
|
|
189
|
+
export function readImageDimensions(buffer) {
|
|
190
|
+
// The view's own window: a `Uint8Array` may be a slice of a larger buffer (a Node `Buffer` usually is).
|
|
191
|
+
const dataView = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
192
|
+
if (dataView.getUint16(0) === 0xffd8) {
|
|
193
|
+
const { width, height, dpi, orientation } = jpegHeader(dataView);
|
|
194
|
+
return { width, height, ...(dpi ? { dpi } : {}), ...(orientation ? { orientation } : {}) };
|
|
30
195
|
}
|
|
31
196
|
// Check for PNG (0x89504E47 is the PNG signature)
|
|
32
197
|
if (dataView.getUint32(0) === 0x89504e47) {
|
|
198
|
+
const dpi = tryRead(() => pngResolution(dataView));
|
|
33
199
|
return {
|
|
34
200
|
width: dataView.getUint32(16, false),
|
|
35
201
|
height: dataView.getUint32(20, false),
|
|
202
|
+
...(dpi ? { dpi } : {}),
|
|
36
203
|
};
|
|
37
204
|
}
|
|
38
205
|
// Check for BMP (0x424D is the BMP signature)
|
|
@@ -122,75 +289,36 @@ export async function convertImageToGrayscaleBuffer(imagePath) {
|
|
|
122
289
|
const grayscaleBuffer = await image.getBuffer(mimeType);
|
|
123
290
|
return grayscaleBuffer;
|
|
124
291
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
let width, height, offsetX, offsetY;
|
|
129
|
-
if (imageAspectRatio > containerAspectRatio) {
|
|
130
|
-
// The images width is bigger than the containers width
|
|
131
|
-
const scaleFactor = containerWidth / imageWidth;
|
|
132
|
-
width = containerWidth;
|
|
133
|
-
height = imageHeight * scaleFactor;
|
|
134
|
-
offsetX = 0;
|
|
135
|
-
offsetY = (containerHeight - height) / 2; // Center vertically
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
// THe images height is bigge rthan the containers height
|
|
139
|
-
const scaleFactor = containerHeight / imageHeight;
|
|
140
|
-
width = imageWidth * scaleFactor;
|
|
141
|
-
height = containerHeight;
|
|
142
|
-
offsetX = (containerWidth - width) / 2; // Center horizontally
|
|
143
|
-
offsetY = 0;
|
|
144
|
-
}
|
|
292
|
+
const CENTER = { x: 0.5, y: 0.5 };
|
|
293
|
+
/** The one place a fitted image is positioned: what is left over (or overhangs) is split by `align`. */
|
|
294
|
+
function placed(width, height, containerWidth, containerHeight, align) {
|
|
145
295
|
return {
|
|
146
296
|
width,
|
|
147
297
|
height,
|
|
148
|
-
offsetX,
|
|
149
|
-
offsetY,
|
|
298
|
+
offsetX: (containerWidth - width) * align.x,
|
|
299
|
+
offsetY: (containerHeight - height) * align.y,
|
|
150
300
|
};
|
|
151
301
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
width = containerWidth;
|
|
168
|
-
height = imageHeight * scaleFactor;
|
|
169
|
-
offsetX = 0;
|
|
170
|
-
offsetY = (containerHeight - height) / 2; // Center vertically
|
|
171
|
-
}
|
|
172
|
-
return {
|
|
173
|
-
width,
|
|
174
|
-
height,
|
|
175
|
-
offsetX,
|
|
176
|
-
offsetY,
|
|
177
|
-
};
|
|
302
|
+
/** Scaled by the width's or the height's factor; that axis takes the box's extent exactly. */
|
|
303
|
+
function scaled(imageWidth, imageHeight, containerWidth, containerHeight, align, byWidthWins) {
|
|
304
|
+
const byWidth = containerWidth / imageWidth;
|
|
305
|
+
const byHeight = containerHeight / imageHeight;
|
|
306
|
+
return byWidthWins(byWidth, byHeight)
|
|
307
|
+
? placed(containerWidth, imageHeight * byWidth, containerWidth, containerHeight, align)
|
|
308
|
+
: placed(imageWidth * byHeight, containerHeight, containerWidth, containerHeight, align);
|
|
309
|
+
}
|
|
310
|
+
/** Scaled to fit inside the box, whole image visible. */
|
|
311
|
+
export function applyContainFit(imageWidth, imageHeight, containerWidth, containerHeight, align = CENTER) {
|
|
312
|
+
return scaled(imageWidth, imageHeight, containerWidth, containerHeight, align, (w, h) => w < h);
|
|
313
|
+
}
|
|
314
|
+
/** Scaled to cover the box, the overhang cut off. */
|
|
315
|
+
export function applyCoverFit(imageWidth, imageHeight, containerWidth, containerHeight, align = CENTER) {
|
|
316
|
+
return scaled(imageWidth, imageHeight, containerWidth, containerHeight, align, (w, h) => w >= h);
|
|
178
317
|
}
|
|
179
318
|
export function applyFillFit(containerWidth, containerHeight) {
|
|
180
|
-
return {
|
|
181
|
-
width: containerWidth,
|
|
182
|
-
height: containerHeight,
|
|
183
|
-
offsetX: 0,
|
|
184
|
-
offsetY: 0,
|
|
185
|
-
};
|
|
319
|
+
return { width: containerWidth, height: containerHeight, offsetX: 0, offsetY: 0 };
|
|
186
320
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
return {
|
|
191
|
-
width: imageWidth, // Hold orignal image size
|
|
192
|
-
height: imageHeight,
|
|
193
|
-
offsetX: offsetX > 0 ? offsetX : 0, // Dont move outside the container...
|
|
194
|
-
offsetY: offsetY > 0 ? offsetY : 0,
|
|
195
|
-
};
|
|
321
|
+
/** At its own size (`naturalSize`), the overhang cut off. */
|
|
322
|
+
export function applyFitNone(imageWidth, imageHeight, containerWidth, containerHeight, align = CENTER) {
|
|
323
|
+
return placed(imageWidth, imageHeight, containerWidth, containerHeight, align);
|
|
196
324
|
}
|
|
@@ -12,6 +12,8 @@ import { DestRegistry } from "./dest-registry.js";
|
|
|
12
12
|
import { AcroFormCollector } from "../forms/acroform.js";
|
|
13
13
|
import type { PDFConfig } from "../renderer/pdf-document-class.js";
|
|
14
14
|
import type { FontMetrics } from "./font-metrics.js";
|
|
15
|
+
import { Diagnostics } from "../common/diagnostics.js";
|
|
16
|
+
import { type ImageColor } from "./image-helper.js";
|
|
15
17
|
interface FontIndexes {
|
|
16
18
|
fontIndex: number;
|
|
17
19
|
resourceIndex: number;
|
|
@@ -44,8 +46,8 @@ export declare class PDFObjectManager implements FontMetrics {
|
|
|
44
46
|
/** Shaped runs, cached face -> feature set -> text. Nested, not one map under a composed key: that
|
|
45
47
|
* key would carry the whole run's text, so every lookup would copy it into a fresh string. */
|
|
46
48
|
private shapeCache;
|
|
47
|
-
/** Bumped
|
|
48
|
-
* family name can mean a different face afterwards. */
|
|
49
|
+
/** Bumped when a face is really ADDED (a repeat registration returns early, untouched); every
|
|
50
|
+
* memoised measurement watches it, since a family name can mean a different face afterwards. */
|
|
49
51
|
fontEpoch: number;
|
|
50
52
|
private shapedOrigins;
|
|
51
53
|
private attachments;
|
|
@@ -78,7 +80,9 @@ export declare class PDFObjectManager implements FontMetrics {
|
|
|
78
80
|
* character went missing instead of a server log nobody reads.
|
|
79
81
|
*/
|
|
80
82
|
private readonly missingGlyphs;
|
|
81
|
-
reportMissingGlyph(codePoints: number[]): void;
|
|
83
|
+
reportMissingGlyph(codePoints: number[], about?: object, page?: number): void;
|
|
84
|
+
/** The render's diagnostics channel (`onDiagnostic`); see `common/diagnostics.ts`. */
|
|
85
|
+
readonly diagnostics: Diagnostics;
|
|
82
86
|
/** The dropped code points, in the order first seen. Empty for a document that hit none. */
|
|
83
87
|
getMissingGlyphs(): number[];
|
|
84
88
|
setOverflowPolicy(policy: OverflowPolicy): void;
|
|
@@ -97,6 +101,11 @@ export declare class PDFObjectManager implements FontMetrics {
|
|
|
97
101
|
* A string INSIDE a content stream needs none of this - the stream is enciphered as a whole.
|
|
98
102
|
*/
|
|
99
103
|
pdfString(text: string): string;
|
|
104
|
+
/**
|
|
105
|
+
* A URI: 7-bit ASCII by definition, so anything else is percent-encoded as UTF-8 - the way a browser
|
|
106
|
+
* writes the link - rather than squeezed into a text encoding.
|
|
107
|
+
*/
|
|
108
|
+
pdfUri(uri: string): string;
|
|
100
109
|
private streamPayload;
|
|
101
110
|
private stream;
|
|
102
111
|
addContentStream(content: string): number;
|
|
@@ -107,7 +116,7 @@ export declare class PDFObjectManager implements FontMetrics {
|
|
|
107
116
|
private getCurrentByteLength;
|
|
108
117
|
setParentObjectNumber(number: number): void;
|
|
109
118
|
getParentObjectNumber(): number;
|
|
110
|
-
registerImage(width: number, height: number, imageType: string, imageData: string, smaskData?: string): number;
|
|
119
|
+
registerImage(width: number, height: number, imageType: string, imageData: string, smaskData?: string, color?: ImageColor): number;
|
|
111
120
|
attachFile(name: string, data: Uint8Array, opts?: {
|
|
112
121
|
relationship?: string;
|
|
113
122
|
mimeType?: string;
|
|
@@ -159,7 +168,7 @@ export declare class PDFObjectManager implements FontMetrics {
|
|
|
159
168
|
*/
|
|
160
169
|
hasGlyph(codePoint: number, fontFamily: string, fontStyle?: FontStyle): boolean;
|
|
161
170
|
/**
|
|
162
|
-
* Whether the document draws this code point through the
|
|
171
|
+
* Whether the document draws this code point through the color-emoji path instead of the text
|
|
163
172
|
* font - a fallback emoji FONT, or an image source (`Document({ emoji })`). Such a code point is
|
|
164
173
|
* missing from the text font ON PURPOSE, so glyph coverage must leave it alone. Mirrors exactly
|
|
165
174
|
* what `getCharWidth` measures it as, or a dropped emoji would have been measured and not drawn.
|
|
@@ -199,7 +208,9 @@ export declare class PDFObjectManager implements FontMetrics {
|
|
|
199
208
|
* `GPOS` tables are unparsed), so it returns zeros - never a wrong value, just no adjustment.
|
|
200
209
|
*/
|
|
201
210
|
/** Kerning between adjacent GLYPHS - what a shaped run needs, and what the backend already holds.
|
|
202
|
-
* Same lookup as the character path; `getKerning` is keyed by glyph id either way.
|
|
211
|
+
* Same lookup as the character path; `getKerning` is keyed by glyph id either way. A pair that
|
|
212
|
+
* touches a space is never kerned, as on the character path: the line breaker measures words and
|
|
213
|
+
* spaces apart, so a pair across a space would be drawn but never measured. */
|
|
203
214
|
getGlyphKernPairs(glyphs: readonly number[], fontFamily: string, fontStyle: FontStyle): number[];
|
|
204
215
|
getKernPairs(text: string, fontFamily: string, fontStyle: FontStyle, ligatures?: boolean): number[];
|
|
205
216
|
/**
|