Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can fields of placeholder be visible. #250

Open
pranay18997 opened this issue Apr 17, 2024 · 20 comments
Open

How can fields of placeholder be visible. #250

pranay18997 opened this issue Apr 17, 2024 · 20 comments
Labels

Comments

@pranay18997
Copy link

Describe the bug and the expected behaviour
I am working on the digital signature, and I want it should look like this. But I am not getting any option in pdflib. Please help here.
image

Is it a bug in signing or in the helpers?
Its a doubt

To Reproduce
Help us reproduce.
Include any relevant code.
Point to where you are having issues with links.
Give us a failing test.
In all cases make sure to include at least a source PDF that you are trying and failing to sign.
To let us easily take a look, it would help if you also include a resulting document (thou probably corrupted one).

@vbuch vbuch added the visual label Apr 17, 2024
@vbuch
Copy link
Owner

vbuch commented Apr 17, 2024

Hi,
Have a look at the "visual" tag if you want.
And we have this example that adds a visual: https://github.com/vbuch/node-signpdf/blob/develop/packages/examples/src/pdfkit010-with-visual.js

@pranay18997
Copy link
Author

@vbuch thanks for the quick response, but what is the significance of this parameter reason,location?

var refs = pdfkitAddPlaceholder({
    pdf: pdf,
    pdfBuffer: Buffer.from([pdf]), // FIXME: This shouldn't be needed.
    reason: 'Showing off.',
    contactInfo: '[email protected]',
    name: 'Sign PDF',
    location: 'The digital world.',
    signatureLength: 1612,
    widgetRect: widgetRect, // <== !!! This is where we tell the widget to be visible
});

@ngvhob
Copy link

ngvhob commented Apr 22, 2024

@pranay18997 Were you able to get the green tick in Adobe Acrobat after signing the PDF ?
Cause I tried using visual tag implementation but I still didn't get the green tick even though the certificate is showing as valid in Acrobat.

@pranay18997
Copy link
Author

@ngvhob no, @vbuch could you please help with #250 (comment) query?

@vbuch
Copy link
Owner

vbuch commented Apr 22, 2024

@pranay18997
Copy link
Author

@vbuch is there any way we can read signature values from the certificate?

@ngvhob
Copy link

ngvhob commented Apr 24, 2024

@vbuch got it, but is there a way to have every page of the PDF digitally signed? In my task, I need the ability to sign all pages, and I have a demo document too. I will attach a screenshot to provide my problem statement. Cause atm I get error if I try to place multiple place holders in the pdf doc. Is this possible ??

Screenshot (1)

@pranay18997
Copy link
Author

pranay18997 commented Apr 25, 2024

@vbuch I was trying to add digital signature in last page of pdf doc. Please find the code below.

        pdflibAddPlaceholder({
            pdfDoc: pdfDoc,
            pdfPage:pages[2],
            reason: 'Digitally Signed by xxx',
            contactInfo: '[email protected]',
            name: 'xxxxxx',
            signatureLength: 13786,
            location: 'Signature Valid',
            widgetRect: widgetRect,
            signingTime: new Date(),
        });

Issue:- Widget is not visible in last page. But if I am not passing 'pdfPage' parameter then widget is visible in first page.
Please help here on prioirty.

@YO-SC
Copy link

YO-SC commented May 9, 2024

Hello y'all, if I understand correctly @pranay18997 and @ngvhob sign widgets seem to be displaying the correct information of their respective pdflibAddPlaceholder() usage. Its kinda hard to tell but I think so (if I am wrong feel free to correct me). That said, I am having some issues with similar implementations.

I don't know if this is the normal behavior of the pdflibAddPlaceholder() , but the widgets on my end don't seem to be displaying any information:
Screenshot 2024-05-09 190532
Screenshot taken from Adobe Acrobat Reader

For context, here is the relevant code:

import { pdflibAddPlaceholder } from '@signpdf/placeholder-pdf-lib'
import { P12Signer } from '@signpdf/signer-p12'
import signpdf from '@signpdf/signpdf'
import { PDFDocument, PDFPage } from 'pdf-lib'

//...

  const p12Signer = new P12Signer(p12Buffer, {
      passphrase: process?.env?.CERT_PASSWORD,
    })
  const widgetRect = [50, 70, 200, 200]

// Add a placeholder for a signature.
    pdflibAddPlaceholder({
      appName: 'Some App',
      pdfDoc: pdfDoc,
      reason: 'The user is declaring consent through JavaScript.',
      contactInfo: '[email protected]', 
      name: 'John Doe',
      location: 'Some place on Earth',
      signingTime: new Date(), 
      widgetRect: widgetRect, // ! <== This is where we tell the widget to be visible
    })

// Get the modified PDFDocument bytes
  const pdfSaveBytes = await pdfDoc.save({ updateFieldAppearances: true })
  const pdfWithPlaceholderBuffer = Buffer.from(pdfSaveBytes)

  // And finally sign the document.
  const signedPdfBuffer = await signpdf.sign(
    pdfWithPlaceholderBuffer,
    p12Signer
  )

  const pdfBlob = new Blob([signedPdfBuffer])

// Save to disk
  await Bun.write(`${process.cwd()}/public/pdf/some-pdf-signed.pdf`, pdfBlob)

As you can see here, I am using @signpdf + pdf-lib. The thing is, I have followed the most up to date methods and read through all the relevant issues and documentation; but I can't seem to be able to display the placeholder information on the "sign box".

I really appreciate any leads to a solution and I am thankful for your time (anybody reading this through). Thanks.

(Also, very nice library/package. Any ways to support/donate? @vbuch)

@vbuch
Copy link
Owner

vbuch commented May 10, 2024

hi @YO-SC ,
The visual part of the widget is up to you. You need to find a way to draw it on the page. The signature widget is just a rectangle that should be interactive if the viewer has support for that. The signature information is supposed to be shown by the Reader itself and not embedded visually in the document.

1st thing would to be to look around the "visual" tag. I think all of these ask the same question - "How do I make the signature visually appealing?".
2nd: I believe you are looking for something like this https://github.com/vbuch/node-signpdf/blob/develop/packages/examples/src/pdfkit010-with-visual.js This is a pdfkit 0.10.0 implementation of the same and you can see at https://github.com/vbuch/node-signpdf/blob/develop/packages/examples/src/pdfkit010-with-visual.js#L82 that it actually draws the content before placing a signature widget on top of it. Not sure if someone ever created an example with pdf-lib so this above the closest I can get you to one.
On the support/donate question: There must be a "Sponsor" link on the repo that takes you to buymeacoffee where you can do that. Thanks if you do!

@YO-SC
Copy link

YO-SC commented May 10, 2024

Thanks for your timely response @vbuch, I will check out the examples provided. Thanks 👍🏼

@jamesjhonatan123
Copy link

Hello, how are you? First of all, congratulations on the initiative. I am facing issues with multiple signatures because when I add a visible placeholder and then sign, everything works perfectly. However, any other signature with a new visual placeholder invalidates the previous ones (in terms of integrity).

I used pdf-lib to add the visible placeholder.

@vbuch
Copy link
Owner

vbuch commented May 27, 2024

@jamesjhonatan123 pdf-lib does not support incremental updates (at least to my knowledge) and those are needed for multiple signatures.

@jamesjhonatan123
Copy link

@vbuch Is there any library or possibility to do this without using pdfKit or pdf-lib? Unfortunately, I can't load an already signed PDF with pdfKit, as it does not allow editing.

@vbuch
Copy link
Owner

vbuch commented May 27, 2024

@jamesjhonatan123 the signing part you can do with placeholder-plain. The visual part - no clue

@jamesjhonatan123
Copy link

jamesjhonatan123 commented May 27, 2024

I did it. I used a fork of HummusJS - Muhammara (maintained) - for incremental writing. Now I can sign as many times as necessary by adding the visible placeholder. Thank you.

Here's the example for those who had the same problem:

import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { SignPdf } from '@signpdf/signpdf';
import { P12Signer } from '@signpdf/signer-p12';
import { plainAddPlaceholder } from '@signpdf/placeholder-plain';
import { Recipe } from 'muhammara';


const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

async function addPlaceholder(pdfBuffer, options) {
    return plainAddPlaceholder({
        pdfBuffer,
        reason: options.reason,
        contactInfo: options.contactInfo,
        name: options.name,
        location: options.location,
        widgetRect: options.widgetRect,
    });
}

async function signPdf(pdfBuffer, certificatePath, passphrase, targetPath) {
    const certificateBuffer = fs.readFileSync(path.join(__dirname, certificatePath));
    const signer = new P12Signer(certificateBuffer, { passphrase });
    const signPdf = new SignPdf();

    const signedPdf = await signPdf.sign(pdfBuffer, signer);
    fs.writeFileSync(targetPath, signedPdf);
    return signedPdf;
}

function drawTextOnWidget(pdfPath, outputPath, text, widgetRect) {
    const pdfDoc = new Recipe(pdfPath, outputPath);

    pdfDoc
        .editPage(1)
        .text(text, widgetRect.x, widgetRect.y)
        .endPage()
        .endPDF();
}

async function work() {
    const pdfPath = path.join(__dirname, 'multiple-signatures-buyer-seller-3.pdf');
    const pdfBuffer = fs.readFileSync(pdfPath);


    const pdfWithBuyerPlaceholder = await addPlaceholder(pdfBuffer, {
        reason: 'Agrees to buy the truck trailer.',
        contactInfo: '[email protected]',
        name: 'John Doe',
        location: 'Free Text Str., Free World',
        widgetRect: [200, 200, 300, 300],
    });


    const buyerSignedPdfPath = path.join(__dirname, './multiple-signatures-buyer-seller-1.pdf');
    const buyerSignedPdf = await signPdf(
        pdfWithBuyerPlaceholder,
        '1/1.p12',
        '123456',
        buyerSignedPdfPath
    );


    drawTextOnWidget(buyerSignedPdfPath, buyerSignedPdfPath, 'Buyer Signature', { x: 250, y: 250 });


    const buyerSignedPdfBuffer = fs.readFileSync(buyerSignedPdfPath);
    const pdfWithSellerPlaceholder = await addPlaceholder(buyerSignedPdfBuffer, {
        reason: 'Agrees to sell a truck trailer to John Doe.',
        contactInfo: '[email protected]',
        name: 'Thug Dealer',
        location: 'Automotive Str., Free World',
        widgetRect: [400, 400, 500, 500],
    });


    const sellerSignedPdfPath = path.join(__dirname, './multiple-signatures-buyer-seller-2.pdf');
    const finalSignedPdf = await signPdf(
        pdfWithSellerPlaceholder,
        '2/2.p12',
        '654321',
        sellerSignedPdfPath
    );


    drawTextOnWidget(sellerSignedPdfPath, path.join(__dirname, './multiple-signatures-buyer-seller-3.pdf'), 'Seller Signature', { x: 450, y: 450 });

    console.log('PDF signed by both buyer and seller with text drawn on widgetRect.');
}

work();

@vbuch
Copy link
Owner

vbuch commented May 28, 2024

@jamesjhonatan123 want to make it a package under packages/examples? Or at least start a PR that I can when I have the time help with/wrap up for you

@jamesjhonatan123
Copy link

Sure, I'll do it.

@vinhnhq
Copy link

vinhnhq commented Jun 19, 2024

I did it. I used a fork of HummusJS - Muhammara (maintained) - for incremental writing. Now I can sign as many times as necessary by adding the visible placeholder. Thank you.

Here's the example for those who had the same problem:

import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { SignPdf } from '@signpdf/signpdf';
import { P12Signer } from '@signpdf/signer-p12';
import { plainAddPlaceholder } from '@signpdf/placeholder-plain';
import { Recipe } from 'muhammara';


const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

async function addPlaceholder(pdfBuffer, options) {
    return plainAddPlaceholder({
        pdfBuffer,
        reason: options.reason,
        contactInfo: options.contactInfo,
        name: options.name,
        location: options.location,
        widgetRect: options.widgetRect,
    });
}

async function signPdf(pdfBuffer, certificatePath, passphrase, targetPath) {
    const certificateBuffer = fs.readFileSync(path.join(__dirname, certificatePath));
    const signer = new P12Signer(certificateBuffer, { passphrase });
    const signPdf = new SignPdf();

    const signedPdf = await signPdf.sign(pdfBuffer, signer);
    fs.writeFileSync(targetPath, signedPdf);
    return signedPdf;
}

function drawTextOnWidget(pdfPath, outputPath, text, widgetRect) {
    const pdfDoc = new Recipe(pdfPath, outputPath);

    pdfDoc
        .editPage(1)
        .text(text, widgetRect.x, widgetRect.y)
        .endPage()
        .endPDF();
}

async function work() {
    const pdfPath = path.join(__dirname, 'multiple-signatures-buyer-seller-3.pdf');
    const pdfBuffer = fs.readFileSync(pdfPath);


    const pdfWithBuyerPlaceholder = await addPlaceholder(pdfBuffer, {
        reason: 'Agrees to buy the truck trailer.',
        contactInfo: '[email protected]',
        name: 'John Doe',
        location: 'Free Text Str., Free World',
        widgetRect: [200, 200, 300, 300],
    });


    const buyerSignedPdfPath = path.join(__dirname, './multiple-signatures-buyer-seller-1.pdf');
    const buyerSignedPdf = await signPdf(
        pdfWithBuyerPlaceholder,
        '1/1.p12',
        '123456',
        buyerSignedPdfPath
    );


    drawTextOnWidget(buyerSignedPdfPath, buyerSignedPdfPath, 'Buyer Signature', { x: 250, y: 250 });


    const buyerSignedPdfBuffer = fs.readFileSync(buyerSignedPdfPath);
    const pdfWithSellerPlaceholder = await addPlaceholder(buyerSignedPdfBuffer, {
        reason: 'Agrees to sell a truck trailer to John Doe.',
        contactInfo: '[email protected]',
        name: 'Thug Dealer',
        location: 'Automotive Str., Free World',
        widgetRect: [400, 400, 500, 500],
    });


    const sellerSignedPdfPath = path.join(__dirname, './multiple-signatures-buyer-seller-2.pdf');
    const finalSignedPdf = await signPdf(
        pdfWithSellerPlaceholder,
        '2/2.p12',
        '654321',
        sellerSignedPdfPath
    );


    drawTextOnWidget(sellerSignedPdfPath, path.join(__dirname, './multiple-signatures-buyer-seller-3.pdf'), 'Seller Signature', { x: 450, y: 450 });

    console.log('PDF signed by both buyer and seller with text drawn on widgetRect.');
}

work();

hey @jamesjhonatan123 thanks for your sample, but when executing that script on mac m1 it throws me this error, did you get it before? thanks for any comments
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants