pdf-oxide 0.3.37 → 0.3.39
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/lib/builders/document-builder.d.ts +350 -0
- package/lib/builders/document-builder.js +724 -0
- package/lib/builders/index.d.ts +4 -2
- package/lib/builders/index.js +4 -2
- package/lib/builders/pdf-builder.d.ts +2 -0
- package/lib/builders/pdf-builder.js +12 -0
- package/lib/builders/streaming-table.d.ts +49 -0
- package/lib/builders/streaming-table.js +110 -0
- package/lib/document-editor.d.ts +122 -0
- package/lib/document-editor.js +313 -0
- package/lib/errors.js +3 -4
- package/lib/form-field-manager.js +3 -1
- package/lib/index.d.ts +41 -7
- package/lib/index.js +266 -90
- package/lib/managers/accessibility-manager.js +19 -8
- package/lib/managers/annotation-manager.js +9 -9
- package/lib/managers/barcode-manager.js +18 -7
- package/lib/managers/batch-manager.js +2 -5
- package/lib/managers/cache-manager.js +1 -3
- package/lib/managers/compliance-manager.js +58 -19
- package/lib/managers/document-utility-manager.js +6 -6
- package/lib/managers/dom-pdf-creator.js +9 -9
- package/lib/managers/enterprise-manager.js +4 -1
- package/lib/managers/extended-managers.js +8 -1
- package/lib/managers/extraction-manager.js +7 -2
- package/lib/managers/final-utilities.d.ts +3 -3
- package/lib/managers/final-utilities.js +9 -4
- package/lib/managers/hybrid-ml-advanced.js +22 -6
- package/lib/managers/index.d.ts +22 -22
- package/lib/managers/index.js +23 -23
- package/lib/managers/layer-manager.js +20 -21
- package/lib/managers/ocr-manager.d.ts +2 -2
- package/lib/managers/ocr-manager.js +7 -7
- package/lib/managers/optimization-manager.js +24 -4
- package/lib/managers/page-manager.js +5 -6
- package/lib/managers/pattern-detection.d.ts +1 -1
- package/lib/managers/pattern-detection.js +4 -6
- package/lib/managers/search-manager.js +3 -3
- package/lib/managers/signature-manager.d.ts +14 -0
- package/lib/managers/signature-manager.js +185 -40
- package/lib/managers/streams.js +8 -2
- package/lib/managers/xfa-manager.js +69 -19
- package/lib/native-loader.d.ts +7 -0
- package/lib/native-loader.js +62 -0
- package/lib/native.d.ts +16 -0
- package/lib/native.js +69 -0
- package/lib/pdf-creator-manager.js +4 -1
- package/lib/result-accessors-manager.js +3 -1
- package/lib/timestamp.d.ts +54 -0
- package/lib/timestamp.js +115 -0
- package/lib/tsa-client.d.ts +44 -0
- package/lib/tsa-client.js +67 -0
- package/lib/types/common.d.ts +80 -1
- package/lib/types/common.js +14 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/index.js +1 -1
- package/lib/types/manager-types.js +4 -2
- package/lib/workers/index.d.ts +1 -1
- package/lib/workers/pool.js +2 -4
- package/package.json +17 -11
- package/prebuilds/darwin-arm64/pdf_oxide.node +0 -0
- package/prebuilds/darwin-x64/pdf_oxide.node +0 -0
- package/prebuilds/linux-arm64/pdf_oxide.node +0 -0
- package/prebuilds/linux-x64/pdf_oxide.node +0 -0
- package/prebuilds/win32-x64/pdf_oxide.node +0 -0
|
@@ -179,7 +179,9 @@ export class SignatureManager extends EventEmitter {
|
|
|
179
179
|
try {
|
|
180
180
|
result = JSON.parse(this.native.verify_signatures());
|
|
181
181
|
}
|
|
182
|
-
catch {
|
|
182
|
+
catch {
|
|
183
|
+
/* defaults */
|
|
184
|
+
}
|
|
183
185
|
}
|
|
184
186
|
this.setCached(cacheKey, result);
|
|
185
187
|
this.emit('signaturesVerified', { isValid: result.isValid, issueCount: result.issues.length });
|
|
@@ -194,7 +196,9 @@ export class SignatureManager extends EventEmitter {
|
|
|
194
196
|
try {
|
|
195
197
|
result = JSON.parse(this.native.verify_signature(signatureName));
|
|
196
198
|
}
|
|
197
|
-
catch {
|
|
199
|
+
catch {
|
|
200
|
+
/* defaults */
|
|
201
|
+
}
|
|
198
202
|
}
|
|
199
203
|
this.setCached(cacheKey, result);
|
|
200
204
|
return result;
|
|
@@ -236,12 +240,20 @@ export class SignatureManager extends EventEmitter {
|
|
|
236
240
|
const certId = `cert_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
237
241
|
const nativeResult = await this.document?.loadCertificate?.(certData, password, format);
|
|
238
242
|
const info = nativeResult?.info ?? {
|
|
239
|
-
subject: nativeResult?.subject ?? 'Unknown',
|
|
240
|
-
|
|
243
|
+
subject: nativeResult?.subject ?? 'Unknown',
|
|
244
|
+
issuer: nativeResult?.issuer ?? 'Unknown',
|
|
245
|
+
serialNumber: nativeResult?.serialNumber ?? '',
|
|
246
|
+
validFrom: new Date(nativeResult?.validFrom ?? Date.now()),
|
|
241
247
|
validTo: new Date(nativeResult?.validTo ?? Date.now() + 365 * 24 * 60 * 60 * 1000),
|
|
242
|
-
isValid: nativeResult?.isValid ?? false,
|
|
248
|
+
isValid: nativeResult?.isValid ?? false,
|
|
249
|
+
isSelfSigned: nativeResult?.isSelfSigned ?? false,
|
|
250
|
+
};
|
|
251
|
+
const loadedCert = {
|
|
252
|
+
certificateId: certId,
|
|
253
|
+
info,
|
|
254
|
+
hasPrivateKey: nativeResult?.hasPrivateKey ?? true,
|
|
255
|
+
chain: nativeResult?.chain,
|
|
243
256
|
};
|
|
244
|
-
const loadedCert = { certificateId: certId, info, hasPrivateKey: nativeResult?.hasPrivateKey ?? true, chain: nativeResult?.chain };
|
|
245
257
|
this.loadedCertificates.set(certId, loadedCert);
|
|
246
258
|
this.emit('certificate-loaded', { certificateId: certId, subject: info.subject });
|
|
247
259
|
return loadedCert;
|
|
@@ -258,12 +270,19 @@ export class SignatureManager extends EventEmitter {
|
|
|
258
270
|
if (!nativeResult)
|
|
259
271
|
throw new Error('Failed to load PEM certificate');
|
|
260
272
|
const info = {
|
|
261
|
-
subject: nativeResult.subject ?? 'Unknown',
|
|
262
|
-
|
|
273
|
+
subject: nativeResult.subject ?? 'Unknown',
|
|
274
|
+
issuer: nativeResult.issuer ?? 'Unknown',
|
|
275
|
+
serialNumber: nativeResult.serialNumber ?? '',
|
|
276
|
+
validFrom: new Date(nativeResult.validFrom ?? Date.now()),
|
|
263
277
|
validTo: new Date(nativeResult.validTo ?? Date.now() + 365 * 24 * 60 * 60 * 1000),
|
|
264
|
-
isValid: nativeResult.isValid ?? false,
|
|
278
|
+
isValid: nativeResult.isValid ?? false,
|
|
279
|
+
isSelfSigned: nativeResult.isSelfSigned ?? false,
|
|
280
|
+
};
|
|
281
|
+
const loadedCert = {
|
|
282
|
+
certificateId: certId,
|
|
283
|
+
info,
|
|
284
|
+
hasPrivateKey: !!privateKeyPem,
|
|
265
285
|
};
|
|
266
|
-
const loadedCert = { certificateId: certId, info, hasPrivateKey: !!privateKeyPem };
|
|
267
286
|
this.loadedCertificates.set(certId, loadedCert);
|
|
268
287
|
this.emit('certificate-loaded', { certificateId: certId, subject: info.subject });
|
|
269
288
|
return loadedCert;
|
|
@@ -283,7 +302,7 @@ export class SignatureManager extends EventEmitter {
|
|
|
283
302
|
return null;
|
|
284
303
|
if (cert.chain)
|
|
285
304
|
return cert.chain;
|
|
286
|
-
return await this.document?.getCertificateChain?.(certificateId) ?? null;
|
|
305
|
+
return (await this.document?.getCertificateChain?.(certificateId)) ?? null;
|
|
287
306
|
}
|
|
288
307
|
catch (error) {
|
|
289
308
|
this.emit('error', error);
|
|
@@ -296,11 +315,19 @@ export class SignatureManager extends EventEmitter {
|
|
|
296
315
|
if (!cert)
|
|
297
316
|
return { valid: false, errors: ['Certificate not found'], warnings: [] };
|
|
298
317
|
const result = await this.document?.validateCertificate?.(certificateId);
|
|
299
|
-
return result ?? {
|
|
318
|
+
return (result ?? {
|
|
319
|
+
valid: cert.info.isValid,
|
|
320
|
+
errors: cert.info.isValid ? [] : ['Certificate validation failed'],
|
|
321
|
+
warnings: [],
|
|
322
|
+
});
|
|
300
323
|
}
|
|
301
324
|
catch (error) {
|
|
302
325
|
this.emit('error', error);
|
|
303
|
-
return {
|
|
326
|
+
return {
|
|
327
|
+
valid: false,
|
|
328
|
+
errors: [error instanceof Error ? error.message : 'Unknown error'],
|
|
329
|
+
warnings: [],
|
|
330
|
+
};
|
|
304
331
|
}
|
|
305
332
|
}
|
|
306
333
|
getLoadedCertificates() {
|
|
@@ -317,7 +344,12 @@ export class SignatureManager extends EventEmitter {
|
|
|
317
344
|
// ===========================================================================
|
|
318
345
|
async addSignatureField(config) {
|
|
319
346
|
try {
|
|
320
|
-
const result = await this.document?.addSignatureField?.(config.fieldName, config.pageIndex, config.x, config.y, config.width, config.height, {
|
|
347
|
+
const result = await this.document?.addSignatureField?.(config.fieldName, config.pageIndex, config.x, config.y, config.width, config.height, {
|
|
348
|
+
appearance: config.appearance,
|
|
349
|
+
tooltip: config.tooltip,
|
|
350
|
+
isRequired: config.isRequired,
|
|
351
|
+
isReadOnly: config.isReadOnly,
|
|
352
|
+
});
|
|
321
353
|
if (result !== false) {
|
|
322
354
|
this.createdFields.set(config.fieldName, config);
|
|
323
355
|
this.clearCachePattern('signatures:');
|
|
@@ -347,7 +379,7 @@ export class SignatureManager extends EventEmitter {
|
|
|
347
379
|
}
|
|
348
380
|
async getSignatureFieldNames() {
|
|
349
381
|
try {
|
|
350
|
-
return await this.document?.getSignatureFields?.() ?? [];
|
|
382
|
+
return (await this.document?.getSignatureFields?.()) ?? [];
|
|
351
383
|
}
|
|
352
384
|
catch (error) {
|
|
353
385
|
this.emit('error', error);
|
|
@@ -387,7 +419,9 @@ export class SignatureManager extends EventEmitter {
|
|
|
387
419
|
if (!cert.hasPrivateKey)
|
|
388
420
|
return { success: false, error: 'Certificate does not have private key' };
|
|
389
421
|
const nativeResult = await this.document?.signDocument?.(fieldName, certId, {
|
|
390
|
-
reason: options?.reason,
|
|
422
|
+
reason: options?.reason,
|
|
423
|
+
location: options?.location,
|
|
424
|
+
contactInfo: options?.contactInfo,
|
|
391
425
|
signatureType: options?.signatureType ?? SignatureType.APPROVAL,
|
|
392
426
|
certificationPermission: options?.certificationPermission,
|
|
393
427
|
algorithm: options?.algorithm ?? SignatureAlgorithm.RSA_SHA256,
|
|
@@ -397,16 +431,28 @@ export class SignatureManager extends EventEmitter {
|
|
|
397
431
|
const signingTime = new Date();
|
|
398
432
|
let timestampTime;
|
|
399
433
|
if (options?.embedTimestamp && options?.timestampServerUrl) {
|
|
400
|
-
const tsResult = await this.embedTimestamp(fieldName, {
|
|
434
|
+
const tsResult = await this.embedTimestamp(fieldName, {
|
|
435
|
+
serverUrl: options.timestampServerUrl,
|
|
436
|
+
hashAlgorithm: options.digestAlgorithm,
|
|
437
|
+
});
|
|
401
438
|
if (tsResult.status === TimestampStatus.SUCCESS)
|
|
402
439
|
timestampTime = tsResult.timestamp;
|
|
403
440
|
}
|
|
404
441
|
if (options?.enableLtv)
|
|
405
|
-
await this.enableLtvForSignature(fieldName, {
|
|
442
|
+
await this.enableLtvForSignature(fieldName, {
|
|
443
|
+
ocspResponderUrl: options.ocspResponderUrl,
|
|
444
|
+
crlDistributionPoints: options.crlDistributionPoints,
|
|
445
|
+
});
|
|
406
446
|
const signatureId = `sig_${fieldName}_${Date.now()}`;
|
|
407
447
|
this.clearCachePattern('signatures:');
|
|
408
448
|
this.emit('document-signed', { signatureId, fieldName, signingTime, timestampTime });
|
|
409
|
-
return {
|
|
449
|
+
return {
|
|
450
|
+
success: true,
|
|
451
|
+
signatureId,
|
|
452
|
+
signingTime,
|
|
453
|
+
timestampTime,
|
|
454
|
+
warnings: nativeResult?.warnings,
|
|
455
|
+
};
|
|
410
456
|
}
|
|
411
457
|
catch (error) {
|
|
412
458
|
this.emit('error', error);
|
|
@@ -414,7 +460,11 @@ export class SignatureManager extends EventEmitter {
|
|
|
414
460
|
}
|
|
415
461
|
}
|
|
416
462
|
async certifyDocument(fieldName, certificate, permission, options) {
|
|
417
|
-
return this.signDocument(fieldName, certificate, {
|
|
463
|
+
return this.signDocument(fieldName, certificate, {
|
|
464
|
+
...options,
|
|
465
|
+
signatureType: SignatureType.CERTIFICATION,
|
|
466
|
+
certificationPermission: permission,
|
|
467
|
+
});
|
|
418
468
|
}
|
|
419
469
|
async signInvisibly(certificate, options) {
|
|
420
470
|
try {
|
|
@@ -430,7 +480,15 @@ export class SignatureManager extends EventEmitter {
|
|
|
430
480
|
async counterSign(certificate, options) {
|
|
431
481
|
try {
|
|
432
482
|
const fieldName = `CounterSig_${Date.now()}`;
|
|
433
|
-
await this.addSignatureField({
|
|
483
|
+
await this.addSignatureField({
|
|
484
|
+
fieldName,
|
|
485
|
+
pageIndex: 0,
|
|
486
|
+
x: 100,
|
|
487
|
+
y: 100,
|
|
488
|
+
width: 200,
|
|
489
|
+
height: 50,
|
|
490
|
+
appearance: options?.appearance,
|
|
491
|
+
});
|
|
434
492
|
return this.signDocument(fieldName, certificate, options);
|
|
435
493
|
}
|
|
436
494
|
catch (error) {
|
|
@@ -450,7 +508,10 @@ export class SignatureManager extends EventEmitter {
|
|
|
450
508
|
}
|
|
451
509
|
async prepareForExternalSigning(fieldName, options) {
|
|
452
510
|
try {
|
|
453
|
-
const result = await this.document?.prepareForExternalSigning?.(fieldName, {
|
|
511
|
+
const result = await this.document?.prepareForExternalSigning?.(fieldName, {
|
|
512
|
+
estimatedSize: options?.estimatedSize ?? 8192,
|
|
513
|
+
digestAlgorithm: options?.digestAlgorithm ?? DigestAlgorithm.SHA256,
|
|
514
|
+
});
|
|
454
515
|
this.emit('prepared-for-external-signing', { fieldName });
|
|
455
516
|
return result ?? null;
|
|
456
517
|
}
|
|
@@ -464,16 +525,36 @@ export class SignatureManager extends EventEmitter {
|
|
|
464
525
|
// ===========================================================================
|
|
465
526
|
async embedTimestamp(fieldName, config) {
|
|
466
527
|
try {
|
|
467
|
-
const result = await this.document?.embedTimestamp?.(fieldName, {
|
|
528
|
+
const result = await this.document?.embedTimestamp?.(fieldName, {
|
|
529
|
+
serverUrl: config.serverUrl,
|
|
530
|
+
username: config.username,
|
|
531
|
+
password: config.password,
|
|
532
|
+
hashAlgorithm: config.hashAlgorithm ?? DigestAlgorithm.SHA256,
|
|
533
|
+
timeout: config.timeout ?? 30000,
|
|
534
|
+
policy: config.policy,
|
|
535
|
+
nonce: config.nonce ?? true,
|
|
536
|
+
certReq: config.certReq ?? true,
|
|
537
|
+
});
|
|
468
538
|
if (result?.success) {
|
|
469
539
|
this.emit('timestamp-embedded', { fieldName, timestamp: result.timestamp });
|
|
470
|
-
return {
|
|
540
|
+
return {
|
|
541
|
+
status: TimestampStatus.SUCCESS,
|
|
542
|
+
timestamp: new Date(result.timestamp),
|
|
543
|
+
serialNumber: result.serialNumber,
|
|
544
|
+
tsaName: result.tsaName,
|
|
545
|
+
};
|
|
471
546
|
}
|
|
472
|
-
return {
|
|
547
|
+
return {
|
|
548
|
+
status: TimestampStatus.FAILED,
|
|
549
|
+
error: result?.error ?? 'Timestamp embedding failed',
|
|
550
|
+
};
|
|
473
551
|
}
|
|
474
552
|
catch (error) {
|
|
475
553
|
this.emit('error', error);
|
|
476
|
-
return {
|
|
554
|
+
return {
|
|
555
|
+
status: TimestampStatus.FAILED,
|
|
556
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
557
|
+
};
|
|
477
558
|
}
|
|
478
559
|
}
|
|
479
560
|
async addDocumentTimestamp(config) {
|
|
@@ -483,18 +564,32 @@ export class SignatureManager extends EventEmitter {
|
|
|
483
564
|
const result = await this.document?.addDocumentTimestamp?.(fieldName, config);
|
|
484
565
|
if (result?.success) {
|
|
485
566
|
this.emit('document-timestamp-added', { timestamp: result.timestamp });
|
|
486
|
-
return {
|
|
567
|
+
return {
|
|
568
|
+
status: TimestampStatus.SUCCESS,
|
|
569
|
+
timestamp: new Date(result.timestamp),
|
|
570
|
+
serialNumber: result.serialNumber,
|
|
571
|
+
tsaName: result.tsaName,
|
|
572
|
+
};
|
|
487
573
|
}
|
|
488
|
-
return {
|
|
574
|
+
return {
|
|
575
|
+
status: TimestampStatus.FAILED,
|
|
576
|
+
error: result?.error ?? 'Document timestamp failed',
|
|
577
|
+
};
|
|
489
578
|
}
|
|
490
579
|
catch (error) {
|
|
491
580
|
this.emit('error', error);
|
|
492
|
-
return {
|
|
581
|
+
return {
|
|
582
|
+
status: TimestampStatus.FAILED,
|
|
583
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
584
|
+
};
|
|
493
585
|
}
|
|
494
586
|
}
|
|
495
587
|
async validateTimestamp(fieldName) {
|
|
496
588
|
try {
|
|
497
|
-
return await this.document?.validateTimestamp?.(fieldName) ?? {
|
|
589
|
+
return ((await this.document?.validateTimestamp?.(fieldName)) ?? {
|
|
590
|
+
valid: false,
|
|
591
|
+
errors: ['Timestamp validation not available'],
|
|
592
|
+
});
|
|
498
593
|
}
|
|
499
594
|
catch (error) {
|
|
500
595
|
this.emit('error', error);
|
|
@@ -503,7 +598,7 @@ export class SignatureManager extends EventEmitter {
|
|
|
503
598
|
}
|
|
504
599
|
async getTimestampInfo(fieldName) {
|
|
505
600
|
try {
|
|
506
|
-
return await this.document?.getTimestampInfo?.(fieldName) ?? null;
|
|
601
|
+
return (await this.document?.getTimestampInfo?.(fieldName)) ?? null;
|
|
507
602
|
}
|
|
508
603
|
catch (error) {
|
|
509
604
|
this.emit('error', error);
|
|
@@ -515,7 +610,10 @@ export class SignatureManager extends EventEmitter {
|
|
|
515
610
|
// ===========================================================================
|
|
516
611
|
async enableLtvForSignature(fieldName, options) {
|
|
517
612
|
try {
|
|
518
|
-
const result = await this.document?.enableLtvForSignature?.(fieldName, {
|
|
613
|
+
const result = await this.document?.enableLtvForSignature?.(fieldName, {
|
|
614
|
+
ocspResponderUrl: options?.ocspResponderUrl,
|
|
615
|
+
crlDistributionPoints: options?.crlDistributionPoints,
|
|
616
|
+
});
|
|
519
617
|
if (result)
|
|
520
618
|
this.emit('ltv-enabled', { fieldName });
|
|
521
619
|
return !!result;
|
|
@@ -554,7 +652,7 @@ export class SignatureManager extends EventEmitter {
|
|
|
554
652
|
}
|
|
555
653
|
async hasLtvEnabled(fieldName) {
|
|
556
654
|
try {
|
|
557
|
-
return await this.document?.hasLtvEnabled?.(fieldName) ?? false;
|
|
655
|
+
return (await this.document?.hasLtvEnabled?.(fieldName)) ?? false;
|
|
558
656
|
}
|
|
559
657
|
catch (error) {
|
|
560
658
|
this.emit('error', error);
|
|
@@ -632,15 +730,20 @@ export class SignatureManager extends EventEmitter {
|
|
|
632
730
|
try {
|
|
633
731
|
const [notBefore, notAfter] = await this.getCertificateValidity(index);
|
|
634
732
|
const certificate = {
|
|
635
|
-
subject: await this.getCertificateSubject(index),
|
|
636
|
-
|
|
733
|
+
subject: await this.getCertificateSubject(index),
|
|
734
|
+
issuer: await this.getCertificateIssuer(index),
|
|
735
|
+
serial: await this.getCertificateSerial(index),
|
|
736
|
+
notBefore,
|
|
737
|
+
notAfter,
|
|
637
738
|
isValid: await this.isCertificateValidByIndex(index),
|
|
638
739
|
};
|
|
639
740
|
return {
|
|
640
|
-
signerName: await this.getSignerName(index),
|
|
741
|
+
signerName: await this.getSignerName(index),
|
|
742
|
+
signingTime: await this.getSigningTime(index),
|
|
641
743
|
reason: (await this.getSigningReason(index)) || undefined,
|
|
642
744
|
location: (await this.getSigningLocation(index)) || undefined,
|
|
643
|
-
certificate,
|
|
745
|
+
certificate,
|
|
746
|
+
isValid: true,
|
|
644
747
|
};
|
|
645
748
|
}
|
|
646
749
|
catch (error) {
|
|
@@ -1339,7 +1442,11 @@ export class SignatureManager extends EventEmitter {
|
|
|
1339
1442
|
this.emit('cacheCleared');
|
|
1340
1443
|
}
|
|
1341
1444
|
getCacheStats() {
|
|
1342
|
-
return {
|
|
1445
|
+
return {
|
|
1446
|
+
cacheSize: this.resultCache.size,
|
|
1447
|
+
maxCacheSize: this.maxCacheSize,
|
|
1448
|
+
entries: Array.from(this.resultCache.keys()),
|
|
1449
|
+
};
|
|
1343
1450
|
}
|
|
1344
1451
|
destroy() {
|
|
1345
1452
|
this.loadedCertificates.clear();
|
|
@@ -1347,6 +1454,42 @@ export class SignatureManager extends EventEmitter {
|
|
|
1347
1454
|
this.resultCache.clear();
|
|
1348
1455
|
this.removeAllListeners();
|
|
1349
1456
|
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Sign a PDF from raw bytes using PEM credentials.
|
|
1459
|
+
*
|
|
1460
|
+
* Calls the native `signPdfBytes` FFI function (two-pass ByteRange writer).
|
|
1461
|
+
* Credentials are loaded and freed within this call.
|
|
1462
|
+
*
|
|
1463
|
+
* @param pdfData - Buffer containing the PDF document bytes
|
|
1464
|
+
* @param certPem - PEM-encoded certificate string
|
|
1465
|
+
* @param keyPem - PEM-encoded private key string
|
|
1466
|
+
* @param reason - Optional signature reason
|
|
1467
|
+
* @param location - Optional signature location
|
|
1468
|
+
* @returns Buffer containing the signed PDF
|
|
1469
|
+
*/
|
|
1470
|
+
async signPdfData(pdfData, certPem, keyPem, reason, location) {
|
|
1471
|
+
if (!this.native?.certificateLoadFromPem) {
|
|
1472
|
+
throw new SignatureException('Native signing not available: certificateLoadFromPem not found');
|
|
1473
|
+
}
|
|
1474
|
+
if (!this.native?.signPdfBytes) {
|
|
1475
|
+
throw new SignatureException('Native signing not available: signPdfBytes not found');
|
|
1476
|
+
}
|
|
1477
|
+
const certHandle = this.native.certificateLoadFromPem(certPem, keyPem);
|
|
1478
|
+
if (!certHandle) {
|
|
1479
|
+
throw new SignatureException('Failed to load PEM certificate');
|
|
1480
|
+
}
|
|
1481
|
+
try {
|
|
1482
|
+
const result = this.native.signPdfBytes(pdfData, certHandle, reason ?? null, location ?? null);
|
|
1483
|
+
if (!result)
|
|
1484
|
+
throw new SignatureException('signPdfBytes returned null');
|
|
1485
|
+
return Buffer.from(result);
|
|
1486
|
+
}
|
|
1487
|
+
finally {
|
|
1488
|
+
if (this.native?.pdf_certificate_free) {
|
|
1489
|
+
this.native.pdf_certificate_free(certHandle);
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1350
1493
|
// Private helpers
|
|
1351
1494
|
setCached(key, value) {
|
|
1352
1495
|
this.resultCache.set(key, value);
|
|
@@ -1357,8 +1500,10 @@ export class SignatureManager extends EventEmitter {
|
|
|
1357
1500
|
}
|
|
1358
1501
|
}
|
|
1359
1502
|
clearCachePattern(prefix) {
|
|
1360
|
-
const keysToDelete = Array.from(this.resultCache.keys()).filter(key => key.startsWith(prefix));
|
|
1361
|
-
keysToDelete.forEach(key =>
|
|
1503
|
+
const keysToDelete = Array.from(this.resultCache.keys()).filter((key) => key.startsWith(prefix));
|
|
1504
|
+
keysToDelete.forEach((key) => {
|
|
1505
|
+
this.resultCache.delete(key);
|
|
1506
|
+
});
|
|
1362
1507
|
}
|
|
1363
1508
|
}
|
|
1364
1509
|
export default SignatureManager;
|
package/lib/managers/streams.js
CHANGED
|
@@ -70,10 +70,16 @@ export class SearchStream extends Readable {
|
|
|
70
70
|
try {
|
|
71
71
|
// Perform search
|
|
72
72
|
if (this.pageIndex !== undefined) {
|
|
73
|
-
this._results = (this.searchManager.search(this.searchTerm, this.pageIndex, {
|
|
73
|
+
this._results = (this.searchManager.search(this.searchTerm, this.pageIndex, {
|
|
74
|
+
caseSensitive: this.caseSensitive,
|
|
75
|
+
wholeWords: this.wholeWords,
|
|
76
|
+
}) || []);
|
|
74
77
|
}
|
|
75
78
|
else {
|
|
76
|
-
this._results = (this.searchManager.searchAll(this.searchTerm, {
|
|
79
|
+
this._results = (this.searchManager.searchAll(this.searchTerm, {
|
|
80
|
+
caseSensitive: this.caseSensitive,
|
|
81
|
+
wholeWords: this.wholeWords,
|
|
82
|
+
}) || []);
|
|
77
83
|
}
|
|
78
84
|
// Apply max results limit
|
|
79
85
|
if (this._results && this._results.length > this.maxResults) {
|
|
@@ -142,7 +142,7 @@ export class XfaManager extends EventEmitter {
|
|
|
142
142
|
}
|
|
143
143
|
async getFieldValue(fieldName) {
|
|
144
144
|
try {
|
|
145
|
-
return await this.document?.getXfaFieldValue?.(fieldName) ?? null;
|
|
145
|
+
return (await this.document?.getXfaFieldValue?.(fieldName)) ?? null;
|
|
146
146
|
}
|
|
147
147
|
catch (error) {
|
|
148
148
|
this.emit('error', error);
|
|
@@ -300,21 +300,56 @@ export class XfaManager extends EventEmitter {
|
|
|
300
300
|
// ===========================================================================
|
|
301
301
|
// Field Creation (from XfaCreationManager)
|
|
302
302
|
// ===========================================================================
|
|
303
|
-
async addTextField(pageIndex, config) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
async
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
async
|
|
310
|
-
|
|
303
|
+
async addTextField(pageIndex, config) {
|
|
304
|
+
return this.addField(pageIndex, { ...config, fieldType: XfaFieldType.TEXT });
|
|
305
|
+
}
|
|
306
|
+
async addNumericField(pageIndex, config) {
|
|
307
|
+
return this.addField(pageIndex, { ...config, fieldType: XfaFieldType.NUMERIC });
|
|
308
|
+
}
|
|
309
|
+
async addDateField(pageIndex, config) {
|
|
310
|
+
return this.addField(pageIndex, { ...config, fieldType: XfaFieldType.DATE });
|
|
311
|
+
}
|
|
312
|
+
async addCheckboxField(pageIndex, config) {
|
|
313
|
+
return this.addField(pageIndex, { ...config, fieldType: XfaFieldType.CHECKBOX });
|
|
314
|
+
}
|
|
315
|
+
async addRadioGroup(pageIndex, config) {
|
|
316
|
+
return this.addField(pageIndex, { ...config, fieldType: XfaFieldType.RADIO });
|
|
317
|
+
}
|
|
318
|
+
async addDropdownField(pageIndex, config) {
|
|
319
|
+
return this.addField(pageIndex, { ...config, fieldType: XfaFieldType.DROPDOWN });
|
|
320
|
+
}
|
|
321
|
+
async addSignatureField(pageIndex, config) {
|
|
322
|
+
return this.addField(pageIndex, { ...config, fieldType: XfaFieldType.SIGNATURE });
|
|
323
|
+
}
|
|
324
|
+
async addButton(pageIndex, config) {
|
|
325
|
+
return this.addField(pageIndex, { ...config, fieldType: XfaFieldType.BUTTON });
|
|
326
|
+
}
|
|
311
327
|
async addField(pageIndex, config) {
|
|
312
328
|
try {
|
|
313
329
|
if (!this.formCreated)
|
|
314
330
|
throw new Error('No XFA form created');
|
|
315
331
|
const fieldId = `xfa_field_${config.name}_${Date.now()}`;
|
|
316
|
-
await this.document?.addXfaField?.(pageIndex, config.name, config.fieldType, config.x, config.y, config.width, config.height, {
|
|
317
|
-
|
|
332
|
+
await this.document?.addXfaField?.(pageIndex, config.name, config.fieldType, config.x, config.y, config.width, config.height, {
|
|
333
|
+
caption: config.caption,
|
|
334
|
+
defaultValue: config.defaultValue,
|
|
335
|
+
tooltip: config.tooltip,
|
|
336
|
+
isRequired: config.isRequired,
|
|
337
|
+
isReadOnly: config.isReadOnly,
|
|
338
|
+
isHidden: config.isHidden,
|
|
339
|
+
maxLength: config.maxLength,
|
|
340
|
+
options: config.options,
|
|
341
|
+
font: config.font,
|
|
342
|
+
border: config.border,
|
|
343
|
+
margin: config.margin,
|
|
344
|
+
bindingType: config.bindingType,
|
|
345
|
+
bindingPath: config.bindingPath,
|
|
346
|
+
});
|
|
347
|
+
const handle = {
|
|
348
|
+
fieldId,
|
|
349
|
+
name: config.name,
|
|
350
|
+
fieldType: config.fieldType,
|
|
351
|
+
pageIndex,
|
|
352
|
+
};
|
|
318
353
|
this.createdFields.set(fieldId, handle);
|
|
319
354
|
this.emit('field-added', handle);
|
|
320
355
|
return handle;
|
|
@@ -374,7 +409,9 @@ export class XfaManager extends EventEmitter {
|
|
|
374
409
|
// ===========================================================================
|
|
375
410
|
async importXfaData(data, options) {
|
|
376
411
|
try {
|
|
377
|
-
const result = await this.document?.importXfaData?.(data, options.format, {
|
|
412
|
+
const result = await this.document?.importXfaData?.(data, options.format, {
|
|
413
|
+
validate: options.validateOnImport,
|
|
414
|
+
});
|
|
378
415
|
this.emit('data-imported', { format: options.format });
|
|
379
416
|
return !!result;
|
|
380
417
|
}
|
|
@@ -385,7 +422,10 @@ export class XfaManager extends EventEmitter {
|
|
|
385
422
|
}
|
|
386
423
|
async exportXfaData(options) {
|
|
387
424
|
try {
|
|
388
|
-
const result = await this.document?.exportXfaData?.(options.format, {
|
|
425
|
+
const result = await this.document?.exportXfaData?.(options.format, {
|
|
426
|
+
includeEmpty: options.includeEmptyFields,
|
|
427
|
+
includeCalculated: options.includeCalculatedFields,
|
|
428
|
+
});
|
|
389
429
|
this.emit('data-exported', { format: options.format });
|
|
390
430
|
return result ?? null;
|
|
391
431
|
}
|
|
@@ -396,7 +436,7 @@ export class XfaManager extends EventEmitter {
|
|
|
396
436
|
}
|
|
397
437
|
async exportAsXdp() {
|
|
398
438
|
try {
|
|
399
|
-
return await this.document?.exportXfaAsXdp?.() ?? null;
|
|
439
|
+
return (await this.document?.exportXfaAsXdp?.()) ?? null;
|
|
400
440
|
}
|
|
401
441
|
catch (error) {
|
|
402
442
|
this.emit('error', error);
|
|
@@ -455,18 +495,28 @@ export class XfaManager extends EventEmitter {
|
|
|
455
495
|
// ===========================================================================
|
|
456
496
|
async validateForm() {
|
|
457
497
|
try {
|
|
458
|
-
return await this.document?.validateXfaForm?.() ?? { valid: true, issues: [] };
|
|
498
|
+
return (await this.document?.validateXfaForm?.()) ?? { valid: true, issues: [] };
|
|
459
499
|
}
|
|
460
500
|
catch (error) {
|
|
461
501
|
this.emit('error', error);
|
|
462
502
|
return { valid: false, issues: [error instanceof Error ? error.message : 'Unknown error'] };
|
|
463
503
|
}
|
|
464
504
|
}
|
|
465
|
-
getCreatedFields() {
|
|
466
|
-
|
|
467
|
-
|
|
505
|
+
getCreatedFields() {
|
|
506
|
+
return Array.from(this.createdFields.values());
|
|
507
|
+
}
|
|
508
|
+
hasForm() {
|
|
509
|
+
return this.formCreated || this.hasXfa();
|
|
510
|
+
}
|
|
511
|
+
clearCache() {
|
|
512
|
+
this.cache.clear();
|
|
513
|
+
}
|
|
468
514
|
getCacheStats() {
|
|
469
|
-
return {
|
|
515
|
+
return {
|
|
516
|
+
cacheSize: this.cache.size,
|
|
517
|
+
maxCacheSize: this.maxCacheSize,
|
|
518
|
+
entries: Array.from(this.cache.keys()),
|
|
519
|
+
};
|
|
470
520
|
}
|
|
471
521
|
destroy() {
|
|
472
522
|
this.createdFields.clear();
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Shared loader for the pdf-oxide native N-API module.
|
|
2
|
+
//
|
|
3
|
+
// The top-level index.ts owns the primary load, but standalone classes like
|
|
4
|
+
// Timestamp and TsaClient need access too. Rather than plumb the module
|
|
5
|
+
// object through every constructor, both files import `getNative()` here.
|
|
6
|
+
//
|
|
7
|
+
// The loader resolves the prebuilt .node via the same prebuilds/<triple>
|
|
8
|
+
// layout index.ts uses, with fallbacks for development builds.
|
|
9
|
+
import { createRequire } from 'node:module';
|
|
10
|
+
import { dirname, join } from 'node:path';
|
|
11
|
+
import { arch, platform } from 'node:process';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
let cached = null;
|
|
17
|
+
function prebuildPath() {
|
|
18
|
+
const paths = {
|
|
19
|
+
darwin: {
|
|
20
|
+
x64: '../prebuilds/darwin-x64/pdf_oxide.node',
|
|
21
|
+
arm64: '../prebuilds/darwin-arm64/pdf_oxide.node',
|
|
22
|
+
},
|
|
23
|
+
linux: {
|
|
24
|
+
x64: '../prebuilds/linux-x64/pdf_oxide.node',
|
|
25
|
+
arm64: '../prebuilds/linux-arm64/pdf_oxide.node',
|
|
26
|
+
},
|
|
27
|
+
win32: {
|
|
28
|
+
x64: '../prebuilds/win32-x64/pdf_oxide.node',
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
const plat = paths[platform];
|
|
32
|
+
const rel = plat?.[arch];
|
|
33
|
+
if (!rel) {
|
|
34
|
+
throw new Error(`Unsupported platform: ${platform}/${arch}`);
|
|
35
|
+
}
|
|
36
|
+
return join(__dirname, rel);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Returns the loaded native module, loading it on first call. Throws if the
|
|
40
|
+
* prebuilt .node is missing and the development fallback also fails.
|
|
41
|
+
*/
|
|
42
|
+
export function getNative() {
|
|
43
|
+
if (cached)
|
|
44
|
+
return cached;
|
|
45
|
+
try {
|
|
46
|
+
cached = require(prebuildPath());
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
if (process.env.NODE_ENV === 'development' || process.env.NAPI_DEV) {
|
|
50
|
+
try {
|
|
51
|
+
cached = require('../build/Release/pdf_oxide.node');
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
throw e;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
throw e;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return cached;
|
|
62
|
+
}
|
package/lib/native.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared loader for the pdf-oxide N-API addon.
|
|
3
|
+
*
|
|
4
|
+
* Extracted so that modules which can't import from `./index.ts`
|
|
5
|
+
* (due to the index ↔ editor / builder cycle) can still reach the
|
|
6
|
+
* addon through the same prebuild-aware resolver. The published
|
|
7
|
+
* package ships a platform-specific `.node` file under
|
|
8
|
+
* `prebuilds/<triple>/`; loading the addon through this helper is
|
|
9
|
+
* what makes `DocumentEditor` / `DocumentBuilder` work for consumers
|
|
10
|
+
* installed from npm (where `build/Release/` does not exist).
|
|
11
|
+
*
|
|
12
|
+
* In development mode (`NODE_ENV=development` or `NAPI_DEV` set), we
|
|
13
|
+
* fall back to `../build/Release/pdf_oxide.node` — the node-gyp
|
|
14
|
+
* output that in-tree tests run against.
|
|
15
|
+
*/
|
|
16
|
+
export declare function loadNative(): any;
|