@ironsoftware/ironpdf 2026.5.1 → 2026.7.2
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/package.json +8 -5
- package/src/index.d.ts +1 -0
- package/src/index.d.ts.map +1 -1
- package/src/index.js +1 -0
- package/src/index.js.map +1 -1
- package/src/index.ts +1 -0
- package/src/internal/IronPdfEngine.ProtoFiles/iron_pdf_service.proto +5 -0
- package/src/internal/IronPdfEngine.ProtoFiles/pdfium_compress.proto +1 -0
- package/src/internal/IronPdfEngine.ProtoFiles/pdfium_form.proto +32 -0
- package/src/internal/IronPdfEngine.ProtoFiles/qpdf.proto +35 -0
- package/src/internal/access.d.ts.map +1 -1
- package/src/internal/access.js +3 -0
- package/src/internal/access.js.map +1 -1
- package/src/internal/access.ts +4 -0
- package/src/internal/generated_proto/iron_pdf_service.d.ts +14 -0
- package/src/internal/generated_proto/iron_pdf_service.d.ts.map +1 -1
- package/src/internal/generated_proto/iron_pdf_service.ts +14 -0
- package/src/internal/generated_proto/ironpdfengineproto/IronPdfService.d.ts +38 -0
- package/src/internal/generated_proto/ironpdfengineproto/IronPdfService.d.ts.map +1 -1
- package/src/internal/generated_proto/ironpdfengineproto/IronPdfService.ts +46 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumCompressImagesRequestP.d.ts +2 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumCompressImagesRequestP.d.ts.map +1 -1
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumCompressImagesRequestP.ts +2 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumDisableFormFontFallbackRequestP.d.ts +8 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumDisableFormFontFallbackRequestP.d.ts.map +1 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumDisableFormFontFallbackRequestP.js +4 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumDisableFormFontFallbackRequestP.js.map +1 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumDisableFormFontFallbackRequestP.ts +11 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumDisableFormFontFallbackResultP.d.ts +12 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumDisableFormFontFallbackResultP.d.ts.map +1 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumDisableFormFontFallbackResultP.js +4 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumDisableFormFontFallbackResultP.js.map +1 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumDisableFormFontFallbackResultP.ts +15 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumSetFormFontRequestStreamP.d.ts +25 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumSetFormFontRequestStreamP.d.ts.map +1 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumSetFormFontRequestStreamP.js +4 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumSetFormFontRequestStreamP.js.map +1 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumSetFormFontRequestStreamP.ts +28 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumSetFormFontResultP.d.ts +12 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumSetFormFontResultP.d.ts.map +1 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumSetFormFontResultP.js +4 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumSetFormFontResultP.js.map +1 -0
- package/src/internal/generated_proto/ironpdfengineproto/PdfiumSetFormFontResultP.ts +15 -0
- package/src/internal/generated_proto/ironpdfengineproto/QPdfCompressAndSaveAsAdvancedFromBytesRequestStreamP.d.ts +23 -0
- package/src/internal/generated_proto/ironpdfengineproto/QPdfCompressAndSaveAsAdvancedFromBytesRequestStreamP.d.ts.map +1 -0
- package/src/internal/generated_proto/ironpdfengineproto/QPdfCompressAndSaveAsAdvancedFromBytesRequestStreamP.js +4 -0
- package/src/internal/generated_proto/ironpdfengineproto/QPdfCompressAndSaveAsAdvancedFromBytesRequestStreamP.js.map +1 -0
- package/src/internal/generated_proto/ironpdfengineproto/QPdfCompressAndSaveAsAdvancedFromBytesRequestStreamP.ts +26 -0
- package/src/internal/generated_proto/ironpdfengineproto/QPdfCompressAndSaveAsAdvancedRequestP.d.ts +15 -0
- package/src/internal/generated_proto/ironpdfengineproto/QPdfCompressAndSaveAsAdvancedRequestP.d.ts.map +1 -0
- package/src/internal/generated_proto/ironpdfengineproto/QPdfCompressAndSaveAsAdvancedRequestP.js +4 -0
- package/src/internal/generated_proto/ironpdfengineproto/QPdfCompressAndSaveAsAdvancedRequestP.js.map +1 -0
- package/src/internal/generated_proto/ironpdfengineproto/QPdfCompressAndSaveAsAdvancedRequestP.ts +18 -0
- package/src/internal/generated_proto/ironpdfengineproto/QPdfCompressionFlagsP.d.ts +30 -0
- package/src/internal/generated_proto/ironpdfengineproto/QPdfCompressionFlagsP.d.ts.map +1 -0
- package/src/internal/generated_proto/ironpdfengineproto/QPdfCompressionFlagsP.js +4 -0
- package/src/internal/generated_proto/ironpdfengineproto/QPdfCompressionFlagsP.js.map +1 -0
- package/src/internal/generated_proto/ironpdfengineproto/QPdfCompressionFlagsP.ts +33 -0
- package/src/internal/grpc_layer/pdfium/compress.d.ts +41 -1
- package/src/internal/grpc_layer/pdfium/compress.d.ts.map +1 -1
- package/src/internal/grpc_layer/pdfium/compress.js +220 -9
- package/src/internal/grpc_layer/pdfium/compress.js.map +1 -1
- package/src/internal/grpc_layer/pdfium/compress.ts +264 -2
- package/src/internal/grpc_layer/pdfium/form.d.ts +15 -0
- package/src/internal/grpc_layer/pdfium/form.d.ts.map +1 -0
- package/src/internal/grpc_layer/pdfium/form.js +99 -0
- package/src/internal/grpc_layer/pdfium/form.js.map +1 -0
- package/src/internal/grpc_layer/pdfium/form.ts +119 -0
- package/src/internal/grpc_layer/pdfium/formFontValidator.d.ts +26 -0
- package/src/internal/grpc_layer/pdfium/formFontValidator.d.ts.map +1 -0
- package/src/internal/grpc_layer/pdfium/formFontValidator.js +81 -0
- package/src/internal/grpc_layer/pdfium/formFontValidator.js.map +1 -0
- package/src/internal/grpc_layer/pdfium/formFontValidator.ts +86 -0
- package/src/internal/grpc_layer/pdfium/io.d.ts +2 -1
- package/src/internal/grpc_layer/pdfium/io.d.ts.map +1 -1
- package/src/internal/grpc_layer/pdfium/io.js +41 -36
- package/src/internal/grpc_layer/pdfium/io.js.map +1 -1
- package/src/internal/grpc_layer/pdfium/io.ts +43 -36
- package/src/internal/grpc_layer/pdfium/signing.d.ts +29 -2
- package/src/internal/grpc_layer/pdfium/signing.d.ts.map +1 -1
- package/src/internal/grpc_layer/pdfium/signing.js +28 -10
- package/src/internal/grpc_layer/pdfium/signing.js.map +1 -1
- package/src/internal/grpc_layer/pdfium/signing.ts +51 -11
- package/src/internal/image_codec.d.ts +20 -0
- package/src/internal/image_codec.d.ts.map +1 -0
- package/src/internal/image_codec.js +127 -0
- package/src/internal/image_codec.js.map +1 -0
- package/src/internal/image_codec.ts +108 -0
- package/src/public/compression.d.ts +92 -0
- package/src/public/compression.d.ts.map +1 -0
- package/src/public/compression.js +26 -0
- package/src/public/compression.js.map +1 -0
- package/src/public/compression.ts +107 -0
- package/src/public/ironpdfglobalconfig.d.ts +12 -0
- package/src/public/ironpdfglobalconfig.d.ts.map +1 -1
- package/src/public/ironpdfglobalconfig.js +7 -0
- package/src/public/ironpdfglobalconfig.js.map +1 -1
- package/src/public/ironpdfglobalconfig.ts +19 -0
- package/src/public/pdfDocument.d.ts +81 -0
- package/src/public/pdfDocument.d.ts.map +1 -1
- package/src/public/pdfDocument.js +212 -24
- package/src/public/pdfDocument.js.map +1 -1
- package/src/public/pdfDocument.ts +228 -28
|
@@ -67,7 +67,10 @@ import {mergePdfs, renderHtmlToPdf, renderHtmlZipToPdf, renderUrlToPdf, renderHt
|
|
|
67
67
|
import {disposePdf, getBinaryData, openPdfFileBuffer} from "../internal/grpc_layer/pdfium/io";
|
|
68
68
|
import {Access} from "../internal/access";
|
|
69
69
|
import {renderImagesBufferToPdf, renderImagesFilesToPdf} from "../internal/grpc_layer/chrome/image";
|
|
70
|
-
import {compressAndSaveAs, compressImage, compressInMemory, compressInMemoryStream, compressStructTree} from "../internal/grpc_layer/pdfium/compress";
|
|
70
|
+
import {compressAndSaveAs, compressImage, compressInMemory, compressInMemoryStream, compressStructTree, compressAndSaveAsAdvanced, compressAndSaveAsAdvancedFromBytes} from "../internal/grpc_layer/pdfium/compress";
|
|
71
|
+
import {disableFormFontFallback, setFormFont} from "../internal/grpc_layer/pdfium/form";
|
|
72
|
+
import {AdvancedCompressionOptions} from "./compression";
|
|
73
|
+
import * as path from "path";
|
|
71
74
|
import {
|
|
72
75
|
isLinearizedFromBytes,
|
|
73
76
|
linearizeCoreFromBytes,
|
|
@@ -88,11 +91,11 @@ import {
|
|
|
88
91
|
setPageRotation
|
|
89
92
|
} from "../internal/grpc_layer/pdfium/page";
|
|
90
93
|
import {extractRawImages, rasterizeToImageBuffers} from "../internal/grpc_layer/pdfium/image";
|
|
91
|
-
import
|
|
94
|
+
import {transcodeImage, writeImageFile} from "../internal/image_codec";
|
|
92
95
|
import {extractAllText, replaceText} from "../internal/grpc_layer/pdfium/text";
|
|
93
96
|
import {PdfAVersions, PdfUAVersions, toPdfA, toPdfUA, toPdfUAForScreenReader} from "../internal/grpc_layer/pdfium/pdfa";
|
|
94
97
|
import {getMetadataDict, removeMetadata, setMetadata, setMetadataDict} from "../internal/grpc_layer/pdfium/metadata";
|
|
95
|
-
import {getSignatureCount, getVerifiedSignatures, signPdf} from "../internal/grpc_layer/pdfium/signing";
|
|
98
|
+
import {getSignatureCount, getVerifiedSignatures, PendingSignature, resolveCertificateBuffer, signPdf} from "../internal/grpc_layer/pdfium/signing";
|
|
96
99
|
import {addHtmlAffix, addTextAffix} from "../internal/grpc_layer/pdfium/headerFooter";
|
|
97
100
|
import {stampBarcode, stampHtml, stampImage, stampText} from "../internal/grpc_layer/chrome/stamp";
|
|
98
101
|
import {addBackgroundForeground} from "../internal/grpc_layer/pdfium/BackgroundForeground";
|
|
@@ -471,6 +474,117 @@ export class PdfDocument{
|
|
|
471
474
|
(filePath, imageQuality);
|
|
472
475
|
}
|
|
473
476
|
|
|
477
|
+
/**
|
|
478
|
+
* Apply the advanced compression pipeline and save the result to a file.
|
|
479
|
+
*
|
|
480
|
+
* Combines pdfium-side image DPI downsampling with qpdf-side structural
|
|
481
|
+
* optimization in a single call.
|
|
482
|
+
*
|
|
483
|
+
* @param filePath the file path to save the compressed PDF
|
|
484
|
+
* @param options advanced compression options; pass an empty object `{}` to
|
|
485
|
+
* reproduce the legacy compressAndSaveAs behaviour with no
|
|
486
|
+
* image downsampling
|
|
487
|
+
* @param password the PDF password (empty string if none)
|
|
488
|
+
*/
|
|
489
|
+
public async compressAndSaveAsAdvanced(
|
|
490
|
+
filePath: string,
|
|
491
|
+
options: AdvancedCompressionOptions,
|
|
492
|
+
password?: string
|
|
493
|
+
): Promise<void> {
|
|
494
|
+
return this.internal_compressAndSaveAsAdvanced(filePath, options, password);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Apply the advanced compression pipeline to byte[] input and save the
|
|
499
|
+
* compressed result to a file. Returns the compressed bytes as well.
|
|
500
|
+
*/
|
|
501
|
+
public static async compressAndSaveAsAdvancedFromBytes(
|
|
502
|
+
pdfBytes: Buffer,
|
|
503
|
+
filePath: string,
|
|
504
|
+
options: AdvancedCompressionOptions,
|
|
505
|
+
password?: string
|
|
506
|
+
): Promise<Buffer> {
|
|
507
|
+
if (!pdfBytes || pdfBytes.length === 0) {
|
|
508
|
+
throw new Error("pdfBytes must not be null or empty");
|
|
509
|
+
}
|
|
510
|
+
if (!options) {
|
|
511
|
+
throw new Error("options must not be null");
|
|
512
|
+
}
|
|
513
|
+
return compressAndSaveAsAdvancedFromBytes(pdfBytes, filePath, options, password ?? "");
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
//#endregion
|
|
517
|
+
|
|
518
|
+
//#region form (PDF-2184)
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Set the document-wide form font for subsequent form-field fills. When
|
|
522
|
+
* set, the engine skips its automatic Tahoma/Arial fallback embed for
|
|
523
|
+
* non-ASCII form values and registers the supplied font in the AcroForm
|
|
524
|
+
* /DR /Font dictionary so default-appearance strings resolve correctly
|
|
525
|
+
* at render time.
|
|
526
|
+
*
|
|
527
|
+
* File-size impact:
|
|
528
|
+
* - Name-only mode (`fontData` omitted or empty): zero bytes added; the
|
|
529
|
+
* font must already be in the document.
|
|
530
|
+
* - Bytes mode and the font is already embedded with usable glyph data:
|
|
531
|
+
* zero bytes added.
|
|
532
|
+
* - Bytes mode and the font is missing or has no glyph data: the full
|
|
533
|
+
* font is embedded.
|
|
534
|
+
* - Bytes mode with `forceEmbed = true`: a fresh font copy is added
|
|
535
|
+
* alongside any existing copy; the document grows on every call.
|
|
536
|
+
*
|
|
537
|
+
* @param fontName PDF font name to register (e.g. "Poppins-Regular").
|
|
538
|
+
* Allowed characters: letters, digits, `_`, `-`, `.`, `+`.
|
|
539
|
+
* @param fontData Raw TrueType/OpenType bytes; pass `undefined` or an
|
|
540
|
+
* empty Buffer for name-only mode.
|
|
541
|
+
* @param forceEmbed When true, embed `fontData` even if a font with the
|
|
542
|
+
* same name and usable glyph data is already present.
|
|
543
|
+
*/
|
|
544
|
+
public async setFormFont(
|
|
545
|
+
fontName: string,
|
|
546
|
+
fontData?: Buffer,
|
|
547
|
+
forceEmbed = false
|
|
548
|
+
): Promise<void> {
|
|
549
|
+
return setFormFont(await this.internal_getId(), fontName, fontData, forceEmbed);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Load a TrueType/OpenType font from disk and apply it as the
|
|
554
|
+
* document-wide form font. The PDF font name is derived from the file
|
|
555
|
+
* name (without extension) when `fontName` is omitted.
|
|
556
|
+
*
|
|
557
|
+
* @param fontFilePath Path to a TTF/OTF/TTC font file
|
|
558
|
+
* @param fontName Optional PDF font name. When omitted the file name
|
|
559
|
+
* (without extension) is used.
|
|
560
|
+
* @param forceEmbed When true, embed even if the font is already present.
|
|
561
|
+
*/
|
|
562
|
+
public async setFormFontFromFile(
|
|
563
|
+
fontFilePath: string,
|
|
564
|
+
fontName?: string,
|
|
565
|
+
forceEmbed = false
|
|
566
|
+
): Promise<void> {
|
|
567
|
+
if (!fontFilePath) {
|
|
568
|
+
throw new Error("fontFilePath must not be empty");
|
|
569
|
+
}
|
|
570
|
+
const fontData = fs.readFileSync(fontFilePath);
|
|
571
|
+
const resolvedName = fontName ?? deriveFontNameFromPath(fontFilePath);
|
|
572
|
+
return setFormFont(await this.internal_getId(), resolvedName, fontData, forceEmbed);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Suppress the automatic Tahoma/Arial fallback embed for non-ASCII form
|
|
577
|
+
* values without registering any replacement font. Use when zero
|
|
578
|
+
* file-size growth is required and the template's existing font
|
|
579
|
+
* references are sufficient for the values being filled.
|
|
580
|
+
*
|
|
581
|
+
* Rendering of non-ASCII characters then depends entirely on the
|
|
582
|
+
* viewer's font substitution.
|
|
583
|
+
*/
|
|
584
|
+
public async disableFormFontFallback(): Promise<void> {
|
|
585
|
+
return disableFormFontFallback(await this.internal_getId());
|
|
586
|
+
}
|
|
587
|
+
|
|
474
588
|
//#endregion
|
|
475
589
|
|
|
476
590
|
//#region linearize
|
|
@@ -1378,6 +1492,13 @@ export class PdfDocument{
|
|
|
1378
1492
|
* @private
|
|
1379
1493
|
*/
|
|
1380
1494
|
private pdfPassword?: PdfPassword | undefined;
|
|
1495
|
+
/**
|
|
1496
|
+
* Digital signatures whose placeholder fields have been added (via
|
|
1497
|
+
* {@link signDigitalSignature}) but still need to be cryptographically finalized when the
|
|
1498
|
+
* document is saved. See {@link internal_saveAsBuffer}.
|
|
1499
|
+
* @private
|
|
1500
|
+
*/
|
|
1501
|
+
private pendingSignatures: PendingSignature[] = [];
|
|
1381
1502
|
|
|
1382
1503
|
/**
|
|
1383
1504
|
* Create a PdfDocument object from a {@link PdfInput}
|
|
@@ -1746,7 +1867,7 @@ export class PdfDocument{
|
|
|
1746
1867
|
* @param saveOptions see {@link SaveOptions}
|
|
1747
1868
|
*/
|
|
1748
1869
|
private async internal_saveAsBuffer(saveOptions?: SaveOptions | undefined): Promise<Buffer> {
|
|
1749
|
-
return getBinaryData(await this.internal_getId(), saveOptions);
|
|
1870
|
+
return getBinaryData(await this.internal_getId(), saveOptions, this.pendingSignatures);
|
|
1750
1871
|
}
|
|
1751
1872
|
|
|
1752
1873
|
//#endregion
|
|
@@ -1839,6 +1960,34 @@ export class PdfDocument{
|
|
|
1839
1960
|
);
|
|
1840
1961
|
}
|
|
1841
1962
|
|
|
1963
|
+
private async internal_compressAndSaveAsAdvanced(
|
|
1964
|
+
filePath: string,
|
|
1965
|
+
options: AdvancedCompressionOptions,
|
|
1966
|
+
password?: string
|
|
1967
|
+
): Promise<void> {
|
|
1968
|
+
if (!filePath || filePath.trim().length === 0) {
|
|
1969
|
+
throw new Error("filePath must not be empty");
|
|
1970
|
+
}
|
|
1971
|
+
if (!options) {
|
|
1972
|
+
throw new Error("options must not be null");
|
|
1973
|
+
}
|
|
1974
|
+
if (
|
|
1975
|
+
options.jpegQuality !== undefined &&
|
|
1976
|
+
options.jpegQuality !== null &&
|
|
1977
|
+
(options.jpegQuality < 1 || options.jpegQuality > 100)
|
|
1978
|
+
) {
|
|
1979
|
+
throw new Error(
|
|
1980
|
+
`Invalid jpegQuality (${options.jpegQuality}). Must be between 1 and 100.`
|
|
1981
|
+
);
|
|
1982
|
+
}
|
|
1983
|
+
return await compressAndSaveAsAdvanced(
|
|
1984
|
+
await this.internal_getId(),
|
|
1985
|
+
filePath,
|
|
1986
|
+
options,
|
|
1987
|
+
password ?? ""
|
|
1988
|
+
);
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1842
1991
|
//#endregion
|
|
1843
1992
|
|
|
1844
1993
|
//#region linearize
|
|
@@ -2119,25 +2268,7 @@ export class PdfDocument{
|
|
|
2119
2268
|
await this.internal_getId(),
|
|
2120
2269
|
options?.fromPages ?? "all"
|
|
2121
2270
|
);
|
|
2122
|
-
|
|
2123
|
-
switch (options?.imageType) {
|
|
2124
|
-
case ImageType.BMP:
|
|
2125
|
-
return Jimp.MIME_BMP;
|
|
2126
|
-
case ImageType.PNG:
|
|
2127
|
-
return Jimp.MIME_PNG;
|
|
2128
|
-
case ImageType.JPG:
|
|
2129
|
-
return Jimp.MIME_JPEG;
|
|
2130
|
-
default:
|
|
2131
|
-
return Jimp.MIME_PNG;
|
|
2132
|
-
}
|
|
2133
|
-
})();
|
|
2134
|
-
return Promise.all(
|
|
2135
|
-
images.map(async (imageBuffer) => {
|
|
2136
|
-
//convert output type
|
|
2137
|
-
const jimp = await Jimp.read(imageBuffer);
|
|
2138
|
-
return await jimp.getBufferAsync(jimpImageType);
|
|
2139
|
-
})
|
|
2140
|
-
);
|
|
2271
|
+
return images.map((imageBuffer) => transcodeImage(imageBuffer, options?.imageType));
|
|
2141
2272
|
}
|
|
2142
2273
|
|
|
2143
2274
|
/**
|
|
@@ -2169,9 +2300,7 @@ export class PdfDocument{
|
|
|
2169
2300
|
|
|
2170
2301
|
const promises = images.map(async (imageBuffer, index) => {
|
|
2171
2302
|
const outputFileName = getFileName(filePath,index.toString(),getImageExtType(options?.type),"image");
|
|
2172
|
-
await
|
|
2173
|
-
return value.write(outputFileName);
|
|
2174
|
-
});
|
|
2303
|
+
await writeImageFile(imageBuffer, outputFileName, options?.type);
|
|
2175
2304
|
return outputFileName;
|
|
2176
2305
|
});
|
|
2177
2306
|
|
|
@@ -2273,7 +2402,64 @@ export class PdfDocument{
|
|
|
2273
2402
|
* @param signature see {@link DigitalSignature}
|
|
2274
2403
|
*/
|
|
2275
2404
|
private async internal_signDigitalSignature(signature: DigitalSignature) {
|
|
2276
|
-
|
|
2405
|
+
// Resolve the certificate up-front: signing finalizes at save time and the certificate
|
|
2406
|
+
// has to be re-sent then (see internal_saveAsBuffer / getBinaryData). If we cannot resolve
|
|
2407
|
+
// it we must fail before reserving a placeholder on the engine, otherwise we would be left
|
|
2408
|
+
// with an un-fillable placeholder that silently produces an invalid signature on save.
|
|
2409
|
+
const certificate = resolveCertificateBuffer(signature);
|
|
2410
|
+
if (!certificate) {
|
|
2411
|
+
throw new Error(
|
|
2412
|
+
"A signing certificate is required: provide either certificatePath or certificateBuffer."
|
|
2413
|
+
);
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
// Multiple signatures cannot be finalized in a single save: filling a later signature's
|
|
2417
|
+
// placeholder rewrites bytes that an earlier signature already covers, which invalidates
|
|
2418
|
+
// the earlier one. So whenever a signature from a previous signDigitalSignature() call is
|
|
2419
|
+
// still pending, finalize it first (save + re-open) so the new signature is applied as a
|
|
2420
|
+
// separate revision. This is the sign -> save -> re-open -> sign flow done transparently,
|
|
2421
|
+
// and it keeps every signature valid.
|
|
2422
|
+
await this.internal_finalizePendingSignatures();
|
|
2423
|
+
|
|
2424
|
+
// Reuse the already-resolved certificate so signPdf does not read the file a second time.
|
|
2425
|
+
const index = await signPdf(await this.internal_getId(), {
|
|
2426
|
+
...signature,
|
|
2427
|
+
certificateBuffer: certificate,
|
|
2428
|
+
});
|
|
2429
|
+
|
|
2430
|
+
// The signature field is only a placeholder until the document is saved. Remember the
|
|
2431
|
+
// certificate so it can be re-sent to finalize the signing in internal_saveAsBuffer().
|
|
2432
|
+
// At most one signature is ever pending here (earlier ones have already been finalized
|
|
2433
|
+
// above), so each save only ever finalizes a single signature.
|
|
2434
|
+
this.pendingSignatures.push({
|
|
2435
|
+
index,
|
|
2436
|
+
password: signature.certificatePassword ?? "",
|
|
2437
|
+
timeStampUrl: signature.timeStampUrl ?? "",
|
|
2438
|
+
certificate,
|
|
2439
|
+
});
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
/**
|
|
2443
|
+
* Finalizes any currently-pending digital signatures into the document by saving it (which
|
|
2444
|
+
* fills the reserved placeholders) and re-opening the result, then swapping in the new
|
|
2445
|
+
* engine-side document. This isolates each signature in its own revision so that adding a
|
|
2446
|
+
* further signature does not invalidate the previous ones. No-op when nothing is pending.
|
|
2447
|
+
* @private
|
|
2448
|
+
*/
|
|
2449
|
+
private async internal_finalizePendingSignatures(): Promise<void> {
|
|
2450
|
+
if (this.pendingSignatures.length === 0) {
|
|
2451
|
+
return;
|
|
2452
|
+
}
|
|
2453
|
+
const finalized = await getBinaryData(
|
|
2454
|
+
await this.internal_getId(),
|
|
2455
|
+
undefined,
|
|
2456
|
+
this.pendingSignatures
|
|
2457
|
+
);
|
|
2458
|
+
this.pendingSignatures = [];
|
|
2459
|
+
this.pdfDocumentId = await openPdfFileBuffer(finalized, {
|
|
2460
|
+
userPassword: this.pdfPassword?.userPassword,
|
|
2461
|
+
ownerPassword: this.pdfPassword?.ownerPassword,
|
|
2462
|
+
});
|
|
2277
2463
|
}
|
|
2278
2464
|
|
|
2279
2465
|
/**
|
|
@@ -2295,7 +2481,10 @@ export class PdfDocument{
|
|
|
2295
2481
|
* Get all verified digital signatures from this PdfDocument.
|
|
2296
2482
|
*/
|
|
2297
2483
|
private async internal_getVerifiedSignatures(): Promise<VerifiedSignature[]> {
|
|
2298
|
-
|
|
2484
|
+
// Verify against the finalized document bytes (including any pending signatures that are
|
|
2485
|
+
// only applied at save time), otherwise the engine re-saves and invalidates the signature.
|
|
2486
|
+
const data = await this.internal_saveAsBuffer();
|
|
2487
|
+
return await getVerifiedSignatures(await this.internal_getId(), data);
|
|
2299
2488
|
}
|
|
2300
2489
|
|
|
2301
2490
|
//#endregion
|
|
@@ -2570,3 +2759,14 @@ export class PdfDocument{
|
|
|
2570
2759
|
|
|
2571
2760
|
//#endregion
|
|
2572
2761
|
}
|
|
2762
|
+
|
|
2763
|
+
/**
|
|
2764
|
+
* Derive a PDF font name from a font file path: strip the directory and the
|
|
2765
|
+
* extension. Used by {@link PdfDocument.setFormFontFromFile} when no
|
|
2766
|
+
* explicit name is supplied.
|
|
2767
|
+
*/
|
|
2768
|
+
function deriveFontNameFromPath(fontFilePath: string): string {
|
|
2769
|
+
const base = path.basename(fontFilePath);
|
|
2770
|
+
const dot = base.lastIndexOf(".");
|
|
2771
|
+
return dot > 0 ? base.substring(0, dot) : base;
|
|
2772
|
+
}
|