前端生成 PDF 求指教

21 小时 58 分钟前
 qingshui33

vue2 中有没有将 HTML 转成 PDF 的类库,要实现的功能是:页面中一半是固定的图片,一半的数据是动态的,并且包含图表、表格类,同时需要有目录,目录中每栏对应各自的页码。

网上查了下,前端做的话,基本上都是使用 html2canvas + jsPDF 来实现,有帅哥美女用过这种方案吗,不知道是否能满足上面的功能?

我个人理解是这种功能,放后端做会更好点,但是后端说这个涉及到图表之类的,后端实现起来会比较麻烦,所以想看看前端能不能做

399 次点击
所在节点    前端开发
12 条回复
pythagorasd
21 小时 50 分钟前
问 gpt-4o 就行吧,我复制你的问题问的

在 Vue2 中,可以使用以下类库和工具组合将 HTML 页面转为 PDF ,同时满足你的需求:

1. html2canvas 和 jspdf

• html2canvas 用于将 HTML 元素渲染为 Canvas 图像。
• jspdf 用于生成 PDF 文件,可以支持动态内容、图片、表格和目录。

安装

npm install html2canvas jspdf

示例代码

以下是实现页面一半固定图片,一半动态数据(包含图表、表格)以及目录的示例代码:

import html2canvas from "html2canvas";
import jsPDF from "jspdf";

export default {
methods: {
async generatePDF() {
const pdf = new jsPDF("p", "mm", "a4"); // 创建 A4 纸张的 PDF
let yOffset = 10; // PDF 初始 Y 位置

// 添加固定的图片
const imgElement = document.getElementById("fixed-image");
const imgCanvas = await html2canvas(imgElement);
const imgData = imgCanvas.toDataURL("image/png");
pdf.addImage(imgData, "PNG", 10, yOffset, 90, 40); // 图片位置和大小
yOffset += 50;

// 添加动态内容
const contentElement = document.getElementById("dynamic-content");
const contentCanvas = await html2canvas(contentElement);
const contentData = contentCanvas.toDataURL("image/png");
pdf.addImage(contentData, "PNG", 10, yOffset, 190, 80); // 动态内容

yOffset += 90;

// 添加表格、图表
const chartElement = document.getElementById("chart-content");
const chartCanvas = await html2canvas(chartElement);
const chartData = chartCanvas.toDataURL("image/png");
pdf.addImage(chartData, "PNG", 10, yOffset, 190, 80);
yOffset += 90;

// 添加目录
pdf.addPage(); // 新页面
pdf.text("目录", 10, 10);
pdf.text("1. 固定图片...............1", 10, 20);
pdf.text("2. 动态数据...............2", 10, 30);
pdf.text("3. 图表、表格.............3", 10, 40);

// 保存 PDF
pdf.save("document.pdf");
},
},
};

关键点

• 固定图片部分:将图片元素渲染为 Canvas ,并嵌入 PDF 。
• 动态数据部分:支持动态表格、图表等通过 HTML 转换。
• 目录功能:手动添加目录页,并标记每个部分的页码。

2. pdfmake

如果你的数据主要是动态的,可以考虑使用 pdfmake 。它允许使用 JSON 格式定义 PDF 内容,支持表格、图表、目录等。

安装

npm install pdfmake

示例代码

import pdfMake from "pdfmake/build/pdfmake";
import pdfFonts from "pdfmake/build/vfs_fonts";

pdfMake.vfs = pdfFonts.pdfMake.vfs;

export default {
methods: {
generatePDF() {
const docDefinition = {
content: [
{ text: "目录", style: "header", toc: { title: { text: "目录", style: "subheader" } } },
{ text: "固定图片", style: "subheader" },
{ image: "data:image/png;base64,...", width: 400 },
{ text: "动态数据", style: "subheader" },
{
table: {
body: [
["列 1", "列 2", "列 3"],
["数据 1", "数据 2", "数据 3"],
],
},
},
{ text: "图表", style: "subheader" },
{ text: "此处插入图表..." },
],
styles: {
header: { fontSize: 22, bold: true },
subheader: { fontSize: 18, bold: true },
},
};

pdfMake.createPdf(docDefinition).download("document.pdf");
},
},
};

3. puppeteer (服务器端解决方案)

如果需要更精确的排版和打印效果,推荐使用 puppeteer 在服务器端生成 PDF 。这种方式可以直接将整个 HTML 页面渲染为 PDF 。

安装

npm install puppeteer

示例代码

const puppeteer = require("puppeteer");

async function generatePDF() {
const browser = await puppeteer.launch();
const page = await browser.newPage();

await page.goto("http://localhost:8080", { waitUntil: "networkidle2" });
await page.pdf({
path: "output.pdf",
format: "A4",
printBackground: true,
});

await browser.close();
}

generatePDF();

选择方案

1. 前端生成( html2canvas + jspdf ):适合小型、动态页面。
2. 动态内容为主( pdfmake ):适合需要大量动态生成内容(如表格、目录)。
3. 高精度排版( puppeteer ):适合对 PDF 排版有较高要求的场景。
carlton
21 小时 44 分钟前
看你写的这个复杂度,纯前端实现会有各种细节问题,更好的可能还是无头浏览器服务端生成,比如 puppeteer
wyhooo
21 小时 43 分钟前
复杂页面用 puppeteer / playwright 是效果最好的。
shadowyue
21 小时 42 分钟前
实话跟你说,前端生成 pdf 的工具,简单的内容还想,复杂的会有各种问题。pdf 格式太复杂了。
SkywalkerJi
21 小时 40 分钟前
这后端太水了吧。
duanxianze
21 小时 40 分钟前
固定格式的,提前设计模板的还可以,比如各种合同或者申请书什么的,完全自定义的,肯定 bug 一大堆
zcf0508
21 小时 40 分钟前
之前做过,生成很慢很慢,不过没做过目录。可以参考一下

https://juejin.cn/post/7330203936286359578
Puteulanus
21 小时 38 分钟前
——后端实现起来会比较麻烦

前端实现起来更麻烦啊。。
qingshui33
21 小时 37 分钟前
@pythagorasd 这个我也搜过,主要是害怕这些方案不靠谱,最后花了时间也没实现 😂

@carlton @wyhooo 好的,感谢
youyouzi
21 小时 18 分钟前
html2canvas + jsPDF 确实是用这个方案,但是你说的好像还有额外的东西
danbai
20 小时 37 分钟前
我建议你们俩一起实现 你拿 html+css 填图 他来负责 转换
qingshui33
17 小时 10 分钟前
@shadowyue @SkywalkerJi @duanxianze @zcf0508 @Puteulanus @youyouzi 明白了,看起来还是后端做起来更好一点

@danbai 目前还有一些其他位置生成 PDF 的,不过没有这么复杂,但确实是这样,前端来写对应模板,后端负责转换,我也无力吐槽了,后端 Java 说他不会 HTML ,CSS ,然后前端去写 thymeleaf 模板 😂

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1090563

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX