diff --git a/README.md b/README.md
index 2a32093..16c76a3 100644
--- a/README.md
+++ b/README.md
@@ -51,9 +51,10 @@ Just import the dependencies and this tool.
```
-When drawing text by non-standard font, importing the fontkit library is necessary.
+When drawing text for signature, importing fontkit and pako library is necessary.
```html
+
```
### [Google Apps Script](https://developers.google.com/apps-script)
@@ -68,8 +69,10 @@ function setTimeout(func, sleep){
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 by non-standard font.
+// It is necessary for drawing text for signature.
eval(UrlFetchApp.fetch("https://unpkg.com/@pdf-lib/fontkit/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
diff --git a/closure/pdflib-ext.js b/closure/pdflib-ext.js
index 4b95961..68b4cbc 100644
--- a/closure/pdflib-ext.js
+++ b/closure/pdflib-ext.js
@@ -9,7 +9,17 @@
var PdfLoadOptions;
/** @const */
-var fontkit = {};
+var pako = {};
+/**
+ * @param {Uint8Array} input
+ * @return {Uint8Array}
+ */
+pako.inflate = function(input){};
+
+/** @constructor */
+var Fontkit = function(){};
+/** @const {Fontkit} */
+var fontkit;
/** @const */
var PDFLib = {};
@@ -41,6 +51,11 @@ PDFLib.lineSplit = function(text){};
* @return {boolean}
*/
PDFLib.isNewlineChar = function(text){};
+/**
+ * @param {string} fntnm
+ * @return {boolean}
+ */
+PDFLib.isStandardFont = function(fntnm){};
/** @constructor */
PDFLib.PDFDocument = function(){};
@@ -105,7 +120,7 @@ PDFLib.PDFDocument.prototype.embedFont = function(font, options){};
*/
PDFLib.PDFDocument.prototype.embedStandardFont = function(font, customName){};
/**
- * @lends {fontkit} fkt
+ * @param {Fontkit} fkt
*/
PDFLib.PDFDocument.prototype.registerFontkit = function(fkt){};
/**
@@ -316,6 +331,8 @@ PDFLib.PDFName.Annots;
* @return {PDFLib.PDFName}
*/
PDFLib.PDFName.of = function(value){};
+/** @return {string} */
+PDFLib.PDFName.prototype.asString = function(){};
/** @type {string} */
PDFLib.PDFName.prototype.encodedName;
/** @type {number} */
@@ -338,10 +355,20 @@ PDFLib.PDFArray.prototype.push = function(object){};
* @return {PDFLib.PDFObject}
*/
PDFLib.PDFArray.prototype.get = function(idx){};
+/**
+ * @return {number}
+ */
+PDFLib.PDFArray.prototype.size = function(){};
/**
* @return {Array}
*/
PDFLib.PDFArray.prototype.asArray = function(){};
+/**
+ * @param {number} idx
+ * @param {*} typ
+ * @return {PDFLib.PDFObject}
+ */
+PDFLib.PDFArray.prototype.lookupMaybe = function(idx, typ){};
/**
* @constructor
@@ -403,8 +430,34 @@ 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} */
@@ -540,6 +593,12 @@ PDFLib.PDFContentStream.of = function(dict, operators, encode){};
* @extends {PDFLib.PDFStream}
*/
PDFLib.PDFRawStream = function(){};
+/** @type {PDFLib.PDFDict} */
+PDFLib.PDFRawStream.prototype.dict;
+/**
+ * @return {Uint8Array}
+ */
+PDFLib.PDFRawStream.prototype.getContents = function(){};
/**
* @constructor
diff --git a/lib/zgaindex.js b/lib/zgaindex.js
index 04703df..c7743fe 100644
--- a/lib/zgaindex.js
+++ b/lib/zgaindex.js
@@ -8,11 +8,11 @@ function genZga(){
const z = {};
/**
- * @param {string} msg
+ * @param {...string} msg
*/
- z.log = function(msg){
+ z.log = function(...msg){
if(z.debug){
- console.log(msg);
+ console.log(...msg);
}
};
diff --git a/lib/zganode.d.ts b/lib/zganode.d.ts
index e602ccb..8f0525e 100644
--- a/lib/zganode.d.ts
+++ b/lib/zganode.d.ts
@@ -37,7 +37,7 @@ export type SignAreaInfo = {
};
export type SignTextInfo = {
text: string,
- fontData?: Array | Uint8Array | ArrayBuffer | string;
+ fontData?: Array | Uint8Array | ArrayBuffer | PDFLib.StandardFonts;
color?: string;
opacity?: number;
blendMode?: string;
@@ -109,3 +109,8 @@ export declare class TsaFetcher {
getToken(forP7?: boolean): forge.asn1.Asn1;
queryTsa(data?: string): Promise;
}
+export declare class PdfFonts {
+ private constructor();
+ static from(pdfdoc: PDFLib.PDFDocument): Promise;
+ getEmbeddedFont(fontData?: Array | Uint8Array | ArrayBuffer | PDFLib.StandardFonts): Promise;
+}
diff --git a/lib/zganode.js b/lib/zganode.js
index d15355f..470b175 100644
--- a/lib/zganode.js
+++ b/lib/zganode.js
@@ -7,6 +7,7 @@ const z = require("./zgaindex.js");
z.forge = require("node-forge");
z.PDFLib = require("pdf-lib");
z.fontkit = require("@pdf-lib/fontkit");
+z.pako = require("pako");
/**
* @param {string} url
* @param {UrlFetchParams} params
diff --git a/lib/zgapdfsigner.js b/lib/zgapdfsigner.js
index 5c8386e..0b0e37d 100644
--- a/lib/zgapdfsigner.js
+++ b/lib/zgapdfsigner.js
@@ -15,6 +15,9 @@ if(z.PDFLib){
if(z.fontkit){
var fontkit = z.fontkit;
}
+if(z.pako){
+ var pako = z.pako;
+}
//Only for nodejs End//
/** @type {Object} */
@@ -243,6 +246,8 @@ z.PdfSigner = class{
this.oriU8pdf = null;
/** @private @type {Array} */
this.apobjs = null;
+ /** @private @type {z.PdfFonts} */
+ this.fonts = null;
if(typeof this.opt.debug == "boolean"){
z.debug = this.opt.debug;
@@ -341,21 +346,8 @@ z.PdfSigner = class{
}
if(_this.opt.drawinf && _this.opt.drawinf.textInfo && !_this.opt.drawinf.font){
- /** @type {Uint8Array|ArrayBuffer|string} */
- var fontData2 = null;
- if(Array.isArray(_this.opt.drawinf.textInfo.fontData)){
- fontData2 = new Uint8Array(_this.opt.drawinf.textInfo.fontData);
- }else if(_this.opt.drawinf.textInfo.fontData){
- fontData2 = _this.opt.drawinf.textInfo.fontData;
- }else{
- fontData2 = "Helvetica";
- }
- if(typeof fontData2 == "string"){
- _this.opt.drawinf.font = pdfdoc.embedStandardFont(fontData2);
- }else{
- pdfdoc.registerFontkit(fontkit);
- _this.opt.drawinf.font = await pdfdoc.embedFont(fontData2);
- }
+ _this.fonts = await z.PdfFonts.from(pdfdoc);
+ _this.opt.drawinf.font = await _this.fonts.getEmbeddedFont(_this.opt.drawinf.textInfo.fontData);
}
/** @type {forge_cert} */
@@ -1839,6 +1831,186 @@ z.SignatureCreator = class{
}
};
+z.PdfFonts = class{
+ /**
+ * @private
+ * @param {PDFLib.PDFDocument} pdfdoc
+ * @param {Array} fonts
+ */
+ constructor(pdfdoc, fonts){
+ /** @private @type {PDFLib.PDFDocument} */
+ this.doc = pdfdoc;
+ /** @private @type {Array} */
+ this.fonts = fonts;
+ }
+
+ /**
+ * @public
+ * @param {PDFLib.PDFDocument} pdfdoc
+ * @return {Promise}
+ */
+ static async from(pdfdoc){
+ /**
+ * @param {PDFLib.PDFDict} dict
+ * @param {string} nm
+ * @return {string|undefined}
+ */
+ var lookupName = function(dict, nm){
+ var pnm = /** @type {PDFLib.PDFName} */(dict.lookupMaybe(PDFLib.PDFName.of(nm), PDFLib.PDFName));
+ if(pnm){
+ return pnm.asString();
+ }else{
+ return undefined;
+ }
+ };
+
+ /** @type Array */
+ var fonts = [];
+ /** @type {Array} */
+ var objs = pdfdoc.context.enumerateIndirectObjects();
+ /** @type {number} */
+ var i = 0;
+ while(i < objs.length){
+ /** @type {PdfObjEntry} */
+ var poe = objs[i];
+ i++;
+
+ if(poe[1] instanceof PDFLib.PDFDict){
+ /** @type {string|undefined} */
+ var typ = lookupName(poe[1], "Type");
+ if(typ !== "/Font"){
+ continue;
+ }
+ /** @type {string|undefined} */
+ var fntnm = lookupName(poe[1], "BaseFont");
+ if(fntnm){
+ fntnm = fntnm.substring(1);
+ if(PDFLib.isStandardFont(fntnm)){
+ fonts.push({
+ font: PDFLib.PDFFont.of(poe[0], pdfdoc, PDFLib.StandardFontEmbedder.for(fntnm)),
+ });
+ continue;
+ }
+ }else{
+ continue;
+ }
+
+ var dfnts = /** @type {PDFLib.PDFArray} */(poe[1].lookupMaybe(PDFLib.PDFName.of("DescendantFonts"), PDFLib.PDFArray));
+ if(dfnts && dfnts.size()){
+ var fntdict = /** @type {PDFLib.PDFDict} */(dfnts.lookupMaybe(0, PDFLib.PDFDict));
+ if(fntdict){
+ var fntdesc = /** @type {PDFLib.PDFDict} */(fntdict.lookupMaybe(PDFLib.PDFName.of("FontDescriptor"), PDFLib.PDFDict));
+ if(fntdesc){
+ var rstm = /** @type {PDFLib.PDFRawStream} */(fntdesc.lookupMaybe(PDFLib.PDFName.of("FontFile2"), PDFLib.PDFRawStream));
+ if(rstm){
+ /** @type {Uint8Array} */
+ var fdat = rstm.getContents();
+ /** @type {string|undefined} */
+ var fltr = lookupName(rstm.dict, "Filter");
+ if(fltr == "/FlateDecode"){
+ fdat = pako.inflate(fdat);
+ }
+ /** @type {PDFLib.CustomFontEmbedder} */
+ var emdr = await PDFLib.CustomFontEmbedder.for(fontkit, fdat);
+ fonts.push({
+ font: PDFLib.PDFFont.of(poe[0], pdfdoc, emdr),
+ data: fdat,
+ });
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return new z.PdfFonts(pdfdoc, fonts);
+ }
+
+ /**
+ * @public
+ * @param {Array|Uint8Array|ArrayBuffer|string|undefined} fontData
+ * @return {Promise}
+ */
+ async getEmbeddedFont(fontData){
+ if(!fontData){
+ if(this.fonts.length){
+ z.log("Use existing default font.", this.fonts[0].font.name);
+ return this.fonts[0].font;
+ }else{
+ fontData = "Helvetica";
+ z.log("Use default font.", fontData);
+ }
+ }
+ if(typeof fontData == "string"){
+ return this.getStandardFont(fontData);
+ }else{
+ /** @type {Uint8Array} */
+ var u8dat = (fontData instanceof Uint8Array) ? fontData : new Uint8Array(fontData);
+ return await this.getCustomFont(u8dat);
+ }
+ }
+
+ /**
+ * @private
+ * @param {string} fontData
+ * @return {PDFLib.PDFFont}
+ */
+ getStandardFont(fontData){
+ /** @type {number} */
+ var i = 0;
+ while(i < this.fonts.length){
+ /** @type {FontInfo} */
+ var fi = this.fonts[i];
+ i++;
+ if(!fi.data && fi.font.name == fontData){
+ z.log("Existing font found.", fi.font.name);
+ return fi.font;
+ }
+ }
+ return this.doc.embedStandardFont(fontData);
+ }
+ /**
+ * @private
+ * @param {Uint8Array} fontData
+ * @return {Promise}
+ */
+ async getCustomFont(fontData){
+ /** @type {number} */
+ var i = 0;
+ while(i < this.fonts.length){
+ /** @type {FontInfo} */
+ var fi = this.fonts[i];
+ i++;
+ if(fi.data && this.isSameData(fi.data, fontData)){
+ z.log("Existing font found.", fi.font.name);
+ return fi.font;
+ }
+ }
+ this.doc.registerFontkit(fontkit);
+ return await this.doc.embedFont(fontData);
+ }
+ /**
+ * @private
+ * @param {Uint8Array} dat1
+ * @param {Uint8Array} dat2
+ * @return {boolean}
+ */
+ isSameData(dat1, dat2){
+ if(dat1.length != dat2.length){
+ return false;
+ }
+ /** @type {number} */
+ var i = 0;
+ while(i < dat1.length){
+ if(dat1[i] != dat2[i]){
+ return false;
+ }
+ i++;
+ }
+ return true;
+ }
+};
+
/**
* @typedef
* {{
@@ -1848,6 +2020,14 @@ z.SignatureCreator = class{
* }}
*/
var SplitLongWordResult;
+/**
+ * @typedef
+ * {{
+ * font: PDFLib.PDFFont,
+ * data: (Uint8Array|undefined),
+ * }}
+ */
+var FontInfo;
}
diff --git a/package.json b/package.json
index 54010aa..c68fc17 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "zgapdfsigner",
- "version": "2.7.0",
+ "version": "2.7.1",
"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",
diff --git a/test-ts/package.json b/test-ts/package.json
index 5485ad7..fdfe375 100644
--- a/test-ts/package.json
+++ b/test-ts/package.json
@@ -11,7 +11,7 @@
"test": "node test/index.js ${pfxpwd}"
},
"dependencies": {
- "zgapdfsigner": "^2.7.0"
+ "zgapdfsigner": "^2.7.1"
},
"devDependencies": {
"@types/node-forge": "^1.3.11",
diff --git a/test-ts/src/index.ts b/test-ts/src/index.ts
index e8d059f..d92979c 100644
--- a/test-ts/src/index.ts
+++ b/test-ts/src/index.ts
@@ -9,7 +9,7 @@ async function sign_protect(pdfPath: string, pfxPath: string, ps: string, perm:
let pfx: Buffer = m_fs.readFileSync(pfxPath);
let img: Buffer | undefined = undefined;
let imgType: string = "";
- let font: Buffer | undefined = undefined;
+ let font: Buffer | Zga.PDFLib.StandardFonts | undefined = undefined;
if (perm == 1) {
console.log("\nTest signing pdf with full protection. (permission 1 and password encryption)");
@@ -22,7 +22,11 @@ async function sign_protect(pdfPath: string, pfxPath: string, ps: string, perm:
imgType = m_path.extname(imgPath).slice(1);
}
if (fontPath) {
- font = m_fs.readFileSync(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,
@@ -38,7 +42,7 @@ async function sign_protect(pdfPath: string, pfxPath: string, ps: string, perm:
if (img || txt) {
sopt.drawinf = {
area: {
- x: 25, // left
+ x: perm ? 25 : 200, // left
y: 50, // top
w: txt ? undefined : 60,
h: txt ? undefined : 100,
@@ -108,7 +112,7 @@ async function main1(angle: number): Promise {
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");
+ let fontPath: string = angle ? Zga.PDFLib.StandardFonts.TimesRomanBold : m_path.join(__dirname, workpath + "_test.ttf");
if (process.argv.length > 3) {
pfxPath = process.argv[2];
@@ -123,8 +127,9 @@ async function main1(angle: number): Promise {
}
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 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);
diff --git a/test.html b/test.html
index b3335e4..237933a 100644
--- a/test.html
+++ b/test.html
@@ -6,6 +6,7 @@
Test for ZgaPdfSigner
+