diff --git a/javascript/src/utilities/pdfUtilities.js b/javascript/src/utilities/pdfUtilities.js
index 543fd2ff860355f2a0cc777a63b698e8b5d22086..f869fd153a022644976085d44b58e0cfeeff14b5 100644
--- a/javascript/src/utilities/pdfUtilities.js
+++ b/javascript/src/utilities/pdfUtilities.js
@@ -1,771 +1,776 @@
-/*
- * Based on PDFSign v1.0.0
- * https://github.com/Communication-Systems-Group/pdfsign.js
- *
- * Copyright 2015, Thomas Bocek, University of Zurich
- *
- * Licensed under the MIT license:
- * http://www.opensource.org/licenses/MIT
- */
-
-import {
-    ObjectIdentifier,
-    UTCTime,
-    OctetString
-} from 'asn1js';
-
-import {
-    ContentInfo,
-    SignedData,
-    Attribute,
-    SignerInfo,
-    IssuerAndSerialNumber,
-    SignedAndUnsignedAttributes,
-    EncapsulatedContentInfo,
-    getCrypto
-} from 'pkijs';
-
-import {
-    parseCertificate,
-    parsePrivateKey,
-} from './signingUtilities';
-
-//keep this import to include the patched version of pdfjs library
-import {PDFJS} from "../lib/pdfjs.parser.js";
-
-
-function createXrefTable(xrefEntries) {
-    xrefEntries = sortOnKeys(xrefEntries);
-    var retVal ='xref\n';
-    var last = -2;
-    for(var i in xrefEntries) {
-        i = parseInt(i);
-        if(typeof xrefEntries[i].offset === 'undefined') { continue; }
-        retVal += calcFlow(i, last, xrefEntries);
-        var offset = xrefEntries[i].offset;
-        retVal += pad10(offset)+' '+pad5(xrefEntries[i].gen)+' '+(xrefEntries[i].free?'f':'n')+' \n';
-        last = i;
-    }
-    return retVal;
-}
-
-function calcFlow(i, last, xrefEntries) {
-    if(last + 1 === i) {return '';}
-    var count = 1;
-    while(typeof xrefEntries[(i+count)] !== 'undefined'
-            && typeof xrefEntries[(i+count)].offset !== 'undefined') {count ++;}
-    return i + ' '+count+'\n';
-}
-
-function createTrailer(topDict, startxref, sha256Hex, size, prev) {
-    var retVal ='trailer <<\n';
-    retVal +='  /Size '+(size)+'\n';
-    var refRoot = topDict.getRaw('Root');
-    if(typeof refRoot !== 'undefined') {
-        retVal +='  /Root '+refRoot.num+' '+refRoot.gen+' R\n';
-    }
-    var refInfo = topDict.getRaw('Info');
-    if(typeof refInfo !== 'undefined') {
-        retVal +='  /Info '+refInfo.num+' '+refInfo.gen+' R\n';
-    }
-    retVal +='  /ID [<'+sha256Hex.substring(0,32)+'><'+sha256Hex.substring(32,64)+'>]\n';
-    if(typeof prev !== 'undefined' ) {
-        retVal +='  /Prev '+prev+'\n';
-    }
-    retVal +='>>\n';
-    retVal +='startxref\n';
-    retVal +=startxref + '\n';
-    retVal +='%%EOF\n';
-    return retVal;
-}
-
-function createXrefTableAppend(xrefEntries) {
-    xrefEntries = sortOnKeys(xrefEntries);
-
-    var retVal ='xref\n';
-    var last = -2;
-    for(var i in xrefEntries) {
-        i = parseInt(i);
-        if(typeof xrefEntries[i].offset === 'undefined') { continue; }
-        retVal += calcFlow(i, last, xrefEntries);
-        var offset = xrefEntries[i].offset;
-        retVal += pad10(offset)+' '+pad5(xrefEntries[i].gen)+' '+(xrefEntries[i].free?'f':'n')+' \n';
-        last = i;
-    }
-    return retVal;
-}
-
-//http://stackoverflow.com/questions/10946880/sort-a-dictionary-or-whatever-key-value-data-structure-in-js-on-word-number-ke
-function sortOnKeys(dict) {
-    var sorted = [];
-    for(var key in dict) {
-        sorted[sorted.length] = key;
-    }
-    sorted.sort();
-
-    var tempDict = {};
-    for(var i = 0; i < sorted.length; i++) {
-        tempDict[sorted[i]] = dict[sorted[i]];
-    }
-
-    return tempDict;
-}
-
-function removeFromArray(array, from, to) {
-    var cutlen = to - from;
-    var buf = new Uint8Array(array.length - cutlen);
-
-    for (var i = 0; i < from; i++) {
-        buf[i] = array[i];
-    }
-    for (var i = to, len = array.length; i < len; i++) {
-        buf[i-cutlen] = array[i];
-    }
-    return buf;
-}
-        
-function findXrefBlocks(xrefBlocks) {
-    var num = xrefBlocks.length / 2;
-    var retVal = [];
-    for (var i=0;i<num;i++) {
-        retVal.push({start: xrefBlocks[i], end: xrefBlocks[i+num]});
-    }
-    return retVal;
-}
-
-function convertUint8ArrayToBinaryString(u8Array) {
-    var i, len = u8Array.length, b_str = "";
-    for (i=0; i<len; i++) {
-        b_str += String.fromCharCode(u8Array[i]);
-    }
-    return b_str;
-}
-
-function arrayObjectIndexOf(array, start, end, orig) {
-    for(var i = 0, len = array.length; i < len; i++) {
-        if ((array[i].start === start) && (array[i].end === end) && (array[i].orig === orig)) {
-            return i;
-        }
-    }
-    return -1;
-}
-
-function pad10(num) {
-    var s = "000000000" + num;
-    return s.substr(s.length-10);
-}
-
-function pad5(num) {
-    var s = "0000" + num;
-    return s.substr(s.length-5);
-}
-
-function pad2(num) {
-    var s = "0" + num;
-    return s.substr(s.length-2);
-}
-
-function findRootEntry(xref) {
-        var rootNr = xref.root.objId.substring(0, xref.root.objId.length - 1);
-        return xref.entries[rootNr];
-}
-        
-function findSuccessorEntry(xrefEntries, current) {
-    //find it first
-    var currentOffset = current.offset;
-    var currentMin = Number.MAX_SAFE_INTEGER;
-    var currentMinIndex = -1;
-    for(var i in xrefEntries) {
-        if(xrefEntries[i].offset > currentOffset) {
-            if(xrefEntries[i].offset < currentMin) {
-                currentMin = xrefEntries[i].offset;
-                currentMinIndex = i;
-            }
-        }
-    }
-    if(currentMinIndex === -1) {
-        return current;
-    }
-    return xrefEntries[currentMinIndex];
-}
-
-function updateArray(array, pos, str) {
-    var upd = stringToUint8Array(str);
-    for (var i = 0, len=upd.length; i < len; i++) {
-        array[i+pos] = upd[i];
-    }
-    return array;
-}
-
-function copyToEnd(array, from, to) {
-    var buf = new Uint8Array(array.length + (to - from));
-    for (var i = 0, len=array.length; i < len; i++) {
-        buf[i] = array[i];
-    }
-
-    for (var i = 0, len=(to - from); i < len; i++) {
-        buf[array.length + i] = array[from + i];
-    }
-    return buf;
-}
-
-function insertIntoArray(array, pos, str) {
-    var ins = stringToUint8Array(str);
-    var buf = new Uint8Array(array.length + ins.length);
-    for (var i = 0; i < pos; i++) {
-        buf[i] = array[i];
-    }
-    for (var i = 0; i < ins.length; i++) {
-        buf[pos+i] = ins[i];
-    }
-    for (var i = pos; i < array.length; i++) {
-        buf[ins.length+i] = array[i];
-    }
-    return buf;
-}
-
-function stringToUint8Array(str) {
-    var buf = new Uint8Array(str.length);
-    for (var i=0, strLen=str.length; i<strLen; i++) {
-        buf[i] = str.charCodeAt(i);
-    }
-    return buf;
-}
-
-function uint8ArrayToString(buf, from, to) {
-    if(typeof from !== 'undefined' && typeof to !== 'undefined') {
-        var s = '';
-        for (var i=from; i<to; i++) {
-            s = s + String.fromCharCode(buf[i]);
-        }
-        return s;
-    }
-    return String.fromCharCode.apply(null, buf);
-}
-
-
-
-function findFreeXrefNr(xrefEntries, used) {
-    used = typeof used !== 'undefined' ?  used : [];
-    var inc = used.length;
-
-    for (var i=1;i<xrefEntries.length;i++) {
-
-        var index = used.indexOf(i);
-        var entry = xrefEntries[""+i];
-        if(index === -1 && (typeof entry === 'undefined' || entry.free)) {
-            return i;
-        }
-        if(index !== -1) {
-            inc--;
-        }
-    }
-    return xrefEntries.length + inc;
-}
-
-function find(uint8, needle, start, limit) {
-    start = typeof start !== 'undefined' ? start : 0;
-    limit = typeof limit !== 'undefined' ? limit : Number.MAX_SAFE_INTEGER;
-
-    var search = stringToUint8Array(needle);
-    var match = 0;
-
-    for(var i=start;i<uint8.length && i<limit;i++) {
-        if(uint8[i] === search[match]) {
-            match++;
-        } else {
-            match = 0;
-            if(uint8[i] === search[match]) {
-                match++;
-            }
-        }
-
-        if(match === search.length) {
-            return (i + 1) - match;
-        }
-    }
-    return -1;
-}
-        
-function findBackwards(uint8, needle, start, limit) {
-    start = typeof start !== 'undefined' ? start : uint8.length;
-    limit = typeof limit !== 'undefined' ? limit : Number.MAX_SAFE_INTEGER;
-
-    var search = stringToUint8Array(needle);
-    var match = search.length - 1;
-
-    for(var i=start;i>=0 && i<limit;i--) {
-        if(uint8[i] === search[match]) {
-            match--;
-        } else {
-            match = search.length - 1;
-            if(uint8[i] === search[match]) {
-                match--;
-            }
-        }
-
-        if(match === 0) {
-            return i - 1;
-        }
-    }
-    return -1;
-}
-        
-function strHex(s) {
-    var a = "";
-    for( var i=0; i<s.length; i++ ) {
-        a = a + pad2(s.charCodeAt(i).toString(16));
-    }
-    return a;
-}
-
-
-async function sha256(array) {
-
-    const cryptoLib = getCrypto();
-    const digestTmpBuf = await cryptoLib.digest({ name: "SHA-256" }, array);
-    const digestTmpArray = new Uint8Array(digestTmpBuf);
-    const digestTmpStr = uint8ArrayToString(digestTmpArray);
-    const sha256Hex = strHex(digestTmpStr);
-    return sha256Hex;
-}
-
-function isSigInRoot(pdf) {
-  if (!pdf.acroForm) {
-    return false;
-  }
-    return pdf.acroForm.get('SigFlags') === 3;
-}
-
-function updateXrefOffset(xref, offset, offsetDelta) {
-    for(var i in xref.entries) {
-        if(xref.entries[i].offset >= offset) {
-            xref.entries[i].offset += offsetDelta;
-        }
-    }
-    for(var i in xref.xrefBlocks) {
-        if(xref.xrefBlocks[i] >= offset) {
-            xref.xrefBlocks[i]  += offsetDelta;
-        }
-    }
-}
-
-function updateXrefBlocks(xrefBlocks, offset, offsetDelta) {
-    for(var i in xrefBlocks) {
-        if(xrefBlocks[i].start >= offset) {
-            xrefBlocks[i].start += offsetDelta;
-        }
-        if(xrefBlocks[i].end >= offset) {
-            xrefBlocks[i].end += offsetDelta;
-        }
-    }
-}
-
-function updateOffset(pos, offset, offsetDelta) {
-    if(pos >= offset) {
-        return pos + offsetDelta;
-    }
-    return pos;
-}
-
-function round256(x) {
-    return (Math.ceil(x/256)*256) - 1;
-}
-
-/**
- * (D:YYYYMMDDHHmmSSOHH'mm)
- * e.g. (D:20151210164400+01'00')
- * where:
- * YYYY shall be the year
- * MM shall be the month (01–12)
- * DD shall be the day (01–31)
- * HH shall be the hour (00–23)
- * mm shall be the minute (00–59)
- * SS shall be the second (00–59)
- * O shall be the relationship of local time to Universal Time (UT), and shall be denoted by one of the characters PLUS SIGN (U+002B) (+), HYPHEN-MINUS (U+002D) (-), or LATIN CAPITAL LETTER Z (U+005A) (Z) (see below)
- * HH followed by APOSTROPHE (U+0027) (') shall be the absolute value of the offset from UT in hours (00–23)
- * mm shall be the absolute value of the offset from UT in minutes (00–59)
- */
-function now(date) {
-    //date = typeof date !== 'undefined' ? date : new Date();
-    var yyyy = date.getFullYear().toString();
-    var MM = pad2(date.getMonth() + 1);
-    var dd = pad2(date.getDate());
-    var hh = pad2(date.getHours());
-    var mm = pad2(date.getMinutes());
-    var ss = pad2(date.getSeconds());
-    return yyyy + MM + dd+  hh + mm + ss + createOffset(date);
-}
-
-function createOffset(date) {
-    var sign = (date.getTimezoneOffset() > 0) ? "-" : "+";
-    var offset = Math.abs(date.getTimezoneOffset());
-    var hours = pad2(Math.floor(offset / 60));
-    var minutes = pad2(offset % 60);
-    return sign + hours + "'" + minutes;
-}
-
-async function newSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey) {
-    try {
-      // {annotEntry} is the ref to the annot widget. If we enlarge the array, make sure all the offsets
-      // after the modification will be updated -> xref table and startxref
-      var annotEntry = findFreeXrefNr(pdf.xref.entries);
-      // we'll store all the modifications we make, as we need to adjust the offset in the PDF
-      var offsetForm = find(pdf.stream.bytes, '<<', root.offset, rootSuccessor.offset) + 2;
-      //first we need to find the root element and add the following:
-      //
-      // /AcroForm<</Fields[{annotEntry} 0 R] /SigFlags 3>>
-      //
-      var appendAcroForm = '/AcroForm<</Fields[' + annotEntry + ' 0 R] /SigFlags 3>>';
-      //before we insert the acroform, we find the right place for annotentry
-
-      //we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Contents[
-      var pages = pdf.catalog.catDict.get('Pages');
-      //get first page, we have hidden sig, so don't bother
-      var ref = pages.get('Kids')[0];
-      var xref = pdf.xref.fetch(ref);
-      var offsetContentEnd = xref.get('#Contents_offset');
-      //we now search backwards, this is safe as we don't expect user content here
-      var offsetContent = findBackwards(pdf.stream.bytes, '/Contents', offsetContentEnd);
-      var appendAnnots = '/Annots[' + annotEntry + ' 0 R]\n ';
-
-      //now insert string into stream
-      var array = insertIntoArray(pdf.stream.bytes, offsetForm, appendAcroForm);
-      //recalculate the offsets in the xref table, only update those that are affected
-      updateXrefOffset(pdf.xref, offsetForm, appendAcroForm.length);
-      offsetContent = updateOffset(offsetContent, offsetForm, appendAcroForm.length);
-
-      var array = insertIntoArray(array, offsetContent, appendAnnots);
-      updateXrefOffset(pdf.xref, offsetContent, appendAnnots.length);
-      offsetContent = -1; //not needed anymore, don't update when offset changes
-
-      //Then add to the next free object (annotEntry)
-      //add right before the xref table or stream
-      //if its a table, place element before the xref table
-      //
-      // sigEntry is the ref to the signature content. Next we need the signature object
-      var sigEntry = findFreeXrefNr(pdf.xref.entries, [annotEntry]);
-
-      //
-      // {annotEntry} 0 obj
-      // <</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature)/V Y 0 R>>
-      // endobj
-      //
-      var append = annotEntry + ' 0 obj\n<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature' + annotEntry + ')/V ' + sigEntry + ' 0 R>>\nendobj\n\n';
-
-      // we want the offset just before the last xref table or entry
-      var blocks = findXrefBlocks(pdf.xref.xrefBlocks);
-      var offsetAnnot = blocks[0].start;
-      array = insertIntoArray(array, offsetAnnot, append);
-      //no updateXrefOffset, as the next entry will be following
-
-      //
-      // {sigEntry} 0 obj
-      // <</Contents <0481801e6d931d561563fb254e27c846e08325570847ed63d6f9e35 ... b2c8788a5>
-      // /Type/Sig/SubFilter/adbe.pkcs7.detached/Location(Ghent)/M(D:20120928104114+02'00')
-      // /ByteRange [A B C D]/Filter/Adobe.PPKLite/Reason(Test)/ContactInfo()>>
-      // endobj
-      //
-
-      //the next entry goes below the above
-      var offsetSig = offsetAnnot + append.length;
-
-      // Both {annotEntry} and {sigEntry} objects need to be added to the last xref table. The byte range needs
-      // to be adjusted. Since the signature will always be in a gap, use first an empty sig
-      // to check the size, add ~25% size, then calculate the signature and place in the empty
-      // space.
-      var start = sigEntry + ' 0 obj\n<</Contents <';
-      var dummy = await sign_pki(signingCert, certificateChain, privateKey, stringToUint8Array('A'), date);
-      //TODO: Adobe thinks its important to have the right size, no idea why this is the case
-      var crypto = new Array(round256(dummy.length * 2)).join('0');
-      var middle = '>\n/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:' + now(date) + '\')\n/ByteRange ';
-      var byteRange = '[0000000000 0000000000 0000000000 0000000000]';
-      var end = '/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>\nendobj\n\n';
-      //all together
-      var append2 = start + crypto + middle + byteRange + end;
-      var offsetByteRange = start.length + crypto.length + middle.length;
-
-      array = insertIntoArray(array, offsetSig, append2);
-      updateXrefOffset(pdf.xref, offsetAnnot, append2.length + append.length);
-
-      //find the xref tables, remove them and also the EOF, as we'll write a new table
-      var xrefBlocks = findXrefBlocks(pdf.xref.xrefBlocks);
-
-      for (var i in xrefBlocks) {
-        var oldSize = array.length;
-        array = removeFromArray(array, xrefBlocks[i].start, xrefBlocks[i].end);
-        var length = array.length - oldSize;
-        updateXrefOffset(pdf.xref, xrefBlocks[i].start, length);
-
-        //check for %%EOF and remove it as well
-        var offsetEOF = find(array, '%%EOF', xrefBlocks[i].start, xrefBlocks[i].start + 20);
-        if (offsetEOF > 0) {
-          var lengthEOF = '%%EOF'.length;
-          array = removeFromArray(array, offsetEOF, offsetEOF + lengthEOF);
-          updateXrefOffset(pdf.xref, offsetEOF, -lengthEOF);
-          updateXrefBlocks(xrefBlocks, offsetEOF, -lengthEOF);
-          offsetAnnot = updateOffset(offsetAnnot, offsetEOF, -lengthEOF);
-          offsetSig = updateOffset(offsetSig, offsetEOF, -lengthEOF);
-        }
-        updateXrefBlocks(xrefBlocks, xrefBlocks[i].start, length);
-        offsetAnnot = updateOffset(offsetAnnot, xrefBlocks[i].start, length);
-        offsetSig = updateOffset(offsetSig, xrefBlocks[i].start, length);
-      }
-
-      var sha256Hex = await sha256(array);
-
-      //add the new entries to the xref
-      pdf.xref.entries[annotEntry] = {offset: offsetAnnot, gen: 0, free: false};
-      pdf.xref.entries[sigEntry] = {offset: offsetSig, gen: 0, free: false};
-
-      var xrefTable = createXrefTable(pdf.xref.entries);
-      //also empty entries count as in the PDF spec, page 720 (example)
-      xrefTable += createTrailer(pdf.xref.topDict, array.length, sha256Hex, pdf.xref.entries.length);
-      array = insertIntoArray(array, array.length, xrefTable);
-
-      //since we consolidate, no prev! [adjust /Prev -> rawparsing + offset]
-      var from1 = 0;
-      var to1 = offsetSig + start.length;
-      var from2 = to1 + crypto.length;
-      var to2 = (array.length - from2) - 1;
-      var byteRange = '[' + pad10(from1) + ' ' + pad10(to1 - 1) + ' ' + pad10(from2 + 1) + ' ' + pad10(to2) + ']';
-      array = updateArray(array, (offsetSig + offsetByteRange), byteRange);
-      var data = removeFromArray(array, to1 - 1, from2 + 1);
-      var crypto2 = await sign_pki(signingCert, certificateChain, privateKey, data.buffer, date);
-      array = updateArray(array, to1, crypto2);
-      return array;
-    } catch (err) {
-      throw new Error('Error creating new signature in PDF: ' + err);
-    }
-}
-
-async function appendSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey) {
-    try {
-      //copy root and the entry with contents to the end
-      var startRoot = pdf.stream.bytes.length + 1;
-
-      var array = copyToEnd(pdf.stream.bytes, root.offset - 1, rootSuccessor.offset);
-
-      //since we signed the first one, we know how the pdf has to look like:
-      var offsetAcroForm = find(array, '/AcroForm<</Fields', startRoot);
-      var endOffsetAcroForm = find(array, ']', offsetAcroForm);
-
-      var annotEntry = findFreeXrefNr(pdf.xref.entries);
-      var sigEntry = findFreeXrefNr(pdf.xref.entries, [annotEntry]);
-
-      var appendAnnot = ' ' + annotEntry + ' 0 R';
-      array = insertIntoArray(array, endOffsetAcroForm, appendAnnot);
-
-      //we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Annots
-      var pages = pdf.catalog.catDict.get('Pages');
-      //get first page, we have hidden sig, so don't bother
-      var contentRef = pages.get('Kids')[0];
-      var xref = pdf.xref.fetch(contentRef);
-      var offsetAnnotEnd = xref.get('#Annots_offset');
-      //we now search ], this is safe as we signed it previously
-      var endOffsetAnnot = find(array, ']', offsetAnnotEnd);
-      var xrefEntry = pdf.xref.getEntry(contentRef.num);
-      var xrefEntrySuccosser = findSuccessorEntry(pdf.xref.entries, xrefEntry);
-      var offsetAnnotRelative = endOffsetAnnot - xrefEntrySuccosser.offset;
-      var startContent = array.length;
-      array = copyToEnd(array, xrefEntry.offset, xrefEntrySuccosser.offset);
-      array = insertIntoArray(array, array.length + offsetAnnotRelative, appendAnnot);
-
-      var startAnnot = array.length;
-      var append = annotEntry + ' 0 obj\n<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature' + annotEntry + ')/V ' + sigEntry + ' 0 R>>\nendobj\n\n';
-      array = insertIntoArray(array, startAnnot, append);
-
-      var startSig = array.length;
-      var start = sigEntry + ' 0 obj\n<</Contents <';
-      var dummy = await sign_pki(signingCert, certificateChain, privateKey, stringToUint8Array('A'), date);
-      //TODO: Adobe thinks its important to have the right size, no idea why this is the case
-      var crypto = new Array(round256(dummy.length * 2)).join('0');
-      var middle = '>\n/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:' + now(date) + '\')\n/ByteRange ';
-      var byteRange = '[0000000000 0000000000 0000000000 0000000000]';
-      var end = '/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>\nendobj\n\n';
-      //all together
-      var append2 = start + crypto + middle + byteRange + end;
-      array = insertIntoArray(array, startSig, append2);
-
-      var sha256Hex = await sha256(array);
-
-      var prev = pdf.xref.xrefBlocks[0];
-      var startxref = array.length;
-      var xrefEntries = [];
-      xrefEntries[0] = {offset: 0, gen: 65535, free: true};
-      xrefEntries[pdf.xref.topDict.getRaw('Root').num] = {offset: startRoot, gen: 0, free: false};
-      xrefEntries[contentRef.num] = {offset: startContent, gen: 0, free: false};
-      xrefEntries[annotEntry] = {offset: startAnnot, gen: 0, free: false};
-      xrefEntries[sigEntry] = {offset: startSig, gen: 0, free: false};
-      var xrefTable = createXrefTableAppend(xrefEntries);
-      xrefTable += createTrailer(pdf.xref.topDict, startxref, sha256Hex, xrefEntries.length, prev);
-      array = insertIntoArray(array, array.length, xrefTable);
-
-      var from1 = 0;
-      var to1 = startSig + start.length;
-      var from2 = to1 + crypto.length;
-      var to2 = (array.length - from2) - 1;
-      var byteRange = '[' + pad10(from1) + ' ' + pad10(to1 - 1) + ' ' + pad10(from2 + 1) + ' ' + pad10(to2) + ']';
-
-      array = updateArray(array, from2 + middle.length, byteRange);
-      //now sign from1-to1 / from2-to2 and update byterange
-
-      var data = removeFromArray(array, to1 - 1, from2 + 1);
-      var crypto2 = await sign_pki(signingCert, certificateChain, privateKey, data.buffer, date);
-      array = updateArray(array, to1, crypto2);
-      return array;
-    } catch (err) {
-      throw new Error('Error appending signature in PDF: ' + err);
-    }
-}
-
-function loadPdf(pdfArray) {
-    try {
-        var pdf = new pdfjsCoreDocument.PDFDocument(false, pdfArray, '');
-        pdf.parseStartXRef();
-        pdf.parse();
-        return pdf;
-    }
-    catch(err) {
-      throw new Error('Error parsing PDF: ' + err);
-    }
-}
-
-
-//data must be Uint8Array
-async function sign_pki(signingCert, certificateChain, privateKey, data, date) {
-  console.log("Calling sign_pki");
-
-    const crypto = getCrypto();
-
-    //date = typeof date !== 'undefined' ?  date : new Date();
-
-    const hashAlg = "SHA-256";
-
-    const digest = await crypto.digest({ name: hashAlg }, data);
-
-    const signedAttr = [];
-
-    signedAttr.push(new Attribute({
-        type: "1.2.840.113549.1.9.3",
-        values: [
-            new ObjectIdentifier({ value: "1.2.840.113549.1.7.1" })
-        ]
-    })); // contentType
-
-    signedAttr.push(new Attribute({
-        type: "1.2.840.113549.1.9.4",
-        values: [
-            new OctetString({ valueHex: digest })
-        ]
-    })); // messageDigest
-
-    signedAttr.push(new Attribute({
-        type: "1.2.840.113549.1.9.5",
-        values: [
-            new UTCTime({ valueDate: date })
-        ]
-    })); // signingTime
-
-    const cmsSignedSimpl = new SignedData({
-        version: 1,
-        encapContentInfo: new EncapsulatedContentInfo({
-            eContentType: "1.2.840.113549.1.7.1" // "data" content type
-        }),
-        signerInfos: [
-            new SignerInfo({
-                version: 1,
-                sid: new IssuerAndSerialNumber({
-                    issuer: signingCert.issuer,
-                    serialNumber: signingCert.serialNumber
-                }),
-                signedAttrs: new SignedAndUnsignedAttributes({
-                    type: 0,
-                    attributes: signedAttr
-                })
-            })
-        ],
-        certificates: certificateChain
-    });
-
-    const signatureBuffer = await cmsSignedSimpl.sign(privateKey, 0, hashAlg, data.buffer);
-
-    const cmsSignedSchema = cmsSignedSimpl.toSchema(true);
-
-    const cmsContentSimp = new ContentInfo({
-        contentType: "1.2.840.113549.1.7.2",
-        content: cmsSignedSchema
-    });
-
-    const _cmsSignedSchema = cmsContentSimp.toSchema();
-
-    //region Make length of some elements in "indefinite form"
-    _cmsSignedSchema.lenBlock.isIndefiniteForm = true;
-
-    const block1 = _cmsSignedSchema.valueBlock.value[1];
-    block1.lenBlock.isIndefiniteForm = true;
-
-    const block2 = block1.valueBlock.value[0];
-    block2.lenBlock.isIndefiniteForm = true;
-
-    //endregion
-
-    let cmsSignedBuffer = _cmsSignedSchema.toBER(false);
-
-    const cmsSignedArray = new Uint8Array(cmsSignedBuffer);
-    const cmsSignedString = uint8ArrayToString(cmsSignedArray);
-    const hex = strHex(cmsSignedString);
-    return hex;
-}
-
-
-async function signPdfObjects (pdfRaw, signingCert, certificateChain, privateKey, date) {
-  console.log("Calling signPdfObjects");
-
-    date = ((typeof date !== 'undefined') && (date !== null)) ? date : new Date();
-
-    if (pdfRaw instanceof Buffer) {
-      pdfRaw = new Uint8Array(pdfRaw);
-    } else
-    if(pdfRaw instanceof ArrayBuffer) {
-        pdfRaw = new Uint8Array(pdfRaw);
-    }
-  console.log("Calling loadPdf");
-    var pdf = loadPdf(pdfRaw);
-  console.log("Calling findRootEntry");
-    var root = findRootEntry(pdf.xref);
-  console.log("Calling findSuccessorEntry");
-    var rootSuccessor = findSuccessorEntry(pdf.xref.entries, root);
-  console.log("Calling isSigInRoot");
-    if(!isSigInRoot(pdf)) {
-      console.log("Calling newSig");
-        return await newSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey);
-    } else {
-      console.log("Calling appendSig");
-        return await appendSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey);
-    }
-}
-
-
-export async function signPdf(pdfRaw, signingCert, certificateChain, privateKey) {
-
-  console.log("Calling signPdf");
-
-  const signingCertObj = parseCertificate(signingCert);
-  const certificateChainObj = [];
-  certificateChainObj[0] = parseCertificate(signingCert);
-  for (let i = 0; i < certificateChain.length; i++) {
-      certificateChainObj[i + 1] = parseCertificate(certificateChain[i])
-  }
-
-  let privateKeyDecoded;
-    try {
-      console.log("Calling parsePrivateKey");
-      privateKeyDecoded = await parsePrivateKey(privateKey);
-    } catch (e) {
-      console.log("Error decoding private key: ", e);
-      throw new Error('Error decoding private key: ' + e);
-    }
-
-    return await signPdfObjects(pdfRaw, signingCertObj, certificateChainObj, privateKeyDecoded);
+/*
+ * Based on PDFSign v1.0.0
+ * https://github.com/Communication-Systems-Group/pdfsign.js
+ *
+ * Copyright 2015, Thomas Bocek, University of Zurich
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+import {
+    ObjectIdentifier,
+    UTCTime,
+    OctetString
+} from 'asn1js';
+
+import {
+    ContentInfo,
+    SignedData,
+    Attribute,
+    SignerInfo,
+    IssuerAndSerialNumber,
+    SignedAndUnsignedAttributes,
+    EncapsulatedContentInfo,
+    getCrypto
+} from 'pkijs';
+
+import {
+    parseCertificate,
+    parsePrivateKey,
+} from './signingUtilities';
+
+//keep this import to include the patched version of pdfjs library
+import {PDFJS} from "../lib/pdfjs.parser.js";
+
+
+function createXrefTable(xrefEntries) {
+    xrefEntries = sortOnKeys(xrefEntries);
+    var retVal ='xref\n';
+    var last = -2;
+    for(var i in xrefEntries) {
+        i = parseInt(i);
+        if(typeof xrefEntries[i].offset === 'undefined') { continue; }
+        retVal += calcFlow(i, last, xrefEntries);
+        var offset = xrefEntries[i].offset;
+        retVal += pad10(offset)+' '+pad5(xrefEntries[i].gen)+' '+(xrefEntries[i].free?'f':'n')+' \n';
+        last = i;
+    }
+    return retVal;
+}
+
+function calcFlow(i, last, xrefEntries) {
+    if(last + 1 === i) {return '';}
+    var count = 1;
+    while(typeof xrefEntries[(i+count)] !== 'undefined'
+            && typeof xrefEntries[(i+count)].offset !== 'undefined') {count ++;}
+    return i + ' '+count+'\n';
+}
+
+function createTrailer(topDict, startxref, sha256Hex, size, prev) {
+    var retVal ='trailer <<\n';
+    retVal +='  /Size '+(size)+'\n';
+    var refRoot = topDict.getRaw('Root');
+    if(typeof refRoot !== 'undefined') {
+        retVal +='  /Root '+refRoot.num+' '+refRoot.gen+' R\n';
+    }
+    var refInfo = topDict.getRaw('Info');
+    if(typeof refInfo !== 'undefined') {
+        retVal +='  /Info '+refInfo.num+' '+refInfo.gen+' R\n';
+    }
+    retVal +='  /ID [<'+sha256Hex.substring(0,32)+'><'+sha256Hex.substring(32,64)+'>]\n';
+    if(typeof prev !== 'undefined' ) {
+        retVal +='  /Prev '+prev+'\n';
+    }
+    retVal +='>>\n';
+    retVal +='startxref\n';
+    retVal +=startxref + '\n';
+    retVal +='%%EOF\n';
+    return retVal;
+}
+
+function createXrefTableAppend(xrefEntries) {
+    xrefEntries = sortOnKeys(xrefEntries);
+
+    var retVal ='xref\n';
+    var last = -2;
+    for(var i in xrefEntries) {
+        i = parseInt(i);
+        if(typeof xrefEntries[i].offset === 'undefined') { continue; }
+        retVal += calcFlow(i, last, xrefEntries);
+        var offset = xrefEntries[i].offset;
+        retVal += pad10(offset)+' '+pad5(xrefEntries[i].gen)+' '+(xrefEntries[i].free?'f':'n')+' \n';
+        last = i;
+    }
+    return retVal;
+}
+
+//http://stackoverflow.com/questions/10946880/sort-a-dictionary-or-whatever-key-value-data-structure-in-js-on-word-number-ke
+function sortOnKeys(dict) {
+    var sorted = [];
+    for(var key in dict) {
+        sorted[sorted.length] = key;
+    }
+    sorted.sort();
+
+    var tempDict = {};
+    for(var i = 0; i < sorted.length; i++) {
+        tempDict[sorted[i]] = dict[sorted[i]];
+    }
+
+    return tempDict;
+}
+
+function removeFromArray(array, from, to) {
+    var cutlen = to - from;
+    var buf = new Uint8Array(array.length - cutlen);
+
+    for (var i = 0; i < from; i++) {
+        buf[i] = array[i];
+    }
+    for (var i = to, len = array.length; i < len; i++) {
+        buf[i-cutlen] = array[i];
+    }
+    return buf;
+}
+        
+function findXrefBlocks(xrefBlocks) {
+    var num = xrefBlocks.length / 2;
+    var retVal = [];
+    for (var i=0;i<num;i++) {
+        retVal.push({start: xrefBlocks[i], end: xrefBlocks[i+num]});
+    }
+    return retVal;
+}
+
+function convertUint8ArrayToBinaryString(u8Array) {
+    var i, len = u8Array.length, b_str = "";
+    for (i=0; i<len; i++) {
+        b_str += String.fromCharCode(u8Array[i]);
+    }
+    return b_str;
+}
+
+function arrayObjectIndexOf(array, start, end, orig) {
+    for(var i = 0, len = array.length; i < len; i++) {
+        if ((array[i].start === start) && (array[i].end === end) && (array[i].orig === orig)) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+function pad10(num) {
+    var s = "000000000" + num;
+    return s.substr(s.length-10);
+}
+
+function pad5(num) {
+    var s = "0000" + num;
+    return s.substr(s.length-5);
+}
+
+function pad2(num) {
+    var s = "0" + num;
+    return s.substr(s.length-2);
+}
+
+function findRootEntry(xref) {
+        var rootNr = xref.root.objId.substring(0, xref.root.objId.length - 1);
+        return xref.entries[rootNr];
+}
+        
+function findSuccessorEntry(xrefEntries, current) {
+    //find it first
+    var currentOffset = current.offset;
+    var currentMin = Number.MAX_SAFE_INTEGER;
+    var currentMinIndex = -1;
+    for(var i in xrefEntries) {
+        if(xrefEntries[i].offset > currentOffset) {
+            if(xrefEntries[i].offset < currentMin) {
+                currentMin = xrefEntries[i].offset;
+                currentMinIndex = i;
+            }
+        }
+    }
+    if(currentMinIndex === -1) {
+        return current;
+    }
+    return xrefEntries[currentMinIndex];
+}
+
+function updateArray(array, pos, str) {
+    var upd = stringToUint8Array(str);
+    for (var i = 0, len=upd.length; i < len; i++) {
+        array[i+pos] = upd[i];
+    }
+    return array;
+}
+
+function copyToEnd(array, from, to) {
+    var buf = new Uint8Array(array.length + (to - from));
+    for (var i = 0, len=array.length; i < len; i++) {
+        buf[i] = array[i];
+    }
+
+    for (var i = 0, len=(to - from); i < len; i++) {
+        buf[array.length + i] = array[from + i];
+    }
+    return buf;
+}
+
+function insertIntoArray(array, pos, str) {
+    var ins = stringToUint8Array(str);
+    var buf = new Uint8Array(array.length + ins.length);
+    for (var i = 0; i < pos; i++) {
+        buf[i] = array[i];
+    }
+    for (var i = 0; i < ins.length; i++) {
+        buf[pos+i] = ins[i];
+    }
+    for (var i = pos; i < array.length; i++) {
+        buf[ins.length+i] = array[i];
+    }
+    return buf;
+}
+
+function stringToUint8Array(str) {
+    var buf = new Uint8Array(str.length);
+    for (var i=0, strLen=str.length; i<strLen; i++) {
+        buf[i] = str.charCodeAt(i);
+    }
+    return buf;
+}
+
+function uint8ArrayToString(buf, from, to) {
+    if(typeof from !== 'undefined' && typeof to !== 'undefined') {
+        var s = '';
+        for (var i=from; i<to; i++) {
+            s = s + String.fromCharCode(buf[i]);
+        }
+        return s;
+    }
+    return String.fromCharCode.apply(null, buf);
+}
+
+
+
+function findFreeXrefNr(xrefEntries, used) {
+    used = typeof used !== 'undefined' ?  used : [];
+    var inc = used.length;
+
+    for (var i=1;i<xrefEntries.length;i++) {
+
+        var index = used.indexOf(i);
+        var entry = xrefEntries[""+i];
+        if(index === -1 && (typeof entry === 'undefined' || entry.free)) {
+            return i;
+        }
+        if(index !== -1) {
+            inc--;
+        }
+    }
+    return xrefEntries.length + inc;
+}
+
+function find(uint8, needle, start, limit) {
+    start = typeof start !== 'undefined' ? start : 0;
+    limit = typeof limit !== 'undefined' ? limit : Number.MAX_SAFE_INTEGER;
+
+    var search = stringToUint8Array(needle);
+    var match = 0;
+
+    for(var i=start;i<uint8.length && i<limit;i++) {
+        if(uint8[i] === search[match]) {
+            match++;
+        } else {
+            match = 0;
+            if(uint8[i] === search[match]) {
+                match++;
+            }
+        }
+
+        if(match === search.length) {
+            return (i + 1) - match;
+        }
+    }
+    return -1;
+}
+        
+function findBackwards(uint8, needle, start, limit) {
+    start = typeof start !== 'undefined' ? start : uint8.length;
+    limit = typeof limit !== 'undefined' ? limit : Number.MAX_SAFE_INTEGER;
+
+    var search = stringToUint8Array(needle);
+    var match = search.length - 1;
+
+    for(var i=start;i>=0 && i<limit;i--) {
+        if(uint8[i] === search[match]) {
+            match--;
+        } else {
+            match = search.length - 1;
+            if(uint8[i] === search[match]) {
+                match--;
+            }
+        }
+
+        if(match === 0) {
+            return i - 1;
+        }
+    }
+    return -1;
+}
+        
+function strHex(s) {
+    var a = "";
+    for( var i=0; i<s.length; i++ ) {
+        a = a + pad2(s.charCodeAt(i).toString(16));
+    }
+    return a;
+}
+
+
+async function sha256(array) {
+
+    const cryptoLib = getCrypto();
+    const digestTmpBuf = await cryptoLib.digest({ name: "SHA-256" }, array);
+    const digestTmpArray = new Uint8Array(digestTmpBuf);
+    const digestTmpStr = uint8ArrayToString(digestTmpArray);
+    const sha256Hex = strHex(digestTmpStr);
+    return sha256Hex;
+}
+
+function isSigInRoot(pdf) {
+  if (typeof pdf.acroForm === 'undefined') {
+    return false;
+  }
+
+  if (!pdf.acroForm) {
+    return false;
+  }
+
+    return pdf.acroForm.get('SigFlags') === 3;
+}
+
+function updateXrefOffset(xref, offset, offsetDelta) {
+    for(var i in xref.entries) {
+        if(xref.entries[i].offset >= offset) {
+            xref.entries[i].offset += offsetDelta;
+        }
+    }
+    for(var i in xref.xrefBlocks) {
+        if(xref.xrefBlocks[i] >= offset) {
+            xref.xrefBlocks[i]  += offsetDelta;
+        }
+    }
+}
+
+function updateXrefBlocks(xrefBlocks, offset, offsetDelta) {
+    for(var i in xrefBlocks) {
+        if(xrefBlocks[i].start >= offset) {
+            xrefBlocks[i].start += offsetDelta;
+        }
+        if(xrefBlocks[i].end >= offset) {
+            xrefBlocks[i].end += offsetDelta;
+        }
+    }
+}
+
+function updateOffset(pos, offset, offsetDelta) {
+    if(pos >= offset) {
+        return pos + offsetDelta;
+    }
+    return pos;
+}
+
+function round256(x) {
+    return (Math.ceil(x/256)*256) - 1;
+}
+
+/**
+ * (D:YYYYMMDDHHmmSSOHH'mm)
+ * e.g. (D:20151210164400+01'00')
+ * where:
+ * YYYY shall be the year
+ * MM shall be the month (01–12)
+ * DD shall be the day (01–31)
+ * HH shall be the hour (00–23)
+ * mm shall be the minute (00–59)
+ * SS shall be the second (00–59)
+ * O shall be the relationship of local time to Universal Time (UT), and shall be denoted by one of the characters PLUS SIGN (U+002B) (+), HYPHEN-MINUS (U+002D) (-), or LATIN CAPITAL LETTER Z (U+005A) (Z) (see below)
+ * HH followed by APOSTROPHE (U+0027) (') shall be the absolute value of the offset from UT in hours (00–23)
+ * mm shall be the absolute value of the offset from UT in minutes (00–59)
+ */
+function now(date) {
+    //date = typeof date !== 'undefined' ? date : new Date();
+    var yyyy = date.getFullYear().toString();
+    var MM = pad2(date.getMonth() + 1);
+    var dd = pad2(date.getDate());
+    var hh = pad2(date.getHours());
+    var mm = pad2(date.getMinutes());
+    var ss = pad2(date.getSeconds());
+    return yyyy + MM + dd+  hh + mm + ss + createOffset(date);
+}
+
+function createOffset(date) {
+    var sign = (date.getTimezoneOffset() > 0) ? "-" : "+";
+    var offset = Math.abs(date.getTimezoneOffset());
+    var hours = pad2(Math.floor(offset / 60));
+    var minutes = pad2(offset % 60);
+    return sign + hours + "'" + minutes;
+}
+
+async function newSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey) {
+    try {
+      // {annotEntry} is the ref to the annot widget. If we enlarge the array, make sure all the offsets
+      // after the modification will be updated -> xref table and startxref
+      var annotEntry = findFreeXrefNr(pdf.xref.entries);
+      // we'll store all the modifications we make, as we need to adjust the offset in the PDF
+      var offsetForm = find(pdf.stream.bytes, '<<', root.offset, rootSuccessor.offset) + 2;
+      //first we need to find the root element and add the following:
+      //
+      // /AcroForm<</Fields[{annotEntry} 0 R] /SigFlags 3>>
+      //
+      var appendAcroForm = '/AcroForm<</Fields[' + annotEntry + ' 0 R] /SigFlags 3>>';
+      //before we insert the acroform, we find the right place for annotentry
+
+      //we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Contents[
+      var pages = pdf.catalog.catDict.get('Pages');
+      //get first page, we have hidden sig, so don't bother
+      var ref = pages.get('Kids')[0];
+      var xref = pdf.xref.fetch(ref);
+      var offsetContentEnd = xref.get('#Contents_offset');
+      //we now search backwards, this is safe as we don't expect user content here
+      var offsetContent = findBackwards(pdf.stream.bytes, '/Contents', offsetContentEnd);
+      var appendAnnots = '/Annots[' + annotEntry + ' 0 R]\n ';
+
+      //now insert string into stream
+      var array = insertIntoArray(pdf.stream.bytes, offsetForm, appendAcroForm);
+      //recalculate the offsets in the xref table, only update those that are affected
+      updateXrefOffset(pdf.xref, offsetForm, appendAcroForm.length);
+      offsetContent = updateOffset(offsetContent, offsetForm, appendAcroForm.length);
+
+      var array = insertIntoArray(array, offsetContent, appendAnnots);
+      updateXrefOffset(pdf.xref, offsetContent, appendAnnots.length);
+      offsetContent = -1; //not needed anymore, don't update when offset changes
+
+      //Then add to the next free object (annotEntry)
+      //add right before the xref table or stream
+      //if its a table, place element before the xref table
+      //
+      // sigEntry is the ref to the signature content. Next we need the signature object
+      var sigEntry = findFreeXrefNr(pdf.xref.entries, [annotEntry]);
+
+      //
+      // {annotEntry} 0 obj
+      // <</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature)/V Y 0 R>>
+      // endobj
+      //
+      var append = annotEntry + ' 0 obj\n<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature' + annotEntry + ')/V ' + sigEntry + ' 0 R>>\nendobj\n\n';
+
+      // we want the offset just before the last xref table or entry
+      var blocks = findXrefBlocks(pdf.xref.xrefBlocks);
+      var offsetAnnot = blocks[0].start;
+      array = insertIntoArray(array, offsetAnnot, append);
+      //no updateXrefOffset, as the next entry will be following
+
+      //
+      // {sigEntry} 0 obj
+      // <</Contents <0481801e6d931d561563fb254e27c846e08325570847ed63d6f9e35 ... b2c8788a5>
+      // /Type/Sig/SubFilter/adbe.pkcs7.detached/Location(Ghent)/M(D:20120928104114+02'00')
+      // /ByteRange [A B C D]/Filter/Adobe.PPKLite/Reason(Test)/ContactInfo()>>
+      // endobj
+      //
+
+      //the next entry goes below the above
+      var offsetSig = offsetAnnot + append.length;
+
+      // Both {annotEntry} and {sigEntry} objects need to be added to the last xref table. The byte range needs
+      // to be adjusted. Since the signature will always be in a gap, use first an empty sig
+      // to check the size, add ~25% size, then calculate the signature and place in the empty
+      // space.
+      var start = sigEntry + ' 0 obj\n<</Contents <';
+      var dummy = await sign_pki(signingCert, certificateChain, privateKey, stringToUint8Array('A'), date);
+      //TODO: Adobe thinks its important to have the right size, no idea why this is the case
+      var crypto = new Array(round256(dummy.length * 2)).join('0');
+      var middle = '>\n/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:' + now(date) + '\')\n/ByteRange ';
+      var byteRange = '[0000000000 0000000000 0000000000 0000000000]';
+      var end = '/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>\nendobj\n\n';
+      //all together
+      var append2 = start + crypto + middle + byteRange + end;
+      var offsetByteRange = start.length + crypto.length + middle.length;
+
+      array = insertIntoArray(array, offsetSig, append2);
+      updateXrefOffset(pdf.xref, offsetAnnot, append2.length + append.length);
+
+      //find the xref tables, remove them and also the EOF, as we'll write a new table
+      var xrefBlocks = findXrefBlocks(pdf.xref.xrefBlocks);
+
+      for (var i in xrefBlocks) {
+        var oldSize = array.length;
+        array = removeFromArray(array, xrefBlocks[i].start, xrefBlocks[i].end);
+        var length = array.length - oldSize;
+        updateXrefOffset(pdf.xref, xrefBlocks[i].start, length);
+
+        //check for %%EOF and remove it as well
+        var offsetEOF = find(array, '%%EOF', xrefBlocks[i].start, xrefBlocks[i].start + 20);
+        if (offsetEOF > 0) {
+          var lengthEOF = '%%EOF'.length;
+          array = removeFromArray(array, offsetEOF, offsetEOF + lengthEOF);
+          updateXrefOffset(pdf.xref, offsetEOF, -lengthEOF);
+          updateXrefBlocks(xrefBlocks, offsetEOF, -lengthEOF);
+          offsetAnnot = updateOffset(offsetAnnot, offsetEOF, -lengthEOF);
+          offsetSig = updateOffset(offsetSig, offsetEOF, -lengthEOF);
+        }
+        updateXrefBlocks(xrefBlocks, xrefBlocks[i].start, length);
+        offsetAnnot = updateOffset(offsetAnnot, xrefBlocks[i].start, length);
+        offsetSig = updateOffset(offsetSig, xrefBlocks[i].start, length);
+      }
+
+      var sha256Hex = await sha256(array);
+
+      //add the new entries to the xref
+      pdf.xref.entries[annotEntry] = {offset: offsetAnnot, gen: 0, free: false};
+      pdf.xref.entries[sigEntry] = {offset: offsetSig, gen: 0, free: false};
+
+      var xrefTable = createXrefTable(pdf.xref.entries);
+      //also empty entries count as in the PDF spec, page 720 (example)
+      xrefTable += createTrailer(pdf.xref.topDict, array.length, sha256Hex, pdf.xref.entries.length);
+      array = insertIntoArray(array, array.length, xrefTable);
+
+      //since we consolidate, no prev! [adjust /Prev -> rawparsing + offset]
+      var from1 = 0;
+      var to1 = offsetSig + start.length;
+      var from2 = to1 + crypto.length;
+      var to2 = (array.length - from2) - 1;
+      var byteRange = '[' + pad10(from1) + ' ' + pad10(to1 - 1) + ' ' + pad10(from2 + 1) + ' ' + pad10(to2) + ']';
+      array = updateArray(array, (offsetSig + offsetByteRange), byteRange);
+      var data = removeFromArray(array, to1 - 1, from2 + 1);
+      var crypto2 = await sign_pki(signingCert, certificateChain, privateKey, data.buffer, date);
+      array = updateArray(array, to1, crypto2);
+      return array;
+    } catch (err) {
+      throw new Error('Error creating new signature in PDF: ' + err);
+    }
+}
+
+async function appendSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey) {
+    try {
+      //copy root and the entry with contents to the end
+      var startRoot = pdf.stream.bytes.length + 1;
+
+      var array = copyToEnd(pdf.stream.bytes, root.offset - 1, rootSuccessor.offset);
+
+      //since we signed the first one, we know how the pdf has to look like:
+      var offsetAcroForm = find(array, '/AcroForm<</Fields', startRoot);
+      var endOffsetAcroForm = find(array, ']', offsetAcroForm);
+
+      var annotEntry = findFreeXrefNr(pdf.xref.entries);
+      var sigEntry = findFreeXrefNr(pdf.xref.entries, [annotEntry]);
+
+      var appendAnnot = ' ' + annotEntry + ' 0 R';
+      array = insertIntoArray(array, endOffsetAcroForm, appendAnnot);
+
+      //we need to add Annots [x y R] to the /Type /Page section. We can do that by searching /Annots
+      var pages = pdf.catalog.catDict.get('Pages');
+      //get first page, we have hidden sig, so don't bother
+      var contentRef = pages.get('Kids')[0];
+      var xref = pdf.xref.fetch(contentRef);
+      var offsetAnnotEnd = xref.get('#Annots_offset');
+      //we now search ], this is safe as we signed it previously
+      var endOffsetAnnot = find(array, ']', offsetAnnotEnd);
+      var xrefEntry = pdf.xref.getEntry(contentRef.num);
+      var xrefEntrySuccosser = findSuccessorEntry(pdf.xref.entries, xrefEntry);
+      var offsetAnnotRelative = endOffsetAnnot - xrefEntrySuccosser.offset;
+      var startContent = array.length;
+      array = copyToEnd(array, xrefEntry.offset, xrefEntrySuccosser.offset);
+      array = insertIntoArray(array, array.length + offsetAnnotRelative, appendAnnot);
+
+      var startAnnot = array.length;
+      var append = annotEntry + ' 0 obj\n<</F 132/Type/Annot/Subtype/Widget/Rect[0 0 0 0]/FT/Sig/DR<<>>/T(signature' + annotEntry + ')/V ' + sigEntry + ' 0 R>>\nendobj\n\n';
+      array = insertIntoArray(array, startAnnot, append);
+
+      var startSig = array.length;
+      var start = sigEntry + ' 0 obj\n<</Contents <';
+      var dummy = await sign_pki(signingCert, certificateChain, privateKey, stringToUint8Array('A'), date);
+      //TODO: Adobe thinks its important to have the right size, no idea why this is the case
+      var crypto = new Array(round256(dummy.length * 2)).join('0');
+      var middle = '>\n/Type/Sig/SubFilter/adbe.pkcs7.detached/Location()/M(D:' + now(date) + '\')\n/ByteRange ';
+      var byteRange = '[0000000000 0000000000 0000000000 0000000000]';
+      var end = '/Filter/Adobe.PPKLite/Reason()/ContactInfo()>>\nendobj\n\n';
+      //all together
+      var append2 = start + crypto + middle + byteRange + end;
+      array = insertIntoArray(array, startSig, append2);
+
+      var sha256Hex = await sha256(array);
+
+      var prev = pdf.xref.xrefBlocks[0];
+      var startxref = array.length;
+      var xrefEntries = [];
+      xrefEntries[0] = {offset: 0, gen: 65535, free: true};
+      xrefEntries[pdf.xref.topDict.getRaw('Root').num] = {offset: startRoot, gen: 0, free: false};
+      xrefEntries[contentRef.num] = {offset: startContent, gen: 0, free: false};
+      xrefEntries[annotEntry] = {offset: startAnnot, gen: 0, free: false};
+      xrefEntries[sigEntry] = {offset: startSig, gen: 0, free: false};
+      var xrefTable = createXrefTableAppend(xrefEntries);
+      xrefTable += createTrailer(pdf.xref.topDict, startxref, sha256Hex, xrefEntries.length, prev);
+      array = insertIntoArray(array, array.length, xrefTable);
+
+      var from1 = 0;
+      var to1 = startSig + start.length;
+      var from2 = to1 + crypto.length;
+      var to2 = (array.length - from2) - 1;
+      var byteRange = '[' + pad10(from1) + ' ' + pad10(to1 - 1) + ' ' + pad10(from2 + 1) + ' ' + pad10(to2) + ']';
+
+      array = updateArray(array, from2 + middle.length, byteRange);
+      //now sign from1-to1 / from2-to2 and update byterange
+
+      var data = removeFromArray(array, to1 - 1, from2 + 1);
+      var crypto2 = await sign_pki(signingCert, certificateChain, privateKey, data.buffer, date);
+      array = updateArray(array, to1, crypto2);
+      return array;
+    } catch (err) {
+      throw new Error('Error appending signature in PDF: ' + err);
+    }
+}
+
+function loadPdf(pdfArray) {
+    try {
+        var pdf = new pdfjsCoreDocument.PDFDocument(false, pdfArray, '');
+        pdf.parseStartXRef();
+        pdf.parse();
+        return pdf;
+    }
+    catch(err) {
+      throw new Error('Error parsing PDF: ' + err);
+    }
+}
+
+
+//data must be Uint8Array
+async function sign_pki(signingCert, certificateChain, privateKey, data, date) {
+  console.log("Calling sign_pki");
+
+    const crypto = getCrypto();
+
+    //date = typeof date !== 'undefined' ?  date : new Date();
+
+    const hashAlg = "SHA-256";
+
+    const digest = await crypto.digest({ name: hashAlg }, data);
+
+    const signedAttr = [];
+
+    signedAttr.push(new Attribute({
+        type: "1.2.840.113549.1.9.3",
+        values: [
+            new ObjectIdentifier({ value: "1.2.840.113549.1.7.1" })
+        ]
+    })); // contentType
+
+    signedAttr.push(new Attribute({
+        type: "1.2.840.113549.1.9.4",
+        values: [
+            new OctetString({ valueHex: digest })
+        ]
+    })); // messageDigest
+
+    signedAttr.push(new Attribute({
+        type: "1.2.840.113549.1.9.5",
+        values: [
+            new UTCTime({ valueDate: date })
+        ]
+    })); // signingTime
+
+    const cmsSignedSimpl = new SignedData({
+        version: 1,
+        encapContentInfo: new EncapsulatedContentInfo({
+            eContentType: "1.2.840.113549.1.7.1" // "data" content type
+        }),
+        signerInfos: [
+            new SignerInfo({
+                version: 1,
+                sid: new IssuerAndSerialNumber({
+                    issuer: signingCert.issuer,
+                    serialNumber: signingCert.serialNumber
+                }),
+                signedAttrs: new SignedAndUnsignedAttributes({
+                    type: 0,
+                    attributes: signedAttr
+                })
+            })
+        ],
+        certificates: certificateChain
+    });
+
+    const signatureBuffer = await cmsSignedSimpl.sign(privateKey, 0, hashAlg, data.buffer);
+
+    const cmsSignedSchema = cmsSignedSimpl.toSchema(true);
+
+    const cmsContentSimp = new ContentInfo({
+        contentType: "1.2.840.113549.1.7.2",
+        content: cmsSignedSchema
+    });
+
+    const _cmsSignedSchema = cmsContentSimp.toSchema();
+
+    //region Make length of some elements in "indefinite form"
+    _cmsSignedSchema.lenBlock.isIndefiniteForm = true;
+
+    const block1 = _cmsSignedSchema.valueBlock.value[1];
+    block1.lenBlock.isIndefiniteForm = true;
+
+    const block2 = block1.valueBlock.value[0];
+    block2.lenBlock.isIndefiniteForm = true;
+
+    //endregion
+
+    let cmsSignedBuffer = _cmsSignedSchema.toBER(false);
+
+    const cmsSignedArray = new Uint8Array(cmsSignedBuffer);
+    const cmsSignedString = uint8ArrayToString(cmsSignedArray);
+    const hex = strHex(cmsSignedString);
+    return hex;
+}
+
+
+async function signPdfObjects (pdfRaw, signingCert, certificateChain, privateKey, date) {
+  console.log("Calling signPdfObjects");
+
+    date = ((typeof date !== 'undefined') && (date !== null)) ? date : new Date();
+
+    if (pdfRaw instanceof Buffer) {
+      pdfRaw = new Uint8Array(pdfRaw);
+    } else
+    if(pdfRaw instanceof ArrayBuffer) {
+        pdfRaw = new Uint8Array(pdfRaw);
+    }
+  console.log("Calling loadPdf");
+    var pdf = loadPdf(pdfRaw);
+  console.log("Calling findRootEntry");
+    var root = findRootEntry(pdf.xref);
+  console.log("Calling findSuccessorEntry");
+    var rootSuccessor = findSuccessorEntry(pdf.xref.entries, root);
+  console.log("Calling isSigInRoot");
+    if(!isSigInRoot(pdf)) {
+      console.log("Calling newSig");
+        return await newSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey);
+    } else {
+      console.log("Calling appendSig");
+        return await appendSig(pdf, root, rootSuccessor, date, signingCert, certificateChain, privateKey);
+    }
+}
+
+
+export async function signPdf(pdfRaw, signingCert, certificateChain, privateKey) {
+
+  console.log("Calling signPdf");
+
+  const signingCertObj = parseCertificate(signingCert);
+  const certificateChainObj = [];
+  certificateChainObj[0] = parseCertificate(signingCert);
+  for (let i = 0; i < certificateChain.length; i++) {
+      certificateChainObj[i + 1] = parseCertificate(certificateChain[i])
+  }
+
+  let privateKeyDecoded;
+    try {
+      console.log("Calling parsePrivateKey");
+      privateKeyDecoded = await parsePrivateKey(privateKey);
+    } catch (e) {
+      console.log("Error decoding private key: ", e);
+      throw new Error('Error decoding private key: ' + e);
+    }
+
+    return await signPdfObjects(pdfRaw, signingCertObj, certificateChainObj, privateKeyDecoded);
 }
\ No newline at end of file