Node.js PNG buffer getting Unknown Image Format Error

96 views Asked by At

I'm using bwip-js to generate barcodes and pdfkit to generate a pdf that will display said barcode.

I'm having bwip-js generate png images of the barcodes, however it only provides a buffer of the png. I'm then using fs.writeFile to produce a png from that buffer (may be doing this part wrong).

Anyway, when I get to put the png in the pdf, pdfkit gives an error that the image is an Uknown image format

Meanwhile, if I open the directory in which the png was saved in my computer's file explorer, I can open the image just fine.

I found this answer seemingly describing the same issue, but I had no luck having bwip-js generate the png using .PNG instead of .png

I feel as though I'm missing something here with taking the buffer that bwip-js provides and converting it into a png file.

Code:

const PDFDocument = require("pdfkit");
const bwipjs = require("bwip-js");
const fs = require("fs");

function generateCustomerInvoice(data) {
  const id = "12345678"
  bwipjs
    .toBuffer({
      bcid: "code128",
      text: id,
      scale: 3,
      height: 10,
      includetext: true,
      textxalign: "center",
    })
    .then((png) => {
      fs.writeFile("./barcodes/" + id + ".png", png, () => {});
      console.log("Barcode Finished");
    })
    .then(() => {
      const doc = new PDFDocument({ autoFirstPage: false });
      doc.pipe(fs.createWriteStream("./pdfs/customer/" + id + ".pdf"));
      doc.addPage({ size: "A5", margin: 20 });
      doc.lineWidth(1);
      doc.lineCap("butt").moveTo(20, 20).lineTo(399.53, 20).stroke();
      doc.lineCap("butt").moveTo(20, 20).lineTo(20, 575.28).stroke();
      doc.lineCap("butt").moveTo(399.53, 20).lineTo(399.53, 575.28).stroke();
      doc.lineCap("butt").moveTo(20, 575.28).lineTo(399.53, 575.28).stroke();
      doc.fontSize(12).text("Customer Invoice", 40, 40);
      doc.image("./barcodes/" + id + ".png", 40, 40);
      doc.end();
    });
}
1

There are 1 answers

2
Bench Vue On BEST ANSWER

Your write ".png" image part is wrong.

Node.js operates on a non-blocking I/O model, it means it doesn't wait for operations like reading from or writing to the filesystem to complete before moving on to the next line of code. This is great for performance but can lead to issues when one operation depends on the completion of another.

In your function, you're using fs.writeFile() to write the barcode image to disk. This method is asynchronous – it starts the writing process and then immediately moves on, not waiting for the file write to complete.

The fs.writeFile() function is asynchronous.

This means it starts the file-writing process and then immediately moves on to the next line of code without waiting for the file-writing to complete.

So next step "fs.createWriteStream()" no chance to reading finished image. So this code will create the correct PDF file.

const PDFDocument = require("pdfkit");
const bwipjs = require("bwip-js");
const fs = require("fs");

function generateCustomerInvoice(invoiceData) {
    const id = invoiceData.invoiceNumber;
    const barcodeDir = "./barcodes/";
    const pdfDir = "./pdfs/customer/";

    if (!fs.existsSync(barcodeDir)) {
        fs.mkdirSync(barcodeDir, { recursive: true });
    }

    if (!fs.existsSync(pdfDir)) {
        fs.mkdirSync(pdfDir, { recursive: true });
    }

    bwipjs.toBuffer({
        bcid: "code128",
        text: id,
        scale: 3,
        height: 10,
        includetext: true,
        textxalign: "center",
    })
        .then((png) => {
            return new Promise((resolve, reject) => {
                fs.writeFile("./barcodes/" + id + ".png", png, (err) => {
                    if (err) {
                        console.error("Error writing file:", err);
                        reject(err);
                    } else {
                        console.log("Barcode Finished");
                        resolve();
                    }
                });
            });
        })
        .then(() => {
            const doc = new PDFDocument({ autoFirstPage: false });
            doc.pipe(fs.createWriteStream("./pdfs/customer/" + id + ".pdf"));
            doc.addPage({ size: "A5", margin: 20 });

            doc.fontSize(12).text("Customer Invoice", 40, 30);

            doc.image("./barcodes/" + id + ".png", 40, 60, { width: 150 });

            doc.fontSize(10).text(`Customer Name: ${invoiceData.customerName}`, 40, 130);

            const tableTop = 160;
            doc.fontSize(10);
            doc.text("Description", 40, tableTop);
            doc.text("Quantity", 200, tableTop);
            doc.text("Price", 280, tableTop);
            doc.text("Total", 360, tableTop);

            let verticalPosition = tableTop;
            invoiceData.items.forEach(item => {
                verticalPosition += 20;
                doc.text(item.description, 40, verticalPosition);
                doc.text(item.quantity, 200, verticalPosition);
                doc.text(`$${item.price.toFixed(2)}`, 280, verticalPosition);
                doc.text(`$${(item.quantity * item.price).toFixed(2)}`, 360, verticalPosition);
            });

            doc.end();
        })
        .catch((err) => {
            console.error("Error in PDF generation: ", err);
        });
}

generateCustomerInvoice({
    customerName: "Tom Cruise",
    invoiceNumber: "INV-10001",
    items: [
        { description: "Item 1", quantity: 2, price: 100.00 },
        { description: "Item 2", quantity: 5, price: 50.00 }
    ],
});

Result enter image description here

More detail in here