0
votes

I've been trying to populate an image in a PDFDoc from PDFKit and am not able to get it to work. When using the Function I have below, I get the following Error in the Firebase Console-Functions-Logs. The file is stored on Firebase Storage as a "image/png" type. The name is just a string. I tried appending ".PNG" to the name, I received the same error. I included the whole function file since maybe there is a more acceptable way to handle this? How am I supposed to insert a PDF into the PDFDoc from Firebase Storage while in a Firebase Function? Thank you.

Error getting documents: Error: Unknown image format. at Function.PDFImage.open (/user_code/node_modules/pdfkit/js/image.js:43:15) at PDFDocument.openImage (/user_code/node_modules/pdfkit/js/mixins/images.js:102:26) at PDFDocument.image (/user_code/node_modules/pdfkit/js/mixins/images.js:30:24) at /user_code/lib/index.js:71:48 at QuerySnapshot.forEach (/user_code/node_modules/firebase-admin/node_modules/@google-cloud/firestore/src/reference.js:1012:16) at /user_code/lib/index.js:24:31 at process._tickDomainCallback (internal/process/next_tick.js:135:7)

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import * as firebase from 'firebase/app';
import * as PDFDocument from 'pdfkit';

admin.initializeApp(functions.config().firebase);

export const genPdf = functions.https.onRequest((request, response) => {

    const bucket = admin.storage().bucket("decteac2-3hcec.appspot.com");

    const text = request.query.text;
    const pdfdoc = new PDFDocument({ autoFirstPage: false });
    pdfdoc.info['Title'] = text + ".pdf";

    let x = 5;
    let y = 5;

    pdfdoc.pipe(response);

    const docRef = admin.firestore().collection("cxforms").doc(text);
    docRef.get().then(function (doc) {
        if (doc.exists) {
            const pagesRef = admin.firestore().collection("pages").where("formKey", "==", text).orderBy("pageno");
            pagesRef.get().then(function (querySnapshot) {
                let cnt = 1;
                querySnapshot.forEach(function (page) {
                    pdfdoc.addPage({ margin: 0 });
                    x = 5;
                    y = 5;
                    pdfdoc.moveTo(x, y)
                        .lineTo(607, 5)
                        .lineTo(607, 787)
                        .lineTo(5, 787)
                        .lineTo(x, y)
                        .stroke();
                    for (let i = 0; i < page.data().tables.length; i++) {
                        const table = page.data().tables[i];
                        y = y + 8.5; // margin

                        var tableWidth = 5;
                        for (let p = 0; p < table.cols.length; p++) {
                            tableWidth = tableWidth + (table.cols[p] * .82);
                        }
                        var offset = (597 - tableWidth) / 2;
                        for (let j = 0; j < table.rows.length; j++) {
                            const row = table.rows[j];
                            x = 10 + offset;
                            for (let k = 0; k < row.cells.length; k++) {
                                const cell = row.cells[k];
                                const colwidth = table.cols[k];
                                var height = 22.2;
                                for (let l = 0; l < cell.cellels.length; l++) {
                                    const cellEl = cell.cellels[l];

                                    if (cellEl.type === "Text") {
                                        pdfdoc.text(cellEl.text, x + 1, y + 7);
                                    } else if (cellEl.type == "Image") {
                                        const filename = cellEl.storageid;
                                        const file = bucket.file(filename);
                                        const bucketFileStream = file.createWriteStream();
                                        const buffer = new Buffer([1000000]);
                                        bucketFileStream.write(buffer);
                                        bucketFileStream.end();
                                        pdfdoc.image(buffer, x, y);

                                    }
                                }
                                if (height < 20) {
                                    height = 20;
                                }
                                pdfdoc.moveTo(x, y)
                                    .lineTo(x + (colwidth * .82), y)
                                    .lineTo(x + (colwidth * .82), y + height)
                                    .lineTo(x, y + height)
                                    .lineTo(x, y)
                                    .stroke();
                                x = x + (colwidth * .82);
                            }
                            y = y + height;
                        }
                    }
                    cnt++;
                });
                pdfdoc.end();
            }).catch(function (error) {
                console.log("Error getting documents: ", error);
            })

        } else {
            console.log("No such document!");
        }
    }).catch(function (error) {
        console.log("Error gettting documents: ", error);
    });
});
1

1 Answers

0
votes

Here is how I wound up solving this. The key items were setting the response encoding to 'base64' for the call to firestorage. The other item is using async and await in order to wait for each cell element image to populate pdfdoc before moving on to the next cell element to be rendered.

if (cellEl.type == "Image") {await toBase64(cellEl.url, x, y, pdfdoc);}

function toBase64(url, x, y, pdfdoc) {
    return new Promise(async(resolve, reject) => {
        var req = await https.get(url, (res) => {
            res.setEncoding('base64');
            let body = "data:" + res.headers["content-type"] + ";base64,";
            res.on('data', (d) => {
                body += d;
            });
            res.on('end', () => {
                pdfdoc.image(body, x, y);
                resolve(res);
            });
        });
        req.on('error', err => {
            console.error('error!');
            reject(err);
        });

    });

}