如何用JS生成带页眉页脚的HTML转PDF?解决空白与离线问题
客户端离线HTML转PDF需求及问题解决
核心需求
- 使用JavaScript在客户端离线完成HTML转PDF
- 内容分页时,每页自动添加页眉和页脚
- 支持大数据量及客户端自定义展示属性
遇到的问题
- 使用html2pdf库生成PDF时,内容开头出现大量空白
- 尝试jsPDF库,输出效果不符合预期
问题截图:
测试用html2pdf代码
<!DOCTYPE html> <html> <head> <title>HTML to PDF Example</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script> </head> <body> <div id="content"> <h1>Welcome to html2pdf Tutorial</h1> <p> This is an example of converting HTML content to a PDF file using the html2pdf JavaScript library. </p> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p><p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. </p> </div> <button onclick="generatePDF()">Generate PDF</button> <script> function generatePDF() { const element = document.getElementById("content"); const options = { margin: 1, filename: "example.pdf", image: { type: "jpeg", quality: 0.98 }, html2canvas: { scale: 2 }, jsPDF: { unit: "in", format: "letter", orientation: "portrait" }, }; html2pdf().set(options).from(element).save(); } </script> </body> </html>
解决方案
1. 修复html2pdf开头空白问题
空白通常由边距过大、渲染偏移导致,调整配置即可:
function generatePDF() { const element = document.getElementById("content"); const options = { margin: [0.5, 0.5, 0.5, 0.5], // 缩小边距 filename: "example.pdf", image: { type: "jpeg", quality: 0.98 }, html2canvas: { scale: 2, scrollY: 0, // 强制从顶部渲染,避免滚动偏移 logging: false }, jsPDF: { unit: "in", format: "letter", orientation: "portrait" }, }; html2pdf().set(options).from(element).save(); }
额外检查#content元素的CSS,确保无多余padding/margin。
2. 为html2pdf添加分页页眉页脚
利用html2pdf的onPdfSave回调,遍历所有页面插入页眉页脚:
function generatePDF() { const element = document.getElementById("content"); const opt = { margin: [1, 0.5, 1, 0.5], // 预留页眉页脚空间 filename: "example.pdf", image: { type: "jpeg", quality: 0.98 }, html2canvas: { scale: 2 }, jsPDF: { unit: "in", format: "letter", orientation: "portrait" }, onPdfSave: function(pdf) { const totalPages = pdf.internal.getNumberOfPages(); for (let i = 1; i <= totalPages; i++) { pdf.setPage(i); // 页眉:居中标题 pdf.text("自定义文档标题", pdf.internal.pageSize.getWidth() / 2, 0.5, { align: "center" }); // 页脚:页码 pdf.text(`第 ${i} 页 / 共 ${totalPages} 页`, pdf.internal.pageSize.getWidth() / 2, 10.5, { align: "center" }); } } }; html2pdf().set(opt).from(element).save(); }
3. 替代方案:jsPDF + html2canvas手动控制
若html2pdf效果仍不理想,直接组合两个库,完全手动控制分页逻辑:
async function generatePDF() { const content = document.getElementById("content"); const canvas = await html2canvas(content, { scale: 2 }); const imgData = canvas.toDataURL("image/jpeg", 0.98); const pdf = new jsPDF("portrait", "in", "letter"); const pageWidth = pdf.internal.pageSize.getWidth(); const pageHeight = pdf.internal.pageSize.getHeight(); const imgWidth = pageWidth; const imgHeight = (canvas.height * imgWidth) / canvas.width; let heightLeft = imgHeight; let position = 0; // 第一页 pdf.addImage(imgData, "JPEG", 0, position, imgWidth, imgHeight); heightLeft -= pageHeight; pdf.text("自定义文档标题", pageWidth / 2, 0.5, { align: "center" }); pdf.text("第 1 页", pageWidth / 2, pageHeight - 0.5, { align: "center" }); // 分页处理 while (heightLeft > 0) { position = heightLeft - imgHeight; pdf.addPage(); pdf.addImage(imgData, "JPEG", 0, position, imgWidth, imgHeight); heightLeft -= pageHeight; const currentPage = pdf.internal.getNumberOfPages(); pdf.text("自定义文档标题", pageWidth / 2, 0.5, { align: "center" }); pdf.text(`第 ${currentPage} 页`, pageWidth / 2, pageHeight - 0.5, { align: "center" }); } pdf.save("example.pdf"); }
内容的提问来源于stack exchange,提问作者shreyansh goel
相关产品推荐
相关产品推荐

