Giter Club home page Giter Club logo

Comments (46)

vbuch avatar vbuch commented on May 20, 2024 1

I'm planning to make an example with pdf-lib soon. Just need the time for that. Would be nice if you can wait. I don't have a good snippet that I can paste at the moment and it's too much to explain.

from node-signpdf.

vbuch avatar vbuch commented on May 20, 2024

I recently mentioned this here: #37 (comment)
This issue #26 asks the same question.
I'm planning to give an example in the coming days on how this can be done with pdfkit. Not planning on the feature currently for the plain version. I'll get back to this (hopefully) this week.

from node-signpdf.

SnigBhaumik avatar SnigBhaumik commented on May 20, 2024

Thanks for the quick response @vbuch and apologies for raising a duplicate ticket.
However, the referenced issues (#26 and #37 ) does not clearly state how to add the visible widget to show the signature. As you have mentioned, may be this feature will be added in the pdfkit version, and not in the plain version.

Unfortunately we need the same in plain version only. Any clue how to do that?

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

Hi @SnigBhaumik , have you been able to solve this issue? In the /helpers/plainAddPlaceholder/index.js code, each one who signs a pdf uses the /helpers/pdfkitAddPlaceholder.js file to capture a widget and the form and add it as an annotation to the pdf. However, I do not understand how a text should be sent so that it appears visible and a position in the pdf. Do you have any idea how to do it?
Thank you!

from node-signpdf.

vbuch avatar vbuch commented on May 20, 2024

I recently stumbled upon pdf-lib. Check it out. Seems promising. Allows editing pdfs.

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

I am working with the dependency you mention, but when making a second signature with a visible mark, everything is damaged, the signatures disappear, the structure of the pdf must be damaging. That's why I better tried to add the widget since yesterday, but I still can't. The pdf-lib dependency is useful to me before signing, once I do it no longer allows me to edit and re-sign.

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

If I am not mistaken, in the file src/helpers/plainAddPlaceholder/index.js on line 34 a constant pdfKitMock is generated where the widget should be added, the truth does not yet understand what form the widget should have and in what part of the constant, because then on line 85, the createBufferPageWithAnnotation function is called which receives the widget by parameter and is added as an annotation to the PDF. You know what is the form in which it opens and in what part of the constant pdfKitMock?
Thanks for your help.

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

Of course, I have already made an example with pdf-lib and if you want I can fork with a new test for signing with a note, for this I use the drawText function like this: pages[0].drawText (textForPDF, drawOptions) , where:

  • page[0]: This is the first page of the PDF, this variable is chosen with the getPages() function.
  • textForPDF: The text to be inserted.
  • drawOptions: The text options you have to insert in object format.

But I fall into the problem that by signing a second time, after adding the text and signing; The pdf does not recognize the signatures, I think it somehow damages the pdf format.

from node-signpdf.

vbuch avatar vbuch commented on May 20, 2024

The best would be to talk with snippets, so, please create fork and include some test that breaks. You are combining two features: drawing on top of a pdf and incremental updates (for signing). Not really sure if pdf-lib supports incremental updates. Haven't had the chance to work with it.

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

Ook, I am already working on this, what I have identified is that an error is jumping in your code, right in the file /src/helpers/plainAddPlaceholder/readRefTable.test, well it's just an observation!
I hope to upload this test tomorrow.

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

I have made a pull request where I tell you about the problem we were dealing with here. If you can check it please!

from node-signpdf.

vbuch avatar vbuch commented on May 20, 2024

I played with your code just a bit. I think you are making a fundamental mistake. You are trying to add the graphic/text with pdf-lib and then use plainAddPlaceholder. If I was working with pdf-lib, I would've definetely find a way to add the placeholder with it instead of relying on the plain... function. It would make my code cleaner. I will try to implement that as soon as I have the time for it. But if you already do, please give it a try. See what's being done in the pdfkit helper. What I will do is create the same thing with pdf-lib. Once you have the placeholder in place, the signing code will probably just work as it does not go through all the details.

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

You're right, reviewing a little I found this, I hope to implement it, even though I still don't understand it very well, I'm going to try! If I finish it I upload it! Thanks for your help!

from node-signpdf.

vbuch avatar vbuch commented on May 20, 2024

Nice find! Commented there. Hopding/pdf-lib#39 (comment)

from node-signpdf.

SnigBhaumik avatar SnigBhaumik commented on May 20, 2024

I'm planning to make an example with pdf-lib soon. Just need the time for that. Would be nice if you can wait. I don't have a good snippet that I can paste at the moment and it's too much to explain.

Sure @vbuch , will eagerly wait for your example with pdf-lib
Meanwhile, trying our own. Seems pdf-lib might be a better choice than pdfkit, because the later does not support opening and editing existing pdfs.

from node-signpdf.

SnigBhaumik avatar SnigBhaumik commented on May 20, 2024

Nice find! Commented there. Hopding/pdf-lib#39 (comment)

2nd that @therpobinski, nice find. Will try to work on this in parallel. Please keep us posted on any progress!

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

Excellent, so what they propose to me is to create a new file in the helpers called pdflibAddPlaceholder similar to the existing pdfAddPlaceholder but with the pdf-lib library and add the similar placeholder as did @Hopding .. I am in the right thing right?

from node-signpdf.

SnigBhaumik avatar SnigBhaumik commented on May 20, 2024

Excellent, so what they propose to me is to create a new file in the helpers called pdflibAddPlaceholder similar to the existing pdfAddPlaceholder but with the pdf-lib library and add the similar placeholder as did @Hopding .. I am in the right thing right?

Yes, agree with the approach @therpobinski. Am trying to do the same thing. Let us share each other's works and also wait for documentation/example code from @vbuch

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

Ready, I made a breakthrough.
But I have 3 problems for which I have not been able to advance.
I have noticed that the example in this issue: How to obtain / configure Annot or Widget from pdf, is very old, most of the functions no longer exist, so I took care to investigate the pdf-lib library for Find the functions similar to the previous ones. In the majority I have been able to solve them but in 3 observations no.
I would like you to help me with this so that I can add it to this great library.
The observations are the file src/helpers/pdflibAddPlaceholder.js.

  1. The PDFContentStream class has a of function that creates an object with the parameters: PDFDict and an array of PDFOperator, but in the examples I see in the old and new version of the library pdf-lib It does not send any PDFOperator array, but several parameters with drawRectangle, drawText, etc.
  2. In the previous version of pdf-lib there is a class PDFDocument with a function register, this function no longer exists in the new version and when I looked in the library I found a similar one, which is in PDFContext, but when called, the same problem occurs, that the register function does not exist. I don't know how to solve this, since it's the only one that doesn't work.
  3. Near the end of the document, I am creating a const widgetDict, which gets a PDFDict, has a P parameter, which I have taken from the example that names first, but this one has TypeScript programming, and in that if I'm lost, I don't know how to complete that field.

I mean the previous version to the 0.6 version. Now the most current version is the 1.2.1-Master, of pdf-lib.

I think that with those 3 things to solve the pdflibPlaceHolder would work.
In my last commit is what I have named. here
Can you help me solve this problem please?

from node-signpdf.

vbuch avatar vbuch commented on May 20, 2024

I will take a look at the code ASAP. Hopefully this weekend. Sorry to hear the snippetsbare outdated. Maybe the author of pdf-lib will help you if you asked their assistance...? Will get back to you as soon as I have a look.

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

I'm sorry to bother you again.
I could not resolve these 3 observations, send an email to @Hopding, I have not yet received an answer.
Has anyone of you found any solution?

from node-signpdf.

vbuch avatar vbuch commented on May 20, 2024

Sorry for being unreliable and unavailable. I'll try to spend some time on it today.

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

I found some of the documentation of the migration v1.x.x, I hope it helps, although I had already understood when I saw the code.
I have resolved the inconveniences, but when placing the placeholder, the mark does not appear anywhere.
I still think the mark is invisible

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

Hi guys, I comment that I have resolved the conflicts we had, but I realize that it does not work in the same way, somehow it is not incorporating signatureDict, this reference contains the ByteRange, and when I try to sign it, it is not possible because it does not You can find the ByteRange.
I feel that everything I've done doesn't work.
Has anyone been able to advance this?
If you wish, you can see my latest progress here.

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

Ready, finished with all the observations, almost everything was wrong, I had to read the pdf-lib programming carefully, now there are two observations:

  1. When it is to be signed, an error appears that says: Could not find ByteRange placeholder: /ByteRange [0 /********** /********** /**********].
    This error occurs because you are specifically searching for a string /ByteRange [0 / ********** / ********** / **********] , but if you have space, like: /ByteRange [ 0 / ********** / ********** / ********** ], It works , which I will implement a Regex for this problem.

  2. The big problem is that no visible widget has been embedded yet, which makes me think that the PDFContentStrem that I use does not work, somehow I have to solve this problem. Does anyone have any ideas to solve this?

With this we would finish everything, to be able to implement it, The last changes are in the last confirmation of the pull request.

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

Hi, The new placeholder is almost ready, but at the time of signing I have a problem, there is a validation on line 161 of signpdf.js, I do not understand why the variable raw.length is multiplied by 2.
I am printing the lengths of the variables, both the placeholder : 2581 and the raw.length: 1592, I see that the placeholder is larger than the raw, but multiplying it by 2, it happens the error . I think you can help me. @vbuch
Thank you!

from node-signpdf.

vbuch avatar vbuch commented on May 20, 2024

The comment above the line you are referring to says it:

        // placeholderLength represents the length of the HEXified symbols but we're
        // checking the actual lengths.

Since the 0 byte is represented by the 00 hex, you get different numbers in lengths and settings. So if your signature has a raw length of 1592, you would need to set the placeholderLength to 3184 which will match its hexadecimal represenation.

from node-signpdf.

moravcik avatar moravcik commented on May 20, 2024

Hi @therpobinski and others, I had also problem with corrupted PDF after signing, but I have used your code from pdflibAddPlaceholder from PR41 and now first tests looks promising.

I have a fork of node-signpdf rewritten into TypeScript, and with type-checking I have easily found few minor bugs in your code, maybe it helped. I will try to incorporate your PR into my fork.

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

Since the 0 byte is represented by the 00 hex, you get different numbers in lengths and settings. So if your signature has a raw length of 1592, you would need to set the placeholderLength to 3184 which will match its hexadecimal represenation.

I understand that part, but I do not understand why when generating the placeholder with the library [pdfkitAddPlaceholder] (

Contents: Buffer.from(String.fromCharCode(0).repeat(signatureLength)),
) ContentLength x2 does not multiply to complete with zeros so as not to have problems with the length of the RAW.
But I must multiply x2 mi [ContentLength] (
PDFHexString.of('0'.repeat(signatureLength))
) to not have problems with the RAW.
Could you help me with this! Thank you. @vbuch

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

Hi @therpobinski and others, I had also problem with corrupted PDF after signing, but I have used your code from pdflibAddPlaceholder from PR41 and now first tests looks promising.

I have a fork of node-signpdf rewritten into TypeScript, and with type-checking I have easily found few minor bugs in your code, maybe it helped. I will try to incorporate your PR into my fork.

Excellent, do not forget to work since the last commit, because from one commit to another there are many changes.
Luck!

from node-signpdf.

vbuch avatar vbuch commented on May 20, 2024

Since the 0 byte is represented by the 00 hex, you get different numbers in lengths and settings. So if your signature has a raw length of 1592, you would need to set the placeholderLength to 3184 which will match its hexadecimal represenation.

I understand that part, but I do not understand why when generating the placeholder with the library [pdfkitAddPlaceholder] (

Contents: Buffer.from(String.fromCharCode(0).repeat(signatureLength)),

) ContentLength x2 does not multiply to complete with zeros so as not to have problems with the length of the RAW.
But I must multiply x2 mi [ContentLength] (

PDFHexString.of('0'.repeat(signatureLength))

) to not have problems with the RAW.
Could you help me with this! Thank you. @vbuch

You may be right. It could me more consistent if the twonworked the same way. But actually mybthinking when writing this was that I need tonplace 100 zeros in there. When I first wrote it I didn't realize that this would then be replaced by 50 hexed bytes. So there are two ways of thinking:

  1. Adding the placeholder is a helper so it should respect the byte size
  2. (the one applied) The addplaceholder method works with string and not bytes. It sais placeholderLenght and not placeholder bytes.

But either way that should nlt be a problem for you once you understand it. For a next major version we could change that.

In addition: my approach on giving this setting a value is the following:

  1. I set the value to something big sso i can sign a pdf. 20000 for example.
  2. I generate a signed document
  3. I open the output document in a text editor and see the actual size of the signature. That is bytes x 2.
  4. Use this value instead of 20000 for next generations.

This easens the flow from the user perspective and adds a tiny bit of confusion in the source wwhich on the other hand is well documented.

from node-signpdf.

hmpvillegas avatar hmpvillegas commented on May 20, 2024

up, btw, is the new pdf-lib works now? please let me know. Thank you :)

from node-signpdf.

achieverprince avatar achieverprince commented on May 20, 2024

Hi @therpobinski
Thanks for the 'pdflibAddPlaceholder' implementation.
any update from your side?
i am still getting error "Could not find ByteRange placeholder: /ByteRange [ 0 / ********** / ********** / ********** ]" with the latest code from your repo, i guess you are still working on it.

when i analyzed the genated pdf file after calling function 'pdflibAddPlaceholder'

pdflibAddPlaceholder({
                        pdfBuffer,
                        infoSignature
                    })

it is not appending the /Sig or /Annot or /AcroForm objects.
I also checked the final file data directly in pdflibAddPlaceholder, result are same.
Not sure if i am missing anything.

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

The error you say should no longer appear if you use the library that I haven't finished yet.
Are you sure you are using this latest version?
This is the version you should work with.

I couldn't finish, since I've been busy, I hope to finish it this week.

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

I'm done, the last changes are in the develop branch, I have a doubt and I think that @vbuch can clarify us, when you open the PDF, in the signatures panel it doesn't show the signer's information.
This is file

from node-signpdf.

achieverprince avatar achieverprince commented on May 20, 2024

@therpobinski yes I was using the latest code from your branch(develop was default branch).
After i imported signer from your code (signpdf.js)(earlier i was importing from 'node-signpdf'), now 'plainAddPlaceholder' works with the PDF exported from pdf-lib (v 1.7), so that solved my issue.
maybe i messed my reference somewhere while I was copying codes from different sources. however i am good for now. when you complete your version I will try that and use it 👍

from node-signpdf.

hmpvillegas avatar hmpvillegas commented on May 20, 2024

hey @achieverprince , can you post your code in here, to share? Thanks if you can.

from node-signpdf.

achieverprince avatar achieverprince commented on May 20, 2024

Forgive me for my unstructured code, just a POC

var file = req.file; //A png file with signature received
    const existingPdfBytes = fs.readFileSync(appRoot+'/sample_pdf/authorization_letter.pdf');
    let signatureImage = fs.readFileSync(file.path);
    var typedPdfByteArray = Uint8Array.from(existingPdfBytes);
    const pdfDoc = PDFDocument.load(typedPdfByteArray,{ ignoreEncryption: true })
        .then((pdfDoc) => {
            const pages = pdfDoc.getPages()
            // Embed the PNG image bytes
            const pngImage = pdfDoc.embedPng(signatureImage).then((pdfImage)=>{
                pages[0].drawImage(pdfImage, {
                    x: 370,
                    y: 100,
                    width: 150,
                    height: 75,
                });
                let pdfBuffer = pdfDoc.save({
                    useObjectStreams : false,
                    objectsPerTick: 150
                }).then(value => {                    
                    const p12Buffer = fs.readFileSync(appRoot+'/certificate/certificate_test.p12');
                    fs.writeFileSync(appRoot+'/sample_pdf/tempSignatureFile.pdf',new Buffer.from(value));
                    // i could just use 'value' here, instead of reading the file again
                    //did this to debug the files generated 
                    let pdfBuffer = fs.readFileSync(appRoot+'/sample_pdf/tempSignatureFile.pdf');
                    pdfBuffer = fs.readFileSync(appRoot+'/sample_pdf/tempSignatureFile.pdf');
                    pdfBuffer = plainAddPlaceholder({
                        pdfBuffer,
                        reason: 'I have reviewed it.',
                        signatureLength: p12Buffer.length,
                    });
                    console.log(pdfBuffer.toString('utf8'));
                    pdfBuffer = signer.sign(pdfBuffer, p12Buffer,{ passphrase : '123456'});
                    var savedFileName = 'output_signed_pdf_'+new Date().getTime()+'.pdf';
                    var savedFilePath = appRoot + '/sample_pdf/'+savedFileName;
                    fs.writeFileSync(savedFilePath, pdfBuffer);
                    res.send({fileName: savedFileName});
                    /* //Old code since pdflibAddPlaceholder wasnt working / plainAddPlaceholder started working with latest library code
                    const infoSignature = {
                        reason: 'SwiftDocuSign',
                        contactInfo: '[email protected]',
                        name: 'Jeba Prince',
                        location: 'SwiftDocuSign',
                    }
                    pdfBuffer = pdflibAddPlaceholder({
                        pdfBuffer,
                        infoSignature
                    }).then(bufferTemp => {
                        console.log(bufferTemp.toString('utf8'));
                        pdfBuffer = signer.sign(bufferTemp, p12Buffer,{ passphrase : 'WeLove9SA'});
                        fs.writeFileSync(appRoot + '/sample_pdf/demo.pdf', pdfBuffer);
                        res.send({success:true});
                    })*/

                });
            });
        });

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

@therpobinski yes I was using the latest code from your branch(develop was default branch).
After i imported signer from your code (signpdf.js)(earlier i was importing from 'node-signpdf'), now 'plainAddPlaceholder' works with the PDF exported from pdf-lib (v 1.7), so that solved my issue.
maybe i messed my reference somewhere while I was copying codes from different sources. however i am good for now. when you complete your version I will try that and use it +1

The idea is that if they can contribute to correcting the only problem we have, it would be a success.
I have finished the development, but when applying the placeholder I do not understand why when opening the pdf, in the signatures panel, the signer's information does not appear.
Does anyone have any idea of ​​the problem?
@vbuch Do you know why that happens?

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

ready, finish the library, it works great! Now I wish I could make several signed ones, since this is not yet implemented. Anyone who can help me with that?
@vizicsaba89 I know you implemented this, could you give me a little guide on how to do it?
I understand that only one Acroform should be modified in the same way / Type/Page/Parent. Can you give me an idea about it?

from node-signpdf.

achieverprince avatar achieverprince commented on May 20, 2024

@therpobinski
So now your modification adds a visible signature placeholder in PDF, and you are still having issues signing this PDF am i correct?
If you can attach your output pdf it would help to debug

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

Hi guys, I have progressed on this, I have a problem signing a second time.
Here I commented on the problem, I think that @vbuch can give me an idea of ​​why this problem occurs.
I look forward to your help in culminating with this problem.
Thanks!

from node-signpdf.

vizicsaba89 avatar vizicsaba89 commented on May 20, 2024

If i remember correctly, in my version i had to add an Annotation Apparance object in order to show the signature. I dont have much time to rewrite the current code, but i can share the code snipped from my version, maybe this can help(this should be implemented in pdfkitAddPlaceHolder.js):

import { UserInformation } from '../model/user-information'
import { DEFAULT_BYTE_RANGE_PLACEHOLDER, DEFAULT_SIGNATURE_LENGTH } from './const'
import fs from 'fs'
import PDFKitReferenceMock from './pdf-kit-reference-mock'
import PNG from 'png-js'
import zlib from 'zlib';

const specialCharacters = ['á', 'Á', 'é', 'É', 'í', 'Í', 'ó', 'Ó', 'ö', 'Ö', 'ő', 'Ő', 'ú', 'Ú', 'ű', 'Ű']

const MARKERS = [
  0xffc0,
  0xffc1,
  0xffc2,
  0xffc3,
  0xffc5,
  0xffc6,
  0xffc7,
  0xffc8,
  0xffc9,
  0xffca,
  0xffcb,
  0xffcc,
  0xffcd,
  0xffce,
  0xffcf
];

const COLOR_SPACE_MAP = {
  1: 'DeviceGray',
  3: 'DeviceRGB',
  4: 'DeviceCMYK'
};

const pdfkitAddPlaceholder = ({
  pdf,
  pdfBuffer,
  reason,
  signatureLength = DEFAULT_SIGNATURE_LENGTH,
  byteRangePlaceholder = DEFAULT_BYTE_RANGE_PLACEHOLDER,
  userInformation,
}: {
  pdf: any
  pdfBuffer: Buffer
  reason: string
  userInformation: UserInformation
  signatureLength?: number
  byteRangePlaceholder?: string
}) => {
  const acroFormPosition = pdfBuffer.lastIndexOf('/Type /AcroForm')
  const isAcroFormExists = acroFormPosition !== -1
  let acroFormId
  let fieldIds: PDFKitReferenceMock[] = []

  if (isAcroFormExists) {
    const acroForm = getAcroForm(pdfBuffer, acroFormPosition)
    acroFormId = getAcroFormId(acroForm)
    fieldIds = getFieldIds(acroForm)
  }

  const FONT = getFont(pdf, 'Helvetica')
  const ZAF = getFont(pdf, 'ZapfDingbats')
  const APFONT = getFont(pdf, 'Helvetica')
  const IMG = getImage(userInformation.imagePath, pdf)

  const AP = getAnnotationApparance(pdf, IMG, APFONT, userInformation)
  const SIGNATURE = getSignature(pdf, byteRangePlaceholder, signatureLength, reason, userInformation)
  const WIDGET = getWidget(pdf, fieldIds, SIGNATURE, AP)

  const ACROFORM = getAcroform(pdf, fieldIds, WIDGET, FONT, ZAF, acroFormId)

  return {
    signature: SIGNATURE,
    form: ACROFORM,
    widget: WIDGET,
  }
}

const getImage = (imagePath: string, pdf) => {
  let img
  const data = fs.readFileSync(imagePath)

  if (data[0] === 0xff && data[1] === 0xd8) {
    img = getJpgImage(pdf, data)
  } else if (data[0] === 0x89 && data.toString('ascii', 1, 4) === 'PNG') {
    img = getPngImage(pdf, data)
  } else {
    throw new Error('Unknown image format.');
  }

  return img
}

const getAcroform = (pdf, fieldIds, WIDGET, FONT, ZAF, acroFormId) => {
  return pdf.ref(
    {
      Type: 'AcroForm',
      SigFlags: 3,
      Fields: [...fieldIds, WIDGET],
      DR: `<</Font\n<</Helvetica ${FONT.index} 0 R/ZapfDingbats ${ZAF.index} 0 R>>\n>>`,
    },
    acroFormId,
  )
}

const getWidget = (pdf, fieldIds, signature, AP) => {
  const signatureBaseName = 'Signature'

  const signatureLeftOffset = fieldIds.length * 125
  const signatureBottomOffset = 5

  return pdf.ref({
    Type: 'Annot',
    Subtype: 'Widget',
    FT: 'Sig',
    Rect: [signatureLeftOffset, 0, signatureLeftOffset + 90, signatureBottomOffset + 60],
    V: signature,
    T: new String(signatureBaseName + (fieldIds.length + 1)), // eslint-disable-line no-new-wrappers
    F: 4,
    AP: `<</N ${AP.index} 0 R>>`,
    P: pdf.page.dictionary, // eslint-disable-line no-underscore-dangle
    DA: new String('/Helvetica 0 Tf 0 g'), // eslint-disable-line no-new-wrappers
  })
}

const getAnnotationApparance = (pdf, IMG, APFONT, userInformation) => {
  return pdf.ref({
    CropBox: [0, 0, 197, 70],
    Type: 'XObject',
    FormType: 1,
    BBox: [-10, 10, 197.0, 70.0],
    Resources: `<</XObject <<\n/Img${IMG.index} ${IMG.index} 0 R\n>>\n/Font <<\n/f1 ${APFONT.index} 0 R\n>>\n>>`,
    MediaBox: [0, 0, 197, 70],
    Subtype: 'Form',
  }, null, getStream(userInformation, IMG.index))
}

const getStream = (userInformation, imgIndex) => {
  return getConvertedText(`
    1.0 1.0 1.0 rg
    0.0 0.0 0.0 RG
    q
    q
    200 0 0 50 0 10 cm
    /Img${imgIndex} Do
    Q
    0 0 0 rg
    BT
    0 Tr
    /f1 10.0 Tf
    1.4 0 0 1 20 45.97412 Tm
    (Aláírta: ${userInformation.commonName}) Tj
    ET
    BT
    0 Tr
    /f1 10.0 Tf
    1.4 0 0 1 20 33.56006 Tm
    (${new Date().toISOString().slice(0,10)}) Tj
    ET
    Q`
  )
}

const getJpgImage = (pdf: any, data: any) => {
  if (data.readUInt16BE(0) !== 0xffd8) {
    throw 'SOI not found in JPEG'
  }

  let pos = 2
  let marker
  while (pos < data.length) {
    marker = data.readUInt16BE(pos);
    pos += 2
    if (MARKERS.includes(marker)) {
      break
    }
    pos += data.readUInt16BE(pos)
  }

  if (!MARKERS.includes(marker)) {
    throw 'Invalid JPEG.'
  }
  pos += 2

  const bits = data[pos++]
  const height = data.readUInt16BE(pos)
  pos += 2

  const width =data.readUInt16BE(pos)
  pos += 2

  const channels = data[pos++]
  const colorSpace = COLOR_SPACE_MAP[channels]

  const baseJpgData = {
    Type: 'XObject',
    Subtype: 'Image',
    BitsPerComponent: bits,
    Width: width,
    Height: height,
    ColorSpace: colorSpace,
    Filter: 'DCTDecode',
  }

  if (colorSpace === 'DeviceCMYK') {
    baseJpgData['Decode'] = [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0]
  }
  const image = pdf.ref(baseJpgData, null, data);

  return image
}

const getPngImage = (pdf: any, data: any) => {
  const image = new PNG(data)
  const hasAlphaChannel = image.hasAlphaChannel

  const pngBaseData = {
    Type: 'XObject',
    Subtype: 'Image',
    BitsPerComponent: hasAlphaChannel ? 8 : image.bits,
    Width: image.width,
    Height: image.height,
    Filter: 'FlateDecode'
  }

  if (!hasAlphaChannel) {
    const params = pdf.ref({
      Predictor: 15,
      Colors: image.colors,
      BitsPerComponent: image.bits,
      Columns: image.width
    });

    pngBaseData['DecodeParms'] = params
  }

  if (image.palette.length === 0) {
    pngBaseData['ColorSpace'] = image.colorSpace
  } else {
    const palette = pdf.ref({
      stream: new Buffer(image.palette)
    })
    pngBaseData['ColorSpace'] = ['Indexed', 'DeviceRGB', image.palette.length / 3 - 1, palette]
  }

  if (image.transparency.grayscale != null) {
    const val = image.transparency.grayscale
    pngBaseData['Mask'] = [val, val]
  } else if (image.transparency.rgb) {
    const { rgb } =image.transparency
    const mask: any[] = []

    for (let x of rgb) {
      mask.push(x, x)
    }
    pngBaseData['Mask'] = mask
  } else if (image.transparency.indexed) {
    loadIndexedAlphaChannel(image)
  } else if (hasAlphaChannel) {
    splitAlphaChannel(pdf)

    const sMask = getSmask(pdf, image)
    pngBaseData['Mask'] = sMask
  }

  const pngImage = pdf.ref(pngBaseData, null, image.imgData)

  return pngImage
}

const loadIndexedAlphaChannel = (image) => {
  const transparency = image.transparency.indexed;
  return image.decodePixels(pixels => {
    const alphaChannel = new Buffer(image.width * image.height);

    let i = 0;
    for (let j = 0, end = pixels.length; j < end; j++) {
      alphaChannel[i++] = transparency[pixels[j]];
    }

    image.alphaChannel = zlib.deflateSync(alphaChannel);
  });
}

const splitAlphaChannel = (image) => {
  image.decodePixels(pixels => {
    let a, p
    const colorCount = image.colors
    const pixelCount = image.width * image.height
    const imgData = new Buffer(pixelCount * colorCount)
    const alphaChannel = new Buffer(pixelCount)

    let i = (p = a = 0)
    const len = pixels.length
    const skipByteCount = image.bits === 16 ? 1 : 0
    while (i < len) {
      for (let colorIndex = 0; colorIndex < colorCount; colorIndex++) {
        imgData[p++] = pixels[i++]
        i += skipByteCount
      }
      alphaChannel[a++] = pixels[i++]
      i += skipByteCount
    }

    image.imgData = zlib.deflateSync(imgData)
    image.alphaChannel = zlib.deflateSync(alphaChannel)
  })
}

const getSmask = (pdf, image) => {
  let sMask
  if (image.hasAlphaChannel) {
      sMask = pdf.ref({
          Type: 'XObject',
          Subtype: 'Image',
          Height: image.height,
          Width: image.width,
          BitsPerComponent: 8,
          Filter: 'FlateDecode',
          ColorSpace: 'DeviceGray',
          Decode: [0, 1],
          stream: image.alphaChannel
      })
  }

  return sMask
}

const getFieldIds = acroForm => {
  let fieldIds: PDFKitReferenceMock[] = []
  const acroFormFields = acroForm.slice(acroForm.indexOf('/Fields [') + 9, acroForm.indexOf(']'))
  fieldIds = acroFormFields
    .split(' ')
    .filter((_element, index) => index % 3 === 0)
    .map(fieldId => new PDFKitReferenceMock(fieldId))

  return fieldIds
}

const getAcroForm = (pdfBuffer, acroFormPosition) => {
  const pdfSlice = pdfBuffer.slice(acroFormPosition - 12)
  const acroForm = pdfSlice.slice(0, pdfSlice.indexOf('endobj')).toString()

  return acroForm
}

const getAcroFormId = acroForm => {
  const acroFormFirsRow = acroForm.split('\n')[0]
  const acroFormId = parseInt(acroFormFirsRow.split(' ')[0])

  return acroFormId
}

const getFont = (pdf, baseFont) => {
  return pdf.ref({
    Type: 'Font',
    BaseFont: baseFont,
    Encoding: 'WinAnsiEncoding',
    Subtype: 'Type1',
  })
}

const getSignature = (
  pdf: any,
  byteRangePlaceholder: string,
  signatureLength: number,
  reason: string,
  userInformation: any
) => {
  return pdf.ref({
    Type: 'Sig',
    Filter: 'Adobe.PPKLite',
    SubFilter: 'adbe.pkcs7.detached',
    ByteRange: [0, byteRangePlaceholder, byteRangePlaceholder, byteRangePlaceholder],
    Contents: Buffer.from(String.fromCharCode(0).repeat(signatureLength)),
    Reason: new String(reason),
    M: new Date(),
    ContactInfo: new String(`${userInformation.emailAddress}`),
    Name: new String(`${userInformation.commonName}`),
    Location: new String('Hungary, HU'),
  })
}

const getConvertedText = (text: string) => {
  return text.split('').map(character => {
    return specialCharacters.includes(character) ? getOctalCodeFromCharacter(character) : character
  }).join('')
}

const getOctalCodeFromCharacter = (character: string) => {
  return '\\' + character.charCodeAt(0).toString(8)
}

export default pdfkitAddPlaceholder

In order to show some image to you have to move stream handling from pdfobject.js to index.js as the following snippet shows:

import { UserInformation } from '../model/user-information'
import { DEFAULT_SIGNATURE_LENGTH } from './const'
import createBufferPageWithAnnotation from './create-buffer-page-with-annotation'
import createBufferRootWithAcroform from './create-buffer-root-with-acrofrom'
import createBufferTrailer from './create-buffer-trailer'
import getIndexFromRef from './get-index-from-ref'
import getPageRef from './get-page-ref'
import pdfkitAddPlaceholder from './pdf-kit-add-placeholder'
import PDFKitReferenceMock from './pdf-kit-reference-mock'
import PDFObject from './pdfkit/pdfobject.js'
import readPdf from './read-pdf'
import removeTrailingNewLine from './remove-trailing-new-line'

const plainAddPlaceholder = (
  pdfBuffer: Buffer,
  userInformation: UserInformation,
  { reason, signatureLength = DEFAULT_SIGNATURE_LENGTH }: { reason: any; signatureLength: number },
) => {
  let pdf = removeTrailingNewLine(pdfBuffer)
  const info = readPdf(pdf)
  const pageRef = getPageRef(pdf, info)
  const pageIndex = getIndexFromRef(info.xref, pageRef)
  const addedReferences = new Map()

  const pdfKitMock = {
    ref: (input: any, additionalIndex: number, stream: any) => {
      info.xref.maxIndex += 1

      const index = additionalIndex != null ? additionalIndex : info.xref.maxIndex

      addedReferences.set(index, pdf.length + 1)

      pdf = getAssembledPdf(pdf, index, input, stream)

      return new PDFKitReferenceMock(info.xref.maxIndex)
    },
    page: {
      dictionary: new PDFKitReferenceMock(pageIndex, {
        data: {
          Annots: [],
        },
      }),
    },
    _root: {
      data: {},
    },
  }

  const { form, widget } = pdfkitAddPlaceholder({
    pdf: pdfKitMock,
    pdfBuffer,
    reason,
    signatureLength,
    userInformation,
  })

  if (!isContainBufferRootWithAcrofrom(pdfBuffer)) {
    const rootIndex = getIndexFromRef(info.xref, info.rootRef)
    addedReferences.set(rootIndex, pdf.length + 1)
    pdf = Buffer.concat([pdf, Buffer.from('\n'), createBufferRootWithAcroform(info, form)])
  }

  addedReferences.set(pageIndex, pdf.length + 1)
  pdf = Buffer.concat([pdf, Buffer.from('\n'), createBufferPageWithAnnotation(pdf, info, pageRef, widget)])

  pdf = Buffer.concat([pdf, Buffer.from('\n'), createBufferTrailer(pdf, info, addedReferences)])

  return pdf
}

const getAssembledPdf = (pdf: any, index: any, input: any, stream: any): Buffer => {
  let finalPdf = pdf

  finalPdf = Buffer.concat([
    finalPdf,
    Buffer.from('\n'),
    Buffer.from(`${index} 0 obj\n`),
    Buffer.from(PDFObject.convert(input)),
  ])

  if (stream) {
    finalPdf = Buffer.concat([
      finalPdf,
        Buffer.from('\nstream\n'),
        Buffer.from(stream),
        Buffer.from('\nendstream'),
    ]);
  }

  finalPdf = Buffer.concat([
    finalPdf,
    Buffer.from('\nendobj\n'),
  ]);

  return finalPdf
}

const isContainBufferRootWithAcrofrom = (pdf: Buffer) => {
  const bufferRootWithAcroformRefRegex = new RegExp('\\/AcroForm\\s+(\\d+\\s\\d+\\sR)', 'g')
  const match = bufferRootWithAcroformRefRegex.exec(pdf.toString())

  return match != null && match[1] != null && match[1] !== ''
}

export default plainAddPlaceholder

from node-signpdf.

vbuch avatar vbuch commented on May 20, 2024

Sorry guys but this issue is totally not readable. Tons of pasted code. Not sure what to help with and whom. From reading I get the impression that @therpobinski was close to proposing a PR. I saw a PR from his fork that fails CI. Is #41 related here?

from node-signpdf.

stale avatar stale commented on May 20, 2024

This issue has been automatically marked as stale because it has not had activity in the past 90 days. It will be closed if no further activity occurs. Thank you for your contributions.

from node-signpdf.

therpobinski avatar therpobinski commented on May 20, 2024

Hi guys, I'm sorry I haven't finished this yet. #41 .
I have not finished because first I have to finish a work presented with the current version. But I am having trouble signing some files and I don't know why. I have created a new issue to deal with the topic separately. Once finished, I will continue with the work I was doing with the "pdf-lib" library.
@vbuch , do you think you can help me with this problem, please? After solving this problem, I will continue with the RP that was left unfinished.

from node-signpdf.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.