Reputation: 788
I am creating a job listing scraper that goes to several job sites and scrapes jobs and then aggregates them together. I want to create an endpoint that if I like a job, I can automatically download my own cover letter with certain dynamic data based on the job. Currently, I have taken the HTML of my cover letter from Google Docs and created a string out of it. I set up a GET endpoint that should take this HTML string and send it to the requestor as a downloadable PDF. Currently, the PDF is downloading but the document says 'failed to load PDF document'. Any help would be greatly appreciated in solving this issue.
Here is my express app:
const express = require('express');
const dotenv = require('dotenv');
const app = express();
dotenv.config();
app.get('/cover-letter', (req, res) => {
const coverLetter = `<html><head><meta content="text/html; charset=UTF-8" http-equiv="content-type"><style type="text/css">@import url('https://themes.googleusercontent.com/fonts/css?kit=1ZpBgFLQKwrA6c9iLOONVLLukJZ0tncL9DlcRrH6sPk');ol{margin:0;padding:0}table td,table th{padding:0}.c1{border-right-style:solid;padding-top:0pt;border-top-width:0pt;border-right-width:0pt;padding-left:0pt;padding-bottom:0pt;line-height:1.0;border-left-width:0pt;border-top-style:solid;background-color:#ffffff;border-left-style:solid;border-bottom-width:0pt;border-bottom-style:solid;orphans:2;widows:2;text-align:justify;padding-right:0pt}.c21{border-right-style:solid;padding:0pt 5.4pt 0pt 5.4pt;border-bottom-color:#000000;border-top-width:0pt;border-right-width:0pt;border-left-color:#000000;vertical-align:top;border-right-color:#000000;border-left-width:0pt;border-top-style:solid;border-left-style:solid;border-bottom-width:0pt;width:311.4pt;border-top-color:#000000;border-bottom-style:solid}.c16{border-right-style:solid;padding:0pt 5.4pt 0pt 5.4pt;border-bottom-color:#000000;border-top-width:0pt;border-right-width:0pt;border-left-color:#000000;vertical-align:top;border-right-color:#000000;border-left-width:0pt;border-top-style:solid;border-left-style:solid;border-bottom-width:0pt;width:195.9pt;border-top-color:#000000;border-bottom-style:solid}.c19{padding-top:5pt;border-top-width:3pt;border-top-color:#000000;padding-bottom:0pt;line-height:1.0;orphans:2;border-top-style:solid;widows:2;text-align:left}.c5{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:11pt;font-family:"Arial";font-style:normal}.c0{color:#000000;font-weight:400;text-decoration:none;vertical-align:baseline;font-size:10pt;font-family:"Tahoma";font-style:normal}.c9{padding-top:0pt;padding-bottom:0pt;line-height:1.0;orphans:2;widows:2;text-align:left}.c7{padding-top:0pt;padding-bottom:0pt;line-height:1.0;orphans:2;widows:2;text-align:right}.c11{padding-top:0pt;padding-bottom:6pt;line-height:1.0;orphans:2;widows:2;text-align:left}.c18{padding-top:0pt;padding-bottom:0pt;line-height:1.0;orphans:2;widows:2;text-align:justify}.c12{padding-top:0pt;padding-bottom:0pt;line-height:1.1500000000000001;text-align:left;height:11pt}.c10{margin-left:-5.4pt;border-spacing:0;border-collapse:collapse;margin-right:auto}.c8{color:#000000;text-decoration:none;vertical-align:baseline;font-style:normal}.c2{vertical-align:baseline;font-size:10pt;font-family:"Tahoma";font-weight:400}.c22{font-weight:400;font-size:18pt;font-family:"Tahoma"}.c6{background-color:#ffffff;max-width:496.8pt;padding:57.6pt 57.6pt 57.6pt 57.6pt}.c15{font-weight:400;font-size:14.5pt;font-family:"Tahoma"}.c14{font-weight:400;font-size:11pt;font-family:"Times New Roman"}.c4{font-size:10pt;font-family:"Tahoma";font-weight:400}.c20{font-size:18pt;font-family:"Tahoma";font-weight:700}.c3{font-size:10.5pt;font-family:"Arial";font-weight:400}.c13{height:11pt}.c17{height:0pt}.title{padding-top:24pt;color:#000000;font-weight:700;font-size:36pt;padding-bottom:6pt;font-family:"Times New Roman";line-height:1.0;page-break-after:avoid;orphans:2;widows:2;text-align:left}.subtitle{padding-top:18pt;color:#666666;font-size:24pt;padding-bottom:4pt;font-family:"Georgia";line-height:1.0;page-break-after:avoid;font-style:italic;orphans:2;widows:2;text-align:left}li{color:#000000;font-size:11pt;font-family:"Times New Roman"}p{margin:0;color:#000000;font-size:11pt;font-family:"Times New Roman"}h1{padding-top:24pt;color:#000000;font-weight:700;font-size:24pt;padding-bottom:6pt;font-family:"Times New Roman";line-height:1.0;page-break-after:avoid;orphans:2;widows:2;text-align:left}h2{padding-top:18pt;color:#000000;font-weight:700;font-size:18pt;padding-bottom:4pt;font-family:"Times New Roman";line-height:1.0;page-break-after:avoid;orphans:2;widows:2;text-align:left}h3{padding-top:14pt;color:#000000;font-weight:700;font-size:14pt;padding-bottom:4pt;font-family:"Times New Roman";line-height:1.0;page-break-after:avoid;orphans:2;widows:2;text-align:left}h4{padding-top:12pt;color:#000000;font-weight:700;font-size:12pt;padding-bottom:2pt;font-family:"Times New Roman";line-height:1.0;page-break-after:avoid;orphans:2;widows:2;text-align:left}h5{padding-top:11pt;color:#000000;font-weight:700;font-size:11pt;padding-bottom:2pt;font-family:"Times New Roman";line-height:1.0;page-break-after:avoid;orphans:2;widows:2;text-align:left}h6{padding-top:10pt;color:#000000;font-weight:700;font-size:10pt;padding-bottom:2pt;font-family:"Times New Roman";line-height:1.0;page-break-after:avoid;orphans:2;widows:2;text-align:left}</style></head><body><div class="c6"><div><p class="c9 c13"><span class="c8 c14"></span></p></div><p class="c12"><span class="c5"></span></p><a id="t.cf2cb48f4187c740031c3f8d361f8f564f9a673c"></a><a id="t.0"></a><table class="c10"><tbody><tr class="c17"><td class="c21" colspan="1" rowspan="1"><p class="c11"><span class="c20">Test</span></p></td><td class="c16" colspan="1" rowspan="1"><p class="c7"><span class="c2">710 </span><span class="c4">Example</span><span class="c0"> Dr. </span></p><p class="c7"><span class="c4">New York</span><span class="c2">, </span><span class="c4">New York</span><span class="c2"> </span><span class="c4">55555</span></p><p class="c7"><span class="c2">m: </span><span class="c4">618</span><span class="c2">.</span><span class="c4">235</span><span class="c2">.</span><span class="c4">1234</span><span class="c0"> </span></p><p class="c7"><span class="c4">example</span><span class="c0">@gmail.com</span></p></td></tr></tbody></table><p class="c13 c19"><span class="c8 c15"></span></p><p class="c9"><span class="c4">8/3/2020</span></p><p class="c9 c13"><span class="c0"></span></p><p class="c9 c13"><span class="c0"></span></p><p class="c9 c13"><span class="c0"></span></p><p class="c9"><span class="c2">Dear </span><span class="c4">Hiring Manager</span><span class="c0">,</span></p><p class="c13 c18"><span class="c0"></span></p><p class="c1"><span class="c3 c8">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas cursus commodo felis. Suspendisse faucibus in lectus sed viverra. Curabitur turpis arcu, finibus vel porta et, commodo sit amet orci. Pellentesque rutrum finibus ipsum ac pharetra. Aenean varius purus erat, id rhoncus purus ornare eget. Nunc vel accumsan diam, sed sagittis augue. Aenean nec mauris dolor. Mauris vel pharetra magna. Nunc convallis id metus sed facilisis. In sed fringilla ligula.</span></p><p class="c1"><span class="c8 c3">Phasellus ut gravida nibh, vel dapibus lectus. Integer ut dui commodo tortor bibendum eleifend vel pharetra nunc. Donec at luctus leo. Praesent at tortor euismod, faucibus ante non, dignissim purus. Suspendisse sodales, lacus eu tincidunt sagittis, dui velit facilisis lacus, quis hendrerit arcu est ut urna. Praesent auctor arcu sit amet ullamcorper pellentesque. Sed dapibus quam eu lacus tempus, a luctus risus consequat. Cras id ipsum efficitur, feugiat sapien eu, mattis nunc. Proin iaculis ultricies ex ut volutpat. Vivamus sit amet consectetur nibh. Maecenas vel dictum ligula, varius porttitor elit. Suspendisse mauris nunc, sodales sit amet dignissim at, cursus dapibus est.</span></p><p class="c1"><span class="c8 c3">Nunc scelerisque efficitur diam non placerat. Aliquam enim velit, viverra id ante non, viverra aliquam quam. Quisque vel diam leo. Etiam tincidunt pellentesque mi sed eleifend. Fusce sollicitudin euismod enim, ac vestibulum metus interdum nec. Ut elementum metus in quam vehicula, sit amet scelerisque urna mollis. Praesent et arcu magna. Donec convallis sed urna vel euismod. Proin auctor faucibus leo mattis laoreet. Donec varius faucibus leo, non porttitor eros. Integer efficitur porta nunc eu ornare. Ut vel tellus nec nisi consequat dignissim.</span></p><p class="c1"><span class="c8 c3">Nunc dictum metus sed metus mattis aliquet. Vestibulum et justo tristique, fermentum metus ut, sagittis massa. Aliquam erat volutpat. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Quisque ut eros ut ligula euismod gravida. Suspendisse consequat tincidunt urna vitae tempor. Nam rhoncus dapibus ante, at iaculis nisl molestie nec. Nunc tincidunt congue viverra.</span></p><p class="c1"><span class="c3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam viverra, nunc sit amet venenatis placerat, arcu risus imperdiet dolor, non vulputate metus nunc pellentesque nulla. Quisque pulvinar interdum maximus. Mauris quis est a nisl vulputate sagittis. Suspendisse laoreet a odio ac tincidunt. Donec suscipit, turpis blandit sollicitudin iaculis, urna justo faucibus velit, varius vehicula purus urna sed orci. Nulla facilisi. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</span></p><p class="c18"><span class="c0">Sincerely,</span></p><p class="c18 c13"><span class="c0"></span></p><p class="c9"><span class="c4">Test</span></p><p class="c9 c13"><span class="c0"></span></p><div><p class="c9 c13"><span class="c8 c14"></span></p></div></div></body></html>`
res.setHeader("Content-Type", "application/pdf");
res.status(200).send(coverLetter);
});
app.listen(process.env.PORT, () => {
console.log(`Listening on port ${process.env.PORT}...`)
})
Upvotes: 0
Views: 141
Reputation: 30725
You could try using puppeteer to generate the PDF document from your html.
The code below will do this, though I've had to strip the style tag (one could possibly add this inline, or figure out how to make puppeteer play nice with it)
const express = require('express');
const dotenv = require('dotenv');
const app = express();
dotenv.config();
async function generatePdf(html) {
const puppeteer = require("puppeteer");
const browser = await puppeteer.launch({
args: ['--no-sandbox'],
headless: true
});
const page = await browser.newPage();
await page.goto(`data:text/html,${html}`, {
waitUntil: 'networkidle0'
});
const options = {
format: 'A4',
margin: { top: "60px", bottom: "60px" },
printBackground: true,
}
const pdf = await page.pdf(options);
await browser.close();
return pdf;
}
app.get('/cover-letter', async (req, res) => {
const coverLetter = `<html><head><meta content="text/html; charset=UTF-8" http-equiv="content-type"></head><body><div class="c6"><div><p class="c9 c13"><span class="c8 c14"></span></p></div><p class="c12"><span class="c5"></span></p><a id="t.cf2cb48f4187c740031c3f8d361f8f564f9a673c"></a><a id="t.0"></a><table class="c10"><tbody><tr class="c17"><td class="c21" colspan="1" rowspan="1"><p class="c11"><span class="c20">Test</span></p></td><td class="c16" colspan="1" rowspan="1"><p class="c7"><span class="c2">710 </span><span class="c4">Example</span><span class="c0"> Dr. </span></p><p class="c7"><span class="c4">New York</span><span class="c2">, </span><span class="c4">New York</span><span class="c2"> </span><span class="c4">55555</span></p><p class="c7"><span class="c2">m: </span><span class="c4">618</span><span class="c2">.</span><span class="c4">235</span><span class="c2">.</span><span class="c4">1234</span><span class="c0"> </span></p><p class="c7"><span class="c4">example</span><span class="c0">@gmail.com</span></p></td></tr></tbody></table><p class="c13 c19"><span class="c8 c15"></span></p><p class="c9"><span class="c4">8/3/2020</span></p><p class="c9 c13"><span class="c0"></span></p><p class="c9 c13"><span class="c0"></span></p><p class="c9 c13"><span class="c0"></span></p><p class="c9"><span class="c2">Dear </span><span class="c4">Hiring Manager</span><span class="c0">,</span></p><p class="c13 c18"><span class="c0"></span></p><p class="c1"><span class="c3 c8">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas cursus commodo felis. Suspendisse faucibus in lectus sed viverra. Curabitur turpis arcu, finibus vel porta et, commodo sit amet orci. Pellentesque rutrum finibus ipsum ac pharetra. Aenean varius purus erat, id rhoncus purus ornare eget. Nunc vel accumsan diam, sed sagittis augue. Aenean nec mauris dolor. Mauris vel pharetra magna. Nunc convallis id metus sed facilisis. In sed fringilla ligula.</span></p><p class="c1"><span class="c8 c3">Phasellus ut gravida nibh, vel dapibus lectus. Integer ut dui commodo tortor bibendum eleifend vel pharetra nunc. Donec at luctus leo. Praesent at tortor euismod, faucibus ante non, dignissim purus. Suspendisse sodales, lacus eu tincidunt sagittis, dui velit facilisis lacus, quis hendrerit arcu est ut urna. Praesent auctor arcu sit amet ullamcorper pellentesque. Sed dapibus quam eu lacus tempus, a luctus risus consequat. Cras id ipsum efficitur, feugiat sapien eu, mattis nunc. Proin iaculis ultricies ex ut volutpat. Vivamus sit amet consectetur nibh. Maecenas vel dictum ligula, varius porttitor elit. Suspendisse mauris nunc, sodales sit amet dignissim at, cursus dapibus est.</span></p><p class="c1"><span class="c8 c3">Nunc scelerisque efficitur diam non placerat. Aliquam enim velit, viverra id ante non, viverra aliquam quam. Quisque vel diam leo. Etiam tincidunt pellentesque mi sed eleifend. Fusce sollicitudin euismod enim, ac vestibulum metus interdum nec. Ut elementum metus in quam vehicula, sit amet scelerisque urna mollis. Praesent et arcu magna. Donec convallis sed urna vel euismod. Proin auctor faucibus leo mattis laoreet. Donec varius faucibus leo, non porttitor eros. Integer efficitur porta nunc eu ornare. Ut vel tellus nec nisi consequat dignissim.</span></p><p class="c1"><span class="c8 c3">Nunc dictum metus sed metus mattis aliquet. Vestibulum et justo tristique, fermentum metus ut, sagittis massa. Aliquam erat volutpat. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Quisque ut eros ut ligula euismod gravida. Suspendisse consequat tincidunt urna vitae tempor. Nam rhoncus dapibus ante, at iaculis nisl molestie nec. Nunc tincidunt congue viverra.</span></p><p class="c1"><span class="c3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam viverra, nunc sit amet venenatis placerat, arcu risus imperdiet dolor, non vulputate metus nunc pellentesque nulla. Quisque pulvinar interdum maximus. Mauris quis est a nisl vulputate sagittis. Suspendisse laoreet a odio ac tincidunt. Donec suscipit, turpis blandit sollicitudin iaculis, urna justo faucibus velit, varius vehicula purus urna sed orci. Nulla facilisi. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.</span></p><p class="c18"><span class="c0">Sincerely,</span></p><p class="c18 c13"><span class="c0"></span></p><p class="c9"><span class="c4">Test</span></p><p class="c9 c13"><span class="c0"></span></p><div><p class="c9 c13"><span class="c8 c14"></span></p></div></div></body></html>`
const pdfBuffer = await generatePdf(coverLetter);
res.setHeader("Content-Type", "application/pdf");
res.setHeader("Content-Disposition","inline; filename=\"test_resume_company_name.pdf\"");
res.status(200).send(pdfBuffer);
});
app.listen(process.env.PORT, () => {
console.log(`Listening on port ${process.env.PORT}...`)
})
Upvotes: 1