pdf-parser.js 4.6.0 → 4.7.0
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/README.md +1 -1
- package/dist/codec.d.cts +8 -0
- package/dist/codec.d.ts +8 -0
- package/dist/filters.cjs +14 -3
- package/dist/filters.d.cts +4 -0
- package/dist/filters.d.ts +4 -0
- package/dist/filters.js +14 -3
- package/dist/images-read.cjs +12 -2
- package/dist/images-read.d.cts +5 -0
- package/dist/images-read.d.ts +5 -0
- package/dist/images-read.js +12 -2
- package/dist/layout.cjs +6 -1
- package/dist/layout.d.cts +16 -0
- package/dist/layout.d.ts +16 -0
- package/dist/layout.js +6 -1
- package/dist/read.cjs +9 -4
- package/dist/read.js +9 -4
- package/dist/write.cjs +33 -0
- package/dist/write.js +33 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -264,7 +264,7 @@ Dependency direction is strictly downward and checkable: `layout` imports only `
|
|
|
264
264
|
- **Nothing in this codec's reader accepts, prompts for, or guesses a password.** Authenticating as owner is a permissions escalation, not a way to read a file you were already allowed to read.
|
|
265
265
|
- **`writePdf` produces real, spec-conformant encryption for all four of the schemes above** (`src/encrypt-write.ts`, `WritePdfOptions.encryption`) — genuine `/O`/`/U`/`/OE`/`/UE`/`/Perms` computation per ISO 32000-2 7.6.4.4's Algorithms 3, 8, 9, and 10, a real user and (optional, defaulting to the user password) owner password, and per-`/P`-bit permission flags. Unlike the reader, the writer's own password is not scoped to empty: a genuinely non-empty user password is fully supported and round-trips against the published algorithm (see `encrypt-write.test.ts`), even though this package's own reader — scoped as above — can only ever open the result back up if the user password used to encrypt it was itself empty. Two boundaries are deliberately narrower than the full spec rather than approximated silently: a revision 2-4 (rc4-40/rc4-128/aes-128) password must be plain printable ASCII, not the full 8-bit PDFDocEncoding table, and a revision 6 (aes-256) password is Unicode-normalised with NFKC rather than the full SASLPrep/stringprep profile (prohibited-character tables and the bidirectional-text rule are not implemented). Both throw `PdfEncryptionError` for a password outside that scope rather than silently mis-encoding it into one that will not open the file it was meant to protect.
|
|
266
266
|
- **`CCITTFaxDecode`, `JBIG2Decode` and `JPXDecode` images all decode for real** via hand-written decoders (`src/image/ccitt.ts` for ITU-T T.4/T.6 fax; `src/image/jbig2*.ts` for ITU-T T.88; `src/image/jpeg2000*.ts` for ISO/IEC 15444-1). JPEG images (`DCTDecode`) pass through losslessly in both directions. See [JBIG2 scope](#jbig2-scope) and [JPEG 2000 scope](#jpeg-2000-scope) for exactly what is and is not implemented.
|
|
267
|
-
- **The write side re-encodes bilevel images as CCITT Group 4** (`src/image/ccitt-encode.ts`, a hand-written ITU-T T.6 encoder sharing the decoder's own code-table transcription): a grayscale image whose samples are all 0 or 255, with no soft mask, is written as `/CCITTFaxDecode` with `/K -1` whenever the G4 encoding comes out smaller than Flate over the same pixels — which for vertically coherent bilevel content (a scan's edges and text baselines) it does by an order of magnitude. Whichever encoding is smaller wins, deterministically, so decorrelated or repetitive bilevel content where Flate happens to win keeps Flate. **JBIG2 and JPEG 2000 re-encoding
|
|
267
|
+
- **The write side re-encodes bilevel images as CCITT Group 4** (`src/image/ccitt-encode.ts`, a hand-written ITU-T T.6 encoder sharing the decoder's own code-table transcription): a grayscale image whose samples are all 0 or 255, with no soft mask, is written as `/CCITTFaxDecode` with `/K -1` whenever the G4 encoding comes out smaller than Flate over the same pixels — which for vertically coherent bilevel content (a scan's edges and text baselines) it does by an order of magnitude. Whichever encoding is smaller wins, deterministically, so decorrelated or repetitive bilevel content where Flate happens to win keeps Flate. **JBIG2 and JPEG 2000 re-encoding remain permanent boundaries, not gaps**: a hand-written JBIG2 encoder is research-grade symbol-dictionary design and a JPEG 2000 encoder is the full EBCOT/wavelet stack, and re-encoding lossy JPX back is not fidelity-preserving anyway. **What no longer follows from that boundary is generation loss on a pdf-to-pdf round trip**: `LayoutImageAsset.original` (the layout twin of `document-schema.js`'s `ContentImageOriginal`, landed through the #975-scorecard schema change it named) carries a source's own JBIG2/JPX compressed stream beside the decoded canonical, captured at read time together with the decoded `/JBIG2Globals` segments a symbol-dictionary stream needs, and the writer re-embeds those bytes verbatim under their own filter — the decoded canonical never reaches the file at all for such an image, a source soft mask still re-emits as a generated `/SMask`, and the round trip pays zero generation loss for exactly the two filters this package cannot re-encode.
|
|
268
268
|
- **`interpret.ts` tracks general vector paths, not just axis-aligned `re` rectangles.** `m`/`l`/`c`/`v`/`y`/`h` (and `re` itself) accumulate real subpaths — CTM-transformed line/cubic segments, open or closed — and any paint operator emits an item built from them. A recovered path matching one of three characteristic shape patterns comes back as that shape's own kind, not a generic `LayoutPath`: an axis-aligned closed four-corner subpath is a `LayoutRect`; a closed subpath of four cubic segments meeting its bounding box at cardinal points with kappa-ratio control points is a `LayoutEllipse`; an open single-straight-segment stroke-only subpath is a `LayoutLine` (tolerance: `max(1e-3pt, 1e-4 × extent)`). **These are deliberate, bounded heuristics** — a false positive changes an item's kind, never its geometry, since every detected shape reproduces its source path's own points exactly. Off-axis rotations, non-quadrant curves, polygons, and multi-subpath figures stay a `LayoutPath`.
|
|
269
269
|
- **`writePdf`/`readPdf` round-trip a page's own `notes` field via a hidden `/Subtype /Text` annotation** with the `Hidden` flag set so it never renders or prints, distinguished from a genuine third-party sticky note by an internal author marker. This is a round-trip mechanism specific to this package's own writer/reader pair. `documents.js` uses this to carry pptx/odp speaker notes through PDF.
|
|
270
270
|
- **STIX Two Math is a CFF-flavoured OpenType font, not TrueType/glyf** — the **entire** `CFF ` table is embedded verbatim as a single `/FontFile3` `/Subtype /CIDFontType0C` stream (a real, correct, working embedded font, just not glyph-subsetted). Everything else genuinely IS built from a targeted parse of only what's used: `cmap` resolves exactly the Unicode code points a document's formulas reference, and the emitted `/W` widths array covers only drawn glyph IDs. A CID-keyed composite font built this way needs no `/CIDToGIDMap` — per ISO 32000-1 9.7.4.2, a `/CIDFontType0` whose `/FontFile3` is a non-CID-keyed CFF program is read with CID directly indexing `CharStrings` by glyph order (CID == GID).
|
package/dist/codec.d.cts
CHANGED
|
@@ -253,6 +253,14 @@ declare const pdfCodec: z.ZodCodec<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Arr
|
|
|
253
253
|
base64: z.ZodString;
|
|
254
254
|
widthPx: z.ZodNumber;
|
|
255
255
|
heightPx: z.ZodNumber;
|
|
256
|
+
original: z.ZodOptional<z.ZodObject<{
|
|
257
|
+
filter: z.ZodEnum<{
|
|
258
|
+
jbig2: "jbig2";
|
|
259
|
+
jpeg2000: "jpeg2000";
|
|
260
|
+
}>;
|
|
261
|
+
base64: z.ZodString;
|
|
262
|
+
jbig2GlobalsBase64: z.ZodOptional<z.ZodString>;
|
|
263
|
+
}, z.core.$strip>>;
|
|
256
264
|
}, z.core.$strip>>;
|
|
257
265
|
destinations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
258
266
|
name: z.ZodString;
|
package/dist/codec.d.ts
CHANGED
|
@@ -253,6 +253,14 @@ declare const pdfCodec: z.ZodCodec<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Arr
|
|
|
253
253
|
base64: z.ZodString;
|
|
254
254
|
widthPx: z.ZodNumber;
|
|
255
255
|
heightPx: z.ZodNumber;
|
|
256
|
+
original: z.ZodOptional<z.ZodObject<{
|
|
257
|
+
filter: z.ZodEnum<{
|
|
258
|
+
jbig2: "jbig2";
|
|
259
|
+
jpeg2000: "jpeg2000";
|
|
260
|
+
}>;
|
|
261
|
+
base64: z.ZodString;
|
|
262
|
+
jbig2GlobalsBase64: z.ZodOptional<z.ZodString>;
|
|
263
|
+
}, z.core.$strip>>;
|
|
256
264
|
}, z.core.$strip>>;
|
|
257
265
|
destinations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
258
266
|
name: z.ZodString;
|
package/dist/filters.cjs
CHANGED
|
@@ -10,6 +10,7 @@ function decodeStream(raw, dict, sink, resolve) {
|
|
|
10
10
|
const filters = filterNames(dict);
|
|
11
11
|
const parms = decodeParmsList(dict, filters.length);
|
|
12
12
|
let bytes = raw;
|
|
13
|
+
let jbig2;
|
|
13
14
|
for (let i = 0; i < filters.length; i++) {
|
|
14
15
|
const filter = filters[i];
|
|
15
16
|
const parm = parms[i];
|
|
@@ -27,7 +28,11 @@ function decodeStream(raw, dict, sink, resolve) {
|
|
|
27
28
|
bytes,
|
|
28
29
|
remainingFilter: "JBIG2Decode"
|
|
29
30
|
};
|
|
30
|
-
|
|
31
|
+
jbig2 = {
|
|
32
|
+
encoded: bytes,
|
|
33
|
+
globals: decoded.globals
|
|
34
|
+
};
|
|
35
|
+
bytes = decoded.bytes;
|
|
31
36
|
} else if (filter === "DCTDecode" || filter === "DCT") return {
|
|
32
37
|
bytes,
|
|
33
38
|
remainingFilter: "DCTDecode"
|
|
@@ -48,7 +53,10 @@ function decodeStream(raw, dict, sink, resolve) {
|
|
|
48
53
|
};
|
|
49
54
|
}
|
|
50
55
|
}
|
|
51
|
-
return { bytes }
|
|
56
|
+
return jbig2 === void 0 ? { bytes } : {
|
|
57
|
+
bytes,
|
|
58
|
+
jbig2
|
|
59
|
+
};
|
|
52
60
|
}
|
|
53
61
|
function applyPredictorIfPresent(data, parm, sink) {
|
|
54
62
|
return require_predictors.applyPredictor(data, require_predictors.readPredictorParams(parm), sink);
|
|
@@ -94,7 +102,10 @@ function jbig2Decode(data, parm, dict, sink, resolve) {
|
|
|
94
102
|
});
|
|
95
103
|
}
|
|
96
104
|
});
|
|
97
|
-
return
|
|
105
|
+
return {
|
|
106
|
+
bytes: Uint8Array.from(image.bytes, (byte) => byte ^ 255),
|
|
107
|
+
globals
|
|
108
|
+
};
|
|
98
109
|
} catch (error) {
|
|
99
110
|
sink({
|
|
100
111
|
code: "pdf/jbig2-undecodable",
|
package/dist/filters.d.cts
CHANGED
|
@@ -4,6 +4,10 @@ import { n as PdfDict, r as PdfObject } from "./objects-B7HdMeWS.cjs";
|
|
|
4
4
|
interface DecodedStream {
|
|
5
5
|
readonly bytes: Uint8Array<ArrayBuffer>;
|
|
6
6
|
readonly remainingFilter?: string;
|
|
7
|
+
readonly jbig2?: {
|
|
8
|
+
readonly encoded: Uint8Array<ArrayBuffer>;
|
|
9
|
+
readonly globals?: Uint8Array<ArrayBuffer>;
|
|
10
|
+
};
|
|
7
11
|
}
|
|
8
12
|
type PdfIndirectResolver = (obj: PdfObject | undefined) => PdfObject | undefined;
|
|
9
13
|
declare function decodeStream(raw: Uint8Array<ArrayBuffer>, dict: PdfDict, sink: PdfDiagnosticSink, resolve?: PdfIndirectResolver): DecodedStream;
|
package/dist/filters.d.ts
CHANGED
|
@@ -4,6 +4,10 @@ import { n as PdfDict, r as PdfObject } from "./objects-B7HdMeWS.js";
|
|
|
4
4
|
interface DecodedStream {
|
|
5
5
|
readonly bytes: Uint8Array<ArrayBuffer>;
|
|
6
6
|
readonly remainingFilter?: string;
|
|
7
|
+
readonly jbig2?: {
|
|
8
|
+
readonly encoded: Uint8Array<ArrayBuffer>;
|
|
9
|
+
readonly globals?: Uint8Array<ArrayBuffer>;
|
|
10
|
+
};
|
|
7
11
|
}
|
|
8
12
|
type PdfIndirectResolver = (obj: PdfObject | undefined) => PdfObject | undefined;
|
|
9
13
|
declare function decodeStream(raw: Uint8Array<ArrayBuffer>, dict: PdfDict, sink: PdfDiagnosticSink, resolve?: PdfIndirectResolver): DecodedStream;
|
package/dist/filters.js
CHANGED
|
@@ -9,6 +9,7 @@ function decodeStream(raw, dict, sink, resolve) {
|
|
|
9
9
|
const filters = filterNames(dict);
|
|
10
10
|
const parms = decodeParmsList(dict, filters.length);
|
|
11
11
|
let bytes = raw;
|
|
12
|
+
let jbig2;
|
|
12
13
|
for (let i = 0; i < filters.length; i++) {
|
|
13
14
|
const filter = filters[i];
|
|
14
15
|
const parm = parms[i];
|
|
@@ -26,7 +27,11 @@ function decodeStream(raw, dict, sink, resolve) {
|
|
|
26
27
|
bytes,
|
|
27
28
|
remainingFilter: "JBIG2Decode"
|
|
28
29
|
};
|
|
29
|
-
|
|
30
|
+
jbig2 = {
|
|
31
|
+
encoded: bytes,
|
|
32
|
+
globals: decoded.globals
|
|
33
|
+
};
|
|
34
|
+
bytes = decoded.bytes;
|
|
30
35
|
} else if (filter === "DCTDecode" || filter === "DCT") return {
|
|
31
36
|
bytes,
|
|
32
37
|
remainingFilter: "DCTDecode"
|
|
@@ -47,7 +52,10 @@ function decodeStream(raw, dict, sink, resolve) {
|
|
|
47
52
|
};
|
|
48
53
|
}
|
|
49
54
|
}
|
|
50
|
-
return { bytes }
|
|
55
|
+
return jbig2 === void 0 ? { bytes } : {
|
|
56
|
+
bytes,
|
|
57
|
+
jbig2
|
|
58
|
+
};
|
|
51
59
|
}
|
|
52
60
|
function applyPredictorIfPresent(data, parm, sink) {
|
|
53
61
|
return applyPredictor(data, readPredictorParams(parm), sink);
|
|
@@ -93,7 +101,10 @@ function jbig2Decode(data, parm, dict, sink, resolve) {
|
|
|
93
101
|
});
|
|
94
102
|
}
|
|
95
103
|
});
|
|
96
|
-
return
|
|
104
|
+
return {
|
|
105
|
+
bytes: Uint8Array.from(image.bytes, (byte) => byte ^ 255),
|
|
106
|
+
globals
|
|
107
|
+
};
|
|
97
108
|
} catch (error) {
|
|
98
109
|
sink({
|
|
99
110
|
code: "pdf/jbig2-undecodable",
|
package/dist/images-read.cjs
CHANGED
|
@@ -249,7 +249,11 @@ function readJpeg2000Image(dict, raw, resolver, sink) {
|
|
|
249
249
|
format: "png",
|
|
250
250
|
bytes: (0, byte_codec.encodePng)(withAlpha),
|
|
251
251
|
widthPx: image.width,
|
|
252
|
-
heightPx: image.height
|
|
252
|
+
heightPx: image.height,
|
|
253
|
+
original: {
|
|
254
|
+
filter: "jpeg2000",
|
|
255
|
+
bytes: raw
|
|
256
|
+
}
|
|
253
257
|
};
|
|
254
258
|
}
|
|
255
259
|
function readImageXObject(dict, raw, resolver, sink) {
|
|
@@ -262,6 +266,11 @@ function readImageXObject(dict, raw, resolver, sink) {
|
|
|
262
266
|
return;
|
|
263
267
|
}
|
|
264
268
|
const decoded = require_filters.decodeStream(raw, dict, sink, (obj) => resolver.resolve(obj));
|
|
269
|
+
const jbig2Original = decoded.jbig2 === void 0 ? void 0 : {
|
|
270
|
+
filter: "jbig2",
|
|
271
|
+
bytes: decoded.jbig2.encoded,
|
|
272
|
+
...decoded.jbig2.globals !== void 0 ? { globalsBytes: decoded.jbig2.globals } : {}
|
|
273
|
+
};
|
|
265
274
|
if (decoded.remainingFilter === "DCTDecode") {
|
|
266
275
|
let info;
|
|
267
276
|
try {
|
|
@@ -323,7 +332,8 @@ function readImageXObject(dict, raw, resolver, sink) {
|
|
|
323
332
|
format: "png",
|
|
324
333
|
bytes: (0, byte_codec.encodePng)(withAlpha),
|
|
325
334
|
widthPx: width,
|
|
326
|
-
heightPx: height
|
|
335
|
+
heightPx: height,
|
|
336
|
+
...jbig2Original !== void 0 ? { original: jbig2Original } : {}
|
|
327
337
|
};
|
|
328
338
|
}
|
|
329
339
|
//#endregion
|
package/dist/images-read.d.cts
CHANGED
|
@@ -7,6 +7,11 @@ interface ExtractedPdfImage {
|
|
|
7
7
|
readonly bytes: Uint8Array<ArrayBuffer>;
|
|
8
8
|
readonly widthPx: number;
|
|
9
9
|
readonly heightPx: number;
|
|
10
|
+
readonly original?: {
|
|
11
|
+
readonly filter: "jbig2" | "jpeg2000";
|
|
12
|
+
readonly bytes: Uint8Array<ArrayBuffer>;
|
|
13
|
+
readonly globalsBytes?: Uint8Array<ArrayBuffer>;
|
|
14
|
+
};
|
|
10
15
|
}
|
|
11
16
|
declare function readImageXObject(dict: PdfDict, raw: Uint8Array<ArrayBuffer>, resolver: PdfObjectResolver, sink: PdfDiagnosticSink): ExtractedPdfImage | undefined;
|
|
12
17
|
//#endregion
|
package/dist/images-read.d.ts
CHANGED
|
@@ -7,6 +7,11 @@ interface ExtractedPdfImage {
|
|
|
7
7
|
readonly bytes: Uint8Array<ArrayBuffer>;
|
|
8
8
|
readonly widthPx: number;
|
|
9
9
|
readonly heightPx: number;
|
|
10
|
+
readonly original?: {
|
|
11
|
+
readonly filter: "jbig2" | "jpeg2000";
|
|
12
|
+
readonly bytes: Uint8Array<ArrayBuffer>;
|
|
13
|
+
readonly globalsBytes?: Uint8Array<ArrayBuffer>;
|
|
14
|
+
};
|
|
10
15
|
}
|
|
11
16
|
declare function readImageXObject(dict: PdfDict, raw: Uint8Array<ArrayBuffer>, resolver: PdfObjectResolver, sink: PdfDiagnosticSink): ExtractedPdfImage | undefined;
|
|
12
17
|
//#endregion
|
package/dist/images-read.js
CHANGED
|
@@ -248,7 +248,11 @@ function readJpeg2000Image(dict, raw, resolver, sink) {
|
|
|
248
248
|
format: "png",
|
|
249
249
|
bytes: encodePng(withAlpha),
|
|
250
250
|
widthPx: image.width,
|
|
251
|
-
heightPx: image.height
|
|
251
|
+
heightPx: image.height,
|
|
252
|
+
original: {
|
|
253
|
+
filter: "jpeg2000",
|
|
254
|
+
bytes: raw
|
|
255
|
+
}
|
|
252
256
|
};
|
|
253
257
|
}
|
|
254
258
|
function readImageXObject(dict, raw, resolver, sink) {
|
|
@@ -261,6 +265,11 @@ function readImageXObject(dict, raw, resolver, sink) {
|
|
|
261
265
|
return;
|
|
262
266
|
}
|
|
263
267
|
const decoded = decodeStream(raw, dict, sink, (obj) => resolver.resolve(obj));
|
|
268
|
+
const jbig2Original = decoded.jbig2 === void 0 ? void 0 : {
|
|
269
|
+
filter: "jbig2",
|
|
270
|
+
bytes: decoded.jbig2.encoded,
|
|
271
|
+
...decoded.jbig2.globals !== void 0 ? { globalsBytes: decoded.jbig2.globals } : {}
|
|
272
|
+
};
|
|
264
273
|
if (decoded.remainingFilter === "DCTDecode") {
|
|
265
274
|
let info;
|
|
266
275
|
try {
|
|
@@ -322,7 +331,8 @@ function readImageXObject(dict, raw, resolver, sink) {
|
|
|
322
331
|
format: "png",
|
|
323
332
|
bytes: encodePng(withAlpha),
|
|
324
333
|
widthPx: width,
|
|
325
|
-
heightPx: height
|
|
334
|
+
heightPx: height,
|
|
335
|
+
...jbig2Original !== void 0 ? { original: jbig2Original } : {}
|
|
326
336
|
};
|
|
327
337
|
}
|
|
328
338
|
//#endregion
|
package/dist/layout.cjs
CHANGED
|
@@ -178,7 +178,12 @@ const LayoutImageAssetSchema = zod.z.object({
|
|
|
178
178
|
format: zod.z.enum(["png", "jpeg"]),
|
|
179
179
|
base64: zod.z.string(),
|
|
180
180
|
widthPx: zod.z.number().int().positive(),
|
|
181
|
-
heightPx: zod.z.number().int().positive()
|
|
181
|
+
heightPx: zod.z.number().int().positive(),
|
|
182
|
+
original: zod.z.object({
|
|
183
|
+
filter: zod.z.enum(["jbig2", "jpeg2000"]),
|
|
184
|
+
base64: zod.z.string(),
|
|
185
|
+
jbig2GlobalsBase64: zod.z.string().optional()
|
|
186
|
+
}).optional()
|
|
182
187
|
});
|
|
183
188
|
const LayoutDestinationTargetSchema = zod.z.object({
|
|
184
189
|
kind: zod.z.enum([
|
package/dist/layout.d.cts
CHANGED
|
@@ -675,6 +675,14 @@ declare const LayoutImageAssetSchema: z.ZodObject<{
|
|
|
675
675
|
base64: z.ZodString;
|
|
676
676
|
widthPx: z.ZodNumber;
|
|
677
677
|
heightPx: z.ZodNumber;
|
|
678
|
+
original: z.ZodOptional<z.ZodObject<{
|
|
679
|
+
filter: z.ZodEnum<{
|
|
680
|
+
jbig2: "jbig2";
|
|
681
|
+
jpeg2000: "jpeg2000";
|
|
682
|
+
}>;
|
|
683
|
+
base64: z.ZodString;
|
|
684
|
+
jbig2GlobalsBase64: z.ZodOptional<z.ZodString>;
|
|
685
|
+
}, z.core.$strip>>;
|
|
678
686
|
}, z.core.$strip>;
|
|
679
687
|
type LayoutImageAsset = z.infer<typeof LayoutImageAssetSchema>;
|
|
680
688
|
declare const LayoutDestinationTargetSchema: z.ZodObject<{
|
|
@@ -1016,6 +1024,14 @@ declare const LayoutDocumentSchema: z.ZodObject<{
|
|
|
1016
1024
|
base64: z.ZodString;
|
|
1017
1025
|
widthPx: z.ZodNumber;
|
|
1018
1026
|
heightPx: z.ZodNumber;
|
|
1027
|
+
original: z.ZodOptional<z.ZodObject<{
|
|
1028
|
+
filter: z.ZodEnum<{
|
|
1029
|
+
jbig2: "jbig2";
|
|
1030
|
+
jpeg2000: "jpeg2000";
|
|
1031
|
+
}>;
|
|
1032
|
+
base64: z.ZodString;
|
|
1033
|
+
jbig2GlobalsBase64: z.ZodOptional<z.ZodString>;
|
|
1034
|
+
}, z.core.$strip>>;
|
|
1019
1035
|
}, z.core.$strip>>;
|
|
1020
1036
|
destinations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1021
1037
|
name: z.ZodString;
|
package/dist/layout.d.ts
CHANGED
|
@@ -675,6 +675,14 @@ declare const LayoutImageAssetSchema: z.ZodObject<{
|
|
|
675
675
|
base64: z.ZodString;
|
|
676
676
|
widthPx: z.ZodNumber;
|
|
677
677
|
heightPx: z.ZodNumber;
|
|
678
|
+
original: z.ZodOptional<z.ZodObject<{
|
|
679
|
+
filter: z.ZodEnum<{
|
|
680
|
+
jbig2: "jbig2";
|
|
681
|
+
jpeg2000: "jpeg2000";
|
|
682
|
+
}>;
|
|
683
|
+
base64: z.ZodString;
|
|
684
|
+
jbig2GlobalsBase64: z.ZodOptional<z.ZodString>;
|
|
685
|
+
}, z.core.$strip>>;
|
|
678
686
|
}, z.core.$strip>;
|
|
679
687
|
type LayoutImageAsset = z.infer<typeof LayoutImageAssetSchema>;
|
|
680
688
|
declare const LayoutDestinationTargetSchema: z.ZodObject<{
|
|
@@ -1016,6 +1024,14 @@ declare const LayoutDocumentSchema: z.ZodObject<{
|
|
|
1016
1024
|
base64: z.ZodString;
|
|
1017
1025
|
widthPx: z.ZodNumber;
|
|
1018
1026
|
heightPx: z.ZodNumber;
|
|
1027
|
+
original: z.ZodOptional<z.ZodObject<{
|
|
1028
|
+
filter: z.ZodEnum<{
|
|
1029
|
+
jbig2: "jbig2";
|
|
1030
|
+
jpeg2000: "jpeg2000";
|
|
1031
|
+
}>;
|
|
1032
|
+
base64: z.ZodString;
|
|
1033
|
+
jbig2GlobalsBase64: z.ZodOptional<z.ZodString>;
|
|
1034
|
+
}, z.core.$strip>>;
|
|
1019
1035
|
}, z.core.$strip>>;
|
|
1020
1036
|
destinations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1021
1037
|
name: z.ZodString;
|
package/dist/layout.js
CHANGED
|
@@ -177,7 +177,12 @@ const LayoutImageAssetSchema = z.object({
|
|
|
177
177
|
format: z.enum(["png", "jpeg"]),
|
|
178
178
|
base64: z.string(),
|
|
179
179
|
widthPx: z.number().int().positive(),
|
|
180
|
-
heightPx: z.number().int().positive()
|
|
180
|
+
heightPx: z.number().int().positive(),
|
|
181
|
+
original: z.object({
|
|
182
|
+
filter: z.enum(["jbig2", "jpeg2000"]),
|
|
183
|
+
base64: z.string(),
|
|
184
|
+
jbig2GlobalsBase64: z.string().optional()
|
|
185
|
+
}).optional()
|
|
181
186
|
});
|
|
182
187
|
const LayoutDestinationTargetSchema = z.object({
|
|
183
188
|
kind: z.enum([
|
package/dist/read.cjs
CHANGED
|
@@ -497,13 +497,18 @@ function imagePlacementFrom(matrix) {
|
|
|
497
497
|
rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
|
|
498
498
|
};
|
|
499
499
|
}
|
|
500
|
-
function registerExtractedImage(format, bytes, widthPx, heightPx, images) {
|
|
500
|
+
function registerExtractedImage(format, bytes, widthPx, heightPx, images, original) {
|
|
501
501
|
const imageId = `img${require_bytes_crc32.crc32(bytes).toString(16)}`;
|
|
502
502
|
if (!(imageId in images)) images[imageId] = {
|
|
503
503
|
format,
|
|
504
504
|
base64: require_util_base64.bytesToBase64(bytes),
|
|
505
505
|
widthPx,
|
|
506
|
-
heightPx
|
|
506
|
+
heightPx,
|
|
507
|
+
...original !== void 0 ? { original: {
|
|
508
|
+
filter: original.filter,
|
|
509
|
+
base64: require_util_base64.bytesToBase64(original.bytes),
|
|
510
|
+
...original.globalsBytes !== void 0 ? { jbig2GlobalsBase64: require_util_base64.bytesToBase64(original.globalsBytes) } : {}
|
|
511
|
+
} } : {}
|
|
507
512
|
};
|
|
508
513
|
return imageId;
|
|
509
514
|
}
|
|
@@ -514,7 +519,7 @@ function resolveCachedImageId(dict, raw, images, cache, resolver, sink) {
|
|
|
514
519
|
cache.set(dict, null);
|
|
515
520
|
return;
|
|
516
521
|
}
|
|
517
|
-
const imageId = registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images);
|
|
522
|
+
const imageId = registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images, decoded.original);
|
|
518
523
|
cache.set(dict, imageId);
|
|
519
524
|
return imageId;
|
|
520
525
|
}
|
|
@@ -536,7 +541,7 @@ function convertInlineImage(item, pageMatrix, images, resolver, sink) {
|
|
|
536
541
|
if (decoded === void 0) return;
|
|
537
542
|
return {
|
|
538
543
|
kind: "image",
|
|
539
|
-
imageId: registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images),
|
|
544
|
+
imageId: registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images, decoded.original),
|
|
540
545
|
...imagePlacementFrom(require_matrix.multiplyMatrices(item.matrix, pageMatrix)),
|
|
541
546
|
...item.layerName !== void 0 ? { layer: item.layerName } : {}
|
|
542
547
|
};
|
package/dist/read.js
CHANGED
|
@@ -496,13 +496,18 @@ function imagePlacementFrom(matrix) {
|
|
|
496
496
|
rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
|
|
497
497
|
};
|
|
498
498
|
}
|
|
499
|
-
function registerExtractedImage(format, bytes, widthPx, heightPx, images) {
|
|
499
|
+
function registerExtractedImage(format, bytes, widthPx, heightPx, images, original) {
|
|
500
500
|
const imageId = `img${crc32(bytes).toString(16)}`;
|
|
501
501
|
if (!(imageId in images)) images[imageId] = {
|
|
502
502
|
format,
|
|
503
503
|
base64: bytesToBase64(bytes),
|
|
504
504
|
widthPx,
|
|
505
|
-
heightPx
|
|
505
|
+
heightPx,
|
|
506
|
+
...original !== void 0 ? { original: {
|
|
507
|
+
filter: original.filter,
|
|
508
|
+
base64: bytesToBase64(original.bytes),
|
|
509
|
+
...original.globalsBytes !== void 0 ? { jbig2GlobalsBase64: bytesToBase64(original.globalsBytes) } : {}
|
|
510
|
+
} } : {}
|
|
506
511
|
};
|
|
507
512
|
return imageId;
|
|
508
513
|
}
|
|
@@ -513,7 +518,7 @@ function resolveCachedImageId(dict, raw, images, cache, resolver, sink) {
|
|
|
513
518
|
cache.set(dict, null);
|
|
514
519
|
return;
|
|
515
520
|
}
|
|
516
|
-
const imageId = registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images);
|
|
521
|
+
const imageId = registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images, decoded.original);
|
|
517
522
|
cache.set(dict, imageId);
|
|
518
523
|
return imageId;
|
|
519
524
|
}
|
|
@@ -535,7 +540,7 @@ function convertInlineImage(item, pageMatrix, images, resolver, sink) {
|
|
|
535
540
|
if (decoded === void 0) return;
|
|
536
541
|
return {
|
|
537
542
|
kind: "image",
|
|
538
|
-
imageId: registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images),
|
|
543
|
+
imageId: registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images, decoded.original),
|
|
539
544
|
...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix)),
|
|
540
545
|
...item.layerName !== void 0 ? { layer: item.layerName } : {}
|
|
541
546
|
};
|
package/dist/write.cjs
CHANGED
|
@@ -206,7 +206,28 @@ function preparePngImage(bytes, compress) {
|
|
|
206
206
|
}
|
|
207
207
|
};
|
|
208
208
|
}
|
|
209
|
+
function preparePassthroughImage(asset, compress) {
|
|
210
|
+
if (asset.original === void 0) throw new Error("preparePassthroughImage: asset carries no original stream");
|
|
211
|
+
const png = preparePngImage(require_util_base64.base64ToBytes(asset.base64), compress);
|
|
212
|
+
const entries = /* @__PURE__ */ new Map([
|
|
213
|
+
["Type", require_objects.pdfName("XObject")],
|
|
214
|
+
["Subtype", require_objects.pdfName("Image")],
|
|
215
|
+
["Width", require_objects.pdfNum(asset.widthPx)],
|
|
216
|
+
["Height", require_objects.pdfNum(asset.heightPx)],
|
|
217
|
+
["Filter", require_objects.pdfName(asset.original.filter === "jbig2" ? "JBIG2Decode" : "JPXDecode")]
|
|
218
|
+
]);
|
|
219
|
+
if (asset.original.filter === "jbig2") {
|
|
220
|
+
entries.set("ColorSpace", require_objects.pdfName("DeviceGray"));
|
|
221
|
+
entries.set("BitsPerComponent", require_objects.pdfNum(1));
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
dict: require_objects.pdfDict(entries),
|
|
225
|
+
raw: require_util_base64.base64ToBytes(asset.original.base64),
|
|
226
|
+
...png.alpha !== void 0 ? { alpha: png.alpha } : {}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
209
229
|
function prepareImage(asset, compress) {
|
|
230
|
+
if (asset.original !== void 0) return preparePassthroughImage(asset, compress);
|
|
210
231
|
const bytes = require_util_base64.base64ToBytes(asset.base64);
|
|
211
232
|
return asset.format === "jpeg" ? prepareJpegImage(bytes) : preparePngImage(bytes, compress);
|
|
212
233
|
}
|
|
@@ -361,9 +382,13 @@ function writePdf(doc, options = {}) {
|
|
|
361
382
|
const prepared = prepareImage(asset, compress);
|
|
362
383
|
const imageNum = nextObjNum++;
|
|
363
384
|
const smaskNum = prepared.alpha === void 0 ? void 0 : nextObjNum++;
|
|
385
|
+
const globalsBase64 = asset.original?.filter === "jbig2" ? asset.original.jbig2GlobalsBase64 : void 0;
|
|
386
|
+
const globalsNum = globalsBase64 !== void 0 ? nextObjNum++ : void 0;
|
|
364
387
|
imageAllocs.set(imageId, {
|
|
365
388
|
imageNum,
|
|
366
389
|
smaskNum,
|
|
390
|
+
globalsNum,
|
|
391
|
+
globalsBase64,
|
|
367
392
|
resourceName: `Im${index + 1}`,
|
|
368
393
|
prepared
|
|
369
394
|
});
|
|
@@ -708,6 +733,14 @@ function writePdf(doc, options = {}) {
|
|
|
708
733
|
value: require_objects.pdfStream(alloc.prepared.alpha.dict, alloc.prepared.alpha.raw)
|
|
709
734
|
});
|
|
710
735
|
}
|
|
736
|
+
if (alloc.globalsNum !== void 0 && alloc.globalsBase64 !== void 0) {
|
|
737
|
+
const globalsRaw = require_util_base64.base64ToBytes(alloc.globalsBase64);
|
|
738
|
+
alloc.prepared.dict.entries.set("DecodeParms", require_objects.pdfDict({ JBIG2Globals: require_objects.pdfRef(alloc.globalsNum, 0) }));
|
|
739
|
+
objects.push({
|
|
740
|
+
num: alloc.globalsNum,
|
|
741
|
+
value: require_objects.pdfStream(require_objects.pdfDict(compress ? /* @__PURE__ */ new Map([["Filter", require_objects.pdfName("FlateDecode")]]) : /* @__PURE__ */ new Map()), compress ? require_bytes_flate.deflate(globalsRaw) : globalsRaw)
|
|
742
|
+
});
|
|
743
|
+
}
|
|
711
744
|
objects.push({
|
|
712
745
|
num: alloc.imageNum,
|
|
713
746
|
value: require_objects.pdfStream(alloc.prepared.dict, alloc.prepared.raw)
|
package/dist/write.js
CHANGED
|
@@ -205,7 +205,28 @@ function preparePngImage(bytes, compress) {
|
|
|
205
205
|
}
|
|
206
206
|
};
|
|
207
207
|
}
|
|
208
|
+
function preparePassthroughImage(asset, compress) {
|
|
209
|
+
if (asset.original === void 0) throw new Error("preparePassthroughImage: asset carries no original stream");
|
|
210
|
+
const png = preparePngImage(base64ToBytes(asset.base64), compress);
|
|
211
|
+
const entries = /* @__PURE__ */ new Map([
|
|
212
|
+
["Type", pdfName("XObject")],
|
|
213
|
+
["Subtype", pdfName("Image")],
|
|
214
|
+
["Width", pdfNum(asset.widthPx)],
|
|
215
|
+
["Height", pdfNum(asset.heightPx)],
|
|
216
|
+
["Filter", pdfName(asset.original.filter === "jbig2" ? "JBIG2Decode" : "JPXDecode")]
|
|
217
|
+
]);
|
|
218
|
+
if (asset.original.filter === "jbig2") {
|
|
219
|
+
entries.set("ColorSpace", pdfName("DeviceGray"));
|
|
220
|
+
entries.set("BitsPerComponent", pdfNum(1));
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
dict: pdfDict(entries),
|
|
224
|
+
raw: base64ToBytes(asset.original.base64),
|
|
225
|
+
...png.alpha !== void 0 ? { alpha: png.alpha } : {}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
208
228
|
function prepareImage(asset, compress) {
|
|
229
|
+
if (asset.original !== void 0) return preparePassthroughImage(asset, compress);
|
|
209
230
|
const bytes = base64ToBytes(asset.base64);
|
|
210
231
|
return asset.format === "jpeg" ? prepareJpegImage(bytes) : preparePngImage(bytes, compress);
|
|
211
232
|
}
|
|
@@ -360,9 +381,13 @@ function writePdf(doc, options = {}) {
|
|
|
360
381
|
const prepared = prepareImage(asset, compress);
|
|
361
382
|
const imageNum = nextObjNum++;
|
|
362
383
|
const smaskNum = prepared.alpha === void 0 ? void 0 : nextObjNum++;
|
|
384
|
+
const globalsBase64 = asset.original?.filter === "jbig2" ? asset.original.jbig2GlobalsBase64 : void 0;
|
|
385
|
+
const globalsNum = globalsBase64 !== void 0 ? nextObjNum++ : void 0;
|
|
363
386
|
imageAllocs.set(imageId, {
|
|
364
387
|
imageNum,
|
|
365
388
|
smaskNum,
|
|
389
|
+
globalsNum,
|
|
390
|
+
globalsBase64,
|
|
366
391
|
resourceName: `Im${index + 1}`,
|
|
367
392
|
prepared
|
|
368
393
|
});
|
|
@@ -707,6 +732,14 @@ function writePdf(doc, options = {}) {
|
|
|
707
732
|
value: pdfStream(alloc.prepared.alpha.dict, alloc.prepared.alpha.raw)
|
|
708
733
|
});
|
|
709
734
|
}
|
|
735
|
+
if (alloc.globalsNum !== void 0 && alloc.globalsBase64 !== void 0) {
|
|
736
|
+
const globalsRaw = base64ToBytes(alloc.globalsBase64);
|
|
737
|
+
alloc.prepared.dict.entries.set("DecodeParms", pdfDict({ JBIG2Globals: pdfRef(alloc.globalsNum, 0) }));
|
|
738
|
+
objects.push({
|
|
739
|
+
num: alloc.globalsNum,
|
|
740
|
+
value: pdfStream(pdfDict(compress ? /* @__PURE__ */ new Map([["Filter", pdfName("FlateDecode")]]) : /* @__PURE__ */ new Map()), compress ? deflate(globalsRaw) : globalsRaw)
|
|
741
|
+
});
|
|
742
|
+
}
|
|
710
743
|
objects.push({
|
|
711
744
|
num: alloc.imageNum,
|
|
712
745
|
value: pdfStream(alloc.prepared.dict, alloc.prepared.raw)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pdf-parser.js",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"description": "Hand-written, dependency-minimal PDF codec: parses arbitrary real-world PDFs and generates new ones, built on its own codec-owned LayoutDocument item model and Zod 4 codecs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -72,8 +72,8 @@
|
|
|
72
72
|
],
|
|
73
73
|
"license": "MIT",
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"byte-codec": "1.
|
|
76
|
-
"document-schema.js": "7.
|
|
75
|
+
"byte-codec": "1.5.0",
|
|
76
|
+
"document-schema.js": "7.11.0",
|
|
77
77
|
"fflate": "0.8.3",
|
|
78
78
|
"zod": "4.4.3"
|
|
79
79
|
},
|