![]() |
||
---|---|---|
closure | ||
lib | ||
.env.sample | ||
.gitignore | ||
LICENSE | ||
README.md | ||
closure.js | ||
logo.png | ||
package-lock.json | ||
package.json | ||
test.html | ||
test4node.js |
README.md

ZgaPdfSigner
A javascript tool to sign a pdf or set protection of a pdf in web browser.
And it is more powerful when used in Google Apps Script or nodejs.
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.
Main features
- Sign a pdf with an invisible pkcs#7 signature.
- Sign a pdf with a visible pkcs#7 signature by drawing an image.
- Sign a pdf and set DocMDP.
- Add a new signature to a pdf if it has been signed already. (An incremental update)
- Add a document timestamp from TSA. (Not available in web browser)
- Sign a pdf with a timestamp from TSA. ((Not available in web browser)
- Enable signature's LTV. (Not available in web browser)
- Set password protection to a pdf. Supported algorithms:
- 40bit RC4 Encryption
- 128bit RC4 Encryption
- 128bit AES Encryption
- 256bit AES Encryption
- Set public-key certificate protection to a pdf. Supported algorithms are as same as the password protection.
About signing with TSA and LTV
Because of the CORS security restrictions in web browser, signing with a timestamp from TSA or enabling LTV can only be used in Google Apps Script or nodejs.
The Dependencies
How to use this tool
Web Browser
Just import the dependencies and this tool.
<script src="https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js" type="text/javascript"></script>
<script src="https://unpkg.com/node-forge@1.3.1/dist/forge.min.js" type="text/javascript"></script>
<script src="https://github.com/zboris12/zgapdfsigner/releases/download/2.5.0/zgapdfsigner.min.js" type="text/javascript"></script>
Google Apps Script
Load the dependencies and this tool.
// 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/2.5.0/zgapdfsigner.min.js").getContentText());
Or simply import the library of ZgaPdfToolkit
- Add the library of ZgaPdfToolkit to your project, and suppose the id of library you defined is "pdfkit".
Script id:1T0UPf50gGp2fJ4dR1rZfEFgKYC5VpCwUVooCRNySiL7klvIUVsFBCZ9m
- Load the library.
pdfkit.loadZga(globalThis);
nodejs
- Install
npm install zgapdfsigner
- Import
const Zga = require("zgapdfsigner");
Let's sign
Sign with an invisible signature.
/**
* @param {ArrayBuffer} pdf
* @param {ArrayBuffer} cert
* @param {string} pwd
* @return {Promise<Blob>}
*/
async function sign1(pdf, cert, pwd){
/** @type {SignOption} */
var sopt = {
p12cert: cert,
pwd: pwd,
permission: 1,
};
var signer = new Zga.PdfSigner(sopt);
var u8arr = await signer.sign(pdf);
return new Blob([u8arr], {"type" : "application/pdf"});
}
Sign with a visible signature of an image.
/**
* @param {ArrayBuffer} pdf
* @param {ArrayBuffer} cert
* @param {string} pwd
* @param {ArrayBuffer} imgdat
* @param {string} imgtyp
* @return {Promise<Blob>}
*/
async function sign2(pdf, cert, pwd, imgdat, imgtyp){
/** @type {SignOption} */
var sopt = {
p12cert: cert,
pwd: pwd,
drawinf: {
area: {
x: 25, // left
y: 150, // top
w: 60, // width
h: 60, // height
},
imgData: imgdat,
imgType: imgtyp,
},
};
var signer = new Zga.PdfSigner(sopt);
var u8arr = await signer.sign(pdf);
return new Blob([u8arr], {"type" : "application/pdf"});
}
Sign with a visible signature of drawing a text.
//TODO
Use it in Google Apps Script
/**
* @param {string} pwd Passphrase of certificate
* @return {Promise}
*/
async function createPdf(pwd){
// Load pdf, certificate
var pdfBlob = DriveApp.getFilesByName("_test.pdf").next().getBlob();
var certBlob = DriveApp.getFilesByName("_test.pfx").next().getBlob();
// Sign the pdf
/** @type {SignOption} */
var sopt = {
p12cert: certBlob.getBytes(),
pwd,
signdate: "1",
ltv: 1,
};
var signer = new Zga.PdfSigner(sopt);
var u8arr = await signer.sign(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"));
}
Use queryPassword function in ZgaPdfToolkit.
function myfunction(){
var spd = SpreadsheetApp.getActiveSpreadsheet();
pdfkit.queryPassword("createPdf", "Please input the passphrase", spd.getName());
}
Use it in nodejs
const m_fs = require("fs");
const m_path = require("path");
async function main(){
/** @type {string} */
var pdfPath = m_path.join(__dirname, "_test.pdf");
/** @type {string} */
var pfxPath = m_path.join(__dirname, "_test.pfx");
/** @type {string} */
var ps = "";
/** @type {string} */
var imgPath = m_path.join(__dirname, "_test.png");
if(process.argv.length > 3){
pfxPath = process.argv[2];
ps = process.argv[3];
}else if(process.argv[2]){
ps = process.argv[2];
}
if(!ps){
// throw new Error("The passphrase is not specified.");
pfxPath = "";
}
/** @type {Buffer} */
var pdf = m_fs.readFileSync(pdfPath);
/** @type {Buffer} */
var pfx = null;
if(pfxPath){
pfx = m_fs.readFileSync(pfxPath);
}
/** @type {Buffer} */
var img = null;
/** @type {string} */
var imgType = "";
if(imgPath){
img = m_fs.readFileSync(imgPath);
imgType = m_path.extname(imgPath).slice(1);
}
/** @type {SignOption} */
var sopt = {
p12cert: pfx,
pwd: ps,
permission: pfx ? 2 : 0,
signdate: "1",
reason: "I have a test reason.",
location: "I am on the earth.",
contact: "zga@zga.com",
ltv: 1,
debug: true,
};
if(img){
sopt.drawinf = {
area: {
x: 25, // left
y: 150, // top
w: 60,
h: 60,
},
imgData: img,
imgType: imgType,
};
}
/** @type {Zga.PdfSigner} */
var ser = new Zga.PdfSigner(sopt);
/** @type {Uint8Array} */
var u8dat = await ser.sign(pdf);
if(u8dat){
/** @type {string} */
var outPath = m_path.join(__dirname, "test_signed.pdf");
m_fs.writeFileSync(outPath, u8dat);
console.log("Output file: " + outPath);
}
console.log("Done");
}
Detail of SignOption
- p12cert: Array|Uint8Array|ArrayBuffer|string 👉 (Optional) Certificate's data. In the case of adding a document timestamp, it must be omitted.
- pwd: string 👉 (Optional) The passphrase of the certificate. In the case of adding a document timestamp, it must be omitted.
- permission: number 👉 (Optional) The modification permissions granted for this document.
This is a setting of DocMDP. Valid values are:
- 1: No changes to the document are permitted; any change to the document invalidates the signature.
- 2: Permitted changes are filling in forms, instantiating page templates, and signing; other changes invalidate the signature.
- 3: Permitted changes are the same as for 2, as well as annotation creation, deletion, and modification; other changes invalidate the signature.
- reason: string 👉 (Optional) The reason for signing
- location: string 👉 (Optional) Your location
- contact: string 👉 (Optional) Your contact information
- signdate: Date|string|TsaServiceInfo 👉 (Optional) In the case of adding a document timestamp, it can't be omitted and can't be a Date.
- signame: string 👉 (Optional) The name of the signature
- ltv: number 👉 (Optional) Type of LTV. Valid values are:
- drawinf: SignDrawInfo 👉 (Optional) Visible signature's information
- area: SignAreaInfo 👉 The signature's drawing area, these numbers are dots on 72dpi.
- x: number 👉 Distance from left
- y: number 👉 Distance from top
- w: number 👉 Width
- h: number 👉 Height
- pageidx: number 👉 (Optional) The index of a page where the signature will be drawn.
- imgData: Array|Uint8Array|ArrayBuffer|string 👉 (Optional) The image's data
- imgType: string 👉 (Optional) The image's type, only support jpg and png
- text: string 👉 (Optional) A text drawing for the signature, not implemented yet
- fontData: PDFLib.StandardFonts|Array|Uint8Array|ArrayBuffer|string 👉 (Optional) The font's data for drawing text, not implemented yet
- area: SignAreaInfo 👉 The signature's drawing area, these numbers are dots on 72dpi.
Let's protect the pdf
Set password protection to the pdf.
/**
* @param {ArrayBuffer} pdf
* @param {string} upwd
* @param {string} opwd
* @return {Promise<Blob>}
*/
async function protect1(pdf, upwd, opwd){
/** @type {EncryptOption} */
var eopt = {
mode: Zga.Crypto.Mode.RC4_40,
permissions: ["modify", "annot-forms", "fill-forms", "extract", "assemble"],
userpwd: upwd,
ownerpwd: opwd,
};
var cyptor = new Zga.PdfCryptor(eopt);
var pdfdoc = await cyptor.encryptPdf(pdf);
u8arr = await pdfdoc.save({"useObjectStreams": false});
return new Blob([u8arr], {"type" : "application/pdf"});
}
Set public-key certificate protection to the pdf.
/**
* @param {ArrayBuffer} pdf
* @param {ArrayBuffer} cert
* @return {Promise<Blob>}
*/
async function protect2(pdf, cert){
/** @type {EncryptOption} */
var eopt = {
mode: Zga.Crypto.Mode.AES_128,
pubkeys: [{
c: cert,
p: ["copy", "modify", "copy-extract", "annot-forms", "fill-forms", "extract", "assemble"],
}],
};
var cyptor = new Zga.PdfCryptor(eopt);
var pdfdoc = await cyptor.encryptPdf(pdf);
u8arr = await pdfdoc.save({"useObjectStreams": false});
return new Blob([u8arr], {"type" : "application/pdf"});
}
Sign and set protection.
/**
* @param {ArrayBuffer} pdf
* @param {ArrayBuffer} cert
* @param {string} pwd
* @param {string} opwd
* @return {Promise<Blob>}
*/
async function signAndProtect1(pdf, cert, pwd, opwd){
/** @type {SignOption} */
var sopt = {
p12cert: cert,
pwd: pwd,
};
/** @type {EncryptOption} */
var eopt = {
mode: Zga.Crypto.Mode.RC4_128,
permissions: ["modify", "annot-forms", "fill-forms", "extract", "assemble"],
ownerpwd: opwd,
};
var signer = new Zga.PdfSigner(sopt);
var u8arr = await signer.sign(pdf, eopt);
return new Blob([u8arr], {"type" : "application/pdf"});
}
Sign and set protection by the same certificate.
/**
* @param {ArrayBuffer} pdf
* @param {ArrayBuffer} cert
* @param {string} pwd
* @return {Promise<Blob>}
*/
async function signAndProtect2(pdf, cert, pwd){
/** @type {SignOption} */
var sopt = {
p12cert: cert,
pwd: pwd,
};
/** @type {EncryptOption} */
var eopt = {
mode: Zga.Crypto.Mode.AES_256,
permissions: ["modify", "annot-forms", "fill-forms", "extract", "assemble"],
pubkeys: [],
};
var signer = new Zga.PdfSigner(sopt);
var u8arr = await signer.sign(pdf, eopt);
return new Blob([u8arr], {"type" : "application/pdf"});
}
Detail of EncryptOption
- mode: Zga.Crypto.Mode 👉 The values of Zga.Crypto.Mode
- RC4_40: 40bit RC4 Encryption
- RC4_128: 128bit RC4 Encryption
- AES_128: 128bit AES Encryption
- AES_256: 256bit AES Encryption
- permissions: Array 👉 (Optional) The set of permissions to be blocked
- "copy": (Only valid on public-key mode) Copy text and graphics from the document;
- "print": Print the document;
- "modify": Modify the contents of the document by operations other than those controlled by 'fill-forms', 'extract' and 'assemble';
- "copy-extract": Copy or otherwise extract text and graphics from the document;
- "annot-forms": Add or modify text annotations, fill in interactive form fields, and, if 'modify' is also set, create or modify interactive form fields (including signature fields);
- "fill-forms": Fill in existing interactive form fields (including signature fields), even if 'annot-forms' is not specified;
- "extract": Extract text and graphics (in support of accessibility to users with disabilities or for other purposes);
- "assemble": Assemble the document (insert, rotate, or delete pages and create bookmarks or thumbnail images), even if 'modify' is not set;
- "print-high": Print the document to a representation from which a faithful digital copy of the PDF content could be generated. When this is not set, printing is limited to a low-level representation of the appearance, possibly of degraded quality.
- userpwd: string 👉 (Optional) User password. Used when opening the pdf.
- ownerpwd: string 👉 (Optional) Owner password. If not specified, a random value is used.
- pubkeys: Array<PubKeyInfo> 👉 (Optional) Array of recipients containing public-key certificates ('c') and permissions ('p').
- c: Array|Uint8Array|ArrayBuffer|string|forge_cert 👉 (Optional) A public-key certificate. Only when you want to encrypt the pdf by the certificate used in signing, the c can be omitted.
- p: Array 👉 (Optional) Permissions
Thanks
- The module of setting protection was almost migrated from TCPDF.
License
This tool is available under the MIT license.
Note
- CORS: Cross-Origin Resource Sharing
- CRL: Certificate Revocation Lists
- DocMDP: Document Modification Detection and Prevention
- LTV: Long-Term Validation
- OCSP: Online Certificate Status Protocol
- TSA: Time Stamp Authority