pdf-codec 4.4.0 → 4.5.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 -0
- package/dist/image/ccitt-encode.cjs +119 -0
- package/dist/image/ccitt-encode.d.cts +9 -0
- package/dist/image/ccitt-encode.d.ts +9 -0
- package/dist/image/ccitt-encode.js +118 -0
- package/dist/image/ccitt.cjs +3 -0
- package/dist/image/ccitt.d.cts +4 -1
- package/dist/image/ccitt.d.ts +4 -1
- package/dist/image/ccitt.js +1 -1
- package/dist/write.cjs +49 -0
- package/dist/write.js +50 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -264,6 +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 are 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. Verbatim passthrough of a source's original JBIG2/JPX compressed bytes would need `LayoutImageAsset` and `document-schema.js`'s own `ContentImageBlock` to carry the original filter and bytes (the model normalises to decoded pixels at read time), a cross-package schema design change recorded in #975's scorecard rather than smuggled into this feature.
|
|
267
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`.
|
|
268
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.
|
|
269
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).
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_image_ccitt = require("./ccitt.cjs");
|
|
3
|
+
//#region src/image/ccitt-encode.ts
|
|
4
|
+
function buildRunCodes(table) {
|
|
5
|
+
const map = /* @__PURE__ */ new Map();
|
|
6
|
+
for (const [bits, run] of table) map.set(run, bits);
|
|
7
|
+
for (const [bits, run] of require_image_ccitt.EXTENDED_MAKEUP) if (!map.has(run)) map.set(run, bits);
|
|
8
|
+
return map;
|
|
9
|
+
}
|
|
10
|
+
const WHITE_RUN_CODES = buildRunCodes(require_image_ccitt.WHITE_TERMINATING_AND_MAKEUP);
|
|
11
|
+
const BLACK_RUN_CODES = buildRunCodes(require_image_ccitt.BLACK_TERMINATING_AND_MAKEUP);
|
|
12
|
+
const PASS_MODE = "0001";
|
|
13
|
+
const HORIZONTAL_MODE = "001";
|
|
14
|
+
const VERTICAL_MODES = /* @__PURE__ */ new Map([
|
|
15
|
+
[0, "1"],
|
|
16
|
+
[1, "011"],
|
|
17
|
+
[-1, "010"],
|
|
18
|
+
[2, "000011"],
|
|
19
|
+
[-2, "000010"],
|
|
20
|
+
[3, "0000011"],
|
|
21
|
+
[-3, "0000010"]
|
|
22
|
+
]);
|
|
23
|
+
const LARGEST_MAKEUP_RUN = 2560;
|
|
24
|
+
const MAKEUP_GRANULARITY = 64;
|
|
25
|
+
var BitWriter = class {
|
|
26
|
+
bytes = [];
|
|
27
|
+
current = 0;
|
|
28
|
+
used = 0;
|
|
29
|
+
get byteLength() {
|
|
30
|
+
return this.bytes.length;
|
|
31
|
+
}
|
|
32
|
+
writeBits(bits) {
|
|
33
|
+
for (const char of bits) {
|
|
34
|
+
this.current = this.current << 1 | (char === "1" ? 1 : 0);
|
|
35
|
+
this.used++;
|
|
36
|
+
if (this.used === 8) {
|
|
37
|
+
this.bytes.push(this.current);
|
|
38
|
+
this.current = 0;
|
|
39
|
+
this.used = 0;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
finish() {
|
|
44
|
+
if (this.used > 0) this.bytes.push(this.current << 8 - this.used);
|
|
45
|
+
return new Uint8Array(this.bytes);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
function writeRun(writer, run, white) {
|
|
49
|
+
const codes = white ? WHITE_RUN_CODES : BLACK_RUN_CODES;
|
|
50
|
+
let remaining = run;
|
|
51
|
+
while (remaining >= MAKEUP_GRANULARITY) {
|
|
52
|
+
const makeup = Math.min(remaining - remaining % MAKEUP_GRANULARITY, LARGEST_MAKEUP_RUN);
|
|
53
|
+
writer.writeBits(codes.get(makeup));
|
|
54
|
+
remaining -= makeup;
|
|
55
|
+
}
|
|
56
|
+
writer.writeBits(codes.get(remaining));
|
|
57
|
+
}
|
|
58
|
+
function changingElements(bitmap, rowByteOffset, columns) {
|
|
59
|
+
const changes = [];
|
|
60
|
+
let previous = 1;
|
|
61
|
+
for (let x = 0; x < columns; x++) {
|
|
62
|
+
const pixel = (bitmap[rowByteOffset + (x >> 3)] ?? 0) >> 7 - (x & 7) & 1;
|
|
63
|
+
if (pixel !== previous) {
|
|
64
|
+
changes.push(x);
|
|
65
|
+
previous = pixel;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return changes;
|
|
69
|
+
}
|
|
70
|
+
function encodeCcittFax(bitmap, options) {
|
|
71
|
+
const { columns, rows, maxBytes } = options;
|
|
72
|
+
if (columns <= 0 || rows <= 0) return /* @__PURE__ */ new Uint8Array(0);
|
|
73
|
+
const rowBytes = columns + 7 >> 3;
|
|
74
|
+
const writer = new BitWriter();
|
|
75
|
+
let reference = [];
|
|
76
|
+
for (let y = 0; y < rows; y++) {
|
|
77
|
+
const offset = y * rowBytes;
|
|
78
|
+
const current = changingElements(bitmap, offset, columns);
|
|
79
|
+
let ci = 0;
|
|
80
|
+
let ri = 0;
|
|
81
|
+
const cur = (i) => current[i] ?? columns;
|
|
82
|
+
const ref = (i) => reference[i] ?? columns;
|
|
83
|
+
let a0 = -1;
|
|
84
|
+
let white = true;
|
|
85
|
+
while (a0 < columns) {
|
|
86
|
+
if (maxBytes !== void 0 && writer.byteLength > maxBytes) return;
|
|
87
|
+
const a1 = cur(ci);
|
|
88
|
+
const a2 = cur(ci + 1);
|
|
89
|
+
while (ri < reference.length && ref(ri) <= a0) ri++;
|
|
90
|
+
let b1Index = ri;
|
|
91
|
+
if (b1Index % 2 !== (white ? 0 : 1)) b1Index++;
|
|
92
|
+
const b1 = ref(b1Index);
|
|
93
|
+
const b2 = ref(b1Index + 1);
|
|
94
|
+
if (b2 < a1) {
|
|
95
|
+
writer.writeBits(PASS_MODE);
|
|
96
|
+
a0 = b2;
|
|
97
|
+
} else {
|
|
98
|
+
const delta = a1 - b1;
|
|
99
|
+
const vertical = VERTICAL_MODES.get(delta);
|
|
100
|
+
if (vertical !== void 0) {
|
|
101
|
+
writer.writeBits(vertical);
|
|
102
|
+
a0 = a1;
|
|
103
|
+
ci++;
|
|
104
|
+
} else {
|
|
105
|
+
writer.writeBits(HORIZONTAL_MODE);
|
|
106
|
+
writeRun(writer, a1 - Math.max(a0, 0), white);
|
|
107
|
+
writeRun(writer, a2 - a1, !white);
|
|
108
|
+
a0 = a2;
|
|
109
|
+
ci += 2;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (a0 < columns) white = ((bitmap[offset + (a0 >> 3)] ?? 0) >> 7 - (a0 & 7) & 1) === 1;
|
|
113
|
+
}
|
|
114
|
+
reference = current;
|
|
115
|
+
}
|
|
116
|
+
return writer.finish();
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
exports.encodeCcittFax = encodeCcittFax;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/image/ccitt-encode.d.ts
|
|
2
|
+
interface EncodeCcittFaxOptions {
|
|
3
|
+
readonly columns: number;
|
|
4
|
+
readonly rows: number;
|
|
5
|
+
readonly maxBytes?: number;
|
|
6
|
+
}
|
|
7
|
+
declare function encodeCcittFax(bitmap: Uint8Array, options: EncodeCcittFaxOptions): Uint8Array<ArrayBuffer> | undefined;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { EncodeCcittFaxOptions, encodeCcittFax };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/image/ccitt-encode.d.ts
|
|
2
|
+
interface EncodeCcittFaxOptions {
|
|
3
|
+
readonly columns: number;
|
|
4
|
+
readonly rows: number;
|
|
5
|
+
readonly maxBytes?: number;
|
|
6
|
+
}
|
|
7
|
+
declare function encodeCcittFax(bitmap: Uint8Array, options: EncodeCcittFaxOptions): Uint8Array<ArrayBuffer> | undefined;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { EncodeCcittFaxOptions, encodeCcittFax };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { BLACK_TERMINATING_AND_MAKEUP, EXTENDED_MAKEUP, WHITE_TERMINATING_AND_MAKEUP } from "./ccitt.js";
|
|
2
|
+
//#region src/image/ccitt-encode.ts
|
|
3
|
+
function buildRunCodes(table) {
|
|
4
|
+
const map = /* @__PURE__ */ new Map();
|
|
5
|
+
for (const [bits, run] of table) map.set(run, bits);
|
|
6
|
+
for (const [bits, run] of EXTENDED_MAKEUP) if (!map.has(run)) map.set(run, bits);
|
|
7
|
+
return map;
|
|
8
|
+
}
|
|
9
|
+
const WHITE_RUN_CODES = buildRunCodes(WHITE_TERMINATING_AND_MAKEUP);
|
|
10
|
+
const BLACK_RUN_CODES = buildRunCodes(BLACK_TERMINATING_AND_MAKEUP);
|
|
11
|
+
const PASS_MODE = "0001";
|
|
12
|
+
const HORIZONTAL_MODE = "001";
|
|
13
|
+
const VERTICAL_MODES = /* @__PURE__ */ new Map([
|
|
14
|
+
[0, "1"],
|
|
15
|
+
[1, "011"],
|
|
16
|
+
[-1, "010"],
|
|
17
|
+
[2, "000011"],
|
|
18
|
+
[-2, "000010"],
|
|
19
|
+
[3, "0000011"],
|
|
20
|
+
[-3, "0000010"]
|
|
21
|
+
]);
|
|
22
|
+
const LARGEST_MAKEUP_RUN = 2560;
|
|
23
|
+
const MAKEUP_GRANULARITY = 64;
|
|
24
|
+
var BitWriter = class {
|
|
25
|
+
bytes = [];
|
|
26
|
+
current = 0;
|
|
27
|
+
used = 0;
|
|
28
|
+
get byteLength() {
|
|
29
|
+
return this.bytes.length;
|
|
30
|
+
}
|
|
31
|
+
writeBits(bits) {
|
|
32
|
+
for (const char of bits) {
|
|
33
|
+
this.current = this.current << 1 | (char === "1" ? 1 : 0);
|
|
34
|
+
this.used++;
|
|
35
|
+
if (this.used === 8) {
|
|
36
|
+
this.bytes.push(this.current);
|
|
37
|
+
this.current = 0;
|
|
38
|
+
this.used = 0;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
finish() {
|
|
43
|
+
if (this.used > 0) this.bytes.push(this.current << 8 - this.used);
|
|
44
|
+
return new Uint8Array(this.bytes);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
function writeRun(writer, run, white) {
|
|
48
|
+
const codes = white ? WHITE_RUN_CODES : BLACK_RUN_CODES;
|
|
49
|
+
let remaining = run;
|
|
50
|
+
while (remaining >= MAKEUP_GRANULARITY) {
|
|
51
|
+
const makeup = Math.min(remaining - remaining % MAKEUP_GRANULARITY, LARGEST_MAKEUP_RUN);
|
|
52
|
+
writer.writeBits(codes.get(makeup));
|
|
53
|
+
remaining -= makeup;
|
|
54
|
+
}
|
|
55
|
+
writer.writeBits(codes.get(remaining));
|
|
56
|
+
}
|
|
57
|
+
function changingElements(bitmap, rowByteOffset, columns) {
|
|
58
|
+
const changes = [];
|
|
59
|
+
let previous = 1;
|
|
60
|
+
for (let x = 0; x < columns; x++) {
|
|
61
|
+
const pixel = (bitmap[rowByteOffset + (x >> 3)] ?? 0) >> 7 - (x & 7) & 1;
|
|
62
|
+
if (pixel !== previous) {
|
|
63
|
+
changes.push(x);
|
|
64
|
+
previous = pixel;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return changes;
|
|
68
|
+
}
|
|
69
|
+
function encodeCcittFax(bitmap, options) {
|
|
70
|
+
const { columns, rows, maxBytes } = options;
|
|
71
|
+
if (columns <= 0 || rows <= 0) return /* @__PURE__ */ new Uint8Array(0);
|
|
72
|
+
const rowBytes = columns + 7 >> 3;
|
|
73
|
+
const writer = new BitWriter();
|
|
74
|
+
let reference = [];
|
|
75
|
+
for (let y = 0; y < rows; y++) {
|
|
76
|
+
const offset = y * rowBytes;
|
|
77
|
+
const current = changingElements(bitmap, offset, columns);
|
|
78
|
+
let ci = 0;
|
|
79
|
+
let ri = 0;
|
|
80
|
+
const cur = (i) => current[i] ?? columns;
|
|
81
|
+
const ref = (i) => reference[i] ?? columns;
|
|
82
|
+
let a0 = -1;
|
|
83
|
+
let white = true;
|
|
84
|
+
while (a0 < columns) {
|
|
85
|
+
if (maxBytes !== void 0 && writer.byteLength > maxBytes) return;
|
|
86
|
+
const a1 = cur(ci);
|
|
87
|
+
const a2 = cur(ci + 1);
|
|
88
|
+
while (ri < reference.length && ref(ri) <= a0) ri++;
|
|
89
|
+
let b1Index = ri;
|
|
90
|
+
if (b1Index % 2 !== (white ? 0 : 1)) b1Index++;
|
|
91
|
+
const b1 = ref(b1Index);
|
|
92
|
+
const b2 = ref(b1Index + 1);
|
|
93
|
+
if (b2 < a1) {
|
|
94
|
+
writer.writeBits(PASS_MODE);
|
|
95
|
+
a0 = b2;
|
|
96
|
+
} else {
|
|
97
|
+
const delta = a1 - b1;
|
|
98
|
+
const vertical = VERTICAL_MODES.get(delta);
|
|
99
|
+
if (vertical !== void 0) {
|
|
100
|
+
writer.writeBits(vertical);
|
|
101
|
+
a0 = a1;
|
|
102
|
+
ci++;
|
|
103
|
+
} else {
|
|
104
|
+
writer.writeBits(HORIZONTAL_MODE);
|
|
105
|
+
writeRun(writer, a1 - Math.max(a0, 0), white);
|
|
106
|
+
writeRun(writer, a2 - a1, !white);
|
|
107
|
+
a0 = a2;
|
|
108
|
+
ci += 2;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (a0 < columns) white = ((bitmap[offset + (a0 >> 3)] ?? 0) >> 7 - (a0 & 7) & 1) === 1;
|
|
112
|
+
}
|
|
113
|
+
reference = current;
|
|
114
|
+
}
|
|
115
|
+
return writer.finish();
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
export { encodeCcittFax };
|
package/dist/image/ccitt.cjs
CHANGED
|
@@ -485,4 +485,7 @@ function decodeCcittFax(data, options = {}) {
|
|
|
485
485
|
};
|
|
486
486
|
}
|
|
487
487
|
//#endregion
|
|
488
|
+
exports.BLACK_TERMINATING_AND_MAKEUP = BLACK_TERMINATING_AND_MAKEUP;
|
|
489
|
+
exports.EXTENDED_MAKEUP = EXTENDED_MAKEUP;
|
|
490
|
+
exports.WHITE_TERMINATING_AND_MAKEUP = WHITE_TERMINATING_AND_MAKEUP;
|
|
488
491
|
exports.decodeCcittFax = decodeCcittFax;
|
package/dist/image/ccitt.d.cts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
//#region src/image/ccitt.d.ts
|
|
2
|
+
declare const WHITE_TERMINATING_AND_MAKEUP: readonly (readonly [string, number])[];
|
|
3
|
+
declare const BLACK_TERMINATING_AND_MAKEUP: readonly (readonly [string, number])[];
|
|
4
|
+
declare const EXTENDED_MAKEUP: readonly (readonly [string, number])[];
|
|
2
5
|
interface CcittFaxOptions {
|
|
3
6
|
readonly k?: number;
|
|
4
7
|
readonly columns?: number;
|
|
@@ -14,4 +17,4 @@ interface CcittFaxImage {
|
|
|
14
17
|
}
|
|
15
18
|
declare function decodeCcittFax(data: Uint8Array<ArrayBuffer>, options?: CcittFaxOptions): CcittFaxImage;
|
|
16
19
|
//#endregion
|
|
17
|
-
export { CcittFaxImage, CcittFaxOptions, decodeCcittFax };
|
|
20
|
+
export { BLACK_TERMINATING_AND_MAKEUP, CcittFaxImage, CcittFaxOptions, EXTENDED_MAKEUP, WHITE_TERMINATING_AND_MAKEUP, decodeCcittFax };
|
package/dist/image/ccitt.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
//#region src/image/ccitt.d.ts
|
|
2
|
+
declare const WHITE_TERMINATING_AND_MAKEUP: readonly (readonly [string, number])[];
|
|
3
|
+
declare const BLACK_TERMINATING_AND_MAKEUP: readonly (readonly [string, number])[];
|
|
4
|
+
declare const EXTENDED_MAKEUP: readonly (readonly [string, number])[];
|
|
2
5
|
interface CcittFaxOptions {
|
|
3
6
|
readonly k?: number;
|
|
4
7
|
readonly columns?: number;
|
|
@@ -14,4 +17,4 @@ interface CcittFaxImage {
|
|
|
14
17
|
}
|
|
15
18
|
declare function decodeCcittFax(data: Uint8Array<ArrayBuffer>, options?: CcittFaxOptions): CcittFaxImage;
|
|
16
19
|
//#endregion
|
|
17
|
-
export { CcittFaxImage, CcittFaxOptions, decodeCcittFax };
|
|
20
|
+
export { BLACK_TERMINATING_AND_MAKEUP, CcittFaxImage, CcittFaxOptions, EXTENDED_MAKEUP, WHITE_TERMINATING_AND_MAKEUP, decodeCcittFax };
|
package/dist/image/ccitt.js
CHANGED
package/dist/write.cjs
CHANGED
|
@@ -12,6 +12,7 @@ const require_util_abort = require("./util/abort.cjs");
|
|
|
12
12
|
const require_crypto_random = require("./crypto/random.cjs");
|
|
13
13
|
const require_encrypt_write = require("./encrypt-write.cjs");
|
|
14
14
|
const require_image_jpeg_info = require("./image/jpeg-info.cjs");
|
|
15
|
+
const require_image_ccitt_encode = require("./image/ccitt-encode.cjs");
|
|
15
16
|
const require_image_png_decode = require("./image/png-decode.cjs");
|
|
16
17
|
const require_embedded_font = require("./embedded-font.cjs");
|
|
17
18
|
const require_content_write = require("./content-write.cjs");
|
|
@@ -145,9 +146,57 @@ function pngImageDict(width, height, colorSpace, compress) {
|
|
|
145
146
|
if (compress) entries.set("Filter", require_objects.pdfName("FlateDecode"));
|
|
146
147
|
return require_objects.pdfDict(entries);
|
|
147
148
|
}
|
|
149
|
+
function packBilevel(raw) {
|
|
150
|
+
const rowBytes = raw.width + 7 >> 3;
|
|
151
|
+
const packed = new Uint8Array(rowBytes * raw.height);
|
|
152
|
+
for (let y = 0; y < raw.height; y++) for (let x = 0; x < raw.width; x++) {
|
|
153
|
+
const sample = raw.data[y * raw.width + x] ?? 0;
|
|
154
|
+
if (sample !== 0 && sample !== 255) return;
|
|
155
|
+
if (sample === 255) {
|
|
156
|
+
const index = y * rowBytes + (x >> 3);
|
|
157
|
+
packed[index] = (packed[index] ?? 0) | 128 >> (x & 7);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return packed;
|
|
161
|
+
}
|
|
148
162
|
function preparePngImage(bytes, compress) {
|
|
149
163
|
const raw = require_image_png_decode.decodePng(bytes);
|
|
150
164
|
const colorSpace = raw.channels === 1 ? "DeviceGray" : "DeviceRGB";
|
|
165
|
+
if (compress && raw.channels === 1 && raw.alpha === void 0) {
|
|
166
|
+
const bilevel = packBilevel(raw);
|
|
167
|
+
if (bilevel !== void 0) {
|
|
168
|
+
const flate = require_bytes_flate.deflate(raw.data);
|
|
169
|
+
const g4 = require_image_ccitt_encode.encodeCcittFax(bilevel, {
|
|
170
|
+
columns: raw.width,
|
|
171
|
+
rows: raw.height,
|
|
172
|
+
maxBytes: flate.length
|
|
173
|
+
});
|
|
174
|
+
if (g4 !== void 0) return {
|
|
175
|
+
dict: require_objects.pdfDict(/* @__PURE__ */ new Map([
|
|
176
|
+
["Type", require_objects.pdfName("XObject")],
|
|
177
|
+
["Subtype", require_objects.pdfName("Image")],
|
|
178
|
+
["Width", require_objects.pdfNum(raw.width)],
|
|
179
|
+
["Height", require_objects.pdfNum(raw.height)],
|
|
180
|
+
["ColorSpace", require_objects.pdfName("DeviceGray")],
|
|
181
|
+
["BitsPerComponent", require_objects.pdfNum(1)],
|
|
182
|
+
["Filter", require_objects.pdfName("CCITTFaxDecode")],
|
|
183
|
+
["DecodeParms", require_objects.pdfDict(/* @__PURE__ */ new Map([
|
|
184
|
+
["K", require_objects.pdfNum(-1)],
|
|
185
|
+
["Columns", require_objects.pdfNum(raw.width)],
|
|
186
|
+
["Rows", require_objects.pdfNum(raw.height)],
|
|
187
|
+
["BlackIs1", require_objects.pdfBool(false)]
|
|
188
|
+
]))]
|
|
189
|
+
])),
|
|
190
|
+
raw: g4,
|
|
191
|
+
alpha: void 0
|
|
192
|
+
};
|
|
193
|
+
return {
|
|
194
|
+
dict: pngImageDict(raw.width, raw.height, colorSpace, true),
|
|
195
|
+
raw: flate,
|
|
196
|
+
alpha: void 0
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
151
200
|
return {
|
|
152
201
|
dict: pngImageDict(raw.width, raw.height, colorSpace, compress),
|
|
153
202
|
raw: compress ? require_bytes_flate.deflate(raw.data) : raw.data,
|
package/dist/write.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as widthOfCode, p as winAnsiGlyphName, t as STANDARD_METRICS } from "./afm-widths-De_QMWgC.js";
|
|
2
2
|
import { NOTES_ANNOTATION_AUTHOR } from "./notes-annotation-author.js";
|
|
3
|
-
import { pdfArray, pdfDict, pdfHexString, pdfLiteralString, pdfName, pdfNull, pdfNum, pdfRef, pdfStream } from "./objects.js";
|
|
3
|
+
import { pdfArray, pdfBool, pdfDict, pdfHexString, pdfLiteralString, pdfName, pdfNull, pdfNum, pdfRef, pdfStream } from "./objects.js";
|
|
4
4
|
import { ByteWriter, concatBytes } from "./bytes/writer.js";
|
|
5
5
|
import { writeObject } from "./serialize.js";
|
|
6
6
|
import { ByteReader } from "./bytes/reader.js";
|
|
@@ -11,6 +11,7 @@ import { throwIfAborted } from "./util/abort.js";
|
|
|
11
11
|
import { randomBytes } from "./crypto/random.js";
|
|
12
12
|
import { createStandardEncryptor, encryptIndirectObject } from "./encrypt-write.js";
|
|
13
13
|
import { readJpegInfo } from "./image/jpeg-info.js";
|
|
14
|
+
import { encodeCcittFax } from "./image/ccitt-encode.js";
|
|
14
15
|
import { decodePng } from "./image/png-decode.js";
|
|
15
16
|
import { collectEmbeddedGlyphs } from "./embedded-font.js";
|
|
16
17
|
import { writeContentStream } from "./content-write.js";
|
|
@@ -144,9 +145,57 @@ function pngImageDict(width, height, colorSpace, compress) {
|
|
|
144
145
|
if (compress) entries.set("Filter", pdfName("FlateDecode"));
|
|
145
146
|
return pdfDict(entries);
|
|
146
147
|
}
|
|
148
|
+
function packBilevel(raw) {
|
|
149
|
+
const rowBytes = raw.width + 7 >> 3;
|
|
150
|
+
const packed = new Uint8Array(rowBytes * raw.height);
|
|
151
|
+
for (let y = 0; y < raw.height; y++) for (let x = 0; x < raw.width; x++) {
|
|
152
|
+
const sample = raw.data[y * raw.width + x] ?? 0;
|
|
153
|
+
if (sample !== 0 && sample !== 255) return;
|
|
154
|
+
if (sample === 255) {
|
|
155
|
+
const index = y * rowBytes + (x >> 3);
|
|
156
|
+
packed[index] = (packed[index] ?? 0) | 128 >> (x & 7);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return packed;
|
|
160
|
+
}
|
|
147
161
|
function preparePngImage(bytes, compress) {
|
|
148
162
|
const raw = decodePng(bytes);
|
|
149
163
|
const colorSpace = raw.channels === 1 ? "DeviceGray" : "DeviceRGB";
|
|
164
|
+
if (compress && raw.channels === 1 && raw.alpha === void 0) {
|
|
165
|
+
const bilevel = packBilevel(raw);
|
|
166
|
+
if (bilevel !== void 0) {
|
|
167
|
+
const flate = deflate(raw.data);
|
|
168
|
+
const g4 = encodeCcittFax(bilevel, {
|
|
169
|
+
columns: raw.width,
|
|
170
|
+
rows: raw.height,
|
|
171
|
+
maxBytes: flate.length
|
|
172
|
+
});
|
|
173
|
+
if (g4 !== void 0) return {
|
|
174
|
+
dict: pdfDict(/* @__PURE__ */ new Map([
|
|
175
|
+
["Type", pdfName("XObject")],
|
|
176
|
+
["Subtype", pdfName("Image")],
|
|
177
|
+
["Width", pdfNum(raw.width)],
|
|
178
|
+
["Height", pdfNum(raw.height)],
|
|
179
|
+
["ColorSpace", pdfName("DeviceGray")],
|
|
180
|
+
["BitsPerComponent", pdfNum(1)],
|
|
181
|
+
["Filter", pdfName("CCITTFaxDecode")],
|
|
182
|
+
["DecodeParms", pdfDict(/* @__PURE__ */ new Map([
|
|
183
|
+
["K", pdfNum(-1)],
|
|
184
|
+
["Columns", pdfNum(raw.width)],
|
|
185
|
+
["Rows", pdfNum(raw.height)],
|
|
186
|
+
["BlackIs1", pdfBool(false)]
|
|
187
|
+
]))]
|
|
188
|
+
])),
|
|
189
|
+
raw: g4,
|
|
190
|
+
alpha: void 0
|
|
191
|
+
};
|
|
192
|
+
return {
|
|
193
|
+
dict: pngImageDict(raw.width, raw.height, colorSpace, true),
|
|
194
|
+
raw: flate,
|
|
195
|
+
alpha: void 0
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
150
199
|
return {
|
|
151
200
|
dict: pngImageDict(raw.width, raw.height, colorSpace, compress),
|
|
152
201
|
raw: compress ? deflate(raw.data) : raw.data,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pdf-codec",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.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": {
|