vbuch / node-signpdf

Simple signing of PDFs in node.
MIT License
711 stars 178 forks source link

placeholder-pdfkit stops the pdf stream emitting the "end" event #240

Open Dmitrijs-Sakovics opened 6 months ago

Dmitrijs-Sakovics commented 6 months ago

Describe the bug and the expected behaviour Using a very slightly modified example based on packages\examples\src\pdfkit010.js to create a signed pdf (code below, calling the function from an external file)

The program is not throwing any errors, but gets stuck waiting for the pdfReady promise to resolve. the pdf stream emits the "data" event 59 times, but does not emit the "end" event (hence the promise not resolving). The "error" event does not get emitted either.

Commenting out the call to pdfkitAddPlaceholder (and the .end()-ing of the new refs) resolves the problem (but the signing process fails because the placeholder is missing)

Tried with pdfkit 0.11.0 and 0.14.0; under nodeJS 18.17.0 and 20.11.1

Is it a bug in signing or in the helpers? Helper: placeholder-pdfkit

To Reproduce

var fs = require("fs");
const PDFDocument = require("pdfkit").default;
var signpdf = require("@signpdf/signpdf").default;
var P12Signer = require("@signpdf/signer-p12").P12Signer;
var pdfkitAddPlaceholder = require("@signpdf/placeholder-pdfkit").pdfkitAddPlaceholder;

export const work = () => {
    console.log("generating example pdf");

    let pdf = new PDFDocument({
        autoFirstPage: false,
        size: "A4",
        layout: "portrait",
        bufferPages: true,
    });
    pdf.info.CreationDate = "";

    var pdfReady = new Promise(function (resolve) {
        var pdfChunks = [];
        pdf.on("data", function (data) {
            // ---------------------------------------------------gets triggered 59 times
            console.log("---data chunk #" + pdfChunks.length);
            pdfChunks.push(data);
        });
        pdf.on("error", () => {
            console.log("error emitted");
        });
        pdf.on("end", function () {
            // ----------------------------------------------------Never gets triggered
            console.log("data end");
            resolve(Buffer.concat(pdfChunks));
        });
    });

    pdf.addPage().fillColor("#333").fontSize(25).moveDown().text("@signpdf").save();

    var refs = pdfkitAddPlaceholder({
        pdf: pdf,
        pdfBuffer: Buffer.from([pdf]), // FIXME: This shouldn't be needed.
        reason: "Showing off.",
        contactInfo: "signpdf@example.com",
        name: "Sign PDF",
        location: "The digital world.",
    });
    Object.keys(refs).forEach(function (key) {
        console.log(`---ending ${key}`);
        refs[key].end();
    });

    var certificatePath = "./certificates/client-identity.p12";
    var certificateBuffer = fs.readFileSync(certificatePath);
    var signer = new P12Signer(certificateBuffer);

    console.log("setting up promise");
    pdfReady
        .then(function (pdfWithPlaceholder) {
            console.log("signing");
            return signpdf.sign(pdfWithPlaceholder, signer);
        })
        .then(function (signedPdf) {
            console.log("saving");
            var targetPath = "./pdfkit010.pdf";
            console.log("writing file");
            fs.writeFileSync(targetPath, signedPdf);
        });

    console.log("---ending pdfDocument stream");
    pdf.end();
};
vbuch commented 6 months ago

Hi, I haven't really used placeholder-pdfkit. @dhensby would be best to reply here. But from what I see in the source signature and widget are already ended. Try to work around that maybe and only end the form. Ideally, once you have it working, share your code and even better prepare a PR to introduce a placeholder-pdfkit example.

Dmitrijs-Sakovics commented 6 months ago

@vbuch @dhensby Hi,

I managed to get this working with pdfkit 0.14.0 by removing the form.end() statement (removing the whole foreach clause that .end()s all the refs after the pdfkitAddPlaceholder call)

While I am getting signed pdf-s out of it, I don't currently understand what's happening well enough to recommend this solution to others (hence no PR). It would be good if someone who knows the system could have a looks to see why this worked

dhensby commented 6 months ago

I'm actually using pdfmake and that uses @foliojs-fork/pdfkit instead of the actual pdfkit lib (probably because of all the problems that we've had previously with pdfkit. Their implementation of AcroForms really messed up integration and I don't recall if we ever got round to fixing it or not.

I guess that pdfkit no longer needs the .end() to be called and (if that's been taken from our examples) our examples may need updating!

vbuch commented 6 months ago

We just don't have a pdfkit example. We have the pdfkit010 ones and I'm guessing that's what @Dmitrijs-Sakovics here based his example on. The pdfkit010 is obviously a legacy one so... yes, an example using pdfkit > 10 is needed.

Dmitrijs-Sakovics commented 6 months ago

Yes, my attempts were based on the pdfkit010 example since the pdfkit >0.10 helper readme states "This works in an identical way to the pdfkit010 package and the pdfkit010.js example is still relevant."