Node.js Puppeteercannot render pdf export css correctly - node.js

I'm trying to get the html output of a website as a pdf with the width and height I specified with Node.js. However, on some sites, it can print without problems, while on some sites, the output is very distorted. I couldn't figure out why. When I turn on the handless feature and examine it, the page is created very smoothly, but in the pdf output, the images are corrupted like the pdf below. Can you please help? Where am I doing wrong?
https://pdfalarm.s3.eu-west-1.wasabisys.com/https%253a%252f%252frun-fix.com%252f30018001652834515056.pdf
const gopdf = async (url, width, height) => {
return new Promise(async (resolve, reject) => {
try {
var name = encodeURIComponent(String(url + width + height + Date.now()) + '.pdf')
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.setViewport({ 'width': width, 'height': height });
await page.goto(url, { waitUntil: 'networkidle2' });
const optionsPDF = {
printBackground: true,
width: width + "px",
height: height + "px",
margin: { top: 0, right: 0, bottom: 0, left: 0 }
}
await delay(800)
const pdf = await page.pdf(optionsPDF);
await browser.close();
aws.upl(name, pdf)
.then(res => {
resolve({
status: res.data.Location?true:false,
data: res.data.Location?res.data.Location:false
})
})
} catch (err) {
resolve({
status: false,
data: err
})
}
})
}
If I put a wait time of 800 ms after the page is created, it looks corrupted as in the pdf in the link.

Related

Error while sending a pdf from Node.js server to Angular client

I try to send a pdf made by Puppeteer to my Angular client but I get this error:
The server-side code:
app.get('/convCVtoPDF',auth, jsonParser,async (req,res)=>{
const id = req.header('id')
const options = {
margin: {
top: '50px',
right: '50px',
bottom: '50px',
left: '50px',
},
pageSize: 'A4',
landscape: false,}
try {
const browser = await puppeteer.launch({"headless": true});
const page = await browser.newPage();
await page.setExtraHTTPHeaders({
'id':id
});
await page.goto('http://localhost:4200/BasicCvPDF');
const pdf = await page.pdf(options);
await browser.close();
// Send the PDF buffer in the response
res.setHeader('Content-Type', 'application/pdf');
res.send(pdf);
} catch (e) {
console.log(e);
res.status(500).send();
}
})
And the client side is:
async ngOnInit(){
await this.http.get<any>('http://localhost:3000/getUserById',{
headers:new HttpHeaders({'Content-Type':'application/json'})
}).toPromise()
}
I am sure the PDF is valid because If I specify a path for Puppeteer to save it, I can open and view it without a problem.
Any thoughts on why it can happen?

Render a page with express, then print it with puppeteer

I want to send the form infos to the same url, get the documents that match the infos on the url, render the page with the documents and then print that page with puppeteer in a pdf format (every document will become a page and then a pdf document at the end).
this is the code I am using:
the route that receives the request in documentRoutes:
router.use(
'/:document',
documentController.getDocument,
documentController.printDocument
);
the middlewares:
getDocument:
exports.getDocument = async (req, res, next) => {
const course = req.params.document.split('-')[0];
const document = req.params.document.split('-')[1];
const documents = await Document.find({ CODE: course });
res.status(200).render(`${document}`, {
title: document,
documents,
});
next();
};
printDocument:
exports.printDocument = async (req, res, next) => {
const url = req.params.document;
const browser = await puppeteer.launch({
executablePath: '/usr/bin/chromium',
});
const page = await browser.newPage();
await page.goto(`http://localhost:3000/${url}`);
await page.pdf({
path: path.join(appRoot.path, 'downloads/certificate.pdf'),
format: 'A4',
margin: {
top: '1cm',
left: '1cm',
right: '1cm',
bottom: '1cm',
},
});
await browser.close();
console.log('pdf generated successfully');
await browser.close();
next();
};
error message:
Error Unexpected extraInfo events for request 70460993C8EB5E671E4D72B16003040A

Puppeteer - PDFPrint but pdf is blank

I have a problem with puppeteer and pdf printing.
Everything looks ok, no error code,... but pdf is blank.
and templateHtml is good ! maybe it's an error in functions call await ?
Please help me !
async function createPDF(quotation, quoteurl, quotname){
var templateHtml = fs.readFileSync(path.join(process.cwd(), './templates/quotation2.html'), 'utf8');
//console.log(templateHtml);
var template = handlebars.compile(templateHtml);
//console.log(template);
var html = template(quotation);
//console.log(html);
var pdfPath = path.join(`${quoteurl}`, `${quotname}.pdf`);
console.log(pdfPath)
var options = {
width: '1230px',
headerTemplate: "<p></p>",
footerTemplate: "<p></p>",
displayHeaderFooter: false,
margin: {
top: "10px",
bottom: "30px"
},
printBackground: true,
path: pdfPath
}
const browser = await puppeteer.launch({
//args: ['--no-sandbox'],
headless: true
});
var page = await browser.newPage();
console.log('OH YES');
await page.goto(`data:text/html;charset=UTF-8,${html}`,{ waitUntil: ['domcontentloaded', 'load'] }).then(function (response) {
// page.emulateMedia('screen')
page.pdf({ path: pdfPath
, format: 'letter' })
.then(function (res) {
browser.close();
}).catch(function (e) {
browser.close();
})
})
}
i don't understand why it's not working.
I Change with this content and it works ! but the logo.png doesn't appear in the pdf (or it's in the same folder than html file.
Is there something to add to puppeteer to include image file in the pdf ? (it's declared in html file)
await page.setContent(html,{ waitUntil: ['domcontentloaded', 'load', "networkidle0"] }).then(function (response) {
// page.emulateMedia('screen')
page.pdf({ path: pdfPath,
format: 'A4',
printBackground: true,
margin: {
top: '20px',
bottom: '20px',
right: '20px',
left: '20px' }})
.then(function (res) {
browser.close();
}).catch(function (e) {
browser.close();
})
})

How to print something on every page on page.pdf

I need to setup an anchor tag on every page. Is that possible?
<a href='#toc'>toc</a>
I tried implementing it like this
async function printPDF(domain,filename){
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.setViewport({ width: 600, height: 400 })
await page.goto(domain + '/docs/' + filename + '.html', {waitUntil: 'networkidle0'});
const pdf = await page.pdf({
format: 'A4',
scale : 1,
printBackground : true,
headerTemplate: "<a href='#toc'>toc</a>" ,
displayHeaderFooter : true,
margin: { top: "2cm", bottom: "2cm", left: "2cm", right: "2cm" }
});
await browser.close();
return pdf;
}
But it's not working. Maybe because the header is treated as seperate from the content so it's not detecting the anchor?

Giving timeout after evaluate in puppeteer is not working

I have a page which contains few visualizations. I have to evaluate and resize the charts. while evaluating, it takes few seconds to resize. the Previously I did this using phantom. I use to give timeout in the callback function and it worked perfectly. I'm trying the same using puppeteer which is not working. Not working in the sense, the resizing did not happen even after the timeout.
sample puppeteer code(not working)
const puppeteer = require('puppeteer');
const fs = require('fs');
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
(async() => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 2048, height: 1024 });
await page.goto("http://localhost:3000", { timeout: 180000 });
await timeout(5000);
page.on('console', msg => console.log('PAGE LOG:', msg.text()));
await page.evaluate(function() {
for (var i = 0; i < $('.react-grid-item').length; i++) {
$('.react-grid-item:eq(' + i + ')').css({
position: 'absolute',
height: '300px',
width: '100%',
top: (i * 300) + 'px',
left: '0px'
})
}
})
.then(function() {
setTimeout(function() {
page.pdf({
path: 'test' + new Date() + '.pdf',
// height: 7777,
// width: 2048,
format: 'a4',
displayHeaderFooter: false,
margin: {
top: "75px",
bottom: "75px"
}
});
// console.log(sum);
}, 5000);
}).catch(function(err) {
console.error(err);
})
})();
using await/async try follow like this:
await page.evaluate((a, b) => {
return a + b
}, 1, 2).then((sum) => {
setTimeout(() => {
console.log(sum)
}, 2000)
})

Resources