I am trying to set up a NodeJs API, for printing pdf using several templates.
My problem is that 2 of those templates use Html customized by our users.
I am trying phantomJS, because so far it was the only I managed to get to work.
However, the logo image is giving me a hard time to insert in the header.
So, I need to know if there is any better aproach or if is possible to make it work.
Since every user has a different logo, I need to insert different images when printing.
What I have tried so far:
import { Request, Response } from 'express';
import { readFileSync } from 'fs';
import Handlebars from 'handlebars';
import pdf, { CreateOptions } from 'html-pdf';
import { encode } from 'node-base64-image';
var path = require('path');
export const generate = async (req: any, res: Response) => {
const dateTime = new Date().toISOString().replace('T', ' ').replace(':', '-').substr(0, 19).replace(' ', '_').replace(':', '-');
const {
name, email, cpfCnpj, documentName = 'textAuthDoc', docTitle = 'Documento de Autorização',
accountingOfficeId = "5cc6adc6-were-1542-etr-acbf0030000"
} = req.body;
const logoPath = await getLogoAsync('https://logowebpath', accountingOfficeId);
const logoImage = await getLogo('https://logowebpath');
var htmlHeader = readFileSync(`src\_templates\header.html`, 'utf8')
var templateHeader = Handlebars.compile((htmlHeader));
var dataHeader = {"headerTitle": docTitle, "logoImage": logoPath};
var resultHeader = templateHeader(dataHeader);
var options: CreateOptions = {
format: 'A4',
orientation: 'portrait',
border: {
// top: '70',
right: '25',
bottom: '60',
left: '25'
},
header: {
height: "70",
contents: resultHeader
},
footer: {
height: '60'
},
zoomFactor: '1.5',
type: 'pdf'
};
var htmlTemplate = await readFileSync(`src/_templates/${documentName}.html`, 'utf8') + `</html>`;
var templateHandled = Handlebars.compile((htmlTemplate));
var data = { "Cliente": name, "email": email, "CPFCNPJ_Cliente": cpfCnpj, "logoImage": logoImage};
var result = `<div style='display:none'><img src='${logoImage}' /></div>`.concat(templateHandled(data));
pdf.create(result, options).toStream(function(err, stream) {
if (err) {
console.log(err)
} else {
res.set('Content-type', 'application/pdf');
stream.pipe(res)
}
});
}
It prints the customized html nicely in the body of Pdf file, but the header have no image.
I tried these two functions to download the images. They work, but I can’t manage to insert in the resulting PDF.
async function getLogo(logoPath: string): Promise<string> {
// encoding a jpg to base64
const url = logoPath;
const options = {
string: true,
headers: {
"User-Agent": "my-app"
}
};
// writing to file named 'example.jpg'
const image = await encode(url, options);
// await decode(image, { fname: 'example', ext: 'jpg' });
return image.toString('base64');
}
async function getLogoAsync(imageUrl: string, accountingOfficeId: string, ) {
const https = require('https');
const fs = require('fs');
var imagePath = path.join(__dirname + '/../../../_images', `${accountingOfficeId}.${'png'}`);
const imageName = imagePath;
const file = await fs.createWriteStream(imageName);
await https.get(imageUrl, (response: { pipe: (arg0: any) => void; }) => {
console.log(`Image downloaded as ${response.pipe}`);
response.pipe(file);
file.on('finish', () => {
file.close();
// // console.log(`Image downloaded as ${imageName}`);
});
}).on('error', (err: { message: any; }) => {
fs.unlink(imageName);
console.error(`Error downloading image: ${err.message}`);
});
return imageName;
}
Sorry for the messy code, I am doing tons of tests.
I am using Nodejs 20.15.0
Here is the html header:
<div id="container" style="overflow: hidden; width: auto">
<div id="inner" style="overflow: hidden; width: auto">
<div style="float: left; height: 104px; display: flex">
<span
style="
display: inline-block;
align-self: flex-end;
font-size: 35px;
font-weight: 700;
color: #00f;
"
>{{headerTitle}}</span
>
</div>
<div style="float: right; height: 104px; text-align: bottom">
<img
src="{{logoImage}}"
alt="logo"
width="auto"
height="104"
style="background-color: antiquewhite;"
/>
</div>
</div>
</div>
I am planning on make the other printing, Tables and Markdown with jsPDF Autotable and @ezpaarse-project/jspdf-md.
Ps.: If there is any better aproach(even other libs), please let me know.
Ps.2: I am new to NodeJs server side…
Ps.3: I am thinking about puppeteer too, however couldn’t make a viable test yet