From fd2b97d5b367e964ea2779278fa55d4a4f2b940a Mon Sep 17 00:00:00 2001 From: zboris12 Date: Sun, 4 Aug 2024 20:07:21 +0900 Subject: [PATCH] Working on text signature. --- closure/pdflib-ext.js | 58 ++++++++++++++++++++++++++++++++ closure/zb-externs.js | 2 +- lib/zganode.js | 1 + lib/zgapdfsigner.js | 78 ++++++++++++++++++++++++++++++++++++++++--- package-lock.json | 17 ++++++++++ package.json | 7 ++-- 6 files changed, 154 insertions(+), 9 deletions(-) diff --git a/closure/pdflib-ext.js b/closure/pdflib-ext.js index 98fba94..269f880 100644 --- a/closure/pdflib-ext.js +++ b/closure/pdflib-ext.js @@ -60,6 +60,28 @@ PDFLib.PDFDocument.prototype.embedPng = function(png){}; * @returns {Promise} */ PDFLib.PDFDocument.prototype.embedJpg = function(jpg){}; + +/** + * @typedef + * {{ + * customName: (string|undefined), + * features: (Object|undefined), + * subset: (boolean|undefined), + * }} + */ +var EmbedFontOptions; +/** + * @param {ArrayBuffer|Uint8Array} font + * @param {EmbedFontOptions=} options + * @returns {Promise} + */ +PDFLib.PDFDocument.prototype.embedFont = function(font, options){}; +/** + * @param {string} font + * @param {string=} customName + * @returns {PDFLib.PDFFont} + */ +PDFLib.PDFDocument.prototype.embedStandardFont = function(font, customName){}; /** * @returns {Promise} */ @@ -359,6 +381,8 @@ PDFLib.PDFImage.prototype.ref; PDFLib.PDFFont = function(){}; /** @type {PDFLib.PDFRef} */ PDFLib.PDFFont.prototype.ref; +/** @type {string} */ +PDFLib.PDFFont.prototype.name; /** @constructor */ PDFLib.StandardFonts = function(){}; @@ -398,8 +422,42 @@ var PdfDrawimgOption; /** * @param {string} name * @param {PdfDrawimgOption} options + * @return {Array} */ 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} lines + * @param {DrawLinesOfTextOptions} options + * @return {Array} + */ +PDFLib.drawLinesOfText = function(lines, options){}; +/** + * @param {number} red + * @param {number} green + * @param {number} blue + * @return {PDFLib.Color} + */ +PDFLib.rgb = function(red, green, blue){}; /** * @constructor diff --git a/closure/zb-externs.js b/closure/zb-externs.js index 73df217..7c91203 100644 --- a/closure/zb-externs.js +++ b/closure/zb-externs.js @@ -26,7 +26,7 @@ var SignAreaInfo; * imgData: (Array|Uint8Array|ArrayBuffer|string|undefined), * imgType: (string|undefined), * text: (string|undefined), - * fontData: (PDFLib.StandardFonts|Array|Uint8Array|ArrayBuffer|string|undefined), + * fontData: (Array|Uint8Array|ArrayBuffer|string|undefined), * img: (PDFLib.PDFImage|undefined), * font: (PDFLib.PDFFont|undefined), * }} diff --git a/lib/zganode.js b/lib/zganode.js index 5d7de8c..d15355f 100644 --- a/lib/zganode.js +++ b/lib/zganode.js @@ -6,6 +6,7 @@ const m_h = { const z = require("./zgaindex.js"); z.forge = require("node-forge"); z.PDFLib = require("pdf-lib"); +z.fontkit = require("@pdf-lib/fontkit"); /** * @param {string} url * @param {UrlFetchParams} params diff --git a/lib/zgapdfsigner.js b/lib/zgapdfsigner.js index 7fe470b..bd0eafb 100644 --- a/lib/zgapdfsigner.js +++ b/lib/zgapdfsigner.js @@ -12,6 +12,9 @@ if(z.forge){ if(z.PDFLib){ var PDFLib = z.PDFLib; } +if(z.fontkit){ + var fontkit = z.fontkit; +} //Only for nodejs End// /** @type {Object} */ @@ -329,6 +332,22 @@ z.PdfSigner = class{ } } + if(_this.opt.drawinf && _this.opt.drawinf.text && _this.opt.drawinf.fontData && !_this.opt.drawinf.font){ + /** @type {Uint8Array|ArrayBuffer|string} */ + var fontData2 = null; + if(Array.isArray(_this.opt.drawinf.fontData)){ + fontData2 = new Uint8Array(_this.opt.drawinf.fontData); + }else{ + fontData2 = _this.opt.drawinf.fontData; + } + if(typeof fontData2 == "string"){ + _this.opt.drawinf.font = pdfdoc.embedStandardFont(fontData2); + }else{ + pdfdoc.registerFontkit(fontkit); + _this.opt.drawinf.font = await pdfdoc.embedFont(fontData2); + } + } + /** @type {forge_cert} */ var cert = _this.loadP12cert(_this.opt.p12cert, _this.opt.pwd); /** @type {Zga.CertsChain} */ @@ -362,6 +381,8 @@ z.PdfSigner = class{ var dmydoc = await _this.addDss(pdfdoc); if(dmydoc){ z.log("In order to enable LTV, DSS informations has been added to the pdf."); + }else{ + await pdfdoc.flush(); } // Clear ltv _this.opt.ltv = 0; @@ -1268,7 +1289,7 @@ z.SignatureCreator = class{ createStream(pdfdoc, signame){ if(!this.drawinf){ return null; - }else if(!(this.drawinf.img || (this.drawinf.font && this.drawinf.font))){ + }else if(!(this.drawinf.img || this.drawinf.text)){ return null; } @@ -1316,10 +1337,18 @@ z.SignatureCreator = class{ }; sigOprs = sigOprs.concat(PDFLib.drawImage(imgName, this.calcDrawImgInf(pgrot, areainf))); } - if(this.drawinf.font){ - rscObj["Font"] = { - [fontName]: this.drawinf.font.ref, - }; + if(this.drawinf.text){ + /** @type {PDFLib.PDFHexString|undefined} */ + var txt = undefined; + if(this.drawinf.font){ + rscObj["Font"] = { + [fontName]: this.drawinf.font.ref, + }; + txt = this.drawinf.font.encodeText(this.drawinf.text); + }else{ + txt = PDFLib.PDFHexString.fromText(this.drawinf.text); + } + sigOprs = sigOprs.concat(PDFLib.drawLinesOfText([txt], this.calcDrawTextInf(pgrot, areainf, this.drawinf.font ? fontName : "Courier8"))); } this.rect = this.calcRect(pgrot.angle, areainf); @@ -1445,6 +1474,45 @@ z.SignatureCreator = class{ } return ret; } + + /** + * Calculate informations for drawing text after rotate + * + * @private + * @param {PDFLib.Rotation} rot + * @param {SignAreaInfo} areainf // { x, y, w, h } + * @param {string=} font + * @return {DrawLinesOfTextOptions} + */ + calcDrawTextInf(rot, areainf, font){ + /** @type {DrawLinesOfTextOptions} */ + var ret = { + "x": 0, + "y": 10, + "color": PDFLib.rgb(0, 0, 0), + "font": font, + "lineHeight": 35, + "size": 35, + "rotate": rot, + "xSkew": PDFLib.degrees(0), + "ySkew": PDFLib.degrees(0), + }; + switch(rot.angle){ + case 90: + ret["x"] = areainf.w; + break; + case 180: + case -180: + ret["x"] = areainf.w; + ret["y"] = areainf.h; + break; + case 270: + case -90: + ret["y"] = areainf.h; + break; + } + return ret; + } }; } diff --git a/package-lock.json b/package-lock.json index 60f16ea..c9236bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "2.5.2", "license": "MIT", "dependencies": { + "@pdf-lib/fontkit": "^1.1.1", "follow-redirects": "1.15.6", "node-forge": "1.3.1", "pdf-lib": "1.17.1" @@ -17,6 +18,14 @@ "google-closure-compiler": "^20231112.0.0" } }, + "node_modules/@pdf-lib/fontkit": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@pdf-lib/fontkit/-/fontkit-1.1.1.tgz", + "integrity": "sha512-KjMd7grNapIWS/Dm0gvfHEilSyAmeLvrEGVcqLGi0VYebuqqzTbgF29efCx7tvx+IEbG3zQciRSWl3GkUSvjZg==", + "dependencies": { + "pako": "^1.0.6" + } + }, "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", @@ -380,6 +389,14 @@ } }, "dependencies": { + "@pdf-lib/fontkit": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@pdf-lib/fontkit/-/fontkit-1.1.1.tgz", + "integrity": "sha512-KjMd7grNapIWS/Dm0gvfHEilSyAmeLvrEGVcqLGi0VYebuqqzTbgF29efCx7tvx+IEbG3zQciRSWl3GkUSvjZg==", + "requires": { + "pako": "^1.0.6" + } + }, "@pdf-lib/standard-fonts": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", diff --git a/package.json b/package.json index d511c9e..68e4fcf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zgapdfsigner", - "version": "2.5.2", + "version": "2.6.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", @@ -33,9 +33,10 @@ "test": "node test4node.js" }, "dependencies": { + "@pdf-lib/fontkit": "^1.1.1", "follow-redirects": "1.15.6", - "pdf-lib": "1.17.1", - "node-forge": "1.3.1" + "node-forge": "1.3.1", + "pdf-lib": "1.17.1" }, "devDependencies": { "google-closure-compiler": "^20231112.0.0"