From 1f92ca61d7a67566326dac126d9fac0fc02cb1b6 Mon Sep 17 00:00:00 2001 From: zboris12 Date: Sat, 17 Aug 2024 11:51:46 +0900 Subject: [PATCH] Added support for typescript development. --- README.md | 9 +++ lib/zganode.d.ts | 111 ++++++++++++++++++++++++++ package.json | 5 +- test-ts/.vscode/settings.json | 5 ++ test-ts/package.json | 20 +++++ test-ts/src/index.ts | 144 ++++++++++++++++++++++++++++++++++ test-ts/tsconfig.json | 24 ++++++ test4node.js | 7 ++ 8 files changed, 323 insertions(+), 2 deletions(-) create mode 100644 lib/zganode.d.ts create mode 100644 test-ts/.vscode/settings.json create mode 100644 test-ts/package.json create mode 100644 test-ts/src/index.ts create mode 100644 test-ts/tsconfig.json diff --git a/README.md b/README.md index 4435fef..2a32093 100644 --- a/README.md +++ b/README.md @@ -88,9 +88,18 @@ pdfkit.loadZga(globalThis); ``` 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 diff --git a/lib/zganode.d.ts b/lib/zganode.d.ts new file mode 100644 index 0000000..e602ccb --- /dev/null +++ b/lib/zganode.d.ts @@ -0,0 +1,111 @@ +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; + ocsps?: Array; + crls?: Array; +}; +export type EncryptOption = { + mode: Crypto.Mode; + permissions?: Array; + userpwd?: string; + ownerpwd?: string; + pubkeys?: Array; +}; +export type PubKeyInfo = { + c?: Array | Uint8Array | ArrayBuffer | string | forge.pki.Certificate; + p?: Array; +}; +export type SignAreaInfo = { + x: number; + y: number; + w?: number; + h?: number; +}; +export type SignTextInfo = { + text: string, + fontData?: Array | Uint8Array | ArrayBuffer | string; + 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 | Uint8Array | ArrayBuffer | string; + imgType: string; + opacity?: number; + blendMode?: string; +}; +export type SignDrawInfo = { + area: SignAreaInfo; + pageidx?: number | string; + /** @deprecated use imgInfo instead */ + imgData?: Array | Uint8Array | ArrayBuffer | string; + /** @deprecated use imgInfo instead */ + imgType?: string; + imgInfo?: SignImageInfo; + textInfo?: SignTextInfo; +}; +export type SignOption = { + p12cert?: Array | 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; +}; + +export declare class CertsChain { + constructor(certs?: Array); + buildChain(cert: forge.pki.Certificate): Promise; + getAllCerts(): Array; + getSignCert(): forge.pki.Certificate; + isSelfSignedCert(): boolean; + prepareDSSInf(crlOnly?: boolean): Promise; +} +export declare class PdfCryptor { + constructor(encopt: EncryptOption); + encryptPdf(pdf: PDFLib.PDFDocument | Array | Uint8Array | ArrayBuffer | string, ref?: PDFLib.PDFRef): Promise; + encryptObject(num: number, val: PDFLib.PDFObject): void; +} +export declare class PdfSigner { + constructor(signopt: SignOption); + sign(pdf: PDFLib.PDFDocument | Array | Uint8Array | ArrayBuffer | string, cypopt?: EncryptOption): Promise; +} +export declare class TsaFetcher { + constructor(inf: TsaServiceInfo); + url: string; + len: number; + getCertsChain(): CertsChain; + getToken(forP7?: boolean): forge.asn1.Asn1; + queryTsa(data?: string): Promise; +} diff --git a/package.json b/package.json index 68e4fcf..54010aa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zgapdfsigner", - "version": "2.6.0", + "version": "2.7.0", "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", @@ -16,6 +16,7 @@ "main": "lib/zganode.js", "files": [ "dist/*.min.js", + "lib/*.d.ts", "lib/*.js" ], "keywords": [ @@ -30,7 +31,7 @@ ], "scripts": { "build": "./build.sh", - "test": "node test4node.js" + "test": "node test4node.js ${pfxpwd}" }, "dependencies": { "@pdf-lib/fontkit": "^1.1.1", diff --git a/test-ts/.vscode/settings.json b/test-ts/.vscode/settings.json new file mode 100644 index 0000000..9dbba42 --- /dev/null +++ b/test-ts/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "editor.tabSize": 2, + "editor.formatOnSave": true, + "files.eol": "\n" +} \ No newline at end of file diff --git a/test-ts/package.json b/test-ts/package.json new file mode 100644 index 0000000..5485ad7 --- /dev/null +++ b/test-ts/package.json @@ -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.0" + }, + "devDependencies": { + "@types/node-forge": "^1.3.11", + "typescript": "~4.9" + } +} \ No newline at end of file diff --git a/test-ts/src/index.ts b/test-ts/src/index.ts new file mode 100644 index 0000000..e8d059f --- /dev/null +++ b/test-ts/src/index.ts @@ -0,0 +1,144 @@ +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 { + 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 | 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) { + 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: 25, // 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 { + 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 { + 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 = 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", fontPath); + pdfPath = await sign_protect(pdfPath, pfxPath, ps, 2, undefined, "ありがとうご\r\n\nThis is an another test of text!\n", fontPath); + await addtsa(pdfPath); + } else { + await addtsa(pdfPath); + } + + console.log("Done"); +} + +async function main(): Promise { + let arr: Array = [0, 90, 180, 270]; + for (let i = 0; i < arr.length; i++) { + await main1(arr[i]); + // break; + } +} + +main(); diff --git a/test-ts/tsconfig.json b/test-ts/tsconfig.json new file mode 100644 index 0000000..cb0cc33 --- /dev/null +++ b/test-ts/tsconfig.json @@ -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" + ] +} \ No newline at end of file diff --git a/test4node.js b/test4node.js index 7e271bd..bb5fe8c 100644 --- a/test4node.js +++ b/test4node.js @@ -1,3 +1,10 @@ +// 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");