@jasy/pdf 1.0.0-alpha.2 → 1.0.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +24 -14
- package/dist/api/args.d.ts +1 -1
- package/dist/api/args.js +2 -5
- package/dist/api/color.d.ts +4 -4
- package/dist/api/color.js +11 -17
- package/dist/api/content.d.ts +8 -8
- package/dist/api/content.js +23 -24
- package/dist/api/descriptor.d.ts +2 -2
- package/dist/api/descriptor.js +75 -31
- package/dist/api/index.d.ts +8 -8
- package/dist/api/index.js +8 -24
- package/dist/api/insets.js +4 -8
- package/dist/api/layout.d.ts +18 -16
- package/dist/api/layout.js +41 -52
- package/dist/api/structure.d.ts +65 -13
- package/dist/api/structure.js +140 -88
- package/dist/api/table.d.ts +5 -5
- package/dist/api/table.js +28 -24
- package/dist/api/text.d.ts +27 -2
- package/dist/api/text.js +45 -27
- package/dist/assets/font-data.d.ts +2 -0
- package/dist/assets/font-data.js +21 -0
- package/dist/assets/font-data.ts +37 -0
- package/dist/common/color.js +1 -5
- package/dist/constants/page-sizes.js +3 -6
- package/dist/constants/pdf-parts.js +1 -4
- package/dist/crypto/security-handler.d.ts +46 -0
- package/dist/crypto/security-handler.js +129 -0
- package/dist/crypto/webcrypto.d.ts +11 -0
- package/dist/crypto/webcrypto.js +62 -0
- package/dist/elements/container-element.d.ts +4 -4
- package/dist/elements/container-element.js +9 -13
- package/dist/elements/image-element.d.ts +18 -2
- package/dist/elements/image-element.js +81 -105
- package/dist/elements/index.d.ts +12 -11
- package/dist/elements/index.js +12 -29
- package/dist/elements/layout/default-text-style-element.d.ts +30 -0
- package/dist/elements/layout/default-text-style-element.js +47 -0
- package/dist/elements/layout/deferred-element.d.ts +3 -3
- package/dist/elements/layout/deferred-element.js +4 -8
- package/dist/elements/layout/expanded-element.d.ts +3 -3
- package/dist/elements/layout/expanded-element.js +10 -14
- package/dist/elements/layout/padding-element.d.ts +3 -3
- package/dist/elements/layout/padding-element.js +9 -14
- package/dist/elements/layout/positioned-element.d.ts +17 -4
- package/dist/elements/layout/positioned-element.js +29 -25
- package/dist/elements/layout/repeating-header-element.d.ts +3 -3
- package/dist/elements/layout/repeating-header-element.js +8 -12
- package/dist/elements/layout/sized-container-element.d.ts +2 -2
- package/dist/elements/layout/sized-container-element.js +6 -11
- package/dist/elements/line-element.d.ts +3 -3
- package/dist/elements/line-element.js +5 -10
- package/dist/elements/page-element.d.ts +8 -6
- package/dist/elements/page-element.js +20 -23
- package/dist/elements/pdf-document-element.d.ts +10 -4
- package/dist/elements/pdf-document-element.js +11 -10
- package/dist/elements/pdf-element.d.ts +12 -3
- package/dist/elements/pdf-element.js +10 -19
- package/dist/elements/rectangle-element.d.ts +5 -5
- package/dist/elements/rectangle-element.js +19 -25
- package/dist/elements/row-element.d.ts +3 -3
- package/dist/elements/row-element.js +7 -11
- package/dist/elements/text-element.d.ts +37 -11
- package/dist/elements/text-element.js +64 -39
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -19
- package/dist/ir/display-list.d.ts +4 -2
- package/dist/ir/display-list.js +1 -2
- package/dist/layout/box-constraints.js +2 -6
- package/dist/layout/fragmentation.d.ts +8 -1
- package/dist/layout/fragmentation.js +22 -10
- package/dist/platform/browser-fs.d.ts +2 -0
- package/dist/platform/browser-fs.js +9 -0
- package/dist/platform/browser-image.d.ts +5 -0
- package/dist/platform/browser-image.js +13 -0
- package/dist/platform/node-fs.d.ts +2 -0
- package/dist/platform/node-fs.js +10 -0
- package/dist/platform/node-image.d.ts +5 -0
- package/dist/platform/node-image.js +9 -0
- package/dist/renderer/container-renderer.d.ts +3 -3
- package/dist/renderer/container-renderer.js +12 -27
- package/dist/renderer/default-text-style-renderer.d.ts +6 -0
- package/dist/renderer/default-text-style-renderer.js +10 -0
- package/dist/renderer/deferred-renderer.d.ts +3 -3
- package/dist/renderer/deferred-renderer.js +8 -23
- package/dist/renderer/expanded-renderer.d.ts +3 -3
- package/dist/renderer/expanded-renderer.js +6 -21
- package/dist/renderer/image-renderer.d.ts +3 -3
- package/dist/renderer/image-renderer.js +77 -75
- package/dist/renderer/index.d.ts +10 -10
- package/dist/renderer/index.js +10 -26
- package/dist/renderer/line-renderer.d.ts +3 -3
- package/dist/renderer/line-renderer.js +13 -28
- package/dist/renderer/padding-renderer.d.ts +3 -3
- package/dist/renderer/padding-renderer.js +6 -21
- package/dist/renderer/page-renderer.d.ts +2 -2
- package/dist/renderer/page-renderer.js +61 -77
- package/dist/renderer/pdf-backend.d.ts +2 -2
- package/dist/renderer/pdf-backend.js +21 -19
- package/dist/renderer/pdf-config.js +4 -7
- package/dist/renderer/pdf-document-class.d.ts +5 -5
- package/dist/renderer/pdf-document-class.js +24 -41
- package/dist/renderer/pdf-document-renderer.d.ts +3 -3
- package/dist/renderer/pdf-document-renderer.js +71 -85
- package/dist/renderer/pdf-renderer.d.ts +2 -2
- package/dist/renderer/pdf-renderer.js +85 -93
- package/dist/renderer/positioned-renderer.d.ts +3 -3
- package/dist/renderer/positioned-renderer.js +8 -23
- package/dist/renderer/rectangle-renderer.d.ts +3 -3
- package/dist/renderer/rectangle-renderer.js +45 -52
- package/dist/renderer/repeating-header-renderer.d.ts +3 -3
- package/dist/renderer/repeating-header-renderer.js +11 -26
- package/dist/renderer/row-renderer.d.ts +3 -3
- package/dist/renderer/row-renderer.js +12 -27
- package/dist/renderer/text-renderer.d.ts +6 -5
- package/dist/renderer/text-renderer.js +33 -42
- package/dist/text/line-breaker.d.ts +8 -5
- package/dist/text/line-breaker.js +67 -16
- package/dist/text/text-style.d.ts +25 -0
- package/dist/text/text-style.js +29 -0
- package/dist/utils/afm-parser.js +3 -13
- package/dist/utils/bytes.d.ts +24 -0
- package/dist/utils/bytes.js +76 -0
- package/dist/utils/flex-layout.d.ts +2 -2
- package/dist/utils/flex-layout.js +15 -20
- package/dist/utils/font-metrics.d.ts +1 -1
- package/dist/utils/font-metrics.js +1 -2
- package/dist/utils/font-path.js +3 -6
- package/dist/utils/image-helper.d.ts +6 -5
- package/dist/utils/image-helper.js +101 -111
- package/dist/utils/md5.d.ts +4 -0
- package/dist/utils/md5.js +79 -0
- package/dist/utils/pdf-object-manager.d.ts +18 -6
- package/dist/utils/pdf-object-manager.js +0 -0
- package/dist/utils/renderer-registry.js +1 -5
- package/dist/utils/ttf-parser.d.ts +2 -2
- package/dist/utils/ttf-parser.js +32 -36
- package/dist/utils/ttf-subsetter.d.ts +1 -1
- package/dist/utils/ttf-subsetter.js +40 -42
- package/dist/utils/utf8-to-windows1252-encoder.js +1 -4
- package/dist/validators/element-validator.d.ts +2 -2
- package/dist/validators/element-validator.js +9 -13
- package/package.json +14 -2
package/dist/utils/afm-parser.js
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.AFMParser = void 0;
|
|
7
|
-
const fs_1 = __importDefault(require("fs"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
class AFMParser {
|
|
1
|
+
import { AGL } from "../assets/font-data.js";
|
|
2
|
+
export class AFMParser {
|
|
10
3
|
constructor(afmData) {
|
|
11
4
|
this.advanceWidths = {};
|
|
12
5
|
this.kerningPairs = {};
|
|
@@ -15,9 +8,7 @@ class AFMParser {
|
|
|
15
8
|
this.loadGlyphList();
|
|
16
9
|
}
|
|
17
10
|
loadGlyphList() {
|
|
18
|
-
const
|
|
19
|
-
const fileContent = fs_1.default.readFileSync(afmFilePath, "utf-8");
|
|
20
|
-
const lines = fileContent.split("\n");
|
|
11
|
+
const lines = AGL.split("\n");
|
|
21
12
|
for (const line of lines) {
|
|
22
13
|
const parts = line.trim().split(";");
|
|
23
14
|
if (parts.length >= 2) {
|
|
@@ -88,4 +79,3 @@ class AFMParser {
|
|
|
88
79
|
return this.kerningPairs[pair] || 0;
|
|
89
80
|
}
|
|
90
81
|
}
|
|
91
|
-
exports.AFMParser = AFMParser;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A `Uint8Array` → a latin1 (ISO-8859-1) string: each byte 0x00-0xFF maps 1:1 to the same code point.
|
|
3
|
+
* This matches `Buffer.toString("latin1")` exactly. NOTE: do NOT use `TextDecoder("latin1")` here - per
|
|
4
|
+
* the encoding spec that label is really windows-1252, which mangles 0x80-0x9F; we need a true 1:1 pass
|
|
5
|
+
* so arbitrary binary (e.g. a compressed stream) rides through unchanged. The PDF body is assembled as
|
|
6
|
+
* such a binary string and the final encoder passes 0x00-0xFF through.
|
|
7
|
+
*/
|
|
8
|
+
export declare function latin1FromBytes(bytes: Uint8Array): string;
|
|
9
|
+
/**
|
|
10
|
+
* A latin1 (ISO-8859-1) string → a `Uint8Array`: each char's low byte. Matches `Buffer.from(str,
|
|
11
|
+
* "latin1")` / `"binary"`. Use for the binary strings the engine assembles char-by-char (CMaps, object
|
|
12
|
+
* bodies) before they become a stream.
|
|
13
|
+
*/
|
|
14
|
+
export declare function bytesFromLatin1(str: string): Uint8Array;
|
|
15
|
+
export declare function u8(b: Uint8Array, o: number): number;
|
|
16
|
+
export declare function u16(b: Uint8Array, o: number): number;
|
|
17
|
+
export declare function u32(b: Uint8Array, o: number): number;
|
|
18
|
+
export declare function i16(b: Uint8Array, o: number): number;
|
|
19
|
+
export declare function wu16(b: Uint8Array, value: number, offset: number): void;
|
|
20
|
+
export declare function wu32(b: Uint8Array, value: number, offset: number): void;
|
|
21
|
+
export declare function wi16(b: Uint8Array, value: number, offset: number): void;
|
|
22
|
+
export declare function writeLatin1(b: Uint8Array, str: string, offset: number): void;
|
|
23
|
+
/** Concatenate Uint8Arrays into one (mirrors Buffer.concat). */
|
|
24
|
+
export declare function concatBytes(parts: Uint8Array[]): Uint8Array;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Byte helpers for an isomorphic engine (Node + browser). Kept tiny + dependency-free; as the engine
|
|
2
|
+
// moves off Node-only APIs, the Node `Buffer` conveniences are replaced by these.
|
|
3
|
+
/**
|
|
4
|
+
* A `Uint8Array` → a latin1 (ISO-8859-1) string: each byte 0x00-0xFF maps 1:1 to the same code point.
|
|
5
|
+
* This matches `Buffer.toString("latin1")` exactly. NOTE: do NOT use `TextDecoder("latin1")` here - per
|
|
6
|
+
* the encoding spec that label is really windows-1252, which mangles 0x80-0x9F; we need a true 1:1 pass
|
|
7
|
+
* so arbitrary binary (e.g. a compressed stream) rides through unchanged. The PDF body is assembled as
|
|
8
|
+
* such a binary string and the final encoder passes 0x00-0xFF through.
|
|
9
|
+
*/
|
|
10
|
+
export function latin1FromBytes(bytes) {
|
|
11
|
+
let out = "";
|
|
12
|
+
const CHUNK = 0x8000; // chunk the apply() to stay under the argument-count limit on big streams
|
|
13
|
+
for (let i = 0; i < bytes.length; i += CHUNK) {
|
|
14
|
+
out += String.fromCharCode.apply(null, bytes.subarray(i, i + CHUNK));
|
|
15
|
+
}
|
|
16
|
+
return out;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A latin1 (ISO-8859-1) string → a `Uint8Array`: each char's low byte. Matches `Buffer.from(str,
|
|
20
|
+
* "latin1")` / `"binary"`. Use for the binary strings the engine assembles char-by-char (CMaps, object
|
|
21
|
+
* bodies) before they become a stream.
|
|
22
|
+
*/
|
|
23
|
+
export function bytesFromLatin1(str) {
|
|
24
|
+
const u8a = new Uint8Array(str.length);
|
|
25
|
+
for (let i = 0; i < str.length; i++)
|
|
26
|
+
u8a[i] = str.charCodeAt(i) & 0xff;
|
|
27
|
+
return u8a;
|
|
28
|
+
}
|
|
29
|
+
// Big-endian integer reads from a Uint8Array (TrueType + PDF are big-endian), mirroring
|
|
30
|
+
// Buffer.readUInt8 / readUInt16BE / readUInt32BE / readInt16BE without needing a Buffer.
|
|
31
|
+
export function u8(b, o) {
|
|
32
|
+
return b[o];
|
|
33
|
+
}
|
|
34
|
+
export function u16(b, o) {
|
|
35
|
+
return (b[o] << 8) | b[o + 1];
|
|
36
|
+
}
|
|
37
|
+
export function u32(b, o) {
|
|
38
|
+
// Multiply the top byte (a left shift by 24 would go negative in 32-bit signed math).
|
|
39
|
+
return b[o] * 0x1000000 + ((b[o + 1] << 16) | (b[o + 2] << 8) | b[o + 3]);
|
|
40
|
+
}
|
|
41
|
+
export function i16(b, o) {
|
|
42
|
+
const v = (b[o] << 8) | b[o + 1];
|
|
43
|
+
return v & 0x8000 ? v - 0x10000 : v;
|
|
44
|
+
}
|
|
45
|
+
// Big-endian integer + latin1-string writes into a Uint8Array, mirroring Buffer.writeUInt16BE /
|
|
46
|
+
// writeUInt32BE / writeInt16BE / write(str, off, len, "latin1"). Arg order matches Buffer: (value, offset).
|
|
47
|
+
export function wu16(b, value, offset) {
|
|
48
|
+
b[offset] = (value >>> 8) & 0xff;
|
|
49
|
+
b[offset + 1] = value & 0xff;
|
|
50
|
+
}
|
|
51
|
+
export function wu32(b, value, offset) {
|
|
52
|
+
b[offset] = (value >>> 24) & 0xff;
|
|
53
|
+
b[offset + 1] = (value >>> 16) & 0xff;
|
|
54
|
+
b[offset + 2] = (value >>> 8) & 0xff;
|
|
55
|
+
b[offset + 3] = value & 0xff;
|
|
56
|
+
}
|
|
57
|
+
export function wi16(b, value, offset) {
|
|
58
|
+
wu16(b, value & 0xffff, offset);
|
|
59
|
+
}
|
|
60
|
+
export function writeLatin1(b, str, offset) {
|
|
61
|
+
for (let i = 0; i < str.length; i++)
|
|
62
|
+
b[offset + i] = str.charCodeAt(i) & 0xff;
|
|
63
|
+
}
|
|
64
|
+
/** Concatenate Uint8Arrays into one (mirrors Buffer.concat). */
|
|
65
|
+
export function concatBytes(parts) {
|
|
66
|
+
let len = 0;
|
|
67
|
+
for (const p of parts)
|
|
68
|
+
len += p.length;
|
|
69
|
+
const out = new Uint8Array(len);
|
|
70
|
+
let off = 0;
|
|
71
|
+
for (const p of parts) {
|
|
72
|
+
out.set(p, off);
|
|
73
|
+
off += p.length;
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PDFElement, LayoutContext } from "../elements/pdf-element";
|
|
2
|
-
import { BoxConstraints, Offset, Size } from "../layout/box-constraints";
|
|
1
|
+
import { PDFElement, LayoutContext } from "../elements/pdf-element.js";
|
|
2
|
+
import { BoxConstraints, Offset, Size } from "../layout/box-constraints.js";
|
|
3
3
|
/** Distribution of the children ALONG the stacking (main) axis when there is leftover
|
|
4
4
|
* space and no flex child to absorb it. */
|
|
5
5
|
export type MainAlign = "start" | "center" | "end" | "between" | "around";
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const pdf_element_1 = require("../elements/pdf-element");
|
|
5
|
-
const box_constraints_1 = require("../layout/box-constraints");
|
|
6
|
-
exports.VERTICAL_AXIS = {
|
|
1
|
+
import { FlexiblePDFElement } from "../elements/pdf-element.js";
|
|
2
|
+
import { BoxConstraints } from "../layout/box-constraints.js";
|
|
3
|
+
export const VERTICAL_AXIS = {
|
|
7
4
|
mainOf: (s) => s.height,
|
|
8
5
|
crossOf: (s) => s.width,
|
|
9
|
-
measureConstraints: (crossAvail) =>
|
|
10
|
-
flexConstraints: (mainExtent, crossAvail) =>
|
|
6
|
+
measureConstraints: (crossAvail) => BoxConstraints.loose(crossAvail, Infinity),
|
|
7
|
+
flexConstraints: (mainExtent, crossAvail) => BoxConstraints.loose(crossAvail, mainExtent),
|
|
11
8
|
offsetAt: (mainPos, crossPos) => ({ x: crossPos, y: mainPos }),
|
|
12
9
|
};
|
|
13
|
-
|
|
10
|
+
export const HORIZONTAL_AXIS = {
|
|
14
11
|
mainOf: (s) => s.width,
|
|
15
12
|
crossOf: (s) => s.height,
|
|
16
|
-
measureConstraints: (crossAvail) =>
|
|
17
|
-
flexConstraints: (mainExtent, crossAvail) =>
|
|
13
|
+
measureConstraints: (crossAvail) => BoxConstraints.loose(Infinity, crossAvail),
|
|
14
|
+
flexConstraints: (mainExtent, crossAvail) => BoxConstraints.loose(mainExtent, crossAvail),
|
|
18
15
|
offsetAt: (mainPos, crossPos) => ({ x: mainPos, y: crossPos }),
|
|
19
16
|
};
|
|
20
17
|
/** Cross-axis offset of a child of size `childCross` within `crossExtent`. */
|
|
@@ -25,7 +22,7 @@ function crossOffset(align, crossExtent, childCross) {
|
|
|
25
22
|
return Math.max(0, crossExtent - childCross);
|
|
26
23
|
return 0; // start, stretch (stretch fills, so no offset)
|
|
27
24
|
}
|
|
28
|
-
class FlexLayoutHelper {
|
|
25
|
+
export class FlexLayoutHelper {
|
|
29
26
|
/**
|
|
30
27
|
* Lays out a flex line along `axis`, IN SOURCE ORDER, and places every child.
|
|
31
28
|
* Fixed children take their natural main extent; flex (`ExpandedElement`) children
|
|
@@ -36,10 +33,9 @@ class FlexLayoutHelper {
|
|
|
36
33
|
* previous Column layout exactly.
|
|
37
34
|
*/
|
|
38
35
|
static layout(children, axis, mainAvail, crossAvail, mainStart, crossOrigin, options, ctx) {
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const cross = (_c = options.cross) !== null && _c !== void 0 ? _c : "stretch";
|
|
36
|
+
const gap = options.gap ?? 0;
|
|
37
|
+
const main = options.main ?? "start";
|
|
38
|
+
const cross = options.cross ?? "stretch";
|
|
43
39
|
const count = children.length;
|
|
44
40
|
// Pass 1: measure the fixed children (main extent + cross size) and total the flex.
|
|
45
41
|
let fixedMain = 0;
|
|
@@ -47,7 +43,7 @@ class FlexLayoutHelper {
|
|
|
47
43
|
let crossUsed = 0;
|
|
48
44
|
const fixedSize = new Map();
|
|
49
45
|
for (const child of children) {
|
|
50
|
-
if (child instanceof
|
|
46
|
+
if (child instanceof FlexiblePDFElement) {
|
|
51
47
|
totalFlex += child.getFlex();
|
|
52
48
|
}
|
|
53
49
|
else {
|
|
@@ -80,7 +76,7 @@ class FlexLayoutHelper {
|
|
|
80
76
|
// toward the line's cross extent - they're placed in pass 2, but crossExtent needs them now.
|
|
81
77
|
if (totalFlex > 0) {
|
|
82
78
|
for (const child of children) {
|
|
83
|
-
if (child instanceof
|
|
79
|
+
if (child instanceof FlexiblePDFElement) {
|
|
84
80
|
const mainExtent = (child.getFlex() / totalFlex) * remaining;
|
|
85
81
|
const size = child.calculateLayout(axis.flexConstraints(mainExtent, crossAvail), axis.offsetAt(mainStart, crossOrigin), ctx);
|
|
86
82
|
crossUsed = Math.max(crossUsed, axis.crossOf(size));
|
|
@@ -98,7 +94,7 @@ class FlexLayoutHelper {
|
|
|
98
94
|
let placedCross = 0;
|
|
99
95
|
children.forEach((child, index) => {
|
|
100
96
|
let mainExtent;
|
|
101
|
-
if (child instanceof
|
|
97
|
+
if (child instanceof FlexiblePDFElement) {
|
|
102
98
|
mainExtent = (child.getFlex() / totalFlex) * remaining;
|
|
103
99
|
const size = child.calculateLayout(axis.flexConstraints(mainExtent, stretch ? crossExtent : crossAvail), axis.offsetAt(mainPos, crossOrigin), ctx);
|
|
104
100
|
placedCross = Math.max(placedCross, axis.crossOf(size));
|
|
@@ -116,4 +112,3 @@ class FlexLayoutHelper {
|
|
|
116
112
|
return { mainUsed: mainPos - mainStart, crossUsed: placedCross };
|
|
117
113
|
}
|
|
118
114
|
}
|
|
119
|
-
exports.FlexLayoutHelper = FlexLayoutHelper;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FontStyle } from "./pdf-object-manager";
|
|
1
|
+
import type { FontStyle } from "./pdf-object-manager.js";
|
|
2
2
|
/**
|
|
3
3
|
* Read-only font measurement - the slice of the object manager that the layout pass
|
|
4
4
|
* needs. Keeping layout (and, later, fragmentation) behind this interface means those
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
package/dist/utils/font-path.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const os = require("os");
|
|
5
|
-
const path = require("path");
|
|
6
|
-
function getFontPath(fontName) {
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export function getFontPath(fontName) {
|
|
7
4
|
const platform = os.platform();
|
|
8
5
|
let fontPath = "";
|
|
9
6
|
if (platform === "win32") {
|
|
@@ -7,7 +7,7 @@ interface ImageDimensions {
|
|
|
7
7
|
width: number;
|
|
8
8
|
height: number;
|
|
9
9
|
}
|
|
10
|
-
export declare function getImageDimensions(buffer:
|
|
10
|
+
export declare function getImageDimensions(buffer: Uint8Array): Promise<ImageDimensions>;
|
|
11
11
|
/**
|
|
12
12
|
* Converts the given image to grayscale and returns its binary data.
|
|
13
13
|
* @param imagePath Path to the input image file.
|
|
@@ -16,15 +16,16 @@ export declare function getImageDimensions(buffer: Buffer): Promise<ImageDimensi
|
|
|
16
16
|
/**
|
|
17
17
|
* Decodes a PNG into raw DeviceRGB samples, Flate-compressed, ready to embed as a PDF
|
|
18
18
|
* image XObject. A PNG file is NOT a valid `/FlateDecode` stream (it's a signature plus
|
|
19
|
-
* chunks of filtered, zlib-compressed scanlines), so it must be decoded first.
|
|
20
|
-
* no alpha
|
|
19
|
+
* chunks of filtered, zlib-compressed scanlines), so it must be decoded first. DeviceRGB has
|
|
20
|
+
* no alpha itself, so any transparency rides alongside as a DeviceGray `/SMask` (the return's `smask`).
|
|
21
21
|
*/
|
|
22
|
-
export declare function decodePngToRgbFlate(pngBuffer:
|
|
22
|
+
export declare function decodePngToRgbFlate(pngBuffer: Uint8Array): Promise<{
|
|
23
23
|
data: string;
|
|
24
|
+
smask?: string;
|
|
24
25
|
width: number;
|
|
25
26
|
height: number;
|
|
26
27
|
}>;
|
|
27
|
-
export declare function convertImageToGrayscaleBuffer(imagePath: string): Promise<
|
|
28
|
+
export declare function convertImageToGrayscaleBuffer(imagePath: string): Promise<Uint8Array>;
|
|
28
29
|
export interface FitResult {
|
|
29
30
|
width: number;
|
|
30
31
|
height: number;
|
|
@@ -1,23 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.getImageDimensions = getImageDimensions;
|
|
13
|
-
exports.decodePngToRgbFlate = decodePngToRgbFlate;
|
|
14
|
-
exports.convertImageToGrayscaleBuffer = convertImageToGrayscaleBuffer;
|
|
15
|
-
exports.applyContainFit = applyContainFit;
|
|
16
|
-
exports.applyCoverFit = applyCoverFit;
|
|
17
|
-
exports.applyFillFit = applyFillFit;
|
|
18
|
-
exports.applyFitNone = applyFitNone;
|
|
19
|
-
const jimp_1 = require("jimp");
|
|
20
|
-
const zlib_1 = require("zlib");
|
|
1
|
+
import { zlibSync } from "fflate";
|
|
2
|
+
import { latin1FromBytes } from "./bytes.js";
|
|
3
|
+
import { pngToRgba } from "../platform/node-image.js";
|
|
21
4
|
// Implement the method to handle 24-bit integers
|
|
22
5
|
DataView.prototype.getUint24 = function (byteOffset, littleEndian = false) {
|
|
23
6
|
const b1 = this.getUint8(byteOffset);
|
|
@@ -25,61 +8,59 @@ DataView.prototype.getUint24 = function (byteOffset, littleEndian = false) {
|
|
|
25
8
|
const b3 = this.getUint8(byteOffset + 2);
|
|
26
9
|
return littleEndian ? (b3 << 16) | (b2 << 8) | b1 : (b1 << 16) | (b2 << 8) | b3;
|
|
27
10
|
};
|
|
28
|
-
function getImageDimensions(buffer) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
offset += dataView.getUint16(offset, false);
|
|
46
|
-
}
|
|
11
|
+
export async function getImageDimensions(buffer) {
|
|
12
|
+
const dataView = new DataView(buffer.buffer);
|
|
13
|
+
// Check for JPEG (0xFFD8 is the start of JPEG file)
|
|
14
|
+
if (dataView.getUint16(0) === 0xffd8) {
|
|
15
|
+
let offset = 2;
|
|
16
|
+
while (offset < buffer.byteLength) {
|
|
17
|
+
const marker = dataView.getUint16(offset, false);
|
|
18
|
+
offset += 2;
|
|
19
|
+
if (marker === 0xffc0 || marker === 0xffc2) {
|
|
20
|
+
// SOF0 or SOF2
|
|
21
|
+
return {
|
|
22
|
+
height: dataView.getUint16(offset + 3, false),
|
|
23
|
+
width: dataView.getUint16(offset + 5, false),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
offset += dataView.getUint16(offset, false);
|
|
47
28
|
}
|
|
48
29
|
}
|
|
49
|
-
|
|
50
|
-
|
|
30
|
+
}
|
|
31
|
+
// Check for PNG (0x89504E47 is the PNG signature)
|
|
32
|
+
if (dataView.getUint32(0) === 0x89504e47) {
|
|
33
|
+
return {
|
|
34
|
+
width: dataView.getUint32(16, false),
|
|
35
|
+
height: dataView.getUint32(20, false),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
// Check for BMP (0x424D is the BMP signature)
|
|
39
|
+
if (dataView.getUint16(0) === 0x424d) {
|
|
40
|
+
return {
|
|
41
|
+
width: dataView.getUint32(18, true),
|
|
42
|
+
height: dataView.getUint32(22, true),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Check for WebP (0x52494646 is the WebP signature 'RIFF')
|
|
46
|
+
if (dataView.getUint32(0) === 0x52494646 && dataView.getUint32(8) === 0x57454250) {
|
|
47
|
+
// 'WEBP'
|
|
48
|
+
if (dataView.getUint32(12) === 0x56503820) {
|
|
49
|
+
// 'VP8 '
|
|
51
50
|
return {
|
|
52
|
-
width: dataView.
|
|
53
|
-
height: dataView.
|
|
51
|
+
width: dataView.getUint16(26, true),
|
|
52
|
+
height: dataView.getUint16(28, true),
|
|
54
53
|
};
|
|
55
54
|
}
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
else if (dataView.getUint32(12) === 0x56503858) {
|
|
56
|
+
// 'VP8X'
|
|
58
57
|
return {
|
|
59
|
-
width: dataView.
|
|
60
|
-
height: dataView.
|
|
58
|
+
width: dataView.getUint24(24, true) + 1,
|
|
59
|
+
height: dataView.getUint24(27, true) + 1,
|
|
61
60
|
};
|
|
62
61
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// 'WEBP'
|
|
66
|
-
if (dataView.getUint32(12) === 0x56503820) {
|
|
67
|
-
// 'VP8 '
|
|
68
|
-
return {
|
|
69
|
-
width: dataView.getUint16(26, true),
|
|
70
|
-
height: dataView.getUint16(28, true),
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
else if (dataView.getUint32(12) === 0x56503858) {
|
|
74
|
-
// 'VP8X'
|
|
75
|
-
return {
|
|
76
|
-
width: dataView.getUint24(24, true) + 1,
|
|
77
|
-
height: dataView.getUint24(27, true) + 1,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
throw new Error("Unsupported image format");
|
|
82
|
-
});
|
|
62
|
+
}
|
|
63
|
+
throw new Error("Unsupported image format");
|
|
83
64
|
}
|
|
84
65
|
/**
|
|
85
66
|
* Converts the given image to grayscale and returns its binary data.
|
|
@@ -89,50 +70,59 @@ function getImageDimensions(buffer) {
|
|
|
89
70
|
/**
|
|
90
71
|
* Decodes a PNG into raw DeviceRGB samples, Flate-compressed, ready to embed as a PDF
|
|
91
72
|
* image XObject. A PNG file is NOT a valid `/FlateDecode` stream (it's a signature plus
|
|
92
|
-
* chunks of filtered, zlib-compressed scanlines), so it must be decoded first.
|
|
93
|
-
* no alpha
|
|
73
|
+
* chunks of filtered, zlib-compressed scanlines), so it must be decoded first. DeviceRGB has
|
|
74
|
+
* no alpha itself, so any transparency rides alongside as a DeviceGray `/SMask` (the return's `smask`).
|
|
94
75
|
*/
|
|
95
|
-
function decodePngToRgbFlate(pngBuffer) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
76
|
+
export async function decodePngToRgbFlate(pngBuffer) {
|
|
77
|
+
// The decode (PNG bytes → RGBA) is platform-specific: jimp on Node, an OffscreenCanvas in the browser
|
|
78
|
+
// (`platform/{node,browser}-image.ts`, swapped by the package `browser` field). The split below is shared.
|
|
79
|
+
const { width, height, rgba } = await pngToRgba(pngBuffer);
|
|
80
|
+
// Raw DeviceRGB samples + (only when the PNG actually has transparency) a DeviceGray alpha channel that
|
|
81
|
+
// rides as the XObject's /SMask. An opaque PNG yields the same raw RGB as before and no SMask, so it stays
|
|
82
|
+
// byte-identical; a transparent one now composites correctly over whatever sits behind it.
|
|
83
|
+
const rgb = new Uint8Array(width * height * 3);
|
|
84
|
+
const alpha = new Uint8Array(width * height);
|
|
85
|
+
let hasAlpha = false;
|
|
86
|
+
for (let i = 0, j = 0, k = 0; i < rgba.length; i += 4, j += 3, k++) {
|
|
87
|
+
rgb[j] = rgba[i];
|
|
88
|
+
rgb[j + 1] = rgba[i + 1];
|
|
89
|
+
rgb[j + 2] = rgba[i + 2];
|
|
90
|
+
alpha[k] = rgba[i + 3];
|
|
91
|
+
if (rgba[i + 3] !== 255)
|
|
92
|
+
hasAlpha = true;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
data: latin1FromBytes(zlibSync(rgb)),
|
|
96
|
+
smask: hasAlpha ? latin1FromBytes(zlibSync(alpha)) : undefined,
|
|
97
|
+
width,
|
|
98
|
+
height,
|
|
99
|
+
};
|
|
109
100
|
}
|
|
110
|
-
function convertImageToGrayscaleBuffer(imagePath) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
});
|
|
101
|
+
export async function convertImageToGrayscaleBuffer(imagePath) {
|
|
102
|
+
// We get the image from the buffer (jimp lazily loaded - see decodePngToRgbFlate).
|
|
103
|
+
const { Jimp, JimpMime } = await import("jimp");
|
|
104
|
+
const image = await Jimp.read(imagePath);
|
|
105
|
+
// Get MIME type. If emtpy throw error
|
|
106
|
+
const mime = image.mime;
|
|
107
|
+
if (!mime)
|
|
108
|
+
throw new Error("Cannot read MIME type");
|
|
109
|
+
// We need to check the MIME type (the exact union `getBuffer` accepts).
|
|
110
|
+
let mimeType;
|
|
111
|
+
switch (mime) {
|
|
112
|
+
case JimpMime.png:
|
|
113
|
+
case JimpMime.jpeg:
|
|
114
|
+
case JimpMime.bmp:
|
|
115
|
+
mimeType = mime;
|
|
116
|
+
break;
|
|
117
|
+
default:
|
|
118
|
+
throw new Error("Unsupported MIME type");
|
|
119
|
+
}
|
|
120
|
+
image.greyscale();
|
|
121
|
+
// Convert the image back to buffer with current MIME type
|
|
122
|
+
const grayscaleBuffer = await image.getBuffer(mimeType);
|
|
123
|
+
return grayscaleBuffer;
|
|
134
124
|
}
|
|
135
|
-
function applyContainFit(imageWidth, imageHeight, containerWidth, containerHeight) {
|
|
125
|
+
export function applyContainFit(imageWidth, imageHeight, containerWidth, containerHeight) {
|
|
136
126
|
const imageAspectRatio = imageWidth / imageHeight;
|
|
137
127
|
const containerAspectRatio = containerWidth / containerHeight;
|
|
138
128
|
let width, height, offsetX, offsetY;
|
|
@@ -159,7 +149,7 @@ function applyContainFit(imageWidth, imageHeight, containerWidth, containerHeigh
|
|
|
159
149
|
offsetY,
|
|
160
150
|
};
|
|
161
151
|
}
|
|
162
|
-
function applyCoverFit(imageWidth, imageHeight, containerWidth, containerHeight) {
|
|
152
|
+
export function applyCoverFit(imageWidth, imageHeight, containerWidth, containerHeight) {
|
|
163
153
|
const imageAspectRatio = imageWidth / imageHeight;
|
|
164
154
|
const containerAspectRatio = containerWidth / containerHeight;
|
|
165
155
|
let width, height, offsetX, offsetY;
|
|
@@ -186,7 +176,7 @@ function applyCoverFit(imageWidth, imageHeight, containerWidth, containerHeight)
|
|
|
186
176
|
offsetY,
|
|
187
177
|
};
|
|
188
178
|
}
|
|
189
|
-
function applyFillFit(containerWidth, containerHeight) {
|
|
179
|
+
export function applyFillFit(containerWidth, containerHeight) {
|
|
190
180
|
return {
|
|
191
181
|
width: containerWidth,
|
|
192
182
|
height: containerHeight,
|
|
@@ -194,7 +184,7 @@ function applyFillFit(containerWidth, containerHeight) {
|
|
|
194
184
|
offsetY: 0,
|
|
195
185
|
};
|
|
196
186
|
}
|
|
197
|
-
function applyFitNone(imageWidth, imageHeight, containerWidth, containerHeight) {
|
|
187
|
+
export function applyFitNone(imageWidth, imageHeight, containerWidth, containerHeight) {
|
|
198
188
|
const offsetX = (containerWidth - imageWidth) / 2; // Center horizontally
|
|
199
189
|
const offsetY = (containerHeight - imageHeight) / 2; // Center vertically
|
|
200
190
|
return {
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// MD5 (RFC 1321), vendored + isomorphic - so the font subset tag and the documentId /ID hash work in the
|
|
2
|
+
// browser too: Node `crypto` is absent there, and Web Crypto has no MD5 and is async. Verified byte-for-byte
|
|
3
|
+
// identical to Node's `createHash("md5")`. Operates on bytes; callers UTF-8-encode strings (TextEncoder),
|
|
4
|
+
// matching `hash.update(string)`'s default encoding.
|
|
5
|
+
// Per-round left-rotate amounts.
|
|
6
|
+
const S = [
|
|
7
|
+
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14,
|
|
8
|
+
20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6,
|
|
9
|
+
10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
|
|
10
|
+
];
|
|
11
|
+
// Per-round constants K[i] = floor(abs(sin(i+1)) * 2^32).
|
|
12
|
+
const K = new Int32Array(64);
|
|
13
|
+
for (let i = 0; i < 64; i++)
|
|
14
|
+
K[i] = Math.floor(Math.abs(Math.sin(i + 1)) * 2 ** 32) | 0;
|
|
15
|
+
/** The raw 16-byte MD5 digest of `input`. */
|
|
16
|
+
export function md5(input) {
|
|
17
|
+
const len = input.length;
|
|
18
|
+
// Pad to a multiple of 64: message + 0x80 + zeros + 64-bit little-endian bit length.
|
|
19
|
+
const total = (((len + 8) >> 6) + 1) << 6;
|
|
20
|
+
const b = new Uint8Array(total);
|
|
21
|
+
b.set(input);
|
|
22
|
+
b[len] = 0x80;
|
|
23
|
+
const bits = len * 8;
|
|
24
|
+
b[total - 8] = bits & 0xff;
|
|
25
|
+
b[total - 7] = (bits >>> 8) & 0xff;
|
|
26
|
+
b[total - 6] = (bits >>> 16) & 0xff;
|
|
27
|
+
b[total - 5] = (bits >>> 24) & 0xff;
|
|
28
|
+
// High 32 bits of the length stay 0 (inputs are well under 512 MB).
|
|
29
|
+
let a0 = 0x67452301, b0 = 0xefcdab89, c0 = 0x98badcfe, d0 = 0x10325476;
|
|
30
|
+
const M = new Int32Array(16);
|
|
31
|
+
for (let off = 0; off < total; off += 64) {
|
|
32
|
+
for (let i = 0; i < 16; i++) {
|
|
33
|
+
const j = off + i * 4;
|
|
34
|
+
M[i] = b[j] | (b[j + 1] << 8) | (b[j + 2] << 16) | (b[j + 3] << 24);
|
|
35
|
+
}
|
|
36
|
+
let A = a0, B = b0, C = c0, D = d0;
|
|
37
|
+
for (let i = 0; i < 64; i++) {
|
|
38
|
+
let f, g;
|
|
39
|
+
if (i < 16) {
|
|
40
|
+
f = (B & C) | (~B & D);
|
|
41
|
+
g = i;
|
|
42
|
+
}
|
|
43
|
+
else if (i < 32) {
|
|
44
|
+
f = (D & B) | (~D & C);
|
|
45
|
+
g = (5 * i + 1) % 16;
|
|
46
|
+
}
|
|
47
|
+
else if (i < 48) {
|
|
48
|
+
f = B ^ C ^ D;
|
|
49
|
+
g = (3 * i + 5) % 16;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
f = C ^ (B | ~D);
|
|
53
|
+
g = (7 * i) % 16;
|
|
54
|
+
}
|
|
55
|
+
f = (f + A + K[i] + M[g]) | 0;
|
|
56
|
+
A = D;
|
|
57
|
+
D = C;
|
|
58
|
+
C = B;
|
|
59
|
+
B = (B + ((f << S[i]) | (f >>> (32 - S[i])) | 0)) | 0;
|
|
60
|
+
}
|
|
61
|
+
a0 = (a0 + A) | 0;
|
|
62
|
+
b0 = (b0 + B) | 0;
|
|
63
|
+
c0 = (c0 + C) | 0;
|
|
64
|
+
d0 = (d0 + D) | 0;
|
|
65
|
+
}
|
|
66
|
+
const out = new Uint8Array(16);
|
|
67
|
+
const w = [a0, b0, c0, d0];
|
|
68
|
+
for (let i = 0; i < 4; i++) {
|
|
69
|
+
out[i * 4] = w[i] & 0xff;
|
|
70
|
+
out[i * 4 + 1] = (w[i] >>> 8) & 0xff;
|
|
71
|
+
out[i * 4 + 2] = (w[i] >>> 16) & 0xff;
|
|
72
|
+
out[i * 4 + 3] = (w[i] >>> 24) & 0xff;
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
/** Lowercase hex of the MD5 digest (matches `digest("hex")`). */
|
|
77
|
+
export function md5Hex(input) {
|
|
78
|
+
return [...md5(input)].map((x) => x.toString(16).padStart(2, "0")).join("");
|
|
79
|
+
}
|