Generating PDF from Dynamic HTML in Vue: Handling Incomplete Tags, Images, and Backend Integration
This article explains how to export HTML content—often fetched from the web with irregular tags and asynchronous images—into PDF files using Vue, html2pdf.js, jsPDF, and html2canvas, while also covering backend alternatives and practical CSS fixes.
Requirement Overview
The project needs to fetch third‑party articles, store them in a database, and let the front‑end display the raw HTML. Later the client requested a PDF export feature, but the HTML structures are often malformed (missing closing tags) and contain images loaded asynchronously.
Why the Backend Struggles
When converting the stored HTML to PDF on the server, third‑party libraries fail on malformed tags and on images that have not finished loading, causing exceptions.
Backend Suggestion
The backend developer noticed that browsers can print the page directly and suggested that the front‑end handle the export.
Front‑End Exploration
Initially the vue-print-nb plugin was considered, but it only triggers the browser print dialog and does not produce a downloadable PDF.
A more suitable library, html2pdf.js , was found. It combines html2canvas and jsPDF to render a DOM element to canvas, then to an image, and finally to a PDF.
npm install html2pdf.jsBasic usage example:
<template>
<div class="container">
<button @click="generatePDF">Download PDF</button>
</div>
</template>
<script setup>
import html2pdf from 'html2pdf.js';
let element = `
<h1>前端人</h1>
<p>学好前端,走遍天下都不怕</p>
<div>前端强,前端狂,交互特效我称王!</div>
<p>JS 写得好,需求改不了!</p>
<p>React Vue 两手抓,高薪 offer 到你家!<p>
<p>浏览器里横着走, bug 见我都绕道!</p>
<p>Chrome 调试一声笑, IE 泪洒旧时光!</p>
<span>Git 提交不留情,版本回退我最行!</span>
`;
function generatePDF() {
const opt = {
margin: 10,
filename: 'hello_world.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2 },
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
};
html2pdf().from(element).set(opt).save();
}
</script>Problems with Images
When the HTML contains <img> tags, the exported PDF shows blank placeholders because the image request is asynchronous and may not finish before the PDF generation runs.
Think about it: the image is fetched via an async GET request, so the canvas is drawn before the image data is available.
Solution: Synchronous Image Loading (Base64)
Convert each image URL to a Base64 data URL before passing the HTML to html2pdf.js . This guarantees the image is embedded in the PDF even without network access.
async function convertImagesToBase64(htmlString) {
const tempDiv = document.createElement('div');
tempDiv.innerHTML = htmlString;
const images = tempDiv.querySelectorAll('img');
for (const img of images) {
try {
const base64 = await getBase64FromUrl(img.src);
img.src = base64;
} catch (e) {
console.error(`Failed to convert ${img.src}:`, e);
}
}
return tempDiv.innerHTML;
}
function getBase64FromUrl(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
resolve(canvas.toDataURL('image/png'));
};
img.onerror = () => reject(new Error('Image load error'));
img.src = url;
});
}
function generatePDF() {
convertImagesToBase64(element).then(convertedHtml => {
const opt = { /* same options as before */ };
html2pdf().from(convertedHtml).set(opt).save();
}).catch(err => console.error('Conversion error:', err));
}Handling Oversized Images
Large images can overflow the page. Adding a CSS rule before PDF generation solves the issue:
img {
max-width: 100%;
max-height: 100%;
vertical-align: middle;
height: auto !important;
width: auto !important;
margin: 10px 0;
}Inject this style into the HTML string:
element = `
` + element;Combined Front‑End & Back‑End Approach
One comment suggested sending the fully‑rendered innerHTML (which the browser automatically fixes) to the backend, where a Java library com.itextpdf.html2pdf can generate the PDF without needing Base64 conversion, but it requires proper Chinese font configuration.
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>html2pdf</artifactId>
<version>5.0.2</version>
</dependency>Conclusion
The article documents the whole journey: from the initial requirement, through backend difficulties, to a front‑end‑centric solution using html2pdf.js , Base64 image conversion, CSS adjustments, and an optional Java backend fallback. It also reflects on the collaborative nature of front‑end and back‑end work.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.