Add support to Google Apps Script.

pull/2/head
zboris12 2022-09-18 16:49:56 +09:00
parent 213cf53474
commit 491e6309e0
4 changed files with 123 additions and 77 deletions

View File

@ -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
<script src="https://unpkg.com/pdf-lib/dist/pdf-lib.min.js" type="text/javascript"></script>
<script src="https://unpkg.com/node-forge/dist/forge.min.js" type="text/javascript"></script>
<script src="https://github.com/zboris12/zgapdfsigner/releases/download/1.0.0/zgapdfsigner.js" type="text/javascript"></script>
<script src="https://github.com/zboris12/zgapdfsigner/releases/download/1.1.0/zgapdfsigner.js" type="text/javascript"></script>
```
## 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

39
closure/zb-externs.js Normal file
View File

@ -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;

View File

@ -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();
});
});

View File

@ -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<Uint8Array>}
*/
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<raw.length; i++){
arr[i] = raw.charCodeAt(i);
}
var opt = null;
if(typ){
opt = {"type" : typ};
}
return new Blob([arr], opt);
}
}
class VisualSignature {
VisualSignature: class {
/**
* @constructor
* @param {SignDrawInfo=} drawinf
@ -504,4 +452,31 @@ class VisualSignature {
}
return ret;
}
}
},
/**
* @param {Uint8Array} uarr
* @return {string}
*/
u8arrToRaw: function(uarr){
/** @type {Array<string>} */
var arr = [];
for(var i=0; i<uarr.length; i++){
arr.push(String.fromCharCode(uarr[i]));
}
return arr.join("");
},
/**
* @param {string} raw
* @return {Uint8Array}
*/
rawToU8arr: function(raw){
var arr = new Uint8Array(raw.length);
for(var i=0; i<raw.length; i++){
arr[i] = raw.charCodeAt(i);
}
return arr;
},
};