@poe-platform/safe-bash 0.1.839 → 0.1.840
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mp4-ast/types.d.ts +4 -2
- package/dist/pdf-ast/cos/security.d.ts +10 -0
- package/dist/pdf-ast/edit/retained-attachment-edits.d.ts +4 -0
- package/dist/pdf-ast/edit/retained-graph.d.ts +1 -1
- package/dist/pdf-ast/image.js +1 -1
- package/dist/pdf-ast/index.d.ts +2 -1
- package/dist/pdf-ast/index.js +7 -1
- package/dist/pdf-ast/vendor/pdfjs-fonts.d.mts +8 -0
- package/dist/pdf-ast/vendor/pdfjs-fonts.mjs +28 -0
- package/dist/safe-bash/command-chunks/{chunk-SG2KOOD4.js → chunk-3SAYZFMD.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-FH5VMAPS.js → chunk-45ZH6CNF.js} +20 -4
- package/dist/safe-bash/command-chunks/{chunk-T6OXG7QQ.js → chunk-AAOPVEPF.js} +66 -1
- package/dist/safe-bash/command-chunks/{chunk-46GA576V.js → chunk-CPKVUB7S.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-MBXNG6C3.js → chunk-DLLN3C3Q.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-ZSQCROFE.js → chunk-DM5T5D2K.js} +5 -1
- package/dist/safe-bash/command-chunks/{chunk-3EAZRKI4.js → chunk-G7HQYFIU.js} +10 -1
- package/dist/safe-bash/command-chunks/{chunk-GYKQ3IOV.js → chunk-LMYQMJYY.js} +94 -18
- package/dist/safe-bash/command-chunks/{chunk-DX7KU6YU.js → chunk-N52AHO6S.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-4BSD2RGK.js → chunk-QPKL5GWH.js} +102 -42
- package/dist/safe-bash/command-chunks/{chunk-Z2T7E4ID.js → chunk-R7QLP5RH.js} +1212 -1119
- package/dist/safe-bash/command-chunks/{chunk-RSFKQE2U.js → chunk-T5WFJ44Z.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-77ROD2TR.js → chunk-X3WNWRVT.js} +11 -5
- package/dist/safe-bash/command-chunks/{chunk-TCXM7BSZ.js → chunk-YFMNXPLP.js} +4 -4
- package/dist/safe-bash/command-chunks/{chunk-DQVOBGI2.js → chunk-YVFQ5I36.js} +7335 -7162
- package/dist/safe-bash-command-exiftool/index.js +25 -1
- package/dist/safe-bash-command-ffmpeg/index.js +174 -41
- package/dist/safe-bash-command-ffprobe/index.js +2 -2
- package/dist/safe-bash-command-ffprobe/media.js +2 -2
- package/dist/safe-bash-command-imagemagick/index.js +482 -98
- package/dist/safe-bash-command-mdq/index.js +2 -0
- package/dist/safe-bash-command-pandoc/citeproc-filters.js +7 -7
- package/dist/safe-bash-command-pandoc/command.js +8 -8
- package/dist/safe-bash-command-pandoc/index.js +51 -51
- package/dist/safe-bash-command-qpdf/index.js +1037 -654
- package/dist/safe-bash-command-qpdf/page-range.d.ts +2 -0
- package/dist/safe-bash-command-soffice/index.js +28 -8
- package/dist/safe-bash-command-sqlite3/btree.d.ts +2 -1
- package/dist/safe-bash-command-sqlite3/engine.d.ts +15 -1
- package/dist/safe-bash-command-sqlite3/index.js +294 -68
- package/dist/safe-bash-command-tar/create.js +2 -2
- package/dist/safe-bash-command-tar/extract.js +2 -2
- package/dist/safe-bash-command-tar/format.js +1 -1
- package/dist/safe-bash-command-tar/index.js +4 -4
- package/dist/safe-bash-command-tar/modes.js +4 -4
- package/dist/safe-bash-command-unzip/index.js +1 -1
- package/dist/safe-bash-command-unzip/unzip.js +1 -1
- package/dist/safe-bash-markdown-engine/commonmark-blocks.d.ts +17 -1
- package/dist/safe-bash-markdown-engine/commonmark-blocks.js +3 -3
- package/dist/safe-bash-markdown-engine/commonmark-inlines.d.ts +1 -1
- package/dist/safe-bash-markdown-engine/commonmark-inlines.js +3 -3
- package/dist/safe-bash-markdown-engine/commonmark-syntax.d.ts +5 -0
- package/dist/safe-bash-markdown-engine/commonmark-syntax.js +5 -1
- package/dist/safe-bash-markdown-engine/gfm-syntax.js +2 -2
- package/dist/safe-bash-markdown-engine/index.js +4 -4
- package/dist/safe-bash-zip-engine/index.js +1 -1
- package/dist/safe-bash-zip-engine/zip/repair.js +1 -1
- package/dist/safe-bash-zip-engine/zip/sfx.js +1 -1
- package/dist/safe-bash-zip-engine/zip-format.d.ts +2 -0
- package/dist/safe-bash-zip-engine/zip-format.js +1 -1
- package/package.json +2 -2
- package/third-party/pdf-ast/THIRD_PARTY_NOTICES.md +2 -0
|
@@ -1,21 +1,504 @@
|
|
|
1
1
|
import "../safe-bash/command-chunks/chunk-65DS4IOB.js";
|
|
2
2
|
|
|
3
|
+
// packages/safe-bash-command-qpdf/src/linearization-report.ts
|
|
4
|
+
import { PdfError } from "../pdf-ast/index.js";
|
|
5
|
+
var HintBits = class _HintBits {
|
|
6
|
+
constructor(size, offset = 0) {
|
|
7
|
+
this.size = size;
|
|
8
|
+
this.position = offset * 8;
|
|
9
|
+
}
|
|
10
|
+
size;
|
|
11
|
+
position;
|
|
12
|
+
bytes = new Uint8Array(0);
|
|
13
|
+
start = 0;
|
|
14
|
+
*read(width) {
|
|
15
|
+
if (!Number.isInteger(width) || width < 0 || width > 32 || this.position + width > this.size * 8) throw new PdfError("E_PARSE", "Invalid linearization hint table");
|
|
16
|
+
let value = 0;
|
|
17
|
+
for (let bit = 0; bit < width; bit++, this.position++) {
|
|
18
|
+
const offset = Math.floor(this.position / 8);
|
|
19
|
+
if (offset < this.start || offset >= this.start + this.bytes.length) {
|
|
20
|
+
this.start = offset;
|
|
21
|
+
this.bytes = yield { offset, length: Math.min(4096, this.size - offset) };
|
|
22
|
+
if (!this.bytes.length) throw new PdfError("E_PARSE", "Truncated linearization hint table");
|
|
23
|
+
}
|
|
24
|
+
value = value * 2 + (this.bytes[offset - this.start] >> 7 - this.position % 8 & 1);
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
column(count, width) {
|
|
29
|
+
const cursor = new _HintBits(this.size, this.position / 8);
|
|
30
|
+
const end = this.position + count * width;
|
|
31
|
+
if (!Number.isSafeInteger(end) || end > this.size * 8) throw new PdfError("E_PARSE", "Truncated linearization hint table");
|
|
32
|
+
this.position = Math.ceil(end / 8) * 8;
|
|
33
|
+
return cursor;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
function* linearizationReport(filename, values, size, sharedOffset, outlineOffset) {
|
|
37
|
+
const adjusted = (offset) => offset >= values.H_offset ? offset + values.H_length : offset;
|
|
38
|
+
yield `${filename}: linearization data:
|
|
39
|
+
|
|
40
|
+
`;
|
|
41
|
+
for (const key of ["file_size", "first_page_object", "first_page_end", "npages", "xref_zero_offset", "first_page", "H_offset", "H_length"]) yield `${key}: ${values[key]}
|
|
42
|
+
`;
|
|
43
|
+
if (!size) return;
|
|
44
|
+
yield "\nPage Offsets Hint Table\n\n";
|
|
45
|
+
const page = new HintBits(size), header = {};
|
|
46
|
+
for (const [key, width] of [
|
|
47
|
+
["min_nobjects", 32],
|
|
48
|
+
["first_page_offset", 32],
|
|
49
|
+
["nbits_delta_nobjects", 16],
|
|
50
|
+
["min_page_length", 32],
|
|
51
|
+
["nbits_delta_page_length", 16],
|
|
52
|
+
["min_content_offset", 32],
|
|
53
|
+
["nbits_delta_content_offset", 16],
|
|
54
|
+
["min_content_length", 32],
|
|
55
|
+
["nbits_delta_content_length", 16],
|
|
56
|
+
["nbits_nshared_objects", 16],
|
|
57
|
+
["nbits_shared_identifier", 16],
|
|
58
|
+
["nbits_shared_numerator", 16],
|
|
59
|
+
["shared_denominator", 16]
|
|
60
|
+
]) {
|
|
61
|
+
header[key] = yield* page.read(width);
|
|
62
|
+
yield `${key}: ${key === "first_page_offset" ? adjusted(header[key]) : header[key]}
|
|
63
|
+
`;
|
|
64
|
+
}
|
|
65
|
+
const n = values.npages, objects = page.column(n, header.nbits_delta_nobjects), lengths = page.column(n, header.nbits_delta_page_length);
|
|
66
|
+
const counts = page.column(n, header.nbits_nshared_objects);
|
|
67
|
+
const countScan = new HintBits(size, counts.position / 8);
|
|
68
|
+
let total = 0;
|
|
69
|
+
for (let i = 0; i < n; i++) total += yield* countScan.read(header.nbits_nshared_objects);
|
|
70
|
+
const identifiers = page.column(total, header.nbits_shared_identifier), numerators = page.column(total, header.nbits_shared_numerator);
|
|
71
|
+
const contentOffsets = page.column(n, header.nbits_delta_content_offset), contentLengths = page.column(n, header.nbits_delta_content_length);
|
|
72
|
+
for (let i = 0; i < n; i++) {
|
|
73
|
+
yield `Page ${i}:
|
|
74
|
+
nobjects: ${header.min_nobjects + (yield* objects.read(header.nbits_delta_nobjects))}
|
|
75
|
+
`;
|
|
76
|
+
yield ` length: ${header.min_page_length + (yield* lengths.read(header.nbits_delta_page_length))}
|
|
77
|
+
`;
|
|
78
|
+
yield ` content_offset: ${header.min_content_offset + (yield* contentOffsets.read(header.nbits_delta_content_offset))}
|
|
79
|
+
`;
|
|
80
|
+
yield ` content_length: ${header.min_content_length + (yield* contentLengths.read(header.nbits_delta_content_length))}
|
|
81
|
+
`;
|
|
82
|
+
const count = yield* counts.read(header.nbits_nshared_objects);
|
|
83
|
+
yield ` nshared_objects: ${count}
|
|
84
|
+
`;
|
|
85
|
+
for (let j = 0; j < count; j++) yield ` identifier ${j}: ${yield* identifiers.read(header.nbits_shared_identifier)}
|
|
86
|
+
numerator ${j}: ${yield* numerators.read(header.nbits_shared_numerator)}
|
|
87
|
+
`;
|
|
88
|
+
}
|
|
89
|
+
yield "\nShared Objects Hint Table\n\n";
|
|
90
|
+
const shared = new HintBits(size, sharedOffset), fields = {};
|
|
91
|
+
for (const [key, width] of [["first_shared_obj", 32], ["first_shared_offset", 32], ["nshared_first_page", 32], ["nshared_total", 32], ["nbits_nobjects", 16], ["min_group_length", 32], ["nbits_delta_group_length", 16]]) {
|
|
92
|
+
fields[key] = yield* shared.read(width);
|
|
93
|
+
yield `${key}: ${key === "first_shared_offset" ? adjusted(fields[key]) : fields[key]}
|
|
94
|
+
`;
|
|
95
|
+
}
|
|
96
|
+
const groups = fields.nshared_total, groupLengths = shared.column(groups, fields.nbits_delta_group_length), signatures = shared.column(groups, 1);
|
|
97
|
+
const signatureScan = new HintBits(size, signatures.position / 8);
|
|
98
|
+
for (let i = 0; i < groups; i++) if (yield* signatureScan.read(1)) shared.column(4, 32);
|
|
99
|
+
const groupCounts = shared.column(groups, fields.nbits_nobjects);
|
|
100
|
+
for (let i = 0; i < groups; i++) {
|
|
101
|
+
yield `Shared Object ${i}:
|
|
102
|
+
group length: ${fields.min_group_length + (yield* groupLengths.read(fields.nbits_delta_group_length))}
|
|
103
|
+
`;
|
|
104
|
+
if (yield* signatures.read(1)) yield " signature present\n";
|
|
105
|
+
const count = yield* groupCounts.read(fields.nbits_nobjects);
|
|
106
|
+
if (count) yield ` nobjects: ${count + 1}
|
|
107
|
+
`;
|
|
108
|
+
}
|
|
109
|
+
if (outlineOffset !== void 0) {
|
|
110
|
+
const outline = new HintBits(size, outlineOffset);
|
|
111
|
+
const first = yield* outline.read(32), offset = yield* outline.read(32), count = yield* outline.read(32), length = yield* outline.read(32);
|
|
112
|
+
if (count) yield `
|
|
113
|
+
Outlines Hint Table
|
|
114
|
+
|
|
115
|
+
first_object: ${first}
|
|
116
|
+
first_object_offset: ${adjusted(offset)}
|
|
117
|
+
nobjects: ${count}
|
|
118
|
+
group_length: ${length}
|
|
119
|
+
`;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// packages/safe-bash-command-qpdf/src/encryption-report.ts
|
|
124
|
+
import { getPdfEncryptionInfo } from "../pdf-ast/index.js";
|
|
125
|
+
function encryptionReport(encryption) {
|
|
126
|
+
if (!encryption) return "File is not encrypted\n";
|
|
127
|
+
const info = getPdfEncryptionInfo(encryption), p = encryption.permissions;
|
|
128
|
+
const legacy = encryption.revision === 2;
|
|
129
|
+
const assembly = legacy ? p.modify : p.assemble;
|
|
130
|
+
const forms = legacy ? p.addNotes : p.fillForms;
|
|
131
|
+
const permissions = [
|
|
132
|
+
["extract for accessibility", legacy ? p.copy : encryption.revision >= 5 || p.extractAccessibility],
|
|
133
|
+
["extract for any purpose", p.copy],
|
|
134
|
+
["print low resolution", p.print],
|
|
135
|
+
["print high resolution", p.print && (legacy || p.printHighRes)],
|
|
136
|
+
["modify document assembly", assembly],
|
|
137
|
+
["modify forms", forms],
|
|
138
|
+
["modify annotations", p.addNotes],
|
|
139
|
+
["modify other", p.modify],
|
|
140
|
+
["modify anything", assembly && forms && p.addNotes && p.modify]
|
|
141
|
+
];
|
|
142
|
+
const lines = [`R = ${encryption.revision}`, `P = ${info.permissionsMask}`, `User password = ${info.userPassword}`];
|
|
143
|
+
if (info.ownerPasswordMatched) lines.push("Supplied password is owner password");
|
|
144
|
+
if (info.userPasswordMatched) lines.push("Supplied password is user password");
|
|
145
|
+
for (const [name, allowed] of permissions) lines.push(`${name}: ${allowed ? "allowed" : "not allowed"}`);
|
|
146
|
+
if (encryption.version >= 4) lines.push(`stream encryption method: ${info.streamMethod}`, `string encryption method: ${info.stringMethod}`, `file encryption method: ${info.fileMethod}`);
|
|
147
|
+
return lines.join("\n") + "\n";
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// packages/safe-bash-command-qpdf/src/json-pages.ts
|
|
151
|
+
import { cosDict, cosNumber, decodePdfString as decodePdfString2, dictGet } from "../pdf-ast/index.js";
|
|
152
|
+
|
|
153
|
+
// packages/safe-bash-command-qpdf/src/json-output.ts
|
|
154
|
+
import { IntegerTable, PagedStorage } from "@poe-platform/safe-fs/storage";
|
|
155
|
+
import { PdfNameIndex, decodePdfString } from "../pdf-ast/index.js";
|
|
156
|
+
function jsonRecord(record) {
|
|
157
|
+
return { kind: "object", entries: Object.entries(record) };
|
|
158
|
+
}
|
|
159
|
+
function cosJson(node, storage, signal) {
|
|
160
|
+
if (!node) return null;
|
|
161
|
+
switch (node.kind) {
|
|
162
|
+
case "null":
|
|
163
|
+
return null;
|
|
164
|
+
case "boolean":
|
|
165
|
+
case "number":
|
|
166
|
+
return node.value;
|
|
167
|
+
case "name":
|
|
168
|
+
return `/${node.decoded}`;
|
|
169
|
+
case "string":
|
|
170
|
+
return `u:${decodePdfString(node)}`;
|
|
171
|
+
case "ref":
|
|
172
|
+
return `${node.objectNumber} ${node.generationNumber} R`;
|
|
173
|
+
case "array":
|
|
174
|
+
return { kind: "array", values: (function* () {
|
|
175
|
+
for (const item of node.items) yield cosJson(item, storage, signal);
|
|
176
|
+
})() };
|
|
177
|
+
case "dict":
|
|
178
|
+
return { kind: "object", entries: (async function* () {
|
|
179
|
+
if (node.entries.length > 64) {
|
|
180
|
+
const names = new PdfNameIndex(storage, Infinity, signal), backing = new PagedStorage({ fs: storage.fs, cwd: storage.directory, env: {}, signal }, 4), latest = new IntegerTable(backing);
|
|
181
|
+
let failed = false;
|
|
182
|
+
try {
|
|
183
|
+
for (let index = 0; index < node.entries.length; index++) {
|
|
184
|
+
signal.throwIfAborted();
|
|
185
|
+
const name = await names.intern(node.entries[index].key.decoded);
|
|
186
|
+
await latest.set(BigInt(name.index), BigInt(index));
|
|
187
|
+
}
|
|
188
|
+
for await (const [, index] of latest.entries()) {
|
|
189
|
+
const entry = node.entries[Number(index)];
|
|
190
|
+
yield [`/${entry.key.decoded}`, cosJson(entry.value, storage, signal)];
|
|
191
|
+
}
|
|
192
|
+
} catch (error) {
|
|
193
|
+
failed = true;
|
|
194
|
+
throw error;
|
|
195
|
+
} finally {
|
|
196
|
+
const results = await Promise.allSettled([names.close(), backing.close()]);
|
|
197
|
+
if (!failed) {
|
|
198
|
+
for (const result of results) if (result.status === "rejected") await Promise.reject(result.reason);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
for (let i = 0; i < node.entries.length; i++) {
|
|
204
|
+
const entry = node.entries[i], key = entry.key.decoded;
|
|
205
|
+
if (node.entries.findIndex((pair) => pair.key.decoded === key) !== i) continue;
|
|
206
|
+
let value = entry.value;
|
|
207
|
+
for (let j = node.entries.length - 1; j > i; j--) if (node.entries[j].key.decoded === key) {
|
|
208
|
+
value = node.entries[j].value;
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
yield [`/${key}`, cosJson(value, storage, signal)];
|
|
212
|
+
}
|
|
213
|
+
})() };
|
|
214
|
+
case "stream":
|
|
215
|
+
return jsonRecord({ dict: cosJson(node.dict, storage, signal), length: node.rawBytes.length });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async function* jsonChunks(value, signal) {
|
|
219
|
+
async function* string(parts2) {
|
|
220
|
+
yield '"';
|
|
221
|
+
for await (const part of parts2) for (let at = 0; at < part.length; ) {
|
|
222
|
+
signal.throwIfAborted();
|
|
223
|
+
let end = Math.min(part.length, at + 4096);
|
|
224
|
+
const last = part.charCodeAt(end - 1), next = part.charCodeAt(end);
|
|
225
|
+
if (last >= 55296 && last <= 56319 && next >= 56320 && next <= 57343) end--;
|
|
226
|
+
yield JSON.stringify(part.slice(at, end)).slice(1, -1);
|
|
227
|
+
at = end;
|
|
228
|
+
}
|
|
229
|
+
yield '"';
|
|
230
|
+
}
|
|
231
|
+
async function* parts(node, depth = 0) {
|
|
232
|
+
signal.throwIfAborted();
|
|
233
|
+
if (typeof node === "string") {
|
|
234
|
+
yield* string([node]);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (node === null || typeof node !== "object") {
|
|
238
|
+
yield JSON.stringify(node);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (node.kind === "text") {
|
|
242
|
+
yield* string(node.parts);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (node.kind === "formatted") {
|
|
246
|
+
const decoder = new TextDecoder(), chunks = node.chunks;
|
|
247
|
+
let newline = false;
|
|
248
|
+
async function* text() {
|
|
249
|
+
for await (const bytes of chunks) yield decoder.decode(bytes, { stream: true });
|
|
250
|
+
yield decoder.decode();
|
|
251
|
+
}
|
|
252
|
+
for await (const part of text()) {
|
|
253
|
+
signal.throwIfAborted();
|
|
254
|
+
let at = 0;
|
|
255
|
+
while (at < part.length) {
|
|
256
|
+
if (newline) {
|
|
257
|
+
yield "\n";
|
|
258
|
+
yield " ".repeat(depth);
|
|
259
|
+
newline = false;
|
|
260
|
+
}
|
|
261
|
+
const end = part.indexOf("\n", at);
|
|
262
|
+
if (end < 0) {
|
|
263
|
+
yield part.slice(at);
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
yield part.slice(at, end);
|
|
267
|
+
newline = true;
|
|
268
|
+
at = end + 1;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const object = node.kind === "object";
|
|
274
|
+
yield object ? "{" : "[";
|
|
275
|
+
let first = true;
|
|
276
|
+
if (node.kind === "object") for await (const [key, child] of node.entries) {
|
|
277
|
+
yield first ? "\n" : ",\n";
|
|
278
|
+
first = false;
|
|
279
|
+
yield " ".repeat(depth + 1);
|
|
280
|
+
yield* string([key]);
|
|
281
|
+
yield ": ";
|
|
282
|
+
yield* parts(child, depth + 1);
|
|
283
|
+
}
|
|
284
|
+
else for await (const child of node.values) {
|
|
285
|
+
yield first ? "\n" : ",\n";
|
|
286
|
+
first = false;
|
|
287
|
+
yield " ".repeat(depth + 1);
|
|
288
|
+
yield* parts(child, depth + 1);
|
|
289
|
+
}
|
|
290
|
+
if (!first) {
|
|
291
|
+
yield "\n";
|
|
292
|
+
yield " ".repeat(depth);
|
|
293
|
+
}
|
|
294
|
+
yield object ? "}" : "]";
|
|
295
|
+
}
|
|
296
|
+
const encoder = new TextEncoder();
|
|
297
|
+
let buffer = new Uint8Array(16384), used = 0, work = 0;
|
|
298
|
+
for await (const part of parts(value)) {
|
|
299
|
+
if (++work % 256 === 0) await new Promise((resolve) => setTimeout(resolve, 0));
|
|
300
|
+
signal.throwIfAborted();
|
|
301
|
+
for (let at = 0; at < part.length; ) {
|
|
302
|
+
const result = encoder.encodeInto(part.slice(at), buffer.subarray(used));
|
|
303
|
+
used += result.written;
|
|
304
|
+
at += result.read;
|
|
305
|
+
if (buffer.length - used < 4) {
|
|
306
|
+
yield buffer.subarray(0, used);
|
|
307
|
+
buffer = new Uint8Array(16384);
|
|
308
|
+
used = 0;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (used === buffer.length) {
|
|
313
|
+
yield buffer;
|
|
314
|
+
buffer = new Uint8Array(16384);
|
|
315
|
+
used = 0;
|
|
316
|
+
}
|
|
317
|
+
buffer[used++] = 10;
|
|
318
|
+
yield buffer.subarray(0, used);
|
|
319
|
+
}
|
|
320
|
+
async function* base64Parts(bytes, signal) {
|
|
321
|
+
let pending = 0, count = 0, output = "";
|
|
322
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
323
|
+
for await (const chunk of bytes) for (const byte of chunk) {
|
|
324
|
+
pending = pending << 8 | byte;
|
|
325
|
+
if (++count === 3) {
|
|
326
|
+
output += alphabet[pending >>> 18 & 63] + alphabet[pending >>> 12 & 63] + alphabet[pending >>> 6 & 63] + alphabet[pending & 63];
|
|
327
|
+
pending = 0;
|
|
328
|
+
count = 0;
|
|
329
|
+
}
|
|
330
|
+
if (output.length >= 4096) {
|
|
331
|
+
signal.throwIfAborted();
|
|
332
|
+
yield output;
|
|
333
|
+
output = "";
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (count) {
|
|
337
|
+
pending <<= (3 - count) * 8;
|
|
338
|
+
output += alphabet[pending >>> 18 & 63] + alphabet[pending >>> 12 & 63] + (count === 2 ? alphabet[pending >>> 6 & 63] : "=") + "=";
|
|
339
|
+
}
|
|
340
|
+
if (output) yield output;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// packages/safe-bash-command-qpdf/src/json-pages.ts
|
|
344
|
+
function* qpdfJsonPage(page, reference, index, root, version) {
|
|
345
|
+
function json(node2) {
|
|
346
|
+
if (!node2 || node2.kind === "null") return null;
|
|
347
|
+
switch (node2.kind) {
|
|
348
|
+
case "string":
|
|
349
|
+
return `${version === 1 ? "" : "u:"}${decodePdfString2(node2)}`;
|
|
350
|
+
case "name":
|
|
351
|
+
return `/${node2.decoded}`;
|
|
352
|
+
case "ref":
|
|
353
|
+
return referenceText(node2);
|
|
354
|
+
case "number":
|
|
355
|
+
case "boolean":
|
|
356
|
+
return node2.value;
|
|
357
|
+
case "array":
|
|
358
|
+
return { kind: "array", values: node2.items.map(json) };
|
|
359
|
+
case "dict":
|
|
360
|
+
return jsonRecord(Object.fromEntries(node2.entries.map((entry) => [`/${entry.key.decoded}`, json(entry.value)])));
|
|
361
|
+
case "stream":
|
|
362
|
+
return json(node2.dict);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const referenceText = (ref) => `${ref.objectNumber} ${ref.generationNumber} R`;
|
|
366
|
+
function* dict(node2) {
|
|
367
|
+
const value2 = yield node2;
|
|
368
|
+
return value2?.kind === "stream" ? value2.dict : value2?.kind === "dict" ? value2 : void 0;
|
|
369
|
+
}
|
|
370
|
+
function* tree(node2, children, visit, ancestors = /* @__PURE__ */ new Set()) {
|
|
371
|
+
if (!node2) return;
|
|
372
|
+
const key = node2.kind === "ref" ? referenceText(node2) : void 0;
|
|
373
|
+
if (key && ancestors.has(key)) return;
|
|
374
|
+
if (key) ancestors.add(key);
|
|
375
|
+
try {
|
|
376
|
+
const value2 = yield* dict(node2);
|
|
377
|
+
if (!value2) return;
|
|
378
|
+
yield* visit(node2, value2);
|
|
379
|
+
const child = dictGet(value2, children);
|
|
380
|
+
if (children === "Kids") {
|
|
381
|
+
const kids = yield child;
|
|
382
|
+
if (kids?.kind === "array") for (const item of kids.items) yield* tree(item, children, visit, ancestors);
|
|
383
|
+
} else {
|
|
384
|
+
yield* tree(child, children, visit, ancestors);
|
|
385
|
+
yield* tree(dictGet(value2, "Next"), children, visit, ancestors);
|
|
386
|
+
}
|
|
387
|
+
} finally {
|
|
388
|
+
if (key) ancestors.delete(key);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const contents = [], images = [], outlines = [];
|
|
392
|
+
const node = dictGet(page, "Contents"), value = yield node;
|
|
393
|
+
for (const item of value?.kind === "array" ? value.items : node ? [node] : []) if (item.kind === "ref") contents.push(referenceText(item));
|
|
394
|
+
let resources = dictGet(page, "Resources"), parent = dictGet(page, "Parent");
|
|
395
|
+
const parents = /* @__PURE__ */ new Set();
|
|
396
|
+
while (!resources && parent) {
|
|
397
|
+
if (parent.kind === "ref") {
|
|
398
|
+
const key = referenceText(parent);
|
|
399
|
+
if (parents.has(key)) break;
|
|
400
|
+
parents.add(key);
|
|
401
|
+
}
|
|
402
|
+
const value2 = yield* dict(parent);
|
|
403
|
+
if (!value2) break;
|
|
404
|
+
resources = dictGet(value2, "Resources");
|
|
405
|
+
parent = dictGet(value2, "Parent");
|
|
406
|
+
}
|
|
407
|
+
let label, labelIndex = -1;
|
|
408
|
+
yield* tree(root && dictGet(root, "PageLabels"), "Kids", function* (_node, value2) {
|
|
409
|
+
const nums = yield dictGet(value2, "Nums");
|
|
410
|
+
if (nums?.kind !== "array") return;
|
|
411
|
+
for (let at = 0; at + 1 < nums.items.length; at += 2) {
|
|
412
|
+
const start = yield nums.items[at];
|
|
413
|
+
if (start?.kind === "number" && start.value <= index && start.value >= labelIndex) {
|
|
414
|
+
label = yield* dict(nums.items[at + 1]);
|
|
415
|
+
labelIndex = start.value;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
if (label) {
|
|
420
|
+
const start = yield dictGet(label, "St");
|
|
421
|
+
label = cosDict({ ...Object.fromEntries(label.entries.map((entry) => [entry.key.decoded, entry.value])), St: cosNumber((start?.kind === "number" ? start.value : 1) + index - labelIndex) });
|
|
422
|
+
}
|
|
423
|
+
const resource = yield* dict(resources), objects = resource && (yield* dict(dictGet(resource, "XObject")));
|
|
424
|
+
for (const entry of objects?.entries.slice().sort((a, b) => a.key.decoded < b.key.decoded ? -1 : a.key.decoded > b.key.decoded ? 1 : 0) ?? []) {
|
|
425
|
+
const image = yield* dict(entry.value), subtype = image && (yield dictGet(image, "Subtype"));
|
|
426
|
+
if (!image || subtype?.kind !== "name" || subtype.decoded !== "Image") continue;
|
|
427
|
+
const mask = yield dictGet(image, "ImageMask");
|
|
428
|
+
if (mask?.kind === "boolean" && mask.value) continue;
|
|
429
|
+
const filter = yield dictGet(image, "Filter"), parms = yield dictGet(image, "DecodeParms");
|
|
430
|
+
const filters = filter?.kind === "array" ? filter.items : [filter];
|
|
431
|
+
const parameters = parms?.kind === "array" ? parms.items : filters.map(() => parms);
|
|
432
|
+
images.push(jsonRecord({
|
|
433
|
+
bitspercomponent: json(yield dictGet(image, "BitsPerComponent")),
|
|
434
|
+
colorspace: json(yield dictGet(image, "ColorSpace")),
|
|
435
|
+
decodeparms: { kind: "array", values: parameters.map(json) },
|
|
436
|
+
filter: { kind: "array", values: filters.map(json) },
|
|
437
|
+
filterable: filters.every((item) => !item || item.kind === "null" || item.kind === "name" && ["FlateDecode", "Fl", "LZWDecode", "LZW", "ASCII85Decode", "A85", "ASCIIHexDecode", "AHx", "RunLengthDecode", "RL"].includes(item.decoded)),
|
|
438
|
+
height: json(yield dictGet(image, "Height")),
|
|
439
|
+
name: `/${entry.key.decoded}`,
|
|
440
|
+
object: json(entry.value),
|
|
441
|
+
width: json(yield dictGet(image, "Width"))
|
|
442
|
+
}));
|
|
443
|
+
}
|
|
444
|
+
const outlineRoot = root && (yield* dict(dictGet(root, "Outlines")));
|
|
445
|
+
yield* tree(outlineRoot && dictGet(outlineRoot, "First"), "First", function* (node2, value2) {
|
|
446
|
+
const action = yield* dict(dictGet(value2, "A"));
|
|
447
|
+
let dest = yield dictGet(value2, "Dest") ?? (action && dictGet(action, "D"));
|
|
448
|
+
if (dest?.kind === "name" || dest?.kind === "string") {
|
|
449
|
+
const name = dest.kind === "name" ? dest.decoded : decodePdfString2(dest), old = root && (yield* dict(dictGet(root, "Dests")));
|
|
450
|
+
let found = dest.kind === "name" && old ? dictGet(old, name) : void 0;
|
|
451
|
+
const names = root && (yield* dict(dictGet(root, "Names")));
|
|
452
|
+
if (dest.kind === "string") yield* tree(names && dictGet(names, "Dests"), "Kids", function* (_node, value3) {
|
|
453
|
+
const pairs = yield dictGet(value3, "Names");
|
|
454
|
+
if (pairs?.kind === "array") for (let at = 0; at + 1 < pairs.items.length; at += 2) {
|
|
455
|
+
const key = yield pairs.items[at];
|
|
456
|
+
if (key?.kind === "string" && decodePdfString2(key) === name) found = pairs.items[at + 1];
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
dest = yield found;
|
|
460
|
+
}
|
|
461
|
+
if (dest?.kind === "dict") dest = yield dictGet(dest, "D");
|
|
462
|
+
const target = dest?.kind === "array" ? dest.items[0] : void 0;
|
|
463
|
+
if (target?.kind !== "ref" || referenceText(target) !== referenceText(reference)) return;
|
|
464
|
+
const title = yield dictGet(value2, "Title");
|
|
465
|
+
outlines.push(jsonRecord({ dest: json(dest), object: json(node2), title: title?.kind === "string" ? decodePdfString2(title) : "" }));
|
|
466
|
+
});
|
|
467
|
+
return jsonRecord({ contents: { kind: "array", values: contents }, images: { kind: "array", values: images }, label: json(label), object: referenceText(reference), outlines: { kind: "array", values: outlines }, pageposfrom1: index + 1 });
|
|
468
|
+
}
|
|
469
|
+
|
|
3
470
|
// packages/safe-bash-command-qpdf/src/page-labels.ts
|
|
4
|
-
function
|
|
471
|
+
function parseQpdfPageLabels(specs, pageCount) {
|
|
472
|
+
const labels = [];
|
|
5
473
|
for (const spec of specs) {
|
|
6
474
|
const colon = spec.indexOf(":");
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const parts = spec.slice(colon + 1).split("/")
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
475
|
+
const page = spec.slice(0, colon);
|
|
476
|
+
const digits = page === "z" ? "1" : page.startsWith("r") ? page.slice(1) : page;
|
|
477
|
+
const parts = spec.slice(colon + 1).split("/");
|
|
478
|
+
const style = parts[0] ?? "";
|
|
479
|
+
const number = parts[1] ?? "";
|
|
480
|
+
if (colon <= 0 || digits.length === 0 || [...digits, ...number].some((char) => char < "0" || char > "9") || !["", "D", "a", "A", "r", "R"].includes(style)) {
|
|
481
|
+
throw new Error("page label spec must be n:[D|a|A|r|R][/start[/prefix]]");
|
|
482
|
+
}
|
|
483
|
+
for (const operand of [digits, number]) {
|
|
484
|
+
if (operand !== "" && BigInt(operand) > 9223372036854775807n) {
|
|
485
|
+
throw new Error(`overflow/underflow converting ${operand} to 64-bit integer`);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
const index = page === "z" ? pageCount - 1 : page.startsWith("r") ? pageCount - Number(digits) : Number(page) - 1;
|
|
489
|
+
const start = number === "" ? 1 : Number(number);
|
|
490
|
+
if (labels.length === 0 && index !== 0) throw new Error("the first page label specification must start with page 1");
|
|
491
|
+
if (start < 1) throw new Error("starting page number must be >= 1");
|
|
492
|
+
if (labels.length > 0 && index <= labels[labels.length - 1].index) {
|
|
493
|
+
throw new Error("page label specifications must be in order by first page");
|
|
494
|
+
}
|
|
495
|
+
labels.push({ index, start, prefix: parts.slice(2).join("/"), ...style === "" ? {} : { style } });
|
|
14
496
|
}
|
|
497
|
+
return labels;
|
|
15
498
|
}
|
|
16
499
|
|
|
17
500
|
// packages/safe-bash-command-qpdf/src/split.ts
|
|
18
|
-
import { IntegerTable, PagedStorage } from "@poe-platform/safe-fs/storage";
|
|
501
|
+
import { IntegerTable as IntegerTable2, PagedStorage as PagedStorage2 } from "@poe-platform/safe-fs/storage";
|
|
19
502
|
|
|
20
503
|
// packages/safe-bash-command-qpdf/src/page-range.ts
|
|
21
504
|
function parseSingleTokenPageNumber(tok, totalPages) {
|
|
@@ -34,7 +517,13 @@ function parseSingleTokenPageNumber(tok, totalPages) {
|
|
|
34
517
|
}
|
|
35
518
|
return num;
|
|
36
519
|
}
|
|
520
|
+
var QpdfPageRangeError = class extends Error {
|
|
521
|
+
};
|
|
37
522
|
function* iterateQpdfPageRange(rangeSpec, totalPages) {
|
|
523
|
+
const suffix = rangeSpec.indexOf(":");
|
|
524
|
+
if (suffix !== -1 && rangeSpec.includes(",", suffix)) {
|
|
525
|
+
throw new QpdfPageRangeError(`error at * in numeric range ${rangeSpec.slice(0, suffix)}*${rangeSpec.slice(suffix)}: expected :even or :odd`);
|
|
526
|
+
}
|
|
38
527
|
const ranges = [];
|
|
39
528
|
let groupParity;
|
|
40
529
|
for (const raw of rangeSpec.trim().split(",").filter((part) => part.length > 0)) {
|
|
@@ -126,8 +615,8 @@ function formatSplitName(template, total, group, start, end) {
|
|
|
126
615
|
return `${stem}-${group === 1 ? first : `${first}-${last}`}.pdf`;
|
|
127
616
|
}
|
|
128
617
|
async function* splitPageOutputs(document, storage, target, group, edits, signal, normalizeContent = false) {
|
|
129
|
-
const backing = edits.length ? new
|
|
130
|
-
const rotations = backing ? new
|
|
618
|
+
const backing = edits.length ? new PagedStorage2({ fs: storage.fs, cwd: storage.directory, env: {}, signal }, 4) : void 0;
|
|
619
|
+
const rotations = backing ? new IntegerTable2(backing) : void 0;
|
|
131
620
|
let failed = false;
|
|
132
621
|
try {
|
|
133
622
|
let count = 0;
|
|
@@ -166,7 +655,7 @@ async function* splitPageOutputs(document, storage, target, group, edits, signal
|
|
|
166
655
|
}
|
|
167
656
|
|
|
168
657
|
// packages/safe-bash-command-qpdf/src/display.ts
|
|
169
|
-
import { decodePdfString } from "../pdf-ast/index.js";
|
|
658
|
+
import { decodePdfString as decodePdfString3 } from "../pdf-ast/index.js";
|
|
170
659
|
function* displayNodeParts(node) {
|
|
171
660
|
if (!node) {
|
|
172
661
|
yield "null";
|
|
@@ -188,7 +677,7 @@ function* displayNodeParts(node) {
|
|
|
188
677
|
return;
|
|
189
678
|
case "string":
|
|
190
679
|
yield "(";
|
|
191
|
-
yield
|
|
680
|
+
yield decodePdfString3(node);
|
|
192
681
|
yield ")";
|
|
193
682
|
return;
|
|
194
683
|
case "ref":
|
|
@@ -305,324 +794,134 @@ async function applyJsonInput(owner, source, options, signal) {
|
|
|
305
794
|
|
|
306
795
|
// packages/safe-bash-command-qpdf/src/json.ts
|
|
307
796
|
import { IntegerTable as IntegerTable4, PagedStorage as PagedStorage4 } from "@poe-platform/safe-fs/storage";
|
|
308
|
-
import { PdfError as
|
|
797
|
+
import { PdfError as PdfError3, PdfFileSource, PdfMutableObjectStore as PdfMutableObjectStore2, PdfStagedOutputs, cosArray, cosDict as cosDict2, cosNumber as cosNumber2, cosString, decodePdfString as decodePdfString5, dictGet as dictGet3 } from "../pdf-ast/index.js";
|
|
309
798
|
|
|
310
799
|
// packages/safe-bash-command-qpdf/src/attachments.ts
|
|
311
|
-
import { IntegerTable as
|
|
312
|
-
import { PdfError, PdfMutableObjectStore, PdfNameIndex, decodePdfString as
|
|
800
|
+
import { IntegerTable as IntegerTable3, PagedStorage as PagedStorage3 } from "@poe-platform/safe-fs/storage";
|
|
801
|
+
import { PdfError as PdfError2, PdfMutableObjectStore, PdfNameIndex as PdfNameIndex2, decodePdfString as decodePdfString4, dictGet as dictGet2 } from "../pdf-ast/index.js";
|
|
313
802
|
var QpdfMissingAttachment = class extends Error {
|
|
314
803
|
constructor(key) {
|
|
315
804
|
super(`qpdf: attachment ${key} not found
|
|
316
|
-
`);
|
|
317
|
-
}
|
|
318
|
-
};
|
|
319
|
-
async function* retainedAttachments(document, storage, signal) {
|
|
320
|
-
const values = new PdfMutableObjectStore(storage, { signal }), names = new
|
|
321
|
-
const backing = new
|
|
322
|
-
const visited = new
|
|
323
|
-
let failed = false, top = -1, work = 0;
|
|
324
|
-
async function dictionary(node) {
|
|
325
|
-
const resolved = await document.lookup(node);
|
|
326
|
-
return resolved?.value.kind === "dict" && !resolved.stream ? resolved.value : void 0;
|
|
327
|
-
}
|
|
328
|
-
async function* specifications() {
|
|
329
|
-
const root = await dictionary(document.crossReference.rootRef), nameRoot = await dictionary(root &&
|
|
330
|
-
let node = nameRoot &&
|
|
331
|
-
for (; ; ) {
|
|
332
|
-
signal.throwIfAborted();
|
|
333
|
-
if (++work % 64 === 0) await new Promise((resolve) => setTimeout(resolve, 0));
|
|
334
|
-
const dict = await dictionary(node);
|
|
335
|
-
if (dict) {
|
|
336
|
-
const entries = (await document.lookup(
|
|
337
|
-
if (entries?.kind === "array") for (let index = 0; index + 1 < entries.items.length; index += 2) {
|
|
338
|
-
const label = (await document.lookup(entries.items[index]))?.value;
|
|
339
|
-
yield { node: entries.items[index + 1], fallback: label?.kind === "string" ?
|
|
340
|
-
}
|
|
341
|
-
const kids = (await document.lookup(
|
|
342
|
-
if (kids?.kind === "array" && kids.items.length) {
|
|
343
|
-
const ref = await values.allocate(kids), bytes = new Uint8Array(24), view = new DataView(bytes.buffer);
|
|
344
|
-
view.setFloat64(0, top);
|
|
345
|
-
view.setFloat64(8, ref.objectNumber);
|
|
346
|
-
const position = backing.allocate(24);
|
|
347
|
-
await backing.write(position, bytes);
|
|
348
|
-
top = position;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
node = void 0;
|
|
352
|
-
while (top >= 0) {
|
|
353
|
-
const bytes = await backing.read(top, 24), frame = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
354
|
-
const kids = (await values.get(frame.getFloat64(8))).value;
|
|
355
|
-
if (kids.kind !== "array") throw new Error("Invalid retained attachment traversal frame");
|
|
356
|
-
const index = frame.getFloat64(16), child = kids.items[index];
|
|
357
|
-
if (!child) {
|
|
358
|
-
top = frame.getFloat64(0);
|
|
359
|
-
continue;
|
|
360
|
-
}
|
|
361
|
-
frame.setFloat64(16, index + 1);
|
|
362
|
-
await backing.write(top, bytes);
|
|
363
|
-
if (child.kind === "ref") {
|
|
364
|
-
if (await visited.get(BigInt(child.objectNumber)) !== void 0) continue;
|
|
365
|
-
await visited.set(BigInt(child.objectNumber), 1n);
|
|
366
|
-
}
|
|
367
|
-
node = child;
|
|
368
|
-
break;
|
|
369
|
-
}
|
|
370
|
-
if (!node) break;
|
|
371
|
-
}
|
|
372
|
-
const associated = (await document.lookup(root && dictGet(root, "AF")))?.value;
|
|
373
|
-
if (associated?.kind === "array") for (const item of associated.items) yield { node: item, fallback: "" };
|
|
374
|
-
for await (const page of document.pages()) {
|
|
375
|
-
const annotations = (await document.lookup(dictGet(page.dict, "Annots")))?.value;
|
|
376
|
-
if (annotations?.kind !== "array") continue;
|
|
377
|
-
for (const item of annotations.items) {
|
|
378
|
-
const annotation = await dictionary(item), subtype = annotation && dictGet(annotation, "Subtype");
|
|
379
|
-
if (subtype?.kind !== "name" || subtype.decoded !== "FileAttachment") continue;
|
|
380
|
-
const file = dictGet(annotation, "FS");
|
|
381
|
-
if (file) yield { node: file, fallback: "" };
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
try {
|
|
386
|
-
for await (const spec of specifications()) {
|
|
387
|
-
const dict = await dictionary(spec.node);
|
|
388
|
-
if (!dict) continue;
|
|
389
|
-
const filenameNode = (await document.lookup(dictGet(dict, "UF") ?? dictGet(dict, "F")))?.value;
|
|
390
|
-
const filename = filenameNode?.kind === "string" ? decodePdfString2(filenameNode) : spec.fallback, name = spec.fallback || filename;
|
|
391
|
-
const identity = (await names.intern(name)).index;
|
|
392
|
-
if (await accepted.get(BigInt(identity)) !== void 0) continue;
|
|
393
|
-
const embedded = await dictionary(dictGet(dict, "EF"));
|
|
394
|
-
const stream = embedded && await document.lookup(dictGet(embedded, "UF") ?? dictGet(embedded, "F") ?? dictGet(embedded, "DOS") ?? dictGet(embedded, "Mac") ?? dictGet(embedded, "Unix"));
|
|
395
|
-
if (!stream?.stream || !stream.reference) continue;
|
|
396
|
-
await accepted.set(BigInt(identity), 1n);
|
|
397
|
-
yield { key: name, filename, reference: stream.reference };
|
|
398
|
-
}
|
|
399
|
-
} catch (error) {
|
|
400
|
-
failed = true;
|
|
401
|
-
if (error instanceof PdfError && error.message.startsWith("Unsupported streaming PDF filter: ")) {
|
|
402
|
-
throw new PdfError(error.code, `Unsupported PDF filter: ${error.message.slice("Unsupported streaming PDF filter: ".length)}`);
|
|
403
|
-
}
|
|
404
|
-
throw error;
|
|
405
|
-
} finally {
|
|
406
|
-
const results = await Promise.allSettled([values.close(), names.close(), backing.close()]);
|
|
407
|
-
if (!failed) {
|
|
408
|
-
for (const result of results) if (result.status === "rejected") await Promise.reject(result.reason);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
async function* attachmentChunks(document, storage, key, signal) {
|
|
413
|
-
let found = false;
|
|
414
|
-
try {
|
|
415
|
-
for await (const attachment of retainedAttachments(document, storage, signal)) {
|
|
416
|
-
const selected = key !== void 0 && !found && (attachment.key === key || attachment.filename === key);
|
|
417
|
-
if (selected) found = true;
|
|
418
|
-
for await (const bytes of document.objects.decodeStream(attachment.reference.objectNumber, attachment.reference.generationNumber)) {
|
|
419
|
-
signal.throwIfAborted();
|
|
420
|
-
if (selected) yield bytes;
|
|
421
|
-
}
|
|
422
|
-
if (key === void 0) yield* encodeDisplayParts([attachment.key, " -> ", attachment.filename, "\n"], signal);
|
|
423
|
-
}
|
|
424
|
-
if (key !== void 0 && !found) throw new QpdfMissingAttachment(key);
|
|
425
|
-
} catch (error) {
|
|
426
|
-
if (error instanceof PdfError && error.message.startsWith("Unsupported streaming PDF filter: ")) {
|
|
427
|
-
throw new PdfError(error.code, `Unsupported PDF filter: ${error.message.slice("Unsupported streaming PDF filter: ".length)}`);
|
|
428
|
-
}
|
|
429
|
-
throw error;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// packages/safe-bash-command-qpdf/src/object-order.ts
|
|
434
|
-
import { retainedObjectOrder } from "../pdf-ast/index.js";
|
|
435
|
-
|
|
436
|
-
// packages/safe-bash-command-qpdf/src/json-output.ts
|
|
437
|
-
import { IntegerTable as IntegerTable3, PagedStorage as PagedStorage3 } from "@poe-platform/safe-fs/storage";
|
|
438
|
-
import { PdfNameIndex as PdfNameIndex2, decodePdfString as decodePdfString3 } from "../pdf-ast/index.js";
|
|
439
|
-
function jsonRecord(record) {
|
|
440
|
-
return { kind: "object", entries: Object.entries(record) };
|
|
441
|
-
}
|
|
442
|
-
function cosJson(node, storage, signal) {
|
|
443
|
-
if (!node) return null;
|
|
444
|
-
switch (node.kind) {
|
|
445
|
-
case "null":
|
|
446
|
-
return null;
|
|
447
|
-
case "boolean":
|
|
448
|
-
case "number":
|
|
449
|
-
return node.value;
|
|
450
|
-
case "name":
|
|
451
|
-
return `/${node.decoded}`;
|
|
452
|
-
case "string":
|
|
453
|
-
return `u:${decodePdfString3(node)}`;
|
|
454
|
-
case "ref":
|
|
455
|
-
return `${node.objectNumber} ${node.generationNumber} R`;
|
|
456
|
-
case "array":
|
|
457
|
-
return { kind: "array", values: (function* () {
|
|
458
|
-
for (const item of node.items) yield cosJson(item, storage, signal);
|
|
459
|
-
})() };
|
|
460
|
-
case "dict":
|
|
461
|
-
return { kind: "object", entries: (async function* () {
|
|
462
|
-
if (node.entries.length > 64) {
|
|
463
|
-
const names = new PdfNameIndex2(storage, Infinity, signal), backing = new PagedStorage3({ fs: storage.fs, cwd: storage.directory, env: {}, signal }, 4), latest = new IntegerTable3(backing);
|
|
464
|
-
let failed = false;
|
|
465
|
-
try {
|
|
466
|
-
for (let index = 0; index < node.entries.length; index++) {
|
|
467
|
-
signal.throwIfAborted();
|
|
468
|
-
const name = await names.intern(node.entries[index].key.decoded);
|
|
469
|
-
await latest.set(BigInt(name.index), BigInt(index));
|
|
470
|
-
}
|
|
471
|
-
for await (const [, index] of latest.entries()) {
|
|
472
|
-
const entry = node.entries[Number(index)];
|
|
473
|
-
yield [`/${entry.key.decoded}`, cosJson(entry.value, storage, signal)];
|
|
474
|
-
}
|
|
475
|
-
} catch (error) {
|
|
476
|
-
failed = true;
|
|
477
|
-
throw error;
|
|
478
|
-
} finally {
|
|
479
|
-
const results = await Promise.allSettled([names.close(), backing.close()]);
|
|
480
|
-
if (!failed) {
|
|
481
|
-
for (const result of results) if (result.status === "rejected") await Promise.reject(result.reason);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
486
|
-
for (let i = 0; i < node.entries.length; i++) {
|
|
487
|
-
const entry = node.entries[i], key = entry.key.decoded;
|
|
488
|
-
if (node.entries.findIndex((pair) => pair.key.decoded === key) !== i) continue;
|
|
489
|
-
let value = entry.value;
|
|
490
|
-
for (let j = node.entries.length - 1; j > i; j--) if (node.entries[j].key.decoded === key) {
|
|
491
|
-
value = node.entries[j].value;
|
|
492
|
-
break;
|
|
493
|
-
}
|
|
494
|
-
yield [`/${key}`, cosJson(value, storage, signal)];
|
|
495
|
-
}
|
|
496
|
-
})() };
|
|
497
|
-
case "stream":
|
|
498
|
-
return jsonRecord({ dict: cosJson(node.dict, storage, signal), length: node.rawBytes.length });
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
async function* jsonChunks(value, signal) {
|
|
502
|
-
async function* string(parts2) {
|
|
503
|
-
yield '"';
|
|
504
|
-
for await (const part of parts2) for (let at = 0; at < part.length; ) {
|
|
505
|
-
signal.throwIfAborted();
|
|
506
|
-
let end = Math.min(part.length, at + 4096);
|
|
507
|
-
const last = part.charCodeAt(end - 1), next = part.charCodeAt(end);
|
|
508
|
-
if (last >= 55296 && last <= 56319 && next >= 56320 && next <= 57343) end--;
|
|
509
|
-
yield JSON.stringify(part.slice(at, end)).slice(1, -1);
|
|
510
|
-
at = end;
|
|
511
|
-
}
|
|
512
|
-
yield '"';
|
|
513
|
-
}
|
|
514
|
-
async function* parts(node, depth = 0) {
|
|
515
|
-
signal.throwIfAborted();
|
|
516
|
-
if (typeof node === "string") {
|
|
517
|
-
yield* string([node]);
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
if (node === null || typeof node !== "object") {
|
|
521
|
-
yield JSON.stringify(node);
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
if (node.kind === "text") {
|
|
525
|
-
yield* string(node.parts);
|
|
526
|
-
return;
|
|
527
|
-
}
|
|
528
|
-
if (node.kind === "formatted") {
|
|
529
|
-
const decoder = new TextDecoder(), chunks = node.chunks;
|
|
530
|
-
let newline = false;
|
|
531
|
-
async function* text() {
|
|
532
|
-
for await (const bytes of chunks) yield decoder.decode(bytes, { stream: true });
|
|
533
|
-
yield decoder.decode();
|
|
805
|
+
`);
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
async function* retainedAttachments(document, storage, signal) {
|
|
809
|
+
const values = new PdfMutableObjectStore(storage, { signal }), names = new PdfNameIndex2(storage, Infinity, signal);
|
|
810
|
+
const backing = new PagedStorage3({ fs: storage.fs, cwd: storage.directory, env: {}, signal }, 4);
|
|
811
|
+
const visited = new IntegerTable3(backing), accepted = new IntegerTable3(backing);
|
|
812
|
+
let failed = false, top = -1, work = 0;
|
|
813
|
+
async function dictionary(node) {
|
|
814
|
+
const resolved = await document.lookup(node);
|
|
815
|
+
return resolved?.value.kind === "dict" && !resolved.stream ? resolved.value : void 0;
|
|
816
|
+
}
|
|
817
|
+
async function* specifications() {
|
|
818
|
+
const root = await dictionary(document.crossReference.rootRef), nameRoot = await dictionary(root && dictGet2(root, "Names"));
|
|
819
|
+
let node = nameRoot && dictGet2(nameRoot, "EmbeddedFiles");
|
|
820
|
+
for (; ; ) {
|
|
821
|
+
signal.throwIfAborted();
|
|
822
|
+
if (++work % 64 === 0) await new Promise((resolve) => setTimeout(resolve, 0));
|
|
823
|
+
const dict = await dictionary(node);
|
|
824
|
+
if (dict) {
|
|
825
|
+
const entries = (await document.lookup(dictGet2(dict, "Names")))?.value;
|
|
826
|
+
if (entries?.kind === "array") for (let index = 0; index + 1 < entries.items.length; index += 2) {
|
|
827
|
+
const label = (await document.lookup(entries.items[index]))?.value;
|
|
828
|
+
yield { node: entries.items[index + 1], fallback: label?.kind === "string" ? decodePdfString4(label) : `att_${index}` };
|
|
829
|
+
}
|
|
830
|
+
const kids = (await document.lookup(dictGet2(dict, "Kids")))?.value;
|
|
831
|
+
if (kids?.kind === "array" && kids.items.length) {
|
|
832
|
+
const ref = await values.allocate(kids), bytes = new Uint8Array(24), view = new DataView(bytes.buffer);
|
|
833
|
+
view.setFloat64(0, top);
|
|
834
|
+
view.setFloat64(8, ref.objectNumber);
|
|
835
|
+
const position = backing.allocate(24);
|
|
836
|
+
await backing.write(position, bytes);
|
|
837
|
+
top = position;
|
|
838
|
+
}
|
|
534
839
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
newline = true;
|
|
551
|
-
at = end + 1;
|
|
840
|
+
node = void 0;
|
|
841
|
+
while (top >= 0) {
|
|
842
|
+
const bytes = await backing.read(top, 24), frame = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
843
|
+
const kids = (await values.get(frame.getFloat64(8))).value;
|
|
844
|
+
if (kids.kind !== "array") throw new Error("Invalid retained attachment traversal frame");
|
|
845
|
+
const index = frame.getFloat64(16), child = kids.items[index];
|
|
846
|
+
if (!child) {
|
|
847
|
+
top = frame.getFloat64(0);
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
frame.setFloat64(16, index + 1);
|
|
851
|
+
await backing.write(top, bytes);
|
|
852
|
+
if (child.kind === "ref") {
|
|
853
|
+
if (await visited.get(BigInt(child.objectNumber)) !== void 0) continue;
|
|
854
|
+
await visited.set(BigInt(child.objectNumber), 1n);
|
|
552
855
|
}
|
|
856
|
+
node = child;
|
|
857
|
+
break;
|
|
553
858
|
}
|
|
554
|
-
|
|
859
|
+
if (!node) break;
|
|
555
860
|
}
|
|
556
|
-
const
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
861
|
+
const associated = (await document.lookup(root && dictGet2(root, "AF")))?.value;
|
|
862
|
+
if (associated?.kind === "array") for (const item of associated.items) yield { node: item, fallback: "" };
|
|
863
|
+
for await (const page of document.pages()) {
|
|
864
|
+
const annotations = (await document.lookup(dictGet2(page.dict, "Annots")))?.value;
|
|
865
|
+
if (annotations?.kind !== "array") continue;
|
|
866
|
+
for (const item of annotations.items) {
|
|
867
|
+
const annotation = await dictionary(item), subtype = annotation && dictGet2(annotation, "Subtype");
|
|
868
|
+
if (subtype?.kind !== "name" || subtype.decoded !== "FileAttachment") continue;
|
|
869
|
+
const file = dictGet2(annotation, "FS");
|
|
870
|
+
if (file) yield { node: file, fallback: "" };
|
|
871
|
+
}
|
|
566
872
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
873
|
+
}
|
|
874
|
+
try {
|
|
875
|
+
for await (const spec of specifications()) {
|
|
876
|
+
const dict = await dictionary(spec.node);
|
|
877
|
+
if (!dict) continue;
|
|
878
|
+
const filenameNode = (await document.lookup(dictGet2(dict, "UF") ?? dictGet2(dict, "F")))?.value;
|
|
879
|
+
const filename = filenameNode?.kind === "string" ? decodePdfString4(filenameNode) : spec.fallback, name = spec.fallback || filename;
|
|
880
|
+
const identity = (await names.intern(name)).index;
|
|
881
|
+
if (await accepted.get(BigInt(identity)) !== void 0) continue;
|
|
882
|
+
const embedded = await dictionary(dictGet2(dict, "EF"));
|
|
883
|
+
const stream = embedded && await document.lookup(dictGet2(embedded, "UF") ?? dictGet2(embedded, "F") ?? dictGet2(embedded, "DOS") ?? dictGet2(embedded, "Mac") ?? dictGet2(embedded, "Unix"));
|
|
884
|
+
if (!stream?.stream || !stream.reference) continue;
|
|
885
|
+
await accepted.set(BigInt(identity), 1n);
|
|
886
|
+
yield { key: name, filename, reference: stream.reference };
|
|
572
887
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
888
|
+
} catch (error) {
|
|
889
|
+
failed = true;
|
|
890
|
+
if (error instanceof PdfError2 && error.message.startsWith("Unsupported streaming PDF filter: ")) {
|
|
891
|
+
throw new PdfError2(error.code, `Unsupported PDF filter: ${error.message.slice("Unsupported streaming PDF filter: ".length)}`);
|
|
576
892
|
}
|
|
577
|
-
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
if (++work % 256 === 0) await new Promise((resolve) => setTimeout(resolve, 0));
|
|
583
|
-
signal.throwIfAborted();
|
|
584
|
-
for (let at = 0; at < part.length; ) {
|
|
585
|
-
const result = encoder.encodeInto(part.slice(at), buffer.subarray(used));
|
|
586
|
-
used += result.written;
|
|
587
|
-
at += result.read;
|
|
588
|
-
if (buffer.length - used < 4) {
|
|
589
|
-
yield buffer.subarray(0, used);
|
|
590
|
-
buffer = new Uint8Array(16384);
|
|
591
|
-
used = 0;
|
|
592
|
-
}
|
|
893
|
+
throw error;
|
|
894
|
+
} finally {
|
|
895
|
+
const results = await Promise.allSettled([values.close(), names.close(), backing.close()]);
|
|
896
|
+
if (!failed) {
|
|
897
|
+
for (const result of results) if (result.status === "rejected") await Promise.reject(result.reason);
|
|
593
898
|
}
|
|
594
899
|
}
|
|
595
|
-
if (used === buffer.length) {
|
|
596
|
-
yield buffer;
|
|
597
|
-
buffer = new Uint8Array(16384);
|
|
598
|
-
used = 0;
|
|
599
|
-
}
|
|
600
|
-
buffer[used++] = 10;
|
|
601
|
-
yield buffer.subarray(0, used);
|
|
602
900
|
}
|
|
603
|
-
async function*
|
|
604
|
-
let
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
901
|
+
async function* attachmentChunks(document, storage, key, signal) {
|
|
902
|
+
let found = false;
|
|
903
|
+
try {
|
|
904
|
+
for await (const attachment of retainedAttachments(document, storage, signal)) {
|
|
905
|
+
const selected = key !== void 0 && !found && (attachment.key === key || attachment.filename === key);
|
|
906
|
+
if (selected) found = true;
|
|
907
|
+
for await (const bytes of document.objects.decodeStream(attachment.reference.objectNumber, attachment.reference.generationNumber)) {
|
|
908
|
+
signal.throwIfAborted();
|
|
909
|
+
if (selected) yield bytes;
|
|
910
|
+
}
|
|
911
|
+
if (key === void 0) yield* encodeDisplayParts([attachment.key, " -> ", attachment.filename, "\n"], signal);
|
|
612
912
|
}
|
|
613
|
-
if (
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
913
|
+
if (key !== void 0 && !found) throw new QpdfMissingAttachment(key);
|
|
914
|
+
} catch (error) {
|
|
915
|
+
if (error instanceof PdfError2 && error.message.startsWith("Unsupported streaming PDF filter: ")) {
|
|
916
|
+
throw new PdfError2(error.code, `Unsupported PDF filter: ${error.message.slice("Unsupported streaming PDF filter: ".length)}`);
|
|
617
917
|
}
|
|
918
|
+
throw error;
|
|
618
919
|
}
|
|
619
|
-
if (count) {
|
|
620
|
-
pending <<= (3 - count) * 8;
|
|
621
|
-
output += alphabet[pending >>> 18 & 63] + alphabet[pending >>> 12 & 63] + (count === 2 ? alphabet[pending >>> 6 & 63] : "=") + "=";
|
|
622
|
-
}
|
|
623
|
-
if (output) yield output;
|
|
624
920
|
}
|
|
625
921
|
|
|
922
|
+
// packages/safe-bash-command-qpdf/src/object-order.ts
|
|
923
|
+
import { retainedObjectOrder } from "../pdf-ast/index.js";
|
|
924
|
+
|
|
626
925
|
// packages/safe-bash-command-qpdf/src/json.ts
|
|
627
926
|
async function createQpdfJson(sourceDocument, document, source, storage, options, inputNames, maxOutputBytes, signal) {
|
|
628
927
|
const pages = new PagedStorage4({ fs: storage.fs, cwd: storage.directory, env: {}, signal }, 4);
|
|
@@ -638,6 +937,8 @@ async function createQpdfJson(sourceDocument, document, source, storage, options
|
|
|
638
937
|
for (const result of results) if (result.status === "rejected") await Promise.reject(result.reason);
|
|
639
938
|
})();
|
|
640
939
|
}
|
|
940
|
+
const include = (key) => !options.jsonKeys.length || options.jsonKeys.includes(key);
|
|
941
|
+
const includeObjects = include(options.jsonVersion === 1 ? "objects" : "qpdf");
|
|
641
942
|
const selected = (number, generation) => !options.jsonObjectSelectors.length || options.jsonObjectSelectors.some((item) => item.kind === "obj" && item.objNum === number && item.genNum === generation);
|
|
642
943
|
async function* objects() {
|
|
643
944
|
for await (const number of retainedObjectOrder(sourceDocument, source, storage, signal)) {
|
|
@@ -663,16 +964,38 @@ async function createQpdfJson(sourceDocument, document, source, storage, options
|
|
|
663
964
|
} else value = jsonRecord({ value: cosJson(object.value, storage, signal) });
|
|
664
965
|
yield [`obj:${object.objectNumber} ${object.generationNumber} R`, value];
|
|
665
966
|
}
|
|
666
|
-
if (!options.jsonObjectSelectors.length || options.jsonObjectSelectors.some((item) => item.kind === "trailer")) yield ["trailer", jsonRecord({ value: cosJson(
|
|
967
|
+
if (!options.jsonObjectSelectors.length || options.jsonObjectSelectors.some((item) => item.kind === "trailer")) yield ["trailer", jsonRecord({ value: cosJson(cosDict2({ Root: document.crossReference.rootRef, ...document.crossReference.infoRef ? { Info: document.crossReference.infoRef } : {}, Size: cosNumber2(maximum + 1) }), storage, signal) })];
|
|
968
|
+
}
|
|
969
|
+
async function* objectInfoEntries() {
|
|
970
|
+
for await (const object of objects()) {
|
|
971
|
+
if (!selected(object.objectNumber, object.generationNumber)) continue;
|
|
972
|
+
const dict = object.stream && object.value.kind === "dict" ? object.value : void 0;
|
|
973
|
+
const filter = dict && dictGet3(dict, "Filter"), length = dict && dictGet3(dict, "Length");
|
|
974
|
+
yield [`${object.objectNumber} ${object.generationNumber} R`, jsonRecord({ stream: jsonRecord({
|
|
975
|
+
filter: filter ? cosJson(filter, storage, signal) : null,
|
|
976
|
+
is: Boolean(dict),
|
|
977
|
+
length: length ? cosJson(length, storage, signal) : null
|
|
978
|
+
}) })];
|
|
979
|
+
}
|
|
667
980
|
}
|
|
668
981
|
async function pageObject(index) {
|
|
669
982
|
if (!count) return "obj:0 0 R";
|
|
670
|
-
if (index >= count) throw new
|
|
983
|
+
if (index >= count) throw new PdfError3("E_CAPABILITY", `Page index out of bounds: ${index}`);
|
|
671
984
|
const bytes = await pages.read(pageBase + index * 16, 16), view = new DataView(bytes.buffer, bytes.byteOffset, bytes.length);
|
|
672
985
|
return `obj:${view.getFloat64(0)} ${view.getFloat64(8)} R`;
|
|
673
986
|
}
|
|
674
987
|
async function* pageValues() {
|
|
675
|
-
|
|
988
|
+
const root = (await document.lookup(document.crossReference.rootRef))?.value;
|
|
989
|
+
let index = 0;
|
|
990
|
+
for await (const page of document.pages()) {
|
|
991
|
+
const steps = qpdfJsonPage(page.dict, page.reference, index++, root?.kind === "dict" ? root : void 0, options.jsonVersion ?? 2);
|
|
992
|
+
let step = steps.next();
|
|
993
|
+
while (!step.done) {
|
|
994
|
+
signal.throwIfAborted();
|
|
995
|
+
step = steps.next((await document.lookup(step.value))?.value);
|
|
996
|
+
}
|
|
997
|
+
yield step.value;
|
|
998
|
+
}
|
|
676
999
|
}
|
|
677
1000
|
async function* outlines() {
|
|
678
1001
|
for await (const outline of document.outlines()) {
|
|
@@ -699,7 +1022,7 @@ async function createQpdfJson(sourceDocument, document, source, storage, options
|
|
|
699
1022
|
async function row(number) {
|
|
700
1023
|
const node = (await values.get(number)).value;
|
|
701
1024
|
if (node.kind !== "array" || node.items[0]?.kind !== "string" || node.items[1]?.kind !== "string") throw new Error("Invalid JSON attachment record");
|
|
702
|
-
return [
|
|
1025
|
+
return [decodePdfString5(node.items[0]), jsonRecord({ filename: decodePdfString5(node.items[1]) })];
|
|
703
1026
|
}
|
|
704
1027
|
try {
|
|
705
1028
|
for await (const attachment of retainedAttachments(document, storage, signal)) {
|
|
@@ -739,14 +1062,14 @@ async function createQpdfJson(sourceDocument, document, source, storage, options
|
|
|
739
1062
|
yield { name, chunks: [] };
|
|
740
1063
|
continue;
|
|
741
1064
|
}
|
|
742
|
-
if (options.jsonStreamDataMode !== "file" || !name.startsWith(prefix)) continue;
|
|
1065
|
+
if (!includeObjects || options.jsonStreamDataMode !== "file" || !name.startsWith(prefix)) continue;
|
|
743
1066
|
const number = Number(name.slice(prefix.length));
|
|
744
1067
|
if (!Number.isSafeInteger(number) || number < 1 || String(number) !== name.slice(prefix.length)) continue;
|
|
745
1068
|
const entry = await document.crossReference.index.get(number, signal);
|
|
746
1069
|
if (!entry || !selected(number, entry.generationNumber ?? 0)) continue;
|
|
747
1070
|
if ((await document.objects.get(number, entry.generationNumber ?? 0))?.stream) yield { name, chunks: [] };
|
|
748
1071
|
}
|
|
749
|
-
if (options.jsonStreamDataMode !== "file") return;
|
|
1072
|
+
if (!includeObjects || options.jsonStreamDataMode !== "file") return;
|
|
750
1073
|
for await (const object of objects()) if (object.stream && selected(object.objectNumber, object.generationNumber)) {
|
|
751
1074
|
async function* admitted() {
|
|
752
1075
|
let size = 0;
|
|
@@ -760,20 +1083,20 @@ async function createQpdfJson(sourceDocument, document, source, storage, options
|
|
|
760
1083
|
}
|
|
761
1084
|
}
|
|
762
1085
|
files = await PdfStagedOutputs.create(storage, fileEntries(), { signal, maxNameChars: Infinity });
|
|
763
|
-
objectJson = await PdfFileSource.fromStream(storage.fs, storage.directory, jsonChunks({ kind: "object", entries: objectEntries() }, signal), { signal, maxInputBytes: maxOutputBytes });
|
|
764
|
-
const objectValue = { kind: "formatted", chunks: objectJson.stream(0, objectJson.size, signal) };
|
|
1086
|
+
if (includeObjects) objectJson = await PdfFileSource.fromStream(storage.fs, storage.directory, jsonChunks({ kind: "object", entries: objectEntries() }, signal), { signal, maxInputBytes: maxOutputBytes });
|
|
1087
|
+
const objectValue = objectJson ? { kind: "formatted", chunks: objectJson.stream(0, objectJson.size, signal) } : jsonRecord({});
|
|
765
1088
|
async function summary(value) {
|
|
766
1089
|
const source2 = await PdfFileSource.fromStream(storage.fs, storage.directory, jsonChunks(value, signal), { signal, maxInputBytes: maxOutputBytes });
|
|
767
1090
|
summaries.push(source2);
|
|
768
1091
|
return { kind: "formatted", chunks: source2.stream(0, source2.size, signal) };
|
|
769
1092
|
}
|
|
770
|
-
const attachmentValue = await summary({ kind: "object", entries: attachments() });
|
|
1093
|
+
const attachmentValue = include("attachments") ? await summary({ kind: "object", entries: attachments() }) : jsonRecord({});
|
|
771
1094
|
const root = (await document.lookup(document.crossReference.rootRef))?.value;
|
|
772
|
-
const form = root?.kind === "dict" ? (await document.lookup(
|
|
773
|
-
const appearances = form?.kind === "dict" ? (await document.lookup(
|
|
774
|
-
const fieldValue = await summary({ kind: "array", values: fields() });
|
|
775
|
-
const outlineValue = await summary({ kind: "array", values: outlines() });
|
|
776
|
-
const labelValue = await summary({ kind: "array", values: labels() });
|
|
1095
|
+
const form = root?.kind === "dict" ? (await document.lookup(dictGet3(root, "AcroForm")))?.value : void 0;
|
|
1096
|
+
const appearances = form?.kind === "dict" ? (await document.lookup(dictGet3(form, "NeedAppearances")))?.value : void 0;
|
|
1097
|
+
const fieldValue = await summary({ kind: "array", values: include("acroform") ? fields() : [] });
|
|
1098
|
+
const outlineValue = await summary({ kind: "array", values: include("outlines") ? outlines() : [] });
|
|
1099
|
+
const labelValue = await summary({ kind: "array", values: include("pagelabels") ? labels() : [] });
|
|
777
1100
|
const metadata = {
|
|
778
1101
|
pdfversion: document.crossReference.version,
|
|
779
1102
|
...options.jsonVersion === 2 ? { maxobjectid: maximum } : {},
|
|
@@ -785,7 +1108,15 @@ async function createQpdfJson(sourceDocument, document, source, storage, options
|
|
|
785
1108
|
attachments: attachmentValue
|
|
786
1109
|
};
|
|
787
1110
|
const payload = options.jsonVersion === 1 ? jsonRecord({ version: 1, ...metadata, objects: objectValue }) : jsonRecord({ version: 2, qpdf: { kind: "array", values: [jsonRecord(metadata), objectValue] } });
|
|
788
|
-
|
|
1111
|
+
const sections = {
|
|
1112
|
+
...metadata,
|
|
1113
|
+
objects: objectValue,
|
|
1114
|
+
objectinfo: { kind: "object", entries: objectInfoEntries() },
|
|
1115
|
+
qpdf: { kind: "array", values: [jsonRecord({ jsonversion: 2, pdfversion: document.crossReference.version, maxobjectid: maximum }), objectValue] }
|
|
1116
|
+
};
|
|
1117
|
+
const selectedPayload = { version: options.jsonVersion ?? 2, parameters: jsonRecord({ decodelevel: "generalized" }) };
|
|
1118
|
+
for (const key of options.jsonKeys) selectedPayload[key] = sections[key];
|
|
1119
|
+
json = await PdfFileSource.fromStream(storage.fs, storage.directory, jsonChunks(options.jsonKeys.length ? jsonRecord(selectedPayload) : payload, signal), { signal, maxInputBytes: maxOutputBytes });
|
|
789
1120
|
let total = json.size;
|
|
790
1121
|
for await (const entry of files.entries()) {
|
|
791
1122
|
if (entry.name === options.outputFile && options.outputFile !== "-") continue;
|
|
@@ -796,14 +1127,14 @@ async function createQpdfJson(sourceDocument, document, source, storage, options
|
|
|
796
1127
|
} catch (error) {
|
|
797
1128
|
await close().catch(() => {
|
|
798
1129
|
});
|
|
799
|
-
if (error instanceof
|
|
1130
|
+
if (error instanceof PdfError3 && error.message.startsWith("Unsupported streaming PDF filter: ")) throw new PdfError3(error.code, `Unsupported PDF filter: ${error.message.slice("Unsupported streaming PDF filter: ".length)}`);
|
|
800
1131
|
throw error;
|
|
801
1132
|
}
|
|
802
1133
|
}
|
|
803
1134
|
|
|
804
1135
|
// packages/safe-bash-command-qpdf/src/linearization.ts
|
|
805
1136
|
import { IntegerTable as IntegerTable5, PagedStorage as PagedStorage5 } from "@poe-platform/safe-fs/storage";
|
|
806
|
-
import { PdfError as
|
|
1137
|
+
import { PdfError as PdfError4, cosDict as cosDict3, dictGet as dictGet4, readCosXrefRevision, scanCosRangeObjects, PdfFileSource as PdfFileSource2 } from "../pdf-ast/index.js";
|
|
807
1138
|
async function firstDictionary(document, source, storage, signal) {
|
|
808
1139
|
const backing = new PagedStorage5({ fs: storage.fs, cwd: storage.directory, env: {}, signal }, 4);
|
|
809
1140
|
const seen = new IntegerTable5(backing), revisions = new IntegerTable5(backing), streams = new IntegerTable5(backing);
|
|
@@ -814,7 +1145,7 @@ async function firstDictionary(document, source, storage, signal) {
|
|
|
814
1145
|
const entry = await document.crossReference.index.get(number, signal);
|
|
815
1146
|
if (!entry || entry.type === "free") return;
|
|
816
1147
|
const object = await document.objects.get(number, entry.generationNumber ?? 0);
|
|
817
|
-
if (!object?.stream && object?.value.kind === "dict" &&
|
|
1148
|
+
if (!object?.stream && object?.value.kind === "dict" && dictGet4(object.value, "Linearized") !== void 0) return { dict: object.value, type: entry.type };
|
|
818
1149
|
return;
|
|
819
1150
|
}
|
|
820
1151
|
try {
|
|
@@ -831,10 +1162,10 @@ async function firstDictionary(document, source, storage, signal) {
|
|
|
831
1162
|
if (match && !compressed) compressed = match.dict;
|
|
832
1163
|
step = await revision.next();
|
|
833
1164
|
}
|
|
834
|
-
const previous =
|
|
1165
|
+
const previous = dictGet4(step.value, "Prev");
|
|
835
1166
|
offset = previous?.kind === "number" ? previous.value : void 0;
|
|
836
1167
|
} finally {
|
|
837
|
-
await revision.return(
|
|
1168
|
+
await revision.return(cosDict3({}));
|
|
838
1169
|
}
|
|
839
1170
|
}
|
|
840
1171
|
if (compressed) return compressed;
|
|
@@ -850,7 +1181,7 @@ async function firstDictionary(document, source, storage, signal) {
|
|
|
850
1181
|
await streams.set(BigInt(number), 1n);
|
|
851
1182
|
const entry = await document.crossReference.index.get(number, signal);
|
|
852
1183
|
if (!entry || entry.type !== "uncompressed") continue;
|
|
853
|
-
const object = await document.objects.get(number, entry.generationNumber ?? 0), type = object?.value.kind === "dict" ?
|
|
1184
|
+
const object = await document.objects.get(number, entry.generationNumber ?? 0), type = object?.value.kind === "dict" ? dictGet4(object.value, "Type") : void 0;
|
|
854
1185
|
if (!object?.stream || type?.kind !== "name" || type.decoded !== "ObjStm") continue;
|
|
855
1186
|
try {
|
|
856
1187
|
for await (const member of document.objects.objectStreamEntries(number, entry.generationNumber ?? 0)) {
|
|
@@ -858,11 +1189,11 @@ async function firstDictionary(document, source, storage, signal) {
|
|
|
858
1189
|
if (match) return match.dict;
|
|
859
1190
|
}
|
|
860
1191
|
} catch (error) {
|
|
861
|
-
if (!(error instanceof
|
|
1192
|
+
if (!(error instanceof PdfError4) || error.code !== "E_PARSE") throw error;
|
|
862
1193
|
}
|
|
863
1194
|
}
|
|
864
1195
|
}
|
|
865
|
-
for await (const page of document.pages()) if (!page.reference &&
|
|
1196
|
+
for await (const page of document.pages()) if (!page.reference && dictGet4(page.dict, "Linearized") !== void 0) return page.dict;
|
|
866
1197
|
return void 0;
|
|
867
1198
|
} catch (error) {
|
|
868
1199
|
failed = true;
|
|
@@ -873,31 +1204,57 @@ async function firstDictionary(document, source, storage, signal) {
|
|
|
873
1204
|
});
|
|
874
1205
|
}
|
|
875
1206
|
}
|
|
876
|
-
async function* linearizationParts(document, source, storage, filename, pageCount, signal) {
|
|
1207
|
+
async function* linearizationParts(document, source, storage, filename, pageCount, signal, mode) {
|
|
877
1208
|
const dict = await firstDictionary(document, source, storage, signal);
|
|
878
|
-
yield filename ?? "empty";
|
|
879
1209
|
if (!dict) {
|
|
880
|
-
yield "
|
|
1210
|
+
yield `${filename ?? "empty"} is not linearized
|
|
1211
|
+
`.repeat(mode === "both" ? 2 : 1);
|
|
881
1212
|
return;
|
|
882
1213
|
}
|
|
1214
|
+
if (mode !== "show") {
|
|
1215
|
+
yield `${filename ?? "empty"}: no linearization errors
|
|
1216
|
+
`;
|
|
1217
|
+
if (mode === "check") return;
|
|
1218
|
+
}
|
|
883
1219
|
async function number(key, fallback = 0) {
|
|
884
|
-
const node = (await document.lookup(
|
|
1220
|
+
const node = (await document.lookup(dictGet4(dict, key)))?.value;
|
|
885
1221
|
return node?.kind === "number" ? node.value : fallback;
|
|
886
1222
|
}
|
|
887
|
-
const hints = (await document.lookup(
|
|
1223
|
+
const hints = (await document.lookup(dictGet4(dict, "H")))?.value;
|
|
888
1224
|
async function hint(index) {
|
|
889
1225
|
const node = hints?.kind === "array" ? (await document.lookup(hints.items[index]))?.value : void 0;
|
|
890
|
-
return node
|
|
1226
|
+
return node?.kind === "number" ? node.value : 0;
|
|
1227
|
+
}
|
|
1228
|
+
const values = { file_size: await number("L"), first_page_object: await number("O"), first_page_end: await number("E"), npages: pageCount, xref_zero_offset: await number("T"), first_page: await number("P"), H_offset: await hint(0), H_length: await hint(1) };
|
|
1229
|
+
let decoded, sharedOffset = 0, outlineOffset;
|
|
1230
|
+
try {
|
|
1231
|
+
for await (const entry of document.crossReference.index.entries(signal)) {
|
|
1232
|
+
if (entry.type !== "uncompressed" || entry.offset !== values.H_offset) continue;
|
|
1233
|
+
const object = await document.objects.get(entry.objectNumber, entry.generationNumber ?? 0);
|
|
1234
|
+
if (!object?.stream || object.value.kind !== "dict") break;
|
|
1235
|
+
const shared = (await document.lookup(dictGet4(object.value, "S")))?.value, outline = (await document.lookup(dictGet4(object.value, "O")))?.value;
|
|
1236
|
+
sharedOffset = shared?.kind === "number" ? shared.value : 0;
|
|
1237
|
+
outlineOffset = outline?.kind === "number" ? outline.value : void 0;
|
|
1238
|
+
decoded = await PdfFileSource2.fromStream(storage.fs, storage.directory, document.objects.decodeStream(entry.objectNumber, entry.generationNumber ?? 0), { signal });
|
|
1239
|
+
break;
|
|
1240
|
+
}
|
|
1241
|
+
const report = linearizationReport(filename ?? "empty", values, decoded?.size ?? 0, sharedOffset, outlineOffset);
|
|
1242
|
+
let step = report.next();
|
|
1243
|
+
while (!step.done) {
|
|
1244
|
+
signal.throwIfAborted();
|
|
1245
|
+
if (typeof step.value === "string") {
|
|
1246
|
+
yield step.value;
|
|
1247
|
+
step = report.next(new Uint8Array());
|
|
1248
|
+
} else step = report.next(await decoded.read(step.value.offset, step.value.length, signal));
|
|
1249
|
+
}
|
|
1250
|
+
} finally {
|
|
1251
|
+
await decoded?.close();
|
|
891
1252
|
}
|
|
892
|
-
yield `: linearized
|
|
893
|
-
Linearization dictionary: L=${await number("L")} H=[${await hint(0)} ${await hint(1)}] O=${await number("O")} E=${await number("E")} N=${await number("N", pageCount)} T=${await number("T")}
|
|
894
|
-
no linearization errors
|
|
895
|
-
`;
|
|
896
1253
|
}
|
|
897
1254
|
|
|
898
1255
|
// packages/safe-bash-command-qpdf/src/selection.ts
|
|
899
1256
|
import { IntegerTable as IntegerTable6, PagedStorage as PagedStorage6 } from "@poe-platform/safe-fs/storage";
|
|
900
|
-
import { PdfRetainedDocument, createRetainedPageCopy, retainedCosObjects, decodePdfString as
|
|
1257
|
+
import { PdfRetainedDocument, createRetainedPageCopy, retainedCosObjects, decodePdfString as decodePdfString6, dictGet as dictGet5 } from "../pdf-ast/index.js";
|
|
901
1258
|
var QpdfMissingInput = class extends Error {
|
|
902
1259
|
constructor(file) {
|
|
903
1260
|
super(`qpdf: cannot open ${file}
|
|
@@ -921,8 +1278,8 @@ async function copyQpdfSelections(base, source, inputs, storage, options, signal
|
|
|
921
1278
|
if (!options.emptyInput) {
|
|
922
1279
|
const info = (await base.lookup(base.crossReference.infoRef))?.value;
|
|
923
1280
|
if (info?.kind === "dict") for (const key of ["Title", "Author"]) {
|
|
924
|
-
const value = (await base.lookup(
|
|
925
|
-
const text = value?.kind === "string" ?
|
|
1281
|
+
const value = (await base.lookup(dictGet5(info, key)))?.value;
|
|
1282
|
+
const text = value?.kind === "string" ? decodePdfString6(value) : value?.kind === "name" ? value.decoded : void 0;
|
|
926
1283
|
if (text) metadata[key] = text;
|
|
927
1284
|
}
|
|
928
1285
|
}
|
|
@@ -1026,67 +1383,50 @@ async function* collatedSelections(inputs, storage, options, signal) {
|
|
|
1026
1383
|
}
|
|
1027
1384
|
|
|
1028
1385
|
// packages/safe-bash-command-qpdf/src/xref-display.ts
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
latest = await PdfObjectIndex.build(readCosXrefRevision2(source, offset, { signal }), storage, { signal, duplicate: "last" });
|
|
1036
|
-
} catch (error) {
|
|
1037
|
-
signal.throwIfAborted();
|
|
1038
|
-
if (!(error instanceof PdfError4) || error.code !== "E_PARSE") throw error;
|
|
1039
|
-
}
|
|
1040
|
-
let count = 0;
|
|
1041
|
-
for await (const entry of document.crossReference.index.entries(signal)) {
|
|
1042
|
-
if (entry.type === "free") continue;
|
|
1043
|
-
const number = entry.objectNumber, generation = entry.generationNumber ?? 0;
|
|
1386
|
+
async function* xrefDisplayParts(document, highest, signal) {
|
|
1387
|
+
let count = 0;
|
|
1388
|
+
for await (const entry of document.crossReference.index.entries(signal)) {
|
|
1389
|
+
if (entry.type === "free") continue;
|
|
1390
|
+
const number = entry.objectNumber, generation = entry.generationNumber ?? 0;
|
|
1391
|
+
if (entry.type === "compressed" && document.crossReference.revisionCount === 0) {
|
|
1044
1392
|
const object = await document.objects.get(number, generation);
|
|
1045
|
-
|
|
1046
|
-
const type = object.stream ? "stream" : object.value.kind, revision = await latest?.get(number, signal);
|
|
1047
|
-
yield revision?.type === "compressed" ? `${number}/0: compressed; stream = ${revision.objectStreamNumber}; index = ${revision.indexInObjectStream}; type = ${type}
|
|
1048
|
-
` : `${number}/${generation}: uncompressed; type = ${type}
|
|
1393
|
+
yield `${number}/${generation}: uncompressed; type = ${object.value.kind}
|
|
1049
1394
|
`;
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
for await (const page of document.pages()) if (!page.reference) {
|
|
1053
|
-
yield `${++highest}/0: uncompressed; type = dict
|
|
1395
|
+
} else yield entry.type === "compressed" ? `${number}/0: compressed; stream = ${entry.objectStreamNumber}, index = ${entry.indexInStream ?? entry.indexInObjectStream}
|
|
1396
|
+
` : `${number}/${generation}: uncompressed; offset = ${entry.offset}
|
|
1054
1397
|
`;
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
} finally {
|
|
1062
|
-
await latest?.close().catch((error) => {
|
|
1063
|
-
if (!failed) return Promise.reject(error);
|
|
1064
|
-
});
|
|
1398
|
+
if (++count % 64 === 0) await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1399
|
+
}
|
|
1400
|
+
for await (const page of document.pages()) if (!page.reference) {
|
|
1401
|
+
yield `${++highest}/0: uncompressed; type = dict
|
|
1402
|
+
`;
|
|
1403
|
+
count++;
|
|
1065
1404
|
}
|
|
1405
|
+
if (!count) yield "\n";
|
|
1066
1406
|
}
|
|
1067
1407
|
|
|
1068
1408
|
// packages/safe-bash-command-qpdf/src/page-display.ts
|
|
1069
1409
|
import { IntegerTable as IntegerTable7, PagedStorage as PagedStorage7 } from "@poe-platform/safe-fs/storage";
|
|
1070
|
-
import { PdfMutableObjectStore as PdfMutableObjectStore3, cosDict as
|
|
1410
|
+
import { PdfMutableObjectStore as PdfMutableObjectStore3, cosDict as cosDict4, dictGet as dictGet6 } from "../pdf-ast/index.js";
|
|
1071
1411
|
async function* imageParts(document, page, storage, signal) {
|
|
1072
1412
|
const values = new PdfMutableObjectStore3(storage, { signal });
|
|
1073
1413
|
const backing = new PagedStorage7({ fs: storage.fs, cwd: storage.directory, env: {}, signal }, 4);
|
|
1074
1414
|
const visited = new IntegerTable7(backing, 64);
|
|
1075
|
-
let top = -1, failed = false, work = 0;
|
|
1415
|
+
let top = -1, failed = false, work = 0, hasImages = false;
|
|
1076
1416
|
async function pageResources() {
|
|
1077
|
-
const direct = await document.lookup(
|
|
1417
|
+
const direct = await document.lookup(dictGet6(page, "Resources"));
|
|
1078
1418
|
if (direct?.value.kind === "dict") return { value: direct.value, inherited: false };
|
|
1079
1419
|
const parents = new IntegerTable7(backing, 64);
|
|
1080
1420
|
let current = page;
|
|
1081
1421
|
while (current) {
|
|
1082
1422
|
signal.throwIfAborted();
|
|
1083
1423
|
if (++work % 64 === 0) await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1084
|
-
const node =
|
|
1424
|
+
const node = dictGet6(current, "Resources");
|
|
1085
1425
|
if (node) {
|
|
1086
1426
|
const found2 = await document.lookup(node);
|
|
1087
1427
|
return found2?.value.kind === "dict" && !found2.stream ? { value: found2.value, inherited: true } : void 0;
|
|
1088
1428
|
}
|
|
1089
|
-
const parent =
|
|
1429
|
+
const parent = dictGet6(current, "Parent");
|
|
1090
1430
|
if (parent?.kind === "ref") {
|
|
1091
1431
|
if (await parents.get(BigInt(parent.objectNumber)) !== void 0) break;
|
|
1092
1432
|
await parents.set(BigInt(parent.objectNumber), 1n);
|
|
@@ -1099,9 +1439,9 @@ async function* imageParts(document, page, storage, signal) {
|
|
|
1099
1439
|
async function push(node, inherited = false) {
|
|
1100
1440
|
const resource = await document.lookup(node);
|
|
1101
1441
|
if (resource?.value.kind !== "dict" || resource.stream) return;
|
|
1102
|
-
const found = await document.lookup(
|
|
1442
|
+
const found = await document.lookup(dictGet6(resource.value, "XObject"));
|
|
1103
1443
|
if (found?.value.kind !== "dict" || found.stream && !inherited) return;
|
|
1104
|
-
const objects = inherited ?
|
|
1444
|
+
const objects = inherited ? cosDict4(Object.fromEntries(found.value.entries.map((entry) => [entry.key.decoded, entry.value]))) : found.value;
|
|
1105
1445
|
const ref = await values.allocate(objects), bytes = new Uint8Array(24), view = new DataView(bytes.buffer);
|
|
1106
1446
|
view.setFloat64(0, top);
|
|
1107
1447
|
view.setFloat64(8, ref.objectNumber);
|
|
@@ -1128,20 +1468,24 @@ async function* imageParts(document, page, storage, signal) {
|
|
|
1128
1468
|
const reference = entry.value.kind === "ref" ? entry.value : void 0;
|
|
1129
1469
|
const value = (await document.lookup(entry.value))?.value;
|
|
1130
1470
|
if (value?.kind !== "dict") continue;
|
|
1131
|
-
const subtype =
|
|
1471
|
+
const subtype = dictGet6(value, "Subtype");
|
|
1132
1472
|
if (subtype?.kind !== "name") continue;
|
|
1133
1473
|
if (subtype.decoded === "Image") {
|
|
1134
|
-
const width =
|
|
1474
|
+
const width = dictGet6(value, "Width"), height = dictGet6(value, "Height");
|
|
1475
|
+
if (!hasImages) {
|
|
1476
|
+
yield " images:\n";
|
|
1477
|
+
hasImages = true;
|
|
1478
|
+
}
|
|
1135
1479
|
yield " /";
|
|
1136
1480
|
yield entry.key.decoded;
|
|
1137
|
-
yield `: ${reference ? `${reference.objectNumber} ${reference.generationNumber} R` : "inline"}
|
|
1481
|
+
yield `: ${reference ? `${reference.objectNumber} ${reference.generationNumber} R` : "inline"}, ${width?.kind === "number" ? width.value : 0} x ${height?.kind === "number" ? height.value : 0}
|
|
1138
1482
|
`;
|
|
1139
1483
|
} else if (subtype.decoded === "Form") {
|
|
1140
1484
|
if (reference) {
|
|
1141
1485
|
if (await visited.get(BigInt(reference.objectNumber)) !== void 0) continue;
|
|
1142
1486
|
await visited.set(BigInt(reference.objectNumber), 1n);
|
|
1143
1487
|
}
|
|
1144
|
-
await push(
|
|
1488
|
+
await push(dictGet6(value, "Resources"));
|
|
1145
1489
|
}
|
|
1146
1490
|
}
|
|
1147
1491
|
} catch (error) {
|
|
@@ -1160,12 +1504,9 @@ async function* pageDisplayParts(document, storage, highest, withImages, signal)
|
|
|
1160
1504
|
const ref = page.reference ?? { objectNumber: ++highest, generationNumber: 0 };
|
|
1161
1505
|
yield `page ${++count}: ${ref.objectNumber} ${ref.generationNumber} R
|
|
1162
1506
|
`;
|
|
1163
|
-
if (withImages)
|
|
1164
|
-
yield " images:\n";
|
|
1165
|
-
yield* imageParts(document, page.dict, storage, signal);
|
|
1166
|
-
}
|
|
1507
|
+
if (withImages) yield* imageParts(document, page.dict, storage, signal);
|
|
1167
1508
|
yield " content:\n";
|
|
1168
|
-
const contents =
|
|
1509
|
+
const contents = dictGet6(page.dict, "Contents");
|
|
1169
1510
|
for (const node of contents?.kind === "array" ? contents.items : contents ? [contents] : []) {
|
|
1170
1511
|
if (node.kind === "ref") yield ` ${node.objectNumber} ${node.generationNumber} R
|
|
1171
1512
|
`;
|
|
@@ -1175,7 +1516,7 @@ async function* pageDisplayParts(document, storage, highest, withImages, signal)
|
|
|
1175
1516
|
}
|
|
1176
1517
|
|
|
1177
1518
|
// packages/safe-bash-command-qpdf/src/retained.ts
|
|
1178
|
-
import { QpdfJsonDocument, encryptRetainedPdfChunks, copyRetainedAttachments, editRetainedDocument, PdfDuplicateAttachment, PdfError as PdfError5, PdfFileSource as
|
|
1519
|
+
import { QpdfJsonDocument, encryptRetainedPdfChunks, copyRetainedAttachments, editRetainedDocument, PdfDuplicateAttachment, PdfMissingAttachment, PdfError as PdfError5, PdfFileSource as PdfFileSource3, PdfRetainedDocument as PdfRetainedDocument2, PdfStagedOutputs as PdfStagedOutputs2, saveRetainedDocumentChunks, retainedCosObjects as retainedCosObjects2, serializeRetainedCosDocumentChunks, cosArray as cosArray2, cosDict as cosDict5, cosName, cosNumber as cosNumber3, cosRef, cosString as cosString2, dictGet as dictGet7 } from "../pdf-ast/index.js";
|
|
1179
1520
|
import { FsError } from "../safe-bash-contracts/errors.js";
|
|
1180
1521
|
import { writeBytes } from "../safe-bash-contracts/io.js";
|
|
1181
1522
|
import { resolvePath } from "../safe-bash-contracts/path.js";
|
|
@@ -1205,7 +1546,7 @@ async function executeRetainedQpdf(context, options, limits, signal, inputBytes
|
|
|
1205
1546
|
yield bytes;
|
|
1206
1547
|
}
|
|
1207
1548
|
}
|
|
1208
|
-
output = await
|
|
1549
|
+
output = await PdfFileSource3.fromStream(context.fs, storage.directory, admitted(), { signal, maxInputBytes: limits.maxOutputBytes });
|
|
1209
1550
|
for await (const bytes of output.stream(0, output.size, signal)) await writeBytes(context.stdout, bytes, signal);
|
|
1210
1551
|
}
|
|
1211
1552
|
try {
|
|
@@ -1215,7 +1556,7 @@ async function executeRetainedQpdf(context, options, limits, signal, inputBytes
|
|
|
1215
1556
|
if (inputs.has(candidate)) return inputs.get(candidate);
|
|
1216
1557
|
let acquired;
|
|
1217
1558
|
try {
|
|
1218
|
-
acquired = await
|
|
1559
|
+
acquired = await PdfFileSource3.open(context.fs, resolvePath(context.cwd, candidate), { signal, maxInputBytes: maximum - inputBytes });
|
|
1219
1560
|
} catch (error) {
|
|
1220
1561
|
signal.throwIfAborted();
|
|
1221
1562
|
if (error instanceof Error && "code" in error && ["ENOENT", "ENOTDIR", "EACCES", "EISDIR"].includes(String(error.code)) || error instanceof PdfError5 && error.message === "PDF source must be a regular file") {
|
|
@@ -1230,7 +1571,7 @@ async function executeRetainedQpdf(context, options, limits, signal, inputBytes
|
|
|
1230
1571
|
return acquired;
|
|
1231
1572
|
};
|
|
1232
1573
|
const reconstruct = async () => {
|
|
1233
|
-
const next = await
|
|
1574
|
+
const next = await PdfFileSource3.fromStream(context.fs, storage.directory, jsonDocument.chunks(), { signal });
|
|
1234
1575
|
rebuilt.push(next);
|
|
1235
1576
|
await document?.close();
|
|
1236
1577
|
document = void 0;
|
|
@@ -1245,14 +1586,14 @@ async function executeRetainedQpdf(context, options, limits, signal, inputBytes
|
|
|
1245
1586
|
await acquire(candidate);
|
|
1246
1587
|
}
|
|
1247
1588
|
if (inputName === "-" || options.updateFromJsonFiles.includes("-") || [...options.pageSpecs, ...options.addAttachmentSpecs, ...options.copyAttachmentsSpecs, ...options.stampSpecs].some((spec) => spec.file === "-")) {
|
|
1248
|
-
const acquired = await
|
|
1589
|
+
const acquired = await PdfFileSource3.fromStream(context.fs, storage.directory, context.stdin, { signal, maxInputBytes: maximum - inputBytes });
|
|
1249
1590
|
inputs.set("-", acquired);
|
|
1250
1591
|
inputBytes += acquired.size;
|
|
1251
1592
|
context.inputBudget?.check(inputBytes);
|
|
1252
1593
|
}
|
|
1253
1594
|
if (useEmpty) {
|
|
1254
|
-
const objects = [
|
|
1255
|
-
source = await
|
|
1595
|
+
const objects = [cosDict5({ Type: cosName("Catalog"), Pages: cosRef(2) }), cosDict5({ Type: cosName("Pages"), Count: cosNumber3(0), Kids: cosArray2([]) }), cosDict5({ Producer: cosString2("@poe-code/pdf-ast") })];
|
|
1596
|
+
source = await PdfFileSource3.fromStream(context.fs, storage.directory, serializeRetainedCosDocumentChunks({
|
|
1256
1597
|
objects: objects.map((value, index) => ({ objectNumber: index + 1, generationNumber: 0, value })),
|
|
1257
1598
|
rootRef: cosRef(1),
|
|
1258
1599
|
infoRef: cosRef(3),
|
|
@@ -1339,7 +1680,7 @@ async function executeRetainedQpdf(context, options, limits, signal, inputBytes
|
|
|
1339
1680
|
selectedValue = object.value;
|
|
1340
1681
|
selectedLength = object.stream?.length;
|
|
1341
1682
|
}
|
|
1342
|
-
if (!object.stream && object.value.kind === "dict" &&
|
|
1683
|
+
if (!object.stream && object.value.kind === "dict" && dictGet7(object.value, "Linearized") !== void 0) linearized = true;
|
|
1343
1684
|
if (object.stream) for await (const ignored of object.stream.chunks) void ignored;
|
|
1344
1685
|
}
|
|
1345
1686
|
for await (const page of document.pages()) {
|
|
@@ -1353,7 +1694,7 @@ async function executeRetainedQpdf(context, options, limits, signal, inputBytes
|
|
|
1353
1694
|
`);
|
|
1354
1695
|
}
|
|
1355
1696
|
if (options.showLinearization) {
|
|
1356
|
-
const parts = linearizationParts(document, source, storage, inputName, count, signal);
|
|
1697
|
+
const parts = linearizationParts(document, source, storage, inputName, count, signal, options.showLinearization);
|
|
1357
1698
|
await publishInspection((async function* () {
|
|
1358
1699
|
for await (const part of parts) yield* encodeDisplayParts([part], signal);
|
|
1359
1700
|
})());
|
|
@@ -1368,7 +1709,7 @@ async function executeRetainedQpdf(context, options, limits, signal, inputBytes
|
|
|
1368
1709
|
for await (const part of parts) yield* encodeDisplayParts([part], signal);
|
|
1369
1710
|
})();
|
|
1370
1711
|
} else if (options.showXref) {
|
|
1371
|
-
const parts = xrefDisplayParts(document,
|
|
1712
|
+
const parts = xrefDisplayParts(document, highest, signal);
|
|
1372
1713
|
chunks = (async function* () {
|
|
1373
1714
|
for await (const part of parts) yield* encodeDisplayParts([part], signal);
|
|
1374
1715
|
})();
|
|
@@ -1400,13 +1741,7 @@ File is ${count === 0 ? "empty" : linearized ? "linearized" : "not linearized"}
|
|
|
1400
1741
|
No syntax or stream encoding errors found; the file may still contain
|
|
1401
1742
|
errors that qpdf cannot detect
|
|
1402
1743
|
` : options.showNpages ? `${count}
|
|
1403
|
-
` : encryption
|
|
1404
|
-
V = ${encryption.version}
|
|
1405
|
-
Length = ${encryption.keyLengthBits}
|
|
1406
|
-
print: ${encryption.permissions.print ? "allowed" : "not allowed"}
|
|
1407
|
-
modify: ${encryption.permissions.modify ? "allowed" : "not allowed"}
|
|
1408
|
-
extract for accessibility: ${encryption.permissions.copy ? "allowed" : "not allowed"}
|
|
1409
|
-
` : "File is not encrypted\n";
|
|
1744
|
+
` : encryptionReport(encryption);
|
|
1410
1745
|
const bytes = new TextEncoder().encode(message);
|
|
1411
1746
|
if (bytes.length > limits.maxOutputBytes) throw new RangeError("Output byte limit exceeded");
|
|
1412
1747
|
await writeBytes(context.stdout, bytes, signal);
|
|
@@ -1466,6 +1801,18 @@ extract for accessibility: ${encryption.permissions.copy ? "allowed" : "not allo
|
|
|
1466
1801
|
selectionGraph = await copyQpdfSelections(document, source, inputs, storage, options, signal);
|
|
1467
1802
|
document = await selectionGraph.openDocument();
|
|
1468
1803
|
}
|
|
1804
|
+
let pageCount = 0;
|
|
1805
|
+
if (options.pageLabelSpecs.length) for await (const page of document.pages()) {
|
|
1806
|
+
void page;
|
|
1807
|
+
pageCount++;
|
|
1808
|
+
}
|
|
1809
|
+
let pageLabels;
|
|
1810
|
+
try {
|
|
1811
|
+
pageLabels = parseQpdfPageLabels(options.pageLabelSpecs, pageCount);
|
|
1812
|
+
} catch (error) {
|
|
1813
|
+
return await diagnostic(`qpdf: ${error.message}
|
|
1814
|
+
`);
|
|
1815
|
+
}
|
|
1469
1816
|
const destination = options.replaceInput ? inputName : options.outputFile;
|
|
1470
1817
|
const { removeInfo, removeMetadata, removeStructure, removeAcroform, removePageLabels } = options;
|
|
1471
1818
|
async function* rotations() {
|
|
@@ -1570,8 +1917,8 @@ extract for accessibility: ${encryption.permissions.copy ? "allowed" : "not allo
|
|
|
1570
1917
|
yield { ...spec, length: input.size, chunks: input.stream(0, input.size, signal) };
|
|
1571
1918
|
}
|
|
1572
1919
|
}
|
|
1573
|
-
if (options.generateAppearances || options.flattenAnnotations || options.stampSpecs.length || options.externalizeInlineImages || options.removeUnreferencedResources !== "no" || options.flattenRotation || options.copyAttachmentsSpecs.length || options.addAttachmentSpecs.length || options.removeAttachmentKeys.length || options.linearize ||
|
|
1574
|
-
editedGraph = await editRetainedDocument(document, storage, { generateAppearances: options.generateAppearances, ...options.flattenAnnotations ? { flattenAnnotations: options.flattenAnnotations === true ? "all" : options.flattenAnnotations } : {}, ...options.stampSpecs.length ? { stamps: stamps() } : {}, ...options.externalizeInlineImages ? { externalizeInlineImages: { minBytes: options.iiMinBytes, compress: options.streamDataMode !== "uncompress" } } : {}, removeUnreferencedResources: options.removeUnreferencedResources !== "no", flattenRotation: options.flattenRotation, ...options.copyAttachmentsSpecs.length ? { attachmentCopies: attachmentCopies() } : {}, ...options.removeAttachmentKeys.length ? { removeAttachments: options.removeAttachmentKeys } : {}, ...options.addAttachmentSpecs.length ? { attachments: attachments() } : {}, ...
|
|
1920
|
+
if (options.generateAppearances || options.flattenAnnotations || options.stampSpecs.length || options.externalizeInlineImages || options.removeUnreferencedResources !== "no" || options.flattenRotation || options.copyAttachmentsSpecs.length || options.addAttachmentSpecs.length || options.removeAttachmentKeys.length || options.linearize || pageLabels.length > 0 || options.splitPagesGroup !== void 0 && (removeInfo || removeMetadata || removeStructure || removeAcroform || removePageLabels)) {
|
|
1921
|
+
editedGraph = await editRetainedDocument(document, storage, { generateAppearances: options.generateAppearances, ...options.flattenAnnotations ? { flattenAnnotations: options.flattenAnnotations === true ? "all" : options.flattenAnnotations } : {}, ...options.stampSpecs.length ? { stamps: stamps() } : {}, ...options.externalizeInlineImages ? { externalizeInlineImages: { minBytes: options.iiMinBytes, compress: options.streamDataMode !== "uncompress" } } : {}, removeUnreferencedResources: options.removeUnreferencedResources !== "no", flattenRotation: options.flattenRotation, ...options.copyAttachmentsSpecs.length ? { attachmentCopies: attachmentCopies() } : {}, ...options.removeAttachmentKeys.length ? { removeAttachments: options.removeAttachmentKeys } : {}, ...options.addAttachmentSpecs.length ? { attachments: attachments() } : {}, ...pageLabels.length ? { pageLabels } : {}, linearize: options.linearize, removeInfo, removeMetadata, removeStructure, removeAcroform, removePageLabels, rotations: rotations(), signal });
|
|
1575
1922
|
}
|
|
1576
1923
|
if (!destination) return await diagnostic("qpdf: an output file is required\n");
|
|
1577
1924
|
if (!options.replaceInput && inputName !== "-" && destination === inputName) return await diagnostic("qpdf: output file may not be the same as the input file (use --replace-input)\n");
|
|
@@ -1613,7 +1960,7 @@ extract for accessibility: ${encryption.permissions.copy ? "allowed" : "not allo
|
|
|
1613
1960
|
const normalizeContent = options.normalizeContentFlag ?? (options.qdf || options.streamDataMode === "uncompress");
|
|
1614
1961
|
const producer = saveRetainedDocumentChunks(editedGraph?.document ?? document, storage, { ...editedGraph ? {} : { removeInfo, removeMetadata, removeStructure, removeAcroform, removePageLabels, rotations: rotations() }, signal, normalizeContent, objectStreams: options.decrypt && document.encryption ? "preserve" : options.objectStreamsMode, maxOutputBytes: limits.maxOutputBytes, ...options.decrypt && document.encryption ? { version: "1.7", omitId: true } : {} });
|
|
1615
1962
|
try {
|
|
1616
|
-
output = await
|
|
1963
|
+
output = await PdfFileSource3.fromStream(context.fs, storage.directory, producer, { signal, maxInputBytes: limits.maxOutputBytes });
|
|
1617
1964
|
} finally {
|
|
1618
1965
|
await producer.return(void 0);
|
|
1619
1966
|
}
|
|
@@ -1621,7 +1968,7 @@ extract for accessibility: ${encryption.permissions.copy ? "allowed" : "not allo
|
|
|
1621
1968
|
const plaintext = output, config = options.encryptConfig;
|
|
1622
1969
|
let encryptionFailed = false;
|
|
1623
1970
|
try {
|
|
1624
|
-
output = await
|
|
1971
|
+
output = await PdfFileSource3.fromStream(context.fs, storage.directory, encryptRetainedPdfChunks(plaintext, storage, { ...config, signal, maxOutputBytes: limits.maxOutputBytes }), { signal, maxInputBytes: limits.maxOutputBytes });
|
|
1625
1972
|
} catch (error) {
|
|
1626
1973
|
encryptionFailed = true;
|
|
1627
1974
|
throw error;
|
|
@@ -1651,6 +1998,8 @@ extract for accessibility: ${encryption.permissions.copy ? "allowed" : "not allo
|
|
|
1651
1998
|
if (error instanceof PdfError5 && error.message.startsWith("Unsupported streaming PDF filter: ")) {
|
|
1652
1999
|
throw new PdfError5(error.code, `Unsupported PDF filter: ${error.message.slice("Unsupported streaming PDF filter: ".length)}`);
|
|
1653
2000
|
}
|
|
2001
|
+
if (error instanceof PdfMissingAttachment) return await diagnostic(`qpdf: attachment ${error.key} not found
|
|
2002
|
+
`);
|
|
1654
2003
|
if (error instanceof PdfDuplicateAttachment) return await diagnostic(`qpdf: duplicate attachment key ${error.key}
|
|
1655
2004
|
`);
|
|
1656
2005
|
if (error instanceof QpdfMissingInput || error instanceof QpdfMissingAttachment) return await diagnostic(error.message);
|
|
@@ -1715,21 +2064,23 @@ import { readBytes, writeBytes as writeBytes2 } from "../safe-bash-contracts/io.
|
|
|
1715
2064
|
import { createOutputOperation } from "../safe-bash-contracts/output.js";
|
|
1716
2065
|
import {
|
|
1717
2066
|
PdfDocument,
|
|
1718
|
-
|
|
2067
|
+
retainedStampTransform,
|
|
2068
|
+
PdfFileSource as PdfFileSource4,
|
|
1719
2069
|
parseCosDocumentSteps,
|
|
1720
2070
|
serializeCosDocumentSteps,
|
|
1721
2071
|
encryptCosDocumentSteps,
|
|
1722
2072
|
generateDocumentFormAppearancesSteps,
|
|
1723
2073
|
resolveDestinationPageIndex,
|
|
1724
|
-
decodePdfString as
|
|
1725
|
-
cosDict as
|
|
1726
|
-
cosNumber as
|
|
2074
|
+
decodePdfString as decodePdfString7,
|
|
2075
|
+
cosDict as cosDict6,
|
|
2076
|
+
cosNumber as cosNumber4,
|
|
2077
|
+
cosNull,
|
|
1727
2078
|
cosArray as cosArray3,
|
|
1728
2079
|
cosName as cosName2,
|
|
1729
2080
|
cosString as cosString3,
|
|
1730
2081
|
cosHexString,
|
|
1731
2082
|
cosStream,
|
|
1732
|
-
dictGet as
|
|
2083
|
+
dictGet as dictGet8,
|
|
1733
2084
|
dictSet,
|
|
1734
2085
|
dictDelete
|
|
1735
2086
|
} from "../pdf-ast/index.js";
|
|
@@ -1796,7 +2147,7 @@ function cosNodeToJson(node) {
|
|
|
1796
2147
|
case "name":
|
|
1797
2148
|
return `/${node.decoded}`;
|
|
1798
2149
|
case "string":
|
|
1799
|
-
return `u:${
|
|
2150
|
+
return `u:${decodePdfString7(node)}`;
|
|
1800
2151
|
case "ref":
|
|
1801
2152
|
return `${node.objectNumber} ${node.generationNumber} R`;
|
|
1802
2153
|
case "array":
|
|
@@ -1876,7 +2227,7 @@ function parseJsonRefToken(raw) {
|
|
|
1876
2227
|
function jsonToCosNode(val) {
|
|
1877
2228
|
if (val === null || val === void 0) return { kind: "null" };
|
|
1878
2229
|
if (typeof val === "boolean") return { kind: "boolean", value: val };
|
|
1879
|
-
if (typeof val === "number") return
|
|
2230
|
+
if (typeof val === "number") return cosNumber4(val);
|
|
1880
2231
|
if (typeof val === "string") {
|
|
1881
2232
|
if (val.startsWith("n:/")) return cosName2(val.slice(3));
|
|
1882
2233
|
if (val.startsWith("n:")) return cosName2(val.slice(2));
|
|
@@ -1891,7 +2242,7 @@ function jsonToCosNode(val) {
|
|
|
1891
2242
|
return cosArray3(val.map(jsonToCosNode));
|
|
1892
2243
|
}
|
|
1893
2244
|
if (typeof val === "object") {
|
|
1894
|
-
const d =
|
|
2245
|
+
const d = cosDict6({});
|
|
1895
2246
|
for (const [k, v] of Object.entries(val)) {
|
|
1896
2247
|
const keyName = k.startsWith("n:/") ? k.slice(3) : k.startsWith("/") ? k.slice(1) : k;
|
|
1897
2248
|
dictSet(d, keyName, jsonToCosNode(v));
|
|
@@ -1929,9 +2280,9 @@ function applyQpdfJsonObjects(targetObjects, parsedJson, files) {
|
|
|
1929
2280
|
if (objKey === "trailer") {
|
|
1930
2281
|
const trVal = jsonToCosNode(entry.value);
|
|
1931
2282
|
if (trVal.kind === "dict") {
|
|
1932
|
-
const rNode =
|
|
2283
|
+
const rNode = dictGet8(trVal, "Root");
|
|
1933
2284
|
if (rNode?.kind === "ref") rootRef = rNode;
|
|
1934
|
-
const iNode =
|
|
2285
|
+
const iNode = dictGet8(trVal, "Info");
|
|
1935
2286
|
if (iNode?.kind === "ref") infoRef = iNode;
|
|
1936
2287
|
}
|
|
1937
2288
|
continue;
|
|
@@ -1941,7 +2292,7 @@ function applyQpdfJsonObjects(targetObjects, parsedJson, files) {
|
|
|
1941
2292
|
if ("stream" in entry && typeof entry.stream === "object" && entry.stream !== null) {
|
|
1942
2293
|
const stObj = entry.stream;
|
|
1943
2294
|
const dictConverted = jsonToCosNode(stObj.dict ?? {});
|
|
1944
|
-
const stDict = dictConverted.kind === "dict" ? dictConverted :
|
|
2295
|
+
const stDict = dictConverted.kind === "dict" ? dictConverted : cosDict6({});
|
|
1945
2296
|
let streamBytes;
|
|
1946
2297
|
if (typeof stObj.data === "string") {
|
|
1947
2298
|
streamBytes = decodeBytesBase64(stObj.data);
|
|
@@ -1955,13 +2306,13 @@ function applyQpdfJsonObjects(targetObjects, parsedJson, files) {
|
|
|
1955
2306
|
const existing = targetObjects.get(ref.objectNumber)?.value;
|
|
1956
2307
|
streamBytes = existing?.kind === "stream" ? existing.rawBytes : new Uint8Array(0);
|
|
1957
2308
|
}
|
|
1958
|
-
const filterNode =
|
|
2309
|
+
const filterNode = dictGet8(stDict, "Filter");
|
|
1959
2310
|
const shouldStripFilter = filterNode?.kind === "name" && DECOMPRESSIBLE_FILTERS.has(filterNode.decoded) || filterNode?.kind === "array" && filterNode.items.every((it) => it.kind === "name" && DECOMPRESSIBLE_FILTERS.has(it.decoded));
|
|
1960
2311
|
if (shouldStripFilter) {
|
|
1961
2312
|
dictDelete(stDict, "Filter");
|
|
1962
2313
|
dictDelete(stDict, "DecodeParms");
|
|
1963
2314
|
}
|
|
1964
|
-
dictSet(stDict, "Length",
|
|
2315
|
+
dictSet(stDict, "Length", cosNumber4(streamBytes.byteLength));
|
|
1965
2316
|
targetObjects.set(ref.objectNumber, {
|
|
1966
2317
|
objectNumber: ref.objectNumber,
|
|
1967
2318
|
generationNumber: ref.generationNumber,
|
|
@@ -1982,7 +2333,7 @@ function applyQpdfJsonObjects(targetObjects, parsedJson, files) {
|
|
|
1982
2333
|
if (!rootRef) {
|
|
1983
2334
|
for (const obj of targetObjects.values()) {
|
|
1984
2335
|
if (obj.value.kind === "dict") {
|
|
1985
|
-
const t =
|
|
2336
|
+
const t = dictGet8(obj.value, "Type");
|
|
1986
2337
|
if (t?.kind === "name" && t.decoded === "Catalog") {
|
|
1987
2338
|
rootRef = { kind: "ref", objectNumber: obj.objectNumber, generationNumber: obj.generationNumber };
|
|
1988
2339
|
break;
|
|
@@ -2026,13 +2377,13 @@ function collectDocumentAttachments(doc) {
|
|
|
2026
2377
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
2027
2378
|
const pushFilespec = (fsDict, fallbackKey) => {
|
|
2028
2379
|
if (!fsDict) return;
|
|
2029
|
-
const ufNode = doc.cos.resolve(
|
|
2030
|
-
const fnStr = ufNode?.kind === "string" ?
|
|
2380
|
+
const ufNode = doc.cos.resolve(dictGet8(fsDict, "UF") ?? dictGet8(fsDict, "F"));
|
|
2381
|
+
const fnStr = ufNode?.kind === "string" ? decodePdfString7(ufNode) : fallbackKey;
|
|
2031
2382
|
const keyStr = fallbackKey || fnStr;
|
|
2032
2383
|
if (seenKeys.has(keyStr)) return;
|
|
2033
|
-
const efDict = asDict(doc.cos.resolve(
|
|
2384
|
+
const efDict = asDict(doc.cos.resolve(dictGet8(fsDict, "EF")));
|
|
2034
2385
|
const stNode = efDict ? doc.cos.resolve(
|
|
2035
|
-
|
|
2386
|
+
dictGet8(efDict, "UF") ?? dictGet8(efDict, "F") ?? dictGet8(efDict, "DOS") ?? dictGet8(efDict, "Mac") ?? dictGet8(efDict, "Unix")
|
|
2036
2387
|
) : void 0;
|
|
2037
2388
|
if (stNode?.kind === "stream") {
|
|
2038
2389
|
seenKeys.add(keyStr);
|
|
@@ -2041,16 +2392,16 @@ function collectDocumentAttachments(doc) {
|
|
|
2041
2392
|
};
|
|
2042
2393
|
const walkNames = (nodeDict, visited = /* @__PURE__ */ new Set()) => {
|
|
2043
2394
|
if (!nodeDict) return;
|
|
2044
|
-
const namesArr = asArray(doc.cos.resolve(
|
|
2395
|
+
const namesArr = asArray(doc.cos.resolve(dictGet8(nodeDict, "Names")));
|
|
2045
2396
|
if (namesArr) {
|
|
2046
2397
|
for (let idx = 0; idx + 1 < namesArr.items.length; idx += 2) {
|
|
2047
2398
|
const kNode = doc.cos.resolve(namesArr.items[idx]);
|
|
2048
|
-
const keyStr = kNode?.kind === "string" ?
|
|
2399
|
+
const keyStr = kNode?.kind === "string" ? decodePdfString7(kNode) : `att_${idx}`;
|
|
2049
2400
|
const fsDict = asDict(doc.cos.resolve(namesArr.items[idx + 1]));
|
|
2050
2401
|
pushFilespec(fsDict, keyStr);
|
|
2051
2402
|
}
|
|
2052
2403
|
}
|
|
2053
|
-
const kidsArr = asArray(doc.cos.resolve(
|
|
2404
|
+
const kidsArr = asArray(doc.cos.resolve(dictGet8(nodeDict, "Kids")));
|
|
2054
2405
|
if (kidsArr) {
|
|
2055
2406
|
for (const kid of kidsArr.items) {
|
|
2056
2407
|
if (kid.kind === "ref") {
|
|
@@ -2062,22 +2413,22 @@ function collectDocumentAttachments(doc) {
|
|
|
2062
2413
|
}
|
|
2063
2414
|
};
|
|
2064
2415
|
const rootDict = asDict(doc.cos.resolve(doc.cos.rootRef));
|
|
2065
|
-
const namesRoot = rootDict ? asDict(doc.cos.resolve(
|
|
2066
|
-
walkNames(namesRoot ? asDict(doc.cos.resolve(
|
|
2067
|
-
const rootAfArr = rootDict ? asArray(doc.cos.resolve(
|
|
2416
|
+
const namesRoot = rootDict ? asDict(doc.cos.resolve(dictGet8(rootDict, "Names"))) : void 0;
|
|
2417
|
+
walkNames(namesRoot ? asDict(doc.cos.resolve(dictGet8(namesRoot, "EmbeddedFiles"))) : void 0);
|
|
2418
|
+
const rootAfArr = rootDict ? asArray(doc.cos.resolve(dictGet8(rootDict, "AF"))) : void 0;
|
|
2068
2419
|
if (rootAfArr) {
|
|
2069
2420
|
for (const item of rootAfArr.items) {
|
|
2070
2421
|
pushFilespec(asDict(doc.cos.resolve(item)), "");
|
|
2071
2422
|
}
|
|
2072
2423
|
}
|
|
2073
2424
|
for (const p of doc.getPages()) {
|
|
2074
|
-
const annotsArr = asArray(doc.cos.resolve(
|
|
2425
|
+
const annotsArr = asArray(doc.cos.resolve(dictGet8(p.dict, "Annots")));
|
|
2075
2426
|
if (!annotsArr) continue;
|
|
2076
2427
|
for (const item of annotsArr.items) {
|
|
2077
2428
|
const aDict = asDict(doc.cos.resolve(item));
|
|
2078
2429
|
if (!aDict) continue;
|
|
2079
|
-
if (asName(
|
|
2080
|
-
pushFilespec(asDict(doc.cos.resolve(
|
|
2430
|
+
if (asName(dictGet8(aDict, "Subtype"))?.decoded === "FileAttachment") {
|
|
2431
|
+
pushFilespec(asDict(doc.cos.resolve(dictGet8(aDict, "FS"))), "");
|
|
2081
2432
|
}
|
|
2082
2433
|
}
|
|
2083
2434
|
}
|
|
@@ -2086,7 +2437,7 @@ function collectDocumentAttachments(doc) {
|
|
|
2086
2437
|
function collectNumberTreeDicts(doc, treeDict, visited = /* @__PURE__ */ new Set()) {
|
|
2087
2438
|
const out = [];
|
|
2088
2439
|
if (!treeDict) return out;
|
|
2089
|
-
const numsArr = asArray(doc.cos.resolve(
|
|
2440
|
+
const numsArr = asArray(doc.cos.resolve(dictGet8(treeDict, "Nums")));
|
|
2090
2441
|
if (numsArr) {
|
|
2091
2442
|
for (let idx = 0; idx + 1 < numsArr.items.length; idx += 2) {
|
|
2092
2443
|
const kNode = doc.cos.resolve(numsArr.items[idx]);
|
|
@@ -2096,7 +2447,7 @@ function collectNumberTreeDicts(doc, treeDict, visited = /* @__PURE__ */ new Set
|
|
|
2096
2447
|
}
|
|
2097
2448
|
}
|
|
2098
2449
|
}
|
|
2099
|
-
const kidsArr = asArray(doc.cos.resolve(
|
|
2450
|
+
const kidsArr = asArray(doc.cos.resolve(dictGet8(treeDict, "Kids")));
|
|
2100
2451
|
if (kidsArr) {
|
|
2101
2452
|
for (const kid of kidsArr.items) {
|
|
2102
2453
|
if (kid.kind === "ref") {
|
|
@@ -2209,7 +2560,8 @@ function parseQpdfArguments(argv) {
|
|
|
2209
2560
|
} else if (arg === "--linearize") {
|
|
2210
2561
|
linearize = true;
|
|
2211
2562
|
} else if (arg === "--show-linearization" || arg === "--check-linearization") {
|
|
2212
|
-
|
|
2563
|
+
const mode = arg === "--show-linearization" ? "show" : "check";
|
|
2564
|
+
showLinearization = !showLinearization || showLinearization === mode ? mode : "both";
|
|
2213
2565
|
} else if (arg === "--collate") {
|
|
2214
2566
|
collateCount = 1;
|
|
2215
2567
|
} else if (arg.startsWith("--collate=")) {
|
|
@@ -2462,23 +2814,30 @@ function parseQpdfArguments(argv) {
|
|
|
2462
2814
|
} else if (arg === "--encrypt") {
|
|
2463
2815
|
const userPassword = argv[++i] ?? "";
|
|
2464
2816
|
const ownerPassword = argv[++i] ?? "";
|
|
2465
|
-
|
|
2817
|
+
if (argv[++i] !== "256") {
|
|
2818
|
+
return { result: { exitCode: 2, stdout: "", stderr: "qpdf: only 256-bit encryption is supported; use --encrypt user-password owner-password 256 --\n" } };
|
|
2819
|
+
}
|
|
2466
2820
|
const cfg = {
|
|
2467
2821
|
userPassword,
|
|
2468
2822
|
ownerPassword,
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2823
|
+
permissions: {
|
|
2824
|
+
print: true,
|
|
2825
|
+
printHighRes: true,
|
|
2826
|
+
modify: true,
|
|
2827
|
+
copy: true,
|
|
2828
|
+
addNotes: true,
|
|
2829
|
+
fillForms: true,
|
|
2830
|
+
assemble: true,
|
|
2831
|
+
extractAccessibility: true
|
|
2832
|
+
}
|
|
2474
2833
|
};
|
|
2475
2834
|
i++;
|
|
2476
2835
|
while (i < argv.length && argv[i] !== "--") {
|
|
2477
2836
|
const sub = argv[i];
|
|
2478
|
-
if (sub === "--print=none") cfg.print = false;
|
|
2479
|
-
if (sub === "--modify=none") cfg.modify = false;
|
|
2480
|
-
if (sub === "--extract=n") cfg.copy = false;
|
|
2481
|
-
if (sub === "--annotate=n") cfg.addNotes = false;
|
|
2837
|
+
if (sub === "--print=none") cfg.permissions.print = cfg.permissions.printHighRes = false;
|
|
2838
|
+
if (sub === "--modify=none") cfg.permissions.modify = cfg.permissions.addNotes = cfg.permissions.fillForms = cfg.permissions.assemble = false;
|
|
2839
|
+
if (sub === "--extract=n") cfg.permissions.copy = false;
|
|
2840
|
+
if (sub === "--annotate=n") cfg.permissions.addNotes = false;
|
|
2482
2841
|
i++;
|
|
2483
2842
|
}
|
|
2484
2843
|
encryptConfig = cfg;
|
|
@@ -2721,27 +3080,45 @@ function* executeQpdfCli(options, files) {
|
|
|
2721
3080
|
let linDict;
|
|
2722
3081
|
for (const obj of cos.objects.values()) {
|
|
2723
3082
|
const d = asDict(obj.value);
|
|
2724
|
-
if (d &&
|
|
3083
|
+
if (d && dictGet8(d, "Linearized") !== void 0) {
|
|
2725
3084
|
linDict = d;
|
|
2726
3085
|
break;
|
|
2727
3086
|
}
|
|
2728
3087
|
}
|
|
2729
3088
|
if (!linDict) {
|
|
2730
|
-
return { exitCode: 0, stdout: `${inputFile ?? "empty"}
|
|
2731
|
-
|
|
3089
|
+
return { exitCode: 0, stdout: `${inputFile ?? "empty"} is not linearized
|
|
3090
|
+
`.repeat(showLinearization === "both" ? 2 : 1), stderr: "" };
|
|
2732
3091
|
}
|
|
2733
3092
|
const numVal = (k, fb = 0) => {
|
|
2734
|
-
const n = cos.resolve(
|
|
3093
|
+
const n = cos.resolve(dictGet8(linDict, k));
|
|
2735
3094
|
return n?.kind === "number" ? n.value : fb;
|
|
2736
3095
|
};
|
|
2737
|
-
const hArr = cos.resolveArray(
|
|
2738
|
-
const
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
3096
|
+
const hArr = cos.resolveArray(dictGet8(linDict, "H"));
|
|
3097
|
+
const hint = (index) => {
|
|
3098
|
+
const value = cos.resolve(hArr?.items[index]);
|
|
3099
|
+
return value?.kind === "number" ? value.value : 0;
|
|
3100
|
+
};
|
|
3101
|
+
const h0 = hint(0), h1 = hint(1);
|
|
3102
|
+
if (showLinearization === "check") return { exitCode: 0, stdout: `${inputFile ?? "empty"}: no linearization errors
|
|
3103
|
+
`, stderr: "" };
|
|
3104
|
+
const values = { file_size: numVal("L"), first_page_object: numVal("O"), first_page_end: numVal("E"), npages: baseDoc.getPageCount(), xref_zero_offset: numVal("T"), first_page: numVal("P"), H_offset: h0, H_length: h1 };
|
|
3105
|
+
let hintStream;
|
|
3106
|
+
for (const object of cos.objects.values()) if (object.span?.start === h0 && object.value.kind === "stream") {
|
|
3107
|
+
hintStream = object.value;
|
|
3108
|
+
break;
|
|
3109
|
+
}
|
|
3110
|
+
const hintBytes = hintStream ? cos.decodeStream(hintStream) : new Uint8Array();
|
|
3111
|
+
const shared = hintStream && cos.resolve(dictGet8(hintStream.dict, "S")), outline = hintStream && cos.resolve(dictGet8(hintStream.dict, "O"));
|
|
3112
|
+
const report = linearizationReport(inputFile ?? "empty", values, hintBytes.length, shared?.kind === "number" ? shared.value : 0, outline?.kind === "number" ? outline.value : void 0);
|
|
3113
|
+
let out = showLinearization === "both" ? `${inputFile ?? "empty"}: no linearization errors
|
|
3114
|
+
` : "", step = report.next();
|
|
3115
|
+
while (!step.done) {
|
|
3116
|
+
yield;
|
|
3117
|
+
if (typeof step.value === "string") {
|
|
3118
|
+
out += step.value;
|
|
3119
|
+
step = report.next(new Uint8Array());
|
|
3120
|
+
} else step = report.next(hintBytes.subarray(step.value.offset, step.value.offset + step.value.length));
|
|
3121
|
+
}
|
|
2745
3122
|
return { exitCode: 0, stdout: out, stderr: "" };
|
|
2746
3123
|
}
|
|
2747
3124
|
if (check) {
|
|
@@ -2749,7 +3126,7 @@ function* executeQpdfCli(options, files) {
|
|
|
2749
3126
|
let isLin = false;
|
|
2750
3127
|
for (const obj of cos.objects.values()) {
|
|
2751
3128
|
const d = asDict(obj.value);
|
|
2752
|
-
if (d &&
|
|
3129
|
+
if (d && dictGet8(d, "Linearized") !== void 0) {
|
|
2753
3130
|
isLin = true;
|
|
2754
3131
|
break;
|
|
2755
3132
|
}
|
|
@@ -2770,22 +3147,22 @@ errors that qpdf cannot detect
|
|
|
2770
3147
|
const pages = baseDoc.getPages();
|
|
2771
3148
|
const collectImagesFromResources = (resDict, visitedForms = /* @__PURE__ */ new Set()) => {
|
|
2772
3149
|
if (!resDict) return;
|
|
2773
|
-
const xobjDict = asDict(baseDoc.cos.resolve(
|
|
3150
|
+
const xobjDict = asDict(baseDoc.cos.resolve(dictGet8(resDict, "XObject")));
|
|
2774
3151
|
if (!xobjDict) return;
|
|
2775
3152
|
for (const entry of xobjDict.entries) {
|
|
2776
3153
|
const ref = asRef(entry.value);
|
|
2777
3154
|
const resolved = baseDoc.cos.resolve(entry.value);
|
|
2778
3155
|
const streamDict = resolved?.kind === "stream" ? resolved.dict : asDict(resolved);
|
|
2779
|
-
const subtype = streamDict ? asName(
|
|
3156
|
+
const subtype = streamDict ? asName(dictGet8(streamDict, "Subtype"))?.decoded : void 0;
|
|
2780
3157
|
if (subtype === "Image") {
|
|
2781
|
-
const w = asNumber(
|
|
2782
|
-
const h = asNumber(
|
|
3158
|
+
const w = asNumber(dictGet8(streamDict, "Width"))?.value ?? 0;
|
|
3159
|
+
const h = asNumber(dictGet8(streamDict, "Height"))?.value ?? 0;
|
|
2783
3160
|
const refStr = ref ? `${ref.objectNumber} ${ref.generationNumber} R` : "inline";
|
|
2784
|
-
lines.push(` /${entry.key.decoded}: ${refStr}
|
|
3161
|
+
lines.push(` /${entry.key.decoded}: ${refStr}, ${w} x ${h}`);
|
|
2785
3162
|
} else if (subtype === "Form" && streamDict) {
|
|
2786
3163
|
if (ref && visitedForms.has(ref.objectNumber)) continue;
|
|
2787
3164
|
if (ref) visitedForms.add(ref.objectNumber);
|
|
2788
|
-
const formRes = asDict(baseDoc.cos.resolve(
|
|
3165
|
+
const formRes = asDict(baseDoc.cos.resolve(dictGet8(streamDict, "Resources")));
|
|
2789
3166
|
collectImagesFromResources(formRes, visitedForms);
|
|
2790
3167
|
}
|
|
2791
3168
|
}
|
|
@@ -2794,11 +3171,12 @@ errors that qpdf cannot detect
|
|
|
2794
3171
|
const p = pages[idx];
|
|
2795
3172
|
lines.push(`page ${idx + 1}: ${p.ref.objectNumber} ${p.ref.generationNumber} R`);
|
|
2796
3173
|
if (withImages) {
|
|
2797
|
-
lines.
|
|
3174
|
+
const imageStart = lines.length;
|
|
2798
3175
|
collectImagesFromResources(p.getResourcesDict());
|
|
3176
|
+
if (lines.length > imageStart) lines.splice(imageStart, 0, " images:");
|
|
2799
3177
|
}
|
|
2800
3178
|
lines.push(" content:");
|
|
2801
|
-
const contentsNode =
|
|
3179
|
+
const contentsNode = dictGet8(p.dict, "Contents");
|
|
2802
3180
|
if (contentsNode?.kind === "ref") {
|
|
2803
3181
|
lines.push(` ${contentsNode.objectNumber} ${contentsNode.generationNumber} R`);
|
|
2804
3182
|
} else if (contentsNode?.kind === "array") {
|
|
@@ -2816,30 +3194,20 @@ errors that qpdf cannot detect
|
|
|
2816
3194
|
`, stderr: "" };
|
|
2817
3195
|
}
|
|
2818
3196
|
if (showEncryption) {
|
|
2819
|
-
|
|
2820
|
-
if (!enc) {
|
|
2821
|
-
return { exitCode: 0, stdout: "File is not encrypted\n", stderr: "" };
|
|
2822
|
-
}
|
|
2823
|
-
const out = `R = ${enc.revision}
|
|
2824
|
-
V = ${enc.version}
|
|
2825
|
-
Length = ${enc.keyLengthBits}
|
|
2826
|
-
print: ${enc.permissions.print ? "allowed" : "not allowed"}
|
|
2827
|
-
modify: ${enc.permissions.modify ? "allowed" : "not allowed"}
|
|
2828
|
-
extract for accessibility: ${enc.permissions.copy ? "allowed" : "not allowed"}
|
|
2829
|
-
`;
|
|
2830
|
-
return { exitCode: 0, stdout: out, stderr: "" };
|
|
3197
|
+
return { exitCode: 0, stdout: encryptionReport(baseDoc.cos.encryption), stderr: "" };
|
|
2831
3198
|
}
|
|
2832
3199
|
if (showXref) {
|
|
2833
3200
|
const lines = [];
|
|
2834
|
-
const revEntries = baseDoc.cos.revisions[
|
|
3201
|
+
const revEntries = new Map(baseDoc.cos.revisions.flatMap((revision) => [...revision.entries]).reverse());
|
|
2835
3202
|
for (const [objNum, obj] of [...baseDoc.cos.objects.entries()].sort((a, b) => a[0] - b[0])) {
|
|
2836
3203
|
const xEntry = revEntries?.get(objNum);
|
|
2837
3204
|
if (xEntry?.type === "compressed") {
|
|
2838
3205
|
lines.push(
|
|
2839
|
-
`${objNum}/0: compressed; stream = ${xEntry.objectStreamNumber}
|
|
3206
|
+
`${objNum}/0: compressed; stream = ${xEntry.objectStreamNumber}, index = ${xEntry.indexInStream ?? xEntry.indexInObjectStream}`
|
|
2840
3207
|
);
|
|
2841
3208
|
} else {
|
|
2842
|
-
|
|
3209
|
+
const offset = xEntry?.offset ?? obj.span?.start;
|
|
3210
|
+
lines.push(`${objNum}/${obj.generationNumber}: uncompressed; ${offset === void 0 ? `type = ${obj.value.kind}` : `offset = ${offset}`}`);
|
|
2843
3211
|
}
|
|
2844
3212
|
}
|
|
2845
3213
|
return { exitCode: 0, stdout: lines.join("\n") + "\n", stderr: "" };
|
|
@@ -2883,11 +3251,23 @@ extract for accessibility: ${enc.permissions.copy ? "allowed" : "not allowed"}
|
|
|
2883
3251
|
stderr: "qpdf: --json-stream-data=file requires --json-stream-prefix or an output file\n"
|
|
2884
3252
|
};
|
|
2885
3253
|
}
|
|
3254
|
+
const include = (key) => !jsonKeys.length || jsonKeys.includes(key);
|
|
3255
|
+
const includeObjects = include(jsonVersion === 1 ? "objects" : "qpdf");
|
|
3256
|
+
const objectInfo = {};
|
|
2886
3257
|
const objectsMap = {};
|
|
2887
3258
|
const hasSelector = jsonObjectSelectors.length > 0;
|
|
2888
3259
|
const matchesSelector = (objNum, genNum) => !hasSelector || jsonObjectSelectors.some((s) => s.kind === "obj" && s.objNum === objNum && s.genNum === genNum);
|
|
2889
3260
|
for (const [objNum, obj] of baseDoc.cos.objects.entries()) {
|
|
2890
3261
|
if (!matchesSelector(objNum, obj.generationNumber)) continue;
|
|
3262
|
+
if (jsonKeys.includes("objectinfo")) {
|
|
3263
|
+
const dict = obj.value.kind === "stream" ? obj.value.dict : void 0;
|
|
3264
|
+
objectInfo[`${objNum} ${obj.generationNumber} R`] = { stream: {
|
|
3265
|
+
filter: dict ? cosNodeToJson(dictGet8(dict, "Filter") ?? cosNull()) : null,
|
|
3266
|
+
is: Boolean(dict),
|
|
3267
|
+
length: dict ? cosNodeToJson(dictGet8(dict, "Length") ?? cosNull()) : null
|
|
3268
|
+
} };
|
|
3269
|
+
}
|
|
3270
|
+
if (!includeObjects) continue;
|
|
2891
3271
|
if (obj.value.kind === "stream") {
|
|
2892
3272
|
const streamObj = {
|
|
2893
3273
|
dict: cosNodeToJson(obj.value.dict)
|
|
@@ -2908,10 +3288,10 @@ extract for accessibility: ${enc.permissions.copy ? "allowed" : "not allowed"}
|
|
|
2908
3288
|
}
|
|
2909
3289
|
}
|
|
2910
3290
|
if (!hasSelector || jsonObjectSelectors.some((s) => s.kind === "trailer")) {
|
|
2911
|
-
const trailerDict =
|
|
3291
|
+
const trailerDict = cosDict6({
|
|
2912
3292
|
Root: baseDoc.cos.rootRef,
|
|
2913
3293
|
...baseDoc.cos.infoRef ? { Info: baseDoc.cos.infoRef } : {},
|
|
2914
|
-
Size:
|
|
3294
|
+
Size: cosNumber4(baseDoc.cos.maxObjectNumber + 1)
|
|
2915
3295
|
});
|
|
2916
3296
|
objectsMap.trailer = {
|
|
2917
3297
|
value: cosNodeToJson(trailerDict)
|
|
@@ -2919,12 +3299,12 @@ extract for accessibility: ${enc.permissions.copy ? "allowed" : "not allowed"}
|
|
|
2919
3299
|
}
|
|
2920
3300
|
const attachmentsJson = {};
|
|
2921
3301
|
const rootDictForJson = asDict(baseDoc.cos.resolve(baseDoc.cos.rootRef));
|
|
2922
|
-
for (const att of collectDocumentAttachments(baseDoc)) {
|
|
3302
|
+
for (const att of include("attachments") ? collectDocumentAttachments(baseDoc) : []) {
|
|
2923
3303
|
attachmentsJson[att.key] = { filename: att.filename };
|
|
2924
3304
|
}
|
|
2925
|
-
const acroDictJson = rootDictForJson ? asDict(baseDoc.cos.resolve(
|
|
2926
|
-
const needAppJson = acroDictJson ? baseDoc.cos.resolve(
|
|
2927
|
-
const acroFieldsJson = baseDoc.getFormFields().map((f) => ({
|
|
3305
|
+
const acroDictJson = rootDictForJson ? asDict(baseDoc.cos.resolve(dictGet8(rootDictForJson, "AcroForm"))) : void 0;
|
|
3306
|
+
const needAppJson = acroDictJson ? baseDoc.cos.resolve(dictGet8(acroDictJson, "NeedAppearances")) : void 0;
|
|
3307
|
+
const acroFieldsJson = (include("acroform") ? baseDoc.getFormFields() : []).map((f) => ({
|
|
2928
3308
|
name: f.name,
|
|
2929
3309
|
type: f.type,
|
|
2930
3310
|
value: f.value
|
|
@@ -2935,7 +3315,7 @@ extract for accessibility: ${enc.permissions.copy ? "allowed" : "not allowed"}
|
|
|
2935
3315
|
fields: acroFieldsJson
|
|
2936
3316
|
};
|
|
2937
3317
|
const outlinesJson = [];
|
|
2938
|
-
const outlinesRootJson = rootDictForJson ? asDict(baseDoc.cos.resolve(
|
|
3318
|
+
const outlinesRootJson = rootDictForJson ? asDict(baseDoc.cos.resolve(dictGet8(rootDictForJson, "Outlines"))) : void 0;
|
|
2939
3319
|
const collectJsonOutlines = (nodeOrRef, visited = /* @__PURE__ */ new Set()) => {
|
|
2940
3320
|
let cur = nodeOrRef;
|
|
2941
3321
|
while (cur) {
|
|
@@ -2945,9 +3325,9 @@ extract for accessibility: ${enc.permissions.copy ? "allowed" : "not allowed"}
|
|
|
2945
3325
|
}
|
|
2946
3326
|
const d = asDict(baseDoc.cos.resolve(cur));
|
|
2947
3327
|
if (!d) break;
|
|
2948
|
-
const tNode = baseDoc.cos.resolve(
|
|
2949
|
-
const title = tNode?.kind === "string" ?
|
|
2950
|
-
const destPageIdx = resolveDestinationPageIndex(baseDoc,
|
|
3328
|
+
const tNode = baseDoc.cos.resolve(dictGet8(d, "Title"));
|
|
3329
|
+
const title = tNode?.kind === "string" ? decodePdfString7(tNode) : "";
|
|
3330
|
+
const destPageIdx = resolveDestinationPageIndex(baseDoc, dictGet8(d, "Dest") ?? dictGet8(d, "A")) ?? 0;
|
|
2951
3331
|
const pageRef = baseDoc.getPageCount() > 0 ? baseDoc.getPage(destPageIdx).ref : { objectNumber: 0, generationNumber: 0 };
|
|
2952
3332
|
if (title) {
|
|
2953
3333
|
outlinesJson.push({
|
|
@@ -2956,29 +3336,46 @@ extract for accessibility: ${enc.permissions.copy ? "allowed" : "not allowed"}
|
|
|
2956
3336
|
destpageobject: `obj:${pageRef.objectNumber} ${pageRef.generationNumber} R`
|
|
2957
3337
|
});
|
|
2958
3338
|
}
|
|
2959
|
-
const firstChild =
|
|
3339
|
+
const firstChild = dictGet8(d, "First");
|
|
2960
3340
|
if (firstChild) collectJsonOutlines(firstChild, visited);
|
|
2961
|
-
cur =
|
|
3341
|
+
cur = dictGet8(d, "Next");
|
|
2962
3342
|
}
|
|
2963
3343
|
};
|
|
2964
|
-
if (outlinesRootJson) collectJsonOutlines(
|
|
3344
|
+
if (include("outlines") && outlinesRootJson) collectJsonOutlines(dictGet8(outlinesRootJson, "First"));
|
|
2965
3345
|
const pagelabelsJson = [];
|
|
2966
|
-
const plTreeJson = rootDictForJson ? asDict(baseDoc.cos.resolve(
|
|
2967
|
-
for (const { index: plIdx, dict: vDict } of collectNumberTreeDicts(baseDoc, plTreeJson)) {
|
|
2968
|
-
const stNode = baseDoc.cos.resolve(
|
|
2969
|
-
const pNode = baseDoc.cos.resolve(
|
|
2970
|
-
const sNode = baseDoc.cos.resolve(
|
|
3346
|
+
const plTreeJson = rootDictForJson ? asDict(baseDoc.cos.resolve(dictGet8(rootDictForJson, "PageLabels"))) : void 0;
|
|
3347
|
+
for (const { index: plIdx, dict: vDict } of include("pagelabels") ? collectNumberTreeDicts(baseDoc, plTreeJson) : []) {
|
|
3348
|
+
const stNode = baseDoc.cos.resolve(dictGet8(vDict, "St"));
|
|
3349
|
+
const pNode = baseDoc.cos.resolve(dictGet8(vDict, "P"));
|
|
3350
|
+
const sNode = baseDoc.cos.resolve(dictGet8(vDict, "S"));
|
|
2971
3351
|
pagelabelsJson.push({
|
|
2972
3352
|
index: plIdx,
|
|
2973
3353
|
start: stNode?.kind === "number" ? stNode.value : 1,
|
|
2974
|
-
...pNode?.kind === "string" ? { prefix:
|
|
3354
|
+
...pNode?.kind === "string" ? { prefix: decodePdfString7(pNode) } : {},
|
|
2975
3355
|
...sNode?.kind === "name" ? { style: sNode.decoded } : {}
|
|
2976
3356
|
});
|
|
2977
3357
|
}
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
3358
|
+
let pagesVal = baseDoc.getPageCount();
|
|
3359
|
+
if (jsonKeys.includes("pages")) {
|
|
3360
|
+
const values = [];
|
|
3361
|
+
for (const [index, page] of baseDoc.getPages().entries()) {
|
|
3362
|
+
let materialize2 = function(value) {
|
|
3363
|
+
if (value === null || typeof value !== "object") return value;
|
|
3364
|
+
if (value.kind === "array") return Array.from(value.values, materialize2);
|
|
3365
|
+
if (value.kind === "object") return Object.fromEntries(Array.from(value.entries, ([key, item]) => [key, materialize2(item)]));
|
|
3366
|
+
throw new Error("Unexpected page JSON value");
|
|
3367
|
+
};
|
|
3368
|
+
var materialize = materialize2;
|
|
3369
|
+
const steps = qpdfJsonPage(page.dict, page.ref, index, rootDictForJson, jsonVersion);
|
|
3370
|
+
let step = steps.next();
|
|
3371
|
+
while (!step.done) {
|
|
3372
|
+
yield;
|
|
3373
|
+
step = steps.next(baseDoc.cos.resolve(step.value));
|
|
3374
|
+
}
|
|
3375
|
+
values.push(materialize2(step.value));
|
|
3376
|
+
}
|
|
3377
|
+
pagesVal = values;
|
|
3378
|
+
}
|
|
2982
3379
|
const payload = jsonVersion === 1 ? {
|
|
2983
3380
|
version: 1,
|
|
2984
3381
|
pdfversion: baseDoc.cos.version,
|
|
@@ -3005,7 +3402,20 @@ extract for accessibility: ${enc.permissions.copy ? "allowed" : "not allowed"}
|
|
|
3005
3402
|
objectsMap
|
|
3006
3403
|
]
|
|
3007
3404
|
};
|
|
3008
|
-
const
|
|
3405
|
+
const sections = {
|
|
3406
|
+
pages: pagesVal,
|
|
3407
|
+
pagelabels: pagelabelsJson,
|
|
3408
|
+
outlines: outlinesJson,
|
|
3409
|
+
acroform: acroformSection,
|
|
3410
|
+
encrypt: { encrypted: Boolean(baseDoc.cos.encryption) },
|
|
3411
|
+
attachments: attachmentsJson,
|
|
3412
|
+
objects: objectsMap,
|
|
3413
|
+
objectinfo: objectInfo,
|
|
3414
|
+
qpdf: [{ jsonversion: 2, pdfversion: baseDoc.cos.version, maxobjectid: baseDoc.cos.maxObjectNumber }, objectsMap]
|
|
3415
|
+
};
|
|
3416
|
+
const selectedPayload = { version: jsonVersion, parameters: { decodelevel: "generalized" } };
|
|
3417
|
+
for (const key of jsonKeys) selectedPayload[key] = sections[key];
|
|
3418
|
+
const jsonOut = JSON.stringify(jsonKeys.length ? selectedPayload : payload, null, 2) + "\n";
|
|
3009
3419
|
if (outputFile && outputFile !== "-") {
|
|
3010
3420
|
files.set(outputFile, new TextEncoder().encode(jsonOut));
|
|
3011
3421
|
return { exitCode: 0, stdout: "", stderr: "" };
|
|
@@ -3132,17 +3542,17 @@ extract for accessibility: ${enc.permissions.copy ? "allowed" : "not allowed"}
|
|
|
3132
3542
|
const targetRes = targetPage.getResourcesDict();
|
|
3133
3543
|
const srcRes = srcStampPage.getResourcesDict();
|
|
3134
3544
|
for (const catKey of ["Font", "XObject", "ExtGState", "ColorSpace", "Pattern", "Shading", "Properties"]) {
|
|
3135
|
-
const srcSub = asDict(stampDoc.cos.resolve(
|
|
3545
|
+
const srcSub = asDict(stampDoc.cos.resolve(dictGet8(srcRes, catKey)));
|
|
3136
3546
|
if (!srcSub) continue;
|
|
3137
|
-
let dstSub = asDict(workingDoc.cos.resolve(
|
|
3547
|
+
let dstSub = asDict(workingDoc.cos.resolve(dictGet8(targetRes, catKey)));
|
|
3138
3548
|
if (!dstSub) {
|
|
3139
|
-
dstSub =
|
|
3549
|
+
dstSub = cosDict6({});
|
|
3140
3550
|
dictSet(targetRes, catKey, dstSub);
|
|
3141
3551
|
}
|
|
3142
3552
|
for (const entry of srcSub.entries) {
|
|
3143
3553
|
const origKey = entry.key.decoded;
|
|
3144
3554
|
let finalKey = origKey;
|
|
3145
|
-
if (
|
|
3555
|
+
if (dictGet8(dstSub, origKey)) {
|
|
3146
3556
|
finalKey = `QStp${stampResCounter++}_${origKey}`;
|
|
3147
3557
|
stampStreamStr = replacePdfNameToken(stampStreamStr, origKey, finalKey);
|
|
3148
3558
|
}
|
|
@@ -3150,43 +3560,10 @@ extract for accessibility: ${enc.permissions.copy ? "allowed" : "not allowed"}
|
|
|
3150
3560
|
}
|
|
3151
3561
|
}
|
|
3152
3562
|
const stampStream = iso88591ToBytes(stampStreamStr);
|
|
3153
|
-
const
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
if (srcSize.width > 0 && srcSize.height > 0) {
|
|
3158
|
-
const fmt6 = (n) => Number(n.toFixed(6));
|
|
3159
|
-
const fmt4 = (n) => Number(n.toFixed(4));
|
|
3160
|
-
if (rotDiff === 90) {
|
|
3161
|
-
const s = Math.min(dstSize.height / srcSize.width, dstSize.width / srcSize.height);
|
|
3162
|
-
const ox = (dstSize.height - srcSize.width * s) / 2;
|
|
3163
|
-
const oy = (dstSize.width - srcSize.height * s) / 2;
|
|
3164
|
-
stampOpenStr = `q
|
|
3165
|
-
0 ${fmt6(s)} ${fmt6(-s)} 0 ${fmt4(dstSize.width - oy)} ${fmt4(ox)} cm
|
|
3166
|
-
`;
|
|
3167
|
-
} else if (rotDiff === 180) {
|
|
3168
|
-
const s = Math.min(dstSize.width / srcSize.width, dstSize.height / srcSize.height);
|
|
3169
|
-
const ox = (dstSize.width - srcSize.width * s) / 2;
|
|
3170
|
-
const oy = (dstSize.height - srcSize.height * s) / 2;
|
|
3171
|
-
stampOpenStr = `q
|
|
3172
|
-
${fmt6(-s)} 0 0 ${fmt6(-s)} ${fmt4(dstSize.width - ox)} ${fmt4(dstSize.height - oy)} cm
|
|
3173
|
-
`;
|
|
3174
|
-
} else if (rotDiff === 270) {
|
|
3175
|
-
const s = Math.min(dstSize.height / srcSize.width, dstSize.width / srcSize.height);
|
|
3176
|
-
const ox = (dstSize.height - srcSize.width * s) / 2;
|
|
3177
|
-
const oy = (dstSize.width - srcSize.height * s) / 2;
|
|
3178
|
-
stampOpenStr = `q
|
|
3179
|
-
0 ${fmt6(-s)} ${fmt6(s)} 0 ${fmt4(oy)} ${fmt4(dstSize.height - ox)} cm
|
|
3180
|
-
`;
|
|
3181
|
-
} else if (Math.abs(srcSize.width - dstSize.width) > 0.5 || Math.abs(srcSize.height - dstSize.height) > 0.5) {
|
|
3182
|
-
const s = Math.min(dstSize.width / srcSize.width, dstSize.height / srcSize.height);
|
|
3183
|
-
const tx = (dstSize.width - srcSize.width * s) / 2;
|
|
3184
|
-
const ty = (dstSize.height - srcSize.height * s) / 2;
|
|
3185
|
-
stampOpenStr = `q
|
|
3186
|
-
${fmt6(s)} 0 0 ${fmt6(s)} ${fmt4(tx)} ${fmt4(ty)} cm
|
|
3187
|
-
`;
|
|
3188
|
-
}
|
|
3189
|
-
}
|
|
3563
|
+
const stampOpenStr = retainedStampTransform(
|
|
3564
|
+
{ mediaBox: targetPage.getMediaBox(), rotation: targetPage.getRotation() },
|
|
3565
|
+
{ mediaBox: srcStampPage.getMediaBox(), rotation: srcStampPage.getRotation() }
|
|
3566
|
+
);
|
|
3190
3567
|
const qOpenTarget = enc.encode("q\n");
|
|
3191
3568
|
const qOpenStamp = enc.encode(stampOpenStr);
|
|
3192
3569
|
const qClose = enc.encode("\nQ\n");
|
|
@@ -3235,10 +3612,10 @@ ${fmt6(s)} 0 0 ${fmt6(s)} ${fmt4(tx)} ${fmt4(ty)} cm
|
|
|
3235
3612
|
const [p0x, p0y] = mapPt(nums[0], nums[1]);
|
|
3236
3613
|
const [p1x, p1y] = mapPt(nums[2], nums[3]);
|
|
3237
3614
|
return cosArray3([
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3615
|
+
cosNumber4(Math.min(p0x, p1x)),
|
|
3616
|
+
cosNumber4(Math.min(p0y, p1y)),
|
|
3617
|
+
cosNumber4(Math.max(p0x, p1x)),
|
|
3618
|
+
cosNumber4(Math.max(p0y, p1y))
|
|
3242
3619
|
]);
|
|
3243
3620
|
};
|
|
3244
3621
|
let cmCmd = "";
|
|
@@ -3246,7 +3623,7 @@ ${fmt6(s)} 0 0 ${fmt6(s)} ${fmt4(tx)} ${fmt4(ty)} cm
|
|
|
3246
3623
|
cmCmd = `q
|
|
3247
3624
|
0 -1 1 0 0 ${W} cm
|
|
3248
3625
|
`;
|
|
3249
|
-
dictSet(page.dict, "MediaBox", cosArray3([
|
|
3626
|
+
dictSet(page.dict, "MediaBox", cosArray3([cosNumber4(0), cosNumber4(0), cosNumber4(H), cosNumber4(W)]));
|
|
3250
3627
|
} else if (rot === 180) {
|
|
3251
3628
|
cmCmd = `q
|
|
3252
3629
|
-1 0 0 -1 ${W} ${H} cm
|
|
@@ -3255,18 +3632,18 @@ ${fmt6(s)} 0 0 ${fmt6(s)} ${fmt4(tx)} ${fmt4(ty)} cm
|
|
|
3255
3632
|
cmCmd = `q
|
|
3256
3633
|
0 1 -1 0 ${H} 0 cm
|
|
3257
3634
|
`;
|
|
3258
|
-
dictSet(page.dict, "MediaBox", cosArray3([
|
|
3635
|
+
dictSet(page.dict, "MediaBox", cosArray3([cosNumber4(0), cosNumber4(0), cosNumber4(H), cosNumber4(W)]));
|
|
3259
3636
|
}
|
|
3260
3637
|
for (const boxKey of ["CropBox", "BleedBox", "TrimBox", "ArtBox"]) {
|
|
3261
|
-
const mapped = mapRectNode(
|
|
3638
|
+
const mapped = mapRectNode(dictGet8(page.dict, boxKey));
|
|
3262
3639
|
if (mapped) dictSet(page.dict, boxKey, mapped);
|
|
3263
3640
|
}
|
|
3264
|
-
const annotsArr = asArray(workingDoc.cos.resolve(
|
|
3641
|
+
const annotsArr = asArray(workingDoc.cos.resolve(dictGet8(page.dict, "Annots")));
|
|
3265
3642
|
if (annotsArr) {
|
|
3266
3643
|
for (const item of annotsArr.items) {
|
|
3267
3644
|
const aDict = asDict(workingDoc.cos.resolve(item));
|
|
3268
3645
|
if (!aDict) continue;
|
|
3269
|
-
const mappedRect = mapRectNode(
|
|
3646
|
+
const mappedRect = mapRectNode(dictGet8(aDict, "Rect"));
|
|
3270
3647
|
if (mappedRect) dictSet(aDict, "Rect", mappedRect);
|
|
3271
3648
|
}
|
|
3272
3649
|
}
|
|
@@ -3286,19 +3663,19 @@ ${fmt6(s)} 0 0 ${fmt6(s)} ${fmt4(tx)} ${fmt4(ty)} cm
|
|
|
3286
3663
|
const enc = new TextEncoder();
|
|
3287
3664
|
for (const page of workingDoc.getPages()) {
|
|
3288
3665
|
yield;
|
|
3289
|
-
const annotsNode = workingDoc.cos.resolve(
|
|
3666
|
+
const annotsNode = workingDoc.cos.resolve(dictGet8(page.dict, "Annots"));
|
|
3290
3667
|
const annotsArr = asArray(annotsNode);
|
|
3291
3668
|
if (!annotsArr) continue;
|
|
3292
3669
|
const survivingAnnots = [];
|
|
3293
3670
|
for (const item of annotsArr.items) {
|
|
3294
3671
|
const annotDict = asDict(workingDoc.cos.resolve(item));
|
|
3295
3672
|
if (!annotDict) continue;
|
|
3296
|
-
const subtype = asName(
|
|
3673
|
+
const subtype = asName(dictGet8(annotDict, "Subtype"))?.decoded;
|
|
3297
3674
|
if (subtype === "Link" || subtype === "Popup") {
|
|
3298
3675
|
survivingAnnots.push(item);
|
|
3299
3676
|
continue;
|
|
3300
3677
|
}
|
|
3301
|
-
const flagsNode = asNumber(workingDoc.cos.resolve(
|
|
3678
|
+
const flagsNode = asNumber(workingDoc.cos.resolve(dictGet8(annotDict, "F")));
|
|
3302
3679
|
if (flagsNode !== void 0) {
|
|
3303
3680
|
const flags = flagsNode.value;
|
|
3304
3681
|
const isInvisibleOrHidden = (flags & 1) !== 0 || (flags & 2) !== 0;
|
|
@@ -3310,26 +3687,26 @@ ${fmt6(s)} 0 0 ${fmt6(s)} ${fmt4(tx)} ${fmt4(ty)} cm
|
|
|
3310
3687
|
continue;
|
|
3311
3688
|
}
|
|
3312
3689
|
}
|
|
3313
|
-
const rectArr = asArray(workingDoc.cos.resolve(
|
|
3690
|
+
const rectArr = asArray(workingDoc.cos.resolve(dictGet8(annotDict, "Rect")));
|
|
3314
3691
|
const rx0 = asNumber(workingDoc.cos.resolve(rectArr?.items[0]))?.value ?? 72;
|
|
3315
3692
|
const ry0 = asNumber(workingDoc.cos.resolve(rectArr?.items[1]))?.value ?? 72;
|
|
3316
3693
|
const rx1 = asNumber(workingDoc.cos.resolve(rectArr?.items[2]))?.value ?? rx0 + 50;
|
|
3317
3694
|
const ry1 = asNumber(workingDoc.cos.resolve(rectArr?.items[3]))?.value ?? ry0 + 20;
|
|
3318
|
-
const apDict = asDict(workingDoc.cos.resolve(
|
|
3319
|
-
let apStreamNode = apDict ? workingDoc.cos.resolve(
|
|
3695
|
+
const apDict = asDict(workingDoc.cos.resolve(dictGet8(annotDict, "AP")));
|
|
3696
|
+
let apStreamNode = apDict ? workingDoc.cos.resolve(dictGet8(apDict, "N")) : void 0;
|
|
3320
3697
|
if (apStreamNode?.kind === "dict") {
|
|
3321
|
-
const asState = asName(workingDoc.cos.resolve(
|
|
3322
|
-
apStreamNode = (asState ? workingDoc.cos.resolve(
|
|
3698
|
+
const asState = asName(workingDoc.cos.resolve(dictGet8(annotDict, "AS")))?.decoded;
|
|
3699
|
+
apStreamNode = (asState ? workingDoc.cos.resolve(dictGet8(apStreamNode, asState)) : void 0) ?? workingDoc.cos.resolve(apStreamNode.entries[0]?.value);
|
|
3323
3700
|
}
|
|
3324
3701
|
if (apStreamNode?.kind === "stream") {
|
|
3325
3702
|
let apBytes = workingDoc.cos.decodeStream(apStreamNode);
|
|
3326
3703
|
if (apBytes.byteLength > 0) {
|
|
3327
|
-
const bboxArr = asArray(workingDoc.cos.resolve(
|
|
3704
|
+
const bboxArr = asArray(workingDoc.cos.resolve(dictGet8(apStreamNode.dict, "BBox")));
|
|
3328
3705
|
const bx0 = asNumber(workingDoc.cos.resolve(bboxArr?.items[0]))?.value ?? 0;
|
|
3329
3706
|
const by0 = asNumber(workingDoc.cos.resolve(bboxArr?.items[1]))?.value ?? 0;
|
|
3330
3707
|
const bx1 = asNumber(workingDoc.cos.resolve(bboxArr?.items[2]))?.value ?? Math.max(1, rx1 - rx0);
|
|
3331
3708
|
const by1 = asNumber(workingDoc.cos.resolve(bboxArr?.items[3]))?.value ?? Math.max(1, ry1 - ry0);
|
|
3332
|
-
const matArr = asArray(workingDoc.cos.resolve(
|
|
3709
|
+
const matArr = asArray(workingDoc.cos.resolve(dictGet8(apStreamNode.dict, "Matrix")));
|
|
3333
3710
|
let ma = 1, mb = 0, mc = 0, md = 1, me = 0, mf = 0;
|
|
3334
3711
|
let hasFormMatrix = false;
|
|
3335
3712
|
if (matArr && matArr.items.length >= 6) {
|
|
@@ -3358,27 +3735,27 @@ ${fmt6(s)} 0 0 ${fmt6(s)} ${fmt4(tx)} ${fmt4(ty)} cm
|
|
|
3358
3735
|
const scaleY = (ry1 - ry0) / bh;
|
|
3359
3736
|
const tx = rx0 - tbx0 * scaleX;
|
|
3360
3737
|
const ty = ry0 - tby0 * scaleY;
|
|
3361
|
-
const apRes = asDict(workingDoc.cos.resolve(
|
|
3738
|
+
const apRes = asDict(workingDoc.cos.resolve(dictGet8(apStreamNode.dict, "Resources")));
|
|
3362
3739
|
const resRenames = /* @__PURE__ */ new Map();
|
|
3363
3740
|
if (apRes) {
|
|
3364
3741
|
const pageRes = page.getResourcesDict();
|
|
3365
3742
|
for (const catKey of ["Font", "XObject", "ExtGState", "ColorSpace", "Pattern", "Shading", "Properties"]) {
|
|
3366
|
-
const srcSub = asDict(workingDoc.cos.resolve(
|
|
3743
|
+
const srcSub = asDict(workingDoc.cos.resolve(dictGet8(apRes, catKey)));
|
|
3367
3744
|
if (!srcSub) continue;
|
|
3368
|
-
let dstSub = asDict(workingDoc.cos.resolve(
|
|
3745
|
+
let dstSub = asDict(workingDoc.cos.resolve(dictGet8(pageRes, catKey)));
|
|
3369
3746
|
if (!dstSub) {
|
|
3370
|
-
dstSub =
|
|
3747
|
+
dstSub = cosDict6({});
|
|
3371
3748
|
dictSet(pageRes, catKey, dstSub);
|
|
3372
3749
|
}
|
|
3373
3750
|
for (const entry of srcSub.entries) {
|
|
3374
3751
|
const origName = entry.key.decoded;
|
|
3375
|
-
const existingEntry =
|
|
3752
|
+
const existingEntry = dictGet8(dstSub, origName);
|
|
3376
3753
|
if (!existingEntry) {
|
|
3377
3754
|
dictSet(dstSub, origName, entry.value);
|
|
3378
3755
|
} else if (existingEntry.kind === "ref" && entry.value.kind === "ref" && existingEntry.objectNumber === entry.value.objectNumber) {
|
|
3379
3756
|
} else {
|
|
3380
3757
|
let suffix2 = 1;
|
|
3381
|
-
while (
|
|
3758
|
+
while (dictGet8(dstSub, `${origName}_qap${suffix2}`)) suffix2++;
|
|
3382
3759
|
const newName = `${origName}_qap${suffix2}`;
|
|
3383
3760
|
dictSet(dstSub, newName, entry.value);
|
|
3384
3761
|
resRenames.set(origName, newName);
|
|
@@ -3410,14 +3787,14 @@ Q
|
|
|
3410
3787
|
continue;
|
|
3411
3788
|
}
|
|
3412
3789
|
}
|
|
3413
|
-
const contentsNode =
|
|
3414
|
-
let text = contentsNode?.kind === "string" ?
|
|
3790
|
+
const contentsNode = dictGet8(annotDict, "Contents");
|
|
3791
|
+
let text = contentsNode?.kind === "string" ? decodePdfString7(contentsNode).trim() : "";
|
|
3415
3792
|
if (!text) {
|
|
3416
|
-
const parentDict = asDict(workingDoc.cos.resolve(
|
|
3417
|
-
const vNode = workingDoc.cos.resolve(
|
|
3418
|
-
const asStateNode = workingDoc.cos.resolve(
|
|
3793
|
+
const parentDict = asDict(workingDoc.cos.resolve(dictGet8(annotDict, "Parent")));
|
|
3794
|
+
const vNode = workingDoc.cos.resolve(dictGet8(annotDict, "V")) ?? (parentDict ? workingDoc.cos.resolve(dictGet8(parentDict, "V")) : void 0);
|
|
3795
|
+
const asStateNode = workingDoc.cos.resolve(dictGet8(annotDict, "AS"));
|
|
3419
3796
|
if (vNode?.kind === "string") {
|
|
3420
|
-
text =
|
|
3797
|
+
text = decodePdfString7(vNode).trim();
|
|
3421
3798
|
} else if (vNode?.kind === "name" && vNode.decoded !== "Off") {
|
|
3422
3799
|
text = vNode.decoded;
|
|
3423
3800
|
} else if (asStateNode?.kind === "name" && asStateNode.decoded !== "Off") {
|
|
@@ -3467,7 +3844,7 @@ Q
|
|
|
3467
3844
|
yield;
|
|
3468
3845
|
let modified = false;
|
|
3469
3846
|
const pageRes = page.getResourcesDict();
|
|
3470
|
-
let xobjSub = asDict(workingDoc.cos.resolve(
|
|
3847
|
+
let xobjSub = asDict(workingDoc.cos.resolve(dictGet8(pageRes, "XObject")));
|
|
3471
3848
|
const transformNodes = (nodes) => {
|
|
3472
3849
|
const out = [];
|
|
3473
3850
|
for (const node of nodes) {
|
|
@@ -3477,14 +3854,14 @@ Q
|
|
|
3477
3854
|
out.push({ ...node, children: transformNodes(node.children) });
|
|
3478
3855
|
} else if (node.kind === "inline-image" && node.data.byteLength >= iiMinBytes) {
|
|
3479
3856
|
if (!xobjSub) {
|
|
3480
|
-
xobjSub =
|
|
3857
|
+
xobjSub = cosDict6({});
|
|
3481
3858
|
dictSet(pageRes, "XObject", xobjSub);
|
|
3482
3859
|
}
|
|
3483
3860
|
let resName = `ImExt${extCounter++}`;
|
|
3484
|
-
while (
|
|
3861
|
+
while (dictGet8(xobjSub, resName)) {
|
|
3485
3862
|
resName = `ImExt${extCounter++}`;
|
|
3486
3863
|
}
|
|
3487
|
-
const imgDict =
|
|
3864
|
+
const imgDict = cosDict6({
|
|
3488
3865
|
Type: cosName2("XObject"),
|
|
3489
3866
|
Subtype: cosName2("Image")
|
|
3490
3867
|
});
|
|
@@ -3553,12 +3930,12 @@ Q
|
|
|
3553
3930
|
yield;
|
|
3554
3931
|
const refNames = /* @__PURE__ */ new Set();
|
|
3555
3932
|
collectPdfNamesFromBytes(page.getRawContentStream(), refNames);
|
|
3556
|
-
const annotsArr = asArray(workingDoc.cos.resolve(
|
|
3933
|
+
const annotsArr = asArray(workingDoc.cos.resolve(dictGet8(page.dict, "Annots")));
|
|
3557
3934
|
if (annotsArr) {
|
|
3558
3935
|
for (const item of annotsArr.items) {
|
|
3559
3936
|
const aDict = asDict(workingDoc.cos.resolve(item));
|
|
3560
|
-
const apDict = aDict ? asDict(workingDoc.cos.resolve(
|
|
3561
|
-
const apN = apDict ? workingDoc.cos.resolve(
|
|
3937
|
+
const apDict = aDict ? asDict(workingDoc.cos.resolve(dictGet8(aDict, "AP"))) : void 0;
|
|
3938
|
+
const apN = apDict ? workingDoc.cos.resolve(dictGet8(apDict, "N")) : void 0;
|
|
3562
3939
|
if (apN?.kind === "stream") {
|
|
3563
3940
|
collectPdfNamesFromBytes(workingDoc.cos.decodeStream(apN), refNames);
|
|
3564
3941
|
}
|
|
@@ -3570,7 +3947,7 @@ Q
|
|
|
3570
3947
|
while (changed) {
|
|
3571
3948
|
changed = false;
|
|
3572
3949
|
for (const catKey of ["XObject", "Pattern"]) {
|
|
3573
|
-
const sub = asDict(workingDoc.cos.resolve(
|
|
3950
|
+
const sub = asDict(workingDoc.cos.resolve(dictGet8(pageRes, catKey)));
|
|
3574
3951
|
if (!sub) continue;
|
|
3575
3952
|
for (const entry of sub.entries) {
|
|
3576
3953
|
const visitId = `${catKey}:${entry.key.decoded}`;
|
|
@@ -3587,7 +3964,7 @@ Q
|
|
|
3587
3964
|
}
|
|
3588
3965
|
}
|
|
3589
3966
|
for (const catKey of resourceCategories) {
|
|
3590
|
-
const sub = asDict(workingDoc.cos.resolve(
|
|
3967
|
+
const sub = asDict(workingDoc.cos.resolve(dictGet8(pageRes, catKey)));
|
|
3591
3968
|
if (!sub) continue;
|
|
3592
3969
|
for (const entry of [...sub.entries]) {
|
|
3593
3970
|
if (!refNames.has(entry.key.decoded)) {
|
|
@@ -3601,33 +3978,40 @@ Q
|
|
|
3601
3978
|
}
|
|
3602
3979
|
}
|
|
3603
3980
|
if (linearize) {
|
|
3604
|
-
const linDict =
|
|
3605
|
-
Linearized:
|
|
3606
|
-
N:
|
|
3981
|
+
const linDict = cosDict6({
|
|
3982
|
+
Linearized: cosNumber4(1),
|
|
3983
|
+
N: cosNumber4(workingDoc.getPageCount())
|
|
3607
3984
|
});
|
|
3608
3985
|
workingDoc.cos.allocateObject(linDict);
|
|
3609
3986
|
}
|
|
3610
|
-
|
|
3987
|
+
let pageLabels;
|
|
3988
|
+
try {
|
|
3989
|
+
pageLabels = parseQpdfPageLabels(pageLabelSpecs, workingDoc.getPageCount());
|
|
3990
|
+
} catch (error) {
|
|
3991
|
+
return { exitCode: 2, stdout: "", stderr: `qpdf: ${error.message}
|
|
3992
|
+
` };
|
|
3993
|
+
}
|
|
3994
|
+
if (removePageLabels || pageLabels.length > 0) {
|
|
3611
3995
|
const rootDict = asDict(workingDoc.cos.resolve(workingDoc.cos.rootRef));
|
|
3612
3996
|
if (rootDict) {
|
|
3613
|
-
if (removePageLabels &&
|
|
3997
|
+
if (removePageLabels && pageLabels.length === 0) {
|
|
3614
3998
|
dictDelete(rootDict, "PageLabels");
|
|
3615
|
-
} else if (
|
|
3999
|
+
} else if (pageLabels.length > 0) {
|
|
3616
4000
|
const numsItems = [];
|
|
3617
|
-
for (const { index, style, start, prefix } of
|
|
3618
|
-
const labelDict =
|
|
4001
|
+
for (const { index, style, start, prefix } of pageLabels) {
|
|
4002
|
+
const labelDict = cosDict6({});
|
|
3619
4003
|
if (style !== void 0) {
|
|
3620
4004
|
dictSet(labelDict, "S", cosName2(style));
|
|
3621
4005
|
}
|
|
3622
4006
|
if (start !== void 0 && start !== 1) {
|
|
3623
|
-
dictSet(labelDict, "St",
|
|
4007
|
+
dictSet(labelDict, "St", cosNumber4(start));
|
|
3624
4008
|
}
|
|
3625
4009
|
if (prefix) {
|
|
3626
4010
|
dictSet(labelDict, "P", cosString3(prefix));
|
|
3627
4011
|
}
|
|
3628
|
-
numsItems.push(
|
|
4012
|
+
numsItems.push(cosNumber4(index), labelDict);
|
|
3629
4013
|
}
|
|
3630
|
-
dictSet(rootDict, "PageLabels", workingDoc.cos.allocateObject(
|
|
4014
|
+
dictSet(rootDict, "PageLabels", workingDoc.cos.allocateObject(cosDict6({ Nums: cosArray3(numsItems) })));
|
|
3631
4015
|
}
|
|
3632
4016
|
}
|
|
3633
4017
|
}
|
|
@@ -3640,7 +4024,7 @@ Q
|
|
|
3640
4024
|
if (rootDict) dictDelete(rootDict, "Metadata");
|
|
3641
4025
|
const infoDict = workingDoc.cos.infoRef ? asDict(workingDoc.cos.resolve(workingDoc.cos.infoRef)) : void 0;
|
|
3642
4026
|
if (infoDict) {
|
|
3643
|
-
const modDate =
|
|
4027
|
+
const modDate = dictGet8(infoDict, "ModDate");
|
|
3644
4028
|
infoDict.entries.length = 0;
|
|
3645
4029
|
if (modDate) {
|
|
3646
4030
|
dictSet(infoDict, "ModDate", modDate);
|
|
@@ -3669,14 +4053,14 @@ Q
|
|
|
3669
4053
|
}
|
|
3670
4054
|
const srcDoc = yield* PdfDocument.loadSteps(srcBytes, copySpec.password ? { password: copySpec.password } : {});
|
|
3671
4055
|
const srcRoot = asDict(srcDoc.cos.resolve(srcDoc.cos.rootRef));
|
|
3672
|
-
const srcNames = srcRoot ? asDict(srcDoc.cos.resolve(
|
|
3673
|
-
const srcEf = srcNames ? asDict(srcDoc.cos.resolve(
|
|
4056
|
+
const srcNames = srcRoot ? asDict(srcDoc.cos.resolve(dictGet8(srcRoot, "Names"))) : void 0;
|
|
4057
|
+
const srcEf = srcNames ? asDict(srcDoc.cos.resolve(dictGet8(srcNames, "EmbeddedFiles"))) : void 0;
|
|
3674
4058
|
const flatPairs = [];
|
|
3675
4059
|
const collectSrcEf = (nodeDict, visited = /* @__PURE__ */ new Set()) => {
|
|
3676
4060
|
if (!nodeDict) return;
|
|
3677
|
-
const nArr = asArray(srcDoc.cos.resolve(
|
|
4061
|
+
const nArr = asArray(srcDoc.cos.resolve(dictGet8(nodeDict, "Names")));
|
|
3678
4062
|
if (nArr) flatPairs.push(...nArr.items);
|
|
3679
|
-
const kArr = asArray(srcDoc.cos.resolve(
|
|
4063
|
+
const kArr = asArray(srcDoc.cos.resolve(dictGet8(nodeDict, "Kids")));
|
|
3680
4064
|
if (kArr) {
|
|
3681
4065
|
for (const kid of kArr.items) {
|
|
3682
4066
|
if (kid.kind === "ref") {
|
|
@@ -3692,56 +4076,56 @@ Q
|
|
|
3692
4076
|
if (srcNamesArr) {
|
|
3693
4077
|
for (let idx = 0; idx + 1 < srcNamesArr.items.length; idx += 2) {
|
|
3694
4078
|
const kNode = srcDoc.cos.resolve(srcNamesArr.items[idx]);
|
|
3695
|
-
const origKey = kNode?.kind === "string" ?
|
|
4079
|
+
const origKey = kNode?.kind === "string" ? decodePdfString7(kNode) : `att_${idx}`;
|
|
3696
4080
|
const fsDict = asDict(srcDoc.cos.resolve(srcNamesArr.items[idx + 1]));
|
|
3697
4081
|
if (!fsDict) continue;
|
|
3698
|
-
const ufNode = srcDoc.cos.resolve(
|
|
3699
|
-
const fnStr = ufNode?.kind === "string" ?
|
|
3700
|
-
const efDict = asDict(srcDoc.cos.resolve(
|
|
4082
|
+
const ufNode = srcDoc.cos.resolve(dictGet8(fsDict, "UF") ?? dictGet8(fsDict, "F"));
|
|
4083
|
+
const fnStr = ufNode?.kind === "string" ? decodePdfString7(ufNode) : origKey;
|
|
4084
|
+
const efDict = asDict(srcDoc.cos.resolve(dictGet8(fsDict, "EF")));
|
|
3701
4085
|
const stNode = efDict ? srcDoc.cos.resolve(
|
|
3702
|
-
|
|
4086
|
+
dictGet8(efDict, "UF") ?? dictGet8(efDict, "F") ?? dictGet8(efDict, "DOS") ?? dictGet8(efDict, "Mac") ?? dictGet8(efDict, "Unix")
|
|
3703
4087
|
) : void 0;
|
|
3704
4088
|
if (stNode?.kind === "stream") {
|
|
3705
4089
|
const data = srcDoc.cos.decodeStream(stNode);
|
|
3706
4090
|
const newKey = `${copySpec.prefix}${origKey}`;
|
|
3707
4091
|
const rootDict = asDict(workingDoc.cos.resolve(workingDoc.cos.rootRef));
|
|
3708
4092
|
if (rootDict) {
|
|
3709
|
-
let namesDict = asDict(workingDoc.cos.resolve(
|
|
4093
|
+
let namesDict = asDict(workingDoc.cos.resolve(dictGet8(rootDict, "Names")));
|
|
3710
4094
|
if (!namesDict) {
|
|
3711
|
-
namesDict =
|
|
4095
|
+
namesDict = cosDict6({});
|
|
3712
4096
|
dictSet(rootDict, "Names", workingDoc.cos.allocateObject(namesDict));
|
|
3713
4097
|
}
|
|
3714
|
-
let efTree = asDict(workingDoc.cos.resolve(
|
|
4098
|
+
let efTree = asDict(workingDoc.cos.resolve(dictGet8(namesDict, "EmbeddedFiles")));
|
|
3715
4099
|
if (!efTree) {
|
|
3716
|
-
efTree =
|
|
4100
|
+
efTree = cosDict6({ Names: cosArray3([]) });
|
|
3717
4101
|
dictSet(namesDict, "EmbeddedFiles", workingDoc.cos.allocateObject(efTree));
|
|
3718
4102
|
}
|
|
3719
|
-
let namesArr = asArray(workingDoc.cos.resolve(
|
|
4103
|
+
let namesArr = asArray(workingDoc.cos.resolve(dictGet8(efTree, "Names")));
|
|
3720
4104
|
if (!namesArr) {
|
|
3721
4105
|
namesArr = cosArray3([]);
|
|
3722
4106
|
dictSet(efTree, "Names", namesArr);
|
|
3723
4107
|
}
|
|
3724
4108
|
for (let j = 0; j + 1 < namesArr.items.length; j += 2) {
|
|
3725
4109
|
const exNode = workingDoc.cos.resolve(namesArr.items[j]);
|
|
3726
|
-
if (exNode?.kind === "string" &&
|
|
4110
|
+
if (exNode?.kind === "string" && decodePdfString7(exNode) === newKey) {
|
|
3727
4111
|
return { exitCode: 2, stdout: "", stderr: `qpdf: duplicate attachment key ${newKey}
|
|
3728
4112
|
` };
|
|
3729
4113
|
}
|
|
3730
4114
|
}
|
|
3731
4115
|
const efStreamRef = workingDoc.cos.allocateObject(
|
|
3732
4116
|
cosStream(data, {
|
|
3733
|
-
dict:
|
|
4117
|
+
dict: cosDict6({
|
|
3734
4118
|
Type: cosName2("EmbeddedFile"),
|
|
3735
|
-
Params:
|
|
4119
|
+
Params: cosDict6({ Size: cosNumber4(data.byteLength) })
|
|
3736
4120
|
})
|
|
3737
4121
|
})
|
|
3738
4122
|
);
|
|
3739
4123
|
const fsRef = workingDoc.cos.allocateObject(
|
|
3740
|
-
|
|
4124
|
+
cosDict6({
|
|
3741
4125
|
Type: cosName2("Filespec"),
|
|
3742
4126
|
F: cosString3(fnStr),
|
|
3743
4127
|
UF: cosString3(fnStr),
|
|
3744
|
-
EF:
|
|
4128
|
+
EF: cosDict6({ F: efStreamRef, UF: efStreamRef })
|
|
3745
4129
|
})
|
|
3746
4130
|
);
|
|
3747
4131
|
namesArr.items.push(cosString3(newKey), fsRef);
|
|
@@ -3753,22 +4137,22 @@ Q
|
|
|
3753
4137
|
if (removeAttachmentKeys.length > 0 || addAttachmentSpecs.length > 0) {
|
|
3754
4138
|
const rootDict = asDict(workingDoc.cos.resolve(workingDoc.cos.rootRef));
|
|
3755
4139
|
if (rootDict) {
|
|
3756
|
-
let namesDict = asDict(workingDoc.cos.resolve(
|
|
4140
|
+
let namesDict = asDict(workingDoc.cos.resolve(dictGet8(rootDict, "Names")));
|
|
3757
4141
|
if (!namesDict) {
|
|
3758
|
-
namesDict =
|
|
4142
|
+
namesDict = cosDict6({});
|
|
3759
4143
|
dictSet(rootDict, "Names", workingDoc.cos.allocateObject(namesDict));
|
|
3760
4144
|
}
|
|
3761
|
-
let efTree = asDict(workingDoc.cos.resolve(
|
|
4145
|
+
let efTree = asDict(workingDoc.cos.resolve(dictGet8(namesDict, "EmbeddedFiles")));
|
|
3762
4146
|
if (!efTree) {
|
|
3763
|
-
efTree =
|
|
4147
|
+
efTree = cosDict6({ Names: cosArray3([]) });
|
|
3764
4148
|
dictSet(namesDict, "EmbeddedFiles", workingDoc.cos.allocateObject(efTree));
|
|
3765
4149
|
}
|
|
3766
4150
|
const flattenedEfPairs = [];
|
|
3767
4151
|
const flattenEfTree = (nodeDict, visited = /* @__PURE__ */ new Set()) => {
|
|
3768
4152
|
if (!nodeDict) return;
|
|
3769
|
-
const nArr = asArray(workingDoc.cos.resolve(
|
|
4153
|
+
const nArr = asArray(workingDoc.cos.resolve(dictGet8(nodeDict, "Names")));
|
|
3770
4154
|
if (nArr) flattenedEfPairs.push(...nArr.items);
|
|
3771
|
-
const kArr = asArray(workingDoc.cos.resolve(
|
|
4155
|
+
const kArr = asArray(workingDoc.cos.resolve(dictGet8(nodeDict, "Kids")));
|
|
3772
4156
|
if (kArr) {
|
|
3773
4157
|
for (const kid of kArr.items) {
|
|
3774
4158
|
if (kid.kind === "ref") {
|
|
@@ -3784,17 +4168,20 @@ Q
|
|
|
3784
4168
|
let namesArr = cosArray3(flattenedEfPairs);
|
|
3785
4169
|
dictSet(efTree, "Names", namesArr);
|
|
3786
4170
|
if (removeAttachmentKeys.length > 0) {
|
|
3787
|
-
const remSet = new Set(removeAttachmentKeys);
|
|
4171
|
+
const remSet = new Set(removeAttachmentKeys), missing = new Set(removeAttachmentKeys);
|
|
3788
4172
|
const kept = [];
|
|
3789
4173
|
for (let k = 0; k + 1 < namesArr.items.length; k += 2) {
|
|
3790
4174
|
const keyNode = workingDoc.cos.resolve(namesArr.items[k]);
|
|
3791
|
-
const keyStr = keyNode?.kind === "string" ?
|
|
4175
|
+
const keyStr = keyNode?.kind === "string" ? decodePdfString7(keyNode) : "";
|
|
4176
|
+
missing.delete(keyStr);
|
|
3792
4177
|
if (!remSet.has(keyStr)) {
|
|
3793
4178
|
kept.push(namesArr.items[k], namesArr.items[k + 1]);
|
|
3794
4179
|
}
|
|
3795
4180
|
}
|
|
4181
|
+
if (missing.size) return { exitCode: 2, stdout: "", stderr: `qpdf: attachment ${missing.values().next().value} not found
|
|
4182
|
+
` };
|
|
3796
4183
|
dictSet(efTree, "Names", cosArray3(kept));
|
|
3797
|
-
namesArr = asArray(
|
|
4184
|
+
namesArr = asArray(dictGet8(efTree, "Names"));
|
|
3798
4185
|
}
|
|
3799
4186
|
for (const addSpec of addAttachmentSpecs) {
|
|
3800
4187
|
yield;
|
|
@@ -3806,7 +4193,7 @@ Q
|
|
|
3806
4193
|
let existingIdx = -1;
|
|
3807
4194
|
for (let idx = 0; idx + 1 < namesArr.items.length; idx += 2) {
|
|
3808
4195
|
const kNode = workingDoc.cos.resolve(namesArr.items[idx]);
|
|
3809
|
-
if (kNode?.kind === "string" &&
|
|
4196
|
+
if (kNode?.kind === "string" && decodePdfString7(kNode) === addSpec.key) {
|
|
3810
4197
|
existingIdx = idx;
|
|
3811
4198
|
break;
|
|
3812
4199
|
}
|
|
@@ -3817,17 +4204,17 @@ Q
|
|
|
3817
4204
|
}
|
|
3818
4205
|
const efStreamRef = workingDoc.cos.allocateObject(
|
|
3819
4206
|
cosStream(attBytes, {
|
|
3820
|
-
dict:
|
|
4207
|
+
dict: cosDict6({
|
|
3821
4208
|
Type: cosName2("EmbeddedFile"),
|
|
3822
|
-
Params:
|
|
4209
|
+
Params: cosDict6({ Size: cosNumber4(attBytes.byteLength) })
|
|
3823
4210
|
})
|
|
3824
4211
|
})
|
|
3825
4212
|
);
|
|
3826
|
-
const fsDict =
|
|
4213
|
+
const fsDict = cosDict6({
|
|
3827
4214
|
Type: cosName2("Filespec"),
|
|
3828
4215
|
F: cosString3(addSpec.filename),
|
|
3829
4216
|
UF: cosString3(addSpec.filename),
|
|
3830
|
-
EF:
|
|
4217
|
+
EF: cosDict6({ F: efStreamRef, UF: efStreamRef })
|
|
3831
4218
|
});
|
|
3832
4219
|
if (addSpec.description) {
|
|
3833
4220
|
dictSet(fsDict, "Desc", cosString3(addSpec.description));
|
|
@@ -3878,16 +4265,7 @@ Q
|
|
|
3878
4265
|
});
|
|
3879
4266
|
if (encryptConfig && !decrypt) {
|
|
3880
4267
|
const parsedOut = yield* parseCosDocumentSteps(outBytes);
|
|
3881
|
-
outBytes = yield* encryptCosDocumentSteps(parsedOut,
|
|
3882
|
-
userPassword: encryptConfig.userPassword,
|
|
3883
|
-
ownerPassword: encryptConfig.ownerPassword,
|
|
3884
|
-
permissions: {
|
|
3885
|
-
print: encryptConfig.print,
|
|
3886
|
-
modify: encryptConfig.modify,
|
|
3887
|
-
copy: encryptConfig.copy,
|
|
3888
|
-
addNotes: encryptConfig.addNotes
|
|
3889
|
-
}
|
|
3890
|
-
});
|
|
4268
|
+
outBytes = yield* encryptCosDocumentSteps(parsedOut, encryptConfig);
|
|
3891
4269
|
} else if (decrypt && workingDoc.cos.encryption) {
|
|
3892
4270
|
workingDoc.cos.encryptRef = void 0;
|
|
3893
4271
|
workingDoc.cos.encryption = void 0;
|
|
@@ -3932,7 +4310,7 @@ async function qpdf(context, options = {}) {
|
|
|
3932
4310
|
const path = token.slice(1);
|
|
3933
4311
|
let source;
|
|
3934
4312
|
try {
|
|
3935
|
-
source = await
|
|
4313
|
+
source = await PdfFileSource4.open(context.fs, resolvePath2(context.cwd, path), { signal: invocation.signal, chunkBytes: 16384 });
|
|
3936
4314
|
} catch {
|
|
3937
4315
|
invocation.signal.throwIfAborted();
|
|
3938
4316
|
await writeBytes2(context.stderr, encoder.encode(`qpdf: cannot open ${path}
|
|
@@ -4103,6 +4481,11 @@ async function qpdf(context, options = {}) {
|
|
|
4103
4481
|
}
|
|
4104
4482
|
}
|
|
4105
4483
|
return { exitCode: res.exitCode };
|
|
4484
|
+
} catch (error) {
|
|
4485
|
+
if (!(error instanceof QpdfPageRangeError)) throw error;
|
|
4486
|
+
await writeBytes2(context.stderr, new TextEncoder().encode(`qpdf: ${error.message}
|
|
4487
|
+
`), invocation.signal);
|
|
4488
|
+
return { exitCode: 2 };
|
|
4106
4489
|
} finally {
|
|
4107
4490
|
await invocation.close();
|
|
4108
4491
|
}
|