pdf-lite 1.2.1 → 1.3.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/EXAMPLES.md +1 -1
- package/dist/acroform/acroform.d.ts +272 -16
- package/dist/acroform/acroform.js +1084 -144
- package/dist/acroform/manager.d.ts +2 -2
- package/dist/acroform/manager.js +3 -3
- package/dist/core/decoder.d.ts +1 -1
- package/dist/core/decoder.js +3 -3
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +2 -2
- package/dist/core/objects/pdf-array.d.ts +1 -0
- package/dist/core/objects/pdf-array.js +4 -0
- package/dist/core/objects/pdf-dictionary.d.ts +1 -0
- package/dist/core/objects/pdf-dictionary.js +12 -0
- package/dist/core/objects/pdf-hexadecimal.d.ts +9 -2
- package/dist/core/objects/pdf-hexadecimal.js +25 -5
- package/dist/core/objects/pdf-indirect-object.d.ts +5 -3
- package/dist/core/objects/pdf-indirect-object.js +23 -5
- package/dist/core/objects/pdf-number.js +3 -0
- package/dist/core/objects/pdf-object.d.ts +6 -0
- package/dist/core/objects/pdf-object.js +10 -0
- package/dist/core/objects/pdf-stream.js +3 -0
- package/dist/core/objects/pdf-string.d.ts +11 -1
- package/dist/core/objects/pdf-string.js +24 -6
- package/dist/core/ref.d.ts +5 -0
- package/dist/core/ref.js +14 -0
- package/dist/core/serializer.d.ts +1 -1
- package/dist/core/serializer.js +1 -1
- package/dist/core/tokeniser.d.ts +2 -2
- package/dist/core/tokeniser.js +37 -75
- package/dist/core/tokens/hexadecimal-token.d.ts +8 -1
- package/dist/core/tokens/hexadecimal-token.js +20 -2
- package/dist/core/tokens/name-token.js +0 -3
- package/dist/core/tokens/string-token.d.ts +8 -1
- package/dist/core/tokens/string-token.js +20 -2
- package/dist/fonts/font-manager.js +6 -8
- package/dist/pdf/pdf-document.d.ts +12 -11
- package/dist/pdf/pdf-document.js +50 -42
- package/dist/pdf/pdf-revision.d.ts +33 -4
- package/dist/pdf/pdf-revision.js +100 -26
- package/dist/pdf/pdf-xref-lookup.js +3 -2
- package/dist/utils/decodeWithFontEncoding.d.ts +20 -0
- package/dist/utils/decodeWithFontEncoding.js +67 -0
- package/dist/utils/escapeString.d.ts +1 -1
- package/dist/utils/escapeString.js +12 -3
- package/dist/utils/glyphNameToUnicode.d.ts +10 -0
- package/dist/utils/glyphNameToUnicode.js +4292 -0
- package/dist/xfa/manager.js +2 -4
- package/package.json +1 -1
- /package/dist/core/{incremental-parser.d.ts → parser/incremental-parser.d.ts} +0 -0
- /package/dist/core/{incremental-parser.js → parser/incremental-parser.js} +0 -0
- /package/dist/core/{parser.d.ts → parser/parser.d.ts} +0 -0
- /package/dist/core/{parser.js → parser/parser.js} +0 -0
package/dist/core/tokeniser.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { assert } from '../utils/assert.js';
|
|
2
2
|
import { bytesToString } from '../utils/bytesToString.js';
|
|
3
|
-
import {
|
|
3
|
+
import { unescapeString } from '../utils/unescapeString.js';
|
|
4
|
+
import { IncrementalParser } from './parser/incremental-parser.js';
|
|
4
5
|
import { PdfBooleanToken } from './tokens/boolean-token.js';
|
|
5
6
|
import { PdfCommentToken } from './tokens/comment-token.js';
|
|
6
7
|
import { PdfEndArrayToken } from './tokens/end-array-token.js';
|
|
@@ -24,7 +25,7 @@ import { PdfWhitespaceToken } from './tokens/whitespace-token.js';
|
|
|
24
25
|
import { PdfXRefTableEntryToken } from './tokens/xref-table-entry-token.js';
|
|
25
26
|
import { PdfXRefTableSectionStartToken } from './tokens/xref-table-section-start-token.js';
|
|
26
27
|
import { PdfXRefTableStartToken } from './tokens/xref-table-start-token.js';
|
|
27
|
-
import { Parser } from './parser.js';
|
|
28
|
+
import { Parser } from './parser/parser.js';
|
|
28
29
|
import { concatUint8Arrays } from '../utils/concatUint8Arrays.js';
|
|
29
30
|
import { stringToBytes } from '../utils/stringToBytes.js';
|
|
30
31
|
const ByteMap = {
|
|
@@ -128,7 +129,8 @@ export class PdfByteStreamTokeniser extends IncrementalParser {
|
|
|
128
129
|
nameBytes.push(this.next());
|
|
129
130
|
byte = this.peek();
|
|
130
131
|
}
|
|
131
|
-
|
|
132
|
+
const name = bytesToString(new Uint8Array(nameBytes));
|
|
133
|
+
return new PdfNameToken(name);
|
|
132
134
|
}
|
|
133
135
|
nextDictionaryEndToken() {
|
|
134
136
|
this.expect(ByteMap.RIGHT_ANGLE_BRACKET);
|
|
@@ -136,6 +138,8 @@ export class PdfByteStreamTokeniser extends IncrementalParser {
|
|
|
136
138
|
return new PdfEndDictionaryToken();
|
|
137
139
|
}
|
|
138
140
|
nextHexadecimalToken() {
|
|
141
|
+
// Capture starting position (before the opening angle bracket)
|
|
142
|
+
const startIndex = this.bufferIndex;
|
|
139
143
|
this.expect(ByteMap.LEFT_ANGLE_BRACKET);
|
|
140
144
|
const hexBytes = [];
|
|
141
145
|
let byte = this.peek();
|
|
@@ -146,7 +150,10 @@ export class PdfByteStreamTokeniser extends IncrementalParser {
|
|
|
146
150
|
byte = this.peek();
|
|
147
151
|
}
|
|
148
152
|
this.expect(ByteMap.RIGHT_ANGLE_BRACKET);
|
|
149
|
-
|
|
153
|
+
// Capture original bytes including angle brackets for incremental updates
|
|
154
|
+
const endIndex = this.bufferIndex; // After the closing angle bracket
|
|
155
|
+
const originalBytes = new Uint8Array(this.buffer.slice(startIndex, endIndex));
|
|
156
|
+
return new PdfHexadecimalToken(new Uint8Array(hexBytes), originalBytes);
|
|
150
157
|
}
|
|
151
158
|
nextNumberToken() {
|
|
152
159
|
const numberBytes = [];
|
|
@@ -187,8 +194,11 @@ export class PdfByteStreamTokeniser extends IncrementalParser {
|
|
|
187
194
|
return new PdfEndArrayToken();
|
|
188
195
|
}
|
|
189
196
|
nextStringToken() {
|
|
197
|
+
// Capture starting position (before the opening parenthesis)
|
|
198
|
+
const startIndex = this.bufferIndex;
|
|
190
199
|
this.expect(ByteMap.LEFT_PARENTHESIS);
|
|
191
|
-
|
|
200
|
+
// Collect raw bytes until we find the matching closing parenthesis
|
|
201
|
+
const rawBytes = [];
|
|
192
202
|
let nesting = 1;
|
|
193
203
|
let inEscape = false;
|
|
194
204
|
while (inEscape || nesting > 0) {
|
|
@@ -196,82 +206,34 @@ export class PdfByteStreamTokeniser extends IncrementalParser {
|
|
|
196
206
|
if (byte === null) {
|
|
197
207
|
throw new Error('Unexpected end of input in string token');
|
|
198
208
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
break;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
else if (byte === ByteMap.BACKSLASH || inEscape) {
|
|
209
|
-
inEscape = true;
|
|
210
|
-
const next = this.next();
|
|
211
|
-
if (next === null) {
|
|
212
|
-
throw new Error('Unexpected end of input in string token');
|
|
209
|
+
// Add byte to rawBytes first (including the closing parenthesis)
|
|
210
|
+
rawBytes.push(byte);
|
|
211
|
+
// Track nesting level for proper parenthesis matching
|
|
212
|
+
if (!inEscape) {
|
|
213
|
+
if (byte === ByteMap.LEFT_PARENTHESIS) {
|
|
214
|
+
nesting++;
|
|
213
215
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
break; // \n
|
|
218
|
-
case ByteMap.r:
|
|
219
|
-
stringBytes.push(0x0d);
|
|
220
|
-
break; // \r
|
|
221
|
-
case ByteMap.t:
|
|
222
|
-
stringBytes.push(0x09);
|
|
223
|
-
break; // \t
|
|
224
|
-
case ByteMap.b:
|
|
225
|
-
stringBytes.push(0x08);
|
|
226
|
-
break; // \b
|
|
227
|
-
case ByteMap.f:
|
|
228
|
-
stringBytes.push(0x0c);
|
|
229
|
-
break; // \f
|
|
230
|
-
case ByteMap.LEFT_PARENTHESIS:
|
|
231
|
-
stringBytes.push(ByteMap.LEFT_PARENTHESIS);
|
|
232
|
-
break; // \(
|
|
233
|
-
case ByteMap.RIGHT_PARENTHESIS:
|
|
234
|
-
stringBytes.push(ByteMap.RIGHT_PARENTHESIS);
|
|
235
|
-
break; // \)
|
|
236
|
-
case ByteMap.BACKSLASH:
|
|
237
|
-
stringBytes.push(ByteMap.BACKSLASH);
|
|
238
|
-
break; // \\
|
|
239
|
-
case ByteMap.LINE_FEED: // Line feed
|
|
240
|
-
case ByteMap.CARRIAGE_RETURN: // Carriage return
|
|
241
|
-
stringBytes.push(next);
|
|
242
|
-
break;
|
|
243
|
-
default:
|
|
244
|
-
if (PdfByteStreamTokeniser.isOctet(next)) {
|
|
245
|
-
let octal = String.fromCharCode(next);
|
|
246
|
-
// Octal: up to 3 digits
|
|
247
|
-
const next2 = this.peek();
|
|
248
|
-
if (next2 === null) {
|
|
249
|
-
throw new Error('Unexpected end of input in string token');
|
|
250
|
-
}
|
|
251
|
-
if (PdfByteStreamTokeniser.isOctet(next2)) {
|
|
252
|
-
octal += String.fromCharCode(this.next());
|
|
253
|
-
}
|
|
254
|
-
const next3 = this.peek();
|
|
255
|
-
if (next3 === null) {
|
|
256
|
-
throw new Error('Unexpected end of input in string token');
|
|
257
|
-
}
|
|
258
|
-
if (PdfByteStreamTokeniser.isOctet(next3)) {
|
|
259
|
-
octal += String.fromCharCode(this.next());
|
|
260
|
-
}
|
|
261
|
-
stringBytes.push(parseInt(octal, 8));
|
|
262
|
-
}
|
|
263
|
-
else {
|
|
264
|
-
// If it's not a valid escape sequence, just add the next byte
|
|
265
|
-
stringBytes.push(next);
|
|
266
|
-
}
|
|
216
|
+
else if (byte === ByteMap.RIGHT_PARENTHESIS) {
|
|
217
|
+
nesting--;
|
|
218
|
+
if (nesting === 0) {
|
|
267
219
|
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
else if (byte === ByteMap.BACKSLASH) {
|
|
223
|
+
inEscape = true;
|
|
268
224
|
}
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
269
227
|
inEscape = false;
|
|
270
|
-
continue;
|
|
271
228
|
}
|
|
272
|
-
stringBytes.push(byte);
|
|
273
229
|
}
|
|
274
|
-
|
|
230
|
+
// Capture original bytes including parentheses for incremental updates
|
|
231
|
+
const endIndex = this.bufferIndex; // After the closing parenthesis
|
|
232
|
+
const originalBytes = new Uint8Array(this.buffer.slice(startIndex, endIndex));
|
|
233
|
+
// Use unescapeString utility to process escape sequences
|
|
234
|
+
// unescapeString expects bytes including the closing parenthesis
|
|
235
|
+
const unescapedBytes = unescapeString(new Uint8Array(rawBytes));
|
|
236
|
+
return new PdfStringToken(unescapedBytes, originalBytes);
|
|
275
237
|
}
|
|
276
238
|
nextEndObjectToken() {
|
|
277
239
|
this.expect(ByteMap.e);
|
|
@@ -2,6 +2,13 @@ import { ByteArray } from '../../types.js';
|
|
|
2
2
|
import { PdfToken } from './token.js';
|
|
3
3
|
export declare class PdfHexadecimalToken extends PdfToken {
|
|
4
4
|
raw: ByteArray;
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Original bytes from the PDF file, including angle brackets.
|
|
7
|
+
* Used to preserve exact formatting for incremental updates.
|
|
8
|
+
* @internal - Non-enumerable to avoid affecting test comparisons
|
|
9
|
+
*/
|
|
10
|
+
private _originalBytes?;
|
|
11
|
+
constructor(hexadecimal: string | ByteArray, originalBytes?: ByteArray);
|
|
12
|
+
get originalBytes(): ByteArray | undefined;
|
|
6
13
|
private static toBytes;
|
|
7
14
|
}
|
|
@@ -2,12 +2,30 @@ import { stringToBytes } from '../../utils/stringToBytes.js';
|
|
|
2
2
|
import { PdfToken } from './token.js';
|
|
3
3
|
export class PdfHexadecimalToken extends PdfToken {
|
|
4
4
|
raw;
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Original bytes from the PDF file, including angle brackets.
|
|
7
|
+
* Used to preserve exact formatting for incremental updates.
|
|
8
|
+
* @internal - Non-enumerable to avoid affecting test comparisons
|
|
9
|
+
*/
|
|
10
|
+
_originalBytes;
|
|
11
|
+
constructor(hexadecimal, originalBytes) {
|
|
12
|
+
super(originalBytes ?? PdfHexadecimalToken.toBytes(hexadecimal));
|
|
7
13
|
this.raw =
|
|
8
14
|
typeof hexadecimal === 'string'
|
|
9
15
|
? stringToBytes(hexadecimal)
|
|
10
16
|
: hexadecimal;
|
|
17
|
+
if (originalBytes) {
|
|
18
|
+
// Make non-enumerable so it doesn't affect .toEqual() comparisons
|
|
19
|
+
Object.defineProperty(this, '_originalBytes', {
|
|
20
|
+
value: originalBytes,
|
|
21
|
+
writable: true,
|
|
22
|
+
enumerable: false,
|
|
23
|
+
configurable: true,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
get originalBytes() {
|
|
28
|
+
return this._originalBytes;
|
|
11
29
|
}
|
|
12
30
|
static toBytes(hexadecimal) {
|
|
13
31
|
const bytes = stringToBytes(hexadecimal);
|
|
@@ -3,9 +3,6 @@ import { PdfToken } from './token.js';
|
|
|
3
3
|
export class PdfNameToken extends PdfToken {
|
|
4
4
|
name;
|
|
5
5
|
constructor(name) {
|
|
6
|
-
if (typeof name !== 'string' || name.length === 0) {
|
|
7
|
-
throw new Error('PdfNameToken name must be a non-empty string');
|
|
8
|
-
}
|
|
9
6
|
super(PdfNameToken.toBytes(name));
|
|
10
7
|
this.name = name;
|
|
11
8
|
}
|
|
@@ -2,6 +2,13 @@ import { ByteArray } from '../../types.js';
|
|
|
2
2
|
import { PdfToken } from './token.js';
|
|
3
3
|
export declare class PdfStringToken extends PdfToken {
|
|
4
4
|
value: ByteArray;
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Original bytes from the PDF file, including parentheses and escape sequences.
|
|
7
|
+
* Used to preserve exact formatting for incremental updates.
|
|
8
|
+
* @internal - Non-enumerable to avoid affecting test comparisons
|
|
9
|
+
*/
|
|
10
|
+
private _originalBytes?;
|
|
11
|
+
constructor(value: string | ByteArray, originalBytes?: ByteArray);
|
|
12
|
+
get originalBytes(): ByteArray | undefined;
|
|
6
13
|
private static toBytes;
|
|
7
14
|
}
|
|
@@ -3,9 +3,27 @@ import { stringToBytes } from '../../utils/stringToBytes.js';
|
|
|
3
3
|
import { PdfToken } from './token.js';
|
|
4
4
|
export class PdfStringToken extends PdfToken {
|
|
5
5
|
value;
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Original bytes from the PDF file, including parentheses and escape sequences.
|
|
8
|
+
* Used to preserve exact formatting for incremental updates.
|
|
9
|
+
* @internal - Non-enumerable to avoid affecting test comparisons
|
|
10
|
+
*/
|
|
11
|
+
_originalBytes;
|
|
12
|
+
constructor(value, originalBytes) {
|
|
13
|
+
super(originalBytes ?? PdfStringToken.toBytes(value));
|
|
8
14
|
this.value = typeof value === 'string' ? stringToBytes(value) : value;
|
|
15
|
+
if (originalBytes) {
|
|
16
|
+
// Make non-enumerable so it doesn't affect .toEqual() comparisons
|
|
17
|
+
Object.defineProperty(this, '_originalBytes', {
|
|
18
|
+
value: originalBytes,
|
|
19
|
+
writable: true,
|
|
20
|
+
enumerable: false,
|
|
21
|
+
configurable: true,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
get originalBytes() {
|
|
26
|
+
return this._originalBytes;
|
|
9
27
|
}
|
|
10
28
|
static toBytes(value) {
|
|
11
29
|
return new Uint8Array([0x28, ...escapeString(value), 0x29]);
|
|
@@ -273,10 +273,10 @@ export class PdfFontManager {
|
|
|
273
273
|
*/
|
|
274
274
|
async collectAllFontsFromPdf() {
|
|
275
275
|
const fonts = new Map();
|
|
276
|
-
const catalog = this.document.
|
|
276
|
+
const catalog = this.document.root;
|
|
277
277
|
if (!catalog)
|
|
278
278
|
return fonts;
|
|
279
|
-
const pagesRef = catalog.get('Pages');
|
|
279
|
+
const pagesRef = catalog.content.get('Pages');
|
|
280
280
|
if (!pagesRef)
|
|
281
281
|
return fonts;
|
|
282
282
|
const pagesObjRef = pagesRef.as(PdfObjectReference);
|
|
@@ -290,11 +290,11 @@ export class PdfFontManager {
|
|
|
290
290
|
* This ensures fonts are available to form fields.
|
|
291
291
|
*/
|
|
292
292
|
async addFontToAcroFormResources(resourceName, fontObject) {
|
|
293
|
-
const catalog = this.document.
|
|
293
|
+
const catalog = this.document.root;
|
|
294
294
|
if (!catalog)
|
|
295
295
|
return;
|
|
296
296
|
// Get AcroForm dictionary
|
|
297
|
-
const acroFormRef = catalog.get('AcroForm');
|
|
297
|
+
const acroFormRef = catalog.content.get('AcroForm');
|
|
298
298
|
if (!acroFormRef)
|
|
299
299
|
return;
|
|
300
300
|
let acroFormDict;
|
|
@@ -339,10 +339,8 @@ export class PdfFontManager {
|
|
|
339
339
|
* This is more efficient than adding to each individual page.
|
|
340
340
|
*/
|
|
341
341
|
async addFontToPageResources(resourceName, fontObject) {
|
|
342
|
-
const catalog = this.document.
|
|
343
|
-
|
|
344
|
-
return;
|
|
345
|
-
const pagesRef = catalog.get('Pages');
|
|
342
|
+
const catalog = this.document.root;
|
|
343
|
+
const pagesRef = catalog.content.get('Pages');
|
|
346
344
|
if (!pagesRef)
|
|
347
345
|
return;
|
|
348
346
|
const pagesObjRef = pagesRef.as(PdfObjectReference);
|
|
@@ -33,8 +33,6 @@ import { PdfFontManager } from '../fonts/font-manager.js';
|
|
|
33
33
|
* ```
|
|
34
34
|
*/
|
|
35
35
|
export declare class PdfDocument extends PdfObject {
|
|
36
|
-
/** PDF version comment header */
|
|
37
|
-
header: PdfComment;
|
|
38
36
|
/** List of document revisions for incremental updates */
|
|
39
37
|
revisions: PdfRevision[];
|
|
40
38
|
/** Signer instance for digital signature operations */
|
|
@@ -46,12 +44,6 @@ export declare class PdfDocument extends PdfObject {
|
|
|
46
44
|
private _fonts?;
|
|
47
45
|
private hasEncryptionDictionary?;
|
|
48
46
|
private toBeCommitted;
|
|
49
|
-
/** XFA manager for handling XFA forms */
|
|
50
|
-
get xfa(): PdfXfaManager;
|
|
51
|
-
/** AcroForm manager for handling form fields */
|
|
52
|
-
get acroForm(): PdfAcroFormManager;
|
|
53
|
-
/** Font manager for embedding and managing fonts */
|
|
54
|
-
get fonts(): PdfFontManager;
|
|
55
47
|
/**
|
|
56
48
|
* Creates a new PDF document instance.
|
|
57
49
|
*
|
|
@@ -71,6 +63,14 @@ export declare class PdfDocument extends PdfObject {
|
|
|
71
63
|
securityHandler?: PdfSecurityHandler;
|
|
72
64
|
signer?: PdfSigner;
|
|
73
65
|
});
|
|
66
|
+
get header(): PdfComment | undefined;
|
|
67
|
+
set header(comment: PdfComment | undefined);
|
|
68
|
+
/** XFA manager for handling XFA forms */
|
|
69
|
+
get xfa(): PdfXfaManager;
|
|
70
|
+
/** AcroForm manager for handling form fields */
|
|
71
|
+
get acroForm(): PdfAcroFormManager;
|
|
72
|
+
/** Font manager for embedding and managing fonts */
|
|
73
|
+
get fonts(): PdfFontManager;
|
|
74
74
|
/**
|
|
75
75
|
* Creates a PdfDocument from an array of PDF objects.
|
|
76
76
|
* Parses objects into revisions based on EOF comments.
|
|
@@ -125,13 +125,14 @@ export declare class PdfDocument extends PdfObject {
|
|
|
125
125
|
* @throws Error if the encryption dictionary reference points to a non-dictionary object
|
|
126
126
|
*/
|
|
127
127
|
get encryptionDictionary(): PdfEncryptionDictionaryObject | undefined;
|
|
128
|
+
get rootReference(): PdfObjectReference;
|
|
128
129
|
/**
|
|
129
|
-
* Gets the document catalog (root) dictionary.
|
|
130
|
+
* Gets the document catalog (root) dictionary, or creates one if it doesn't exist.
|
|
130
131
|
*
|
|
131
|
-
* @returns The root dictionary
|
|
132
|
+
* @returns The root dictionary
|
|
132
133
|
* @throws Error if the Root reference points to a non-dictionary object
|
|
133
134
|
*/
|
|
134
|
-
get
|
|
135
|
+
get root(): PdfIndirectObject<PdfDictionary>;
|
|
135
136
|
/**
|
|
136
137
|
* Gets the reference to the metadata stream from the document catalog.
|
|
137
138
|
*
|
package/dist/pdf/pdf-document.js
CHANGED
|
@@ -20,6 +20,7 @@ import { PdfSigner } from '../signing/signer.js';
|
|
|
20
20
|
import { PdfXfaManager } from '../xfa/manager.js';
|
|
21
21
|
import { PdfAcroFormManager } from '../acroform/manager.js';
|
|
22
22
|
import { PdfFontManager } from '../fonts/font-manager.js';
|
|
23
|
+
import { concatUint8Arrays } from '../utils/concatUint8Arrays.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.
|
|
@@ -38,8 +39,6 @@ import { PdfFontManager } from '../fonts/font-manager.js';
|
|
|
38
39
|
* ```
|
|
39
40
|
*/
|
|
40
41
|
export class PdfDocument extends PdfObject {
|
|
41
|
-
/** PDF version comment header */
|
|
42
|
-
header = PdfComment.versionComment('1.7');
|
|
43
42
|
/** List of document revisions for incremental updates */
|
|
44
43
|
revisions;
|
|
45
44
|
/** Signer instance for digital signature operations */
|
|
@@ -51,27 +50,6 @@ export class PdfDocument extends PdfObject {
|
|
|
51
50
|
_fonts;
|
|
52
51
|
hasEncryptionDictionary = false;
|
|
53
52
|
toBeCommitted = [];
|
|
54
|
-
/** XFA manager for handling XFA forms */
|
|
55
|
-
get xfa() {
|
|
56
|
-
if (!this._xfa) {
|
|
57
|
-
this._xfa = new PdfXfaManager(this);
|
|
58
|
-
}
|
|
59
|
-
return this._xfa;
|
|
60
|
-
}
|
|
61
|
-
/** AcroForm manager for handling form fields */
|
|
62
|
-
get acroForm() {
|
|
63
|
-
if (!this._acroForm) {
|
|
64
|
-
this._acroForm = new PdfAcroFormManager(this);
|
|
65
|
-
}
|
|
66
|
-
return this._acroForm;
|
|
67
|
-
}
|
|
68
|
-
/** Font manager for embedding and managing fonts */
|
|
69
|
-
get fonts() {
|
|
70
|
-
if (!this._fonts) {
|
|
71
|
-
this._fonts = new PdfFontManager(this);
|
|
72
|
-
}
|
|
73
|
-
return this._fonts;
|
|
74
|
-
}
|
|
75
53
|
/**
|
|
76
54
|
* Creates a new PDF document instance.
|
|
77
55
|
*
|
|
@@ -104,6 +82,34 @@ export class PdfDocument extends PdfObject {
|
|
|
104
82
|
this.securityHandler =
|
|
105
83
|
options?.securityHandler ?? this.getSecurityHandler();
|
|
106
84
|
}
|
|
85
|
+
get header() {
|
|
86
|
+
return this.revisions[0].header;
|
|
87
|
+
}
|
|
88
|
+
set header(comment) {
|
|
89
|
+
if (comment)
|
|
90
|
+
this.revisions[0].header = comment;
|
|
91
|
+
}
|
|
92
|
+
/** XFA manager for handling XFA forms */
|
|
93
|
+
get xfa() {
|
|
94
|
+
if (!this._xfa) {
|
|
95
|
+
this._xfa = new PdfXfaManager(this);
|
|
96
|
+
}
|
|
97
|
+
return this._xfa;
|
|
98
|
+
}
|
|
99
|
+
/** AcroForm manager for handling form fields */
|
|
100
|
+
get acroForm() {
|
|
101
|
+
if (!this._acroForm) {
|
|
102
|
+
this._acroForm = new PdfAcroFormManager(this);
|
|
103
|
+
}
|
|
104
|
+
return this._acroForm;
|
|
105
|
+
}
|
|
106
|
+
/** Font manager for embedding and managing fonts */
|
|
107
|
+
get fonts() {
|
|
108
|
+
if (!this._fonts) {
|
|
109
|
+
this._fonts = new PdfFontManager(this);
|
|
110
|
+
}
|
|
111
|
+
return this._fonts;
|
|
112
|
+
}
|
|
107
113
|
/**
|
|
108
114
|
* Creates a PdfDocument from an array of PDF objects.
|
|
109
115
|
* Parses objects into revisions based on EOF comments.
|
|
@@ -157,6 +163,9 @@ export class PdfDocument extends PdfObject {
|
|
|
157
163
|
this.startNewRevision();
|
|
158
164
|
}
|
|
159
165
|
for (const obj of objects) {
|
|
166
|
+
if (obj.isImmutable()) {
|
|
167
|
+
throw new Error('Risky adding a immutable obj');
|
|
168
|
+
}
|
|
160
169
|
this.toBeCommitted.push(obj);
|
|
161
170
|
this.latestRevision.addObject(obj);
|
|
162
171
|
}
|
|
@@ -227,16 +236,24 @@ export class PdfDocument extends PdfObject {
|
|
|
227
236
|
encryptionDictObject.encryptable = false;
|
|
228
237
|
return encryptionDictObject;
|
|
229
238
|
}
|
|
239
|
+
get rootReference() {
|
|
240
|
+
return this.root.reference;
|
|
241
|
+
}
|
|
230
242
|
/**
|
|
231
|
-
* Gets the document catalog (root) dictionary.
|
|
243
|
+
* Gets the document catalog (root) dictionary, or creates one if it doesn't exist.
|
|
232
244
|
*
|
|
233
|
-
* @returns The root dictionary
|
|
245
|
+
* @returns The root dictionary
|
|
234
246
|
* @throws Error if the Root reference points to a non-dictionary object
|
|
235
247
|
*/
|
|
236
|
-
get
|
|
248
|
+
get root() {
|
|
237
249
|
const rootRef = this.trailerDict.get('Root')?.as(PdfObjectReference);
|
|
238
250
|
if (!rootRef) {
|
|
239
|
-
|
|
251
|
+
const rootObject = new PdfIndirectObject({
|
|
252
|
+
content: new PdfDictionary(),
|
|
253
|
+
});
|
|
254
|
+
this.add(rootObject);
|
|
255
|
+
this.trailerDict.set('Root', rootObject.reference);
|
|
256
|
+
return rootObject;
|
|
240
257
|
}
|
|
241
258
|
const rootObject = this.findUncompressedObject(rootRef);
|
|
242
259
|
if (!rootObject) {
|
|
@@ -245,7 +262,7 @@ export class PdfDocument extends PdfObject {
|
|
|
245
262
|
if (!(rootObject?.content instanceof PdfDictionary)) {
|
|
246
263
|
throw new Error(`Root object ${rootRef.objectNumber} ${rootRef.generationNumber} is not a dictionary, it is a ${rootObject?.content.objectType}`);
|
|
247
264
|
}
|
|
248
|
-
return rootObject
|
|
265
|
+
return rootObject;
|
|
249
266
|
}
|
|
250
267
|
/**
|
|
251
268
|
* Gets the reference to the metadata stream from the document catalog.
|
|
@@ -253,11 +270,11 @@ export class PdfDocument extends PdfObject {
|
|
|
253
270
|
* @returns The metadata stream reference or undefined if not present
|
|
254
271
|
*/
|
|
255
272
|
get metadataStreamReference() {
|
|
256
|
-
const root = this.
|
|
273
|
+
const root = this.root;
|
|
257
274
|
if (!root) {
|
|
258
275
|
return;
|
|
259
276
|
}
|
|
260
|
-
const metadataRef = root.get('Metadata')?.as(PdfObjectReference);
|
|
277
|
+
const metadataRef = root.content.get('Metadata')?.as(PdfObjectReference);
|
|
261
278
|
if (!metadataRef) {
|
|
262
279
|
return;
|
|
263
280
|
}
|
|
@@ -501,9 +518,6 @@ export class PdfDocument extends PdfObject {
|
|
|
501
518
|
foundObject = foundObject.clone();
|
|
502
519
|
await this.securityHandler.decryptObject(foundObject);
|
|
503
520
|
}
|
|
504
|
-
else if (this.isIncremental()) {
|
|
505
|
-
foundObject = foundObject.clone(); // Clone to prevent modifications in locked revisions
|
|
506
|
-
}
|
|
507
521
|
return foundObject;
|
|
508
522
|
}
|
|
509
523
|
/**
|
|
@@ -591,7 +605,7 @@ export class PdfDocument extends PdfObject {
|
|
|
591
605
|
* @throws Error if the document has no root dictionary
|
|
592
606
|
*/
|
|
593
607
|
async setDocumentSecurityStore(dss) {
|
|
594
|
-
let rootDictionary = this.
|
|
608
|
+
let rootDictionary = this.root?.content;
|
|
595
609
|
if (!rootDictionary) {
|
|
596
610
|
throw new Error('Cannot set DSS - document has no root dictionary');
|
|
597
611
|
}
|
|
@@ -615,10 +629,6 @@ export class PdfDocument extends PdfObject {
|
|
|
615
629
|
}
|
|
616
630
|
return tokens.map((token) => ({ token, object: obj }));
|
|
617
631
|
});
|
|
618
|
-
const headerTokens = this.header
|
|
619
|
-
.toTokens()
|
|
620
|
-
.map((token) => ({ token, object: this.header }));
|
|
621
|
-
documentTokens.unshift(...headerTokens);
|
|
622
632
|
return documentTokens;
|
|
623
633
|
}
|
|
624
634
|
tokenize() {
|
|
@@ -698,9 +708,7 @@ export class PdfDocument extends PdfObject {
|
|
|
698
708
|
toBytes() {
|
|
699
709
|
this.calculateOffsets();
|
|
700
710
|
this.updateRevisions();
|
|
701
|
-
|
|
702
|
-
serializer.feedMany(this.toTokens());
|
|
703
|
-
return serializer.toBytes();
|
|
711
|
+
return concatUint8Arrays(...this.revisions.map((x) => x.toBytes()));
|
|
704
712
|
}
|
|
705
713
|
/**
|
|
706
714
|
* Serializes the document to a Base64-encoded string.
|
|
@@ -725,7 +733,7 @@ export class PdfDocument extends PdfObject {
|
|
|
725
733
|
const clonedRevisions = this.revisions.map((rev) => rev.clone());
|
|
726
734
|
return new PdfDocument({
|
|
727
735
|
revisions: clonedRevisions,
|
|
728
|
-
version: this.header
|
|
736
|
+
version: this.header?.clone(),
|
|
729
737
|
securityHandler: this.securityHandler,
|
|
730
738
|
});
|
|
731
739
|
}
|
|
@@ -2,6 +2,8 @@ import { PdfDictionary } from '../core/objects/pdf-dictionary.js';
|
|
|
2
2
|
import { PdfObject } from '../core/objects/pdf-object.js';
|
|
3
3
|
import { PdfTrailerEntries } from '../core/objects/pdf-trailer.js';
|
|
4
4
|
import { PdfToken } from '../core/tokens/token.js';
|
|
5
|
+
import { PdfComment } from '../index.js';
|
|
6
|
+
import { ByteArray } from '../types.js';
|
|
5
7
|
import { PdfXrefLookup } from './pdf-xref-lookup.js';
|
|
6
8
|
/**
|
|
7
9
|
* Represents a single revision of a PDF document.
|
|
@@ -9,12 +11,13 @@ import { PdfXrefLookup } from './pdf-xref-lookup.js';
|
|
|
9
11
|
* where each revision contains its own set of objects and cross-reference table.
|
|
10
12
|
*/
|
|
11
13
|
export declare class PdfRevision extends PdfObject {
|
|
12
|
-
/** Objects contained in this revision */
|
|
13
|
-
|
|
14
|
+
/** Objects contained in this revision (private backing field) */
|
|
15
|
+
private _objects;
|
|
16
|
+
/** Whether this revision is locked (private backing field) */
|
|
17
|
+
private _locked;
|
|
14
18
|
/** Cross-reference lookup table for this revision */
|
|
15
19
|
xref: PdfXrefLookup;
|
|
16
|
-
|
|
17
|
-
locked: boolean;
|
|
20
|
+
private cachedBytes?;
|
|
18
21
|
/**
|
|
19
22
|
* Creates a new PDF revision.
|
|
20
23
|
*
|
|
@@ -28,6 +31,29 @@ export declare class PdfRevision extends PdfObject {
|
|
|
28
31
|
prev?: PdfXrefLookup | PdfRevision;
|
|
29
32
|
locked?: boolean;
|
|
30
33
|
});
|
|
34
|
+
get header(): PdfComment | undefined;
|
|
35
|
+
set header(comment: PdfComment);
|
|
36
|
+
/**
|
|
37
|
+
* Gets whether this revision is locked (cannot be modified).
|
|
38
|
+
*/
|
|
39
|
+
get locked(): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Sets whether this revision is locked.
|
|
42
|
+
* When locking, creates a cached clone of all objects to freeze their state.
|
|
43
|
+
* When unlocking, clears the cache.
|
|
44
|
+
*/
|
|
45
|
+
set locked(value: boolean);
|
|
46
|
+
/**
|
|
47
|
+
* Gets the objects in this revision.
|
|
48
|
+
* Returns fresh clones of cached objects if the revision is locked, otherwise returns live objects.
|
|
49
|
+
* Each access to a locked revision's objects returns new clones to prevent mutations.
|
|
50
|
+
*/
|
|
51
|
+
get objects(): ReadonlyArray<PdfObject>;
|
|
52
|
+
/**
|
|
53
|
+
* Sets the objects array.
|
|
54
|
+
* @throws Error if the revision is locked
|
|
55
|
+
*/
|
|
56
|
+
set objects(value: PdfObject[]);
|
|
31
57
|
/**
|
|
32
58
|
* Links this revision to a previous revision's cross-reference table.
|
|
33
59
|
*
|
|
@@ -52,12 +78,14 @@ export declare class PdfRevision extends PdfObject {
|
|
|
52
78
|
* Adds objects to the beginning of the revision's object list.
|
|
53
79
|
*
|
|
54
80
|
* @param objects - Objects to add at the beginning
|
|
81
|
+
* @throws Error if the revision is locked
|
|
55
82
|
*/
|
|
56
83
|
unshift(...objects: PdfObject[]): void;
|
|
57
84
|
/**
|
|
58
85
|
* Adds objects to the revision.
|
|
59
86
|
*
|
|
60
87
|
* @param objects - Objects to add to the revision
|
|
88
|
+
* @throws Error if the revision is locked
|
|
61
89
|
*/
|
|
62
90
|
addObject(...objects: PdfObject[]): void;
|
|
63
91
|
/**
|
|
@@ -98,4 +126,5 @@ export declare class PdfRevision extends PdfObject {
|
|
|
98
126
|
*/
|
|
99
127
|
clone(): this;
|
|
100
128
|
protected tokenize(): PdfToken[];
|
|
129
|
+
toBytes(): ByteArray;
|
|
101
130
|
}
|