From 491e6309e08b0f9b659d4f8efca16a35775b8543 Mon Sep 17 00:00:00 2001 From: zboris12 Date: Sun, 18 Sep 2022 16:49:56 +0900 Subject: [PATCH] Add support to Google Apps Script. --- README.md | 41 ++++++++++++++-- closure/zb-externs.js | 39 +++++++++++++++ test.html | 9 ++-- zgapdfsigner.js | 111 ++++++++++++++++-------------------------- 4 files changed, 123 insertions(+), 77 deletions(-) create mode 100644 closure/zb-externs.js diff --git a/README.md b/README.md index 56e928a..45a83b2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # ZgaPdfSigner A javascript tool to sign a pdf in web browser. +And it also can be used in Google Apps Script. PS: __ZGA__ is the abbreviation of my father's name. And I use this name to hope the merits from this application will be dedicated to my parents. @@ -15,7 +16,7 @@ Just import the dependencies and this tool. ```html - + ``` ## Let's sign @@ -35,7 +36,7 @@ async function sign1(pdf, cert, pwd){ p12cert: cert, pwd: pwd, }; - var signer = new PdfSigner(sopt); + var signer = new Zga.PdfSigner(sopt); return await signer.sign(pdf); } ``` @@ -67,7 +68,7 @@ async function sign2(pdf, cert, pwd, imgdat, imgtyp){ imgType: imgtyp, }, }; - var signer = new PdfSigner(sopt); + var signer = new Zga.PdfSigner(sopt); return await signer.sign(pdf); } ``` @@ -78,9 +79,41 @@ Sign with a visible signature of drawing a text. //TODO ``` +Use it in Google Apps Script + +```js +// Simulate setTimeout function for pdf-lib +function setTimeout(func, sleep){ + Utilities.sleep(sleep); + func(); +} +// Simulate window for node-forge +var window = globalThis; +// Load pdf-lib +eval(UrlFetchApp.fetch("https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js").getContentText()); +// Load node-forge +eval(UrlFetchApp.fetch("https://unpkg.com/node-forge@1.3.1/dist/forge.min.js").getContentText()); +// Load ZgaPdfSigner +eval(UrlFetchApp.fetch("https://github.com/zboris12/zgapdfsigner/releases/download/1.1.0/zgapdfsigner.js").getContentText()); + +// Load pdf, certificate +var pdfBlob = DriveApp.getFilesByName("_test.pdf").next().getBlob(); +var certBlob = DriveApp.getFilesByName("_test.pfx").next().getBlob(); +// Sign the pdf +var sopt = { + p12cert: new Uint8Array(certBlob.getBytes()), + pwd: "some passphrase", +}; +var signer = new Zga.PdfSigner(sopt); +var u8arr = await signer.sign(new Uint8Array(pdfBlob.getBytes())); +// Save the result pdf to some folder +var fld = DriveApp.getFolderById("a folder's id"); +fld.createFile(Utilities.newBlob(u8arr, "application/pdf").setName("signed_test.pdf")); +``` + ## Detail of SignOption -* __p12cert__: string :point_right: A raw data of the certificate +* __p12cert__: (Uint8Array|ArrayBuffer|string) :point_right: Certificate's data * __pwd__: string :point_right: The passphrase of the certificate * __reason__: string :point_right: (Optional) The reason for signing * __location__: string :point_right: (Optional) Your location diff --git a/closure/zb-externs.js b/closure/zb-externs.js new file mode 100644 index 0000000..ab09bbb --- /dev/null +++ b/closure/zb-externs.js @@ -0,0 +1,39 @@ +/** + * the base point of x, y is top left corner. + * @typedef + * {{ + * x: number, + * y: number, + * w: number, + * h: number, + * }} + */ +var SignAreaInfo; +/** + * @typedef + * {{ + * area: SignAreaInfo, + * pageidx: (number|undefined), + * imgData: (Uint8Array|ArrayBuffer|string|undefined), + * imgType: (string|undefined), + * text: (string|undefined), + * fontData: (PDFLib.StandardFonts|Uint8Array|ArrayBuffer|string|undefined), + * img: (PDFLib.PDFImage|undefined), + * font: (PDFLib.PDFFont|undefined), + * }} + */ +var SignDrawInfo; +/** + * @typedef + * {{ + * p12cert: (Uint8Array|ArrayBuffer|string), + * pwd: string, + * reason: (string|undefined), + * location: (string|undefined), + * contact: (string|undefined), + * signdate: (Date|undefined), + * signame: (string|undefined), + * drawinf: (SignDrawInfo|undefined), + * }} + */ +var SignOption; diff --git a/test.html b/test.html index aaecd2b..72d2977 100644 --- a/test.html +++ b/test.html @@ -39,9 +39,8 @@ function testSign(imgdat, imgtyp){ var kf = document.getElementById("kkk").files[0]; readAsArrayBuffer(fil, function(a_pdf){ readAsArrayBuffer(kf, async function(b_kfbuf){ - var b_kfdat = String.fromCharCode.apply(null, new Uint8Array(b_kfbuf)); var b_opt = { - p12cert: b_kfdat, + p12cert: b_kfbuf, pwd: document.getElementById("pwd").value, }; if(imgdat){ @@ -56,10 +55,10 @@ function testSign(imgdat, imgtyp){ imgType: imgtyp, }; } - var b_signer = new PdfSigner(b_opt); - var b_blob = await b_signer.sign(a_pdf); + var b_signer = new Zga.PdfSigner(b_opt); + var b_u8s = await b_signer.sign(a_pdf); document.getElementById("download").download = "zzzs3.pdf"; - document.getElementById("download").href = window.URL.createObjectURL(b_blob); + document.getElementById("download").href = window.URL.createObjectURL(new Blob([b_u8s], {"type" : "application/pdf"})); document.getElementById("download").click(); }); }); diff --git a/zgapdfsigner.js b/zgapdfsigner.js index 50c6272..eea2057 100644 --- a/zgapdfsigner.js +++ b/zgapdfsigner.js @@ -1,44 +1,8 @@ -/** - * the base point of x, y is top left corner. - * @typedef - * {{ - * x: number, - * y: number, - * w: number, - * h: number, - * }} - */ -var SignAreaInfo; -/** - * @typedef - * {{ - * area: SignAreaInfo, - * pageidx: (number|undefined), - * imgData: (Uint8Array|ArrayBuffer|string|undefined), - * imgType: (string|undefined), - * text: (string|undefined), - * fontData: (PDFLib.StandardFonts|Uint8Array|ArrayBuffer|string|undefined), - * img: (PDFLib.PDFImage|undefined), - * font: (PDFLib.PDFFont|undefined), - * }} - */ -var SignDrawInfo; -/** - * @typedef - * {{ - * p12cert: string, - * pwd: string, - * reason: (string|undefined), - * location: (string|undefined), - * contact: (string|undefined), - * signdate: (Date|undefined), - * signame: (string|undefined), - * drawinf: (SignDrawInfo|undefined), - * }} - */ -var SignOption; +'use strict'; -class PdfSigner { +globalThis.Zga = { + +PdfSigner: class { /** * @constructor * @param {SignOption} signopt @@ -51,7 +15,7 @@ class PdfSigner { /** * @public * @param {PDFLib.PDFDocument|Uint8Array|ArrayBuffer|string} pdf - * @return {Blob} + * @return {Promise} */ async sign(pdf){ /** @type {PDFLib.PDFDocument} */ @@ -74,8 +38,7 @@ class PdfSigner { this.addSignHolder(pdfdoc); var uarr = await pdfdoc.save({"useObjectStreams": false}); - // return new Blob([uarr], {"type" : "application/pdf"}); - var pdfstr = String.fromCharCode.apply(null, uarr); + var pdfstr = Zga.u8arrToRaw(uarr); return this.signPdf(pdfstr); } @@ -91,7 +54,7 @@ class PdfSigner { const SIGNATURE_LENGTH = 3322; /** @const {VisualSignature} */ - const visign = new VisualSignature(this.opt.drawinf); + const visign = new Zga.VisualSignature(this.opt.drawinf); /** @const {PDFLib.PDFRef} */ const strmRef = visign.createStream(pdfdoc, this.opt.signame); /** @const {PDFLib.PDFPage} */ @@ -118,7 +81,7 @@ class PdfSigner { "M": PDFLib.PDFString.fromDate(this.opt.signdate), "Prop_Build": pdfdoc.context.obj({ "App": pdfdoc.context.obj({ - "Name": "ZbPdfSinger", + "Name": "ZgaPdfSinger", }), }), }; @@ -167,7 +130,7 @@ class PdfSigner { /** * @private * @param {string} pdfstr - * @return {Blob} + * @return {Uint8Array} */ signPdf(pdfstr){ if(!this.opt.signdate){ @@ -200,6 +163,9 @@ class PdfSigner { // Remove the placeholder signature pdfstr = pdfstr.slice(0, byteRange[1]) + pdfstr.slice(byteRange[2], byteRange[2] + byteRange[3]); + if(typeof this.opt.p12cert !== "string"){ + this.opt.p12cert = Zga.u8arrToRaw(new Uint8Array(this.opt.p12cert)); + } // Convert Buffer P12 to a forge implementation. var p12Asn1 = forge.asn1.fromDer(this.opt.p12cert); var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, true, this.opt.pwd); @@ -271,29 +237,11 @@ class PdfSigner { // Place it in the document. pdfstr = pdfstr.slice(0, byteRange[1]) + "<" + sighex + ">" + pdfstr.slice(byteRange[1]); - return this.rawToBlob(pdfstr, "application/pdf"); + return Zga.rawToU8arr(pdfstr); } +}, - /** - * @private - * @param {string} raw - * @param {string=} typ - * @return {Blob} - */ - rawToBlob(raw, typ){ - var arr = new Uint8Array(raw.length); - for(var i=0; i} */ + var arr = []; + for(var i=0; i