Add support to Google Apps Script.
parent
213cf53474
commit
491e6309e0
41
README.md
41
README.md
|
@ -1,5 +1,6 @@
|
||||||
# ZgaPdfSigner
|
# ZgaPdfSigner
|
||||||
A javascript tool to sign a pdf in web browser.
|
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.
|
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.
|
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
|
```html
|
||||||
<script src="https://unpkg.com/pdf-lib/dist/pdf-lib.min.js" type="text/javascript"></script>
|
<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://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
|
## Let's sign
|
||||||
|
@ -35,7 +36,7 @@ async function sign1(pdf, cert, pwd){
|
||||||
p12cert: cert,
|
p12cert: cert,
|
||||||
pwd: pwd,
|
pwd: pwd,
|
||||||
};
|
};
|
||||||
var signer = new PdfSigner(sopt);
|
var signer = new Zga.PdfSigner(sopt);
|
||||||
return await signer.sign(pdf);
|
return await signer.sign(pdf);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -67,7 +68,7 @@ async function sign2(pdf, cert, pwd, imgdat, imgtyp){
|
||||||
imgType: imgtyp,
|
imgType: imgtyp,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
var signer = new PdfSigner(sopt);
|
var signer = new Zga.PdfSigner(sopt);
|
||||||
return await signer.sign(pdf);
|
return await signer.sign(pdf);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -78,9 +79,41 @@ Sign with a visible signature of drawing a text.
|
||||||
//TODO
|
//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
|
## 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
|
* __pwd__: string :point_right: The passphrase of the certificate
|
||||||
* __reason__: string :point_right: (Optional) The reason for signing
|
* __reason__: string :point_right: (Optional) The reason for signing
|
||||||
* __location__: string :point_right: (Optional) Your location
|
* __location__: string :point_right: (Optional) Your location
|
||||||
|
|
|
@ -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;
|
|
@ -39,9 +39,8 @@ function testSign(imgdat, imgtyp){
|
||||||
var kf = document.getElementById("kkk").files[0];
|
var kf = document.getElementById("kkk").files[0];
|
||||||
readAsArrayBuffer(fil, function(a_pdf){
|
readAsArrayBuffer(fil, function(a_pdf){
|
||||||
readAsArrayBuffer(kf, async function(b_kfbuf){
|
readAsArrayBuffer(kf, async function(b_kfbuf){
|
||||||
var b_kfdat = String.fromCharCode.apply(null, new Uint8Array(b_kfbuf));
|
|
||||||
var b_opt = {
|
var b_opt = {
|
||||||
p12cert: b_kfdat,
|
p12cert: b_kfbuf,
|
||||||
pwd: document.getElementById("pwd").value,
|
pwd: document.getElementById("pwd").value,
|
||||||
};
|
};
|
||||||
if(imgdat){
|
if(imgdat){
|
||||||
|
@ -56,10 +55,10 @@ function testSign(imgdat, imgtyp){
|
||||||
imgType: imgtyp,
|
imgType: imgtyp,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
var b_signer = new PdfSigner(b_opt);
|
var b_signer = new Zga.PdfSigner(b_opt);
|
||||||
var b_blob = await b_signer.sign(a_pdf);
|
var b_u8s = await b_signer.sign(a_pdf);
|
||||||
document.getElementById("download").download = "zzzs3.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();
|
document.getElementById("download").click();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
111
zgapdfsigner.js
111
zgapdfsigner.js
|
@ -1,44 +1,8 @@
|
||||||
/**
|
'use strict';
|
||||||
* 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;
|
|
||||||
|
|
||||||
class PdfSigner {
|
globalThis.Zga = {
|
||||||
|
|
||||||
|
PdfSigner: class {
|
||||||
/**
|
/**
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {SignOption} signopt
|
* @param {SignOption} signopt
|
||||||
|
@ -51,7 +15,7 @@ class PdfSigner {
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
* @param {PDFLib.PDFDocument|Uint8Array|ArrayBuffer|string} pdf
|
* @param {PDFLib.PDFDocument|Uint8Array|ArrayBuffer|string} pdf
|
||||||
* @return {Blob}
|
* @return {Promise<Uint8Array>}
|
||||||
*/
|
*/
|
||||||
async sign(pdf){
|
async sign(pdf){
|
||||||
/** @type {PDFLib.PDFDocument} */
|
/** @type {PDFLib.PDFDocument} */
|
||||||
|
@ -74,8 +38,7 @@ class PdfSigner {
|
||||||
|
|
||||||
this.addSignHolder(pdfdoc);
|
this.addSignHolder(pdfdoc);
|
||||||
var uarr = await pdfdoc.save({"useObjectStreams": false});
|
var uarr = await pdfdoc.save({"useObjectStreams": false});
|
||||||
// return new Blob([uarr], {"type" : "application/pdf"});
|
var pdfstr = Zga.u8arrToRaw(uarr);
|
||||||
var pdfstr = String.fromCharCode.apply(null, uarr);
|
|
||||||
|
|
||||||
return this.signPdf(pdfstr);
|
return this.signPdf(pdfstr);
|
||||||
}
|
}
|
||||||
|
@ -91,7 +54,7 @@ class PdfSigner {
|
||||||
const SIGNATURE_LENGTH = 3322;
|
const SIGNATURE_LENGTH = 3322;
|
||||||
|
|
||||||
/** @const {VisualSignature} */
|
/** @const {VisualSignature} */
|
||||||
const visign = new VisualSignature(this.opt.drawinf);
|
const visign = new Zga.VisualSignature(this.opt.drawinf);
|
||||||
/** @const {PDFLib.PDFRef} */
|
/** @const {PDFLib.PDFRef} */
|
||||||
const strmRef = visign.createStream(pdfdoc, this.opt.signame);
|
const strmRef = visign.createStream(pdfdoc, this.opt.signame);
|
||||||
/** @const {PDFLib.PDFPage} */
|
/** @const {PDFLib.PDFPage} */
|
||||||
|
@ -118,7 +81,7 @@ class PdfSigner {
|
||||||
"M": PDFLib.PDFString.fromDate(this.opt.signdate),
|
"M": PDFLib.PDFString.fromDate(this.opt.signdate),
|
||||||
"Prop_Build": pdfdoc.context.obj({
|
"Prop_Build": pdfdoc.context.obj({
|
||||||
"App": pdfdoc.context.obj({
|
"App": pdfdoc.context.obj({
|
||||||
"Name": "ZbPdfSinger",
|
"Name": "ZgaPdfSinger",
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
@ -167,7 +130,7 @@ class PdfSigner {
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {string} pdfstr
|
* @param {string} pdfstr
|
||||||
* @return {Blob}
|
* @return {Uint8Array}
|
||||||
*/
|
*/
|
||||||
signPdf(pdfstr){
|
signPdf(pdfstr){
|
||||||
if(!this.opt.signdate){
|
if(!this.opt.signdate){
|
||||||
|
@ -200,6 +163,9 @@ class PdfSigner {
|
||||||
// Remove the placeholder signature
|
// Remove the placeholder signature
|
||||||
pdfstr = pdfstr.slice(0, byteRange[1]) + pdfstr.slice(byteRange[2], byteRange[2] + byteRange[3]);
|
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.
|
// Convert Buffer P12 to a forge implementation.
|
||||||
var p12Asn1 = forge.asn1.fromDer(this.opt.p12cert);
|
var p12Asn1 = forge.asn1.fromDer(this.opt.p12cert);
|
||||||
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, true, this.opt.pwd);
|
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, true, this.opt.pwd);
|
||||||
|
@ -271,29 +237,11 @@ class PdfSigner {
|
||||||
// Place it in the document.
|
// Place it in the document.
|
||||||
pdfstr = pdfstr.slice(0, byteRange[1]) + "<" + sighex + ">" + pdfstr.slice(byteRange[1]);
|
pdfstr = pdfstr.slice(0, byteRange[1]) + "<" + sighex + ">" + pdfstr.slice(byteRange[1]);
|
||||||
|
|
||||||
return this.rawToBlob(pdfstr, "application/pdf");
|
return Zga.rawToU8arr(pdfstr);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
VisualSignature: class {
|
||||||
* @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 {
|
|
||||||
/**
|
/**
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {SignDrawInfo=} drawinf
|
* @param {SignDrawInfo=} drawinf
|
||||||
|
@ -504,4 +452,31 @@ class VisualSignature {
|
||||||
}
|
}
|
||||||
return ret;
|
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;
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue