pdf-lite 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EXAMPLES.md +51 -70
- package/README.md +1 -1
- package/dist/acroform/appearance/index.d.ts +4 -4
- package/dist/acroform/appearance/index.js +4 -4
- package/dist/acroform/appearance/{PdfAppearanceStream.d.ts → pdf-appearance-stream.d.ts} +9 -3
- package/dist/acroform/appearance/{PdfAppearanceStream.js → pdf-appearance-stream.js} +14 -5
- package/dist/acroform/appearance/{PdfButtonAppearanceStream.d.ts → pdf-button-appearance-stream.d.ts} +3 -2
- package/dist/acroform/appearance/pdf-button-appearance-stream.js +58 -0
- package/dist/acroform/appearance/pdf-choice-appearance-stream.d.ts +22 -0
- package/dist/acroform/appearance/pdf-choice-appearance-stream.js +75 -0
- package/dist/acroform/appearance/pdf-graphics.d.ts +51 -0
- package/dist/acroform/appearance/pdf-graphics.js +239 -0
- package/dist/acroform/appearance/{PdfTextAppearanceStream.d.ts → pdf-text-appearance-stream.d.ts} +7 -2
- package/dist/acroform/appearance/pdf-text-appearance-stream.js +104 -0
- package/dist/acroform/fields/index.d.ts +7 -7
- package/dist/acroform/fields/index.js +7 -7
- package/dist/acroform/fields/pdf-button-form-field.d.ts +14 -0
- package/dist/acroform/fields/pdf-button-form-field.js +70 -0
- package/dist/acroform/fields/pdf-choice-form-field.d.ts +19 -0
- package/dist/acroform/fields/pdf-choice-form-field.js +112 -0
- package/dist/acroform/fields/{PdfFormFieldFlags.d.ts → pdf-form-field-flags.d.ts} +5 -6
- package/dist/acroform/fields/{PdfFormFieldFlags.js → pdf-form-field-flags.js} +12 -18
- package/dist/acroform/fields/{PdfFormField.d.ts → pdf-form-field.d.ts} +37 -38
- package/dist/acroform/fields/pdf-form-field.js +519 -0
- package/dist/acroform/fields/{PdfSignatureFormField.d.ts → pdf-signature-form-field.d.ts} +1 -1
- package/dist/acroform/fields/{PdfSignatureFormField.js → pdf-signature-form-field.js} +1 -1
- package/dist/acroform/fields/{PdfTextFormField.d.ts → pdf-text-form-field.d.ts} +1 -1
- package/dist/acroform/fields/{PdfTextFormField.js → pdf-text-form-field.js} +11 -13
- package/dist/acroform/fields/types.d.ts +6 -1
- package/dist/acroform/index.d.ts +1 -3
- package/dist/acroform/index.js +1 -3
- package/dist/acroform/pdf-acro-form.d.ts +45 -0
- package/dist/acroform/pdf-acro-form.js +203 -0
- package/dist/acroform/xfa/index.d.ts +3 -3
- package/dist/acroform/xfa/index.js +2 -2
- package/dist/acroform/xfa/{PdfXfaData.d.ts → pdf-xfa-data.d.ts} +4 -3
- package/dist/acroform/xfa/{PdfXfaData.js → pdf-xfa-data.js} +16 -12
- package/dist/acroform/xfa/pdf-xfa-form.d.ts +16 -0
- package/dist/acroform/xfa/pdf-xfa-form.js +34 -0
- package/dist/annotations/index.d.ts +3 -4
- package/dist/annotations/index.js +3 -4
- package/dist/annotations/{PdfAnnotationFlags.d.ts → pdf-annotation-flags.d.ts} +3 -4
- package/dist/annotations/{PdfAnnotationFlags.js → pdf-annotation-flags.js} +5 -6
- package/dist/annotations/{PdfAnnotation.d.ts → pdf-annotation.d.ts} +31 -5
- package/dist/annotations/{PdfAnnotation.js → pdf-annotation.js} +31 -19
- package/dist/annotations/pdf-default-resources.d.ts +11 -0
- package/dist/annotations/pdf-default-resources.js +3 -0
- package/dist/annotations/{PdfWidgetAnnotation.d.ts → pdf-widget-annotation.d.ts} +1 -1
- package/dist/annotations/{PdfWidgetAnnotation.js → pdf-widget-annotation.js} +1 -1
- package/dist/core/decoder.js +1 -1
- package/dist/core/objects/pdf-array.d.ts +8 -1
- package/dist/core/objects/pdf-array.js +31 -0
- package/dist/core/objects/pdf-dictionary.d.ts +2 -0
- package/dist/core/objects/pdf-dictionary.js +14 -7
- package/dist/core/objects/pdf-hexadecimal.d.ts +1 -0
- package/dist/core/objects/pdf-hexadecimal.js +3 -3
- package/dist/core/objects/pdf-indirect-object.d.ts +18 -9
- package/dist/core/objects/pdf-indirect-object.js +75 -16
- package/dist/core/objects/pdf-number.d.ts +1 -0
- package/dist/core/objects/pdf-number.js +5 -4
- package/dist/core/objects/pdf-object-reference.d.ts +8 -1
- package/dist/core/objects/pdf-object-reference.js +14 -0
- package/dist/core/objects/pdf-object.d.ts +14 -0
- package/dist/core/objects/pdf-object.js +36 -0
- package/dist/core/objects/pdf-start-xref.d.ts +1 -0
- package/dist/core/objects/pdf-start-xref.js +4 -0
- package/dist/core/objects/pdf-stream.d.ts +44 -7
- package/dist/core/objects/pdf-stream.js +284 -26
- package/dist/core/objects/pdf-string.d.ts +1 -0
- package/dist/core/objects/pdf-string.js +3 -6
- package/dist/core/objects/pdf-trailer.d.ts +1 -0
- package/dist/core/objects/pdf-trailer.js +6 -3
- package/dist/core/objects/pdf-xref-table.js +1 -1
- package/dist/core/parser/incremental-parser.d.ts +0 -13
- package/dist/core/parser/incremental-parser.js +1 -18
- package/dist/core/ref.d.ts +3 -1
- package/dist/core/ref.js +8 -5
- package/dist/core/streams/object-stream.d.ts +1 -1
- package/dist/core/streams/object-stream.js +1 -1
- package/dist/core/tokens/token.d.ts +2 -1
- package/dist/core/tokens/token.js +3 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +24 -0
- package/dist/fonts/index.d.ts +0 -1
- package/dist/fonts/index.js +0 -1
- package/dist/fonts/pdf-font.d.ts +94 -32
- package/dist/fonts/pdf-font.js +301 -83
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/pdf/index.d.ts +2 -1
- package/dist/pdf/index.js +2 -1
- package/dist/pdf/pdf-document.d.ts +61 -36
- package/dist/pdf/pdf-document.js +315 -117
- package/dist/pdf/pdf-page.d.ts +50 -0
- package/dist/pdf/pdf-page.js +144 -0
- package/dist/pdf/pdf-pages.d.ts +28 -0
- package/dist/pdf/pdf-pages.js +94 -0
- package/dist/pdf/pdf-reader.d.ts +5 -1
- package/dist/pdf/pdf-reader.js +36 -2
- package/dist/pdf/pdf-revision.d.ts +3 -3
- package/dist/pdf/pdf-revision.js +7 -7
- package/dist/pdf/pdf-xref-lookup.js +34 -14
- package/dist/signing/document-security-store.d.ts +14 -17
- package/dist/signing/document-security-store.js +19 -34
- package/dist/signing/signer.d.ts +23 -8
- package/dist/signing/signer.js +51 -17
- package/dist/utils/encodePdfText.d.ts +17 -0
- package/dist/utils/encodePdfText.js +61 -0
- package/dist/utils/index.d.ts +1 -2
- package/dist/utils/index.js +1 -2
- package/dist/utils/needsCentralWhitespace.d.ts +10 -0
- package/dist/utils/needsCentralWhitespace.js +34 -0
- package/package.json +3 -3
- package/dist/acroform/PdfAcroForm.d.ts +0 -63
- package/dist/acroform/PdfAcroForm.js +0 -279
- package/dist/acroform/PdfFontEncodingCache.d.ts +0 -16
- package/dist/acroform/PdfFontEncodingCache.js +0 -75
- package/dist/acroform/acroform.d.ts +0 -9
- package/dist/acroform/acroform.js +0 -7
- package/dist/acroform/appearance/PdfButtonAppearanceStream.js +0 -54
- package/dist/acroform/appearance/PdfChoiceAppearanceStream.d.ts +0 -15
- package/dist/acroform/appearance/PdfChoiceAppearanceStream.js +0 -48
- package/dist/acroform/appearance/PdfTextAppearanceStream.js +0 -75
- package/dist/acroform/fields/PdfButtonFormField.d.ts +0 -9
- package/dist/acroform/fields/PdfButtonFormField.js +0 -35
- package/dist/acroform/fields/PdfChoiceFormField.d.ts +0 -9
- package/dist/acroform/fields/PdfChoiceFormField.js +0 -46
- package/dist/acroform/fields/PdfFormField.js +0 -499
- package/dist/acroform/manager.d.ts +0 -33
- package/dist/acroform/manager.js +0 -51
- package/dist/acroform/xfa/PdfXfaForm.d.ts +0 -12
- package/dist/acroform/xfa/PdfXfaForm.js +0 -64
- package/dist/annotations/PdfAnnotationWriter.d.ts +0 -20
- package/dist/annotations/PdfAnnotationWriter.js +0 -76
- package/dist/fonts/font-manager.d.ts +0 -127
- package/dist/fonts/font-manager.js +0 -378
- package/dist/pdf/errors.d.ts +0 -6
- package/dist/pdf/errors.js +0 -6
- package/dist/utils/predictors.d.ts +0 -113
- package/dist/utils/predictors.js +0 -279
- /package/dist/acroform/fields/{PdfDefaultAppearance.d.ts → pdf-default-appearance.d.ts} +0 -0
- /package/dist/acroform/fields/{PdfDefaultAppearance.js → pdf-default-appearance.js} +0 -0
- /package/dist/utils/{IterableReadableStream.d.ts → iterable-readable-stream.d.ts} +0 -0
- /package/dist/utils/{IterableReadableStream.js → iterable-readable-stream.js} +0 -0
package/dist/pdf/pdf-document.js
CHANGED
|
@@ -5,8 +5,9 @@ import { PdfIndirectObject } from '../core/objects/pdf-indirect-object.js';
|
|
|
5
5
|
import { PdfComment } from '../core/objects/pdf-comment.js';
|
|
6
6
|
import { PdfWhitespaceToken } from '../core/tokens/whitespace-token.js';
|
|
7
7
|
import { PdfObjStream, PdfStream, PdfXRefStreamCompressedEntry, } from '../core/objects/pdf-stream.js';
|
|
8
|
+
import { PdfArray } from '../core/objects/pdf-array.js';
|
|
8
9
|
import { PdfDictionary } from '../core/objects/pdf-dictionary.js';
|
|
9
|
-
import { PdfObjectReference } from '../core/objects/pdf-object-reference.js';
|
|
10
|
+
import { PdfObjectReference, } from '../core/objects/pdf-object-reference.js';
|
|
10
11
|
import { PdfTokenSerializer } from '../core/serializer.js';
|
|
11
12
|
import { PdfRevision } from './pdf-revision.js';
|
|
12
13
|
import { PdfV5SecurityHandler } from '../security/handlers/v5.js';
|
|
@@ -14,12 +15,12 @@ import { PdfByteOffsetToken } from '../core/tokens/byte-offset-token.js';
|
|
|
14
15
|
import { PdfNumberToken } from '../core/tokens/number-token.js';
|
|
15
16
|
import { PdfXRefTableEntryToken } from '../core/tokens/xref-table-entry-token.js';
|
|
16
17
|
import { PdfStartXRef } from '../core/objects/pdf-start-xref.js';
|
|
17
|
-
import { FoundCompressedObjectError } from '
|
|
18
|
+
import { FoundCompressedObjectError } from '../errors.js';
|
|
18
19
|
import { PdfReader } from './pdf-reader.js';
|
|
19
20
|
import { PdfSigner } from '../signing/signer.js';
|
|
20
|
-
import { PdfAcroFormManager } from '../acroform/manager.js';
|
|
21
|
-
import { PdfFontManager } from '../fonts/font-manager.js';
|
|
22
21
|
import { concatUint8Arrays } from '../utils/concatUint8Arrays.js';
|
|
22
|
+
import { PdfAcroForm } from '../acroform/pdf-acro-form.js';
|
|
23
|
+
import { PdfPages } from './pdf-pages.js';
|
|
23
24
|
/**
|
|
24
25
|
* Represents a PDF document with support for reading, writing, and modifying PDF files.
|
|
25
26
|
* Handles document structure, revisions, encryption, and digital signatures.
|
|
@@ -44,11 +45,14 @@ export class PdfDocument extends PdfObject {
|
|
|
44
45
|
signer;
|
|
45
46
|
/** Security handler for encryption/decryption operations */
|
|
46
47
|
securityHandler;
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
/** Whether the document is currently in incremental mode (appending changes as a new revision) */
|
|
49
|
+
incremental = false;
|
|
50
|
+
originalSecurityHandler;
|
|
50
51
|
hasEncryptionDictionary = false;
|
|
51
|
-
|
|
52
|
+
_resolvedCache = new Map();
|
|
53
|
+
_committing = false;
|
|
54
|
+
_finalized = false;
|
|
55
|
+
_signed = false;
|
|
52
56
|
/**
|
|
53
57
|
* Creates a new PDF document instance.
|
|
54
58
|
*
|
|
@@ -75,13 +79,48 @@ export class PdfDocument extends PdfObject {
|
|
|
75
79
|
if (options?.ownerPassword) {
|
|
76
80
|
this.setOwnerPassword(options.ownerPassword);
|
|
77
81
|
}
|
|
78
|
-
this.signer = options?.signer ?? new PdfSigner();
|
|
82
|
+
this.signer = options?.signer ?? new PdfSigner({ document: this });
|
|
79
83
|
this.linkRevisions();
|
|
84
|
+
this.wireResolvers(...this.objects.filter((x) => x instanceof PdfIndirectObject), ...this.revisions.map((rev) => rev.xref.trailerDict));
|
|
80
85
|
this.calculateOffsets();
|
|
81
|
-
this.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
this.originalSecurityHandler = options?.securityHandler;
|
|
87
|
+
this.resetSecurityHandler();
|
|
88
|
+
}
|
|
89
|
+
resolve(objectNumber, generationNumber) {
|
|
90
|
+
const cacheKey = `${objectNumber} ${generationNumber}`;
|
|
91
|
+
const cached = this._resolvedCache.get(cacheKey);
|
|
92
|
+
if (cached)
|
|
93
|
+
return cached;
|
|
94
|
+
const found = this.readObject({ objectNumber, generationNumber });
|
|
95
|
+
if (!found) {
|
|
96
|
+
throw new Error(`Object ${objectNumber} ${generationNumber} not found`);
|
|
97
|
+
}
|
|
98
|
+
this.wireResolvers(found);
|
|
99
|
+
this._resolvedCache.set(cacheKey, found);
|
|
100
|
+
return found;
|
|
101
|
+
}
|
|
102
|
+
get acroform() {
|
|
103
|
+
const root = this.root;
|
|
104
|
+
const acroFormEntry = root.content.get('AcroForm');
|
|
105
|
+
if (!acroFormEntry)
|
|
106
|
+
return null;
|
|
107
|
+
const acroFormRef = acroFormEntry.as(PdfObjectReference)?.resolve();
|
|
108
|
+
if (!acroFormRef)
|
|
109
|
+
return null;
|
|
110
|
+
return acroFormRef.becomes(PdfAcroForm);
|
|
111
|
+
}
|
|
112
|
+
get pages() {
|
|
113
|
+
const root = this.root;
|
|
114
|
+
const pagesEntry = root.content.get('Pages');
|
|
115
|
+
if (pagesEntry) {
|
|
116
|
+
const pagesRef = pagesEntry.as(PdfObjectReference)?.resolve();
|
|
117
|
+
if (pagesRef)
|
|
118
|
+
return pagesRef.becomes(PdfPages);
|
|
119
|
+
}
|
|
120
|
+
const pages = new PdfPages();
|
|
121
|
+
this.add(pages);
|
|
122
|
+
root.content.set('Pages', pages.reference);
|
|
123
|
+
return pages;
|
|
85
124
|
}
|
|
86
125
|
get header() {
|
|
87
126
|
return this.revisions[0].header;
|
|
@@ -132,6 +171,9 @@ export class PdfDocument extends PdfObject {
|
|
|
132
171
|
}
|
|
133
172
|
return this;
|
|
134
173
|
}
|
|
174
|
+
hasObjectInLatestRevision(obj) {
|
|
175
|
+
return this.latestRevision.objects.includes(obj);
|
|
176
|
+
}
|
|
135
177
|
/**
|
|
136
178
|
* Adds objects to the document's latest revision.
|
|
137
179
|
* Automatically starts a new revision if the current one is locked.
|
|
@@ -143,12 +185,21 @@ export class PdfDocument extends PdfObject {
|
|
|
143
185
|
this.startNewRevision();
|
|
144
186
|
}
|
|
145
187
|
for (const obj of objects) {
|
|
146
|
-
|
|
147
|
-
|
|
188
|
+
this.wireResolvers(obj);
|
|
189
|
+
if (this.hasObjectInLatestRevision(obj)) {
|
|
190
|
+
continue;
|
|
148
191
|
}
|
|
149
|
-
this.toBeCommitted.push(obj);
|
|
150
192
|
this.latestRevision.addObject(obj);
|
|
151
193
|
}
|
|
194
|
+
// Auto-add any referenced-but-missing objects
|
|
195
|
+
const missing = this.collectMissingReferences(...objects);
|
|
196
|
+
if (missing.length > 0) {
|
|
197
|
+
this.add(...missing);
|
|
198
|
+
}
|
|
199
|
+
this.update();
|
|
200
|
+
for (const obj of objects) {
|
|
201
|
+
obj.setModified(false);
|
|
202
|
+
}
|
|
152
203
|
}
|
|
153
204
|
/**
|
|
154
205
|
* Packs non-stream indirect objects into compressed ObjStm containers.
|
|
@@ -290,6 +341,10 @@ export class PdfDocument extends PdfObject {
|
|
|
290
341
|
}
|
|
291
342
|
return metadataRef;
|
|
292
343
|
}
|
|
344
|
+
resetSecurityHandler() {
|
|
345
|
+
this.securityHandler =
|
|
346
|
+
this.originalSecurityHandler ?? this.getSecurityHandler();
|
|
347
|
+
}
|
|
293
348
|
getSecurityHandler() {
|
|
294
349
|
const encryptionDictionaryRef = this.trailerDict
|
|
295
350
|
.get('Encrypt')
|
|
@@ -383,11 +438,39 @@ export class PdfDocument extends PdfObject {
|
|
|
383
438
|
}
|
|
384
439
|
return true;
|
|
385
440
|
}
|
|
441
|
+
commitIncrementalUpdates() {
|
|
442
|
+
if (!this.incremental || this._committing) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
this._committing = true;
|
|
446
|
+
try {
|
|
447
|
+
for (const revision of this.revisions) {
|
|
448
|
+
if (!revision.locked)
|
|
449
|
+
continue;
|
|
450
|
+
for (const obj of revision.objects) {
|
|
451
|
+
if (!(obj instanceof PdfIndirectObject)) {
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
if (obj.isModified()) {
|
|
455
|
+
const adding = obj.clone();
|
|
456
|
+
this.add(adding);
|
|
457
|
+
adding.setModified(false);
|
|
458
|
+
obj.setModified(false);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
finally {
|
|
464
|
+
this._committing = false;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
386
467
|
/**
|
|
387
|
-
* Decrypts all encrypted
|
|
388
|
-
*
|
|
468
|
+
* Decrypts all encrypted object data in-place without removing
|
|
469
|
+
* the encryption infrastructure. Useful in incremental mode where
|
|
470
|
+
* the original (encrypted) bytes are preserved via cached tokens
|
|
471
|
+
* but the live object data needs to be readable.
|
|
389
472
|
*/
|
|
390
|
-
async
|
|
473
|
+
async decryptObjects() {
|
|
391
474
|
if (!this.securityHandler) {
|
|
392
475
|
return;
|
|
393
476
|
}
|
|
@@ -400,19 +483,55 @@ export class PdfDocument extends PdfObject {
|
|
|
400
483
|
}
|
|
401
484
|
await this.securityHandler.decryptObject(object);
|
|
402
485
|
}
|
|
403
|
-
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Re-encrypts all objects and updates the document structure.
|
|
489
|
+
* No-op if the document has no security handler (unencrypted document).
|
|
490
|
+
*/
|
|
491
|
+
async finalize() {
|
|
492
|
+
if (this._finalized) {
|
|
493
|
+
throw new Error('Document has already been finalized');
|
|
494
|
+
}
|
|
495
|
+
this._finalized = true;
|
|
496
|
+
this.update();
|
|
497
|
+
if (this.securityHandler) {
|
|
498
|
+
await this.encrypt();
|
|
499
|
+
}
|
|
500
|
+
this.update();
|
|
501
|
+
await this.sign();
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Decrypts all encrypted objects in the document.
|
|
505
|
+
* Removes the security handler and encryption dictionary after decryption.
|
|
506
|
+
*/
|
|
507
|
+
async decrypt() {
|
|
508
|
+
if (!this.securityHandler) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
await this.decryptObjects();
|
|
404
512
|
this.hasEncryptionDictionary = false;
|
|
513
|
+
this.securityHandler = undefined;
|
|
405
514
|
const encryptionDict = this.encryptionDictionary;
|
|
515
|
+
this.trailerDict.delete('Encrypt');
|
|
406
516
|
if (encryptionDict) {
|
|
407
|
-
|
|
517
|
+
this.deleteObject(encryptionDict);
|
|
408
518
|
}
|
|
409
|
-
|
|
519
|
+
this.update();
|
|
410
520
|
}
|
|
411
521
|
/**
|
|
412
|
-
* Encrypts all objects
|
|
413
|
-
*
|
|
522
|
+
* Encrypts all encryptable objects using the security handler.
|
|
523
|
+
* Re-uses the existing encryption dictionary or creates one if needed,
|
|
524
|
+
* propagating it to all revisions.
|
|
414
525
|
*/
|
|
415
526
|
async encrypt() {
|
|
527
|
+
if (!this.securityHandler) {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
const addingEncryption = !this.hasEncryptionDictionary;
|
|
531
|
+
const wasIncremental = this.incremental;
|
|
532
|
+
if (addingEncryption) {
|
|
533
|
+
this.setIncremental(false);
|
|
534
|
+
}
|
|
416
535
|
this.initSecurityHandler({});
|
|
417
536
|
await this.securityHandler.write();
|
|
418
537
|
for (const object of this.objects) {
|
|
@@ -423,20 +542,24 @@ export class PdfDocument extends PdfObject {
|
|
|
423
542
|
continue;
|
|
424
543
|
}
|
|
425
544
|
await this.securityHandler.encryptObject(object);
|
|
545
|
+
object.setModified(false);
|
|
426
546
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
547
|
+
if (addingEncryption) {
|
|
548
|
+
const encryptionDictObject = new PdfIndirectObject({
|
|
549
|
+
content: this.securityHandler.dict,
|
|
550
|
+
encryptable: false,
|
|
551
|
+
});
|
|
552
|
+
this.add(encryptionDictObject);
|
|
553
|
+
this.hasEncryptionDictionary = true;
|
|
554
|
+
for (const revision of this.revisions) {
|
|
555
|
+
revision.xref.trailerDict.set('Encrypt', encryptionDictObject.reference);
|
|
556
|
+
if (!revision.xref.trailerDict.get('ID')) {
|
|
557
|
+
revision.xref.trailerDict.set('ID', this.securityHandler.getDocumentId());
|
|
558
|
+
}
|
|
435
559
|
}
|
|
560
|
+
this.setIncremental(wasIncremental);
|
|
436
561
|
}
|
|
437
|
-
|
|
438
|
-
this.hasEncryptionDictionary = true;
|
|
439
|
-
await this.update();
|
|
562
|
+
this.update();
|
|
440
563
|
}
|
|
441
564
|
/**
|
|
442
565
|
* Finds a compressed object by its object number within an object stream.
|
|
@@ -445,7 +568,7 @@ export class PdfDocument extends PdfObject {
|
|
|
445
568
|
* @returns The found indirect object or undefined if not found
|
|
446
569
|
* @throws Error if the object cannot be found in the expected object stream
|
|
447
570
|
*/
|
|
448
|
-
|
|
571
|
+
findCompressedObject(options) {
|
|
449
572
|
const xrefEntry = this.xrefLookup.getObject(options.objectNumber);
|
|
450
573
|
if (!(xrefEntry instanceof PdfXRefStreamCompressedEntry)) {
|
|
451
574
|
throw new Error('Cannot find object inside object stream via PdfDocument.findObject');
|
|
@@ -456,10 +579,6 @@ export class PdfDocument extends PdfObject {
|
|
|
456
579
|
if (!objectStreamIndirect) {
|
|
457
580
|
throw new Error(`Cannot find object stream ${xrefEntry.objectStreamNumber.value} for object ${options.objectNumber}`);
|
|
458
581
|
}
|
|
459
|
-
if (this.securityHandler &&
|
|
460
|
-
this.isObjectEncryptable(objectStreamIndirect)) {
|
|
461
|
-
await this.securityHandler.decryptObject(objectStreamIndirect);
|
|
462
|
-
}
|
|
463
582
|
const objectStream = objectStreamIndirect.content
|
|
464
583
|
.as(PdfStream)
|
|
465
584
|
.parseAs(PdfObjStream);
|
|
@@ -502,14 +621,14 @@ export class PdfDocument extends PdfObject {
|
|
|
502
621
|
* @param options.allowUnindexed - If true, searches unindexed objects as fallback
|
|
503
622
|
* @returns A cloned and decrypted copy of the object, or undefined if not found
|
|
504
623
|
*/
|
|
505
|
-
|
|
624
|
+
readObject(options) {
|
|
506
625
|
let foundObject;
|
|
507
626
|
try {
|
|
508
627
|
foundObject = this.findUncompressedObject(options);
|
|
509
628
|
}
|
|
510
629
|
catch (e) {
|
|
511
630
|
if (e instanceof FoundCompressedObjectError) {
|
|
512
|
-
foundObject =
|
|
631
|
+
foundObject = this.findCompressedObject(options);
|
|
513
632
|
}
|
|
514
633
|
else {
|
|
515
634
|
throw e;
|
|
@@ -524,24 +643,32 @@ export class PdfDocument extends PdfObject {
|
|
|
524
643
|
if (!foundObject) {
|
|
525
644
|
return undefined;
|
|
526
645
|
}
|
|
527
|
-
if (
|
|
646
|
+
if (options.cloned) {
|
|
528
647
|
foundObject = foundObject.clone();
|
|
529
|
-
await this.securityHandler.decryptObject(foundObject);
|
|
530
648
|
}
|
|
531
649
|
return foundObject;
|
|
532
650
|
}
|
|
651
|
+
/**
|
|
652
|
+
* Finds the revision that contains a given PDF object.
|
|
653
|
+
* Useful for determining the origin of an object across multiple revisions.
|
|
654
|
+
* @param obj - The PDF object to find the revision for
|
|
655
|
+
* @returns The PdfRevision that contains the object, or undefined if not found in any revision
|
|
656
|
+
*/
|
|
657
|
+
findRevisionForObject(obj) {
|
|
658
|
+
return this.revisions.find((rev) => rev.objects.includes(obj));
|
|
659
|
+
}
|
|
533
660
|
/**
|
|
534
661
|
* Deletes an object from all revisions in the document.
|
|
535
662
|
*
|
|
536
663
|
* @param obj - The PDF object to delete
|
|
537
664
|
*/
|
|
538
|
-
|
|
665
|
+
deleteObject(obj) {
|
|
539
666
|
if (!obj)
|
|
540
667
|
return;
|
|
541
668
|
for (const revision of this.revisions) {
|
|
542
669
|
revision.deleteObject(obj);
|
|
543
670
|
}
|
|
544
|
-
|
|
671
|
+
this.update();
|
|
545
672
|
}
|
|
546
673
|
/**
|
|
547
674
|
* Sets the PDF version for the document.
|
|
@@ -565,10 +692,10 @@ export class PdfDocument extends PdfObject {
|
|
|
565
692
|
if (value === this.isIncremental()) {
|
|
566
693
|
return;
|
|
567
694
|
}
|
|
695
|
+
this.incremental = value;
|
|
568
696
|
for (const revision of this.revisions) {
|
|
569
697
|
revision.locked = value;
|
|
570
698
|
}
|
|
571
|
-
this.startNewRevision();
|
|
572
699
|
}
|
|
573
700
|
/**
|
|
574
701
|
* Checks if the document is in incremental mode.
|
|
@@ -576,53 +703,7 @@ export class PdfDocument extends PdfObject {
|
|
|
576
703
|
* @returns True if all revisions are locked for incremental updates
|
|
577
704
|
*/
|
|
578
705
|
isIncremental() {
|
|
579
|
-
return this.
|
|
580
|
-
}
|
|
581
|
-
/**
|
|
582
|
-
* Commits pending objects to the document.
|
|
583
|
-
* Adds objects, applies encryption if configured, and updates the document structure.
|
|
584
|
-
*
|
|
585
|
-
* @param newObjects - Additional objects to add before committing
|
|
586
|
-
*/
|
|
587
|
-
async commit(...newObjects) {
|
|
588
|
-
this.add(...newObjects);
|
|
589
|
-
const queue = this.toBeCommitted.slice();
|
|
590
|
-
this.toBeCommitted = [];
|
|
591
|
-
for (const newObject of queue) {
|
|
592
|
-
if (this.securityHandler &&
|
|
593
|
-
newObject instanceof PdfIndirectObject &&
|
|
594
|
-
this.isObjectEncryptable(newObject)) {
|
|
595
|
-
await this.securityHandler.write();
|
|
596
|
-
if (!this.hasEncryptionDictionary) {
|
|
597
|
-
const encryptionDictObject = new PdfIndirectObject({
|
|
598
|
-
content: this.securityHandler.dict,
|
|
599
|
-
encryptable: false,
|
|
600
|
-
});
|
|
601
|
-
this.latestRevision.addObject(encryptionDictObject);
|
|
602
|
-
this.trailerDict.set('Encrypt', encryptionDictObject.reference);
|
|
603
|
-
this.hasEncryptionDictionary = true;
|
|
604
|
-
}
|
|
605
|
-
await this.securityHandler.encryptObject(newObject);
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
await this.update();
|
|
609
|
-
}
|
|
610
|
-
/**
|
|
611
|
-
* Sets the Document Security Store (DSS) for the document.
|
|
612
|
-
* Used for long-term validation of digital signatures.
|
|
613
|
-
*
|
|
614
|
-
* @param dss - The Document Security Store object to set
|
|
615
|
-
* @throws Error if the document has no root dictionary
|
|
616
|
-
*/
|
|
617
|
-
async setDocumentSecurityStore(dss) {
|
|
618
|
-
let rootDictionary = this.root?.content;
|
|
619
|
-
if (!rootDictionary) {
|
|
620
|
-
throw new Error('Cannot set DSS - document has no root dictionary');
|
|
621
|
-
}
|
|
622
|
-
rootDictionary.set('DSS', dss.reference);
|
|
623
|
-
if (!this.hasObject(dss)) {
|
|
624
|
-
await this.commit(dss);
|
|
625
|
-
}
|
|
706
|
+
return this.incremental;
|
|
626
707
|
}
|
|
627
708
|
/**
|
|
628
709
|
* Returns tokens paired with their source objects.
|
|
@@ -644,12 +725,93 @@ export class PdfDocument extends PdfObject {
|
|
|
644
725
|
tokenize() {
|
|
645
726
|
return this.tokensWithObjects().map(({ token }) => token);
|
|
646
727
|
}
|
|
728
|
+
wireResolvers(...objects) {
|
|
729
|
+
const seen = new Set();
|
|
730
|
+
const walk = (obj) => {
|
|
731
|
+
if (seen.has(obj))
|
|
732
|
+
return;
|
|
733
|
+
seen.add(obj);
|
|
734
|
+
if (obj instanceof PdfObjectReference &&
|
|
735
|
+
!(obj instanceof PdfIndirectObject)) {
|
|
736
|
+
obj.resolver = this;
|
|
737
|
+
}
|
|
738
|
+
else if (obj instanceof PdfStream) {
|
|
739
|
+
walk(obj.header);
|
|
740
|
+
}
|
|
741
|
+
else if (obj instanceof PdfDictionary) {
|
|
742
|
+
for (const [, value] of obj.entries()) {
|
|
743
|
+
if (value)
|
|
744
|
+
walk(value);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
else if (obj instanceof PdfArray) {
|
|
748
|
+
for (const item of obj.items) {
|
|
749
|
+
walk(item);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
else if (obj instanceof PdfIndirectObject) {
|
|
753
|
+
walk(obj.content);
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
for (const obj of objects) {
|
|
757
|
+
walk(obj);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
collectMissingReferences(...objects) {
|
|
761
|
+
const seen = new Set();
|
|
762
|
+
const missing = [];
|
|
763
|
+
const queue = [...objects];
|
|
764
|
+
while (queue.length > 0) {
|
|
765
|
+
const obj = queue.pop();
|
|
766
|
+
if (seen.has(obj))
|
|
767
|
+
continue;
|
|
768
|
+
seen.add(obj);
|
|
769
|
+
if (obj instanceof PdfObjectReference &&
|
|
770
|
+
!(obj instanceof PdfIndirectObject)) {
|
|
771
|
+
if (obj.resolver) {
|
|
772
|
+
try {
|
|
773
|
+
const resolved = obj.resolve();
|
|
774
|
+
if (resolved instanceof PdfIndirectObject &&
|
|
775
|
+
!resolved.inPdf()) {
|
|
776
|
+
missing.push(resolved);
|
|
777
|
+
queue.push(resolved);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
catch {
|
|
781
|
+
// Skip unresolvable references
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
else if (obj instanceof PdfStream) {
|
|
786
|
+
queue.push(obj.header);
|
|
787
|
+
}
|
|
788
|
+
else if (obj instanceof PdfDictionary) {
|
|
789
|
+
for (const [, value] of obj.entries()) {
|
|
790
|
+
if (value)
|
|
791
|
+
queue.push(value);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
else if (obj instanceof PdfArray) {
|
|
795
|
+
for (const item of obj.items)
|
|
796
|
+
queue.push(item);
|
|
797
|
+
}
|
|
798
|
+
else if (obj instanceof PdfIndirectObject) {
|
|
799
|
+
queue.push(obj.content);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return missing;
|
|
803
|
+
}
|
|
647
804
|
linkRevisions() {
|
|
648
805
|
const xrefLookups = this.revisions.map((rev) => rev.xref);
|
|
649
806
|
const indirectObjects = this.objects.filter((x) => x instanceof PdfIndirectObject);
|
|
650
807
|
for (const revision of this.revisions) {
|
|
651
808
|
revision.xref.linkPrev(xrefLookups);
|
|
652
|
-
|
|
809
|
+
// Walk the entire prev chain to link all xref entries
|
|
810
|
+
let xref = revision.xref;
|
|
811
|
+
while (xref) {
|
|
812
|
+
xref.linkIndirectObjects(indirectObjects);
|
|
813
|
+
xref = xref.prev;
|
|
814
|
+
}
|
|
653
815
|
}
|
|
654
816
|
}
|
|
655
817
|
linkOffsets() {
|
|
@@ -696,7 +858,7 @@ export class PdfDocument extends PdfObject {
|
|
|
696
858
|
}
|
|
697
859
|
updateRevisions() {
|
|
698
860
|
let modified = false;
|
|
699
|
-
this.revisions.forEach((rev
|
|
861
|
+
this.revisions.forEach((rev) => {
|
|
700
862
|
if (rev.isModified()) {
|
|
701
863
|
modified = true;
|
|
702
864
|
}
|
|
@@ -704,11 +866,62 @@ export class PdfDocument extends PdfObject {
|
|
|
704
866
|
rev.update();
|
|
705
867
|
}
|
|
706
868
|
});
|
|
869
|
+
// Always rebuild xref binary data to reflect recalculated offsets
|
|
870
|
+
// This walks the entire prev chain for each revision
|
|
871
|
+
for (const rev of this.revisions) {
|
|
872
|
+
let xref = rev.xref;
|
|
873
|
+
while (xref) {
|
|
874
|
+
xref.update();
|
|
875
|
+
xref = xref.prev;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
707
878
|
}
|
|
708
|
-
|
|
879
|
+
/**
|
|
880
|
+
* Performs a full update cycle to ensure all revisions are consistent and offsets are correct.
|
|
881
|
+
*/
|
|
882
|
+
update() {
|
|
883
|
+
this.commitIncrementalUpdates();
|
|
884
|
+
this.flushResolvedCache();
|
|
885
|
+
this.registerNewReferences();
|
|
886
|
+
this.calculateOffsets();
|
|
887
|
+
this.updateRevisions();
|
|
888
|
+
// Second pass: xref binary may have changed size (e.g. FlateDecode removed
|
|
889
|
+
// from xref stream), shifting objects that follow it. Recalculate so entry
|
|
890
|
+
// byteOffset refs hold the new positions, then rebuild the xref binary once
|
|
891
|
+
// more so the baked bytes match those positions.
|
|
709
892
|
this.calculateOffsets();
|
|
710
893
|
this.updateRevisions();
|
|
711
|
-
|
|
894
|
+
// Third pass: confirm positions are stable (xref binary size should not
|
|
895
|
+
// change again because W widths and entry count are the same).
|
|
896
|
+
this.calculateOffsets();
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* Walks all objects in the document and registers any newly created
|
|
900
|
+
* PdfIndirectObjects that are referenced but not yet part of the document
|
|
901
|
+
* (e.g. appearance streams created by generateAppearance).
|
|
902
|
+
*/
|
|
903
|
+
registerNewReferences() {
|
|
904
|
+
const missing = this.collectMissingReferences(...this.latestRevision.objects);
|
|
905
|
+
if (missing.length > 0) {
|
|
906
|
+
this.add(...missing);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
flushResolvedCache() {
|
|
910
|
+
for (const [, obj] of this._resolvedCache) {
|
|
911
|
+
if (obj.isModified() && !this.objects.includes(obj)) {
|
|
912
|
+
this.add(obj);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
async sign() {
|
|
917
|
+
if (this._signed) {
|
|
918
|
+
throw new Error('Document has already been signed');
|
|
919
|
+
}
|
|
920
|
+
if (!this.signer) {
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
await this.signer.sign();
|
|
924
|
+
this._signed = true;
|
|
712
925
|
}
|
|
713
926
|
/**
|
|
714
927
|
* Serializes the document to a byte array.
|
|
@@ -716,24 +929,9 @@ export class PdfDocument extends PdfObject {
|
|
|
716
929
|
* @returns The PDF document as a Uint8Array
|
|
717
930
|
*/
|
|
718
931
|
toBytes() {
|
|
719
|
-
this.
|
|
720
|
-
this.updateRevisions();
|
|
932
|
+
this.update();
|
|
721
933
|
return concatUint8Arrays(...this.revisions.map((x) => x.toBytes()));
|
|
722
934
|
}
|
|
723
|
-
/**
|
|
724
|
-
* Serializes the document to a Base64-encoded string.
|
|
725
|
-
*
|
|
726
|
-
* @returns A promise that resolves to the PDF document as a Base64 string
|
|
727
|
-
*/
|
|
728
|
-
toBase64() {
|
|
729
|
-
const bytes = this.toBytes();
|
|
730
|
-
let binary = '';
|
|
731
|
-
const len = bytes.byteLength;
|
|
732
|
-
for (let i = 0; i < len; i++) {
|
|
733
|
-
binary += String.fromCharCode(bytes[i]);
|
|
734
|
-
}
|
|
735
|
-
return btoa(binary);
|
|
736
|
-
}
|
|
737
935
|
/**
|
|
738
936
|
* Creates a deep copy of the document.
|
|
739
937
|
*
|
|
@@ -753,8 +951,8 @@ export class PdfDocument extends PdfObject {
|
|
|
753
951
|
* @param input - Async or sync iterable of byte arrays
|
|
754
952
|
* @returns A promise that resolves to the parsed PdfDocument
|
|
755
953
|
*/
|
|
756
|
-
static fromBytes(input) {
|
|
757
|
-
return PdfReader.fromBytes(input);
|
|
954
|
+
static fromBytes(input, options) {
|
|
955
|
+
return PdfReader.fromBytes(input, options);
|
|
758
956
|
}
|
|
759
957
|
isModified() {
|
|
760
958
|
return (super.isModified() || this.revisions.some((rev) => rev.isModified()));
|
|
@@ -765,6 +963,6 @@ export class PdfDocument extends PdfObject {
|
|
|
765
963
|
* @returns A promise that resolves to the verification result
|
|
766
964
|
*/
|
|
767
965
|
async verifySignatures() {
|
|
768
|
-
return await this.signer.verify(
|
|
966
|
+
return await this.signer.verify();
|
|
769
967
|
}
|
|
770
968
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { PdfDictionary } from '../core/objects/pdf-dictionary.js';
|
|
2
|
+
import { PdfArray } from '../core/objects/pdf-array.js';
|
|
3
|
+
import { PdfObjectReference } from '../core/objects/pdf-object-reference.js';
|
|
4
|
+
import { PdfIndirectObject } from '../core/objects/pdf-indirect-object.js';
|
|
5
|
+
import { PdfNumber } from '../core/objects/pdf-number.js';
|
|
6
|
+
import { PdfName } from '../core/objects/pdf-name.js';
|
|
7
|
+
import { PdfPages } from './pdf-pages.js';
|
|
8
|
+
type PdfPageDictionary = PdfDictionary<{
|
|
9
|
+
Type: PdfName<'Page'>;
|
|
10
|
+
Parent: PdfObjectReference;
|
|
11
|
+
MediaBox: PdfArray<PdfNumber>;
|
|
12
|
+
CropBox?: PdfArray<PdfNumber>;
|
|
13
|
+
BleedBox?: PdfArray<PdfNumber>;
|
|
14
|
+
TrimBox?: PdfArray<PdfNumber>;
|
|
15
|
+
ArtBox?: PdfArray<PdfNumber>;
|
|
16
|
+
Rotate?: PdfNumber;
|
|
17
|
+
Contents?: PdfObjectReference | PdfArray<PdfObjectReference>;
|
|
18
|
+
Resources?: PdfDictionary | PdfObjectReference;
|
|
19
|
+
Annots?: PdfArray<PdfObjectReference> | PdfObjectReference;
|
|
20
|
+
}>;
|
|
21
|
+
export declare class PdfPage extends PdfIndirectObject<PdfPageDictionary> {
|
|
22
|
+
constructor(options?: PdfIndirectObject);
|
|
23
|
+
private getBox;
|
|
24
|
+
private setBox;
|
|
25
|
+
get mediaBox(): [number, number, number, number];
|
|
26
|
+
set mediaBox(value: [number, number, number, number]);
|
|
27
|
+
get cropBox(): [number, number, number, number] | null;
|
|
28
|
+
set cropBox(value: [number, number, number, number] | null);
|
|
29
|
+
get bleedBox(): [number, number, number, number] | null;
|
|
30
|
+
set bleedBox(value: [number, number, number, number] | null);
|
|
31
|
+
get trimBox(): [number, number, number, number] | null;
|
|
32
|
+
set trimBox(value: [number, number, number, number] | null);
|
|
33
|
+
get artBox(): [number, number, number, number] | null;
|
|
34
|
+
set artBox(value: [number, number, number, number] | null);
|
|
35
|
+
get width(): number;
|
|
36
|
+
get height(): number;
|
|
37
|
+
get rotate(): number;
|
|
38
|
+
set rotate(value: number);
|
|
39
|
+
get contents(): PdfObjectReference | PdfArray<PdfObjectReference> | null;
|
|
40
|
+
set contents(value: PdfObjectReference | PdfArray<PdfObjectReference> | null);
|
|
41
|
+
get resources(): PdfDictionary | null;
|
|
42
|
+
set resources(value: PdfDictionary | PdfObjectReference | null);
|
|
43
|
+
get annotations(): PdfArray<PdfObjectReference>;
|
|
44
|
+
set annotations(value: PdfArray<PdfObjectReference>);
|
|
45
|
+
get parentRef(): PdfObjectReference;
|
|
46
|
+
set parentRef(value: PdfObjectReference);
|
|
47
|
+
get parent(): PdfPages;
|
|
48
|
+
set parent(value: PdfPages);
|
|
49
|
+
}
|
|
50
|
+
export {};
|