2022-11-14 14:20:19 +01:00
'use strict' ;
2022-10-02 14:45:06 +02:00
/ * *
* @ param { Object < string , * > } z
* /
function supplyZgaSigner ( z ) {
2022-09-18 09:49:56 +02:00
2022-11-06 10:03:48 +01:00
//Only for nodejs Start//
if ( z . forge ) {
var forge = z . forge ;
}
if ( z . PDFLib ) {
var PDFLib = z . PDFLib ;
}
2024-08-04 13:07:21 +02:00
if ( z . fontkit ) {
var fontkit = z . fontkit ;
}
2022-11-06 10:03:48 +01:00
//Only for nodejs End//
2022-09-24 06:59:30 +02:00
/** @type {Object<string, TsaServiceInfo>} */
2022-10-02 14:45:06 +02:00
z . TSAURLS = {
2022-11-06 10:03:48 +01:00
"1" : { url : "http://ts.ssl.com" , len : 12100 } ,
"2" : { url : "http://timestamp.digicert.com" , len : 11900 } ,
"3" : { url : "http://timestamp.sectigo.com" , len : 9900 } ,
"4" : { url : "http://timestamp.entrust.net/TSS/RFC3161sha2TS" , len : 10850 } ,
"5" : { url : "http://timestamp.apple.com/ts01" , len : 8600 } ,
"6" : { url : "http://www.langedge.jp/tsa" , len : 5700 } ,
"7" : { url : "https://freetsa.org/tsr" , len : 11000 } ,
2022-10-02 14:45:06 +02:00
} ;
2022-10-14 14:12:43 +02:00
z . NewRef = class {
/ * *
* @ param { PDFLib . PDFRef } ref
* @ param { number = } num
* @ param { string = } nm
* /
constructor ( ref , num , nm ) {
/** @private @type {number} */
this . oriNumber = ref . objectNumber ;
/** @private @type {number} */
this . oriGeneration = ref . generationNumber ;
/** @private @type {string} */
this . name = nm ? nm : "" ;
/** @private @type {number} */
this . newNumber = num ? num : 0 ;
}
/ * *
* @ public
* @ param { number } num
* /
setNewNumber ( num ) {
this . newNumber = num ;
}
/ * *
* @ public
* @ param { boolean = } restore
* /
changeNumber ( restore ) {
if ( ! this . newNumber ) {
if ( restore ) {
return ;
} else {
throw new Error ( "Can NOT change number since new number is not set." ) ;
}
}
/** @type {PDFLib.PDFRef} */
var ref = PDFLib . PDFRef . of ( this . oriNumber , this . oriGeneration ) ;
ref . objectNumber = restore ? this . oriNumber : this . newNumber ;
ref . tag = ref . objectNumber + " " + this . oriGeneration + " R" ;
}
/ * *
* @ public
* @ return { string }
* /
toString ( ) {
return this . name + " -> old:" + this . oriNumber + ", new:" + this . newNumber ;
}
} ;
z . NewRefMap = class extends Map {
constructor ( ) {
super ( ) ;
/** @private @type {number} */
this . idx = 0 ;
/** @private @type {PDFLib.PDFContext} */
this . pdfcont = null ;
2022-10-20 14:28:27 +02:00
/** @private @type {number} */
this . oriLastOnum = 0 ;
2022-10-14 14:12:43 +02:00
}
/ * *
* @ public
* @ param { PDFLib . PDFDocument } pdfdoc
* @ param { boolean = } enc
* @ return { PDFLib . PDFRef } If enc is true , the return value is the unique reference reserved for encrypting information .
* /
reorderPdfRefs ( pdfdoc , enc ) {
2022-10-20 14:28:27 +02:00
/** @type {z.NewRefMap} */
const _this = this ;
_this . pdfcont = pdfdoc . context ;
2022-10-14 14:12:43 +02:00
/** @type {PDFLib.PDFRef} */
2022-10-20 14:28:27 +02:00
var encref = enc ? _this . pdfcont . nextRef ( ) : null ;
2022-10-14 14:12:43 +02:00
pdfdoc . getPages ( ) . forEach ( function ( /** @type {PDFLib.PDFPage} */ a _pg ) {
2022-10-20 14:28:27 +02:00
_this . addAndFindRelates ( a _pg . ref , "Page" ) ;
} ) ;
_this . addAndFindRelates ( _this . pdfcont . trailerInfo . Root , "Catalog" ) ;
2022-10-14 14:12:43 +02:00
if ( encref ) {
2022-10-20 14:28:27 +02:00
_this . addAndFindRelates ( encref , "Encrypt" ) ;
2022-10-14 14:12:43 +02:00
}
2022-10-20 14:28:27 +02:00
_this . pdfcont . enumerateIndirectObjects ( ) . forEach ( function ( /** @type {PdfObjEntry} */ a _oety ) {
2022-10-14 14:12:43 +02:00
/** @type {string} */
var a _tag = a _oety [ 0 ] . tag ;
/** @type {z.NewRef} */
2022-10-20 14:28:27 +02:00
var a _new = _this . get ( a _tag ) ;
2022-10-14 14:12:43 +02:00
if ( ! a _new ) {
2022-10-20 14:28:27 +02:00
a _new = new z . NewRef ( a _oety [ 0 ] , ++ _this . idx ) ;
_this . set ( a _tag , a _new ) ;
2022-10-14 14:12:43 +02:00
}
2022-10-20 14:28:27 +02:00
} ) ;
_this . changeAll ( ) ;
_this . oriLastOnum = _this . pdfcont . largestObjectNumber ;
_this . pdfcont . largestObjectNumber = _this . idx ;
2022-10-14 14:12:43 +02:00
return encref ;
}
/ * *
* @ public
* /
restoreAll ( ) {
2022-10-15 15:06:13 +02:00
this . changeAll ( true ) ;
2022-10-20 14:28:27 +02:00
this . pdfcont . largestObjectNumber = this . oriLastOnum ;
2022-10-14 14:12:43 +02:00
this . clear ( ) ;
this . idx = 0 ;
2022-10-20 14:28:27 +02:00
this . oriLastOnum = 0 ;
2022-10-14 14:12:43 +02:00
this . pdfcont = null ;
}
/ * *
* @ private
* @ param { PDFLib . PDFRef } a _ref
* @ param { string = } a _nm
* /
addAndFindRelates ( a _ref , a _nm ) {
if ( ! this . get ( a _ref . tag ) ) {
this . set ( a _ref . tag , new z . NewRef ( a _ref , ++ this . idx , a _nm ) ) ;
this . findRefs ( this . pdfcont . lookup ( a _ref ) , a _nm ) ;
}
}
/ * *
* @ private
2022-10-20 14:28:27 +02:00
* @ param { PDFLib . PDFObject | Array < PDFLib . PDFObject > | Map } a _val
2022-10-14 14:12:43 +02:00
* @ param { string = } a _nm
* /
findRefs ( a _val , a _nm ) {
if ( ! a _val || a _nm == "/Parent" ) {
return ;
}
if ( a _val instanceof PDFLib . PDFRef ) {
this . addAndFindRelates ( a _val , a _nm ) ;
return ;
}
if ( a _val . array ) {
a _val = a _val . array ;
}
if ( Array . isArray ( a _val ) ) {
a _val . forEach ( function ( /** @type {PDFLib.PDFObject} */ b _val ) {
this . findRefs ( b _val , a _nm ) ;
} . bind ( this ) ) ;
return ;
}
if ( a _val instanceof PDFLib . PDFPage ) {
a _val = a _val . node ;
}
while ( a _val . dict && ! ( a _val instanceof Map ) ) {
a _val = a _val . dict ;
}
if ( a _val instanceof Map ) {
/** @type {Iterator} */
var a _es = a _val . entries ( ) ;
/** @type {IIterableResult<PdfObjEntry>} */
var a _result = a _es . next ( ) ;
while ( ! a _result . done ) {
this . findRefs ( a _result . value [ 1 ] , a _result . value [ 0 ] . encodedName ) ;
a _result = a _es . next ( ) ;
}
return ;
}
}
2022-10-15 15:06:13 +02:00
/ * *
* @ private
* @ param { boolean = } restore
* /
changeAll ( restore ) {
/** @type {Iterator} */
var es = this . entries ( ) ;
/** @type {IIterableResult} */
var result = es . next ( ) ;
while ( ! result . done ) {
result . value [ 1 ] . changeNumber ( restore ) ;
result = es . next ( ) ;
}
}
2022-10-14 14:12:43 +02:00
} ;
/** @type {z.NewRefMap<string, z.NewRef>} */
z . newRefs = new z . NewRefMap ( ) ;
2022-10-02 14:45:06 +02:00
z . PdfSigner = class {
2022-09-17 14:55:09 +02:00
/ * *
* @ param { SignOption } signopt
* /
constructor ( signopt ) {
2022-11-06 10:03:48 +01:00
/** @public @type {Zga.TsaFetcher} */
this . tsaFetcher = null ;
2022-11-14 14:20:19 +01:00
/** @public @type {Zga.PdfCryptor} */
this . cyptr = null ;
2022-11-06 10:03:48 +01:00
2022-10-02 14:45:06 +02:00
/** @private @const {string} */
this . DEFAULT _BYTE _RANGE _PLACEHOLDER = "**********" ;
2022-10-20 14:28:27 +02:00
/** @private @const {number} */
this . NEWLINE = 10 ;
2022-09-17 14:55:09 +02:00
/** @private @type {SignOption} */
this . opt = signopt ;
2022-10-06 15:02:11 +02:00
/** @type {forge_key} */
this . privateKey = null ;
2022-11-06 10:03:48 +01:00
/** @type {Zga.CertsChain} */
this . cchain = null ;
/** @private @type {string} */
this . signature = "" ;
2022-10-02 14:45:06 +02:00
/** @private @type {number} */
this . siglen = 0 ;
/** @private @type {PDFLib.PDFHexString} */
this . sigContents = null ;
2022-10-20 14:28:27 +02:00
/** @private @type {Uint8Array} */
this . oriU8pdf = null ;
2022-11-06 10:03:48 +01:00
/** @private @type {Array<PdfObjEntry>} */
this . apobjs = null ;
2022-09-24 06:59:30 +02:00
2022-11-06 10:03:48 +01:00
if ( typeof this . opt . debug == "boolean" ) {
z . debug = this . opt . debug ;
} else if ( globalThis . debug ) {
z . debug = true ;
}
if ( ! ( globalThis . PDFLib || PDFLib ) ) {
2022-09-24 06:59:30 +02:00
throw new Error ( "pdf-lib is not imported." ) ;
}
2022-11-06 10:03:48 +01:00
if ( ! ( globalThis . forge || forge ) ) {
2022-09-24 06:59:30 +02:00
throw new Error ( "node-forge is not imported." ) ;
}
2022-11-14 14:20:19 +01:00
/** @type {?TsaServiceInfo} */
var tsainf = null ;
2022-09-24 06:59:30 +02:00
if ( signopt . signdate ) {
if ( typeof signopt . signdate == "string" ) {
2022-11-14 14:20:19 +01:00
tsainf = {
2022-09-24 06:59:30 +02:00
url : signopt . signdate ,
} ;
} else if ( signopt . signdate . url ) {
2022-11-14 14:20:19 +01:00
tsainf = /** @type {TsaServiceInfo} */ ( Object . assign ( { } , signopt . signdate ) ) ;
2022-09-24 06:59:30 +02:00
}
}
2022-11-14 14:20:19 +01:00
if ( tsainf ) {
2022-10-24 14:36:47 +02:00
if ( ! z . urlFetch ) {
2022-09-24 06:59:30 +02:00
throw new Error ( "Because of the CORS security restrictions, signing with TSA is not supported in web browser." ) ;
}
2022-11-14 14:20:19 +01:00
if ( z . TSAURLS [ tsainf . url ] ) {
Object . assign ( tsainf , z . TSAURLS [ tsainf . url ] ) ;
} else if ( ! ( new RegExp ( "^https?://" ) ) . test ( tsainf . url ) ) {
throw new Error ( "Unknown tsa data. " + JSON . stringify ( tsainf ) ) ;
2022-09-24 06:59:30 +02:00
}
2022-11-14 14:20:19 +01:00
if ( ! tsainf . len ) {
tsainf . len = 16000 ;
2022-09-24 06:59:30 +02:00
}
2022-11-14 14:20:19 +01:00
this . tsaFetcher = new z . TsaFetcher ( /** @type {TsaServiceInfo} */ ( tsainf ) ) ;
2022-09-24 06:59:30 +02:00
}
2022-11-06 10:03:48 +01:00
if ( signopt . ltv && ! z . urlFetch ) {
throw new Error ( "Because of the CORS security restrictions, signing with LTV is not supported in web browser." ) ;
}
2022-11-14 14:20:19 +01:00
// if(signopt.permission == 1 && signopt.ltv){
// z.log("To enable LTV we need to append informations after signing, this will destroy the signature if full DocMDP protection is set. (Sign with permission = 1)");
// throw new Error("When set full DocMDP protection, LTV can't be enabled.");
// }
2022-09-17 14:55:09 +02:00
}
/ * *
* @ public
2022-09-24 06:59:30 +02:00
* @ param { PDFLib . PDFDocument | Array < number > | Uint8Array | ArrayBuffer | string } pdf
2022-10-02 14:45:06 +02:00
* @ param { EncryptOption = } cypopt
2022-09-18 09:49:56 +02:00
* @ return { Promise < Uint8Array > }
2022-09-17 14:55:09 +02:00
* /
2022-10-02 14:45:06 +02:00
async sign ( pdf , cypopt ) {
if ( cypopt && ! z . PdfCryptor ) {
throw new Error ( "ZgaPdfCryptor is not imported." ) ;
2022-09-17 14:55:09 +02:00
}
2022-10-20 14:28:27 +02:00
/** @const {z.PdfSigner} */
const _this = this ;
2022-10-02 14:45:06 +02:00
/** @type {PDFLib.PDFDocument} */
2022-10-20 14:28:27 +02:00
var pdfdoc = null ;
if ( pdf . addPage ) {
pdfdoc = /** @type {PDFLib.PDFDocument} */ ( pdf ) ;
} else {
if ( Array . isArray ( pdf ) ) {
_this . oriU8pdf = new Uint8Array ( pdf ) ;
} else {
2022-11-14 14:20:19 +01:00
_this . oriU8pdf = PDFLib . toUint8Array ( /** @type {ArrayBuffer|Uint8Array|string} */ ( pdf ) ) ;
2022-10-20 14:28:27 +02:00
}
pdfdoc = await PDFLib . PDFDocument . load ( _this . oriU8pdf ) ;
}
2022-10-02 14:45:06 +02:00
2024-08-09 14:57:20 +02:00
// For backward compatibility
if ( _this . opt . drawinf && _this . opt . drawinf . imgData && ! _this . opt . drawinf . imgInfo ) {
_this . opt . drawinf . imgInfo = {
imgData : _this . opt . drawinf . imgData ,
imgType : _this . opt . drawinf . imgType ,
} ;
}
if ( _this . opt . drawinf && _this . opt . drawinf . imgInfo && ! _this . opt . drawinf . img ) {
2022-09-24 06:59:30 +02:00
/** @type {Uint8Array|ArrayBuffer|string} */
var imgData2 = null ;
2024-08-09 14:57:20 +02:00
if ( Array . isArray ( _this . opt . drawinf . imgInfo . imgData ) ) {
imgData2 = new Uint8Array ( _this . opt . drawinf . imgInfo . imgData ) ;
2022-09-24 06:59:30 +02:00
} else {
2024-08-09 14:57:20 +02:00
imgData2 = _this . opt . drawinf . imgInfo . imgData ;
2022-09-24 06:59:30 +02:00
}
2024-08-09 14:57:20 +02:00
if ( _this . opt . drawinf . imgInfo . imgType == "png" ) {
2022-10-20 14:28:27 +02:00
_this . opt . drawinf . img = await pdfdoc . embedPng ( imgData2 ) ;
2024-08-09 14:57:20 +02:00
} else if ( _this . opt . drawinf . imgInfo . imgType == "jpg" ) {
2022-10-20 14:28:27 +02:00
_this . opt . drawinf . img = await pdfdoc . embedJpg ( imgData2 ) ;
2022-09-17 14:55:09 +02:00
} else {
2024-08-09 14:57:20 +02:00
throw new Error ( "Unkown image type. " + _this . opt . drawinf . imgInfo . imgType ) ;
2022-09-17 14:55:09 +02:00
}
}
2024-08-09 14:57:20 +02:00
if ( _this . opt . drawinf && _this . opt . drawinf . textInfo && ! _this . opt . drawinf . font ) {
2024-08-04 13:07:21 +02:00
/** @type {Uint8Array|ArrayBuffer|string} */
var fontData2 = null ;
2024-08-09 14:57:20 +02:00
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 ;
2024-08-04 13:07:21 +02:00
} else {
2024-08-09 14:57:20 +02:00
fontData2 = "Helvetica" ;
2024-08-04 13:07:21 +02:00
}
if ( typeof fontData2 == "string" ) {
_this . opt . drawinf . font = pdfdoc . embedStandardFont ( fontData2 ) ;
} else {
pdfdoc . registerFontkit ( fontkit ) ;
_this . opt . drawinf . font = await pdfdoc . embedFont ( fontData2 ) ;
}
}
2022-10-06 15:02:11 +02:00
/** @type {forge_cert} */
2022-10-20 14:28:27 +02:00
var cert = _this . loadP12cert ( _this . opt . p12cert , _this . opt . pwd ) ;
2022-11-06 10:03:48 +01:00
/** @type {Zga.CertsChain} */
var cchain = null ;
2022-10-06 15:02:11 +02:00
if ( cert ) {
2022-11-06 10:03:48 +01:00
if ( z . urlFetch ) {
cchain = new z . CertsChain ( ) ;
/** @type {?boolean} */
var rootok = await cchain . buildChain ( cert ) ;
if ( rootok ) {
_this . cchain = cchain ;
}
}
2022-10-06 15:02:11 +02:00
z . fixCertAttributes ( cert ) ;
2022-11-14 14:20:19 +01:00
} else if ( _this . tsaFetcher ) {
2022-11-06 10:03:48 +01:00
z . log ( "No certificate is specified, so only add a document timestamp." )
2022-10-24 14:36:47 +02:00
} else {
throw new Error ( "Nothing to do because no certificate nor tsa is specified." ) ;
2022-10-06 15:02:11 +02:00
}
2022-10-24 14:36:47 +02:00
/** @type {boolean} */ //append mode or not
var apmode = _this . addSignHolder ( pdfdoc ) ;
2022-11-06 10:03:48 +01:00
z . log ( "A signature holder has been added to the pdf." ) ;
2022-10-24 14:36:47 +02:00
2022-11-14 14:20:19 +01:00
if ( _this . opt . permission == 1 && ( _this . opt . ltv == 1 || _this . opt . ltv == 2 ) ) {
if ( ! _this . cchain ) {
// Query a timestamp from tsa with dummy string to obtain the certificates.
await _this . queryTsa ( "dummy" ) ;
}
/** @type {PDFLib.PDFDocument} */
var dmydoc = await _this . addDss ( pdfdoc ) ;
if ( dmydoc ) {
z . log ( "In order to enable LTV, DSS informations has been added to the pdf." ) ;
2024-08-04 13:07:21 +02:00
} else {
await pdfdoc . flush ( ) ;
2022-11-14 14:20:19 +01:00
}
// Clear ltv
_this . opt . ltv = 0 ;
} else {
await pdfdoc . flush ( ) ;
}
2022-10-20 14:28:27 +02:00
if ( apmode ) {
if ( _this . oriU8pdf ) {
2022-11-06 10:03:48 +01:00
z . log ( "The pdf has been signed already, so we add a new signature to it." ) ;
2022-10-20 14:28:27 +02:00
} else {
throw new Error ( "When adding a new signature to a signed pdf, the original literal datas are necessary." ) ;
}
// Find the changed objects
2022-11-06 10:03:48 +01:00
await _this . findChangedObjects ( pdfdoc ) ;
2022-10-20 14:28:27 +02:00
} else {
// If the definitions of references are too chaotic, a signature contains DocMDP or after adding a new signature,
// this signature may be invalid. So we make the order of references more neet.
/** @type {PDFLib.PDFRef} */
var encref = z . newRefs . reorderPdfRefs ( pdfdoc , cypopt ? true : false ) ;
if ( cypopt ) {
if ( cypopt . pubkeys ) {
if ( cypopt . pubkeys . length == 0 ) {
cypopt . pubkeys . push ( {
c : cert ,
} ) ;
} else {
cypopt . pubkeys . forEach ( function ( /** @type {PubKeyInfo} */ a _pubkey ) {
// If there is no c in the PubKeyInfo, set cert to it.
if ( ! a _pubkey . c ) {
a _pubkey . c = cert ;
}
} ) ;
}
2022-10-06 15:02:11 +02:00
}
2022-10-20 14:28:27 +02:00
/** @type {Zga.PdfCryptor} */
2022-11-14 14:20:19 +01:00
_this . cyptr = new z . PdfCryptor ( cypopt ) ;
await _this . cyptr . encryptPdf ( pdfdoc , encref ) ;
2022-11-06 10:03:48 +01:00
z . log ( "Pdf data has been encrypted." ) ;
2022-10-06 15:02:11 +02:00
}
2022-10-02 14:45:06 +02:00
}
2022-09-24 06:59:30 +02:00
/** @type {Uint8Array} */
2022-10-20 14:28:27 +02:00
var ret = await _this . saveAndSign ( pdfdoc ) ;
2022-10-02 14:45:06 +02:00
if ( ! ret ) {
2022-11-06 10:03:48 +01:00
z . log ( "Change size of signature's placeholder and retry." ) ;
_this . sigContents . value = "0" . repeat ( _this . siglen + 10 ) ;
2022-10-20 14:28:27 +02:00
ret = await _this . saveAndSign ( pdfdoc ) ;
2022-10-02 14:45:06 +02:00
}
2022-10-14 14:12:43 +02:00
// Because PDFRefs in PDFLib are stored staticly,
// we need to restore all changed PDFRefs
// for preparing the next execution.
if ( z . newRefs . size > 0 ) {
z . newRefs . restoreAll ( ) ;
}
2022-11-06 10:03:48 +01:00
if ( ret ) {
z . log ( "Pdf has been signed." ) ;
} else {
throw new Error ( "Failed to sign the pdf." ) ;
}
2022-11-14 14:20:19 +01:00
pdfdoc = await _this . addDss ( ret ) ;
if ( pdfdoc ) {
await _this . findChangedObjects ( pdfdoc , true ) ;
ret = _this . appendIncrement ( pdfdoc ) ;
z . log ( "LTV has been enabled." ) ;
2022-11-06 10:03:48 +01:00
}
2022-09-24 06:59:30 +02:00
return ret ;
2022-09-17 14:55:09 +02:00
}
/ * *
* @ private
* @ param { PDFLib . PDFDocument } pdfdoc
2022-10-02 14:45:06 +02:00
* @ return { Promise < Uint8Array > }
2022-09-17 14:55:09 +02:00
* /
2022-10-02 14:45:06 +02:00
async saveAndSign ( pdfdoc ) {
/** @type {Uint8Array} */
2022-10-20 14:28:27 +02:00
var uarr = null ;
2022-11-06 10:03:48 +01:00
if ( this . apobjs && this . apobjs . length > 0 ) {
uarr = this . appendIncrement ( pdfdoc ) ;
2022-10-20 14:28:27 +02:00
} else {
uarr = await pdfdoc . save ( { "useObjectStreams" : false } ) ;
}
2022-10-02 14:45:06 +02:00
/** @type {string} */
2022-10-20 14:28:27 +02:00
var pdfstr = z . u8arrToRaw ( uarr ) + String . fromCharCode ( this . NEWLINE ) ;
2022-10-22 11:42:13 +02:00
return await this . signPdf ( pdfstr ) ;
2022-10-02 14:45:06 +02:00
}
2022-09-17 14:55:09 +02:00
2022-11-06 10:03:48 +01:00
/ * *
* @ private
* @ param { PDFLib . PDFDocument } pdfdoc
* @ param { boolean = } ignoreInfo
* @ return { Promise }
* /
async findChangedObjects ( pdfdoc , ignoreInfo ) {
/** @const {z.PdfSigner} */
const _this = this ;
// Find the changed objects
/** @type {PDFLib.PDFDocument} */
var oriPdfdoc = await PDFLib . PDFDocument . load ( _this . oriU8pdf , { ignoreEncryption : true } ) ;
_this . apobjs = [ ] ;
pdfdoc . context . enumerateIndirectObjects ( ) . forEach ( function ( /** @type {PdfObjEntry} */ a _ele ) {
if ( ! ( ignoreInfo && a _ele [ 0 ] == pdfdoc . context . trailerInfo . Info ) ) {
/** @type {PDFLib.PDFObject} */
var a _obj = oriPdfdoc . context . lookup ( a _ele [ 0 ] ) ;
if ( ! ( a _obj && _this . isamePdfObject ( a _ele [ 1 ] , a _obj ) ) ) {
2022-11-14 14:20:19 +01:00
if ( _this . cyptr ) {
_this . cyptr . encryptObject ( a _ele [ 0 ] . objectNumber , a _ele [ 1 ] ) ;
}
2022-11-06 10:03:48 +01:00
_this . apobjs . push ( a _ele ) ;
}
}
} ) ;
}
2022-10-02 14:45:06 +02:00
/ * *
* @ private
* @ param { PDFLib . PDFDocument } pdfdoc
2022-10-20 14:28:27 +02:00
* @ return { Uint8Array }
* /
2022-11-06 10:03:48 +01:00
appendIncrement ( pdfdoc ) {
2022-10-20 14:28:27 +02:00
/** @const {z.PdfSigner} */
const _this = this ;
/** @type {PDFLib.PDFCrossRefSection} */
var xref = PDFLib . PDFCrossRefSection . create ( ) ;
/** @type {number} */
var stpos = _this . oriU8pdf . length ;
/** @type {Array<number>} */
var buff = [ ] ;
buff [ 0 ] = _this . NEWLINE ;
stpos ++ ;
_this . apobjs . forEach ( function ( /** @type {PdfObjEntry} */ a _ele ) {
/** @type {number} */
var a _len = _this . objEntryToBytes ( a _ele , buff ) ;
xref . addEntry ( a _ele [ 0 ] , stpos ) ;
stpos += a _len ;
} ) ;
xref . copyBytesInto ( buff , buff . length ) ;
/** @type {PDFLib.PDFDict} */
var tdic = PDFLib . PDFWriter . forContext ( pdfdoc . context , 0 ) . createTrailerDict ( ) ;
tdic . set ( PDFLib . PDFName . of ( "Prev" ) , PDFLib . PDFNumber . of ( _this . findPrev ( _this . oriU8pdf ) ) ) ;
/** @type {PDFLib.PDFTrailerDict} */
var ptdic = PDFLib . PDFTrailerDict . of ( tdic ) ;
ptdic . copyBytesInto ( buff , buff . length ) ;
buff . push ( _this . NEWLINE ) ;
/** @type {PDFLib.PDFTrailer} */
var ptlr = PDFLib . PDFTrailer . forLastCrossRefSectionOffset ( stpos ) ;
ptlr . copyBytesInto ( buff , buff . length ) ;
/** @type {Uint8Array} */
var ret = new Uint8Array ( _this . oriU8pdf . length + buff . length ) ;
ret . set ( /** @type {!ArrayBufferView} */ ( _this . oriU8pdf ) ) ;
ret . set ( /** @type {!ArrayBufferView} */ ( new Uint8Array ( buff ) ) , _this . oriU8pdf . length ) ;
return ret ;
}
/ * *
* @ private
* @ param { Uint8Array } u8pdf
* @ return { number }
* /
findPrev ( u8pdf ) {
/** @const {Uint8Array} */
2022-11-06 10:03:48 +01:00
const eof = z . rawToU8arr ( "%%EOF" ) ;
2022-10-20 14:28:27 +02:00
/** @const {number} */
const c0 = "0" . charCodeAt ( 0 ) ;
/** @const {number} */
const c9 = "9" . charCodeAt ( 0 ) ;
/** @type {number} */
var step = 0 ;
/** @type {string} */
var num = "" ;
for ( var i = u8pdf . length - eof . length ; i >= 0 ; i -- ) {
switch ( step ) {
case 0 :
/** @type {boolean} */
var flg = true ;
for ( var j = 0 ; j < eof . length ; j ++ ) {
if ( u8pdf [ i + j ] != eof [ j ] ) {
flg = false ;
break ;
}
}
if ( flg ) {
step = 1 ;
}
break ;
case 1 :
if ( u8pdf [ i ] >= c0 && u8pdf [ i ] <= c9 ) {
num = String . fromCharCode ( u8pdf [ i ] ) ;
step = 2 ;
}
break ;
case 2 :
if ( u8pdf [ i ] >= c0 && u8pdf [ i ] <= c9 ) {
num = String . fromCharCode ( u8pdf [ i ] ) + num ;
} else {
step = 9 ;
}
break ;
}
if ( step >= 9 ) {
break ;
}
}
return parseInt ( num , 10 ) ;
}
/ * *
* @ private
* @ param { PDFLib . PDFObject } obj1
* @ param { PDFLib . PDFObject } obj2
* @ return { boolean }
* /
isamePdfObject ( obj1 , obj2 ) {
/** @type {Array<number>} */
var buff1 = [ ] ;
obj1 . copyBytesInto ( buff1 , 0 ) ;
/** @type {Array<number>} */
var buff2 = [ ] ;
obj2 . copyBytesInto ( buff2 , 0 ) ;
if ( buff1 . length != buff2 . length ) {
return false ;
}
for ( var i = 0 ; i < buff1 . length ; i ++ ) {
if ( buff1 [ i ] != buff2 [ i ] ) {
return false ;
}
}
return true ;
}
/ * *
* @ private
* @ param { PdfObjEntry } objety
* @ param { Array < number > } buff
* @ return { number }
* /
objEntryToBytes ( objety , buff ) {
/** @type {number} */
var before = buff . length ;
objety [ 0 ] . copyBytesInto ( buff , buff . length ) ;
PDFLib . copyStringIntoBuffer ( "obj" , buff , buff . length - 1 ) ;
buff [ buff . length ] = this . NEWLINE ;
objety [ 1 ] . copyBytesInto ( buff , buff . length ) ;
buff [ buff . length ] = this . NEWLINE ;
PDFLib . copyStringIntoBuffer ( "endobj" , buff , buff . length ) ;
buff [ buff . length ] = this . NEWLINE ;
return buff . length - before ;
}
2022-10-24 14:36:47 +02:00
/ * *
* @ private
* @ param { Array < number > | Uint8Array | ArrayBuffer | string = } p12cert
* @ param { string = } pwd
* @ return { forge _cert }
* /
loadP12cert ( p12cert , pwd ) {
2022-11-06 10:03:48 +01:00
/** @const {z.PdfSigner} */
const _this = this ;
2022-10-24 14:36:47 +02:00
// load P12 certificate
if ( ! p12cert ) {
return null ;
} else if ( typeof p12cert !== "string" ) {
p12cert = z . u8arrToRaw ( new Uint8Array ( p12cert ) ) ;
}
// Convert Buffer P12 to a forge implementation.
/** @type {forge.asn1} */
var p12Asn1 = forge . asn1 . fromDer ( p12cert ) ;
/** @type {forge.pkcs12} */
var p12 = forge . pkcs12 . pkcs12FromAsn1 ( p12Asn1 , true , pwd ) ;
// Extract safe bags by type.
// We will need all the certificates and the private key.
/** @type {Object<string|number, P12Bag>} */
var certBags = p12 . getBags ( {
"bagType" : forge . pki . oids . certBag ,
} ) [ forge . pki . oids . certBag ] ;
/** @type {Object<string|number, P12Bag>} */
var keyBags = p12 . getBags ( {
"bagType" : forge . pki . oids . pkcs8ShroudedKeyBag ,
} ) [ forge . pki . oids . pkcs8ShroudedKeyBag ] ;
2022-11-06 10:03:48 +01:00
_this . privateKey = keyBags [ 0 ] . key ;
/** @type {Array<forge_cert>} */
var certs = [ ] ;
/** @type {number} */
var certIdx = - 1 ;
2022-10-24 14:36:47 +02:00
if ( certBags ) {
// Get 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.
Object . keys ( certBags ) . forEach ( function ( a _ele ) {
/** @type {forge_cert} */
var a _cert = certBags [ a _ele ] . cert ;
2022-11-06 10:03:48 +01:00
certs . push ( a _cert ) ;
2022-10-24 14:36:47 +02:00
// Try to find the certificate that matches the private key.
2022-11-06 10:03:48 +01:00
if ( _this . privateKey . n . compareTo ( a _cert . publicKey . n ) === 0
&& _this . privateKey . e . compareTo ( a _cert . publicKey . e ) === 0 ) {
certIdx = certs . length ;
2022-10-24 14:36:47 +02:00
}
2022-11-06 10:03:48 +01:00
} ) ;
2022-10-24 14:36:47 +02:00
}
2022-11-06 10:03:48 +01:00
if ( certIdx > 0 ) {
certIdx -- ;
_this . cchain = new z . CertsChain ( certs ) ;
if ( _this . cchain . getSignCert ( ) != certs [ certIdx ] ) {
throw new Error ( "Chain of certificates is invalid." ) ;
}
return certs [ certIdx ] ;
2022-10-24 14:36:47 +02:00
} else {
throw new Error ( "Failed to find a certificate." ) ;
}
}
2022-10-20 14:28:27 +02:00
/ * *
* @ private
* @ param { PDFLib . PDFDocument } pdfdoc
* @ return { boolean } append mode or not
2022-10-02 14:45:06 +02:00
* /
addSignHolder ( pdfdoc ) {
2022-10-24 14:36:47 +02:00
/** @const {z.PdfSigner} */
const _this = this ;
2022-10-14 14:12:43 +02:00
/** @const {number} */
2022-11-06 10:03:48 +01:00
const docMdp = ( _this . cchain && _this . opt . permission >= 1 && _this . opt . permission <= 3 ) ? _this . opt . permission : 0 ;
2022-10-14 14:12:43 +02:00
/** @const {PDFLib.PDFContext} */
const pdfcont = pdfdoc . context ;
/** @const {z.SignatureCreator} */
2024-08-09 14:57:20 +02:00
const signcrt = new z . SignatureCreator ( _this . opt . drawinf , pdfdoc . getPageCount ( ) ) ;
/** @type {Array<number>} */
var pgidxs = signcrt . getPageIndexes ( ) ;
2022-09-17 14:55:09 +02:00
/** @const {PDFLib.PDFPage} */
2024-08-09 14:57:20 +02:00
const page = pdfdoc . getPages ( ) [ pgidxs [ 0 ] ] ;
2022-10-14 14:12:43 +02:00
/** @type {PDFLib.PDFRef} */
2022-10-24 14:36:47 +02:00
var strmRef = signcrt . createStream ( pdfdoc , _this . opt . signame ) ;
2022-10-14 14:12:43 +02:00
if ( docMdp && ! strmRef ) {
strmRef = signcrt . createEmptyField ( pdfcont ) ;
2024-08-09 14:57:20 +02:00
// For invisible signature, only place on one page.
pgidxs = [ pgidxs [ 0 ] ] ;
2022-10-14 14:12:43 +02:00
}
2022-09-17 14:55:09 +02:00
2022-10-20 14:28:27 +02:00
/** @type {Array<string>} */
var oldSigs = [ ] ;
/** @type {PDFLib.PDFAcroForm} */
var afrm = pdfdoc . catalog . getOrCreateAcroForm ( ) ;
afrm . getAllFields ( ) . forEach ( function ( /** @type {PdfFieldInfo} */ a _finf ) {
if ( a _finf [ 0 ] instanceof PDFLib . PDFAcroSignature ) {
/** @type {PDFLib.PDFString|PDFLib.PDFHexString} */
var a _t = a _finf [ 0 ] . T ( ) ;
if ( a _t instanceof PDFLib . PDFString ) {
oldSigs . push ( a _t . asString ( ) ) ;
} else if ( a _t instanceof PDFLib . PDFHexString ) {
oldSigs . push ( a _t . decodeText ( ) ) ;
}
}
} ) ;
if ( oldSigs . length > 0 && docMdp ) {
throw new Error ( "Since the pdf has been signed, can NOT sign with DocMDP. Because the signature field that contains DocMDP must be the first signed field in the document." ) ;
}
/** @type {string} */
2022-10-24 14:36:47 +02:00
var signm = _this . fixSigName ( oldSigs , _this . opt . signame ) ;
2022-10-20 14:28:27 +02:00
2022-09-24 06:59:30 +02:00
/** @type {Date} */
var signdate = new Date ( ) ;
2022-11-14 14:20:19 +01:00
if ( _this . opt . signdate instanceof Date && ! _this . tsaFetcher ) {
2022-10-24 14:36:47 +02:00
signdate = _this . opt . signdate ;
2022-09-17 14:55:09 +02:00
}
/** @type {PDFLib.PDFArray} */
2022-10-14 14:12:43 +02:00
var bytrng = new PDFLib . PDFArray ( pdfcont ) ;
2022-09-17 14:55:09 +02:00
bytrng . push ( PDFLib . PDFNumber . of ( 0 ) ) ;
2022-10-24 14:36:47 +02:00
bytrng . push ( PDFLib . PDFName . of ( _this . DEFAULT _BYTE _RANGE _PLACEHOLDER ) ) ;
bytrng . push ( PDFLib . PDFName . of ( _this . DEFAULT _BYTE _RANGE _PLACEHOLDER ) ) ;
bytrng . push ( PDFLib . PDFName . of ( _this . DEFAULT _BYTE _RANGE _PLACEHOLDER ) ) ;
2022-10-02 14:45:06 +02:00
2022-11-14 14:20:19 +01:00
_this . siglen = /** @type {number} */ ( _this . tsaFetcher ? _this . tsaFetcher . len : 3322 ) ;
2022-10-24 14:36:47 +02:00
_this . sigContents = PDFLib . PDFHexString . of ( "0" . repeat ( _this . siglen ) ) ;
2022-09-17 14:55:09 +02:00
/** @type {Object<string, *>} */
var signObj = {
"Type" : "Sig" ,
"Filter" : "Adobe.PPKLite" ,
"SubFilter" : "adbe.pkcs7.detached" ,
"ByteRange" : bytrng ,
2022-10-24 14:36:47 +02:00
"Contents" : _this . sigContents ,
2022-10-14 14:12:43 +02:00
"Prop_Build" : pdfcont . obj ( {
"App" : pdfcont . obj ( {
2022-09-18 09:49:56 +02:00
"Name" : "ZgaPdfSinger" ,
2022-09-17 14:55:09 +02:00
} ) ,
} ) ,
} ;
2022-11-06 10:03:48 +01:00
if ( _this . cchain ) {
2022-10-24 14:36:47 +02:00
signObj . M = PDFLib . PDFString . fromDate ( signdate ) ;
} else {
signObj . Type = "DocTimeStamp" ;
signObj . SubFilter = "ETSI.RFC3161" ;
}
2022-10-14 14:12:43 +02:00
if ( docMdp ) {
/** @type {PDFLib.PDFArray} */
var rfrc = new PDFLib . PDFArray ( pdfcont ) ;
rfrc . push ( pdfcont . obj ( {
"Type" : "SigRef" ,
"TransformMethod" : "DocMDP" ,
"TransformParams" : pdfcont . obj ( {
"Type" : "TransformParams" ,
"P" : docMdp ,
"V" : "1.2" ,
} ) ,
} ) ) ;
signObj [ "Reference" ] = rfrc ;
}
2022-10-24 14:36:47 +02:00
if ( _this . opt . reason ) {
signObj [ "Reason" ] = _this . convToPDFString ( _this . opt . reason ) ;
2022-09-17 14:55:09 +02:00
}
2023-02-27 07:05:24 +01:00
if ( _this . opt . signame ) {
signObj [ "Name" ] = _this . convToPDFString ( _this . opt . signame ) ;
}
2022-10-24 14:36:47 +02:00
if ( _this . opt . location ) {
signObj [ "Location" ] = _this . convToPDFString ( _this . opt . location ) ;
2022-09-17 14:55:09 +02:00
}
2022-10-24 14:36:47 +02:00
if ( _this . opt . contact ) {
signObj [ "ContactInfo" ] = _this . convToPDFString ( _this . opt . contact ) ;
2022-09-17 14:55:09 +02:00
}
2022-10-14 14:12:43 +02:00
/** @type {PDFLib.PDFRef} */
var signatureDictRef = pdfcont . register ( pdfcont . obj ( signObj ) ) ;
2022-09-17 14:55:09 +02:00
/** @type {Object<string, *>} */
var widgetObj = {
"Type" : "Annot" ,
"Subtype" : "Widget" ,
"FT" : "Sig" ,
2022-10-14 14:12:43 +02:00
"Rect" : signcrt . getSignRect ( ) ,
2022-09-17 14:55:09 +02:00
"V" : signatureDictRef ,
2022-10-24 14:36:47 +02:00
"T" : _this . convToPDFString ( signm ) ,
2022-09-17 14:55:09 +02:00
"F" : 132 ,
"P" : page . ref ,
} ;
if ( strmRef ) {
2022-10-14 14:12:43 +02:00
widgetObj [ "AP" ] = pdfcont . obj ( {
2022-09-17 14:55:09 +02:00
"N" : strmRef ,
} ) ;
}
2022-10-14 14:12:43 +02:00
/** @type {PDFLib.PDFRef} */
var widgetDictRef = pdfcont . register ( pdfcont . obj ( widgetObj ) ) ;
2022-09-17 14:55:09 +02:00
2024-08-09 14:57:20 +02:00
// Add our signature widget to the pages
pgidxs . forEach ( function ( pi ) {
/** @const {PDFLib.PDFPage} */
var p = pdfdoc . getPages ( ) [ pi ] ;
/** @type {PDFLib.PDFArray} */
var ans = p . node . Annots ( ) ;
if ( ! ans ) {
ans = new PDFLib . PDFArray ( pdfcont ) ;
// if(docMdp){
p . node . set ( PDFLib . PDFName . Annots , ans ) ;
// }else{
// p.node.set(PDFLib.PDFName.Annots, pdfcont.register(ans));
// }
}
ans . push ( widgetDictRef ) ;
} ) ;
2022-10-20 14:28:27 +02:00
if ( ! afrm . dict . lookup ( PDFLib . PDFName . of ( "SigFlags" ) ) ) {
afrm . dict . set ( PDFLib . PDFName . of ( "SigFlags" ) , PDFLib . PDFNumber . of ( 3 ) ) ;
}
afrm . addField ( widgetDictRef ) ;
2022-10-14 14:12:43 +02:00
if ( docMdp ) {
pdfdoc . catalog . set (
PDFLib . PDFName . of ( "Perms" ) ,
pdfcont . obj ( {
"DocMDP" : signatureDictRef ,
} ) ,
) ;
2022-10-20 14:28:27 +02:00
}
2022-09-17 14:55:09 +02:00
2022-10-20 14:28:27 +02:00
return ( oldSigs . length > 0 ) ;
}
/ * *
* @ private
* @ param { Array < string > } oldSigs
* @ param { string = } signm
* @ param { number = } idx
* @ return { string }
* /
fixSigName ( oldSigs , signm , idx ) {
if ( ! signm ) {
signm = "Signature" ;
idx = 1 ;
}
/** @type {string} */
var nm = signm ;
if ( idx ) {
nm += idx ;
2022-10-14 14:12:43 +02:00
} else {
2022-10-20 14:28:27 +02:00
idx = 0 ;
}
if ( oldSigs . indexOf ( nm ) >= 0 ) {
return this . fixSigName ( oldSigs , signm , idx + 1 ) ;
} else {
return nm ;
2022-10-02 14:45:06 +02:00
}
}
2022-10-06 15:02:11 +02:00
/ * *
* @ private
2022-10-24 14:36:47 +02:00
* @ param { string } str
* @ return { PDFLib . PDFString | PDFLib . PDFHexString }
2022-10-06 15:02:11 +02:00
* /
2022-10-24 14:36:47 +02:00
convToPDFString ( str ) {
// Check if there is a multi-bytes char in the string.
/** @type {boolean} */
var flg = false ;
for ( var i = 0 ; i < str . length ; i ++ ) {
if ( str . charCodeAt ( i ) > 0xFF ) {
flg = true ;
break ;
}
2022-10-06 15:02:11 +02:00
}
2022-10-24 14:36:47 +02:00
if ( flg ) {
return PDFLib . PDFHexString . fromText ( str ) ;
2022-10-06 15:02:11 +02:00
} else {
2022-10-24 14:36:47 +02:00
return PDFLib . PDFString . of ( str ) ;
2022-10-06 15:02:11 +02:00
}
}
2022-09-17 14:55:09 +02:00
/ * *
* @ private
* @ param { string } pdfstr
2022-10-22 11:42:13 +02:00
* @ return { Promise < Uint8Array > }
2022-09-17 14:55:09 +02:00
* /
2022-10-22 11:42:13 +02:00
async signPdf ( pdfstr ) {
2022-11-06 10:03:48 +01:00
/** @const {z.PdfSigner} */
const _this = this ;
2022-09-17 14:55:09 +02:00
// Finds ByteRange information within a given PDF Buffer if one exists
2022-10-04 14:37:17 +02:00
/** @type {Array<string>} */
2022-09-17 14:55:09 +02:00
var byteRangeStrings = pdfstr . match ( /\/ByteRange\s*\[{1}\s*(?:(?:\d*|\/\*{10})\s+){3}(?:\d+|\/\*{10}){1}\s*]{1}/g ) ;
2022-10-04 14:37:17 +02:00
/** @type {string|undefined} */
2022-11-06 10:03:48 +01:00
var byteRangePlaceholder = byteRangeStrings . find ( function ( /** @type {string} */ a _str ) {
return a _str . includes ( "/" + _this . DEFAULT _BYTE _RANGE _PLACEHOLDER ) ;
} ) ;
2022-09-17 14:55:09 +02:00
if ( ! byteRangePlaceholder ) {
throw new Error ( "no signature placeholder" ) ;
}
2022-10-04 14:37:17 +02:00
/** @type {number} */
2022-09-17 14:55:09 +02:00
var byteRangePos = pdfstr . indexOf ( byteRangePlaceholder ) ;
2022-10-04 14:37:17 +02:00
/** @type {number} */
2022-09-17 14:55:09 +02:00
var byteRangeEnd = byteRangePos + byteRangePlaceholder . length ;
2022-10-04 14:37:17 +02:00
/** @type {number} */
2022-09-17 14:55:09 +02:00
var contentsTagPos = pdfstr . indexOf ( '/Contents ' , byteRangeEnd ) ;
2022-10-04 14:37:17 +02:00
/** @type {number} */
2022-09-17 14:55:09 +02:00
var placeholderPos = pdfstr . indexOf ( '<' , contentsTagPos ) ;
2022-10-04 14:37:17 +02:00
/** @type {number} */
2022-09-17 14:55:09 +02:00
var placeholderEnd = pdfstr . indexOf ( '>' , placeholderPos ) ;
2022-10-04 14:37:17 +02:00
/** @type {number} */
2022-09-17 14:55:09 +02:00
var placeholderLengthWithBrackets = placeholderEnd + 1 - placeholderPos ;
2022-10-04 14:37:17 +02:00
/** @type {number} */
2022-09-17 14:55:09 +02:00
var placeholderLength = placeholderLengthWithBrackets - 2 ;
2022-10-04 14:37:17 +02:00
/** @type {Array<number>} */
2022-09-17 14:55:09 +02:00
var byteRange = [ 0 , 0 , 0 , 0 ] ;
byteRange [ 1 ] = placeholderPos ;
byteRange [ 2 ] = byteRange [ 1 ] + placeholderLengthWithBrackets ;
byteRange [ 3 ] = pdfstr . length - byteRange [ 2 ] ;
2022-10-04 14:37:17 +02:00
/** @type {string} */
2022-09-17 14:55:09 +02:00
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 ] ) ;
2022-10-24 14:36:47 +02:00
/** @type {forge.asn1} */
var asn1sig = null ;
2022-11-06 10:03:48 +01:00
if ( _this . cchain ) {
2022-10-24 14:36:47 +02:00
/** @type {Date} */
var signdate = new Date ( ) ;
2022-11-14 14:20:19 +01:00
if ( _this . opt . signdate instanceof Date && ! _this . tsaFetcher ) {
2022-11-06 10:03:48 +01:00
signdate = _this . opt . signdate ;
2022-10-24 14:36:47 +02:00
}
2022-09-17 14:55:09 +02:00
2022-10-24 14:36:47 +02:00
// Here comes the actual PKCS#7 signing.
/** @type {forge.pkcs7} */
var p7 = null ;
p7 = forge . pkcs7 . createSignedData ( ) ;
// Start off by setting the content.
p7 . content = forge . util . createBuffer ( pdfstr ) ;
2022-09-17 14:55:09 +02:00
2022-10-24 14:36:47 +02:00
// Add all the certificates (-cacerts & -clcerts) to p7
2022-11-06 10:03:48 +01:00
_this . cchain . getAllCerts ( ) . forEach ( function ( /** @type {forge_cert} */ a _cert ) {
2022-10-24 14:36:47 +02:00
p7 . addCertificate ( a _cert ) ;
} ) ;
2022-09-17 14:55:09 +02:00
2022-10-24 14:36:47 +02:00
// Add a sha256 signer. That's what Adobe.PPKLite adbe.pkcs7.detached expects.
p7 . addSigner ( {
2022-11-06 10:03:48 +01:00
key : _this . privateKey ,
certificate : _this . cchain . getSignCert ( ) ,
2022-10-24 14:36:47 +02:00
digestAlgorithm : forge . pki . oids . sha256 ,
authenticatedAttributes : [
{
"type" : forge . pki . oids . contentType ,
"value" : forge . pki . oids . data ,
} , {
"type" : forge . pki . oids . signingTime ,
"value" : signdate ,
2023-02-24 12:09:38 +01:00
} , {
"type" : forge . pki . oids . messageDigest ,
2022-10-24 14:36:47 +02:00
} ,
] ,
} ) ;
2022-09-24 06:59:30 +02:00
2022-10-24 14:36:47 +02:00
// Sign in detached mode.
p7 . sign ( { "detached" : true } ) ;
2022-11-14 14:20:19 +01:00
if ( _this . tsaFetcher ) {
2022-10-24 14:36:47 +02:00
/** @type {forge.asn1} */
2022-11-06 10:03:48 +01:00
var tsatoken = await _this . queryTsa ( p7 . signers [ 0 ] . signature , true ) ;
2022-10-24 14:36:47 +02:00
p7 . signerInfos [ 0 ] . value . push ( tsatoken ) ;
}
asn1sig = p7 . toAsn1 ( ) ;
} else {
2022-11-06 10:03:48 +01:00
asn1sig = await _this . queryTsa ( pdfstr ) ;
2022-09-24 06:59:30 +02:00
}
2022-09-17 14:55:09 +02:00
// Check if the PDF has a good enough placeholder to fit the signature.
2022-11-06 10:03:48 +01:00
/** @type {forge.util.ByteStringBuffer} */
var sigbuf = forge . asn1 . toDer ( asn1sig ) ;
2022-10-04 14:37:17 +02:00
/** @type {string} */
2022-11-06 10:03:48 +01:00
var sighex = sigbuf . toHex ( ) ;
_this . signature = sigbuf . getBytes ( ) ;
2022-09-17 14:55:09 +02:00
// placeholderLength represents the length of the HEXified symbols but we're
// checking the actual lengths.
2022-11-06 10:03:48 +01:00
z . log ( "Size of signature is " + sighex . length + "/" + placeholderLength ) ;
2022-09-17 14:55:09 +02:00
if ( sighex . length > placeholderLength ) {
2022-10-02 14:45:06 +02:00
// throw new Error("Signature is too big. Needs: " + sighex.length);
2022-11-06 10:03:48 +01:00
_this . siglen = sighex . length ;
2022-10-02 14:45:06 +02:00
return null ;
2022-09-17 14:55:09 +02:00
} 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 ] ) ;
2022-10-02 14:45:06 +02:00
return z . rawToU8arr ( pdfstr ) ;
2022-09-17 14:55:09 +02:00
}
2022-09-19 04:53:54 +02:00
/ * *
* @ private
2022-10-24 14:36:47 +02:00
* @ param { string = } data
2022-11-06 10:03:48 +01:00
* @ param { boolean = } forP7
2022-10-24 14:36:47 +02:00
* @ return { Promise < forge . asn1 > }
2022-09-19 04:53:54 +02:00
* /
2022-11-06 10:03:48 +01:00
async queryTsa ( data , forP7 ) {
/** @const {z.PdfSigner} */
const _this = this ;
/** @type {?string} */
var err = await _this . tsaFetcher . queryTsa ( data ) ;
if ( err ) {
throw new Error ( err ) ;
2022-10-24 14:36:47 +02:00
} else {
2022-11-06 10:03:48 +01:00
/** @type {forge.asn1} */
var asn1 = _this . tsaFetcher . getToken ( forP7 ) ;
2022-11-14 14:20:19 +01:00
z . log ( "Timestamp from " + _this . tsaFetcher . url + " has been obtained." ) ;
2022-11-06 10:03:48 +01:00
return asn1 ;
}
}
/ * *
2022-11-14 14:20:19 +01:00
* @ private
* @ param { PDFLib . PDFDocument | Uint8Array } pdf
* @ return { Promise < PDFLib . PDFDocument > }
2022-11-06 10:03:48 +01:00
* /
2022-11-14 14:20:19 +01:00
async addDss ( pdf ) {
2022-11-06 10:03:48 +01:00
/** @const {z.PdfSigner} */
const _this = this ;
2022-11-14 14:20:19 +01:00
if ( _this . opt . ltv != 1 && _this . opt . ltv != 2 ) {
2022-11-06 10:03:48 +01:00
return null ;
}
2022-11-14 14:20:19 +01:00
/** @type {Zga.CertsChain} */
var cchain = _this . cchain ? _this . cchain : _this . tsaFetcher . getCertsChain ( ) ;
if ( cchain . isSelfSignedCert ( ) ) {
z . log ( "No need to enable LTV because the certificate is a self signed one." ) ;
return null ;
}
/** @type {boolean} */
var crlOnly = ( _this . opt . ltv == 2 ) ;
/** @type {?DSSInfo} */
var dssinf = await cchain . prepareDSSInf ( crlOnly ) ;
if ( ! dssinf ) {
2022-11-06 10:03:48 +01:00
return null ;
}
2022-09-24 06:59:30 +02:00
2022-11-14 14:20:19 +01:00
/** @type {PDFLib.PDFDocument} */
var pdfdoc = null ;
if ( pdf . addPage ) {
pdfdoc = /** @type {PDFLib.PDFDocument} */ ( pdf ) ;
} else {
_this . oriU8pdf = /** @type {Uint8Array} */ ( pdf ) ;
pdfdoc = await PDFLib . PDFDocument . load ( _this . oriU8pdf , { ignoreEncryption : true } ) ;
}
2022-11-06 10:03:48 +01:00
/** @type {PDFLib.PDFContext} */
var pdfcont = pdfdoc . context ;
/** @type {Array<PDFLib.PDFRef>} */
var certRefs = null ;
/** @type {Array<PDFLib.PDFRef>} */
var ocspRefs = null ;
/** @type {Array<PDFLib.PDFRef>} */
var crlRefs = null ;
if ( dssinf && dssinf . ocsps && dssinf . ocsps . length > 0 ) {
ocspRefs = [ ] ;
dssinf . ocsps . forEach ( function ( /** @type {string|Uint8Array} */ a _ocsp ) {
/** @type {PDFLib.PDFRawStream} */
var a _strmOcsp = pdfcont . flateStream ( a _ocsp ) ;
ocspRefs . push ( pdfcont . register ( a _strmOcsp ) ) ;
} ) ;
}
if ( dssinf && dssinf . crls && dssinf . crls . length > 0 ) {
crlRefs = [ ] ;
dssinf . crls . forEach ( function ( /** @type {string|Uint8Array} */ a _crl ) {
/** @type {PDFLib.PDFRawStream} */
var a _strmCrl = pdfcont . flateStream ( a _crl ) ;
crlRefs . push ( pdfcont . register ( a _strmCrl ) ) ;
} ) ;
}
if ( ! ( ocspRefs || crlRefs ) ) {
// Nothing to do.
2022-11-14 14:20:19 +01:00
return null ;
2022-11-06 10:03:48 +01:00
}
if ( dssinf && dssinf . certs && dssinf . certs . length > 0 ) {
certRefs = [ ] ;
dssinf . certs . forEach ( function ( /** @type {forge_cert} */ a _cert ) {
/** @type {forge.asn1} */
var a _asn1Cert = forge . pki . certificateToAsn1 ( a _cert ) ;
/** @type {PDFLib.PDFRawStream} */
var a _strmCert = pdfcont . flateStream ( forge . asn1 . toDer ( a _asn1Cert ) . getBytes ( ) ) ;
certRefs . push ( pdfcont . register ( a _strmCert ) ) ;
} ) ;
}
/** @type {string} */
2022-11-14 14:20:19 +01:00
var sighex = "" ;
if ( _this . signature ) {
/** @type {forge.md.digest} */
var md = forge . md . sha1 . create ( ) ;
md . update ( _this . signature ) ;
sighex = md . digest ( ) . toHex ( ) . toUpperCase ( ) ;
}
2022-11-06 10:03:48 +01:00
var dss = /** @type {PDFLib.PDFDict} */ ( pdfdoc . catalog . lookupMaybe ( PDFLib . PDFName . of ( "DSS" ) , PDFLib . PDFDict ) ) ;
/** @type {PDFLib.PDFArray} */
var certsarr = null ;
/** @type {PDFLib.PDFArray} */
var ocpsarr = null ;
/** @type {PDFLib.PDFArray} */
var crlsarr = null ;
/** @type {PDFLib.PDFDict} */
var vri = null ;
/** @type {Object<string, *>} */
2022-11-14 14:20:19 +01:00
var vriObj = null ;
if ( sighex ) {
vriObj = {
TU : PDFLib . PDFString . fromDate ( new Date ( ) ) ,
} ;
}
2022-11-06 10:03:48 +01:00
/** @type {PDFLib.PDFArray} */
var sigcertsarr = null ;
/** @type {PDFLib.PDFArray} */
var sigocpsarr = null ;
/** @type {PDFLib.PDFArray} */
var sigcrlsarr = null ;
if ( dss ) {
certsarr = /** @type {PDFLib.PDFArray} */ ( dss . lookupMaybe ( PDFLib . PDFName . of ( "Certs" ) , PDFLib . PDFArray ) ) ;
crlsarr = /** @type {PDFLib.PDFArray} */ ( dss . lookupMaybe ( PDFLib . PDFName . of ( "CRLs" ) , PDFLib . PDFArray ) ) ;
ocpsarr = /** @type {PDFLib.PDFArray} */ ( dss . lookupMaybe ( PDFLib . PDFName . of ( "OCSPs" ) , PDFLib . PDFArray ) ) ;
vri = /** @type {PDFLib.PDFDict} */ ( dss . lookupMaybe ( PDFLib . PDFName . of ( "VRI" ) , PDFLib . PDFDict ) ) ;
} else {
dss = /** @type {PDFLib.PDFDict} */ ( pdfcont . obj ( { } ) ) ;
pdfdoc . catalog . set ( PDFLib . PDFName . of ( "DSS" ) , pdfcont . register ( dss ) ) ;
2022-09-24 06:59:30 +02:00
}
2022-11-06 10:03:48 +01:00
if ( certRefs ) {
2022-11-14 14:20:19 +01:00
if ( vriObj ) {
sigcertsarr = new PDFLib . PDFArray ( pdfcont ) ;
vriObj [ "Cert" ] = sigcertsarr ;
}
2022-11-06 10:03:48 +01:00
if ( ! certsarr ) {
certsarr = new PDFLib . PDFArray ( pdfcont ) ;
dss . set ( PDFLib . PDFName . of ( "Certs" ) , pdfcont . register ( certsarr ) ) ;
}
certRefs . forEach ( function ( /** @type {PDFLib.PDFRef} */ a _ref ) {
2022-11-14 14:20:19 +01:00
if ( sigcertsarr ) {
sigcertsarr . push ( a _ref ) ;
}
2022-11-06 10:03:48 +01:00
certsarr . push ( a _ref ) ;
} ) ;
}
if ( ocspRefs ) {
2022-11-14 14:20:19 +01:00
if ( vriObj ) {
sigocpsarr = new PDFLib . PDFArray ( pdfcont ) ;
vriObj [ "OCSP" ] = sigocpsarr ;
}
2022-11-06 10:03:48 +01:00
if ( ! ocpsarr ) {
ocpsarr = new PDFLib . PDFArray ( pdfcont ) ;
dss . set ( PDFLib . PDFName . of ( "OCSPs" ) , pdfcont . register ( ocpsarr ) ) ;
}
ocspRefs . forEach ( function ( /** @type {PDFLib.PDFRef} */ a _ref ) {
2022-11-14 14:20:19 +01:00
if ( sigocpsarr ) {
sigocpsarr . push ( a _ref ) ;
}
2022-11-06 10:03:48 +01:00
ocpsarr . push ( a _ref ) ;
} ) ;
}
if ( crlRefs ) {
2022-11-14 14:20:19 +01:00
if ( vriObj ) {
sigcrlsarr = new PDFLib . PDFArray ( pdfcont ) ;
vriObj [ "CRL" ] = sigcrlsarr ;
}
2022-11-06 10:03:48 +01:00
if ( ! crlsarr ) {
crlsarr = new PDFLib . PDFArray ( pdfcont ) ;
dss . set ( PDFLib . PDFName . of ( "CRLs" ) , pdfcont . register ( crlsarr ) ) ;
}
crlRefs . forEach ( function ( /** @type {PDFLib.PDFRef} */ a _ref ) {
2022-11-14 14:20:19 +01:00
if ( sigcrlsarr ) {
sigcrlsarr . push ( a _ref ) ;
}
2022-11-06 10:03:48 +01:00
crlsarr . push ( a _ref ) ;
} ) ;
}
2022-11-14 14:20:19 +01:00
if ( sighex && vriObj ) {
if ( ! vri ) {
vri = /** @type {PDFLib.PDFDict} */ ( pdfcont . obj ( { } ) ) ;
dss . set ( PDFLib . PDFName . of ( "VRI" ) , pdfcont . register ( vri ) ) ;
}
vri . set ( PDFLib . PDFName . of ( sighex ) , pdfcont . register ( pdfcont . obj ( vriObj ) ) ) ;
2022-11-06 10:03:48 +01:00
}
2022-11-14 14:20:19 +01:00
await pdfdoc . flush ( ) ;
return pdfdoc ;
2022-09-24 06:59:30 +02:00
}
2022-10-02 14:45:06 +02:00
} ;
2022-09-17 14:55:09 +02:00
2022-10-14 14:12:43 +02:00
z . SignatureCreator = class {
2022-09-17 14:55:09 +02:00
/ * *
* @ param { SignDrawInfo = } drawinf
2024-08-09 14:57:20 +02:00
* @ param { number = } pgcnt
2022-09-17 14:55:09 +02:00
* /
2024-08-09 14:57:20 +02:00
constructor ( drawinf , pgcnt ) {
/** @private @type {Array<number>} */
this . pgidxs = [ ] ;
2022-09-17 14:55:09 +02:00
/** @private @type {Array<number>} */
this . rect = [ 0 , 0 , 0 , 0 ] ;
2022-10-04 14:37:17 +02:00
/** @private @type {?SignDrawInfo} */
2022-09-17 14:55:09 +02:00
this . drawinf = null ;
if ( drawinf ) {
this . drawinf = drawinf ;
2024-08-09 14:57:20 +02:00
if ( typeof this . drawinf . pageidx == "string" ) {
/** @type {Array<string>} */
var sarr = this . drawinf . pageidx . split ( "," ) ;
/** @type {number} */
var i = 0 ;
for ( i = 0 ; i < sarr . length ; i ++ ) {
if ( sarr [ i ] ) {
/** @type {Array<string>} */
var sarr2 = sarr [ i ] . split ( "-" ) ;
/** @type {number} */
var j = sarr2 [ 0 ] ? parseInt ( sarr2 [ 0 ] , 10 ) : 0 ;
/** @type {number} */
2024-08-10 13:12:19 +02:00
var ed = sarr2 [ sarr2 . length - 1 ] ? parseInt ( sarr2 [ sarr2 . length - 1 ] , 10 ) : ( pgcnt ? pgcnt - 1 : j ) ;
2024-08-09 14:57:20 +02:00
while ( j <= ed ) {
this . pgidxs . push ( j ) ;
j ++ ;
}
}
}
} else if ( this . drawinf . pageidx ) {
this . pgidxs = [ /** @type {number} */ ( this . drawinf . pageidx ) ] ;
2022-09-17 14:55:09 +02:00
}
}
2024-08-09 14:57:20 +02:00
if ( this . pgidxs . length == 0 ) {
this . pgidxs = [ 0 ] ;
}
2022-09-17 14:55:09 +02:00
}
/ * *
* @ public
2024-08-09 14:57:20 +02:00
* @ return { Array < number > }
2022-09-17 14:55:09 +02:00
* /
2024-08-09 14:57:20 +02:00
getPageIndexes ( ) {
return this . pgidxs ;
2022-09-17 14:55:09 +02:00
}
/ * *
* @ public
* @ return { Array < number > }
* /
getSignRect ( ) {
return this . rect ;
}
2022-10-14 14:12:43 +02:00
/ * *
* @ public
* @ param { PDFLib . PDFContext } pdfcont
* @ return { PDFLib . PDFRef }
* /
createEmptyField ( pdfcont ) {
return pdfcont . register ( pdfcont . obj ( {
"Type" : "XObject" ,
"Subtype" : "Form" ,
"FormType" : 1 ,
"BBox" : [ 0 , 0 , 0 , 0 ] ,
} ) ) ;
}
2022-09-17 14:55:09 +02:00
/ * *
* @ 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 ;
2024-08-09 14:57:20 +02:00
} else if ( ! ( this . drawinf . img || this . drawinf . textInfo ) ) {
2022-09-17 14:55:09 +02:00
return null ;
}
/** @type {Array<PDFLib.PDFPage>} */
var pages = pdfdoc . getPages ( ) ;
/** @type {PDFLib.PDFPage} */
var page = null ;
2024-08-09 14:57:20 +02:00
if ( this . pgidxs [ 0 ] < pages . length ) {
page = pages [ this . pgidxs [ 0 ] ] ;
2022-09-17 14:55:09 +02:00
} else {
throw new Error ( "Page index is overflow to pdf pages." ) ;
}
2022-10-04 14:37:17 +02:00
/** @type {PDFLib.Rotation} */
2022-09-17 14:55:09 +02:00
var pgrot = page . getRotation ( ) ;
pgrot . angle = PDFLib . toDegrees ( pgrot ) % 360 ;
pgrot . type = PDFLib . RotationTypes . Degrees ;
2022-10-04 14:37:17 +02:00
/** @type {PdfSize} */
2022-09-17 14:55:09 +02:00
var pgsz = page . getSize ( ) ;
2022-10-04 14:37:17 +02:00
/** @type {SignAreaInfo} */
2024-08-09 14:57:20 +02:00
var areainf = this . drawinf . area ;
2022-09-17 14:55:09 +02:00
// resources object
2022-10-04 14:37:17 +02:00
/** @type {Object<string, *>} */
2022-09-17 14:55:09 +02:00
var rscObj = { } ;
2022-10-04 14:37:17 +02:00
/** @type {string} */
2022-09-17 14:55:09 +02:00
var imgName = signame ? signame . concat ( "Img" ) : "SigImg" ;
2022-10-04 14:37:17 +02:00
/** @type {string} */
2022-09-17 14:55:09 +02:00
var fontName = signame ? signame . concat ( "Font" ) : "SigFont" ;
2024-08-09 14:57:20 +02:00
/** @type {Array<PDFLib.PDFOperator>} */
var txtOprs = [ ] ;
if ( this . drawinf . textInfo ) {
rscObj [ "Font" ] = {
[ fontName ] : this . drawinf . font . ref ,
} ;
txtOprs = this . createDrawTextOper ( pdfdoc , pgrot , fontName , areainf ) ;
}
/** @type {Array<PDFLib.PDFOperator>} */
var imgOprs = [ ] ;
if ( this . drawinf . img ) {
2022-09-17 14:55:09 +02:00
rscObj [ "XObject" ] = {
[ imgName ] : this . drawinf . img . ref ,
} ;
2024-08-09 14:57:20 +02:00
imgOprs = PDFLib . drawImage ( imgName , this . calcDrawImgInf ( pgrot , this . drawinf . img . size ( ) , areainf , txtOprs . length == 0 ) ) ;
2022-09-17 14:55:09 +02:00
}
2024-08-09 14:57:20 +02:00
areainf = this . calcAreaInf ( pgsz , pgrot . angle , areainf ) ;
2022-09-17 14:55:09 +02:00
this . rect = this . calcRect ( pgrot . angle , areainf ) ;
2022-10-20 14:28:27 +02:00
var frmDict = /** @type {PDFLib.PDFDict} */ ( pdfdoc . context . obj ( {
2022-09-17 14:55:09 +02:00
"Type" : "XObject" ,
"Subtype" : "Form" ,
"FormType" : 1 ,
2024-08-09 14:57:20 +02:00
"BBox" : [ 0 , 0 , areainf . wDraw , areainf . hDraw ] ,
2022-09-17 14:55:09 +02:00
"Resources" : rscObj ,
2022-10-20 14:28:27 +02:00
} ) ) ;
2022-10-04 14:37:17 +02:00
/** @type {PDFLib.PDFContentStream} */
2024-08-09 14:57:20 +02:00
var strm = PDFLib . PDFContentStream . of ( frmDict , imgOprs . concat ( txtOprs ) , true ) ;
2022-09-17 14:55:09 +02:00
return pdfdoc . context . register ( strm ) ;
}
/ * *
* Calculate area informations for drawing signature after rotate
*
* @ private
2022-10-04 14:37:17 +02:00
* @ param { PdfSize } pgsz
2022-09-17 14:55:09 +02:00
* @ param { number } angle
* @ param { SignAreaInfo } visinf
* @ return { SignAreaInfo }
* /
calcAreaInf ( pgsz , angle , visinf ) {
2022-10-04 14:37:17 +02:00
var ret = /** @type {SignAreaInfo} */ ( Object . assign ( { } , visinf ) ) ;
2022-09-17 14:55:09 +02:00
// Calculate position after rotate
switch ( angle ) {
case 90 :
2024-08-09 14:57:20 +02:00
ret . wDraw = visinf . hDraw ;
ret . hDraw = visinf . wDraw ;
ret . x = visinf . y + visinf . hDraw ;
2022-09-17 14:55:09 +02:00
ret . y = visinf . x ;
break ;
case 180 :
case - 180 :
ret . x = pgsz . width - visinf . x ;
2024-08-09 14:57:20 +02:00
ret . y = visinf . y + visinf . hDraw ;
2022-09-17 14:55:09 +02:00
break ;
case 270 :
case - 90 :
2024-08-09 14:57:20 +02:00
ret . wDraw = visinf . hDraw ;
ret . hDraw = visinf . wDraw ;
ret . x = pgsz . width - visinf . y - visinf . hDraw ;
2022-09-17 14:55:09 +02:00
ret . y = pgsz . height - visinf . x ;
break ;
default :
2024-08-09 14:57:20 +02:00
ret . y = pgsz . height - visinf . y - visinf . hDraw ;
2022-09-17 14:55:09 +02:00
}
return ret ;
}
/ * *
* @ private
* @ param { number } angle
2024-08-09 14:57:20 +02:00
* @ param { SignAreaInfo } areainf
2022-09-17 14:55:09 +02:00
* @ return { Array < number > }
* /
calcRect ( angle , areainf ) {
2022-10-04 14:37:17 +02:00
/** @type {Array<number>} */
2022-09-17 14:55:09 +02:00
var rect = [ 0 , 0 , 0 , 0 ] ;
rect [ 0 ] = areainf . x ;
rect [ 1 ] = areainf . y ;
switch ( angle ) {
case 90 :
2024-08-09 14:57:20 +02:00
rect [ 2 ] = areainf . x - areainf . wDraw ;
rect [ 3 ] = areainf . y + areainf . hDraw ;
2022-09-17 14:55:09 +02:00
break ;
case 180 :
case - 180 :
2024-08-09 14:57:20 +02:00
rect [ 2 ] = areainf . x - areainf . wDraw ;
rect [ 3 ] = areainf . y - areainf . hDraw ;
2022-09-17 14:55:09 +02:00
break ;
case 270 :
case - 90 :
2024-08-09 14:57:20 +02:00
rect [ 2 ] = areainf . x + areainf . wDraw ;
rect [ 3 ] = areainf . y - areainf . hDraw ;
2022-09-17 14:55:09 +02:00
break ;
default :
2024-08-09 14:57:20 +02:00
rect [ 2 ] = areainf . x + areainf . wDraw ;
rect [ 3 ] = areainf . y + areainf . hDraw ;
2022-09-17 14:55:09 +02:00
}
return rect ;
}
/ * *
* Calculate informations for drawing image after rotate
*
* @ private
* @ param { PDFLib . Rotation } rot
2024-08-09 14:57:20 +02:00
* @ param { PdfSize } imgsz
* @ param { SignAreaInfo } areainf
* @ param { boolean } canResize
2022-10-04 14:37:17 +02:00
* @ return { PdfDrawimgOption }
2022-09-17 14:55:09 +02:00
* /
2024-08-09 14:57:20 +02:00
calcDrawImgInf ( rot , imgsz , areainf , canResize ) {
if ( ! areainf . wDraw ) {
if ( areainf . w ) {
areainf . wDraw = areainf . w ;
} else {
areainf . wDraw = imgsz . width ;
}
}
if ( ! areainf . hDraw ) {
if ( areainf . h ) {
areainf . hDraw = areainf . h ;
} else {
areainf . hDraw = imgsz . height ;
}
}
/** @type {number} */
var wImg = areainf . wDraw ;
/** @type {number} */
var hImg = areainf . hDraw ;
if ( wImg != imgsz . width && hImg != imgsz . height ) {
/** @type {number} */
var tmp = wImg * imgsz . height / imgsz . width ;
if ( tmp <= hImg ) {
hImg = tmp ;
} else {
wImg = hImg * imgsz . width / imgsz . height ;
}
}
if ( canResize ) {
areainf . wDraw = wImg ;
areainf . hDraw = hImg ;
}
2022-10-04 14:37:17 +02:00
/** @type {PdfDrawimgOption} */
2022-09-17 14:55:09 +02:00
var ret = {
"x" : 0 ,
"y" : 0 ,
2024-08-09 14:57:20 +02:00
"width" : wImg ,
"height" : hImg ,
2022-09-17 14:55:09 +02:00
"rotate" : rot ,
"xSkew" : PDFLib . degrees ( 0 ) ,
"ySkew" : PDFLib . degrees ( 0 ) ,
2024-08-09 14:57:20 +02:00
// "graphicsState": "",
2022-09-17 14:55:09 +02:00
} ;
switch ( rot . angle ) {
2024-08-09 14:57:20 +02:00
case 0 :
ret [ "y" ] = areainf . hDraw - hImg - ret [ "y" ] ;
break ;
2022-09-17 14:55:09 +02:00
case 90 :
2024-08-09 14:57:20 +02:00
ret [ "x" ] += hImg ;
2022-09-17 14:55:09 +02:00
break ;
case 180 :
case - 180 :
2024-08-09 14:57:20 +02:00
ret [ "x" ] = areainf . wDraw - ret [ "x" ] ;
ret [ "y" ] += hImg ;
2022-09-17 14:55:09 +02:00
break ;
case 270 :
case - 90 :
2024-08-09 14:57:20 +02:00
ret [ "x" ] = areainf . hDraw - hImg - ret [ "x" ] ;
ret [ "y" ] = areainf . wDraw - ret [ "y" ] ;
2022-09-17 14:55:09 +02:00
break ;
}
return ret ;
}
2024-08-04 13:07:21 +02:00
/ * *
2024-08-09 14:57:20 +02:00
* Create operations for drawing text after rotate
2024-08-04 13:07:21 +02:00
*
* @ private
2024-08-09 14:57:20 +02:00
* @ param { PDFLib . PDFDocument } pdfdoc
2024-08-04 13:07:21 +02:00
* @ param { PDFLib . Rotation } rot
2024-08-09 14:57:20 +02:00
* @ param { string } fontName
* @ param { SignAreaInfo } areainf
* @ return { Array < PDFLib . PDFOperator > }
2024-08-04 13:07:21 +02:00
* /
2024-08-09 14:57:20 +02:00
createDrawTextOper ( pdfdoc , rot , fontName , areainf ) {
/** @const {z.SignatureCreator} */
const _this = this ;
var txtInf = /** @type {SignTextInfo} */ ( _this . drawinf . textInfo ) ;
var font = /** @type {!PDFLib.PDFFont} */ ( _this . drawinf . font ) ;
2024-08-04 13:07:21 +02:00
/** @type {DrawLinesOfTextOptions} */
2024-08-09 14:57:20 +02:00
var opts = {
"x" : txtInf . xOffset || 0 ,
"y" : txtInf . yOffset || 0 ,
"color" : _this . hexToColor ( txtInf . color ) ,
"font" : fontName ,
"lineHeight" : txtInf . lineHeight || font . heightAtSize ( txtInf . size , { descender : true } ) ,
"size" : txtInf . size ,
2024-08-04 13:07:21 +02:00
"rotate" : rot ,
"xSkew" : PDFLib . degrees ( 0 ) ,
"ySkew" : PDFLib . degrees ( 0 ) ,
2024-08-09 14:57:20 +02:00
// "graphicsState": "",
2024-08-04 13:07:21 +02:00
} ;
2024-08-09 14:57:20 +02:00
/ * *
* @ param { string } t
* @ return { number }
* /
var calcTextWidth = function ( t ) {
return font . widthOfTextAtSize ( t , txtInf . size ) ;
} ;
/** @type {Array<string>} */
var txts = [ ] ;
/** @type {boolean} */
var needW = false ;
/** @type {number} */
var w = txtInf . wMax || areainf . w || 0 ;
if ( w ) {
txts = _this . breakTextIntoLines ( txtInf . text , w , calcTextWidth , txtInf . noBreaks ) ;
} else {
txts = PDFLib . lineSplit ( PDFLib . cleanText ( txtInf . text ) ) ;
needW = true ;
}
/** @type {Array<number>} */
var wids = [ ] ;
/** @type {Array<PDFLib.PDFHexString>} */
var enctxts = txts . map ( function ( t2 ) {
/** @type {number} */
var cw = 0 ;
t2 = t2 . trim ( ) ;
if ( needW || txtInf . align ) {
cw = calcTextWidth ( t2 ) ;
wids . push ( cw ) ;
}
if ( needW ) {
w = Math . max ( w , cw ) ;
}
return font . encodeText ( t2 ) ;
} ) ;
if ( areainf . w ) {
areainf . wDraw = areainf . w ;
} else {
areainf . wDraw = w + opts [ "x" ] ;
}
if ( areainf . h ) {
areainf . hDraw = areainf . h ;
} else {
areainf . hDraw = txts . length * opts [ "lineHeight" ] + opts [ "y" ] ;
}
/** @type {Array<PDFLib.PDFOperator>} */
var ret = [ ] ;
/** @type {Array<number>} */
var pos = null ;
if ( txtInf . align ) {
wids . forEach ( function ( w1 , i1 ) {
/** @type {number} */
var x = opts [ "x" ] ;
if ( txtInf . align == 1 ) {
// center alignment
x = ( w - w1 ) / 2 + opts [ "x" ] ;
} else {
// right alignment
x = ( w - w1 ) + opts [ "x" ] ;
}
ret = ret . concat ( PDFLib . drawLinesOfText ( [ enctxts [ i1 ] ] , _this . calcTextPos ( opts , areainf . wDraw , areainf . hDraw , i1 , x ) ) ) ;
} ) ;
} else {
ret = PDFLib . drawLinesOfText ( enctxts , _this . calcTextPos ( opts , areainf . wDraw , areainf . hDraw ) ) ;
}
return ret ;
}
/ * *
* Convert hex string to Color
*
* @ private
* @ param { string = } hex
* @ return { PDFLib . Color }
* /
hexToColor ( hex ) {
/** @type {Array<number>} */
var rgb = [ 0 , 0 , 0 ] ;
if ( hex ) {
if ( hex . charAt ( 0 ) == "#" ) {
hex = hex . substring ( 1 ) ;
}
if ( hex . length == 3 ) {
rgb [ 0 ] = parseInt ( hex . charAt ( 0 ) + hex . charAt ( 0 ) , 16 ) ;
rgb [ 1 ] = parseInt ( hex . charAt ( 1 ) + hex . charAt ( 1 ) , 16 ) ;
rgb [ 2 ] = parseInt ( hex . charAt ( 2 ) + hex . charAt ( 2 ) , 16 ) ;
} else if ( hex . length == 6 ) {
rgb [ 0 ] = parseInt ( hex . substring ( 0 , 2 ) , 16 ) ;
rgb [ 1 ] = parseInt ( hex . substring ( 2 , 4 ) , 16 ) ;
rgb [ 2 ] = parseInt ( hex . substring ( 4 , 6 ) , 16 ) ;
} else {
throw new Error ( "The hex string is not a valid color." ) ;
}
}
return PDFLib . rgb ( rgb [ 0 ] / 255 , rgb [ 1 ] / 255 , rgb [ 2 ] / 255 ) ;
}
/ * *
* @ private
* @ param { string } text
* @ param { number } maxWidth
* @ param { function ( string ) : number } computeWidthOfText
* @ param { string = } noBreakRx
* @ return { Array < string > }
* /
breakTextIntoLines ( text , maxWidth , computeWidthOfText , noBreakRx ) {
/** @type {string} */
var ctxt = PDFLib . cleanText ( text ) ;
/** @type {string} */
var currLine = "" ;
/** @type {number} */
var currWidth = 0 ;
/** @type {Array<string>} */
var lines = [ ] ;
var nwRegexp = new RegExp ( noBreakRx || "[A-Za-z0-9]" ) ;
/** @type {Array<string>} */
var words = [ ] ;
/** @type {number} */
var idx = 0 ;
/** @type {number} */
var len = ctxt . length ;
while ( idx < len ) {
/** @type {string} */
var c = ctxt . charAt ( idx ) ;
if ( nwRegexp . test ( c ) ) {
currLine += c ;
} else {
if ( currLine ) words . push ( currLine ) ;
currLine = "" ;
words . push ( c ) ;
}
if ( c == "\r" && idx + 1 < len && ctxt . charAt ( idx + 1 ) == "\n" ) {
idx ++ ;
}
idx ++ ;
}
if ( currLine ) words . push ( currLine ) ;
currLine = "" ;
idx = 0 ;
len = words . length ;
while ( idx < len ) {
/** @type {string} */
var word = words [ idx ] ;
if ( PDFLib . isNewlineChar ( word ) ) {
lines . push ( currLine ) ;
currLine = "" ;
currWidth = 0 ;
} else {
/** @type {number} */
var width = computeWidthOfText ( word ) ;
2024-08-10 13:12:19 +02:00
if ( width > maxWidth ) {
if ( idx > 0 ) {
lines . push ( currLine ) ;
currLine = "" ;
currWidth = 0 ;
}
/** @type {SplitLongWordResult} */
var slwr = this . splitLongWord ( word , width , maxWidth , computeWidthOfText ) ;
lines = lines . concat ( slwr . words ) ;
word = slwr . lastWord ;
width = slwr . lastWidth ;
} else if ( currWidth + width > maxWidth ) {
2024-08-09 14:57:20 +02:00
lines . push ( currLine ) ;
currLine = "" ;
currWidth = 0 ;
}
currLine += word ;
currWidth += width ;
}
idx ++ ;
}
if ( currLine ) lines . push ( currLine ) ;
return lines ;
}
2024-08-10 13:12:19 +02:00
/ * *
* @ private
* @ param { string } word
* @ param { number } wordWidth
* @ param { number } maxWidth
* @ param { function ( string ) : number } computeWidthOfText
* @ return { SplitLongWordResult }
* /
splitLongWord ( word , wordWidth , maxWidth , computeWidthOfText ) {
/** @type {Array<string>} */
var splited = [ ] ;
/** @type {number} */
var wordLen = word . length ;
while ( wordWidth > maxWidth ) {
/** @type {number} */
var maxIdx = Math . floor ( wordLen * maxWidth / wordWidth ) - 1 ;
/** @type {number} */
var w = computeWidthOfText ( word . substring ( 0 , maxIdx + 1 ) ) ;
if ( w > maxWidth ) {
while ( w > maxWidth ) {
maxIdx -- ;
w -= computeWidthOfText ( word . charAt ( maxIdx ) ) ;
}
maxIdx ++ ;
} else {
while ( w < maxWidth ) {
maxIdx ++ ;
if ( maxIdx < wordLen ) {
/** @type {number} */
var w2 = w + computeWidthOfText ( word . charAt ( maxIdx ) ) ;
if ( w2 > maxWidth ) {
break ;
} else {
w = w2 ;
}
} else {
break ;
}
}
}
splited . push ( word . substring ( 0 , maxIdx ) ) ;
word = word . substring ( maxIdx ) ;
wordLen -= maxIdx ;
wordWidth -= w ;
}
return {
words : splited ,
lastWord : word ,
lastWidth : wordWidth ,
} ;
}
2024-08-09 14:57:20 +02:00
/ * *
* @ private
* @ param { DrawLinesOfTextOptions } opts
* @ param { number = } w // It must not be undefined, but need to suppress warning of mismatch
* @ param { number = } h // It must not be undefined, but need to suppress warning of mismatch
* @ param { number = } idx // line index
* @ param { number = } aX // x of alignment
* @ return { DrawLinesOfTextOptions } // A copy of opts, and x, y are calculated.
* /
calcTextPos ( opts , w , h , idx , aX ) {
var newopts = /** @type {DrawLinesOfTextOptions} */ ( Object . assign ( { } , opts ) ) ;
/** @type {number} */
var i = idx || 0 ;
/** @type {number} */
var x = aX || opts [ "x" ] ;
switch ( opts [ "rotate" ] . angle ) {
case 0 :
newopts [ "x" ] = x ;
newopts [ "y" ] = h - opts [ "lineHeight" ] - opts [ "y" ] - ( opts [ "lineHeight" ] * i ) ;
break ;
2024-08-04 13:07:21 +02:00
case 90 :
2024-08-09 14:57:20 +02:00
newopts [ "x" ] = opts [ "lineHeight" ] + opts [ "y" ] + ( opts [ "lineHeight" ] * i ) ;
newopts [ "y" ] = x ;
2024-08-04 13:07:21 +02:00
break ;
case 180 :
case - 180 :
2024-08-09 14:57:20 +02:00
newopts [ "x" ] = w - x ;
newopts [ "y" ] = opts [ "lineHeight" ] + opts [ "y" ] + ( opts [ "lineHeight" ] * i ) ;
2024-08-04 13:07:21 +02:00
break ;
case 270 :
case - 90 :
2024-08-09 14:57:20 +02:00
newopts [ "x" ] = h - opts [ "lineHeight" ] - opts [ "y" ] - ( opts [ "lineHeight" ] * i ) ;
newopts [ "y" ] = w - x ;
2024-08-04 13:07:21 +02:00
break ;
}
2024-08-10 13:12:19 +02:00
2024-08-09 14:57:20 +02:00
return newopts ;
2024-08-04 13:07:21 +02:00
}
2022-10-02 14:45:06 +02:00
} ;
2022-09-18 09:49:56 +02:00
2024-08-10 13:12:19 +02:00
/ * *
* @ typedef
* { {
* words : Array < string > ,
* lastWord : string ,
* lastWidth : number ,
* } }
* /
var SplitLongWordResult ;
2022-10-02 14:45:06 +02:00
}
2022-09-18 09:49:56 +02:00
2022-11-06 10:03:48 +01:00
//Only for nodejs Start//
if ( typeof exports === "object" && typeof module !== "undefined" ) {
module . exports = supplyZgaSigner ;
2022-10-02 14:45:06 +02:00
}
2022-11-06 10:03:48 +01:00
//Only for nodejs End//