Revel 本身不生成 PDF,需配合 Puppeteer(高保真HTML转PDF)、html2canvas+jsPDF(前端截图导出)或 go-pdf/unidoc(服务端直出)三种方案;关键在字体配置、分页控制与职责分离。

Revel 后端返回 HTML 模板供 Puppeteer 渲染 PDF
这是目前最可靠、样式还原度最高的生产级做法,尤其适合带图表、复杂 CSS、响应式布局的报表页。
- Puppeteer 在服务端启动无头 Chromium,加载 Revel 渲染出的 HTML 页面(如
/report/123),调用page.pdf()直接输出 PDF 流 - Revel 只需确保该路由返回干净、可复现的 HTML(禁用 JS 动态渲染、避免 localStorage 依赖),并启用
@media print样式微调分页 - 关键配置:必须设置
printBackground: true,否则 CSS 背景色/图将丢失;中文需提前在系统安装 Noto Sans CJK 或思源字体,并在 Puppeteer 启动时通过--font-render-hinting=none和--disable-gpu降低乱码风险 - 注意不要在 Revel 中用
RenderHTML返回含内联style的巨量动态样式——Puppeteer 加载慢且易触发内存溢出;建议抽离为静态 CSS 文件,由 Revel 的Static路由托管
Revel 提供 JSON 接口 + 前端用 html2canvas + jsPDF
适用于轻量、内部系统、用户接受“导出即截图”且不需文字复制的场景。
- Revel 定义一个 API(如
GET /api/report-data?id=123),返回结构化数据,不渲染 HTML - 前端 Vue/React 拿到数据后,用模板组件重新渲染一个专用 区域(隐藏导航栏、按钮等非内容元素)
- 调用
html2canvas(element, { scale: 2, useCORS: true })截图,再传给jsPDF分页拼接;注意scale过高会导致内存崩溃,useCORS必须开启才能加载跨域图表资源(如 ECharts 的 canvas)- 常见坑:
html2canvas不支持transform、filter、CSS Grid 复杂嵌套;若页面含 SVG,需先转为或用canvg预渲染Revel 集成 go-pdf 或 unidoc 服务端直出 PDF
适合格式固定、无交互、强合规要求(如带数字签名、PDF/A 归档)的单据类文档。
- Revel Controller 中调用
unidoc/pdf/core或go-pdf构建 PDF:手动写入文本、表格、图片、页眉页脚;不依赖浏览器,体积小、速度快 - 缺点明显:所有样式、布局、分页逻辑全靠代码控制;中文需显式注册字体(如
pdf.AddTTFFont("simhei", "./fonts/simhei.ttf")),否则报invalid font - 若需保留超链接或书签,
unidoc支持pdf.AddLink,但go-pdf基本不支持;分页需自己计算每页高度,遇到长表格极易断行错位 - 不要尝试把整个 HTML 字符串喂给这类库——它们不是 HTML 解析器,只认结构化指令
别让 Revel 承担不该承担的事
比如在 Controller 里调
window.print()、或试图用 Revel 模板语法生成 PDF 内容——这些都无效。Revel 是服务端框架,没有 DOM,也没有浏览器上下文。真正容易被忽略的是字体和分页的耦合性:即使你选了 Puppeteer,若 HTML 中用了
font-family: "PingFang SC"但服务器没装该字体,PDF 里中文照样变方块;即使用了jsPDF,若未对element.scrollHeight做分页切片,第二页内容就直接被截断。这些不是框架问题,是 PDF 生成链路上每个环节的隐性契约。 - 调用











