@muhammara/native-core 7.0.0-beta.3 → 7.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -7
- package/THIRD_PARTY_NOTICES.md +39 -0
- package/index.js +120 -1
- package/lib/PDFRStreamForBuffer.js +52 -7
- package/lib/PDFRStreamForFile.js +56 -7
- package/lib/PDFStreamForResponse.js +22 -5
- package/lib/PDFWStreamForBuffer.js +49 -10
- package/lib/PDFWStreamForFile.js +26 -5
- package/lib/Recipe.js +86 -30
- package/lib/content-stream.js +503 -0
- package/lib/css-colors.js +149 -0
- package/lib/font-text.js +1461 -0
- package/lib/glyph-list.js +1127 -0
- package/lib/recipe/annotation.js +482 -105
- package/lib/recipe/appendPage.js +8 -1
- package/lib/recipe/colors.js +132 -35
- package/lib/recipe/coordinate.js +82 -16
- package/lib/recipe/encrypt.js +32 -13
- package/lib/recipe/font.js +70 -25
- package/lib/recipe/graphics-state.js +3 -2
- package/lib/recipe/htmlToTextObjects.js +154 -21
- package/lib/recipe/image.js +34 -9
- package/lib/recipe/info.js +93 -22
- package/lib/recipe/insertPage.js +12 -3
- package/lib/recipe/overlay.js +4 -2
- package/lib/recipe/page-references.js +436 -0
- package/lib/recipe/page.js +379 -275
- package/lib/recipe/parameters.js +11 -2
- package/lib/recipe/replaceText.js +464 -30
- package/lib/recipe/shapes.js +418 -45
- package/lib/recipe/split.js +6 -2
- package/lib/recipe/table.js +260 -173
- package/lib/recipe/text.helper.js +176 -10
- package/lib/recipe/text.js +420 -115
- package/lib/recipe/utils.js +66 -3
- package/lib/recipe/vector-line.js +101 -39
- package/lib/recipe/vector-polygon.js +18 -6
- package/lib/recipe/vector.helper.js +150 -13
- package/lib/recipe/vector.js +150 -53
- package/lib/recipe/xObjectForm.js +73 -10
- package/lib/recipe-constants.js +373 -0
- package/muhammara.d.ts +3487 -152
- package/package.json +1 -1
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
// Finds and prunes references to deleted pages in the structures deletePage()
|
|
2
|
+
// retains: the catalog, retained pages and every object reachable from them.
|
|
3
|
+
|
|
4
|
+
const REFERENCED_PAGE_ERROR =
|
|
5
|
+
"deletePage cannot remove a page referenced by retained document structures";
|
|
6
|
+
|
|
7
|
+
// Holders pruning cannot rewrite, named for the error message.
|
|
8
|
+
const UNSUPPORTED_HOLDERS = Object.freeze({
|
|
9
|
+
stream: "a stream dictionary",
|
|
10
|
+
pages: "the page tree",
|
|
11
|
+
pageLabels: "the page labels",
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @private
|
|
16
|
+
* @param {Object} value - A PDF object.
|
|
17
|
+
* @param {Set<number>} deletedPageIDs - Object IDs of the deleted pages.
|
|
18
|
+
* @returns {boolean} Whether the value is a reference to a deleted page.
|
|
19
|
+
*/
|
|
20
|
+
function isDeletedPageReference(value, deletedPageIDs) {
|
|
21
|
+
const reference = value?.toPDFIndirectObjectReference?.();
|
|
22
|
+
return Boolean(reference && deletedPageIDs.has(reference.getObjectID()));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @private
|
|
27
|
+
* @param {Object} value - A PDF object.
|
|
28
|
+
* @returns {Object|null} The value as an array, or null.
|
|
29
|
+
*/
|
|
30
|
+
function asArray(value) {
|
|
31
|
+
return (
|
|
32
|
+
value.toPDFArray?.() ||
|
|
33
|
+
(typeof value.toJSArray === "function" ? value : null)
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @private
|
|
39
|
+
* @param {Object} value - A PDF object.
|
|
40
|
+
* @returns {Object|null} The value as a dictionary, or null.
|
|
41
|
+
*/
|
|
42
|
+
function asDictionary(value) {
|
|
43
|
+
return (
|
|
44
|
+
value.toPDFDictionary?.() ||
|
|
45
|
+
(typeof value.toJSObject === "function" ? value : null)
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A pruned value is a reference to a deleted page, or a destination array
|
|
51
|
+
* whose page is a deleted page. Pruning replaces it with null.
|
|
52
|
+
* @private
|
|
53
|
+
* @param {Object} value - A PDF object.
|
|
54
|
+
* @param {Set<number>} deletedPageIDs - Object IDs of the deleted pages.
|
|
55
|
+
* @returns {boolean} Whether pruning drops the whole value.
|
|
56
|
+
*/
|
|
57
|
+
function isPrunedValue(value, deletedPageIDs) {
|
|
58
|
+
if (isDeletedPageReference(value, deletedPageIDs)) return true;
|
|
59
|
+
const array = value.toPDFIndirectObjectReference?.() ? null : asArray(value);
|
|
60
|
+
if (!array) return false;
|
|
61
|
+
const entries = array.toJSArray();
|
|
62
|
+
return (
|
|
63
|
+
entries.length > 0 && isDeletedPageReference(entries[0], deletedPageIDs)
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Walk the direct and indirect values reachable from retained structures and
|
|
69
|
+
* record which holder directly contains a deleted-page reference. A holder is
|
|
70
|
+
* the catalog entry, retained page or indirect object whose own value holds
|
|
71
|
+
* the reference.
|
|
72
|
+
* @private
|
|
73
|
+
* @param {Object} parser - The source PDF parser.
|
|
74
|
+
* @param {Object} [value] - The PDF object to scan.
|
|
75
|
+
* @param {Object} holder - The holder the value belongs to.
|
|
76
|
+
* @param {Object} scan - Shared scan state: deletedPageIDs, skippedObjectIDs,
|
|
77
|
+
* visited, prune and the holders found.
|
|
78
|
+
* @param {number} [depth=0] - The nesting depth, limited to 1000.
|
|
79
|
+
* @returns {void}
|
|
80
|
+
* @throws {Error} If a retained structure references a deleted page and
|
|
81
|
+
* pruning is off, or nesting is too deep.
|
|
82
|
+
*/
|
|
83
|
+
function scanValue(parser, value, holder, scan, depth = 0) {
|
|
84
|
+
if (!value) return;
|
|
85
|
+
if (depth > 1000) {
|
|
86
|
+
throw new Error("deletePage cannot validate deeply nested references");
|
|
87
|
+
}
|
|
88
|
+
const reference = value.toPDFIndirectObjectReference?.();
|
|
89
|
+
if (reference) {
|
|
90
|
+
const objectID = reference.getObjectID();
|
|
91
|
+
if (scan.deletedPageIDs.has(objectID)) {
|
|
92
|
+
if (!scan.prune) throw new Error(REFERENCED_PAGE_ERROR);
|
|
93
|
+
scan.holders.push(holder);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (scan.skippedObjectIDs.has(objectID) || scan.visited.has(objectID)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
scan.visited.add(objectID);
|
|
100
|
+
const object = parser.parseNewObject(objectID);
|
|
101
|
+
const stream = object?.toPDFStream?.();
|
|
102
|
+
scanValue(
|
|
103
|
+
parser,
|
|
104
|
+
stream ? stream.getDictionary() : object,
|
|
105
|
+
stream
|
|
106
|
+
? { kind: "stream", objectID }
|
|
107
|
+
: { kind: "object", objectID, generation: reference.getVersion() },
|
|
108
|
+
scan,
|
|
109
|
+
depth + 1,
|
|
110
|
+
);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const array = asArray(value);
|
|
114
|
+
if (array) {
|
|
115
|
+
array
|
|
116
|
+
.toJSArray()
|
|
117
|
+
.forEach((entry) => scanValue(parser, entry, holder, scan, depth + 1));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const dictionary = asDictionary(value);
|
|
121
|
+
if (dictionary) {
|
|
122
|
+
Object.values(dictionary.toJSObject()).forEach((entry) =>
|
|
123
|
+
scanValue(parser, entry, holder, scan, depth + 1),
|
|
124
|
+
);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const stream = value.toPDFStream?.();
|
|
128
|
+
if (stream) {
|
|
129
|
+
scanValue(
|
|
130
|
+
parser,
|
|
131
|
+
stream.getDictionary(),
|
|
132
|
+
{ kind: "stream" },
|
|
133
|
+
scan,
|
|
134
|
+
depth + 1,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Find the retained structures that reference deleted pages. Without
|
|
141
|
+
* pruning, the first reference throws. With pruning, the result lists the
|
|
142
|
+
* objects and catalog entries to rewrite.
|
|
143
|
+
* @private
|
|
144
|
+
* @param {Object} parser - The source PDF parser.
|
|
145
|
+
* @param {Object} deletion - The deletion: catalog entries, rootID, the page
|
|
146
|
+
* tree built by readPageTree(), prepared pageLabels, deletedPageIDs,
|
|
147
|
+
* modifiedPageIDs and sourcePageCount.
|
|
148
|
+
* @param {boolean} prune - Whether to plan pruning instead of throwing.
|
|
149
|
+
* @returns {{objects: Map<number, number>, catalogKeys: string[]}} The
|
|
150
|
+
* object IDs to rewrite with their generations, and the catalog keys to
|
|
151
|
+
* replace.
|
|
152
|
+
* @throws {Error} If a reference cannot be pruned, or pruning is off and a
|
|
153
|
+
* retained structure references a deleted page.
|
|
154
|
+
*/
|
|
155
|
+
function planDeletedPageReferences(parser, deletion, prune) {
|
|
156
|
+
const scan = {
|
|
157
|
+
deletedPageIDs: deletion.deletedPageIDs,
|
|
158
|
+
skippedObjectIDs: new Set([deletion.rootID]),
|
|
159
|
+
visited: new Set(),
|
|
160
|
+
prune,
|
|
161
|
+
holders: [],
|
|
162
|
+
};
|
|
163
|
+
const pagesNodeIDs = new Set();
|
|
164
|
+
const pending = [deletion.tree];
|
|
165
|
+
while (pending.length) {
|
|
166
|
+
const node = pending.pop();
|
|
167
|
+
if (node.children) {
|
|
168
|
+
pagesNodeIDs.add(node.objectID);
|
|
169
|
+
pending.push(...node.children);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
pagesNodeIDs.forEach((objectID) => scan.skippedObjectIDs.add(objectID));
|
|
173
|
+
for (let pageIndex = 0; pageIndex < deletion.sourcePageCount; pageIndex++) {
|
|
174
|
+
scan.skippedObjectIDs.add(parser.getPageObjectID(pageIndex));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
Object.entries(deletion.catalog)
|
|
178
|
+
.filter(([key]) => !["PageLabels", "Pages"].includes(key))
|
|
179
|
+
.forEach(([key, value]) =>
|
|
180
|
+
scanValue(parser, value, { kind: "catalog", key }, scan),
|
|
181
|
+
);
|
|
182
|
+
if (deletion.pageLabels) {
|
|
183
|
+
Object.entries(deletion.pageLabels.values)
|
|
184
|
+
.filter(([key]) => !["Kids", "Limits", "Nums"].includes(key))
|
|
185
|
+
.forEach(([, value]) =>
|
|
186
|
+
scanValue(parser, value, { kind: "pageLabels" }, scan),
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
const retained = [deletion.tree];
|
|
190
|
+
while (retained.length) {
|
|
191
|
+
const node = retained.pop();
|
|
192
|
+
const isLeaf = !node.children;
|
|
193
|
+
const skipKeys = isLeaf ? ["Parent"] : ["Count", "Kids", "Parent"];
|
|
194
|
+
const holder = isLeaf
|
|
195
|
+
? { kind: "page", objectID: node.objectID, generation: node.generation }
|
|
196
|
+
: { kind: "pages" };
|
|
197
|
+
Object.entries(node.values)
|
|
198
|
+
.filter(([key]) => !skipKeys.includes(key))
|
|
199
|
+
.forEach(([, value]) => scanValue(parser, value, holder, scan));
|
|
200
|
+
if (node.children) retained.push(...node.children);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const objects = new Map();
|
|
204
|
+
const catalogKeys = [];
|
|
205
|
+
scan.holders.forEach((holder) => {
|
|
206
|
+
if (holder.kind === "catalog") {
|
|
207
|
+
if (!catalogKeys.includes(holder.key)) catalogKeys.push(holder.key);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (
|
|
211
|
+
holder.kind === "page" &&
|
|
212
|
+
deletion.modifiedPageIDs.has(holder.objectID)
|
|
213
|
+
) {
|
|
214
|
+
throw new Error(
|
|
215
|
+
"deletePage cannot prune references held by a page edited in this Recipe",
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
if (holder.kind !== "object" && holder.kind !== "page") {
|
|
219
|
+
throw new Error(
|
|
220
|
+
`deletePage cannot prune references held by ${UNSUPPORTED_HOLDERS[holder.kind]}`,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
if (holder.generation !== 0) {
|
|
224
|
+
throw new Error(
|
|
225
|
+
"deletePage does not support rewriting nonzero-generation objects",
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
objects.set(holder.objectID, holder.generation);
|
|
229
|
+
});
|
|
230
|
+
return { objects, catalogKeys };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* @private
|
|
235
|
+
* @param {Object} value - A direct PDF object.
|
|
236
|
+
* @param {Set<number>} deletedPageIDs - Object IDs of the deleted pages.
|
|
237
|
+
* @returns {boolean} Whether the value directly contains a pruned value.
|
|
238
|
+
*/
|
|
239
|
+
function containsPrunedValue(value, deletedPageIDs) {
|
|
240
|
+
if (isPrunedValue(value, deletedPageIDs)) return true;
|
|
241
|
+
if (value.toPDFIndirectObjectReference?.()) return false;
|
|
242
|
+
const array = asArray(value);
|
|
243
|
+
if (array) {
|
|
244
|
+
return array
|
|
245
|
+
.toJSArray()
|
|
246
|
+
.some((entry) => containsPrunedValue(entry, deletedPageIDs));
|
|
247
|
+
}
|
|
248
|
+
const dictionary = asDictionary(value);
|
|
249
|
+
if (dictionary) {
|
|
250
|
+
return Object.values(dictionary.toJSObject()).some((entry) =>
|
|
251
|
+
containsPrunedValue(entry, deletedPageIDs),
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Plan how to write a direct value with every pruned value removed:
|
|
259
|
+
* dictionary entries are dropped and array elements become null. A changed
|
|
260
|
+
* dictionary nested inside the value is written right away as its own
|
|
261
|
+
* indirect object and referenced from the plan, so writing the value never
|
|
262
|
+
* nests dictionaries, which the Wasm objects context does not support.
|
|
263
|
+
* @private
|
|
264
|
+
* @param {Object} objectsContext - The writer's objects context.
|
|
265
|
+
* @param {Object} copyingContext - Copies unchanged values.
|
|
266
|
+
* @param {Object} value - A direct PDF object that is not itself pruned.
|
|
267
|
+
* @param {Set<number>} deletedPageIDs - Object IDs of the deleted pages.
|
|
268
|
+
* @param {boolean} [nested=false] - Whether the value is inside another.
|
|
269
|
+
* @returns {Object} The write plan: copy, null, ref, array or dict.
|
|
270
|
+
*/
|
|
271
|
+
function planPrunedValue(
|
|
272
|
+
objectsContext,
|
|
273
|
+
copyingContext,
|
|
274
|
+
value,
|
|
275
|
+
deletedPageIDs,
|
|
276
|
+
nested = false,
|
|
277
|
+
) {
|
|
278
|
+
if (isPrunedValue(value, deletedPageIDs)) return { kind: "null" };
|
|
279
|
+
if (!containsPrunedValue(value, deletedPageIDs)) {
|
|
280
|
+
return { kind: "copy", value };
|
|
281
|
+
}
|
|
282
|
+
const array = asArray(value);
|
|
283
|
+
if (array) {
|
|
284
|
+
return {
|
|
285
|
+
kind: "array",
|
|
286
|
+
items: array
|
|
287
|
+
.toJSArray()
|
|
288
|
+
.map((entry) =>
|
|
289
|
+
planPrunedValue(
|
|
290
|
+
objectsContext,
|
|
291
|
+
copyingContext,
|
|
292
|
+
entry,
|
|
293
|
+
deletedPageIDs,
|
|
294
|
+
true,
|
|
295
|
+
),
|
|
296
|
+
),
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
const entries = Object.entries(asDictionary(value).toJSObject())
|
|
300
|
+
.filter(([, entry]) => !isPrunedValue(entry, deletedPageIDs))
|
|
301
|
+
.map(([key, entry]) => [
|
|
302
|
+
key,
|
|
303
|
+
planPrunedValue(
|
|
304
|
+
objectsContext,
|
|
305
|
+
copyingContext,
|
|
306
|
+
entry,
|
|
307
|
+
deletedPageIDs,
|
|
308
|
+
true,
|
|
309
|
+
),
|
|
310
|
+
]);
|
|
311
|
+
const plan = { kind: "dict", entries };
|
|
312
|
+
if (!nested) return plan;
|
|
313
|
+
const objectID = objectsContext.startNewIndirectObject();
|
|
314
|
+
writePrunedPlan(objectsContext, copyingContext, plan);
|
|
315
|
+
objectsContext.endIndirectObject();
|
|
316
|
+
return { kind: "ref", objectID };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Write a plan built by planPrunedValue().
|
|
321
|
+
* @private
|
|
322
|
+
* @param {Object} objectsContext - The writer's objects context.
|
|
323
|
+
* @param {Object} copyingContext - Copies unchanged values.
|
|
324
|
+
* @param {Object} plan - The write plan.
|
|
325
|
+
* @returns {void}
|
|
326
|
+
*/
|
|
327
|
+
function writePrunedPlan(objectsContext, copyingContext, plan) {
|
|
328
|
+
if (plan.kind === "copy") {
|
|
329
|
+
copyingContext.copyDirectObjectAsIs(plan.value);
|
|
330
|
+
} else if (plan.kind === "null") {
|
|
331
|
+
objectsContext.writeKeyword("null");
|
|
332
|
+
} else if (plan.kind === "ref") {
|
|
333
|
+
objectsContext.writeIndirectObjectReference(plan.objectID);
|
|
334
|
+
} else if (plan.kind === "array") {
|
|
335
|
+
objectsContext.startArray();
|
|
336
|
+
plan.items.forEach((item) =>
|
|
337
|
+
writePrunedPlan(objectsContext, copyingContext, item),
|
|
338
|
+
);
|
|
339
|
+
objectsContext.endArray();
|
|
340
|
+
} else {
|
|
341
|
+
const dictionary = objectsContext.startDictionary();
|
|
342
|
+
plan.entries.forEach(([key, item]) => {
|
|
343
|
+
dictionary.writeKey(key);
|
|
344
|
+
writePrunedPlan(objectsContext, copyingContext, item);
|
|
345
|
+
});
|
|
346
|
+
objectsContext.endDictionary(dictionary);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Replace a catalog entry when the catalog is written. The native writer
|
|
352
|
+
* rewrites the catalog through its OnCatalogWrite event; the Wasm modifier
|
|
353
|
+
* through _setCatalogEntry().
|
|
354
|
+
* @private
|
|
355
|
+
* @param {Object} writer - The PDF writer.
|
|
356
|
+
* @param {string} key - The catalog key.
|
|
357
|
+
* @param {number} objectID - The new value's object ID, or 0 for null.
|
|
358
|
+
* @returns {void}
|
|
359
|
+
*/
|
|
360
|
+
function setCatalogEntry(writer, key, objectID) {
|
|
361
|
+
if (writer._setCatalogEntry) {
|
|
362
|
+
writer._setCatalogEntry(key, objectID);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (!writer._catalogEntries) {
|
|
366
|
+
const entries = new Map();
|
|
367
|
+
writer._catalogEntries = entries;
|
|
368
|
+
writer.requireCatalogUpdate();
|
|
369
|
+
writer.getEvents().on("OnCatalogWrite", (event) => {
|
|
370
|
+
const catalog = event.catalogDictionaryContext;
|
|
371
|
+
entries.forEach((id, name) => {
|
|
372
|
+
catalog.writeKey(name);
|
|
373
|
+
if (id) catalog.writeObjectReferenceValue(id);
|
|
374
|
+
else writer.getObjectsContext().writeKeyword("null");
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
writer._catalogEntries.set(key, objectID);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Rewrite the planned objects and catalog entries without their references
|
|
383
|
+
* to deleted pages.
|
|
384
|
+
* @private
|
|
385
|
+
* @param {Object} writer - The PDF writer.
|
|
386
|
+
* @param {Object} copyingContext - Copies unchanged values.
|
|
387
|
+
* @param {Object} catalog - The catalog entries.
|
|
388
|
+
* @param {{objects: Map<number, number>, catalogKeys: string[]}} plan - The
|
|
389
|
+
* result of planDeletedPageReferences().
|
|
390
|
+
* @param {Set<number>} deletedPageIDs - Object IDs of the deleted pages.
|
|
391
|
+
* @returns {void}
|
|
392
|
+
*/
|
|
393
|
+
function pruneDeletedPageReferences(
|
|
394
|
+
writer,
|
|
395
|
+
copyingContext,
|
|
396
|
+
catalog,
|
|
397
|
+
plan,
|
|
398
|
+
deletedPageIDs,
|
|
399
|
+
) {
|
|
400
|
+
const objectsContext = writer.getObjectsContext();
|
|
401
|
+
const parser = copyingContext.getSourceDocumentParser();
|
|
402
|
+
plan.objects.forEach((generation, objectID) => {
|
|
403
|
+
const plan = planPrunedValue(
|
|
404
|
+
objectsContext,
|
|
405
|
+
copyingContext,
|
|
406
|
+
parser.parseNewObject(objectID),
|
|
407
|
+
deletedPageIDs,
|
|
408
|
+
);
|
|
409
|
+
objectsContext.startModifiedIndirectObject(objectID);
|
|
410
|
+
writePrunedPlan(objectsContext, copyingContext, plan);
|
|
411
|
+
objectsContext.endIndirectObject();
|
|
412
|
+
});
|
|
413
|
+
plan.catalogKeys.forEach((key) => {
|
|
414
|
+
const value = catalog[key];
|
|
415
|
+
if (isPrunedValue(value, deletedPageIDs)) {
|
|
416
|
+
setCatalogEntry(writer, key, 0);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
const plan = planPrunedValue(
|
|
420
|
+
objectsContext,
|
|
421
|
+
copyingContext,
|
|
422
|
+
value,
|
|
423
|
+
deletedPageIDs,
|
|
424
|
+
);
|
|
425
|
+
const objectID = objectsContext.startNewIndirectObject();
|
|
426
|
+
writePrunedPlan(objectsContext, copyingContext, plan);
|
|
427
|
+
objectsContext.endIndirectObject();
|
|
428
|
+
setCatalogEntry(writer, key, objectID);
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
module.exports = {
|
|
433
|
+
planDeletedPageReferences,
|
|
434
|
+
pruneDeletedPageReferences,
|
|
435
|
+
setCatalogEntry,
|
|
436
|
+
};
|