Compare commits
44 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
0d39f569ae | |
![]() |
e5372e2652 | |
![]() |
9706bfe31a | |
![]() |
4def4068f7 | |
![]() |
1f92ca61d7 | |
![]() |
e1ed49d43f | |
![]() |
de687d8bf7 | |
![]() |
0505fd1e0e | |
![]() |
fd2b97d5b3 | |
![]() |
fa4eb2d5c0 | |
![]() |
cc045171da | |
![]() |
90474e9393 | |
![]() |
0473f700a1 | |
![]() |
8417fad06b | |
![]() |
3216d917ed | |
![]() |
9a47c162aa | |
![]() |
d5b9c4ad22 | |
![]() |
201e5a99b9 | |
![]() |
2c8de1a007 | |
![]() |
f4cbbcdb7c | |
![]() |
d6d6d23dfb | |
![]() |
e6361df479 | |
![]() |
30727cafc3 | |
![]() |
8ca688b8c5 | |
![]() |
e37d5b1ce1 | |
![]() |
202edbc768 | |
![]() |
4136451365 | |
![]() |
5494783141 | |
![]() |
9210c41068 | |
![]() |
dd52e4e8be | |
![]() |
3f0dd0af9f | |
![]() |
8b008ccc34 | |
![]() |
528eaad5eb | |
![]() |
22f6628584 | |
![]() |
5c9ac474b5 | |
![]() |
fe2fd4840d | |
![]() |
adc260b794 | |
![]() |
299aeb3419 | |
![]() |
14ad61b5cf | |
![]() |
c4e5dafa17 | |
![]() |
8c2a614a08 | |
![]() |
8fbedd2c8a | |
![]() |
f932a2bb2a | |
![]() |
b72d38473a |
|
@ -0,0 +1,16 @@
|
|||
name: Build
|
||||
run-name: Build for browser and GAS
|
||||
on: [push]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: ['lts/*']
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm ci
|
||||
- run: npm run build
|
409
README.md
409
README.md
|
@ -1,22 +1,117 @@
|
|||
<div align="center"><img src="logo.png" title="zgapdfsigner"></div>
|
||||
|
||||
# ZgaPdfSigner
|
||||
A javascript tool to sign a pdf in web browser.
|
||||
And it also can be used in Google Apps Script.
|
||||

|
||||

|
||||

|
||||
|
||||
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](https://developers.google.com/apps-script) or [nodejs](https://nodejs.org/).
|
||||
|
||||
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 or a text or both.
|
||||
* A visible signature can be placed on multiple pages. (In the same position)
|
||||
* Sign a pdf and set [DocMDP](https://github.com/zboris12/zgapdfsigner/wiki/API#note).
|
||||
* Add a new signature to a pdf if it has been signed already. (An incremental update)
|
||||
* Add a document timestamp from [TSA](https://github.com/zboris12/zgapdfsigner/wiki/API#note). ( :no_entry_sign:__Not__ available in web browser :sunflower:)
|
||||
* Sign a pdf with a timestamp from [TSA](https://github.com/zboris12/zgapdfsigner/wiki/API#note). ( :no_entry_sign:__Not__ available in web browser :sunflower:)
|
||||
* Enable signature's [LTV](https://github.com/zboris12/zgapdfsigner/wiki/API#note). ( :no_entry_sign:__Not__ available in web browser :sunflower:)
|
||||
* 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](https://github.com/zboris12/zgapdfsigner/wiki/API#note) and [LTV](https://github.com/zboris12/zgapdfsigner/wiki/API#note)
|
||||
|
||||
Because of the [CORS](https://github.com/zboris12/zgapdfsigner/wiki/API#note) security restrictions in web browser,
|
||||
signing with a timestamp from [TSA](https://github.com/zboris12/zgapdfsigner/wiki/API#note) or enabling [LTV](https://github.com/zboris12/zgapdfsigner/wiki/API#note) can only be used in [Google Apps Script](https://developers.google.com/apps-script) or [nodejs](https://nodejs.org/).
|
||||
:sunflower: However, if you can avoid the [CORS](https://github.com/zboris12/zgapdfsigner/wiki/API#note) security restrictions
|
||||
by creating your own service or providing a reverse proxy server, these features are also available in web browser.
|
||||
|
||||
## The Dependencies
|
||||
|
||||
* [pdf-lib](https://pdf-lib.js.org/)
|
||||
* [node-forge](https://github.com/digitalbazaar/forge)
|
||||
* [follow-redirects](https://github.com/follow-redirects/follow-redirects)
|
||||
* [pako](https://github.com/nodeca/pako) - For drawing text
|
||||
* [pdf-fontkit](https://github.com/znacloud/pdf-fontkit) - For drawing text
|
||||
|
||||
## How to use this tool
|
||||
|
||||
:question: For more details please see the [wiki](https://github.com/zboris12/zgapdfsigner/wiki/API).
|
||||
|
||||
### Web Browser
|
||||
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.1.0/zgapdfsigner.js" type="text/javascript"></script>
|
||||
<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://cdn.jsdelivr.net/npm/zgapdfsigner/dist/zgapdfsigner.min.js" type="text/javascript"></script>
|
||||
```
|
||||
When drawing text for signature, importing fontkit and pako library is necessary.
|
||||
```html
|
||||
<script src="https://unpkg.com/pdf-fontkit@1.8.9/dist/fontkit.umd.min.js" type="text/javascript"></script>
|
||||
<script src="https://unpkg.com/pako@1.0.11/dist/pako_inflate.min.js" type="text/javascript"></script>
|
||||
```
|
||||
Thanks to [znacloud](https://github.com/znacloud/pdf-fontkit) for fixing the font subsetting issue in [@pdf-lib/fontkit](https://github.com/Hopding/fontkit).
|
||||
|
||||
### [Google Apps Script](https://developers.google.com/apps-script)
|
||||
Load the dependencies and this tool.
|
||||
```js
|
||||
// Simulate setTimeout function for pdf-lib
|
||||
function setTimeout(func, sleep){
|
||||
Utilities.sleep(sleep);
|
||||
func();
|
||||
}
|
||||
// Simulate clearTimeout function for pdf-fontkit
|
||||
function clearTimeout(timeoutID){
|
||||
// Do nothing
|
||||
}
|
||||
// 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());
|
||||
// It is necessary for drawing text for signature.
|
||||
eval(UrlFetchApp.fetch("https://unpkg.com/pdf-fontkit@1.8.9/dist/fontkit.umd.min.js").getContentText());
|
||||
// Load pako, It is necessary for drawing text for signature.
|
||||
eval(UrlFetchApp.fetch("https://unpkg.com/pako@1.0.11/dist/pako_inflate.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://cdn.jsdelivr.net/npm/zgapdfsigner/dist/zgapdfsigner.min.js").getContentText());
|
||||
```
|
||||
Or simply import the library of [ZgaPdfToolkit](https://script.google.com/macros/library/d/1T0UPf50gGp2fJ4dR1rZfEFgKYC5VpCwUVooCRNySiL7klvIUVsFBCZ9m/7)
|
||||
1. Add the library of ZgaPdfToolkit to your project, and suppose the id of library you defined is "pdfkit".
|
||||
Script id: `1T0UPf50gGp2fJ4dR1rZfEFgKYC5VpCwUVooCRNySiL7klvIUVsFBCZ9m`
|
||||
2. Load the library.
|
||||
```js
|
||||
pdfkit.loadZga(globalThis);
|
||||
```
|
||||
|
||||
### [nodejs](https://nodejs.org/)
|
||||
1. Install
|
||||
```
|
||||
npm install zgapdfsigner
|
||||
```
|
||||
If using [typescript](https://www.typescriptlang.org/) for development, installation of [definitely typed for node-forge](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node-forge) is necessary.
|
||||
```
|
||||
npm install --save-dev @types/node-forge
|
||||
```
|
||||
2. Import
|
||||
```js
|
||||
// CommonJS Mode
|
||||
const Zga = require("zgapdfsigner");
|
||||
// ES Module Mode
|
||||
import { default as Zga } from "zgapdfsigner";
|
||||
// Typescript
|
||||
import * as Zga from "zgapdfsigner";
|
||||
```
|
||||
|
||||
## Let's sign
|
||||
|
@ -35,6 +130,7 @@ async function sign1(pdf, cert, pwd){
|
|||
var sopt = {
|
||||
p12cert: cert,
|
||||
pwd: pwd,
|
||||
permission: 1,
|
||||
};
|
||||
var signer = new Zga.PdfSigner(sopt);
|
||||
var u8arr = await signer.sign(pdf);
|
||||
|
@ -42,7 +138,7 @@ async function sign1(pdf, cert, pwd){
|
|||
}
|
||||
```
|
||||
|
||||
Sign with a visible signature of a picture.
|
||||
Sign with a visible signature of an image.
|
||||
|
||||
```js
|
||||
/**
|
||||
|
@ -65,9 +161,11 @@ async function sign2(pdf, cert, pwd, imgdat, imgtyp){
|
|||
w: 60, // width
|
||||
h: 60, // height
|
||||
},
|
||||
imgInfo: {
|
||||
imgData: imgdat,
|
||||
imgType: imgtyp,
|
||||
},
|
||||
},
|
||||
};
|
||||
var signer = new Zga.PdfSigner(sopt);
|
||||
var u8arr = await signer.sign(pdf);
|
||||
|
@ -75,65 +173,286 @@ async function sign2(pdf, cert, pwd, imgdat, imgtyp){
|
|||
}
|
||||
```
|
||||
|
||||
Sign with a visible signature of drawing a text.
|
||||
Sign with a visible signature by drawing a text.
|
||||
|
||||
```js
|
||||
//TODO
|
||||
/**
|
||||
* @param {ArrayBuffer} pdf
|
||||
* @param {ArrayBuffer} cert
|
||||
* @param {string} pwd
|
||||
* @param {string} txt
|
||||
* @param {ArrayBuffer} fontdat
|
||||
* @return {Promise<Blob>}
|
||||
*/
|
||||
async function sign3(pdf, cert, pwd, txt, fontdat){
|
||||
/** @type {SignOption} */
|
||||
var sopt = {
|
||||
p12cert: cert,
|
||||
pwd: pwd,
|
||||
drawinf: {
|
||||
area: {
|
||||
x: 25, // left
|
||||
y: 150, // top
|
||||
w: 60, // width
|
||||
h: 60, // height
|
||||
},
|
||||
textInfo: {
|
||||
text: txt,
|
||||
fontData: fontdat,
|
||||
color: "#00f0f1",
|
||||
size: 16,
|
||||
},
|
||||
},
|
||||
};
|
||||
var signer = new Zga.PdfSigner(sopt);
|
||||
var u8arr = await signer.sign(pdf);
|
||||
return new Blob([u8arr], {"type" : "application/pdf"});
|
||||
}
|
||||
```
|
||||
|
||||
Use it in Google Apps Script
|
||||
Use it in [Google Apps Script](https://developers.google.com/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());
|
||||
|
||||
/**
|
||||
* @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: new Uint8Array(certBlob.getBytes()),
|
||||
pwd: "some passphrase",
|
||||
p12cert: certBlob.getBytes(),
|
||||
pwd,
|
||||
signdate: "1",
|
||||
ltv: 1,
|
||||
};
|
||||
var signer = new Zga.PdfSigner(sopt);
|
||||
var u8arr = await signer.sign(new Uint8Array(pdfBlob.getBytes()));
|
||||
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"));
|
||||
}
|
||||
```
|
||||
|
||||
## Detail of SignOption
|
||||
Use queryPassword function in [ZgaPdfToolkit](https://script.google.com/macros/library/d/1T0UPf50gGp2fJ4dR1rZfEFgKYC5VpCwUVooCRNySiL7klvIUVsFBCZ9m/5).
|
||||
|
||||
* __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
|
||||
* __contact__: string :point_right: (Optional) Your contact information
|
||||
* __signdate__: Date :point_right: (Optional) The date and time of signing
|
||||
* __signame__: string :point_right: (Optional) The name of the signature
|
||||
* __drawinf__: SignDrawInfo :point_right: (Optional) Visible signature's information
|
||||
* __area__: SignAreaInfo :point_right: The signature's drawing area
|
||||
* __x__: number :point_right: Distance from left
|
||||
* __y__: number :point_right: Distance from top
|
||||
* __w__: number :point_right: Width
|
||||
* __h__: number :point_right: Height
|
||||
* __pageidx__: number :point_right: (Optional) The page index for drawing the signature
|
||||
* __imgData__: Uint8Array|ArrayBuffer|string :point_right: (Optional) The image's data
|
||||
* __imgType__: string :point_right: (Optional) The image's type, __only support jpg and png__
|
||||
* __text__: string :point_right: (Optional) A text drawing on signature, __not implemented yet__
|
||||
* __fontData__: PDFLib.StandardFonts|Uint8Array|ArrayBuffer|string :point_right: (Optional) The font's data for drawing text, __not implemented yet__
|
||||
```js
|
||||
function myfunction(){
|
||||
var spd = SpreadsheetApp.getActiveSpreadsheet();
|
||||
pdfkit.queryPassword("createPdf", "Please input the passphrase", spd.getName());
|
||||
}
|
||||
```
|
||||
|
||||
Use it in [nodejs](https://nodejs.org/)
|
||||
```js
|
||||
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");
|
||||
/** @type {string} */
|
||||
var txt = "I am a test string!";
|
||||
/** @type {string} */
|
||||
var fontPath = m_path.join(__dirname, "_test.ttf");
|
||||
|
||||
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 {Buffer} */
|
||||
var font = null;
|
||||
if(fontPath){
|
||||
font = m_fs.readFileSync(fontPath);
|
||||
}
|
||||
|
||||
/** @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 || txt){
|
||||
sopt.drawinf = {
|
||||
area: {
|
||||
x: 25, // left
|
||||
y: 50, // top
|
||||
w: txt ? undefined : 60,
|
||||
h: txt ? undefined : 100,
|
||||
},
|
||||
pageidx: "2-3", // Placed the signature on the 3rd page and the 4th page. (Indexes of pages start from 0)
|
||||
imgInfo: img ? {
|
||||
imgData: img,
|
||||
imgType: imgType,
|
||||
} : undefined,
|
||||
textInfo: txt ? {
|
||||
text: txt,
|
||||
fontData: font,
|
||||
size: 16,
|
||||
} : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/** @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");
|
||||
}
|
||||
```
|
||||
|
||||
## Let's protect the pdf
|
||||
|
||||
Set password protection to the pdf.
|
||||
|
||||
```js
|
||||
/**
|
||||
* @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.
|
||||
|
||||
```js
|
||||
/**
|
||||
* @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.
|
||||
|
||||
```js
|
||||
/**
|
||||
* @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.
|
||||
|
||||
```js
|
||||
/**
|
||||
* @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"});
|
||||
}
|
||||
```
|
||||
|
||||
## Thanks
|
||||
* The module of setting protection was almost migrated from [TCPDF](http://www.tcpdf.org).
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
# !/bin/sh
|
||||
# set -x
|
||||
|
||||
OUTFLDR=dist
|
||||
if [ -d ${OUTFLDR} ]
|
||||
then
|
||||
rm -f ${OUTFLDR}/*
|
||||
else
|
||||
mkdir ${OUTFLDR}
|
||||
fi
|
||||
|
||||
VER=$(sed -n -r "s/^.*\"version\": ?\"([0-9.]+)\".*$/\1/p" package.json)
|
||||
|
||||
GCCOPT="--charset UTF-8 --compilation_level SIMPLE_OPTIMIZATIONS --warning_level VERBOSE"
|
||||
GCCEXT="--externs closure/google-ext.js --externs closure/forge-ext.js --externs closure/pdflib-ext.js --externs closure/zb-externs.js"
|
||||
jss=""
|
||||
while read js
|
||||
do
|
||||
if [ -n "${js}" ]
|
||||
then
|
||||
c=$(echo "${js}" | cut -b1)
|
||||
if [ "$c" != "#" ]
|
||||
then
|
||||
outf="${OUTFLDR}/_${js}"
|
||||
if [ "${js}" = "zgaindex.js" ]
|
||||
then
|
||||
sed -e "s/\/\/Only for nodejs Start\/\//\/*/g" -e "s/\/\/Only for nodejs End\/\//*\//g" -e "s/ver: \"\"/ver: \"${VER}\"/" "lib/${js}" > "${outf}"
|
||||
else
|
||||
sed -e "s/\/\/Only for nodejs Start\/\//\/*/g" -e "s/\/\/Only for nodejs End\/\//*\//g" "lib/${js}" > "${outf}"
|
||||
fi
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
echo "Created js file: ${outf}"
|
||||
jss="${jss} --js ${outf}"
|
||||
else
|
||||
echo "Failed create js file: ${outf}"
|
||||
exit 10
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done <<EOF
|
||||
zgafetch.js
|
||||
zgacertsutil.js
|
||||
zgapdfcryptor.js
|
||||
zgapdfsigner.js
|
||||
zgaindex.js
|
||||
EOF
|
||||
npx google-closure-compiler ${GCCOPT} ${GCCEXT} ${jss} --js_output_file ${OUTFLDR}/zgapdfsigner.min.js
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
echo "Build result:"
|
||||
ls -l ${OUTFLDR}/*.min.js
|
||||
else
|
||||
echo "google-closure-compiler failed."
|
||||
exit 20
|
||||
fi
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,433 @@
|
|||
/** @const */
|
||||
var forge = {};
|
||||
forge.random = {};
|
||||
/**
|
||||
* @param {number} count
|
||||
* @return {string}
|
||||
*/
|
||||
forge.random.getBytesSync = function(count){};
|
||||
|
||||
forge.util = {};
|
||||
/** @constructor */
|
||||
forge.util.ByteStringBuffer = function(){};
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
forge.util.ByteStringBuffer.prototype.getBytes = function(){};
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {forge.util.ByteStringBuffer}
|
||||
*/
|
||||
forge.util.ByteStringBuffer.prototype.putBytes = function(value){};
|
||||
/**
|
||||
* @param {number} i
|
||||
* @return {forge.util.ByteStringBuffer}
|
||||
*/
|
||||
forge.util.ByteStringBuffer.prototype.putInt16 = function(i){};
|
||||
/**
|
||||
* @param {number} i
|
||||
* @return {forge.util.ByteStringBuffer}
|
||||
*/
|
||||
forge.util.ByteStringBuffer.prototype.putInt24Le = function(i){};
|
||||
/**
|
||||
* @param {number} i
|
||||
* @return {forge.util.ByteStringBuffer}
|
||||
*/
|
||||
forge.util.ByteStringBuffer.prototype.putInt32Le = function(i){};
|
||||
/**
|
||||
* @param {number} b
|
||||
* @param {number} n
|
||||
* @return {forge.util.ByteStringBuffer}
|
||||
*/
|
||||
forge.util.ByteStringBuffer.prototype.fillWithByte = function(b, n){};
|
||||
/**
|
||||
* @param {number} count
|
||||
* @return {forge.util.ByteStringBuffer}
|
||||
*/
|
||||
forge.util.ByteStringBuffer.prototype.truncate = function(count){};
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
forge.util.ByteStringBuffer.prototype.toHex = function(){};
|
||||
/** @type {string} */
|
||||
forge.util.ByteStringBuffer.prototype.data;
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {string=} encoding
|
||||
* @return {forge.util.ByteStringBuffer}
|
||||
*/
|
||||
forge.util.createBuffer = function(input, encoding){};
|
||||
/**
|
||||
* @param {string} hex
|
||||
* @return {string}
|
||||
*/
|
||||
forge.util.hexToBytes = function(hex){};
|
||||
/**
|
||||
* @param {string=} value
|
||||
* @return {string}
|
||||
*/
|
||||
forge.util.decodeUtf8 = function(value){};
|
||||
forge.util.binary = {};
|
||||
forge.util.binary.hex = {};
|
||||
/**
|
||||
* @param {string} str
|
||||
* @return {Uint8Array}
|
||||
*/
|
||||
forge.util.binary.hex.decode = function(str){};
|
||||
|
||||
/** @constructor */
|
||||
forge.asn1 = function(){};
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* strict: (boolean|undefined),
|
||||
* parseAllBytes: (boolean|undefined),
|
||||
* decodeBitStrings: (boolean|undefined),
|
||||
* }}
|
||||
*/
|
||||
var DerOption;
|
||||
/**
|
||||
* @param {forge.util.ByteStringBuffer|string} bytes
|
||||
* @param {DerOption=} options
|
||||
* @return {forge.asn1}
|
||||
*/
|
||||
forge.asn1.fromDer = function(bytes, options){};
|
||||
/**
|
||||
* @param {forge.asn1} obj
|
||||
* @return {forge.util.ByteStringBuffer}
|
||||
*/
|
||||
forge.asn1.toDer = function(obj){};
|
||||
/**
|
||||
* @param {number} num
|
||||
* @return {forge.util.ByteStringBuffer}
|
||||
*/
|
||||
forge.asn1.integerToDer = function(num){};
|
||||
/**
|
||||
* @param {string} oid
|
||||
* @return {forge.util.ByteStringBuffer}
|
||||
*/
|
||||
forge.asn1.oidToDer = function(oid){};
|
||||
forge.asn1.Type = {};
|
||||
/** @type {number} */
|
||||
forge.asn1.Type.NONE;
|
||||
/** @type {number} */
|
||||
forge.asn1.Type.UTF8;
|
||||
/** @type {number} */
|
||||
forge.asn1.Type.SET;
|
||||
/** @type {number} */
|
||||
forge.asn1.Type.SEQUENCE;
|
||||
/** @type {number} */
|
||||
forge.asn1.Type.BOOLEAN;
|
||||
/** @type {number} */
|
||||
forge.asn1.Type.INTEGER;
|
||||
/** @type {number} */
|
||||
forge.asn1.Type.OID;
|
||||
/** @type {number} */
|
||||
forge.asn1.Type.NULL;
|
||||
/** @type {number} */
|
||||
forge.asn1.Type.OCTETSTRING;
|
||||
/** @type {number} */
|
||||
forge.asn1.Type.PRINTABLESTRING;
|
||||
/** @type {number} */
|
||||
forge.asn1.Type.ENUMERATED;
|
||||
/** @type {number} */
|
||||
forge.asn1.Type.BITSTRING;
|
||||
forge.asn1.Class = {};
|
||||
/** @type {number} */
|
||||
forge.asn1.Class.UNIVERSAL;
|
||||
/** @type {number} */
|
||||
forge.asn1.Class.CONTEXT_SPECIFIC;
|
||||
/**
|
||||
* @param {number} tagClass
|
||||
* @param {number} type
|
||||
* @param {boolean} constructed
|
||||
* @param {Array<forge.asn1>|forge.asn1|number|string} value
|
||||
* @param {Object=} options
|
||||
* @return {forge.asn1}
|
||||
*/
|
||||
forge.asn1.create = function(tagClass, type, constructed, value, options){};
|
||||
/**
|
||||
* @param {forge.asn1} obj
|
||||
* @param {Object<string, *>} v
|
||||
* @param {Object<string, *>} capture
|
||||
* @param {Array<string>} errors
|
||||
*/
|
||||
forge.asn1.validate = function(obj, v, capture, errors){};
|
||||
/** @type {Array<forge.asn1>|string} */
|
||||
forge.asn1.prototype.value;
|
||||
/** @type {number} */
|
||||
forge.asn1.prototype.tagClass;
|
||||
/** @type {number} */
|
||||
forge.asn1.prototype.type;
|
||||
/** @type {boolean} */
|
||||
forge.asn1.prototype.constructed;
|
||||
|
||||
/** @constructor */
|
||||
const forge_BigInteger = function(){};
|
||||
/**
|
||||
* @param {forge_BigInteger} a
|
||||
* @return {number}
|
||||
*/
|
||||
forge_BigInteger.prototype.compareTo = function(a){};
|
||||
/** @constructor */
|
||||
const forge_cert = function(){};
|
||||
/** @type {forge_key} */
|
||||
forge_cert.prototype.publicKey;
|
||||
/** @type {forge_cert_issuer} */
|
||||
forge_cert.prototype.issuer;
|
||||
/** @type {string} */
|
||||
forge_cert.prototype.serialNumber;
|
||||
/** @type {forge_cert_issuer} */
|
||||
forge_cert.prototype.subject;
|
||||
/**
|
||||
* @param {forge_cert} parent
|
||||
* @return {boolean}
|
||||
*/
|
||||
forge_cert.prototype.isIssuer = function(parent){};
|
||||
/**
|
||||
* @param {string|forge_cert_extension} nm
|
||||
* @return {forge_cert_extension}
|
||||
*/
|
||||
forge_cert.prototype.getExtension = function(nm){};
|
||||
/** @constructor */
|
||||
const forge_key = function(){};
|
||||
/** @type {forge_BigInteger} */
|
||||
forge_key.prototype.n;
|
||||
/** @type {forge_BigInteger} */
|
||||
forge_key.prototype.e;
|
||||
/** @constructor */
|
||||
const forge_cert_issuer = function(){};
|
||||
/** @type {Array<forge_cert_attr>} */
|
||||
forge_cert_issuer.prototype.attributes;
|
||||
/**
|
||||
* @param {string} sn
|
||||
* @return {forge.asn1}
|
||||
*/
|
||||
forge_cert_issuer.prototype.getField = function(sn){};
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* id: (string|undefined),
|
||||
* name: (string|undefined),
|
||||
* critical: (boolean|undefined),
|
||||
* value: (string|undefined),
|
||||
* cA: (boolean|undefined),
|
||||
* }}
|
||||
*/
|
||||
var forge_cert_extension;
|
||||
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* valueTagClass: (number|undefined),
|
||||
* type: (string|undefined),
|
||||
* value: (string|undefined),
|
||||
* }}
|
||||
*/
|
||||
var forge_cert_attr;
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* key: (forge_key|undefined),
|
||||
* certificate: (forge_cert|undefined),
|
||||
* digestAlgorithm: (string|undefined),
|
||||
* authenticatedAttributes: (Array<forge_cert_attr>|undefined),
|
||||
* unauthenticatedAttributes: (Array<forge_cert_attr>|undefined),
|
||||
* signature: (string|undefined),
|
||||
* }}
|
||||
*/
|
||||
var forge_signer;
|
||||
|
||||
/** @constructor */
|
||||
forge.pkcs7 = function(){};
|
||||
/**
|
||||
* @return {forge.pkcs7}
|
||||
*/
|
||||
forge.pkcs7.createSignedData = function(){};
|
||||
/**
|
||||
* @return {forge.pkcs7}
|
||||
*/
|
||||
forge.pkcs7.createEnvelopedData = function(){};
|
||||
/**
|
||||
* @param {forge_cert} cert
|
||||
*/
|
||||
forge.pkcs7.prototype.addCertificate = function(cert){};
|
||||
/**
|
||||
* @param {forge_signer} signer
|
||||
*/
|
||||
forge.pkcs7.prototype.addSigner = function(signer){};
|
||||
/** @type {Array<forge_signer>} */
|
||||
forge.pkcs7.prototype.signers;
|
||||
/** @type {Array<forge.asn1>} */
|
||||
forge.pkcs7.prototype.signerInfos;
|
||||
/**
|
||||
* @param {forge_cert} cert
|
||||
*/
|
||||
forge.pkcs7.prototype.addRecipient = function(cert){};
|
||||
/** @type {forge.util.ByteStringBuffer} */
|
||||
forge.pkcs7.prototype.content;
|
||||
/**
|
||||
* @param {forge.util.ByteStringBuffer=} key
|
||||
* @param {string=} cipher
|
||||
*/
|
||||
forge.pkcs7.prototype.encrypt = function(key, cipher){};
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* detached: (boolean|undefined),
|
||||
* }}
|
||||
*/
|
||||
var forge_sign_option;
|
||||
/**
|
||||
* @param {forge_sign_option} options
|
||||
*/
|
||||
forge.pkcs7.prototype.sign = function(options){};
|
||||
/**
|
||||
* @return {forge.asn1}
|
||||
*/
|
||||
forge.pkcs7.prototype.toAsn1 = function(){};
|
||||
|
||||
/** @constructor */
|
||||
forge.pkcs12 = function(){};
|
||||
/**
|
||||
* @param {forge.asn1} obj
|
||||
* @param {boolean=} strict
|
||||
* @param {string=} password
|
||||
* @return {forge.pkcs12}
|
||||
*/
|
||||
forge.pkcs12.pkcs12FromAsn1 = function(obj, strict, password){};
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* localKeyId: (string|undefined),
|
||||
* localKeyIdHex: (string|undefined),
|
||||
* friendlyName: (string|undefined),
|
||||
* bagType: (string|undefined),
|
||||
* }}
|
||||
*/
|
||||
var P12BagsFilter;
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* cert: forge_cert,
|
||||
* key: forge_key,
|
||||
* }}
|
||||
*/
|
||||
var P12Bag;
|
||||
/**
|
||||
* @param {P12BagsFilter} filter
|
||||
* @return {Object<string, Object<string|number, P12Bag>>}
|
||||
*/
|
||||
forge.pkcs12.prototype.getBags = function(filter){};
|
||||
|
||||
forge.pki = {};
|
||||
forge.pki.oids = {};
|
||||
/** @type {string} */
|
||||
forge.pki.oids.certBag;
|
||||
/** @type {string} */
|
||||
forge.pki.oids.pkcs8ShroudedKeyBag;
|
||||
/** @type {string} */
|
||||
forge.pki.oids.sha1;
|
||||
/** @type {string} */
|
||||
forge.pki.oids.sha256;
|
||||
/** @type {string} */
|
||||
forge.pki.oids.contentType;
|
||||
/** @type {string} */
|
||||
forge.pki.oids.data;
|
||||
/** @type {string} */
|
||||
forge.pki.oids.messageDigest;
|
||||
/** @type {string} */
|
||||
forge.pki.oids.signingTime;
|
||||
/** @type {string} */
|
||||
forge.pki.oids.rsaEncryption;
|
||||
/** @type {string} */
|
||||
forge.pki.oids.sha256WithRSAEncryption;
|
||||
/** @type {string} */
|
||||
forge.pki.oids.commonName;
|
||||
/** @lends {forge.pki.oids} */
|
||||
forge.oids = forge.pki.oids;
|
||||
/**
|
||||
* @param {forge.asn1} obj
|
||||
* @param {boolean=} computeHash
|
||||
* @return {forge_cert}
|
||||
*/
|
||||
forge.pki.certificateFromAsn1 = function(obj, computeHash){};
|
||||
/**
|
||||
* @param {forge_cert} cert
|
||||
* @return {forge.asn1}
|
||||
*/
|
||||
forge.pki.certificateToAsn1 = function(cert){};
|
||||
/**
|
||||
* @param {forge_cert_issuer} obj
|
||||
* @return {forge.asn1}
|
||||
*/
|
||||
forge.pki.distinguishedNameToAsn1 = function(obj){};
|
||||
/**
|
||||
* @param {forge_key} key
|
||||
* @return {forge.asn1}
|
||||
*/
|
||||
forge.pki.publicKeyToRSAPublicKey = function(key){};
|
||||
|
||||
forge.md = {};
|
||||
/** @constructor */
|
||||
forge.md.digest = function(){};
|
||||
/**
|
||||
* @param {string=} msg
|
||||
* @param {string=} encoding
|
||||
* @return {forge.md.digest}
|
||||
*/
|
||||
forge.md.digest.prototype.update = function(msg, encoding){};
|
||||
/**
|
||||
* @return {forge.util.ByteStringBuffer}
|
||||
*/
|
||||
forge.md.digest.prototype.digest = function(){};
|
||||
forge.md.md5 = {};
|
||||
forge.md.sha1 = {};
|
||||
forge.md.sha256 = {};
|
||||
/**
|
||||
* @return {forge.md.digest}
|
||||
*/
|
||||
forge.md.md5.create = function(){};
|
||||
/**
|
||||
* @return {forge.md.digest}
|
||||
*/
|
||||
forge.md.sha1.create = function(){};
|
||||
/**
|
||||
* @return {forge.md.digest}
|
||||
*/
|
||||
forge.md.sha256.create = function(){};
|
||||
|
||||
forge.cipher = {};
|
||||
/** @constructor */
|
||||
forge.cipher.BlockCipher = function(){};
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* iv: (string|undefined),
|
||||
* additionalData: (string|undefined),
|
||||
* tagLength: (number|undefined),
|
||||
* tag: (string|undefined),
|
||||
* output: (forge.util.ByteStringBuffer|undefined),
|
||||
* }}
|
||||
*/
|
||||
var CipherOptions;
|
||||
/**
|
||||
* @param {CipherOptions} options
|
||||
*/
|
||||
forge.cipher.BlockCipher.prototype.start = function(options){};
|
||||
/**
|
||||
* @param {forge.util.ByteStringBuffer} input
|
||||
*/
|
||||
forge.cipher.BlockCipher.prototype.update = function(input){};
|
||||
/**
|
||||
* @return {boolean}
|
||||
*/
|
||||
forge.cipher.BlockCipher.prototype.finish = function(){};
|
||||
/** @type {forge.util.ByteStringBuffer} */
|
||||
forge.cipher.BlockCipher.prototype.output;
|
||||
/**
|
||||
* @param {string} algorithm
|
||||
* @param {forge.util.ByteStringBuffer} key
|
||||
* @return {forge.cipher.BlockCipher}
|
||||
*/
|
||||
forge.cipher.createCipher = function(algorithm, key) {};
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* contentType: (string|undefined),
|
||||
* headers: (Object<string, *>|undefined),
|
||||
* method: (string|undefined),
|
||||
* payload: (string|Uint8Array|undefined),
|
||||
* useIntranet: (boolean|undefined),
|
||||
* validateHttpsCertificates: (boolean|undefined),
|
||||
* followRedirects: (boolean|undefined),
|
||||
* muteHttpExceptions: (boolean|undefined),
|
||||
* escaping: (boolean|undefined),
|
||||
* }}
|
||||
*/
|
||||
var UrlFetchParams;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function GoogleUrlFetchApp(){}
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {UrlFetchParams} params
|
||||
* @return {HTTPResponse}
|
||||
*/
|
||||
GoogleUrlFetchApp.prototype.fetch = function(url, params){};
|
||||
|
||||
/**
|
||||
* @const {!GoogleUrlFetchApp}
|
||||
*/
|
||||
var UrlFetchApp;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function HTTPResponse(){}
|
||||
/**
|
||||
* @return {GBlob}
|
||||
*/
|
||||
HTTPResponse.prototype.getBlob = function(){};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function GBlob(){}
|
||||
/**
|
||||
* @return {Array<number>}
|
||||
*/
|
||||
GBlob.prototype.getBytes = function(){};
|
||||
|
|
@ -0,0 +1,673 @@
|
|||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* ignoreEncryption: (boolean|undefined),
|
||||
* parseSpeed: (number|undefined),
|
||||
* throwOnInvalidObject: (boolean|undefined),
|
||||
* }}
|
||||
*/
|
||||
var PdfLoadOptions;
|
||||
|
||||
/** @const */
|
||||
var pako = {};
|
||||
/**
|
||||
* @param {Uint8Array} input
|
||||
* @return {Uint8Array}
|
||||
*/
|
||||
pako.inflate = function(input){};
|
||||
|
||||
/** @constructor */
|
||||
var Fontkit = function(){};
|
||||
/** @const {Fontkit} */
|
||||
var fontkit;
|
||||
|
||||
/** @const */
|
||||
var PDFLib = {};
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @param {Array<number>} buffer
|
||||
* @param {number} offset
|
||||
* @return {number}
|
||||
*/
|
||||
PDFLib.copyStringIntoBuffer = function(str, buffer, offset){};
|
||||
/**
|
||||
* @param {string|ArrayBuffer|Uint8Array} input
|
||||
* @return {Uint8Array}
|
||||
*/
|
||||
PDFLib.toUint8Array = function(input){};
|
||||
/**
|
||||
* @param {string} text
|
||||
* @return {string}
|
||||
*/
|
||||
PDFLib.cleanText = function(text){};
|
||||
/**
|
||||
* @param {string} text
|
||||
* @return {Array<string>}
|
||||
*/
|
||||
PDFLib.lineSplit = function(text){};
|
||||
/**
|
||||
* @param {string} text
|
||||
* @return {boolean}
|
||||
*/
|
||||
PDFLib.isNewlineChar = function(text){};
|
||||
/**
|
||||
* @param {string} fntnm
|
||||
* @return {boolean}
|
||||
*/
|
||||
PDFLib.isStandardFont = function(fntnm){};
|
||||
|
||||
/** @constructor */
|
||||
PDFLib.PDFDocument = function(){};
|
||||
/**
|
||||
* @param {string|Uint8Array|ArrayBuffer} pdf
|
||||
* @param {PdfLoadOptions=} options
|
||||
* @return {PDFLib.PDFDocument}
|
||||
*/
|
||||
PDFLib.PDFDocument.load = function(pdf, options){};
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* useObjectStreams: (boolean|undefined),
|
||||
* addDefaultPage: (boolean|undefined),
|
||||
* objectsPerTick: (number|undefined),
|
||||
* }}
|
||||
*/
|
||||
var PdfSaveOptions;
|
||||
/**
|
||||
* @param {PdfSaveOptions} options
|
||||
* @returns {Promise<Uint8Array>}
|
||||
*/
|
||||
PDFLib.PDFDocument.prototype.save = function(options){};
|
||||
/**
|
||||
* @returns {Array<PDFLib.PDFPage>}
|
||||
*/
|
||||
PDFLib.PDFDocument.prototype.getPages = function(){};
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
PDFLib.PDFDocument.prototype.getPageCount = function(){};
|
||||
/**
|
||||
* @param {ArrayBuffer|Uint8Array|string} png
|
||||
* @returns {Promise<PDFLib.PDFImage>}
|
||||
*/
|
||||
PDFLib.PDFDocument.prototype.embedPng = function(png){};
|
||||
/**
|
||||
* @param {ArrayBuffer|Uint8Array|string} jpg
|
||||
* @returns {Promise<PDFLib.PDFImage>}
|
||||
*/
|
||||
PDFLib.PDFDocument.prototype.embedJpg = function(jpg){};
|
||||
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* customName: (string|undefined),
|
||||
* features: (Object<string, boolean>|undefined),
|
||||
* subset: (boolean|undefined),
|
||||
* }}
|
||||
*/
|
||||
var EmbedFontOptions;
|
||||
/**
|
||||
* @param {ArrayBuffer|Uint8Array} font
|
||||
* @param {EmbedFontOptions=} options
|
||||
* @returns {Promise<PDFLib.PDFFont>}
|
||||
*/
|
||||
PDFLib.PDFDocument.prototype.embedFont = function(font, options){};
|
||||
/**
|
||||
* @param {string} font
|
||||
* @param {string=} customName
|
||||
* @returns {PDFLib.PDFFont}
|
||||
*/
|
||||
PDFLib.PDFDocument.prototype.embedStandardFont = function(font, customName){};
|
||||
/**
|
||||
* @param {Fontkit} fkt
|
||||
*/
|
||||
PDFLib.PDFDocument.prototype.registerFontkit = function(fkt){};
|
||||
/**
|
||||
* @returns {Promise<number>}
|
||||
*/
|
||||
PDFLib.PDFDocument.prototype.flush = function(){};
|
||||
/** @type {PDFLib.PDFCatalog} */
|
||||
PDFLib.PDFDocument.prototype.catalog;
|
||||
/** @type {PDFLib.PDFContext} */
|
||||
PDFLib.PDFDocument.prototype.context;
|
||||
|
||||
/** @constructor */
|
||||
PDFLib.PDFAcroField = function(){};
|
||||
/**
|
||||
* @returns {PDFLib.PDFString|PDFLib.PDFHexString}
|
||||
*/
|
||||
PDFLib.PDFAcroField.prototype.T = function(){};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFAcroField}
|
||||
*/
|
||||
PDFLib.PDFAcroTerminal = function(){};
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFAcroTerminal}
|
||||
*/
|
||||
PDFLib.PDFAcroSignature = function(){};
|
||||
|
||||
/** @constructor */
|
||||
PDFLib.PDFAcroForm = function(){};
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* 0: PDFLib.PDFAcroField,
|
||||
* 1: PDFLib.PDFRef,
|
||||
* }}
|
||||
*/
|
||||
var PdfFieldInfo;
|
||||
/**
|
||||
* @return {Array<PdfFieldInfo>}
|
||||
*/
|
||||
PDFLib.PDFAcroForm.prototype.getAllFields = function(){};
|
||||
/**
|
||||
* @param {PDFLib.PDFRef} field
|
||||
*/
|
||||
PDFLib.PDFAcroForm.prototype.addField = function(field){};
|
||||
/** @type {PDFLib.PDFDict} */
|
||||
PDFLib.PDFAcroForm.prototype.dict;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFDict}
|
||||
*/
|
||||
PDFLib.PDFCatalog = function(){};
|
||||
/**
|
||||
* @param {PDFLib.PDFName} name
|
||||
* @param {PDFLib.PDFObject} object
|
||||
*/
|
||||
PDFLib.PDFCatalog.prototype.set = function(name, object){};
|
||||
/**
|
||||
* @return {PDFLib.PDFDict}
|
||||
*/
|
||||
PDFLib.PDFCatalog.prototype.AcroForm = function(){};
|
||||
/**
|
||||
* @return {PDFLib.PDFAcroForm}
|
||||
*/
|
||||
PDFLib.PDFCatalog.prototype.getOrCreateAcroForm = function(){};
|
||||
|
||||
/** @constructor */
|
||||
PDFLib.PDFPage = function(){};
|
||||
/** @type {PDFLib.PDFRef} */
|
||||
PDFLib.PDFPage.prototype.ref;
|
||||
/** @type {PDFLib.PDFPageLeaf} */
|
||||
PDFLib.PDFPage.prototype.node;
|
||||
/**
|
||||
* @return {PDFLib.Rotation}
|
||||
*/
|
||||
PDFLib.PDFPage.prototype.getRotation = function(){};
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* width: number,
|
||||
* height: number,
|
||||
* }}
|
||||
*/
|
||||
var PdfSize;
|
||||
/**
|
||||
* @return {PdfSize}
|
||||
*/
|
||||
PDFLib.PDFPage.prototype.getSize = function(){};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFDict}
|
||||
*/
|
||||
PDFLib.PDFPageLeaf = function(){};
|
||||
/**
|
||||
* @param {PDFLib.PDFName} name
|
||||
* @param {PDFLib.PDFObject} object
|
||||
*/
|
||||
PDFLib.PDFPageLeaf.prototype.set = function(name, object){};
|
||||
/**
|
||||
* @return {PDFLib.PDFArray}
|
||||
*/
|
||||
PDFLib.PDFPageLeaf.prototype.Annots = function(){};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFRef = function(){};
|
||||
/** @type {number} */
|
||||
PDFLib.PDFRef.prototype.objectNumber;
|
||||
/** @type {number} */
|
||||
PDFLib.PDFRef.prototype.generationNumber;
|
||||
/**
|
||||
* @param {number} objectNumber
|
||||
* @param {number=} generationNumber
|
||||
* @return {PDFLib.PDFRef}
|
||||
*/
|
||||
PDFLib.PDFRef.of = function(objectNumber, generationNumber){};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
PDFLib.PDFContext = function(){};
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* 0: PDFLib.PDFRef,
|
||||
* 1: PDFLib.PDFObject,
|
||||
* }}
|
||||
*/
|
||||
var PdfObjEntry;
|
||||
/** @return {Array<PdfObjEntry>} */
|
||||
PDFLib.PDFContext.prototype.enumerateIndirectObjects = function(){};
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* Root: PDFLib.PDFRef,
|
||||
* Info: PDFLib.PDFRef,
|
||||
* ID: (PDFLib.PDFArray|undefined),
|
||||
* }}
|
||||
*/
|
||||
var PdfTrailerInfo;
|
||||
/** @type {PdfTrailerInfo} */
|
||||
PDFLib.PDFContext.prototype.trailerInfo;
|
||||
/**
|
||||
* @param {PDFLib.PDFRef} ref
|
||||
* @param {PDFLib.PDFObject} object
|
||||
*/
|
||||
PDFLib.PDFContext.prototype.assign = function(ref, object){};
|
||||
/**
|
||||
* @param {PDFLib.PDFObject} object
|
||||
* @return {PDFLib.PDFRef}
|
||||
*/
|
||||
PDFLib.PDFContext.prototype.register = function(object){};
|
||||
/**
|
||||
* @return {PDFLib.PDFRef}
|
||||
*/
|
||||
PDFLib.PDFContext.prototype.nextRef = function(){};
|
||||
/**
|
||||
* @param {*} literal
|
||||
* @return {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFContext.prototype.obj = function(literal){};
|
||||
/**
|
||||
* @param {string|Uint8Array} contents
|
||||
* @param {PDFLib.PDFDict=} dict
|
||||
* @return {PDFLib.PDFRawStream}
|
||||
*/
|
||||
PDFLib.PDFContext.prototype.flateStream = function(contents, dict){};
|
||||
/**
|
||||
* @param {PDFLib.PDFRef} ref
|
||||
* @return {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFContext.prototype.lookup = function(ref){};
|
||||
/**
|
||||
* @param {PDFLib.PDFRef} ref
|
||||
* @param {*} typ
|
||||
* @return {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFContext.prototype.lookupMaybe = function(ref, typ){};
|
||||
|
||||
/** @constructor */
|
||||
PDFLib.PDFObject = function(){};
|
||||
/** @type {Map<PDFLib.PDFRef, PDFLib.PDFObject>} */
|
||||
PDFLib.PDFObject.prototype.dict;
|
||||
/** @type {Array<PDFLib.PDFName>} */
|
||||
PDFLib.PDFObject.prototype.array;
|
||||
/**
|
||||
* @param {Array<number>} _buffer
|
||||
* @param {number} _offset
|
||||
* @return {number}
|
||||
*/
|
||||
PDFLib.PDFObject.prototype.copyBytesInto = function(_buffer, _offset){};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFName = function(){};
|
||||
/** @type {PDFLib.PDFName} */
|
||||
PDFLib.PDFName.Annots;
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {PDFLib.PDFName}
|
||||
*/
|
||||
PDFLib.PDFName.of = function(value){};
|
||||
/** @return {string} */
|
||||
PDFLib.PDFName.prototype.asString = function(){};
|
||||
/** @type {string} */
|
||||
PDFLib.PDFName.prototype.encodedName;
|
||||
/** @type {number} */
|
||||
PDFLib.PDFName.prototype.numberValue;
|
||||
/** @return {string} */
|
||||
PDFLib.PDFName.prototype.value = function(){};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFObject}
|
||||
* @param {PDFLib.PDFContext} context
|
||||
*/
|
||||
PDFLib.PDFArray = function(context){};
|
||||
/**
|
||||
* @param {PDFLib.PDFObject} object
|
||||
*/
|
||||
PDFLib.PDFArray.prototype.push = function(object){};
|
||||
/**
|
||||
* @param {number} idx
|
||||
* @return {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFArray.prototype.get = function(idx){};
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
PDFLib.PDFArray.prototype.size = function(){};
|
||||
/**
|
||||
* @return {Array<PDFLib.PDFObject>}
|
||||
*/
|
||||
PDFLib.PDFArray.prototype.asArray = function(){};
|
||||
/**
|
||||
* @param {number} idx
|
||||
* @param {*} typ
|
||||
* @return {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFArray.prototype.lookupMaybe = function(idx, typ){};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFString = function(){};
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {PDFLib.PDFString}
|
||||
*/
|
||||
PDFLib.PDFString.of = function(value){};
|
||||
/**
|
||||
* @param {Date} value
|
||||
* @return {PDFLib.PDFString}
|
||||
*/
|
||||
PDFLib.PDFString.fromDate = function(value){};
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
PDFLib.PDFString.prototype.asString = function(){};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFHexString = function(){};
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {PDFLib.PDFHexString}
|
||||
*/
|
||||
PDFLib.PDFHexString.of = function(value){};
|
||||
/**
|
||||
* @param {string} value
|
||||
* @return {PDFLib.PDFHexString}
|
||||
*/
|
||||
PDFLib.PDFHexString.fromText = function(value){};
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
PDFLib.PDFHexString.prototype.decodeText = function(){};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFNumber = function(){};
|
||||
/**
|
||||
* @param {number} value
|
||||
* @return {PDFLib.PDFNumber}
|
||||
*/
|
||||
PDFLib.PDFNumber.of = function(value){};
|
||||
|
||||
/** @constructor */
|
||||
PDFLib.PDFImage = function(){};
|
||||
/**
|
||||
* @return {PdfSize}
|
||||
*/
|
||||
PDFLib.PDFImage.prototype.size = function(){};
|
||||
/** @type {PDFLib.PDFRef} */
|
||||
PDFLib.PDFImage.prototype.ref;
|
||||
|
||||
/** @constructor */
|
||||
PDFLib.StandardFontEmbedder = function(){};
|
||||
/**
|
||||
* @param {string} fontName
|
||||
* @param {string=} customName
|
||||
* @return {PDFLib.StandardFontEmbedder}
|
||||
*/
|
||||
PDFLib.StandardFontEmbedder.for = function(fontName, customName){};
|
||||
|
||||
/** @constructor */
|
||||
PDFLib.CustomFontEmbedder = function(){};
|
||||
/**
|
||||
* @param {Fontkit} fontkit
|
||||
* @param {Uint8Array} fontData
|
||||
* @param {string=} customName
|
||||
* @return {PDFLib.CustomFontEmbedder}
|
||||
*/
|
||||
PDFLib.CustomFontEmbedder.for = function(fontkit, fontData, customName){};
|
||||
|
||||
/** @constructor */
|
||||
PDFLib.PDFFont = function(){};
|
||||
/**
|
||||
* @param {PDFLib.PDFRef} ref
|
||||
* @param {PDFLib.PDFDocument} doc
|
||||
* @param {PDFLib.StandardFontEmbedder|PDFLib.CustomFontEmbedder} embedder
|
||||
* @return {PDFLib.PDFFont}
|
||||
*/
|
||||
PDFLib.PDFFont.of = function(ref, doc, embedder){};
|
||||
/** @type {PDFLib.PDFRef} */
|
||||
PDFLib.PDFFont.prototype.ref;
|
||||
/** @type {string} */
|
||||
PDFLib.PDFFont.prototype.name;
|
||||
/**
|
||||
* @param {string} text
|
||||
* @return {PDFLib.PDFHexString }
|
||||
*/
|
||||
PDFLib.PDFFont.prototype.encodeText = function(text){};
|
||||
/**
|
||||
* @param {number} size
|
||||
* @param {Object<string, boolean>=} options
|
||||
* @return {number}
|
||||
*/
|
||||
PDFLib.PDFFont.prototype.heightAtSize = function(size, options){};
|
||||
/**
|
||||
* @param {string} text
|
||||
* @param {number} size
|
||||
* @return {number}
|
||||
*/
|
||||
PDFLib.PDFFont.prototype.widthOfTextAtSize = function(text, size){};
|
||||
|
||||
PDFLib.RotationTypes = {};
|
||||
/** @type {string} */
|
||||
PDFLib.RotationTypes.Degrees;
|
||||
/** @constructor */
|
||||
PDFLib.Rotation = function(){};
|
||||
/** @type {string} */
|
||||
PDFLib.Rotation.prototype.type;
|
||||
/**
|
||||
* @param {number} d
|
||||
* @return {PDFLib.Rotation}
|
||||
*/
|
||||
PDFLib.degrees = function(d){};
|
||||
/**
|
||||
* @param {PDFLib.Rotation} rot
|
||||
* @return {PDFLib.Rotation}
|
||||
*/
|
||||
PDFLib.toDegrees = function(rot){};
|
||||
|
||||
/** @constructor */
|
||||
PDFLib.PDFOperator = function(){};
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* x: (number|undefined),
|
||||
* y: (number|undefined),
|
||||
* width: (number|undefined),
|
||||
* height: (number|undefined),
|
||||
* rotate: (PDFLib.Rotation|undefined),
|
||||
* xSkew: (PDFLib.Rotation|undefined),
|
||||
* ySkew: (PDFLib.Rotation|undefined),
|
||||
* graphicsState: (string|undefined),
|
||||
* }}
|
||||
*/
|
||||
var PdfDrawimgOption;
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {PdfDrawimgOption} options
|
||||
* @return {Array<PDFLib.PDFOperator>}
|
||||
*/
|
||||
PDFLib.drawImage = function(name, options){};
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
PDFLib.Color = function(){};
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* color: PDFLib.Color,
|
||||
* font: string,
|
||||
* graphicsState: (string|undefined),
|
||||
* lineHeight: number,
|
||||
* size: number,
|
||||
* rotate: (PDFLib.Rotation|undefined),
|
||||
* xSkew: (PDFLib.Rotation|undefined),
|
||||
* ySkew: (PDFLib.Rotation|undefined),
|
||||
* x: number,
|
||||
* y: number,
|
||||
* }}
|
||||
*/
|
||||
var DrawLinesOfTextOptions;
|
||||
/**
|
||||
* @param {Array<PDFLib.PDFHexString>} lines
|
||||
* @param {DrawLinesOfTextOptions} options
|
||||
* @return {Array<PDFLib.PDFOperator>}
|
||||
*/
|
||||
PDFLib.drawLinesOfText = function(lines, options){};
|
||||
/**
|
||||
* @param {number} red
|
||||
* @param {number} green
|
||||
* @param {number} blue
|
||||
* @return {PDFLib.Color}
|
||||
*/
|
||||
PDFLib.rgb = function(red, green, blue){};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
PDFLib.Cache = function(){};
|
||||
/**
|
||||
* @return {Uint8Array}
|
||||
*/
|
||||
PDFLib.Cache.prototype.access = function(){};
|
||||
/** @type {Uint8Array} */
|
||||
PDFLib.Cache.prototype.value;
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFStream = function(){};
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFStream}
|
||||
*/
|
||||
PDFLib.PDFFlateStream = function(){};
|
||||
/** @type {PDFLib.Cache} */
|
||||
PDFLib.PDFFlateStream.prototype.contentsCache;
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFFlateStream}
|
||||
*/
|
||||
PDFLib.PDFContentStream = function(){};
|
||||
/**
|
||||
* @param {PDFLib.PDFDict} dict
|
||||
* @param {Array<PDFLib.PDFOperator>} operators
|
||||
* @param {boolean=} encode
|
||||
* @return {PDFLib.PDFContentStream}
|
||||
*/
|
||||
PDFLib.PDFContentStream.of = function(dict, operators, encode){};
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFStream}
|
||||
*/
|
||||
PDFLib.PDFRawStream = function(){};
|
||||
/** @type {PDFLib.PDFDict} */
|
||||
PDFLib.PDFRawStream.prototype.dict;
|
||||
/**
|
||||
* @return {Uint8Array}
|
||||
*/
|
||||
PDFLib.PDFRawStream.prototype.getContents = function(){};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFCrossRefSection = function(){};
|
||||
/**
|
||||
* @return {PDFLib.PDFCrossRefSection}
|
||||
*/
|
||||
PDFLib.PDFCrossRefSection.create = function(){};
|
||||
/**
|
||||
* @param {PDFLib.PDFRef} ref
|
||||
* @param {number} offset
|
||||
*/
|
||||
PDFLib.PDFCrossRefSection.prototype.addEntry = function (ref, offset){};
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFDict = function(){};
|
||||
/**
|
||||
* @param {PDFLib.PDFName} key
|
||||
* @param {PDFLib.PDFObject} value
|
||||
*/
|
||||
PDFLib.PDFDict.prototype.set = function(key, value){};
|
||||
/**
|
||||
* @param {PDFLib.PDFName} key
|
||||
* @param {*} typ
|
||||
* @return {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFDict.prototype.lookupMaybe = function(key, typ){};
|
||||
/**
|
||||
* @param {PDFLib.PDFName} key
|
||||
* @return {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFDict.prototype.lookup = function(key){};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFWriter = function(){};
|
||||
/**
|
||||
* @param {PDFLib.PDFContext} context
|
||||
* @param {number} objectsPerTick
|
||||
* @return {PDFLib.PDFWriter}
|
||||
*/
|
||||
PDFLib.PDFWriter.forContext = function(context, objectsPerTick){};
|
||||
/**
|
||||
* @return {PDFLib.PDFDict}
|
||||
*/
|
||||
PDFLib.PDFWriter.prototype.createTrailerDict = function(){};
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFTrailerDict = function(){};
|
||||
/**
|
||||
* @param {PDFLib.PDFDict} dict
|
||||
* @return {PDFLib.PDFTrailerDict}
|
||||
*/
|
||||
PDFLib.PDFTrailerDict.of = function(dict){};
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFTrailer = function(){};
|
||||
/**
|
||||
* @param {number} offset
|
||||
* @return {PDFLib.PDFTrailer}
|
||||
*/
|
||||
PDFLib.PDFTrailer.forLastCrossRefSectionOffset = function(offset){};
|
|
@ -1,39 +1,287 @@
|
|||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* url: string,
|
||||
* len: (number|undefined),
|
||||
* headers: (Object<string, *>|undefined),
|
||||
* }}
|
||||
*/
|
||||
var TsaServiceInfo;
|
||||
/**
|
||||
* the base point of x, y is top left corner.
|
||||
* wDraw, hDraw: Only for internal process.
|
||||
* @typedef
|
||||
* {{
|
||||
* x: number,
|
||||
* y: number,
|
||||
* w: number,
|
||||
* h: number,
|
||||
* w: (number|undefined),
|
||||
* h: (number|undefined),
|
||||
* wDraw: (number|undefined),
|
||||
* hDraw: (number|undefined),
|
||||
* }}
|
||||
*/
|
||||
var SignAreaInfo;
|
||||
/**
|
||||
* fontData: default: StandardFonts.Helvetica
|
||||
* color: A Hex string of color. default: #000
|
||||
* opacity: valid value is from 0 to 1. default: 1 // Not implemented
|
||||
* blendMode: https://pdf-lib.js.org/docs/api/enums/blendmode // Not implemented
|
||||
* lineHeight: default is the height of the font at the given size
|
||||
* xOffset: An offset from SignAreaInfo's x
|
||||
* yOffset: An offset from SignAreaInfo's y
|
||||
* align: Text alignment: 0 left, 1 center, 2 right. default: 0
|
||||
* noBreaks: A regular expression string that indicates which characters should not be used to break a word. default: [A-Za-z0-9]
|
||||
*
|
||||
* @typedef
|
||||
* {{
|
||||
* text: string,
|
||||
* fontData: (Array<number>|Uint8Array|ArrayBuffer|string|undefined),
|
||||
* subset: (boolean|undefined),
|
||||
* color: (string|undefined),
|
||||
* opacity: (number|undefined),
|
||||
* blendMode: (string|undefined),
|
||||
* lineHeight: (number|undefined),
|
||||
* size: number,
|
||||
* xOffset: (number|undefined),
|
||||
* yOffset: (number|undefined),
|
||||
* wMax: (number|undefined),
|
||||
* align: (number|undefined),
|
||||
* noBreaks: (string|undefined),
|
||||
* }}
|
||||
*/
|
||||
var SignTextInfo;
|
||||
/**
|
||||
* opacity: valid value is from 0 to 1. default: 1 // Not implemented
|
||||
* blendMode: https://pdf-lib.js.org/docs/api/enums/blendmode // Not implemented
|
||||
*
|
||||
* @typedef
|
||||
* {{
|
||||
* imgData: (Array<number>|Uint8Array|ArrayBuffer|string|undefined),
|
||||
* imgType: (string|undefined),
|
||||
* opacity: (number|undefined),
|
||||
* blendMode: (string|undefined),
|
||||
* }}
|
||||
*/
|
||||
var SignImageInfo;
|
||||
/**
|
||||
* The signature can be placed in the same position on multiple pages, but all pages must have the same size and rotation angle.
|
||||
* pageidx: Can be a string to indicate placing the signature on multiple pages.
|
||||
* For example: A pdf contains 17 pages and specify "-3,5-7,9,12,15-" means [0,1,2,3,5,6,7,9,12,15,16]
|
||||
* imgData, imgType: Deprecated, use imgInfo instead.
|
||||
* img, font: Only for internal process.
|
||||
*
|
||||
* @typedef
|
||||
* {{
|
||||
* area: SignAreaInfo,
|
||||
* pageidx: (number|undefined),
|
||||
* imgData: (Uint8Array|ArrayBuffer|string|undefined),
|
||||
* pageidx: (number|string|undefined),
|
||||
* imgData: (Array<number>|Uint8Array|ArrayBuffer|string|undefined),
|
||||
* imgType: (string|undefined),
|
||||
* text: (string|undefined),
|
||||
* fontData: (PDFLib.StandardFonts|Uint8Array|ArrayBuffer|string|undefined),
|
||||
* imgInfo: (SignImageInfo|undefined),
|
||||
* textInfo: (SignTextInfo|undefined),
|
||||
* img: (PDFLib.PDFImage|undefined),
|
||||
* font: (PDFLib.PDFFont|undefined),
|
||||
* }}
|
||||
*/
|
||||
var SignDrawInfo;
|
||||
/**
|
||||
* In the case of adding a document timestamp, the p12cert and pwd must be omitted. But meanwhile the tsa must be provided.
|
||||
*
|
||||
* permission: (DocMDP) The modification permissions granted for this document. 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.
|
||||
*
|
||||
* ltv: Type of Long-Term Validation. Valid values are:
|
||||
* 1 : auto; Try using ocsp only to enable the LTV first; If can't, try using crl to enable the LTV.
|
||||
* 2 : crl only; Only try using crl to enable the LTV.
|
||||
*
|
||||
* @typedef
|
||||
* {{
|
||||
* p12cert: (Uint8Array|ArrayBuffer|string),
|
||||
* pwd: string,
|
||||
* p12cert: (Array<number>|Uint8Array|ArrayBuffer|string|undefined),
|
||||
* pwd: (string|undefined),
|
||||
* permission: (number|undefined),
|
||||
* reason: (string|undefined),
|
||||
* location: (string|undefined),
|
||||
* contact: (string|undefined),
|
||||
* signdate: (Date|undefined),
|
||||
* signdate: (Date|TsaServiceInfo|string|undefined),
|
||||
* signame: (string|undefined),
|
||||
* drawinf: (SignDrawInfo|undefined),
|
||||
* ltv: (number|undefined),
|
||||
* debug: (boolean|undefined),
|
||||
* }}
|
||||
*/
|
||||
var SignOption;
|
||||
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* c: (Array<number>|Uint8Array|ArrayBuffer|string|forge_cert|undefined),
|
||||
* p: (Array<string>|undefined),
|
||||
* }}
|
||||
*/
|
||||
var PubKeyInfo;
|
||||
/**
|
||||
* permissions: The set of permissions (specify the ones you want to block):
|
||||
* 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.
|
||||
*
|
||||
* ownerpwd: Owner password If not specified, a random value is used.
|
||||
*
|
||||
* pubkeys: Array of recipients containing public-key certificates ('c') and permissions ('p'). If want to encrypt the pdf by the certificate of signing, just apply a PubKeyInfo without c.
|
||||
*
|
||||
* @typedef
|
||||
* {{
|
||||
* mode: Zga.Crypto.Mode,
|
||||
* permissions: (Array<string>|undefined),
|
||||
* userpwd: (string|undefined),
|
||||
* ownerpwd: (string|undefined),
|
||||
* pubkeys: (Array<PubKeyInfo>|undefined),
|
||||
* }}
|
||||
*/
|
||||
var EncryptOption;
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* CFM: string,
|
||||
* Length: (number|undefined),
|
||||
* EncryptMetadata: (boolean|undefined),
|
||||
* AuthEvent: (string|undefined),
|
||||
* }}
|
||||
*/
|
||||
var CFType;
|
||||
/**
|
||||
* enckey: Last RC4 key encrypted.
|
||||
* enckeyc: Last RC4 computed key.
|
||||
* @typedef
|
||||
* {{
|
||||
* enckey: string,
|
||||
* enckeyc: Array<number>,
|
||||
* }}
|
||||
*/
|
||||
var RC4LastInfo;
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* certs: (Array<forge_cert>|undefined),
|
||||
* ocsps: (Array<Uint8Array>|undefined),
|
||||
* crls: (Array<Uint8Array>|undefined),
|
||||
* }}
|
||||
*/
|
||||
var DSSInfo;
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* resp: (Uint8Array|undefined),
|
||||
* cchainIdx: (number|undefined),
|
||||
* }}
|
||||
*/
|
||||
var OcspData;
|
||||
|
||||
var Zga = {};
|
||||
/**
|
||||
* @param {Uint8Array} uarr
|
||||
* @return {string}
|
||||
*/
|
||||
Zga.u8arrToRaw = function(uarr){};
|
||||
/**
|
||||
* @param {string} raw
|
||||
* @return {Uint8Array}
|
||||
*/
|
||||
Zga.rawToU8arr = function(raw){};
|
||||
|
||||
Zga.Crypto = {};
|
||||
/** @enum {number} */
|
||||
Zga.Crypto.Mode = {
|
||||
RC4_40: 0,
|
||||
RC4_128: 1,
|
||||
AES_128: 2,
|
||||
AES_256: 3,
|
||||
};
|
||||
/**
|
||||
* @constructor
|
||||
* @param {EncryptOption} encopt
|
||||
*/
|
||||
Zga.PdfCryptor = function(encopt){};
|
||||
/**
|
||||
* @param {PDFLib.PDFDocument|Array<number>|Uint8Array|ArrayBuffer|string} pdf
|
||||
* @param {PDFLib.PDFRef=} ref
|
||||
* @return {Promise<PDFLib.PDFDocument>}
|
||||
*/
|
||||
Zga.PdfCryptor.prototype.encryptPdf = function(pdf, ref){};
|
||||
/**
|
||||
* @param {number} num
|
||||
* @param {PDFLib.PDFObject} val
|
||||
*/
|
||||
Zga.PdfCryptor.prototype.encryptObject = function(num, val){};
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Array<forge_cert|forge.asn1|string>=} certs
|
||||
*/
|
||||
Zga.CertsChain = function(certs){};
|
||||
/**
|
||||
* @return {forge_cert}
|
||||
*/
|
||||
Zga.CertsChain.prototype.getSignCert = function(){};
|
||||
/**
|
||||
* @public
|
||||
* @return {boolean}
|
||||
*/
|
||||
Zga.CertsChain.prototype.isSelfSignedCert = function(){};
|
||||
/**
|
||||
* @return {Array<forge_cert>}
|
||||
*/
|
||||
Zga.CertsChain.prototype.getAllCerts = function(){};
|
||||
/**
|
||||
* @public
|
||||
* @param {forge_cert} cert
|
||||
* @return {Promise<boolean>}
|
||||
*/
|
||||
Zga.CertsChain.prototype.buildChain = function(cert){};
|
||||
/**
|
||||
* @param {boolean=} crlOnly
|
||||
* @return {Promise<DSSInfo>}
|
||||
*/
|
||||
Zga.CertsChain.prototype.prepareDSSInf = function(crlOnly){};
|
||||
/**
|
||||
* @constructor
|
||||
* @param {TsaServiceInfo} inf
|
||||
*/
|
||||
Zga.TsaFetcher = function(inf){};
|
||||
/** @type {string} */
|
||||
Zga.TsaFetcher.prototype.url;
|
||||
/** @type {number} */
|
||||
Zga.TsaFetcher.prototype.len;
|
||||
/**
|
||||
* @param {string=} data
|
||||
* @return {Promise<string>}
|
||||
*/
|
||||
Zga.TsaFetcher.prototype.queryTsa = function(data){};
|
||||
/**
|
||||
* @param {boolean=} forP7
|
||||
* @return {forge.asn1}
|
||||
*/
|
||||
Zga.TsaFetcher.prototype.getToken = function(forP7){};
|
||||
/**
|
||||
* @return {Zga.CertsChain}
|
||||
*/
|
||||
Zga.TsaFetcher.prototype.getCertsChain = function(){};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {SignOption} signopt
|
||||
*/
|
||||
Zga.PdfSigner = function(signopt){};
|
||||
/**
|
||||
* @public
|
||||
* @param {PDFLib.PDFDocument|Array<number>|Uint8Array|ArrayBuffer|string} pdf
|
||||
* @param {EncryptOption=} cypopt
|
||||
* @return {Promise<Uint8Array>}
|
||||
*/
|
||||
Zga.PdfSigner.prototype.sign = function(pdf, cypopt){};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,140 @@
|
|||
/**
|
||||
* @param {Object<string, *>} z
|
||||
*/
|
||||
function supplyZgaUrlFetch(z){
|
||||
|
||||
//Only for nodejs Start//
|
||||
const m_urlparser = require("url");
|
||||
const m_h = {
|
||||
"http:": require('follow-redirects').http,
|
||||
"https:": require('follow-redirects').https,
|
||||
};
|
||||
// @type {boolean}
|
||||
z.isNode = function(){return this === globalThis.global;}();
|
||||
//Only for nodejs End//
|
||||
|
||||
/** @type {boolean} */
|
||||
z.isBrowser = function(){return this === globalThis.self;}();
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {UrlFetchParams} params
|
||||
* @return {Promise<Uint8Array>}
|
||||
*/
|
||||
z.urlFetch = function(url, params){
|
||||
|
||||
//Only for nodejs Start//
|
||||
if(z.isNode){
|
||||
return new Promise(function(resolve, reject){
|
||||
// @type {URL}
|
||||
var opts = m_urlparser.parse(url);
|
||||
var http = m_h[opts.protocol];
|
||||
// @type {string|Buffer}
|
||||
var dat = null;
|
||||
var encoding = undefined;
|
||||
opts.method = "GET";
|
||||
if(params){
|
||||
if(params.payload instanceof Buffer){
|
||||
dat = params.payload;
|
||||
}else if(params.payload instanceof Uint8Array){
|
||||
dat = Buffer.from(params.payload.buffer);
|
||||
}else if(params.payload instanceof ArrayBuffer){
|
||||
dat = Buffer.from(params.payload);
|
||||
}else{
|
||||
dat = params.payload;
|
||||
encoding = "binary";
|
||||
}
|
||||
if(params.headers){
|
||||
opts.headers = params.headers;
|
||||
}
|
||||
if(params.method){
|
||||
opts.method = params.method;
|
||||
}
|
||||
if(params.validateHttpsCertificates === false){
|
||||
opts.rejectUnauthorized = false;
|
||||
}
|
||||
}
|
||||
|
||||
// @type {http.ClientRequest}
|
||||
var hreq = http.request(opts, function(a_res){ // @type {http.IncomingMessage} a_res
|
||||
if(a_res.statusCode !== 200){
|
||||
var a_err = new Error("Failed to request url. " + url + "\n Status Code: " + a_res.statusCode);
|
||||
a_res.resume();
|
||||
throw a_err;
|
||||
}
|
||||
// @type {Array<Buffer>}
|
||||
var a_bufs = [];
|
||||
var a_bufs_len = 0;
|
||||
a_res.on("data", function(b_chunk){ // @type {Buffer} b_chunk
|
||||
a_bufs.push(b_chunk);
|
||||
a_bufs_len += b_chunk.length;
|
||||
});
|
||||
a_res.on("end", function(){
|
||||
// @type {Buffer}
|
||||
var b_bdat = Buffer.concat(a_bufs, a_bufs_len);
|
||||
resolve(b_bdat);
|
||||
});
|
||||
});
|
||||
hreq.on("error", function(a_err){
|
||||
throw a_err;
|
||||
});
|
||||
hreq.end(dat, encoding);
|
||||
});
|
||||
}
|
||||
//Only for nodejs End//
|
||||
|
||||
// Google Apps Script
|
||||
if(globalThis.UrlFetchApp){
|
||||
return new Promise(function(resolve){
|
||||
/** @type {GBlob} */
|
||||
var tblob = UrlFetchApp.fetch(url, params).getBlob();
|
||||
resolve(new Uint8Array(tblob.getBytes()));
|
||||
});
|
||||
}
|
||||
|
||||
// browser
|
||||
if(z.isBrowser && globalThis.self.fetch){
|
||||
/**
|
||||
* @return {Promise<Uint8Array>}
|
||||
*/
|
||||
var func = async function(){
|
||||
/** @type {!RequestInit} */
|
||||
var reqinf = {
|
||||
method: "GET",
|
||||
redirect: "follow",
|
||||
};
|
||||
if(params){
|
||||
if(params.payload){
|
||||
reqinf.body = params.payload;
|
||||
}
|
||||
if(params.headers){
|
||||
reqinf.headers = params.headers;
|
||||
}
|
||||
if(params.method){
|
||||
reqinf.method = params.method;
|
||||
}
|
||||
}
|
||||
/** @type {Response} */
|
||||
var resp = await fetch(url, reqinf);
|
||||
if(resp.ok){
|
||||
/** @type {ArrayBuffer} */
|
||||
var abdat = await resp.arrayBuffer();
|
||||
return new Uint8Array(abdat);
|
||||
}else{
|
||||
/** @type {string} */
|
||||
var msg = await resp.text();
|
||||
throw new Error("Fetch failed." + resp.status + ": " + msg);
|
||||
}
|
||||
};
|
||||
return func();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
//Only for nodejs Start//
|
||||
if(typeof exports === "object" && typeof module !== "undefined"){
|
||||
module.exports = supplyZgaUrlFetch;
|
||||
}
|
||||
//Only for nodejs End//
|
|
@ -0,0 +1,64 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* @return {Object<string, *>}
|
||||
*/
|
||||
function genZga(){
|
||||
/** @const {Object<string, *>} */
|
||||
const z = {
|
||||
ver: "",
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {...string} msg
|
||||
*/
|
||||
z.log = function(...msg){
|
||||
if(z.debug){
|
||||
console.log(...msg);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} uarr
|
||||
* @return {string}
|
||||
*/
|
||||
z.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}
|
||||
*/
|
||||
z.rawToU8arr = function(raw){
|
||||
/** @type {Uint8Array} */
|
||||
var arr = new Uint8Array(raw.length);
|
||||
for(var i=0; i<raw.length; i++){
|
||||
arr[i] = raw.charCodeAt(i);
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
//Only for nodejs Start//
|
||||
if(typeof exports === "object" && typeof module !== "undefined"){
|
||||
module.exports = genZga();
|
||||
}else{
|
||||
//Only for nodejs End//
|
||||
if(!globalThis.Zga){
|
||||
globalThis.Zga = genZga();
|
||||
supplyZgaUrlFetch(globalThis.Zga);
|
||||
supplyZgaCertsChain(globalThis.Zga);
|
||||
supplyZgaCryptor(globalThis.Zga);
|
||||
supplyZgaSigner(globalThis.Zga);
|
||||
}
|
||||
//Only for nodejs Start//
|
||||
}
|
||||
//Only for nodejs End//
|
|
@ -0,0 +1,117 @@
|
|||
import * as forge from "node-forge";
|
||||
import * as PDFLib from "pdf-lib";
|
||||
export * as forge from "node-forge";
|
||||
export * as PDFLib from "pdf-lib";
|
||||
|
||||
export declare function u8arrToRaw(uarr: Uint8Array): string;
|
||||
export declare function rawToU8arr(raw: string): Uint8Array;
|
||||
export declare namespace Crypto {
|
||||
enum Mode {
|
||||
RC4_40,
|
||||
RC4_128,
|
||||
AES_128,
|
||||
AES_256,
|
||||
}
|
||||
}
|
||||
export type DSSInfo = {
|
||||
certs?: Array<forge.pki.Certificate>;
|
||||
ocsps?: Array<Uint8Array>;
|
||||
crls?: Array<Uint8Array>;
|
||||
};
|
||||
export type EncryptOption = {
|
||||
mode: Crypto.Mode;
|
||||
permissions?: Array<string>;
|
||||
userpwd?: string;
|
||||
ownerpwd?: string;
|
||||
pubkeys?: Array<PubKeyInfo>;
|
||||
};
|
||||
export type PubKeyInfo = {
|
||||
c?: Array<number> | Uint8Array | ArrayBuffer | string | forge.pki.Certificate;
|
||||
p?: Array<string>;
|
||||
};
|
||||
export type SignAreaInfo = {
|
||||
x: number;
|
||||
y: number;
|
||||
w?: number;
|
||||
h?: number;
|
||||
};
|
||||
export type SignTextInfo = {
|
||||
text: string,
|
||||
fontData?: Array<number> | Uint8Array | ArrayBuffer | PDFLib.StandardFonts;
|
||||
subset?: boolean;
|
||||
color?: string;
|
||||
opacity?: number;
|
||||
blendMode?: string;
|
||||
lineHeight?: number;
|
||||
size: number,
|
||||
xOffset?: number;
|
||||
yOffset?: number;
|
||||
wMax?: number;
|
||||
align?: number;
|
||||
noBreaks?: string;
|
||||
};
|
||||
export type SignImageInfo = {
|
||||
imgData: Array<number> | Uint8Array | ArrayBuffer | string;
|
||||
imgType: string;
|
||||
opacity?: number;
|
||||
blendMode?: string;
|
||||
};
|
||||
export type SignDrawInfo = {
|
||||
area: SignAreaInfo;
|
||||
pageidx?: number | string;
|
||||
/** @deprecated use imgInfo instead */
|
||||
imgData?: Array<number> | Uint8Array | ArrayBuffer | string;
|
||||
/** @deprecated use imgInfo instead */
|
||||
imgType?: string;
|
||||
imgInfo?: SignImageInfo;
|
||||
textInfo?: SignTextInfo;
|
||||
};
|
||||
export type SignOption = {
|
||||
p12cert?: Array<number> | Uint8Array | ArrayBuffer | string;
|
||||
pwd?: string;
|
||||
permission?: number;
|
||||
reason?: string;
|
||||
location?: string;
|
||||
contact?: string;
|
||||
signdate?: Date | TsaServiceInfo | string;
|
||||
signame?: string;
|
||||
drawinf?: SignDrawInfo;
|
||||
ltv?: number;
|
||||
debug?: boolean;
|
||||
};
|
||||
export type TsaServiceInfo = {
|
||||
url: string;
|
||||
len?: number;
|
||||
headers?: Record<string, any>;
|
||||
};
|
||||
|
||||
export declare class CertsChain {
|
||||
constructor(certs?: Array<forge.pki.Certificate | forge.asn1.Asn1 | string>);
|
||||
buildChain(cert: forge.pki.Certificate): Promise<boolean>;
|
||||
getAllCerts(): Array<forge.pki.Certificate>;
|
||||
getSignCert(): forge.pki.Certificate;
|
||||
isSelfSignedCert(): boolean;
|
||||
prepareDSSInf(crlOnly?: boolean): Promise<DSSInfo>;
|
||||
}
|
||||
export declare class PdfCryptor {
|
||||
constructor(encopt: EncryptOption);
|
||||
encryptPdf(pdf: PDFLib.PDFDocument | Array<number> | Uint8Array | ArrayBuffer | string, ref?: PDFLib.PDFRef): Promise<PDFLib.PDFDocument>;
|
||||
encryptObject(num: number, val: PDFLib.PDFObject): void;
|
||||
}
|
||||
export declare class PdfSigner {
|
||||
constructor(signopt: SignOption);
|
||||
sign(pdf: PDFLib.PDFDocument | Array<number> | Uint8Array | ArrayBuffer | string, cypopt?: EncryptOption): Promise<Uint8Array>;
|
||||
}
|
||||
export declare class TsaFetcher {
|
||||
constructor(inf: TsaServiceInfo);
|
||||
url: string;
|
||||
len: number;
|
||||
getCertsChain(): CertsChain;
|
||||
getToken(forP7?: boolean): forge.asn1.Asn1;
|
||||
queryTsa(data?: string): Promise<string>;
|
||||
}
|
||||
export declare class PdfFonts {
|
||||
private constructor();
|
||||
static from(pdfdoc: PDFLib.PDFDocument): Promise<PdfFonts>;
|
||||
getEmbeddedFont(fontData?: Array<number> | Uint8Array | ArrayBuffer | PDFLib.StandardFonts, subset?: boolean): Promise<PDFLib.PDFFont>;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
const m_urlparser = require("url");
|
||||
const m_h = {
|
||||
"http:": require('follow-redirects').http,
|
||||
"https:": require('follow-redirects').https,
|
||||
};
|
||||
const z = require("./zgaindex.js");
|
||||
z.forge = require("node-forge");
|
||||
z.PDFLib = require("pdf-lib");
|
||||
// z.fontkit = require("@pdf-lib/fontkit");
|
||||
z.fontkit = require("pdf-fontkit");
|
||||
z.pako = require("pako");
|
||||
|
||||
require("./zgafetch.js")(z);
|
||||
require("./zgacertsutil.js")(z);
|
||||
require("./zgapdfcryptor.js")(z);
|
||||
require("./zgapdfsigner.js")(z);
|
||||
module.exports = z;
|
|
@ -0,0 +1,968 @@
|
|||
'use strict';
|
||||
|
||||
// This module was migrated from [TCPDF](http://www.tcpdf.org)
|
||||
/**
|
||||
* @param {Object<string, *>} z
|
||||
*/
|
||||
function supplyZgaCryptor(z){
|
||||
|
||||
//Only for nodejs Start//
|
||||
if(z.forge){
|
||||
var forge = z.forge;
|
||||
}
|
||||
if(z.PDFLib){
|
||||
var PDFLib = z.PDFLib;
|
||||
}
|
||||
//Only for nodejs End//
|
||||
|
||||
/**
|
||||
* @param {PDFLib.PDFDocument|Array<number>|Uint8Array|ArrayBuffer|string} pdf
|
||||
* @return {Promise<PDFLib.PDFDocument>}
|
||||
*/
|
||||
z.loadPdf = async function(pdf){
|
||||
/** @type {PDFLib.PDFDocument} */
|
||||
var pdfdoc = null;
|
||||
if(pdf.addPage){
|
||||
pdfdoc = /** @type {PDFLib.PDFDocument} */(pdf);
|
||||
}else if(Array.isArray(pdf)){
|
||||
pdfdoc = await PDFLib.PDFDocument.load(new Uint8Array(pdf));
|
||||
}else{
|
||||
pdfdoc = await PDFLib.PDFDocument.load(/** @type {(ArrayBuffer|Uint8Array|string)} */(pdf));
|
||||
}
|
||||
return pdfdoc;
|
||||
};
|
||||
|
||||
z.Crypto = {
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Mode: {
|
||||
RC4_40: 0,
|
||||
RC4_128: 1,
|
||||
AES_128: 2,
|
||||
AES_256: 3,
|
||||
},
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
*/
|
||||
Permission: {
|
||||
// "owner": 2, // bit 2 -- inverted logic: cleared by default
|
||||
"copy": 2, // bit 2 -- Only valid on public-key mode
|
||||
"print": 4, // bit 3
|
||||
"modify": 8, // bit 4
|
||||
"copy-extract": 16, // bit 5
|
||||
"annot-forms": 32, // bit 6
|
||||
"fill-forms": 256, // bit 9
|
||||
"extract": 512, // bit 10
|
||||
"assemble": 1024,// bit 11 -- On public-key mode, it means adding, deleting or rotating pages.
|
||||
"print-high": 2048 // bit 12
|
||||
},
|
||||
|
||||
/** @type {string} */
|
||||
EncPadding: "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A",
|
||||
|
||||
/**
|
||||
* Add "\" before "\", "(" and ")", and chr(13) => '\r'
|
||||
* @param {string} s string to escape.
|
||||
* @return {string} escaped string.
|
||||
*/
|
||||
_escape: function(s){
|
||||
if(!s){
|
||||
return s;
|
||||
}
|
||||
/** @const {Array<string>} */
|
||||
const CHARS = "\\()".split("");
|
||||
/** @type {Array<string>} */
|
||||
var arr = [];
|
||||
for(var i=0; i<s.length; i++){
|
||||
/** @type {string} */
|
||||
var c = s.charAt(i);
|
||||
if(c == "\r"){
|
||||
arr.push("\\r");
|
||||
}else{
|
||||
if(CHARS.indexOf(c) >= 0){
|
||||
arr.push("\\");
|
||||
}
|
||||
arr.push(c);
|
||||
}
|
||||
}
|
||||
return arr.join("");
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a string containing random data to be used as a seed for encryption methods.
|
||||
* @param {string=} seed starting seed value
|
||||
* @return {string} containing random data
|
||||
*/
|
||||
getRandomSeed: function(seed){
|
||||
/** @type {string} */
|
||||
var ret = forge.random.getBytesSync(256);
|
||||
if(seed){
|
||||
ret += seed;
|
||||
}
|
||||
ret += new Date().getTime();
|
||||
return ret;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the input text encrypted using RC4 algorithm and the specified key.
|
||||
* RC4 is the standard encryption algorithm used in PDF format
|
||||
* @param {string} key Encryption key.
|
||||
* @param {string} txt Input text to be encrypted.
|
||||
* @param {RC4LastInfo} lastinf Last RC4 information.
|
||||
* @return {string} encrypted text
|
||||
*/
|
||||
_RC4: function(key, txt, lastinf){
|
||||
/** @type {Array<number>} */
|
||||
var rc4 = null;
|
||||
/** @type {number} */
|
||||
var i = 0;
|
||||
/** @type {number} */
|
||||
var j = 0;
|
||||
/** @type {number} */
|
||||
var t = 0;
|
||||
if(lastinf.enckey != key){
|
||||
/** @type {string} */
|
||||
var k = key.repeat(256 / key.length + 1);
|
||||
rc4 = new Array(256);
|
||||
// Initialize rc4
|
||||
for(i=0; i<rc4.length; i++){
|
||||
rc4[i] = i;
|
||||
}
|
||||
for(i=0; i<rc4.length; i++){
|
||||
t = rc4[i];
|
||||
j = (j + t + k.charCodeAt(i)) & 0xFF;
|
||||
rc4[i] = rc4[j];
|
||||
rc4[j] = t;
|
||||
}
|
||||
lastinf.enckey = key;
|
||||
lastinf.enckeyc = [].concat(rc4);
|
||||
}else{
|
||||
rc4 = [].concat(lastinf.enckeyc);
|
||||
}
|
||||
/** @type {number} */
|
||||
var len = txt.length;
|
||||
/** @type {number} */
|
||||
var a = 0;
|
||||
/** @type {number} */
|
||||
var b = 0;
|
||||
/** @type {string} */
|
||||
var out = "";
|
||||
for(i=0; i<len; i++){
|
||||
a = (a + 1) & 0xFF;
|
||||
t = rc4[a];
|
||||
b = (b + t) & 0xFF;
|
||||
rc4[a] = rc4[b];
|
||||
rc4[b] = t;
|
||||
k = rc4[(rc4[a] + rc4[b]) & 0xFF];
|
||||
out += String.fromCharCode(txt.charCodeAt(i) ^ k);
|
||||
}
|
||||
return out;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the permission code used on encryption (P value).
|
||||
*
|
||||
* @param {Array<string>=} permissions the set of permissions (specify the ones you want to block).
|
||||
* @param {z.Crypto.Mode=} mode
|
||||
* @return {number}
|
||||
*/
|
||||
getUserPermissionCode: function(permissions, mode){
|
||||
/** @type {number} */
|
||||
var protection = 2147422012; // 32 bit: (01111111 11111111 00001111 00111100)
|
||||
if(permissions){
|
||||
permissions.forEach(function(a_itm){
|
||||
/** @type {number} */
|
||||
var a_p = z.Crypto.Permission[a_itm];
|
||||
if(a_p && a_itm != "copy"){
|
||||
if(mode > 0 || a_p <= 32){
|
||||
// set only valid permissions
|
||||
if(a_p == 2){
|
||||
// the logic for bit 2 is inverted (cleared by default)
|
||||
protection += a_p;
|
||||
}else{
|
||||
protection -= a_p;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return protection;
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert encryption P value to a string of bytes, low-order byte first.
|
||||
* @param {number} protection 32bit encryption permission value (P value)
|
||||
* @return {string}
|
||||
*/
|
||||
getUserPermissionsString: function(protection){
|
||||
/** @type {forge.util.ByteStringBuffer} */
|
||||
var buff = new forge.util.ByteStringBuffer();
|
||||
buff.putInt32Le(protection);
|
||||
return buff.getBytes();
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a string of permissions, high-order byte first.
|
||||
* @param {Array<string>=} permissions the set of permissions (specify the ones you want to block).
|
||||
* @return {string}
|
||||
*/
|
||||
getCertPermissionsString: function(permissions){
|
||||
/**
|
||||
* Although the permissions is 4 bytes(32 bits), the first 2 bytes are 0xFF,0xFF,
|
||||
* so we only check the last 2 bytes.
|
||||
* @type {number}
|
||||
*/
|
||||
var protection = 65535; // 16 bit: (11111111 11111111)
|
||||
if(permissions){
|
||||
permissions.forEach(function(a_itm){
|
||||
/** @type {number} */
|
||||
var a_p = z.Crypto.Permission[a_itm];
|
||||
if(a_p){
|
||||
protection -= a_p;
|
||||
}
|
||||
});
|
||||
}
|
||||
/** @type {forge.util.ByteStringBuffer} */
|
||||
var buff = new forge.util.ByteStringBuffer();
|
||||
buff.fillWithByte(0xFF, 2);
|
||||
buff.putInt16(protection);
|
||||
return buff.getBytes();
|
||||
},
|
||||
|
||||
/**
|
||||
* Encrypts a string using MD5 and returns it's value as a binary string.
|
||||
* @param {string} str input string
|
||||
* @return {string} MD5 encrypted binary string
|
||||
*/
|
||||
_md5_16: function(str){
|
||||
/** @type {forge.md.digest} */
|
||||
var md = forge.md.md5.create();
|
||||
md.update(str);
|
||||
return md.digest().getBytes();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the input text encrypted using AES algorithm and the specified key.
|
||||
* Text is padded to 16bytes blocks
|
||||
* @param {string} key encryption key
|
||||
* @param {string} txt input text to be encrypted
|
||||
* @return {string} encrypted text
|
||||
*/
|
||||
_AES: function(key, txt){
|
||||
// padding (RFC 2898, PKCS #5: Password-Based Cryptography Specification Version 2.0)
|
||||
/** @type {number} */
|
||||
var padding = 16 - (txt.length & 0x0F);
|
||||
/** @type {forge.util.ByteStringBuffer} */
|
||||
var buff = forge.util.createBuffer(txt);
|
||||
buff.fillWithByte(padding, padding);
|
||||
|
||||
/** @type {string} */
|
||||
var iv = forge.random.getBytesSync(16);
|
||||
/** @type {forge.util.ByteStringBuffer} */
|
||||
var key2 = forge.util.createBuffer(key);
|
||||
/** @type {forge.cipher.BlockCipher} */
|
||||
var cipher = forge.cipher.createCipher("AES-CBC", key2);
|
||||
cipher.start({iv: iv});
|
||||
cipher.update(buff);
|
||||
cipher.finish();
|
||||
return iv + cipher.output.truncate(16).getBytes();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the input text encrypted using AES algorithm and the specified key.
|
||||
* Text is not padded
|
||||
* @param {string} key encryption key
|
||||
* @param {string} txt input text to be encrypted
|
||||
* @return {string} encrypted text
|
||||
*/
|
||||
_AESnopad: function(key, txt) {
|
||||
/** @type {forge.util.ByteStringBuffer} */
|
||||
var buff = forge.util.createBuffer(txt);
|
||||
/** @type {string} */
|
||||
var iv = String.fromCharCode(0).repeat(16);
|
||||
/** @type {forge.util.ByteStringBuffer} */
|
||||
var key2 = forge.util.createBuffer(key);
|
||||
/** @type {forge.cipher.BlockCipher} */
|
||||
var cipher = forge.cipher.createCipher("AES-CBC", key2);
|
||||
cipher.start({iv: iv});
|
||||
cipher.update(buff);
|
||||
cipher.finish();
|
||||
return cipher.output.truncate(16).getBytes();
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
z.PdfCryptor = class{
|
||||
/**
|
||||
* @param {EncryptOption} encopt
|
||||
*/
|
||||
constructor(encopt){
|
||||
/** @private @type {string} */
|
||||
this.fileid = "";
|
||||
/** @private @type {string} */
|
||||
this.key = "";
|
||||
/** @private @type {Array<PubKeyInfo>|undefined} */
|
||||
this.pubkeys = encopt.pubkeys;
|
||||
/** @private @type {z.Crypto.Mode} */
|
||||
this.mode = /** @type {z.Crypto.Mode} */(encopt.mode);
|
||||
/** @private @type {Array<string>|undefined} */
|
||||
this.permissions = encopt.permissions;
|
||||
/** @private @type {string} */
|
||||
this.userpwd = "";
|
||||
/** @private @type {string} */
|
||||
this.ownerpwd = "";
|
||||
|
||||
/** @private @type {string} */
|
||||
this.Filter = "Standard";
|
||||
/** @private @type {string} */
|
||||
this.StmF = "StdCF";
|
||||
/** @private @type {string} */
|
||||
this.StrF = "StdCF";
|
||||
|
||||
/** @private @type {number} */
|
||||
this.V = 1;
|
||||
/** @private @type {number} */
|
||||
this.Length = 0;
|
||||
/** @private @type {?CFType} */
|
||||
this.CF = null;
|
||||
/** @private @type {string} */
|
||||
this.SubFilter = "";
|
||||
/** @private @type {Array<string>} */
|
||||
this.Recipients = null;
|
||||
|
||||
/** @private @type {string} */// User Validation Salt
|
||||
this.UVS = "";
|
||||
/** @private @type {string} */// User Key Salt
|
||||
this.UKS = "";
|
||||
/** @private @type {string} */// U value
|
||||
this.U = "";
|
||||
/** @private @type {string} */// UE value
|
||||
this.UE = "";
|
||||
|
||||
/** @private @type {string} */// Owner Validation Salt
|
||||
this.OVS = "";
|
||||
/** @private @type {string} */// Owner Key Salt
|
||||
this.OKS = "";
|
||||
/** @private @type {string} */// O value
|
||||
this.O = "";
|
||||
/** @private @type {string} */// OE value
|
||||
this.OE = "";
|
||||
/** @private @type {number} */// P value
|
||||
this.P = 0;
|
||||
/** @private @type {string} */
|
||||
this.perms = "";
|
||||
|
||||
/** @private @type {RC4LastInfo} */
|
||||
this.rc4inf = {
|
||||
enckey: "",
|
||||
enckeyc: null,
|
||||
};
|
||||
|
||||
if(this.pubkeys){
|
||||
if(this.pubkeys.length == 0){
|
||||
throw new Error("Public key information is not specified.");
|
||||
}
|
||||
if(this.mode == z.Crypto.Mode.RC4_40){
|
||||
// public-Key Security requires at least 128 bit
|
||||
this.mode = z.Crypto.Mode.RC4_128;
|
||||
}
|
||||
this.Filter = "Adobe.PubSec";
|
||||
this.StmF = "DefaultCryptFilter";
|
||||
this.StrF = "DefaultCryptFilter";
|
||||
}
|
||||
|
||||
if(encopt.userpwd){
|
||||
this.userpwd = encopt.userpwd;
|
||||
}
|
||||
if(encopt.ownerpwd){
|
||||
this.ownerpwd = encopt.ownerpwd;
|
||||
}else{
|
||||
/** @type {forge.md.digest} */
|
||||
var md = forge.md.md5.create();
|
||||
md.update(z.Crypto.getRandomSeed());
|
||||
this.ownerpwd = md.digest().toHex();
|
||||
}
|
||||
|
||||
switch(this.mode){
|
||||
case z.Crypto.Mode.RC4_40:
|
||||
this.V = 1;
|
||||
this.Length = 40;
|
||||
this.CF = {CFM: "V2"};
|
||||
break;
|
||||
case z.Crypto.Mode.RC4_128:
|
||||
this.V = 2;
|
||||
this.Length = 128;
|
||||
this.CF = {CFM: "V2"};
|
||||
if(this.pubkeys){
|
||||
this.SubFilter = "adbe.pkcs7.s4";
|
||||
this.Recipients = [];
|
||||
}
|
||||
break;
|
||||
case z.Crypto.Mode.AES_128:
|
||||
this.V = 4;
|
||||
this.Length = 128;
|
||||
this.CF = {CFM: "AESV2", Length: 128};
|
||||
if(this.pubkeys){
|
||||
this.SubFilter = "adbe.pkcs7.s5";
|
||||
this.Recipients = [];
|
||||
}
|
||||
break;
|
||||
case z.Crypto.Mode.AES_256:
|
||||
this.V = 5;
|
||||
this.Length = 256;
|
||||
this.CF = {CFM: "AESV3", Length: 256};
|
||||
if(this.pubkeys){
|
||||
this.SubFilter = "adbe.pkcs7.s5";
|
||||
this.Recipients = [];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unknown crypto mode. " + this.mode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param {PDFLib.PDFDocument|Array<number>|Uint8Array|ArrayBuffer|string} pdf
|
||||
* @param {PDFLib.PDFRef=} ref The unique reference will be assigned to the encryption information.
|
||||
* @return {Promise<PDFLib.PDFDocument>}
|
||||
*/
|
||||
async encryptPdf(pdf, ref){
|
||||
/** @const {z.PdfCryptor} */
|
||||
const _this = this;
|
||||
/** @type {PDFLib.PDFDocument} */
|
||||
var pdfdoc = await z.loadPdf(pdf);
|
||||
if(pdfdoc === pdf && !ref){
|
||||
await pdfdoc.flush();
|
||||
}
|
||||
|
||||
/** @type {PDFLib.PDFContext} */
|
||||
var pdfcont = pdfdoc.context;
|
||||
/** @type {PDFLib.PDFObject} */
|
||||
var trobj = _this.prepareEncrypt(pdfcont);
|
||||
|
||||
pdfcont.enumerateIndirectObjects().forEach(function(/** @type {PdfObjEntry} */a_arr){
|
||||
_this.encryptObject(a_arr[0].objectNumber, a_arr[1]);
|
||||
});
|
||||
|
||||
if(ref){
|
||||
pdfcont.assign(ref, trobj);
|
||||
}else{
|
||||
ref = pdfcont.register(trobj);
|
||||
}
|
||||
pdfcont.trailerInfo.Encrypt = ref;
|
||||
|
||||
return pdfdoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param {number} num
|
||||
* @param {PDFLib.PDFObject} val
|
||||
*/
|
||||
encryptObject(num, val){
|
||||
if(val instanceof PDFLib.PDFContentStream){
|
||||
/** @type {Uint8Array} */
|
||||
var dat = val.contentsCache.access();
|
||||
if(dat){
|
||||
val.contentsCache.value = this.encryptU8arr(num, dat);
|
||||
}
|
||||
}else if(val instanceof PDFLib.PDFStream){
|
||||
if(val.contents){
|
||||
val.contents = this.encryptU8arr(num, val.contents);
|
||||
}
|
||||
}else if(val instanceof PDFLib.PDFHexString){
|
||||
if(val.value){
|
||||
val.value = this.encryptHexstr(num, val.value);
|
||||
}
|
||||
}else if(val instanceof PDFLib.PDFString){
|
||||
if(val.value){
|
||||
val.value = z.Crypto._escape(this._encrypt_data(num, val.value));
|
||||
}
|
||||
}
|
||||
if(val.dict instanceof Map){
|
||||
/** @type {Iterator} */
|
||||
var es = val.dict.entries();
|
||||
/** @type {IIterableResult<PdfObjEntry>} */
|
||||
var res = es.next();
|
||||
while(!res.done){
|
||||
this.encryptObject(num, res.value[1]);
|
||||
res = es.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare for encryption and create the object for saving in trailer.
|
||||
*
|
||||
* @private
|
||||
* @param {PDFLib.PDFContext} pdfcont
|
||||
* @return {PDFLib.PDFObject}
|
||||
*/
|
||||
prepareEncrypt(pdfcont){
|
||||
if(!pdfcont.trailerInfo.ID){
|
||||
/** @type {forge.md.digest} */
|
||||
var md = forge.md.md5.create();
|
||||
md.update(z.Crypto.getRandomSeed());
|
||||
/** @type {forge.util.ByteStringBuffer} */
|
||||
var res = md.digest();
|
||||
/** @type {string} */
|
||||
var idhex = res.toHex();
|
||||
/** @type {string} */
|
||||
this.fileid = res.getBytes();
|
||||
|
||||
/** @type {PDFLib.PDFArray} */
|
||||
var trIds = new PDFLib.PDFArray(pdfcont);
|
||||
trIds.push(PDFLib.PDFHexString.of(idhex));
|
||||
trIds.push(PDFLib.PDFHexString.of(idhex));
|
||||
pdfcont.trailerInfo.ID = trIds;
|
||||
|
||||
}else{
|
||||
this.fileid = forge.util.hexToBytes(pdfcont.trailerInfo.ID.get(0).value);
|
||||
}
|
||||
this._generateencryptionkey();
|
||||
|
||||
var obj = {};
|
||||
obj.Filter = this.Filter;
|
||||
if(this.SubFilter){
|
||||
obj.SubFilter = this.SubFilter;
|
||||
}
|
||||
// V is a code specifying the algorithm to be used in encrypting and decrypting the document
|
||||
obj.V = this.V;
|
||||
// The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
|
||||
obj.Length = this.Length;
|
||||
if(this.V >= 4){
|
||||
// A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
|
||||
if(this.CF){
|
||||
/** @type {Object<string, *>} */
|
||||
var objStmF = {
|
||||
Type: "CryptFilter",
|
||||
};
|
||||
// The method used
|
||||
if(this.CF.CFM){
|
||||
objStmF.CFM = this.CF.CFM;
|
||||
if(this.pubkeys){
|
||||
/** @type {PDFLib.PDFArray} */
|
||||
var recps1 = new PDFLib.PDFArray(pdfcont);
|
||||
this.Recipients.forEach(function(a_ele){
|
||||
recps1.push(PDFLib.PDFHexString.of(a_ele));
|
||||
});
|
||||
objStmF.Recipients = recps1;
|
||||
if(typeof this.CF.EncryptMetadata == "boolean" && !this.CF.EncryptMetadata){
|
||||
objStmF.EncryptMetadata = false;
|
||||
}else{
|
||||
objStmF.EncryptMetadata = true;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
objStmF.CFM = "None";
|
||||
}
|
||||
// The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
|
||||
if(this.CF.AuthEvent){
|
||||
objStmF.AuthEvent = this.CF.AuthEvent;
|
||||
}else{
|
||||
objStmF.AuthEvent = "DocOpen";
|
||||
}
|
||||
// The bit length of the encryption key.
|
||||
if(this.CF.Length){
|
||||
objStmF.Length = this.CF.Length;
|
||||
}
|
||||
|
||||
/** @type {Object<string, *>} */
|
||||
var objCF = {
|
||||
[this.StmF]: pdfcont.obj(objStmF),
|
||||
};
|
||||
obj.CF = pdfcont.obj(objCF);
|
||||
}
|
||||
// The name of the crypt filter that shall be used by default when decrypting streams.
|
||||
obj.StmF = this.StmF;
|
||||
// The name of the crypt filter that shall be used when decrypting all strings in the document.
|
||||
obj.StrF = this.StrF;
|
||||
}
|
||||
// Additional encryption dictionary entries for the standard security handler
|
||||
if(this.pubkeys){
|
||||
if(this.V < 4 && this.Recipients && this.Recipients.length > 0){
|
||||
/** @type {PDFLib.PDFArray} */
|
||||
var recps = new PDFLib.PDFArray(pdfcont);
|
||||
this.Recipients.forEach(function(a_ele){
|
||||
recps.push(PDFLib.PDFHexString.of(a_ele));
|
||||
});
|
||||
obj.Recipients = recps;
|
||||
}
|
||||
}else{
|
||||
if(this.V == 5){ // AES-256
|
||||
obj.R = 5;
|
||||
obj.OE = PDFLib.PDFString.of(z.Crypto._escape(this.OE));
|
||||
obj.UE = PDFLib.PDFString.of(z.Crypto._escape(this.UE));
|
||||
obj.Perms = PDFLib.PDFString.of(z.Crypto._escape(this.perms));
|
||||
}else if(this.V == 4){ // AES-128
|
||||
obj.R = 4;
|
||||
}else if(this.V < 2){ // RC-40
|
||||
obj.R = 2;
|
||||
}else{ // RC-128
|
||||
obj.R = 3;
|
||||
}
|
||||
obj.O = PDFLib.PDFString.of(z.Crypto._escape(this.O));
|
||||
obj.U = PDFLib.PDFString.of(z.Crypto._escape(this.U));
|
||||
obj.P = this.P;
|
||||
if(typeof this.EncryptMetadata == "boolean" && !this.EncryptMetadata){
|
||||
obj.EncryptMetadata = false;
|
||||
}else{
|
||||
obj.EncryptMetadata = true;
|
||||
}
|
||||
}
|
||||
return pdfcont.obj(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {number} num
|
||||
* @param {Uint8Array} dat
|
||||
* @return {Uint8Array}
|
||||
*/
|
||||
encryptU8arr(num, dat){
|
||||
/** @type {string} */
|
||||
var str = z.u8arrToRaw(dat);
|
||||
/** @type {string} */
|
||||
var enc = this._encrypt_data(num, str);
|
||||
return z.rawToU8arr(enc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {number} num
|
||||
* @param {string} dat
|
||||
* @return {string}
|
||||
*/
|
||||
encryptHexstr(num, dat){
|
||||
/** @type {string} */
|
||||
var str = forge.util.hexToBytes(dat);
|
||||
/** @type {string} */
|
||||
var enc = this._encrypt_data(num, str);
|
||||
return forge.util.createBuffer(enc).toHex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute encryption key depending on object number where the encrypted data is stored.
|
||||
* This is used for all strings and streams without crypt filter specifier.
|
||||
*
|
||||
* @private
|
||||
* @param {number} n object number
|
||||
* @return {string} object key
|
||||
*/
|
||||
_objectkey(n){
|
||||
/** @type {forge.util.ByteStringBuffer} */
|
||||
var buff = forge.util.createBuffer(this.key);
|
||||
//pack('VXxx', $n)
|
||||
buff.putInt24Le(n);
|
||||
buff.putBytes(String.fromCharCode(0) + String.fromCharCode(0));
|
||||
if (this.mode == z.Crypto.Mode.AES_128) {
|
||||
// AES padding
|
||||
buff.putBytes("sAlT");
|
||||
}
|
||||
|
||||
/** @type {forge.md.digest} */
|
||||
var md = forge.md.md5.create();
|
||||
md.update(buff.getBytes());
|
||||
/** @type {forge.util.ByteStringBuffer} */
|
||||
var ret = md.digest();
|
||||
return ret.getBytes().substr(0, Math.min(16, (this.Length / 8) + 5));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt the input string.
|
||||
*
|
||||
* @private
|
||||
* @param {number} n object number
|
||||
* @param {string} s data string to encrypt
|
||||
* @return {string} encrypted string
|
||||
*/
|
||||
_encrypt_data(n, s){
|
||||
switch(this.mode){
|
||||
case z.Crypto.Mode.RC4_40:
|
||||
case z.Crypto.Mode.RC4_128:
|
||||
s = z.Crypto._RC4(this._objectkey(n), s, this.rc4inf);
|
||||
break;
|
||||
case z.Crypto.Mode.AES_128:
|
||||
s = z.Crypto._AES(this._objectkey(n), s);
|
||||
break;
|
||||
case z.Crypto.Mode.AES_256:
|
||||
s = z.Crypto._AES(this.key, s);
|
||||
break;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute U value (used for encryption)
|
||||
* @private
|
||||
* @return {string} U value
|
||||
*/
|
||||
_Uvalue(){
|
||||
/** @type {string} */
|
||||
var ret = "";
|
||||
if(this.mode == z.Crypto.Mode.RC4_40){
|
||||
ret = z.Crypto._RC4(this.key, z.Crypto.EncPadding, this.rc4inf);
|
||||
}else if(this.mode < z.Crypto.Mode.AES_256) { // RC4-128, AES-128
|
||||
/** @type {string} */
|
||||
var tmp = z.Crypto._md5_16(z.Crypto.EncPadding + this.fileid);
|
||||
/** @type {string} */
|
||||
var enc = z.Crypto._RC4(this.key, tmp, this.rc4inf);
|
||||
/** @type {number} */
|
||||
var len = tmp.length;
|
||||
for(var i=1; i<=19; i++){
|
||||
/** @type {string} */
|
||||
var ek = "";
|
||||
for(var j=0; j<len; j++){
|
||||
ek += String.fromCharCode(this.key.charCodeAt(j) ^ i);
|
||||
}
|
||||
enc = z.Crypto._RC4(ek, enc, this.rc4inf);
|
||||
}
|
||||
enc += String.fromCharCode(0).repeat(16);
|
||||
ret = enc.substr(0, 32);
|
||||
|
||||
}else if(this.mode == z.Crypto.Mode.AES_256){
|
||||
/** @type {string} */
|
||||
var seed = z.Crypto._md5_16(z.Crypto.getRandomSeed());
|
||||
// User Validation Salt
|
||||
/** @type {string} */
|
||||
this.UVS = seed.substr(0, 8);
|
||||
// User Key Salt
|
||||
/** @type {string} */
|
||||
this.UKS = seed.substr(8, 16);
|
||||
|
||||
/** @type {forge.md.digest} */
|
||||
var md = forge.md.sha256.create();
|
||||
md.update(this.userpwd + this.UVS);
|
||||
ret = md.digest().getBytes() + this.UVS + this.UKS;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute UE value (used for encryption)
|
||||
* @private
|
||||
* @return {string} UE value
|
||||
*/
|
||||
_UEvalue(){
|
||||
/** @type {forge.md.digest} */
|
||||
var md = forge.md.sha256.create();
|
||||
md.update(this.userpwd + this.UKS);
|
||||
return z.Crypto._AESnopad(md.digest().getBytes(), this.key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute O value (used for encryption)
|
||||
* @private
|
||||
* @return {string} O value
|
||||
*/
|
||||
_Ovalue(){
|
||||
/** @type {string} */
|
||||
var ret = "";
|
||||
if(this.mode < z.Crypto.Mode.AES_256){ // RC4-40, RC4-128, AES-128
|
||||
/** @type {string} */
|
||||
var tmp = z.Crypto._md5_16(this.ownerpwd);
|
||||
if(this.mode > z.Crypto.Mode.RC4_40){
|
||||
for(var i=0; i<50; i++){
|
||||
tmp = z.Crypto._md5_16(tmp);
|
||||
}
|
||||
}
|
||||
/** @type {string} */
|
||||
var owner_key = tmp.substr(0, this.Length / 8);
|
||||
ret = z.Crypto._RC4(owner_key, this.userpwd, this.rc4inf);
|
||||
if(this.mode > z.Crypto.Mode.RC4_40){
|
||||
/** @type {number} */
|
||||
var len = owner_key.length;
|
||||
for(var i=1; i<=19; i++){
|
||||
/** @type {string} */
|
||||
var ek = "";
|
||||
for(var j=0; j<len; j++){
|
||||
ek += String.fromCharCode(owner_key.charCodeAt(j) ^ i);
|
||||
}
|
||||
ret = z.Crypto._RC4(ek, ret, this.rc4inf);
|
||||
}
|
||||
}
|
||||
}else if(this.mode == z.Crypto.Mode.AES_256){
|
||||
/** @type {string} */
|
||||
var seed = z.Crypto._md5_16(z.Crypto.getRandomSeed());
|
||||
// Owner Validation Salt
|
||||
/** @type {string} */
|
||||
this.OVS = seed.substr(0, 8);
|
||||
// Owner Key Salt
|
||||
/** @type {string} */
|
||||
this.OKS = seed.substr(8, 16);
|
||||
|
||||
/** @type {forge.md.digest} */
|
||||
var md = forge.md.sha256.create();
|
||||
md.update(this.ownerpwd + this.OVS + this.U);
|
||||
ret = md.digest().getBytes() + this.OVS + this.OKS;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute OE value (used for encryption)
|
||||
* @private
|
||||
* @return {string} OE value
|
||||
*/
|
||||
_OEvalue(){
|
||||
/** @type {forge.md.digest} */
|
||||
var md = forge.md.sha256.create();
|
||||
md.update(this.ownerpwd + this.OKS + this.U);
|
||||
return z.Crypto._AESnopad(md.digest().getBytes(), this.key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert password for AES-256 encryption mode
|
||||
* @private
|
||||
* @param {string} pwd password
|
||||
* @return {string} password
|
||||
*/
|
||||
_fixAES256Password(pwd) {
|
||||
return pwd.substr(0, 127);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute encryption key
|
||||
* @private
|
||||
*/
|
||||
_generateencryptionkey(){
|
||||
/** @type {number} */
|
||||
var keybytelen = this.Length / 8;
|
||||
/** @type {forge.md.digest} */
|
||||
var md = null;
|
||||
// standard mode
|
||||
if(!this.pubkeys){
|
||||
/** @type {number} */
|
||||
var protection = z.Crypto.getUserPermissionCode(this.permissions, this.mode);
|
||||
if(this.mode == z.Crypto.Mode.AES_256){
|
||||
// generate 256 bit random key
|
||||
md = forge.md.sha256.create();
|
||||
md.update(z.Crypto.getRandomSeed());
|
||||
this.key = md.digest().getBytes().substr(0, keybytelen);
|
||||
// truncate passwords
|
||||
this.userpwd = this._fixAES256Password(this.userpwd);
|
||||
this.ownerpwd = this._fixAES256Password(this.ownerpwd);
|
||||
// Compute U value
|
||||
this.U = this._Uvalue();
|
||||
// Compute UE value
|
||||
this.UE = this._UEvalue();
|
||||
// Compute O value
|
||||
this.O = this._Ovalue();
|
||||
// Compute OE value
|
||||
this.OE = this._OEvalue();
|
||||
// Compute P value
|
||||
this.P = protection;
|
||||
// Computing the encryption dictionary's Perms (permissions) value
|
||||
/** @type {string} */
|
||||
var perms = z.Crypto.getUserPermissionsString(protection); // bytes 0-3
|
||||
perms += String.fromCharCode(255).repeat(4); // bytes 4-7
|
||||
if(typeof this.CF.EncryptMetadata == "boolean" && !this.CF.EncryptMetadata){ // byte 8
|
||||
perms += "F";
|
||||
}else{
|
||||
perms += "T";
|
||||
}
|
||||
perms += "adb"; // bytes 9-11
|
||||
perms += "nick"; // bytes 12-15
|
||||
this.perms = z.Crypto._AESnopad(this.key, perms);
|
||||
}else{ // RC4-40, RC4-128, AES-128
|
||||
// Pad passwords
|
||||
this.userpwd = (this.userpwd + z.Crypto.EncPadding).substr(0, 32);
|
||||
this.ownerpwd = (this.ownerpwd + z.Crypto.EncPadding).substr(0, 32);
|
||||
// Compute O value
|
||||
this.O = this._Ovalue();
|
||||
// get default permissions string (reverse byte order)
|
||||
/** @type {string} */
|
||||
var permissionstr = z.Crypto.getUserPermissionsString(protection);
|
||||
// Compute encryption key
|
||||
/** @type {string} */
|
||||
var tmp = z.Crypto._md5_16(this.userpwd + this.O + permissionstr + this.fileid);
|
||||
if(this.mode > z.Crypto.Mode.RC4_40) {
|
||||
for(var i=0; i<50; i++){
|
||||
tmp = z.Crypto._md5_16(tmp.substr(0, keybytelen));
|
||||
}
|
||||
}
|
||||
this.key = tmp.substr(0, keybytelen);
|
||||
// Compute U value
|
||||
this.U = this._Uvalue();
|
||||
// Compute P value
|
||||
this.P = protection;
|
||||
}
|
||||
// Public-Key mode
|
||||
}else{
|
||||
// random 20-byte seed
|
||||
md = forge.md.sha1.create();
|
||||
md.update(z.Crypto.getRandomSeed());
|
||||
/** @type {string} */
|
||||
var seed = md.digest().getBytes();
|
||||
/** @type {string} */
|
||||
var recipient_bytes = "";
|
||||
/** @type {string} */
|
||||
var pkpermissionstr = z.Crypto.getCertPermissionsString(this.permissions);
|
||||
// for each public certificate
|
||||
this.pubkeys.forEach(function(/** @type {PubKeyInfo} */a_pubkey){
|
||||
// get permissions string
|
||||
/** @type {string} */
|
||||
var a_pkpermissionstr = pkpermissionstr;
|
||||
if(a_pubkey.p){
|
||||
a_pkpermissionstr = z.Crypto.getCertPermissionsString(a_pubkey.p);
|
||||
}
|
||||
// envelope data
|
||||
/** @type {string} */
|
||||
var a_envelope = seed + a_pkpermissionstr;
|
||||
/** @type {forge_cert} */
|
||||
var a_cert = null;
|
||||
if(a_pubkey.c){
|
||||
if(a_pubkey.c.issuer){
|
||||
a_cert = /** @type {forge_cert} */(a_pubkey.c);
|
||||
}else{
|
||||
/** @type {string} */
|
||||
var a_cerstr = "";
|
||||
if(typeof a_pubkey.c == "string"){
|
||||
a_cerstr = a_pubkey.c;
|
||||
}else{
|
||||
a_cerstr = z.u8arrToRaw(new Uint8Array(/** @type {Array<number>|ArrayBuffer|Uint8Array} */(a_pubkey.c)));
|
||||
}
|
||||
a_cert = z.loadCert(a_cerstr);
|
||||
z.fixCertAttributes(a_cert);
|
||||
}
|
||||
}else{
|
||||
throw new Error("We need a certificate.");
|
||||
}
|
||||
// create a p7 enveloped message
|
||||
/** @type {forge.pkcs7} */
|
||||
var a_p7 = forge.pkcs7.createEnvelopedData();
|
||||
// add a recipient
|
||||
a_p7.addRecipient(a_cert);
|
||||
// set content
|
||||
a_p7.content = forge.util.createBuffer(a_envelope);
|
||||
// encrypt
|
||||
a_p7.encrypt();
|
||||
/** @type {forge.util.ByteStringBuffer} */
|
||||
var a_signature = forge.asn1.toDer(a_p7.toAsn1());
|
||||
/** @type {string} */
|
||||
var a_hexsignature = a_signature.toHex();
|
||||
this.Recipients.push(a_hexsignature);
|
||||
recipient_bytes += a_signature.getBytes();
|
||||
}.bind(this));
|
||||
// calculate encryption key
|
||||
if(this.mode == z.Crypto.Mode.AES_256){
|
||||
md = forge.md.sha256.create();
|
||||
}else{ // RC4-40, RC4-128, AES-128
|
||||
md = forge.md.sha1.create();
|
||||
}
|
||||
md.update(seed + recipient_bytes);
|
||||
this.key = md.digest().getBytes().substr(0, keybytelen);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
//Only for nodejs Start//
|
||||
if(typeof exports === "object" && typeof module !== "undefined"){
|
||||
module.exports = supplyZgaCryptor;
|
||||
}
|
||||
//Only for nodejs End//
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,362 @@
|
|||
{
|
||||
"name": "zgapdfsigner",
|
||||
"version": "2.7.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "zgapdfsigner",
|
||||
"version": "2.7.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "1.15.6",
|
||||
"node-forge": "1.3.1",
|
||||
"pdf-fontkit": "1.8.9",
|
||||
"pdf-lib": "1.17.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"google-closure-compiler": "^20231112.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@pdf-lib/standard-fonts": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
|
||||
"integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==",
|
||||
"dependencies": {
|
||||
"pako": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@pdf-lib/upng": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz",
|
||||
"integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==",
|
||||
"dependencies": {
|
||||
"pako": "^1.0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/clone": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
|
||||
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/clone-buffer": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz",
|
||||
"integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/clone-stats": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz",
|
||||
"integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/cloneable-readable": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz",
|
||||
"integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.1",
|
||||
"process-nextick-args": "^2.0.0",
|
||||
"readable-stream": "^2.3.5"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.6",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/google-closure-compiler": {
|
||||
"version": "20231112.0.0",
|
||||
"resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20231112.0.0.tgz",
|
||||
"integrity": "sha512-C/MPRThIxRAFomGhpEwXyVcWRIVnmqGraJ5BTJ+EQcfAiPNBvl+Q5nKU2J/lICPcx+YQ+3c+FJ/gBJsTXPjcwg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chalk": "4.x",
|
||||
"google-closure-compiler-java": "^20231112.0.0",
|
||||
"minimist": "1.x",
|
||||
"vinyl": "2.x",
|
||||
"vinyl-sourcemaps-apply": "^0.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"google-closure-compiler": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"google-closure-compiler-linux": "^20231112.0.0",
|
||||
"google-closure-compiler-osx": "^20231112.0.0",
|
||||
"google-closure-compiler-windows": "^20231112.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/google-closure-compiler-java": {
|
||||
"version": "20231112.0.0",
|
||||
"resolved": "https://registry.npmjs.org/google-closure-compiler-java/-/google-closure-compiler-java-20231112.0.0.tgz",
|
||||
"integrity": "sha512-E45cJD6/xLJlL8pL6HEoxu8nEKp87CnrojUK0UuHiT7ZjCsrJfR4WhZwNNCq2+/6gYD9unGgMsunV4DDtBbvaA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/google-closure-compiler-windows": {
|
||||
"version": "20231112.0.0",
|
||||
"resolved": "https://registry.npmjs.org/google-closure-compiler-windows/-/google-closure-compiler-windows-20231112.0.0.tgz",
|
||||
"integrity": "sha512-wbN5EOCGz53HVENVtOEO1brn/G3ZmCV1ULiJljNuASQc62vQ36QHA6XnAZOAGTEpAoMnYRv3dtXtBKd07wBdsA==",
|
||||
"cpu": [
|
||||
"x32",
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/node-forge": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
|
||||
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
|
||||
"engines": {
|
||||
"node": ">= 6.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
|
||||
},
|
||||
"node_modules/pdf-fontkit": {
|
||||
"version": "1.8.9",
|
||||
"resolved": "https://registry.npmjs.org/pdf-fontkit/-/pdf-fontkit-1.8.9.tgz",
|
||||
"integrity": "sha512-TTq+umfhlFjUuQYOq6dCKT/wLslCrX4zVr5gqrIvrGHfo+vJ3ETapZTb4YLOCErohX7pF+HxlXSZuiToSRhNmA==",
|
||||
"dependencies": {
|
||||
"pako": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/pdf-lib": {
|
||||
"version": "1.17.1",
|
||||
"resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz",
|
||||
"integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==",
|
||||
"dependencies": {
|
||||
"@pdf-lib/standard-fonts": "^1.0.0",
|
||||
"@pdf-lib/upng": "^1.0.1",
|
||||
"pako": "^1.0.11",
|
||||
"tslib": "^1.11.1"
|
||||
}
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/remove-trailing-separator": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
|
||||
"integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/replace-ext": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz",
|
||||
"integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/vinyl": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz",
|
||||
"integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"clone": "^2.1.1",
|
||||
"clone-buffer": "^1.0.0",
|
||||
"clone-stats": "^1.0.0",
|
||||
"cloneable-readable": "^1.0.0",
|
||||
"remove-trailing-separator": "^1.0.1",
|
||||
"replace-ext": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/vinyl-sourcemaps-apply": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz",
|
||||
"integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"source-map": "^0.5.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"name": "zgapdfsigner",
|
||||
"version": "2.7.3",
|
||||
"author": "zboris12",
|
||||
"description": "A javascript tool to sign a pdf or set protection to a pdf in web browser, Google Apps Script and nodejs.",
|
||||
"homepage": "https://github.com/zboris12/zgapdfsigner",
|
||||
"private": false,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/zboris12/zgapdfsigner.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/zboris12/zgapdfsigner/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"main": "lib/zganode.js",
|
||||
"unpkg": "dist/zgapdfsigner.min.js",
|
||||
"files": [
|
||||
"dist/*.min.js",
|
||||
"lib/*.d.ts",
|
||||
"lib/*.js"
|
||||
],
|
||||
"keywords": [
|
||||
"pdf-sign",
|
||||
"pdf-signature",
|
||||
"pdf-protection",
|
||||
"pdf-encryption",
|
||||
"google-apps-script",
|
||||
"LTV",
|
||||
"TSA",
|
||||
"長期署名"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "./build.sh",
|
||||
"test": "node test4node.js ${pfxpwd}"
|
||||
},
|
||||
"dependencies": {
|
||||
"follow-redirects": "1.15.6",
|
||||
"node-forge": "1.3.1",
|
||||
"pdf-fontkit": "1.8.9",
|
||||
"pdf-lib": "1.17.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"google-closure-compiler": "^20231112.0.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"editor.tabSize": 2,
|
||||
"editor.formatOnSave": true,
|
||||
"files.eol": "\n"
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "zgapdfsigner-test-ts",
|
||||
"version": "1.0.0",
|
||||
"author": "zboris12",
|
||||
"description": "A typescript program to test zgapdfsigner.",
|
||||
"private": false,
|
||||
"license": "MIT",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test": "node test/index.js ${pfxpwd}"
|
||||
},
|
||||
"dependencies": {
|
||||
"zgapdfsigner": "^2.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node-forge": "^1.3.11",
|
||||
"typescript": "~4.9"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
import * as m_fs from "node:fs";
|
||||
import * as m_path from "node:path";
|
||||
import * as Zga from "zgapdfsigner";
|
||||
|
||||
const workpath = "./";
|
||||
|
||||
async function sign_protect(pdfPath: string, pfxPath: string, ps: string, perm: number, imgPath?: string, txt?: string, fontPath?: string): Promise<string> {
|
||||
let pdf: Buffer = m_fs.readFileSync(pdfPath);
|
||||
let pfx: Buffer = m_fs.readFileSync(pfxPath);
|
||||
let img: Buffer | undefined = undefined;
|
||||
let imgType: string = "";
|
||||
let font: Buffer | Zga.PDFLib.StandardFonts | undefined = undefined;
|
||||
|
||||
if (perm == 1) {
|
||||
console.log("\nTest signing pdf with full protection. (permission 1 and password encryption)");
|
||||
} else {
|
||||
console.log("\nTest signing pdf with permission " + perm);
|
||||
}
|
||||
|
||||
if (imgPath) {
|
||||
img = m_fs.readFileSync(imgPath);
|
||||
imgType = m_path.extname(imgPath).slice(1);
|
||||
}
|
||||
if (fontPath) {
|
||||
if (Zga.PDFLib.isStandardFont(fontPath)) {
|
||||
font = fontPath as string as Zga.PDFLib.StandardFonts;
|
||||
} else {
|
||||
font = m_fs.readFileSync(fontPath);
|
||||
}
|
||||
}
|
||||
let sopt: Zga.SignOption = {
|
||||
p12cert: pfx,
|
||||
pwd: ps,
|
||||
permission: perm,
|
||||
signdate: "1",
|
||||
reason: "I have a test reason " + perm + ".",
|
||||
location: "I am on the earth " + perm + ".",
|
||||
contact: "zga" + perm + "@zga.com",
|
||||
ltv: 1,
|
||||
debug: true,
|
||||
};
|
||||
if (img || txt) {
|
||||
sopt.drawinf = {
|
||||
area: {
|
||||
x: perm ? 25 : 200, // left
|
||||
y: 50, // top
|
||||
w: txt ? undefined : 60,
|
||||
h: txt ? undefined : 100,
|
||||
},
|
||||
pageidx: "-",
|
||||
imgInfo: img ? {
|
||||
imgData: img,
|
||||
imgType: imgType,
|
||||
} : undefined,
|
||||
textInfo: txt ? {
|
||||
text: txt,
|
||||
fontData: font,
|
||||
color: "00f0f1",
|
||||
lineHeight: 20,
|
||||
size: 16,
|
||||
align: 1,
|
||||
wMax: 80,
|
||||
yOffset: 10,
|
||||
xOffset: 20,
|
||||
noBreaks: "[あいうえおA-Za-z0-9]",
|
||||
} : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
let eopt: Zga.EncryptOption | undefined = undefined;
|
||||
if (perm == 1) {
|
||||
eopt = {
|
||||
mode: Zga.Crypto.Mode.AES_256,
|
||||
permissions: ["copy", "copy-extract", "print-high"],
|
||||
userpwd: "123",
|
||||
};
|
||||
}
|
||||
|
||||
let ser: Zga.PdfSigner = new Zga.PdfSigner(sopt);
|
||||
let u8dat: Uint8Array = await ser.sign(pdf, eopt);
|
||||
let outPath: string = "";
|
||||
if (u8dat) {
|
||||
outPath = m_path.join(__dirname, workpath + "test_perm" + perm + m_path.basename(pdfPath));
|
||||
m_fs.writeFileSync(outPath, u8dat);
|
||||
console.log("Output file: " + outPath);
|
||||
}
|
||||
return outPath;
|
||||
}
|
||||
|
||||
async function addtsa(pdfPath: string): Promise<string> {
|
||||
console.log("\nTest signing pdf by a timestamp.");
|
||||
|
||||
let pdf: Buffer = m_fs.readFileSync(pdfPath);
|
||||
let sopt: Zga.SignOption = {
|
||||
signdate: "2",
|
||||
reason: "I have a test reason tsa.",
|
||||
location: "I am on the earth tsa.",
|
||||
contact: "zgatsa@zga.com",
|
||||
ltv: 1,
|
||||
debug: true,
|
||||
};
|
||||
let ser: Zga.PdfSigner = new Zga.PdfSigner(sopt);
|
||||
let u8dat: Uint8Array = await ser.sign(pdf);
|
||||
let outPath: string = m_path.join(__dirname, workpath + "tsa_" + m_path.basename(pdfPath));
|
||||
m_fs.writeFileSync(outPath, u8dat);
|
||||
console.log("Output file: " + outPath);
|
||||
return outPath;
|
||||
}
|
||||
|
||||
async function main1(angle: number): Promise<void> {
|
||||
let pdfPath: string = m_path.join(__dirname, workpath + "_test" + (angle ? "_" + angle : "") + ".pdf");
|
||||
let pfxPath: string = m_path.join(__dirname, workpath + "_test.pfx");
|
||||
let ps: string = "";
|
||||
let imgPath: string = m_path.join(__dirname, workpath + "_test.png");
|
||||
let fontPath: string = angle ? Zga.PDFLib.StandardFonts.TimesRomanBold : m_path.join(__dirname, workpath + "_test.ttf");
|
||||
|
||||
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 = "";
|
||||
}
|
||||
|
||||
if (pfxPath) {
|
||||
await sign_protect(pdfPath, pfxPath, ps, 1, imgPath, "あいうえおあいうえおか\r\n\nThis is a test of text!\n");
|
||||
pdfPath = await sign_protect(pdfPath, pfxPath, ps, 2, imgPath, (angle ? "" : "ありがとうご\r\n\n") + "This is an another test of text!\n", fontPath);
|
||||
pdfPath = await sign_protect(pdfPath, pfxPath, ps, 0, undefined, (angle ? "" : "たちつて得\n\n") + "This is a test for same font!\n", fontPath);
|
||||
await addtsa(pdfPath);
|
||||
} else {
|
||||
await addtsa(pdfPath);
|
||||
}
|
||||
|
||||
console.log("Done");
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
let arr: Array<number> = [0, 90, 180, 270];
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
await main1(arr[i]);
|
||||
// break;
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2015",
|
||||
"module": "commonjs",
|
||||
"newLine": "LF",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitThis": true,
|
||||
"rootDir": "./src",
|
||||
"outDir": "./test",
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
250
test.html
250
test.html
|
@ -4,22 +4,36 @@
|
|||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title> Test for ZgaPdfSigner </title>
|
||||
<script src="https://unpkg.com/node-forge/dist/forge.min.js" type="text/javascript"></script>
|
||||
<script src="https://unpkg.com/pdf-lib/dist/pdf-lib.min.js" type="text/javascript"></script>
|
||||
<script src="zgapdfsigner.js" type="text/javascript"></script>
|
||||
<script src="https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js" type="text/javascript"></script>
|
||||
<script src="https://unpkg.com/pdf-fontkit@1.8.9/dist/fontkit.umd.min.js" type="text/javascript"></script>
|
||||
<script src="https://unpkg.com/pako@1.0.11/dist/pako_inflate.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="dist/zgapdfsigner.min.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
function test(){
|
||||
var img = document.getElementById("img").files[0];
|
||||
if(img){
|
||||
readAsArrayBuffer(img, function(a_buff){
|
||||
testSign(a_buff, getFilExt(img));
|
||||
});
|
||||
/**
|
||||
* @param {string} fid
|
||||
* @return {Promise<ArrayBuffer>}
|
||||
*/
|
||||
function readFile(fid){
|
||||
return new Promise((resolve, reject) => {
|
||||
var f = document.getElementById(fid).files[0];
|
||||
if(f){
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(a_evt){
|
||||
resolve(a_evt.target.result);
|
||||
};
|
||||
reader.readAsArrayBuffer(f);
|
||||
}else{
|
||||
testSign();
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
function getFilExt(f){
|
||||
var n = f.name;
|
||||
/**
|
||||
* @param {string} fid
|
||||
* @return {string}
|
||||
*/
|
||||
function getFilExt(fid){
|
||||
var n = document.getElementById(fid).files[0].name;
|
||||
var i = n.lastIndexOf(".");
|
||||
if(i >= 0){
|
||||
return n.slice(i + 1);
|
||||
|
@ -27,50 +41,210 @@ function getFilExt(f){
|
|||
return null;
|
||||
}
|
||||
}
|
||||
function readAsArrayBuffer(f, func){
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(a_evt){
|
||||
func(a_evt.target.result);
|
||||
};
|
||||
reader.readAsArrayBuffer(f);
|
||||
async function testMe(){
|
||||
/** @type {ArrayBuffer} */
|
||||
var pdf = await readFile("fff");
|
||||
if(!pdf){
|
||||
alert("The target pdf is not specified.");
|
||||
return;
|
||||
}
|
||||
function testSign(imgdat, imgtyp){
|
||||
var fil = document.getElementById("fff").files[0];
|
||||
var kf = document.getElementById("kkk").files[0];
|
||||
readAsArrayBuffer(fil, function(a_pdf){
|
||||
readAsArrayBuffer(kf, async function(b_kfbuf){
|
||||
var b_opt = {
|
||||
p12cert: b_kfbuf,
|
||||
pwd: document.getElementById("pwd").value,
|
||||
|
||||
/** @type {ArrayBuffer} */
|
||||
var pfx = await readFile("kkk");
|
||||
/** @type {string} */
|
||||
var ps = document.getElementById("pwd").value;
|
||||
if(pfx && !ps){
|
||||
alert("The passphrase is not specified.");
|
||||
return;
|
||||
}
|
||||
|
||||
/** @type {ArrayBuffer} */
|
||||
var img = await readFile("img");
|
||||
/** @type {string} */
|
||||
var imgType = "";
|
||||
if(img){
|
||||
imgType = getFilExt("img");
|
||||
}
|
||||
/** @type {string} */
|
||||
var txt = document.getElementById("txt").value;
|
||||
/** @type {ArrayBuffer} */
|
||||
var font = await readFile("font");
|
||||
|
||||
/** @type {ArrayBuffer} */
|
||||
var pubcert = await readFile("pubcert");
|
||||
/** @type {string} */
|
||||
var upwd = document.getElementById("upwd").value;
|
||||
|
||||
/** @type {SignOption} */
|
||||
var sopt = null;
|
||||
if(pfx){
|
||||
sopt = {
|
||||
p12cert: pfx,
|
||||
pwd: ps,
|
||||
permission: parseInt(document.getElementById("sperm").value),
|
||||
reason: document.getElementById("tReason").value,
|
||||
location: document.getElementById("tLocation").value,
|
||||
contact: document.getElementById("tContact").value,
|
||||
debug: true,
|
||||
};
|
||||
if(imgdat){
|
||||
b_opt.drawinf = {
|
||||
if(img || txt){
|
||||
sopt.drawinf = {
|
||||
area: {
|
||||
x: 25, // left
|
||||
x: parseInt(document.getElementById("drawx").value), // left
|
||||
y: 150, // top
|
||||
w: 60,
|
||||
h: 60,
|
||||
w: txt ? undefined : 60,
|
||||
h: txt ? undefined : 100,
|
||||
},
|
||||
imgData: imgdat,
|
||||
imgType: imgtyp,
|
||||
pageidx: "-",
|
||||
imgInfo: img ? {
|
||||
imgData: img,
|
||||
imgType: imgType,
|
||||
} : undefined,
|
||||
textInfo: txt ? {
|
||||
text: txt,
|
||||
fontData: font,
|
||||
subset: true,
|
||||
color: "f00",
|
||||
size: 16,
|
||||
align: 2,
|
||||
wMax: 80,
|
||||
yOffset: 10,
|
||||
xOffset: 20,
|
||||
noBreaks: "[あいうえおA-Za-z0-9]",
|
||||
} : undefined,
|
||||
};
|
||||
}
|
||||
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(new Blob([b_u8s], {"type" : "application/pdf"}));
|
||||
}
|
||||
|
||||
/** @type {EncryptOption} */
|
||||
var eopt = undefined;
|
||||
if(pubcert || upwd){
|
||||
eopt = {
|
||||
mode: parseInt(document.getElementById("mode").value),
|
||||
permissions: [],
|
||||
};
|
||||
Array.from(document.getElementsByName("perms")).forEach((a_chk) => {
|
||||
if(!a_chk.checked){
|
||||
eopt.permissions.push(a_chk.value);
|
||||
}
|
||||
});
|
||||
if(pubcert){
|
||||
eopt.pubkeys = [{
|
||||
c: pubcert,
|
||||
}];
|
||||
}else if(upwd){
|
||||
eopt.userpwd = upwd;
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {Uint8Array} */
|
||||
var u8dat = null;
|
||||
if(sopt){
|
||||
/** @type {Zga.PdfSigner} */
|
||||
var ser = new Zga.PdfSigner(sopt);
|
||||
u8dat = await ser.sign(pdf, eopt);
|
||||
}else if(eopt){
|
||||
/** @type {Zga.PdfCryptor} */
|
||||
var crypr = new Zga.PdfCryptor(eopt);
|
||||
/** @type {PDFLib.PDFDocument} */
|
||||
var pdfdoc = await crypr.encryptPdf(pdf);
|
||||
u8dat = await pdfdoc.save({"useObjectStreams": false});
|
||||
}else{
|
||||
alert("Nothing to do.");
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById("download").download = "test_test.pdf";
|
||||
document.getElementById("download").href = window.URL.createObjectURL(new Blob([u8dat], {"type" : "application/pdf"}));
|
||||
document.getElementById("download").click();
|
||||
}
|
||||
function test(){
|
||||
testMe().catch((err) => {
|
||||
console.error(err);
|
||||
alert(err.message);
|
||||
});
|
||||
}
|
||||
function clearFiles(){
|
||||
["fff","kkk","img","font","pubcert"].forEach((a_id) => {
|
||||
document.getElementById(a_id).value = "";
|
||||
});
|
||||
}
|
||||
function changeSperm(){
|
||||
/** @type {Element} */
|
||||
var sel = window.event.currentTarget || window.event.srcElement || window.event.target;
|
||||
/** @type {Element} */
|
||||
var spn = document.getElementById("spcmt");
|
||||
switch(sel.value){
|
||||
case "1":
|
||||
spn.innerText = "No changes to the document are permitted; any change to the document invalidates the signature.";
|
||||
break;
|
||||
case "2":
|
||||
spn.innerText = "Permitted changes are filling in forms, instantiating page templates, and signing; other changes invalidate the signature.";
|
||||
break;
|
||||
case "3":
|
||||
spn.innerText = "Permitted changes are the same as for 2, as well as annotation creation, deletion, and modification; other changes invalidate the signature.";
|
||||
break;
|
||||
default:
|
||||
spn.innerText = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
line-height: 1.5em;
|
||||
}
|
||||
span.header {
|
||||
font-weight: bold;
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<label>pdf </label><input type="file" id="fff" /><br />
|
||||
<hr />
|
||||
<span class="header">Sign the PDF:</span><br />
|
||||
<label>certificate </label><input type="file" id="kkk" /><br />
|
||||
<label>passphrase </label><input type="password" id="pwd" /><br />
|
||||
<label>signature image </label><input type="file" id="img" /><br />
|
||||
<a id="download" href="#" download="" style="display: none;" target="_blank">dummy</a>
|
||||
<label>signature text </label><input type="text" id="txt" /><br />
|
||||
<label>text font </label><input type="file" id="font" /><br />
|
||||
<label>left </label><input type="text" id="drawx" value="25" /><br />
|
||||
<label>permission </label>
|
||||
<select id="sperm" onchange="changeSperm()">
|
||||
<option value="0">No DocMDP</option>
|
||||
<option value="1">DocMDP pattern 1</option>
|
||||
<option value="2">DocMDP pattern 2</option>
|
||||
<option value="3">DocMDP pattern 3</option>
|
||||
</select>
|
||||
<span id="spcmt"></span><br />
|
||||
<label>reason </label><input type="text" id="tReason" value="I have a test reason." /><br />
|
||||
<label>location </label><input type="text" id="tLocation" value="I am on the earth." /><br />
|
||||
<label>contact </label><input type="text" id="tContact" value="zga@zga.com" /><br />
|
||||
<hr />
|
||||
<span class="header">Encrypt the PDF:</span><br />
|
||||
<label>encryption </label>
|
||||
<select id="mode">
|
||||
<option value="0">RC4-40</option>
|
||||
<option value="1">RC4-128</option>
|
||||
<option value="2">AES-128</option>
|
||||
<option value="3">AES-256</option>
|
||||
</select><br />
|
||||
<label>user password </label><input type="password" id="upwd" /><br />
|
||||
<label>public certificate </label><input type="file" id="pubcert" /><br />
|
||||
<label>permissions: </label><br />
|
||||
<input type="checkbox" id="pCopy" name="perms" value="copy" checked><label for="pCopy">"copy": Copy text and graphics from the document. (Only valid on public-key mode)</label><br />
|
||||
<input type="checkbox" id="pPrint" name="perms" value="print" checked><label for="pPrint">"print": Print the document.</label><br />
|
||||
<input type="checkbox" id="pModify" name="perms" value="modify" checked><label for="pModify">"modify": Modify the contents of the document by operations other than those controlled by 'fill-forms', 'extract' and 'assemble'.</label><br />
|
||||
<input type="checkbox" id="pCopyExtract" name="perms" value="copy-extract" checked><label for="pCopyExtract">"copy-extract": Copy or otherwise extract text and graphics from the document.</label><br />
|
||||
<input type="checkbox" id="pAnnotForms" name="perms" value="annot-forms" checked><label for="pAnnotForms">"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).</label><br />
|
||||
<input type="checkbox" id="pFillForms" name="perms" value="fill-forms" checked><label for="pFillForms">"fill-forms": Fill in existing interactive form fields (including signature fields), even if 'annot-forms' is not specified.</label><br />
|
||||
<input type="checkbox" id="pExtract" name="perms" value="extract" checked><label for="pExtract">"extract": Extract text and graphics (in support of accessibility to users with disabilities or for other purposes).</label><br />
|
||||
<input type="checkbox" id="pAssemble" name="perms" value="assemble" checked><label for="pAssemble">"assemble": Assemble the document (insert, rotate, or delete pages and create bookmarks or thumbnail images), even if 'modify' is not set.</label><br />
|
||||
<input type="checkbox" id="pPrintHigh" name="perms" value="print-high" checked><label for="pPrintHigh">"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.</label><br />
|
||||
<hr />
|
||||
<input type="button" value="test" onclick="test()" />
|
||||
<input type="button" value="clear files" onclick="clearFiles()" />
|
||||
<a id="download" href="#" download="" style="display: none;" target="_blank">dummy</a>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
// ES Module Mode
|
||||
// import * as m_fs from "node:fs";
|
||||
// import * as m_path from "node:path";
|
||||
// import { fileURLToPath } from "node:url";
|
||||
// import { default as Zga } from "./lib/zganode.js";
|
||||
// const __dirname = m_path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const m_fs = require("fs");
|
||||
const m_path = require("path");
|
||||
const Zga = require("./lib/zganode.js");
|
||||
|
||||
const workpath = "test/";
|
||||
|
||||
/**
|
||||
* @param {string} pdfPath
|
||||
* @param {string} pfxPath
|
||||
* @param {string} ps
|
||||
* @param {number} perm
|
||||
* @param {string=} imgPath
|
||||
* @param {string=} txt
|
||||
* @param {string=} fontPath
|
||||
* @return {Promise<string>} output path
|
||||
*/
|
||||
async function sign_protect(pdfPath, pfxPath, ps, perm, imgPath, txt, fontPath){
|
||||
/** @type {Buffer} */
|
||||
var pdf = m_fs.readFileSync(pdfPath);
|
||||
/** @type {Buffer} */
|
||||
var pfx = m_fs.readFileSync(pfxPath);
|
||||
/** @type {Buffer} */
|
||||
var img = null;
|
||||
/** @type {string} */
|
||||
var imgType = "";
|
||||
/** @type {Buffer|string} */
|
||||
var font = null;
|
||||
|
||||
if(perm == 1){
|
||||
console.log("\nTest signing pdf with full protection. (permission 1 and password encryption)");
|
||||
}else{
|
||||
console.log("\nTest signing pdf with permission "+perm);
|
||||
}
|
||||
|
||||
if(imgPath){
|
||||
img = m_fs.readFileSync(imgPath);
|
||||
imgType = m_path.extname(imgPath).slice(1);
|
||||
}
|
||||
if(fontPath){
|
||||
if(Zga.PDFLib.isStandardFont(fontPath)){
|
||||
font = fontPath;
|
||||
}else{
|
||||
font = m_fs.readFileSync(fontPath);
|
||||
}
|
||||
}
|
||||
/** @type {SignOption} */
|
||||
var sopt = {
|
||||
p12cert: pfx,
|
||||
pwd: ps,
|
||||
permission: perm,
|
||||
signdate: "1",
|
||||
reason: "I have a test reason "+perm+".",
|
||||
location: "I am on the earth "+perm+".",
|
||||
contact: "zga"+perm+"@zga.com",
|
||||
ltv: 1,
|
||||
debug: true,
|
||||
};
|
||||
if(img || txt){
|
||||
sopt.drawinf = {
|
||||
area: {
|
||||
x: perm ? 25 : 200, // left
|
||||
y: 50, // top
|
||||
w: txt ? undefined : 60,
|
||||
h: txt ? undefined : 100,
|
||||
},
|
||||
pageidx: "-",
|
||||
imgInfo: img ? {
|
||||
imgData: img,
|
||||
imgType: imgType,
|
||||
} : undefined,
|
||||
textInfo: txt ? {
|
||||
text: txt,
|
||||
fontData: font,
|
||||
color: "00f0f1",
|
||||
lineHeight: 20,
|
||||
size: 16,
|
||||
align: 1,
|
||||
wMax: 80,
|
||||
yOffset: 10,
|
||||
xOffset: 20,
|
||||
noBreaks: "[あいうえおA-Za-z0-9]",
|
||||
} : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/** @type {EncryptOption} */
|
||||
var eopt = undefined;
|
||||
if(perm == 1){
|
||||
eopt = {
|
||||
mode: Zga.Crypto.Mode.AES_256,
|
||||
permissions: ["copy", "copy-extract", "print-high"],
|
||||
userpwd: "123",
|
||||
};
|
||||
}
|
||||
|
||||
/** @type {Zga.PdfSigner} */
|
||||
var ser = new Zga.PdfSigner(sopt);
|
||||
/** @type {Uint8Array} */
|
||||
var u8dat = await ser.sign(pdf, eopt);
|
||||
if(u8dat){
|
||||
/** @type {string} */
|
||||
var outPath = m_path.join(__dirname, workpath+"test_perm"+perm+m_path.basename(pdfPath));
|
||||
m_fs.writeFileSync(outPath, u8dat);
|
||||
console.log("Output file: " + outPath);
|
||||
}
|
||||
return outPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} pdfPath
|
||||
* @return {Promise<string>} output path
|
||||
*/
|
||||
async function addtsa(pdfPath){
|
||||
console.log("\nTest signing pdf by a timestamp.");
|
||||
|
||||
/** @type {Buffer} */
|
||||
var pdf = m_fs.readFileSync(pdfPath);
|
||||
/** @type {SignOption} */
|
||||
var sopt = {
|
||||
signdate: "2",
|
||||
reason: "I have a test reason tsa.",
|
||||
location: "I am on the earth tsa.",
|
||||
contact: "zgatsa@zga.com",
|
||||
ltv: 1,
|
||||
debug: true,
|
||||
};
|
||||
/** @type {Zga.PdfSigner} */
|
||||
var ser = new Zga.PdfSigner(sopt);
|
||||
/** @type {Uint8Array} */
|
||||
var u8dat = await ser.sign(pdf);
|
||||
/** @type {string} */
|
||||
var outPath = m_path.join(__dirname, workpath+"tsa_"+m_path.basename(pdfPath));
|
||||
m_fs.writeFileSync(outPath, u8dat);
|
||||
console.log("Output file: " + outPath);
|
||||
return outPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} angle
|
||||
*/
|
||||
async function main1(angle){
|
||||
/** @type {string} */
|
||||
var pdfPath = m_path.join(__dirname, workpath+"_test"+(angle ? "_"+angle : "")+".pdf");
|
||||
/** @type {string} */
|
||||
var pfxPath = m_path.join(__dirname, workpath+"_test.pfx");
|
||||
/** @type {string} */
|
||||
var ps = "";
|
||||
/** @type {string} */
|
||||
var imgPath = m_path.join(__dirname, workpath+"_test.png");
|
||||
/** @type {string} */
|
||||
var fontPath = m_path.join(__dirname, workpath+"_test.ttf");
|
||||
// var fontPath = Zga.PDFLib.StandardFonts.CourierBold;
|
||||
|
||||
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 = "";
|
||||
}
|
||||
|
||||
if(pfxPath){
|
||||
await sign_protect(pdfPath, pfxPath, ps, 1, imgPath, "あいうえおあいうえおか\r\n\nThis is a test of text!\n");
|
||||
if(Zga.PDFLib.isStandardFont(fontPath)){
|
||||
pdfPath = await sign_protect(pdfPath, pfxPath, ps, 2, imgPath, "This is an another test of text!\n", fontPath);
|
||||
pdfPath = await sign_protect(pdfPath, pfxPath, ps, 0, undefined, "This is a test for same font!\n", fontPath);
|
||||
}else{
|
||||
pdfPath = await sign_protect(pdfPath, pfxPath, ps, 2, imgPath, "ありがとうご\r\nThis is an another test of text!\n", fontPath);
|
||||
pdfPath = await sign_protect(pdfPath, pfxPath, ps, 0, undefined, "たちつてと\n\nThis is a test for same font!\n", fontPath);
|
||||
}
|
||||
await addtsa(pdfPath);
|
||||
}else{
|
||||
await addtsa(pdfPath);
|
||||
}
|
||||
|
||||
console.log("Done");
|
||||
}
|
||||
|
||||
async function main(){
|
||||
/** @type {Array<number>} */
|
||||
var arr = [0, 90, 180, 270];
|
||||
/** @type {number} */
|
||||
for(var i=0; i<arr.length; i++){
|
||||
await main1(arr[i]);
|
||||
// break;
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
482
zgapdfsigner.js
482
zgapdfsigner.js
|
@ -1,482 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
globalThis.Zga = {
|
||||
|
||||
PdfSigner: class {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {SignOption} signopt
|
||||
*/
|
||||
constructor(signopt){
|
||||
/** @private @type {SignOption} */
|
||||
this.opt = signopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param {PDFLib.PDFDocument|Uint8Array|ArrayBuffer|string} pdf
|
||||
* @return {Promise<Uint8Array>}
|
||||
*/
|
||||
async sign(pdf){
|
||||
/** @type {PDFLib.PDFDocument} */
|
||||
var pdfdoc = null;
|
||||
if(pdf.addPage){
|
||||
pdfdoc = pdf;
|
||||
}else{
|
||||
pdfdoc = await PDFLib.PDFDocument.load(pdf);
|
||||
}
|
||||
|
||||
if(this.opt.drawinf && this.opt.drawinf.imgData && !this.opt.drawinf.img){
|
||||
if(this.opt.drawinf.imgType == "png"){
|
||||
this.opt.drawinf.img = await pdfdoc.embedPng(this.opt.drawinf.imgData);
|
||||
}else if(this.opt.drawinf.imgType == "jpg"){
|
||||
this.opt.drawinf.img = await pdfdoc.embedJpg(this.opt.drawinf.imgData);
|
||||
}else{
|
||||
throw new Error("Unkown image type. " + this.opt.drawinf.imgType);
|
||||
}
|
||||
}
|
||||
|
||||
this.addSignHolder(pdfdoc);
|
||||
var uarr = await pdfdoc.save({"useObjectStreams": false});
|
||||
var pdfstr = Zga.u8arrToRaw(uarr);
|
||||
|
||||
return this.signPdf(pdfstr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {PDFLib.PDFDocument} pdfdoc
|
||||
*/
|
||||
addSignHolder(pdfdoc){
|
||||
/** @const {string} */
|
||||
const DEFAULT_BYTE_RANGE_PLACEHOLDER = "**********";
|
||||
/** @const {number} */
|
||||
const SIGNATURE_LENGTH = 3322;
|
||||
|
||||
/** @const {VisualSignature} */
|
||||
const visign = new Zga.VisualSignature(this.opt.drawinf);
|
||||
/** @const {PDFLib.PDFRef} */
|
||||
const strmRef = visign.createStream(pdfdoc, this.opt.signame);
|
||||
/** @const {PDFLib.PDFPage} */
|
||||
const page = pdfdoc.getPages()[visign.getPageIndex()];
|
||||
|
||||
if(!this.opt.signdate){
|
||||
this.opt.signdate = new Date();
|
||||
}
|
||||
|
||||
/** @type {PDFLib.PDFArray} */
|
||||
var bytrng = new PDFLib.PDFArray(pdfdoc.context);
|
||||
bytrng.push(PDFLib.PDFNumber.of(0));
|
||||
bytrng.push(PDFLib.PDFName.of(DEFAULT_BYTE_RANGE_PLACEHOLDER));
|
||||
bytrng.push(PDFLib.PDFName.of(DEFAULT_BYTE_RANGE_PLACEHOLDER));
|
||||
bytrng.push(PDFLib.PDFName.of(DEFAULT_BYTE_RANGE_PLACEHOLDER));
|
||||
|
||||
/** @type {Object<string, *>} */
|
||||
var signObj = {
|
||||
"Type": "Sig",
|
||||
"Filter": "Adobe.PPKLite",
|
||||
"SubFilter": "adbe.pkcs7.detached",
|
||||
"ByteRange": bytrng,
|
||||
"Contents": PDFLib.PDFHexString.of("0".repeat(SIGNATURE_LENGTH)),
|
||||
"M": PDFLib.PDFString.fromDate(this.opt.signdate),
|
||||
"Prop_Build": pdfdoc.context.obj({
|
||||
"App": pdfdoc.context.obj({
|
||||
"Name": "ZgaPdfSinger",
|
||||
}),
|
||||
}),
|
||||
};
|
||||
if(this.opt.reason){
|
||||
signObj["Reason"] = PDFLib.PDFString.of(this.opt.reason);
|
||||
}
|
||||
if(this.opt.location){
|
||||
signObj["Location"] = PDFLib.PDFString.of(this.opt.location);
|
||||
}
|
||||
if(this.opt.contact){
|
||||
signObj["ContactInfo"] = PDFLib.PDFString.of(this.opt.contact);
|
||||
}
|
||||
var signatureDictRef = pdfdoc.context.register(pdfdoc.context.obj(signObj));
|
||||
|
||||
/** @type {Object<string, *>} */
|
||||
var widgetObj = {
|
||||
"Type": "Annot",
|
||||
"Subtype": "Widget",
|
||||
"FT": "Sig",
|
||||
"Rect": visign.getSignRect(),
|
||||
"V": signatureDictRef,
|
||||
"T": PDFLib.PDFString.of(this.opt.signame ? this.opt.signame : "Signature1"),
|
||||
"F": 132,
|
||||
"P": page.ref,
|
||||
};
|
||||
if(strmRef){
|
||||
widgetObj["AP"] = pdfdoc.context.obj({
|
||||
"N": strmRef,
|
||||
});
|
||||
}
|
||||
var widgetDictRef = pdfdoc.context.register(pdfdoc.context.obj(widgetObj));
|
||||
|
||||
// Add our signature widget to the page
|
||||
page.node.set(PDFLib.PDFName.of("Annots"), pdfdoc.context.obj([widgetDictRef]));
|
||||
|
||||
// Create an AcroForm object containing our signature widget
|
||||
pdfdoc.catalog.set(
|
||||
PDFLib.PDFName.of("AcroForm"),
|
||||
pdfdoc.context.obj({
|
||||
"SigFlags": 3,
|
||||
"Fields": [widgetDictRef],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {string} pdfstr
|
||||
* @return {Uint8Array}
|
||||
*/
|
||||
signPdf(pdfstr){
|
||||
if(!this.opt.signdate){
|
||||
this.opt.signdate = new Date();
|
||||
}
|
||||
|
||||
// Finds ByteRange information within a given PDF Buffer if one exists
|
||||
var byteRangeStrings = pdfstr.match(/\/ByteRange\s*\[{1}\s*(?:(?:\d*|\/\*{10})\s+){3}(?:\d+|\/\*{10}){1}\s*]{1}/g);
|
||||
var byteRangePlaceholder = byteRangeStrings.find(function(a_str){
|
||||
return a_str.includes("/**********");
|
||||
});
|
||||
if(!byteRangePlaceholder){
|
||||
throw new Error("no signature placeholder");
|
||||
}
|
||||
var byteRangePos = pdfstr.indexOf(byteRangePlaceholder);
|
||||
var byteRangeEnd = byteRangePos + byteRangePlaceholder.length;
|
||||
var contentsTagPos = pdfstr.indexOf('/Contents ', byteRangeEnd);
|
||||
var placeholderPos = pdfstr.indexOf('<', contentsTagPos);
|
||||
var placeholderEnd = pdfstr.indexOf('>', placeholderPos);
|
||||
var placeholderLengthWithBrackets = placeholderEnd + 1 - placeholderPos;
|
||||
var placeholderLength = placeholderLengthWithBrackets - 2;
|
||||
var byteRange = [0, 0, 0, 0];
|
||||
byteRange[1] = placeholderPos;
|
||||
byteRange[2] = byteRange[1] + placeholderLengthWithBrackets;
|
||||
byteRange[3] = pdfstr.length - byteRange[2];
|
||||
var actualByteRange = "/ByteRange [" + byteRange.join(" ") +"]";
|
||||
actualByteRange += ' '.repeat(byteRangePlaceholder.length - actualByteRange.length);
|
||||
// Replace the /ByteRange placeholder with the actual ByteRange
|
||||
pdfstr = pdfstr.slice(0, byteRangePos) + actualByteRange + pdfstr.slice(byteRangeEnd);
|
||||
// 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);
|
||||
|
||||
// Extract safe bags by type.
|
||||
// We will need all the certificates and the private key.
|
||||
var certBags = p12.getBags({
|
||||
"bagType": forge.pki.oids.certBag,
|
||||
})[forge.pki.oids.certBag];
|
||||
var keyBags = p12.getBags({
|
||||
"bagType": forge.pki.oids.pkcs8ShroudedKeyBag,
|
||||
})[forge.pki.oids.pkcs8ShroudedKeyBag];
|
||||
|
||||
var privateKey = keyBags[0].key;
|
||||
// Here comes the actual PKCS#7 signing.
|
||||
var p7 = forge.pkcs7.createSignedData();
|
||||
// Start off by setting the content.
|
||||
p7.content = forge.util.createBuffer(pdfstr);
|
||||
|
||||
// Then add all the certificates (-cacerts & -clcerts)
|
||||
// Keep track of the last found client certificate.
|
||||
// This will be the public key that will be bundled in the signature.
|
||||
var cert = null;
|
||||
Object.keys(certBags).forEach(function(a_ele){
|
||||
var a_cert = certBags[a_ele].cert;
|
||||
|
||||
p7.addCertificate(a_cert);
|
||||
|
||||
// Try to find the certificate that matches the private key.
|
||||
if(privateKey.n.compareTo(a_cert.publicKey.n) === 0
|
||||
&& privateKey.e.compareTo(a_cert.publicKey.e) === 0){
|
||||
cert = a_cert;
|
||||
}
|
||||
});
|
||||
if(!cert){
|
||||
throw new Error("Failed to find a certificate.");
|
||||
}
|
||||
|
||||
// Add a sha256 signer. That's what Adobe.PPKLite adbe.pkcs7.detached expects.
|
||||
p7.addSigner({
|
||||
key: privateKey,
|
||||
certificate: cert,
|
||||
digestAlgorithm: forge.pki.oids.sha256,
|
||||
authenticatedAttributes: [
|
||||
{
|
||||
"type": forge.pki.oids.contentType,
|
||||
"value": forge.pki.oids.data,
|
||||
}, {
|
||||
"type": forge.pki.oids.messageDigest,
|
||||
}, {
|
||||
"type": forge.pki.oids.signingTime,
|
||||
"value": this.opt.signdate,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Sign in detached mode.
|
||||
p7.sign({"detached": true});
|
||||
// Check if the PDF has a good enough placeholder to fit the signature.
|
||||
var sighex = forge.asn1.toDer(p7.toAsn1()).toHex();
|
||||
// placeholderLength represents the length of the HEXified symbols but we're
|
||||
// checking the actual lengths.
|
||||
if(sighex.length > placeholderLength){
|
||||
throw new Error("Signature is too big.");
|
||||
}else{
|
||||
// Pad the signature with zeroes so the it is the same length as the placeholder
|
||||
sighex += "0".repeat(placeholderLength - sighex.length);
|
||||
}
|
||||
// Place it in the document.
|
||||
pdfstr = pdfstr.slice(0, byteRange[1]) + "<" + sighex + ">" + pdfstr.slice(byteRange[1]);
|
||||
|
||||
return Zga.rawToU8arr(pdfstr);
|
||||
}
|
||||
},
|
||||
|
||||
VisualSignature: class {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {SignDrawInfo=} drawinf
|
||||
*/
|
||||
constructor(drawinf){
|
||||
/** @private @type {number} */
|
||||
this.pgidx = 0;
|
||||
/** @private @type {Array<number>} */
|
||||
this.rect = [0, 0, 0, 0];
|
||||
/** @private @type {SignDrawInfo} */
|
||||
this.drawinf = null;
|
||||
|
||||
if(drawinf){
|
||||
this.drawinf = drawinf;
|
||||
if(this.drawinf.pageidx){
|
||||
this.pgidx = this.drawinf.pageidx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @return {number}
|
||||
*/
|
||||
getPageIndex(){
|
||||
return this.pgidx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @return {Array<number>}
|
||||
*/
|
||||
getSignRect(){
|
||||
return this.rect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param {PDFLib.PDFDocument} pdfdoc
|
||||
* @param {string=} signame
|
||||
* @return {PDFLib.PDFRef} The unique reference assigned to the signature stream
|
||||
*/
|
||||
createStream(pdfdoc, signame){
|
||||
if(!this.drawinf){
|
||||
return null;
|
||||
}else if(!(this.drawinf.img || (this.drawinf.font && this.drawinf.font))){
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @type {Array<PDFLib.PDFPage>} */
|
||||
var pages = pdfdoc.getPages();
|
||||
/** @type {PDFLib.PDFPage} */
|
||||
var page = null;
|
||||
if(this.pgidx < pages.length){
|
||||
page = pages[this.pgidx];
|
||||
}else{
|
||||
throw new Error("Page index is overflow to pdf pages.");
|
||||
}
|
||||
var pgrot = page.getRotation();
|
||||
pgrot.angle = PDFLib.toDegrees(pgrot) % 360;
|
||||
pgrot.type = PDFLib.RotationTypes.Degrees;
|
||||
var pgsz = page.getSize();
|
||||
var areainf = this.calcAreaInf(pgsz, pgrot.angle, this.drawinf.area);
|
||||
|
||||
// resources object
|
||||
var rscObj = {};
|
||||
/** @type {Array<PDFLib.PDFOperator>} */
|
||||
var sigOprs = [];
|
||||
var imgName = signame ? signame.concat("Img") : "SigImg";
|
||||
var fontName = signame ? signame.concat("Font") : "SigFont";
|
||||
if(this.drawinf.img){
|
||||
// Get scaled image size
|
||||
var imgsz = this.drawinf.img.size();
|
||||
var tmp = areainf.w * imgsz.height / imgsz.width;
|
||||
if(tmp <= areainf.h){
|
||||
areainf.h = tmp;
|
||||
}else{
|
||||
areainf.w = areainf.h * imgsz.width / imgsz.height;
|
||||
}
|
||||
|
||||
rscObj["XObject"] = {
|
||||
[imgName]: this.drawinf.img.ref,
|
||||
};
|
||||
sigOprs = sigOprs.concat(PDFLib.drawImage(imgName, this.calcDrawImgInf(pgrot, areainf)));
|
||||
}
|
||||
if(this.drawinf.font){
|
||||
rscObj["Font"] = {
|
||||
[fontName]: this.drawinf.font.ref,
|
||||
};
|
||||
}
|
||||
|
||||
this.rect = this.calcRect(pgrot.angle, areainf);
|
||||
|
||||
var frmDict = pdfdoc.context.obj({
|
||||
"Type": "XObject",
|
||||
"Subtype": "Form",
|
||||
"FormType": 1,
|
||||
"BBox": [0, 0, areainf.w, areainf.h],
|
||||
"Resources": rscObj,
|
||||
});
|
||||
var strm = PDFLib.PDFContentStream.of(frmDict, sigOprs, false);
|
||||
return pdfdoc.context.register(strm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate area informations for drawing signature after rotate
|
||||
*
|
||||
* @private
|
||||
* @param {Object<string, number>} pgsz // { width, height }
|
||||
* @param {number} angle
|
||||
* @param {SignAreaInfo} visinf
|
||||
* @return {SignAreaInfo}
|
||||
*/
|
||||
calcAreaInf(pgsz, angle, visinf){
|
||||
var ret = Object.assign({}, visinf);
|
||||
// Calculate position after rotate
|
||||
switch(angle){
|
||||
case 90:
|
||||
ret.w = visinf.h;
|
||||
ret.h = visinf.w;
|
||||
ret.x = visinf.y;
|
||||
ret.y = visinf.x;
|
||||
break;
|
||||
case 180:
|
||||
case -180:
|
||||
ret.x = pgsz.width - visinf.x;
|
||||
break;
|
||||
case 270:
|
||||
case -90:
|
||||
ret.w = visinf.h;
|
||||
ret.h = visinf.w;
|
||||
ret.x = pgsz.width - visinf.y;
|
||||
ret.y = pgsz.height - visinf.x;
|
||||
break;
|
||||
default:
|
||||
ret.y = pgsz.height - visinf.y;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {number} angle
|
||||
* @param {SignAreaInfo} areainf // { x, y, w, h }
|
||||
* @return {Array<number>}
|
||||
*/
|
||||
calcRect(angle, areainf){
|
||||
var rect = [0, 0, 0, 0];
|
||||
rect[0] = areainf.x;
|
||||
rect[1] = areainf.y;
|
||||
switch(angle){
|
||||
case 90:
|
||||
rect[2] = areainf.x - areainf.h;
|
||||
rect[3] = areainf.y + areainf.w;
|
||||
break;
|
||||
case 180:
|
||||
case -180:
|
||||
rect[2] = areainf.x - areainf.w;
|
||||
rect[3] = areainf.y - areainf.h;
|
||||
break;
|
||||
case 270:
|
||||
case -90:
|
||||
rect[2] = areainf.x + areainf.h;
|
||||
rect[3] = areainf.y - areainf.w;
|
||||
break;
|
||||
default:
|
||||
rect[2] = areainf.x + areainf.w;
|
||||
rect[3] = areainf.y + areainf.h;
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate informations for drawing image after rotate
|
||||
*
|
||||
* @private
|
||||
* @param {PDFLib.Rotation} rot
|
||||
* @param {SignAreaInfo} areainf // { x, y, w, h }
|
||||
* @return {Object<string, *>} // { x, y, width, height, rotate, xSkew, ySkew }
|
||||
*/
|
||||
calcDrawImgInf(rot, areainf){
|
||||
var ret = {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": areainf.w,
|
||||
"height": areainf.h,
|
||||
"rotate": rot,
|
||||
"xSkew": PDFLib.degrees(0),
|
||||
"ySkew": PDFLib.degrees(0),
|
||||
};
|
||||
switch(rot.angle){
|
||||
case 90:
|
||||
ret["x"] = areainf.w;
|
||||
ret["width"] = areainf.h;
|
||||
ret["height"] = areainf.w;
|
||||
break;
|
||||
case 180:
|
||||
case -180:
|
||||
ret["x"] = areainf.w;
|
||||
ret["y"] = areainf.h;
|
||||
break;
|
||||
case 270:
|
||||
case -90:
|
||||
ret["y"] = areainf.h;
|
||||
ret["width"] = areainf.h;
|
||||
ret["height"] = areainf.w;
|
||||
break;
|
||||
}
|
||||
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