@ironsoftware/ironpdf 2026.6.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/internal/grpc_layer/pdfium/compress.d.ts +15 -3
- package/src/internal/grpc_layer/pdfium/compress.d.ts.map +1 -1
- package/src/internal/grpc_layer/pdfium/compress.js +87 -27
- package/src/internal/grpc_layer/pdfium/compress.js.map +1 -1
- package/src/internal/grpc_layer/pdfium/compress.ts +100 -28
- 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/pdfDocument.d.ts +15 -0
- package/src/public/pdfDocument.d.ts.map +1 -1
- package/src/public/pdfDocument.js +64 -24
- package/src/public/pdfDocument.js.map +1 -1
- package/src/public/pdfDocument.ts +74 -27
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"io.js","sourceRoot":"","sources":["io.ts"],"names":[],"mappings":";;;;;;;;;;;;AAGA,yCAAoC;AAKpC,kCAAsE;AACtE,iDAAuE;
|
|
1
|
+
{"version":3,"file":"io.js","sourceRoot":"","sources":["io.ts"],"names":[],"mappings":";;;;;;;;;;;;AAGA,yCAAoC;AAKpC,kCAAsE;AACtE,iDAAuE;AAOvE,SAAsB,iBAAiB,CACtC,MAAc,EACd,MAA8I;;QAE9I,MAAM,MAAM,GAAyB,MAAM,eAAM,CAAC,gBAAgB,EAAE,CAAC;QACrE,OAAO,IAAI,OAAO,CACjB,CAAC,OAA4B,EAAE,MAAkC,EAAE,EAAE;;YACpE,MAAM,MAAM,GACX,MAAM,CAAC,gBAAgB,CACtB,CACC,GAAwB,EACxB,KAA6C,EAC5C,EAAE;gBACH,IAAI,GAAG,EAAE;oBACR,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;iBACtC;qBAAM,IAAI,KAAK,EAAE;oBACjB,IAAA,uCAAgC,EAC/B,KAAK,EACL,OAAO,EACP,MAAM,CACN,CAAC;iBACF;YACF,CAAC,CACD,CAAC;YAEH,MAAM,CAAC,KAAK,CAAC;gBACZ,IAAI,EAAE;oBACL,aAAa,EAAE,MAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,aAAa,mCAAI,EAAE;oBAC1C,YAAY,EAAE,MAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,YAAY,mCAAI,EAAE;oBACxC,YAAY,EAAE,MAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,YAAY,mCAAI,2BAAmB,CAAC,kBAAkB;iBAC5E;aACD,CAAC,CAAC;YAEH,IAAA,kBAAW,EAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;gBACrC,MAAM,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC;YACxC,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,GAAG,EAAE,CAAC;QACd,CAAC,CACD,CAAC;IACH,CAAC;CAAA;AAxCD,8CAwCC;AAED,SAAsB,aAAa,CAClC,aAAqB,EACrB,WAAqC,EACrC,UAA2C;;QAE3C,MAAM,MAAM,GAAyB,MAAM,eAAM,CAAC,gBAAgB,EAAE,CAAC;QAErE,OAAO,IAAI,OAAO,CACjB,CAAC,OAA4B,EAAE,MAAkC,EAAE,EAAE;;YACpE,MAAM,MAAM,GAGR,MAAM,CAAC,oBAAoB,EAAE,CAAC;YAElC,kBAAkB;YAClB,MAAM,OAAO,GAAa,EAAE,CAAC;YAC7B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAgC,EAAE,EAAE;gBACtD,IAAI,IAAI,CAAC,SAAS,EAAE;oBACnB,MAAM,CACL,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,KAAK,IAAI,CAAC,SAAS,CAAC,gBAAgB,EAAE,CAC/D,CAAC;iBACF;qBAAM,IAAI,IAAI,CAAC,WAAW,EAAE;oBAC5B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;iBAC/B;YACF,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;gBACrB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YACjC,CAAC,CAAC,CAAC;YAEH,cAAc;YAEd,MAAM;YACN,MAAM,CAAC,KAAK,CAAC;gBACZ,IAAI,EAAE;oBACL,QAAQ,EAAE,EAAE,UAAU,EAAE,aAAa,EAAE;oBACvC,aAAa,EAAE,KAAK;oBACpB,aAAa,EAAE,MAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,WAAW,mCAAI,KAAK;iBAChD;aACD,CAAC,CAAC;YAEH,kFAAkF;YAClF,mFAAmF;YACnF,qFAAqF;YACrF,mFAAmF;YACnF,4DAA4D;YAC5D,IAAI,UAAU,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE;gBACxC,4EAA4E;gBAC5E,yEAAyE;gBACzE,EAAE;gBACF,6EAA6E;gBAC7E,oFAAoF;gBACpF,qFAAqF;gBACrF,4EAA4E;gBAC5E,mFAAmF;gBACnF,kFAAkF;gBAClF,oFAAoF;gBACpF,kFAAkF;gBAClF,EAAE;gBACF,+EAA+E;gBAC/E,mEAAmE;gBACnE,MAAM,CAAC,KAAK,CAAC;oBACZ,UAAU,EAAE;wBACX,SAAS,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;4BACzC,KAAK,EAAE,SAAS,CAAC,KAAK;4BACtB,QAAQ,EAAE,SAAS,CAAC,QAAQ;4BAC5B,YAAY,EAAE,SAAS,CAAC,YAAY;yBACpC,CAAC,CAAC;qBACH;iBACD,CAAC,CAAC;gBAEH,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,cAAc,EAAE,EAAE;oBAChD,IAAA,kBAAW,EAAC,SAAS,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;wBACpD,MAAM,CAAC,KAAK,CAAC;4BACZ,kBAAkB,EAAE;gCACnB,iBAAiB,EAAE,KAAK;gCACxB,cAAc;6BACd;yBACD,CAAC,CAAC;oBACJ,CAAC,CAAC,CAAC;gBACJ,CAAC,CAAC,CAAC;aACH;YACD,kBAAkB;YAClB,MAAM,CAAC,GAAG,EAAE,CAAC;QACd,CAAC,CACD,CAAC;IACH,CAAC;CAAA;AAtFD,sCAsFC;AAED,SAAsB,UAAU,CAAC,aAAqB;;QACrD,MAAM,MAAM,GAAyB,MAAM,eAAM,CAAC,gBAAgB,EAAE,CAAC;QAErE,OAAO,IAAI,OAAO,CACjB,CAAC,OAAmB,EAAE,MAAkC,EAAE,EAAE;YAC3D,MAAM,CAAC,aAAa,CACnB,EAAE,UAAU,EAAE,aAAa,EAAE,EAC7B,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;gBACd,IAAI,GAAG,EAAE;oBACR,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;iBACtC;qBAAM,IAAI,KAAK,EAAE;oBACjB,eAAe;oBACf,OAAO,EAAE,CAAC;iBACV;YACF,CAAC,CACD,CAAC;QACH,CAAC,CACD,CAAC;IACH,CAAC;CAAA;AAlBD,gCAkBC"}
|
|
@@ -12,6 +12,7 @@ import {BytesResultStreamP__Output} from "../../generated_proto/ironpdfenginepro
|
|
|
12
12
|
import {
|
|
13
13
|
PdfiumGetBinaryDataRequestStreamP
|
|
14
14
|
} from "../../generated_proto/ironpdfengineproto/PdfiumGetBinaryDataRequestStreamP";
|
|
15
|
+
import {PendingSignature} from "./signing";
|
|
15
16
|
|
|
16
17
|
export async function openPdfFileBuffer(
|
|
17
18
|
buffer: Buffer,
|
|
@@ -57,7 +58,8 @@ export async function openPdfFileBuffer(
|
|
|
57
58
|
|
|
58
59
|
export async function getBinaryData(
|
|
59
60
|
pdfDocumentId: string,
|
|
60
|
-
saveOptions?: SaveOptions | undefined
|
|
61
|
+
saveOptions?: SaveOptions | undefined,
|
|
62
|
+
signatures?: PendingSignature[] | undefined
|
|
61
63
|
): Promise<Buffer> {
|
|
62
64
|
const client: IronPdfServiceClient = await Access.ensureConnection();
|
|
63
65
|
|
|
@@ -95,42 +97,47 @@ export async function getBinaryData(
|
|
|
95
97
|
},
|
|
96
98
|
});
|
|
97
99
|
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
100
|
+
// Digital signatures are added as placeholder fields by signPdf(), but the actual
|
|
101
|
+
// cryptographic signing only happens here, at save time. The signature descriptors
|
|
102
|
+
// (index/password/timestamp) and the raw certificate bytes must therefore be re-sent
|
|
103
|
+
// so the engine can fill in the reserved byte range. Skipping this leaves an empty
|
|
104
|
+
// placeholder and produces a PDF with an invalid signature.
|
|
105
|
+
if (signatures && signatures.length > 0) {
|
|
106
|
+
// The engine pairs each certificate with its reserved placeholder using two
|
|
107
|
+
// distinct indices (this matches the C# client, GrpcPdfClient.GetBytes):
|
|
108
|
+
//
|
|
109
|
+
// * rawSignaturesChunk.signatureIndex = the certificate's position in the
|
|
110
|
+
// `signatures` collection below. The engine groups the chunks by this value and
|
|
111
|
+
// looks the descriptor up positionally (`signatures.Signature[signatureIndex]`),
|
|
112
|
+
// so it must be the 0..n-1 array index, NOT the engine signature index.
|
|
113
|
+
// * PdfiumPdfSignatureP.index = the engine signature index returned by
|
|
114
|
+
// signPdf when the placeholder was reserved. The engine uses it to know which
|
|
115
|
+
// placeholder field this certificate fills. For an already-signed document this
|
|
116
|
+
// differs from the array position, which is exactly why the two are separate.
|
|
117
|
+
//
|
|
118
|
+
// Keep the collection order and the chunk order identical so position i in the
|
|
119
|
+
// collection always corresponds to signatureIndex i in the chunks.
|
|
120
|
+
stream.write({
|
|
121
|
+
signatures: {
|
|
122
|
+
signature: signatures.map((signature) => ({
|
|
123
|
+
index: signature.index,
|
|
124
|
+
password: signature.password,
|
|
125
|
+
timestampUrl: signature.timeStampUrl,
|
|
126
|
+
})),
|
|
127
|
+
},
|
|
128
|
+
});
|
|
111
129
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
// rawSignatureChunk: chunk,
|
|
124
|
-
// signatureIndex,
|
|
125
|
-
// };
|
|
126
|
-
// stream.write({
|
|
127
|
-
// rawSignaturesChunk:
|
|
128
|
-
// pdfiumRawSignatureChunkWithIndexP,
|
|
129
|
-
// });
|
|
130
|
-
// });
|
|
131
|
-
// }
|
|
132
|
-
// }
|
|
133
|
-
// );
|
|
130
|
+
signatures.forEach((signature, signatureIndex) => {
|
|
131
|
+
chunkBuffer(signature.certificate).forEach((chunk) => {
|
|
132
|
+
stream.write({
|
|
133
|
+
rawSignaturesChunk: {
|
|
134
|
+
rawSignatureChunk: chunk,
|
|
135
|
+
signatureIndex,
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
}
|
|
134
141
|
//request finished
|
|
135
142
|
stream.end();
|
|
136
143
|
}
|
|
@@ -1,5 +1,32 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import { Buffer } from "buffer";
|
|
1
4
|
import { DigitalSignature, VerifiedSignature } from "../../../public/signature";
|
|
2
|
-
|
|
5
|
+
/**
|
|
6
|
+
* A digital signature that has had its placeholder field added to the document, but still needs
|
|
7
|
+
* to be cryptographically finalized when the document is saved (see {@link getBinaryData}).
|
|
8
|
+
*
|
|
9
|
+
* Adding a signature is a two-step process: {@link signPdf} reserves a signature field with byte
|
|
10
|
+
* padding, and the actual signing over the final byte range only happens at save time. The
|
|
11
|
+
* certificate and password therefore have to be re-sent during the save, otherwise the signature
|
|
12
|
+
* placeholder is never filled in and the resulting PDF reports an invalid signature.
|
|
13
|
+
*/
|
|
14
|
+
export interface PendingSignature {
|
|
15
|
+
/** The signature index returned by the engine when the placeholder field was added. */
|
|
16
|
+
index: number;
|
|
17
|
+
/** The certificate password. */
|
|
18
|
+
password: string;
|
|
19
|
+
/** Url to use for timestamping. */
|
|
20
|
+
timeStampUrl: string;
|
|
21
|
+
/** The raw .pfx / .p12 certificate bytes used to sign. */
|
|
22
|
+
certificate: Buffer;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Resolves the raw certificate bytes from a {@link DigitalSignature}, reading from the file path
|
|
26
|
+
* if a buffer was not supplied directly.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveCertificateBuffer(signature: DigitalSignature): Buffer | undefined;
|
|
29
|
+
export declare function signPdf(id: string, signature: DigitalSignature): Promise<number>;
|
|
3
30
|
export declare function getSignatureCount(id: string): Promise<number>;
|
|
4
|
-
export declare function getVerifiedSignatures(id: string): Promise<VerifiedSignature[]>;
|
|
31
|
+
export declare function getVerifiedSignatures(id: string, data?: Buffer | undefined): Promise<VerifiedSignature[]>;
|
|
5
32
|
//# sourceMappingURL=signing.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signing.d.ts","sourceRoot":"","sources":["signing.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"signing.d.ts","sourceRoot":"","sources":["signing.ts"],"names":[],"mappings":";;AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAShF;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAChC,uFAAuF;IACvF,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACvC,SAAS,EAAE,gBAAgB,GACzB,MAAM,GAAG,SAAS,CAOpB;AAED,wBAAsB,OAAO,CAC5B,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,gBAAgB,GACzB,OAAO,CAAC,MAAM,CAAC,CAyEjB;AAED,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAsBnE;AAED,wBAAsB,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAmD/G"}
|
|
@@ -12,10 +12,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.getVerifiedSignatures = exports.getSignatureCount = exports.signPdf = void 0;
|
|
15
|
+
exports.getVerifiedSignatures = exports.getSignatureCount = exports.signPdf = exports.resolveCertificateBuffer = void 0;
|
|
16
16
|
const fs_1 = __importDefault(require("fs"));
|
|
17
17
|
const access_1 = require("../../access");
|
|
18
18
|
const util_1 = require("../util");
|
|
19
|
+
/**
|
|
20
|
+
* Resolves the raw certificate bytes from a {@link DigitalSignature}, reading from the file path
|
|
21
|
+
* if a buffer was not supplied directly.
|
|
22
|
+
*/
|
|
23
|
+
function resolveCertificateBuffer(signature) {
|
|
24
|
+
if (signature.certificateBuffer) {
|
|
25
|
+
return signature.certificateBuffer;
|
|
26
|
+
}
|
|
27
|
+
else if (signature.certificatePath) {
|
|
28
|
+
return fs_1.default.readFileSync(signature.certificatePath);
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
exports.resolveCertificateBuffer = resolveCertificateBuffer;
|
|
19
33
|
function signPdf(id, signature) {
|
|
20
34
|
return __awaiter(this, void 0, void 0, function* () {
|
|
21
35
|
const client = yield access_1.Access.ensureConnection();
|
|
@@ -23,6 +37,7 @@ function signPdf(id, signature) {
|
|
|
23
37
|
return new Promise((resolve, reject) => {
|
|
24
38
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
|
|
25
39
|
const stream = client.Pdfium_Signature_Sign((err, value) => {
|
|
40
|
+
var _a;
|
|
26
41
|
if (err) {
|
|
27
42
|
reject(`${err.name}/n${err.message}`);
|
|
28
43
|
}
|
|
@@ -30,7 +45,7 @@ function signPdf(id, signature) {
|
|
|
30
45
|
if (value === null || value === void 0 ? void 0 : value.exception) {
|
|
31
46
|
(0, util_1.handleRemoteException)(value.exception, reject);
|
|
32
47
|
}
|
|
33
|
-
resolve();
|
|
48
|
+
resolve((_a = value.result) !== null && _a !== void 0 ? _a : 0);
|
|
34
49
|
}
|
|
35
50
|
});
|
|
36
51
|
const info = {
|
|
@@ -55,13 +70,7 @@ function signPdf(id, signature) {
|
|
|
55
70
|
};
|
|
56
71
|
}
|
|
57
72
|
stream.write({ info: info });
|
|
58
|
-
|
|
59
|
-
if (signature.certificateBuffer) {
|
|
60
|
-
certBuffer = signature.certificateBuffer;
|
|
61
|
-
}
|
|
62
|
-
else if (signature.certificatePath) {
|
|
63
|
-
certBuffer = fs_1.default.readFileSync(signature.certificatePath);
|
|
64
|
-
}
|
|
73
|
+
const certBuffer = resolveCertificateBuffer(signature);
|
|
65
74
|
if (certBuffer) {
|
|
66
75
|
(0, util_1.chunkBuffer)(certBuffer).forEach((chunk) => {
|
|
67
76
|
stream.write({ certificateFileBytesChunk: chunk });
|
|
@@ -103,7 +112,7 @@ function getSignatureCount(id) {
|
|
|
103
112
|
});
|
|
104
113
|
}
|
|
105
114
|
exports.getSignatureCount = getSignatureCount;
|
|
106
|
-
function getVerifiedSignatures(id) {
|
|
115
|
+
function getVerifiedSignatures(id, data) {
|
|
107
116
|
return __awaiter(this, void 0, void 0, function* () {
|
|
108
117
|
const client = yield access_1.Access.ensureConnection();
|
|
109
118
|
return new Promise((resolve, reject) => {
|
|
@@ -135,6 +144,15 @@ function getVerifiedSignatures(id) {
|
|
|
135
144
|
}
|
|
136
145
|
});
|
|
137
146
|
stream.write({ info: { document: { documentId: id } } });
|
|
147
|
+
// Verification must run against the actual document bytes (the finalized, signed
|
|
148
|
+
// output). If we don't send them, the engine falls back to a fresh non-incremental
|
|
149
|
+
// save that recomputes object offsets and breaks the signed byte range, making every
|
|
150
|
+
// signature look invalid. This mirrors the C# client which always passes the bytes.
|
|
151
|
+
if (data) {
|
|
152
|
+
(0, util_1.chunkBuffer)(data).forEach((chunk) => {
|
|
153
|
+
stream.write({ dataChunk: chunk });
|
|
154
|
+
});
|
|
155
|
+
}
|
|
138
156
|
stream.end();
|
|
139
157
|
});
|
|
140
158
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signing.js","sourceRoot":"","sources":["signing.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AACA,4CAAoB;AAIpB,yCAAsC;AAEtC,kCAA6D;
|
|
1
|
+
{"version":3,"file":"signing.js","sourceRoot":"","sources":["signing.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,14 +10,49 @@ import { _ironpdfengineproto_PdfiumSignRequestStreamP_InfoP } from "../../genera
|
|
|
10
10
|
import { PdfiumGetSignatureCountResultP__Output } from "../../generated_proto/ironpdfengineproto/PdfiumGetSignatureCountResultP";
|
|
11
11
|
import { PdfiumGetVerifySignatureResultP__Output } from "../../generated_proto/ironpdfengineproto/PdfiumGetVerifySignatureResultP";
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* A digital signature that has had its placeholder field added to the document, but still needs
|
|
15
|
+
* to be cryptographically finalized when the document is saved (see {@link getBinaryData}).
|
|
16
|
+
*
|
|
17
|
+
* Adding a signature is a two-step process: {@link signPdf} reserves a signature field with byte
|
|
18
|
+
* padding, and the actual signing over the final byte range only happens at save time. The
|
|
19
|
+
* certificate and password therefore have to be re-sent during the save, otherwise the signature
|
|
20
|
+
* placeholder is never filled in and the resulting PDF reports an invalid signature.
|
|
21
|
+
*/
|
|
22
|
+
export interface PendingSignature {
|
|
23
|
+
/** The signature index returned by the engine when the placeholder field was added. */
|
|
24
|
+
index: number;
|
|
25
|
+
/** The certificate password. */
|
|
26
|
+
password: string;
|
|
27
|
+
/** Url to use for timestamping. */
|
|
28
|
+
timeStampUrl: string;
|
|
29
|
+
/** The raw .pfx / .p12 certificate bytes used to sign. */
|
|
30
|
+
certificate: Buffer;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Resolves the raw certificate bytes from a {@link DigitalSignature}, reading from the file path
|
|
35
|
+
* if a buffer was not supplied directly.
|
|
36
|
+
*/
|
|
37
|
+
export function resolveCertificateBuffer(
|
|
38
|
+
signature: DigitalSignature
|
|
39
|
+
): Buffer | undefined {
|
|
40
|
+
if (signature.certificateBuffer) {
|
|
41
|
+
return signature.certificateBuffer;
|
|
42
|
+
} else if (signature.certificatePath) {
|
|
43
|
+
return fs.readFileSync(signature.certificatePath);
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
13
48
|
export async function signPdf(
|
|
14
49
|
id: string,
|
|
15
50
|
signature: DigitalSignature
|
|
16
|
-
): Promise<
|
|
51
|
+
): Promise<number> {
|
|
17
52
|
const client: IronPdfServiceClient = await Access.ensureConnection();
|
|
18
53
|
const existing_sigs = await getSignatureCount(id);
|
|
19
54
|
return new Promise(
|
|
20
|
-
(resolve: () => void, reject: (errorMsg: string) => void) => {
|
|
55
|
+
(resolve: (_: number) => void, reject: (errorMsg: string) => void) => {
|
|
21
56
|
const stream = client.Pdfium_Signature_Sign(
|
|
22
57
|
(
|
|
23
58
|
err: ServiceError | null,
|
|
@@ -29,7 +64,7 @@ export async function signPdf(
|
|
|
29
64
|
if (value?.exception) {
|
|
30
65
|
handleRemoteException(value.exception, reject);
|
|
31
66
|
}
|
|
32
|
-
resolve();
|
|
67
|
+
resolve(value.result ?? 0);
|
|
33
68
|
}
|
|
34
69
|
}
|
|
35
70
|
);
|
|
@@ -59,13 +94,7 @@ export async function signPdf(
|
|
|
59
94
|
|
|
60
95
|
stream.write({ info: info });
|
|
61
96
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (signature.certificateBuffer) {
|
|
65
|
-
certBuffer = signature.certificateBuffer;
|
|
66
|
-
} else if (signature.certificatePath) {
|
|
67
|
-
certBuffer = fs.readFileSync(signature.certificatePath);
|
|
68
|
-
}
|
|
97
|
+
const certBuffer: Buffer | undefined = resolveCertificateBuffer(signature);
|
|
69
98
|
|
|
70
99
|
if (certBuffer) {
|
|
71
100
|
chunkBuffer(certBuffer).forEach((chunk) => {
|
|
@@ -118,7 +147,7 @@ export async function getSignatureCount(id: string): Promise<number> {
|
|
|
118
147
|
);
|
|
119
148
|
}
|
|
120
149
|
|
|
121
|
-
export async function getVerifiedSignatures(id: string): Promise<VerifiedSignature[]> {
|
|
150
|
+
export async function getVerifiedSignatures(id: string, data?: Buffer | undefined): Promise<VerifiedSignature[]> {
|
|
122
151
|
const client: IronPdfServiceClient = await Access.ensureConnection();
|
|
123
152
|
return new Promise(
|
|
124
153
|
(resolve: (_: VerifiedSignature[]) => void, reject: (errorMsg: string) => void) => {
|
|
@@ -155,6 +184,17 @@ export async function getVerifiedSignatures(id: string): Promise<VerifiedSignatu
|
|
|
155
184
|
);
|
|
156
185
|
|
|
157
186
|
stream.write({ info: { document: { documentId: id } } });
|
|
187
|
+
|
|
188
|
+
// Verification must run against the actual document bytes (the finalized, signed
|
|
189
|
+
// output). If we don't send them, the engine falls back to a fresh non-incremental
|
|
190
|
+
// save that recomputes object offsets and breaks the signed byte range, making every
|
|
191
|
+
// signature look invalid. This mirrors the C# client which always passes the bytes.
|
|
192
|
+
if (data) {
|
|
193
|
+
chunkBuffer(data).forEach((chunk) => {
|
|
194
|
+
stream.write({ dataChunk: chunk });
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
158
198
|
stream.end();
|
|
159
199
|
}
|
|
160
200
|
);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import { ImageType } from "../public/image";
|
|
4
|
+
export interface Bitmap {
|
|
5
|
+
/** RGBA bytes, row-major, top-to-bottom. */
|
|
6
|
+
data: Buffer;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function decodeImage(buffer: Buffer): Bitmap;
|
|
11
|
+
/**
|
|
12
|
+
* Decode the engine's image bytes and re-encode to the requested format.
|
|
13
|
+
* Fast path: when input is PNG and target is PNG, the bytes pass through unchanged.
|
|
14
|
+
*/
|
|
15
|
+
export declare function transcodeImage(buffer: Buffer, type: ImageType | undefined): Buffer;
|
|
16
|
+
/**
|
|
17
|
+
* Transcode + write to disk. Format is selected by `type`, not by the file extension.
|
|
18
|
+
*/
|
|
19
|
+
export declare function writeImageFile(buffer: Buffer, outputPath: string, type: ImageType | undefined): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=image_codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image_codec.d.ts","sourceRoot":"","sources":["image_codec.ts"],"names":[],"mappings":";;AAIA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,MAAM,WAAW,MAAM;IACtB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AAiCD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAkBlD;AAgBD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAclF;AAED;;GAEG;AACH,wBAAsB,cAAc,CACnC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,SAAS,GAAG,SAAS,GACzB,OAAO,CAAC,IAAI,CAAC,CAEf"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
exports.writeImageFile = exports.transcodeImage = exports.decodeImage = void 0;
|
|
36
|
+
const fs = __importStar(require("fs/promises"));
|
|
37
|
+
const pngjs_1 = require("pngjs");
|
|
38
|
+
const jpeg = __importStar(require("jpeg-js"));
|
|
39
|
+
const bmp = __importStar(require("bmp-js"));
|
|
40
|
+
const image_1 = require("../public/image");
|
|
41
|
+
const PNG_MAGIC = 0x89504e47;
|
|
42
|
+
function isPng(buffer) {
|
|
43
|
+
return buffer.length >= 8 && buffer.readUInt32BE(0) === PNG_MAGIC;
|
|
44
|
+
}
|
|
45
|
+
function isJpeg(buffer) {
|
|
46
|
+
return buffer.length >= 3
|
|
47
|
+
&& buffer.readUInt8(0) === 0xff
|
|
48
|
+
&& buffer.readUInt8(1) === 0xd8
|
|
49
|
+
&& buffer.readUInt8(2) === 0xff;
|
|
50
|
+
}
|
|
51
|
+
function isBmp(buffer) {
|
|
52
|
+
return buffer.length >= 2
|
|
53
|
+
&& buffer.readUInt8(0) === 0x42
|
|
54
|
+
&& buffer.readUInt8(1) === 0x4d;
|
|
55
|
+
}
|
|
56
|
+
// bmp-js stores pixels in ABGR; pngjs/jpeg-js use RGBA. Swap on both sides.
|
|
57
|
+
function swapAbgrRgba(src) {
|
|
58
|
+
const out = Buffer.alloc(src.length);
|
|
59
|
+
for (let i = 0; i < src.length; i += 4) {
|
|
60
|
+
out.writeUInt8(src.readUInt8(i + 3), i + 0);
|
|
61
|
+
out.writeUInt8(src.readUInt8(i + 2), i + 1);
|
|
62
|
+
out.writeUInt8(src.readUInt8(i + 1), i + 2);
|
|
63
|
+
out.writeUInt8(src.readUInt8(i + 0), i + 3);
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
function decodeImage(buffer) {
|
|
68
|
+
if (isPng(buffer)) {
|
|
69
|
+
const png = pngjs_1.PNG.sync.read(buffer);
|
|
70
|
+
return { data: png.data, width: png.width, height: png.height };
|
|
71
|
+
}
|
|
72
|
+
if (isJpeg(buffer)) {
|
|
73
|
+
const d = jpeg.decode(buffer, { useTArray: true, formatAsRGBA: true });
|
|
74
|
+
return {
|
|
75
|
+
data: Buffer.from(d.data.buffer, d.data.byteOffset, d.data.byteLength),
|
|
76
|
+
width: d.width,
|
|
77
|
+
height: d.height,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (isBmp(buffer)) {
|
|
81
|
+
const d = bmp.decode(buffer);
|
|
82
|
+
return { data: swapAbgrRgba(d.data), width: d.width, height: d.height };
|
|
83
|
+
}
|
|
84
|
+
throw new Error("Unsupported image format in engine response");
|
|
85
|
+
}
|
|
86
|
+
exports.decodeImage = decodeImage;
|
|
87
|
+
function encodePng({ data, width, height }) {
|
|
88
|
+
const png = new pngjs_1.PNG({ width, height });
|
|
89
|
+
data.copy(png.data);
|
|
90
|
+
return pngjs_1.PNG.sync.write(png);
|
|
91
|
+
}
|
|
92
|
+
function encodeJpeg({ data, width, height }) {
|
|
93
|
+
return Buffer.from(jpeg.encode({ data, width, height }, 90).data);
|
|
94
|
+
}
|
|
95
|
+
function encodeBmp({ data, width, height }) {
|
|
96
|
+
return bmp.encode({ data: swapAbgrRgba(data), width, height }).data;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Decode the engine's image bytes and re-encode to the requested format.
|
|
100
|
+
* Fast path: when input is PNG and target is PNG, the bytes pass through unchanged.
|
|
101
|
+
*/
|
|
102
|
+
function transcodeImage(buffer, type) {
|
|
103
|
+
if ((type === undefined || type === image_1.ImageType.PNG) && isPng(buffer)) {
|
|
104
|
+
return buffer;
|
|
105
|
+
}
|
|
106
|
+
const bitmap = decodeImage(buffer);
|
|
107
|
+
switch (type) {
|
|
108
|
+
case image_1.ImageType.BMP:
|
|
109
|
+
return encodeBmp(bitmap);
|
|
110
|
+
case image_1.ImageType.JPG:
|
|
111
|
+
return encodeJpeg(bitmap);
|
|
112
|
+
case image_1.ImageType.PNG:
|
|
113
|
+
default:
|
|
114
|
+
return encodePng(bitmap);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
exports.transcodeImage = transcodeImage;
|
|
118
|
+
/**
|
|
119
|
+
* Transcode + write to disk. Format is selected by `type`, not by the file extension.
|
|
120
|
+
*/
|
|
121
|
+
function writeImageFile(buffer, outputPath, type) {
|
|
122
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
123
|
+
yield fs.writeFile(outputPath, transcodeImage(buffer, type));
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
exports.writeImageFile = writeImageFile;
|
|
127
|
+
//# sourceMappingURL=image_codec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image_codec.js","sourceRoot":"","sources":["image_codec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import * as fs from "fs/promises";
|
|
2
|
+
import { PNG } from "pngjs";
|
|
3
|
+
import * as jpeg from "jpeg-js";
|
|
4
|
+
import * as bmp from "bmp-js";
|
|
5
|
+
import { ImageType } from "../public/image";
|
|
6
|
+
|
|
7
|
+
export interface Bitmap {
|
|
8
|
+
/** RGBA bytes, row-major, top-to-bottom. */
|
|
9
|
+
data: Buffer;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const PNG_MAGIC = 0x89504e47;
|
|
15
|
+
|
|
16
|
+
function isPng(buffer: Buffer): boolean {
|
|
17
|
+
return buffer.length >= 8 && buffer.readUInt32BE(0) === PNG_MAGIC;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isJpeg(buffer: Buffer): boolean {
|
|
21
|
+
return buffer.length >= 3
|
|
22
|
+
&& buffer.readUInt8(0) === 0xff
|
|
23
|
+
&& buffer.readUInt8(1) === 0xd8
|
|
24
|
+
&& buffer.readUInt8(2) === 0xff;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isBmp(buffer: Buffer): boolean {
|
|
28
|
+
return buffer.length >= 2
|
|
29
|
+
&& buffer.readUInt8(0) === 0x42
|
|
30
|
+
&& buffer.readUInt8(1) === 0x4d;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// bmp-js stores pixels in ABGR; pngjs/jpeg-js use RGBA. Swap on both sides.
|
|
34
|
+
function swapAbgrRgba(src: Buffer): Buffer {
|
|
35
|
+
const out = Buffer.alloc(src.length);
|
|
36
|
+
for (let i = 0; i < src.length; i += 4) {
|
|
37
|
+
out.writeUInt8(src.readUInt8(i + 3), i + 0);
|
|
38
|
+
out.writeUInt8(src.readUInt8(i + 2), i + 1);
|
|
39
|
+
out.writeUInt8(src.readUInt8(i + 1), i + 2);
|
|
40
|
+
out.writeUInt8(src.readUInt8(i + 0), i + 3);
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function decodeImage(buffer: Buffer): Bitmap {
|
|
46
|
+
if (isPng(buffer)) {
|
|
47
|
+
const png = PNG.sync.read(buffer);
|
|
48
|
+
return { data: png.data, width: png.width, height: png.height };
|
|
49
|
+
}
|
|
50
|
+
if (isJpeg(buffer)) {
|
|
51
|
+
const d = jpeg.decode(buffer, { useTArray: true, formatAsRGBA: true });
|
|
52
|
+
return {
|
|
53
|
+
data: Buffer.from(d.data.buffer, d.data.byteOffset, d.data.byteLength),
|
|
54
|
+
width: d.width,
|
|
55
|
+
height: d.height,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (isBmp(buffer)) {
|
|
59
|
+
const d = bmp.decode(buffer);
|
|
60
|
+
return { data: swapAbgrRgba(d.data), width: d.width, height: d.height };
|
|
61
|
+
}
|
|
62
|
+
throw new Error("Unsupported image format in engine response");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function encodePng({ data, width, height }: Bitmap): Buffer {
|
|
66
|
+
const png = new PNG({ width, height });
|
|
67
|
+
data.copy(png.data);
|
|
68
|
+
return PNG.sync.write(png);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function encodeJpeg({ data, width, height }: Bitmap): Buffer {
|
|
72
|
+
return Buffer.from(jpeg.encode({ data, width, height }, 90).data);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function encodeBmp({ data, width, height }: Bitmap): Buffer {
|
|
76
|
+
return bmp.encode({ data: swapAbgrRgba(data), width, height }).data;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Decode the engine's image bytes and re-encode to the requested format.
|
|
81
|
+
* Fast path: when input is PNG and target is PNG, the bytes pass through unchanged.
|
|
82
|
+
*/
|
|
83
|
+
export function transcodeImage(buffer: Buffer, type: ImageType | undefined): Buffer {
|
|
84
|
+
if ((type === undefined || type === ImageType.PNG) && isPng(buffer)) {
|
|
85
|
+
return buffer;
|
|
86
|
+
}
|
|
87
|
+
const bitmap = decodeImage(buffer);
|
|
88
|
+
switch (type) {
|
|
89
|
+
case ImageType.BMP:
|
|
90
|
+
return encodeBmp(bitmap);
|
|
91
|
+
case ImageType.JPG:
|
|
92
|
+
return encodeJpeg(bitmap);
|
|
93
|
+
case ImageType.PNG:
|
|
94
|
+
default:
|
|
95
|
+
return encodePng(bitmap);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Transcode + write to disk. Format is selected by `type`, not by the file extension.
|
|
101
|
+
*/
|
|
102
|
+
export async function writeImageFile(
|
|
103
|
+
buffer: Buffer,
|
|
104
|
+
outputPath: string,
|
|
105
|
+
type: ImageType | undefined
|
|
106
|
+
): Promise<void> {
|
|
107
|
+
await fs.writeFile(outputPath, transcodeImage(buffer, type));
|
|
108
|
+
}
|