pdf-lite 1.6.1 → 1.6.3

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.
@@ -0,0 +1,454 @@
1
+ // ---------------------------------------------------------------------------
2
+ // util.printd — format a Date as a string using Acrobat date tokens
3
+ // ---------------------------------------------------------------------------
4
+ const MONTH_NAMES = [
5
+ 'January',
6
+ 'February',
7
+ 'March',
8
+ 'April',
9
+ 'May',
10
+ 'June',
11
+ 'July',
12
+ 'August',
13
+ 'September',
14
+ 'October',
15
+ 'November',
16
+ 'December',
17
+ ];
18
+ const MONTH_ABBR = [
19
+ 'Jan',
20
+ 'Feb',
21
+ 'Mar',
22
+ 'Apr',
23
+ 'May',
24
+ 'Jun',
25
+ 'Jul',
26
+ 'Aug',
27
+ 'Sep',
28
+ 'Oct',
29
+ 'Nov',
30
+ 'Dec',
31
+ ];
32
+ const DAY_NAMES = [
33
+ 'Sunday',
34
+ 'Monday',
35
+ 'Tuesday',
36
+ 'Wednesday',
37
+ 'Thursday',
38
+ 'Friday',
39
+ 'Saturday',
40
+ ];
41
+ const DAY_ABBR = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
42
+ function pad(n, len) {
43
+ return String(n).padStart(len, '0');
44
+ }
45
+ export function printd(fmt, date) {
46
+ const y = date.getFullYear();
47
+ const M = date.getMonth(); // 0-based
48
+ const d = date.getDate();
49
+ const dow = date.getDay();
50
+ const H = date.getHours();
51
+ const m = date.getMinutes();
52
+ const s = date.getSeconds();
53
+ const h12 = H % 12 || 12;
54
+ // Replace tokens longest-first to avoid partial matches
55
+ return fmt
56
+ .replace(/yyyy/g, String(y))
57
+ .replace(/yy/g, pad(y % 100, 2))
58
+ .replace(/mmmm/g, MONTH_NAMES[M])
59
+ .replace(/mmm/g, MONTH_ABBR[M])
60
+ .replace(/mm/g, pad(M + 1, 2))
61
+ .replace(/(?<![\w])m(?![\w])/g, String(M + 1))
62
+ .replace(/dddd/g, DAY_NAMES[dow])
63
+ .replace(/ddd/g, DAY_ABBR[dow])
64
+ .replace(/dd/g, pad(d, 2))
65
+ .replace(/(?<![\w])d(?![\w])/g, String(d))
66
+ .replace(/HH/g, pad(H, 2))
67
+ .replace(/hh/g, pad(h12, 2))
68
+ .replace(/(?<![\w])h(?![\w])/g, String(h12))
69
+ .replace(/MM/g, pad(m, 2))
70
+ .replace(/ss/g, pad(s, 2))
71
+ .replace(/tt/g, H < 12 ? 'AM' : 'PM')
72
+ .replace(/(?<![\w])t(?![\w])/g, H < 12 ? 'am' : 'pm');
73
+ }
74
+ // ---------------------------------------------------------------------------
75
+ // util.scand — parse a date string using an Acrobat format
76
+ // ---------------------------------------------------------------------------
77
+ export function scand(fmt, str) {
78
+ // Build a regex from the format, capturing groups for each token
79
+ const tokens = [];
80
+ let pattern = '';
81
+ let i = 0;
82
+ while (i < fmt.length) {
83
+ let matched = false;
84
+ for (const tok of [
85
+ 'yyyy',
86
+ 'yy',
87
+ 'mmmm',
88
+ 'mmm',
89
+ 'mm',
90
+ 'm',
91
+ 'dddd',
92
+ 'ddd',
93
+ 'dd',
94
+ 'd',
95
+ 'HH',
96
+ 'hh',
97
+ 'h',
98
+ 'MM',
99
+ 'ss',
100
+ ]) {
101
+ if (fmt.substring(i, i + tok.length) === tok) {
102
+ tokens.push({ token: tok, index: tokens.length });
103
+ switch (tok) {
104
+ case 'yyyy':
105
+ pattern += '(\\d{4})';
106
+ break;
107
+ case 'yy':
108
+ pattern += '(\\d{2})';
109
+ break;
110
+ case 'mmmm':
111
+ pattern += '([A-Za-z]+)';
112
+ break;
113
+ case 'mmm':
114
+ pattern += '([A-Za-z]{3})';
115
+ break;
116
+ case 'mm':
117
+ pattern += '(\\d{2})';
118
+ break;
119
+ case 'm':
120
+ pattern += '(\\d{1,2})';
121
+ break;
122
+ case 'dddd':
123
+ pattern += '([A-Za-z]+)';
124
+ break;
125
+ case 'ddd':
126
+ pattern += '([A-Za-z]{3})';
127
+ break;
128
+ case 'dd':
129
+ pattern += '(\\d{2})';
130
+ break;
131
+ case 'd':
132
+ pattern += '(\\d{1,2})';
133
+ break;
134
+ case 'HH':
135
+ case 'hh':
136
+ pattern += '(\\d{2})';
137
+ break;
138
+ case 'h':
139
+ pattern += '(\\d{1,2})';
140
+ break;
141
+ case 'MM':
142
+ pattern += '(\\d{2})';
143
+ break;
144
+ case 'ss':
145
+ pattern += '(\\d{2})';
146
+ break;
147
+ }
148
+ i += tok.length;
149
+ matched = true;
150
+ break;
151
+ }
152
+ }
153
+ if (!matched) {
154
+ pattern += fmt[i].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
155
+ i++;
156
+ }
157
+ }
158
+ const re = new RegExp('^' + pattern + '$');
159
+ const match = str.match(re);
160
+ if (!match)
161
+ return null;
162
+ let year = 0, month = 0, day = 1, hours = 0, minutes = 0, seconds = 0;
163
+ for (const { token, index } of tokens) {
164
+ const val = match[index + 1];
165
+ switch (token) {
166
+ case 'yyyy':
167
+ year = parseInt(val, 10);
168
+ break;
169
+ case 'yy': {
170
+ const n = parseInt(val, 10);
171
+ year = n < 50 ? 2000 + n : 1900 + n;
172
+ break;
173
+ }
174
+ case 'mmmm': {
175
+ const idx = MONTH_NAMES.findIndex((m) => m.toLowerCase() === val.toLowerCase());
176
+ if (idx >= 0)
177
+ month = idx;
178
+ break;
179
+ }
180
+ case 'mmm': {
181
+ const idx = MONTH_ABBR.findIndex((m) => m.toLowerCase() === val.toLowerCase());
182
+ if (idx >= 0)
183
+ month = idx;
184
+ break;
185
+ }
186
+ case 'mm':
187
+ case 'm':
188
+ month = parseInt(val, 10) - 1;
189
+ break;
190
+ case 'dd':
191
+ case 'd':
192
+ day = parseInt(val, 10);
193
+ break;
194
+ case 'HH':
195
+ case 'hh':
196
+ case 'h':
197
+ hours = parseInt(val, 10);
198
+ break;
199
+ case 'MM':
200
+ minutes = parseInt(val, 10);
201
+ break;
202
+ case 'ss':
203
+ seconds = parseInt(val, 10);
204
+ break;
205
+ // dddd/ddd are day-of-week names — informational only, ignored
206
+ }
207
+ }
208
+ return new Date(year, month, day, hours, minutes, seconds);
209
+ }
210
+ // ---------------------------------------------------------------------------
211
+ // util.printf — sprintf subset: %d, %f, %s with width/precision/flags
212
+ // ---------------------------------------------------------------------------
213
+ export function printf(fmt, ...args) {
214
+ let argIdx = 0;
215
+ return fmt.replace(/%([0 #+-]*)(\d+)?(?:\.(\d+))?([dfsxXo%])/g, (_match, flags, widthStr, precStr, type) => {
216
+ if (type === '%')
217
+ return '%';
218
+ const val = args[argIdx++];
219
+ const width = widthStr ? parseInt(widthStr, 10) : 0;
220
+ const prec = precStr !== undefined ? parseInt(precStr, 10) : undefined;
221
+ const leftAlign = flags.includes('-');
222
+ const zeroPad = flags.includes('0') && !leftAlign;
223
+ const plusSign = flags.includes('+');
224
+ const spaceSign = flags.includes(' ');
225
+ let result;
226
+ switch (type) {
227
+ case 'd': {
228
+ const n = Math.trunc(Number(val));
229
+ const sign = n < 0 ? '-' : plusSign ? '+' : spaceSign ? ' ' : '';
230
+ const digits = String(Math.abs(n));
231
+ result =
232
+ sign +
233
+ (zeroPad
234
+ ? digits.padStart(Math.max(0, width - sign.length), '0')
235
+ : digits);
236
+ break;
237
+ }
238
+ case 'f': {
239
+ const n = Number(val);
240
+ const p = prec !== undefined ? prec : 6;
241
+ const sign = n < 0 ? '-' : plusSign ? '+' : spaceSign ? ' ' : '';
242
+ const formatted = Math.abs(n).toFixed(p);
243
+ result =
244
+ sign +
245
+ (zeroPad
246
+ ? formatted.padStart(Math.max(0, width - sign.length), '0')
247
+ : formatted);
248
+ break;
249
+ }
250
+ case 'x':
251
+ result = (Math.trunc(Number(val)) >>> 0).toString(16);
252
+ break;
253
+ case 'X':
254
+ result = (Math.trunc(Number(val)) >>> 0)
255
+ .toString(16)
256
+ .toUpperCase();
257
+ break;
258
+ case 'o':
259
+ result = (Math.trunc(Number(val)) >>> 0).toString(8);
260
+ break;
261
+ case 's':
262
+ default:
263
+ result = String(val);
264
+ if (prec !== undefined)
265
+ result = result.slice(0, prec);
266
+ break;
267
+ }
268
+ if (result.length < width) {
269
+ result = leftAlign
270
+ ? result.padEnd(width)
271
+ : result.padStart(width, zeroPad ? '0' : ' ');
272
+ }
273
+ return result;
274
+ });
275
+ }
276
+ // ---------------------------------------------------------------------------
277
+ // util object
278
+ // ---------------------------------------------------------------------------
279
+ export const util = { printd, scand, printf };
280
+ function parseNumericValue(str) {
281
+ // Strip currency symbols, spaces, commas
282
+ const cleaned = str.replace(/[^0-9.\-eE]/g, '');
283
+ const n = parseFloat(cleaned);
284
+ return isNaN(n) ? 0 : n;
285
+ }
286
+ function formatNumber(value, nDec, sepStyle, negStyle, _currStyle, strCurrency, bCurrencyPrepend) {
287
+ const absVal = Math.abs(value);
288
+ const fixed = absVal.toFixed(nDec);
289
+ // sepStyle: 0 = 1,234.56 1 = 1234.56 2 = 1.234,56 3 = 1234,56
290
+ let [intPart, decPart] = fixed.split('.');
291
+ const decSep = sepStyle >= 2 ? ',' : '.';
292
+ const thousandSep = sepStyle === 0 ? ',' : sepStyle === 2 ? '.' : '';
293
+ if (thousandSep) {
294
+ intPart = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, thousandSep);
295
+ }
296
+ let formatted = decPart !== undefined ? intPart + decSep + decPart : intPart;
297
+ // Currency
298
+ if (strCurrency) {
299
+ if (bCurrencyPrepend) {
300
+ formatted = strCurrency + formatted;
301
+ }
302
+ else {
303
+ formatted = formatted + strCurrency;
304
+ }
305
+ }
306
+ // Negative style: 0 = -val 1 = red text (we just use -) 2 = (val) 3 = (val) red
307
+ if (value < 0) {
308
+ if (negStyle === 0 || negStyle === 1) {
309
+ formatted = '-' + formatted;
310
+ }
311
+ else {
312
+ formatted = '(' + formatted + ')';
313
+ }
314
+ }
315
+ return formatted;
316
+ }
317
+ export function createBuiltins(event, getFieldValue) {
318
+ // --- AF functions that close over event ---
319
+ function AFDate_FormatEx(fmt) {
320
+ if (!event.value)
321
+ return;
322
+ // Try parsing the current value as a date
323
+ const d = new Date(event.value);
324
+ if (isNaN(d.getTime()))
325
+ return;
326
+ event.value = printd(fmt, d);
327
+ }
328
+ function AFDate_KeystrokeEx(fmt) {
329
+ if (event.willCommit) {
330
+ if (!event.value)
331
+ return;
332
+ const d = scand(fmt, event.value);
333
+ if (!d) {
334
+ event.rc = false;
335
+ }
336
+ }
337
+ // On non-commit keystrokes, allow all input
338
+ }
339
+ function AFNumber_Format(nDec, sepStyle, negStyle, _currStyle, strCurrency, bCurrencyPrepend) {
340
+ if (!event.value && event.value !== '0')
341
+ return;
342
+ const num = parseNumericValue(event.value);
343
+ event.value = formatNumber(num, nDec, sepStyle, negStyle, _currStyle, strCurrency, bCurrencyPrepend);
344
+ }
345
+ function AFNumber_Keystroke(nDec, _sepStyle, _negStyle, _currStyle, _strCurrency, _bCurrencyPrepend) {
346
+ if (event.willCommit) {
347
+ if (!event.value)
348
+ return;
349
+ const num = parseFloat(event.value);
350
+ if (isNaN(num)) {
351
+ event.rc = false;
352
+ return;
353
+ }
354
+ // Check decimal places
355
+ const parts = event.value.split('.');
356
+ if (nDec === 0 && parts.length > 1) {
357
+ event.rc = false;
358
+ }
359
+ }
360
+ }
361
+ function AFSimple_Calculate(op, fields) {
362
+ if (!getFieldValue)
363
+ return;
364
+ const values = fields.map((name) => parseNumericValue(getFieldValue(name)));
365
+ let result;
366
+ switch (op.toUpperCase()) {
367
+ case 'SUM':
368
+ result = values.reduce((a, b) => a + b, 0);
369
+ break;
370
+ case 'AVG':
371
+ result = values.length
372
+ ? values.reduce((a, b) => a + b, 0) / values.length
373
+ : 0;
374
+ break;
375
+ case 'PRD':
376
+ result = values.reduce((a, b) => a * b, 1);
377
+ break;
378
+ case 'MIN':
379
+ result = values.length ? Math.min(...values) : 0;
380
+ break;
381
+ case 'MAX':
382
+ result = values.length ? Math.max(...values) : 0;
383
+ break;
384
+ default:
385
+ return;
386
+ }
387
+ event.value = String(result);
388
+ }
389
+ function AFSpecial_Format(psf) {
390
+ if (!event.value)
391
+ return;
392
+ const digits = event.value.replace(/\D/g, '');
393
+ switch (psf) {
394
+ case 0: // zip: 12345
395
+ event.value = digits.slice(0, 5).padEnd(5, '0');
396
+ break;
397
+ case 1: // ssn: 123-45-6789
398
+ event.value =
399
+ digits.slice(0, 3) +
400
+ '-' +
401
+ digits.slice(3, 5) +
402
+ '-' +
403
+ digits.slice(5, 9);
404
+ break;
405
+ case 2: // phone: (123) 456-7890
406
+ event.value =
407
+ '(' +
408
+ digits.slice(0, 3) +
409
+ ') ' +
410
+ digits.slice(3, 6) +
411
+ '-' +
412
+ digits.slice(6, 10);
413
+ break;
414
+ case 3: // zip+4: 12345-6789
415
+ event.value = digits.slice(0, 5) + '-' + digits.slice(5, 9);
416
+ break;
417
+ }
418
+ }
419
+ function AFSpecial_Keystroke(psf) {
420
+ if (!event.willCommit)
421
+ return;
422
+ if (!event.value)
423
+ return;
424
+ const digits = event.value.replace(/\D/g, '');
425
+ switch (psf) {
426
+ case 0: // zip
427
+ if (digits.length !== 5)
428
+ event.rc = false;
429
+ break;
430
+ case 1: // ssn
431
+ if (digits.length !== 9)
432
+ event.rc = false;
433
+ break;
434
+ case 2: // phone
435
+ if (digits.length !== 10 && digits.length !== 7)
436
+ event.rc = false;
437
+ break;
438
+ case 3: // zip+4
439
+ if (digits.length !== 9)
440
+ event.rc = false;
441
+ break;
442
+ }
443
+ }
444
+ return {
445
+ util,
446
+ AFDate_FormatEx,
447
+ AFDate_KeystrokeEx,
448
+ AFNumber_Format,
449
+ AFNumber_Keystroke,
450
+ AFSimple_Calculate,
451
+ AFSpecial_Format,
452
+ AFSpecial_Keystroke,
453
+ };
454
+ }
@@ -0,0 +1,18 @@
1
+ import type { PdfJsEngine, PdfJsEvent } from './pdf-js-engine.js';
2
+ export interface PdfJavaScriptEngineOptions {
3
+ getFieldValue?: (name: string) => string;
4
+ }
5
+ /**
6
+ * Default JavaScript engine that executes PDF JS actions via `new Function()`.
7
+ *
8
+ * **Security note:** This engine runs PDF-sourced JavaScript with access to the
9
+ * ambient JS environment (e.g. `globalThis`, constructors, network APIs). It is
10
+ * NOT sandboxed and should only be used with trusted PDF documents. For untrusted
11
+ * documents, provide your own `PdfJsEngine` implementation that evaluates code
12
+ * in an isolated context (e.g. Node `vm`, a dedicated realm, or a Web Worker).
13
+ */
14
+ export declare class PdfJavaScriptEngine implements PdfJsEngine {
15
+ private _getFieldValue?;
16
+ constructor(options?: PdfJavaScriptEngineOptions);
17
+ execute(code: string, event: PdfJsEvent): void;
18
+ }
@@ -0,0 +1,22 @@
1
+ import { createBuiltins } from './pdf-js-builtins.js';
2
+ /**
3
+ * Default JavaScript engine that executes PDF JS actions via `new Function()`.
4
+ *
5
+ * **Security note:** This engine runs PDF-sourced JavaScript with access to the
6
+ * ambient JS environment (e.g. `globalThis`, constructors, network APIs). It is
7
+ * NOT sandboxed and should only be used with trusted PDF documents. For untrusted
8
+ * documents, provide your own `PdfJsEngine` implementation that evaluates code
9
+ * in an isolated context (e.g. Node `vm`, a dedicated realm, or a Web Worker).
10
+ */
11
+ export class PdfJavaScriptEngine {
12
+ _getFieldValue;
13
+ constructor(options) {
14
+ this._getFieldValue = options?.getFieldValue;
15
+ }
16
+ execute(code, event) {
17
+ const builtins = createBuiltins(event, this._getFieldValue);
18
+ const names = Object.keys(builtins);
19
+ const fn = new Function('event', 'app', ...names, code);
20
+ fn(event, Object.freeze({ alert() { } }), ...names.map((k) => builtins[k]));
21
+ }
22
+ }
@@ -0,0 +1,10 @@
1
+ export interface PdfJsEvent {
2
+ fieldName: string;
3
+ value: string;
4
+ willCommit?: boolean;
5
+ rc: boolean;
6
+ changeEx?: string;
7
+ }
8
+ export interface PdfJsEngine {
9
+ execute(code: string, event: PdfJsEvent): void;
10
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -12,6 +12,7 @@ import './fields/pdf-choice-form-field.js';
12
12
  import './fields/pdf-signature-form-field.js';
13
13
  import { PdfDefaultResourcesDictionary } from '../annotations/pdf-default-resources.js';
14
14
  import { PdfXfaForm } from './xfa/pdf-xfa-form.js';
15
+ import type { PdfJsEngine } from './js/pdf-js-engine.js';
15
16
  export declare class PdfAcroForm<T extends Record<string, string> = Record<string, string>> extends PdfIndirectObject<PdfDictionary<{
16
17
  Fields: PdfArray<PdfObjectReference> | PdfObjectReference;
17
18
  NeedAppearances?: PdfBoolean;
@@ -38,7 +39,10 @@ export declare class PdfAcroForm<T extends Record<string, string> = Record<strin
38
39
  set fields(newFields: PdfFormField[]);
39
40
  setValues(values: Partial<T>): void;
40
41
  importData(fields: T): void;
42
+ private _fireValidate;
43
+ private _fireCalculate;
41
44
  exportData(): Partial<T>;
45
+ jsEngine?: PdfJsEngine;
42
46
  fontEncodingMaps: Map<string, Map<number, string>>;
43
47
  getFontEncodingMap(fontName: string): Map<number, string> | null;
44
48
  get xfa(): PdfXfaForm | null;
@@ -122,15 +122,70 @@ export class PdfAcroForm extends PdfIndirectObject {
122
122
  for (const field of this.fields) {
123
123
  const name = field.name;
124
124
  if (name in values && values[name] !== undefined) {
125
- field.value = values[name];
125
+ const result = this._fireValidate(field, values[name]);
126
+ if (!result.rc)
127
+ continue;
128
+ field.value = result.value;
126
129
  }
127
130
  }
131
+ this._fireCalculate();
128
132
  }
129
133
  importData(fields) {
130
134
  for (const field of this.fields) {
131
135
  const name = field.name;
132
136
  if (name && name in fields) {
133
- field.value = fields[name];
137
+ const result = this._fireValidate(field, fields[name]);
138
+ if (!result.rc)
139
+ continue;
140
+ field.value = result.value;
141
+ }
142
+ }
143
+ this._fireCalculate();
144
+ }
145
+ _fireValidate(field, value) {
146
+ const validateAction = field.actions?.validate;
147
+ if (!validateAction?.code)
148
+ return { rc: true, value };
149
+ const event = {
150
+ fieldName: field.name,
151
+ value,
152
+ willCommit: true,
153
+ rc: true,
154
+ };
155
+ validateAction.execute(event);
156
+ return { rc: event.rc, value: event.value };
157
+ }
158
+ _fireCalculate() {
159
+ const co = this.content.get('CO');
160
+ if (!(co instanceof PdfArray))
161
+ return;
162
+ const allFields = this.fields;
163
+ const fieldsByObjNum = new Map();
164
+ for (const field of allFields) {
165
+ if (field.objectNumber != null) {
166
+ fieldsByObjNum.set(field.objectNumber, field);
167
+ }
168
+ }
169
+ for (const item of co.items) {
170
+ if (!(item instanceof PdfObjectReference))
171
+ continue;
172
+ const resolved = item.resolve();
173
+ if (!resolved)
174
+ continue;
175
+ const field = fieldsByObjNum.get(resolved.objectNumber);
176
+ if (!field)
177
+ continue;
178
+ const calcAction = field.actions?.calculate;
179
+ if (!calcAction?.code)
180
+ continue;
181
+ const event = {
182
+ fieldName: field.name,
183
+ value: field.value,
184
+ rc: true,
185
+ };
186
+ calcAction.execute(event);
187
+ if (event.rc && event.value !== field.value) {
188
+ field.value = event.value;
134
189
  }
135
190
  }
136
191
  }
@@ -144,6 +199,7 @@ export class PdfAcroForm extends PdfIndirectObject {
144
199
  }
145
200
  return result;
146
201
  }
202
+ jsEngine;
147
203
  fontEncodingMaps = new Map();
148
204
  getFontEncodingMap(fontName) {
149
205
  if (this.fontEncodingMaps.has(fontName)) {
@@ -34,6 +34,26 @@ export declare class PdfAnnotation extends PdfIndirectObject<PdfDictionary<{
34
34
  AP?: PdfAppearanceStreamDictionary;
35
35
  P?: PdfObjectReference;
36
36
  Parent?: PdfObjectReference<PdfPage>;
37
+ A?: PdfDictionary<{
38
+ Type?: PdfName;
39
+ S?: PdfName;
40
+ D?: PdfArray | PdfDictionary;
41
+ T?: PdfString;
42
+ F?: PdfNumber;
43
+ Win?: PdfDictionary;
44
+ JS?: PdfString;
45
+ }>;
46
+ AA?: PdfDictionary<{
47
+ D?: PdfObjectReference | PdfDictionary;
48
+ U?: PdfObjectReference | PdfDictionary;
49
+ Fo?: PdfObjectReference | PdfDictionary;
50
+ Bl?: PdfObjectReference | PdfDictionary;
51
+ PO?: PdfObjectReference | PdfDictionary;
52
+ K?: PdfObjectReference | PdfDictionary;
53
+ V?: PdfObjectReference | PdfDictionary;
54
+ C?: PdfObjectReference | PdfDictionary;
55
+ F?: PdfObjectReference | PdfDictionary;
56
+ }>;
37
57
  }>> {
38
58
  private _annotationFlags?;
39
59
  private get flags_();
@@ -22,6 +22,8 @@ export declare class PdfDictionary<T extends PdfDictionaryEntries = PdfDictionar
22
22
  entries(): IterableIterator<[string, PdfObject | undefined]>;
23
23
  get isTrailingDelimited(): boolean;
24
24
  protected tokenize(): PdfToken[];
25
+ /** Factory-style type conversion: constructs a new instance passing `this` as the first argument */
26
+ becomes<U extends PdfDictionary, A extends unknown[]>(cls: new (source: PdfDictionary, ...args: A) => U, ...args: A): U;
25
27
  copyFrom(other: PdfDictionary<any>): void;
26
28
  cloneImpl(): this;
27
29
  setModified(modified?: boolean): void;
@@ -131,6 +131,20 @@ export class PdfDictionary extends PdfObject {
131
131
  new PdfEndDictionaryToken(),
132
132
  ];
133
133
  }
134
+ /** Factory-style type conversion: constructs a new instance passing `this` as the first argument */
135
+ becomes(cls, ...args) {
136
+ if (this instanceof cls)
137
+ return this;
138
+ const donor = new cls(this, ...args);
139
+ Object.setPrototypeOf(this, cls.prototype);
140
+ for (const key of Object.getOwnPropertyNames(donor)) {
141
+ if (!Object.prototype.hasOwnProperty.call(this, key)) {
142
+ ;
143
+ this[key] = donor[key];
144
+ }
145
+ }
146
+ return this;
147
+ }
134
148
  copyFrom(other) {
135
149
  for (const [key, value] of other.#entries) {
136
150
  this.#entries.set(key, value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdf-lite",
3
- "version": "1.6.1",
3
+ "version": "1.6.3",
4
4
  "main": "dist/index.js",
5
5
  "type": "module",
6
6
  "exports": {