Use Cases (更新: 2026/6/2)

用 Claude Code 实现 PDF 生成:Playwright 发票、报告与打印 CSS

用 Claude Code 做 PDF 生成:Playwright、打印 CSS、中文字体、发票、报告、截图回归和常见坑。

用 Claude Code 实现 PDF 生成:Playwright 发票、报告与打印 CSS

很多应用都会有“下载 PDF”按钮,但真正上线后才会发现,PDF 不是网页截图。发票要有正确的金额、税额、页边距和付款信息;月报要包含表格、说明、图表和自然的分页;证书要有姓名、编号、日期和可打印的版式。如果中文字体在服务器上缺失,或者背景色没有进入 PDF,用户看到的文件就会显得不专业。

这篇文章采用最稳妥的入门路线:用 HTML 写文档,用打印 CSS 控制纸张,再用 Playwright 或 Puppeteer 的 Chromium 渲染成 PDF。不要一开始就把所有内容画到 canvas 里。图片式 PDF 看起来快,但文本不能搜索,文件更大,缩放容易模糊,也很难做视觉回归测试。Claude Code 很适合实现这类功能,但提示词必须写清楚页面尺寸、字体、分页、截图检查和禁止的脆弱做法。

官方资料建议同时打开:Playwright 的 page.pdf、Puppeteer 的 PDF generationPDFOptions、MDN 的 @page打印 CSS,以及 Claude Code overview。相关的站内阅读可以看电子表格自动化Playwright 测试测试策略

为什么优先选择 HTML 到 PDF

PDF 生成常见有三种方式。第一种是用 jsPDF 这类库按坐标画文字和线条,适合非常简单的标签或单页表单,但复杂发票会很快变成维护困难的坐标表。第二种是把页面或 canvas 变成图片再塞进 PDF,初看漂亮,实际却牺牲搜索、复制、可访问性和文件体积。第三种是把文档写成 HTML,再让浏览器用打印模式输出 PDF。

对大多数 Web 团队来说,第三种最容易维护。表格继续用 table,标题继续用 h1h2,纸张尺寸和页边距交给 @page,打印专用样式放在独立模板里。这样 Claude Code 修改的是普通 HTML、CSS、Node 脚本和 Playwright 测试,而不是难以理解的坐标计算。

flowchart TD
  A["业务数据"] --> B["HTML 模板"]
  B --> C["打印 CSS 与 @page"]
  C --> D["Chromium 渲染"]
  D --> E["PDF 文件"]
  C --> F["截图对比"]
  F --> G["审查证据"]

三个以上的真实场景

第一个场景是发票。它需要卖方、买方、明细、税率、合计、付款期限和发票编号。最危险的错误不是颜色不好看,而是四舍五入错误、税额丢失、合计栏被分页切掉,或者中文字体回退后行高变化。

第二个场景是月度报告。SEO、广告投放、SaaS 使用情况或销售分析经常需要表格、图表、截图和文字说明。这里要特别关注分页:标题不应单独留在页尾,图表不应和解释文字分离。break-inside: avoid 可以保护较小的区块,但超长表格仍要设计多页方案。

第三个场景是课程证书或完成证明。姓名、课程名、日期、证书 ID、Logo 和二维码通常在一页内完成。主要信息应保留为 HTML 文本,装饰、签名和二维码才使用图片,这样后续搜索、归档和邮件发送都更可靠。

第四个场景是内部审计报告。权限变更、部署记录、审批历史和事故摘要经常要给非工程团队阅读。建议在页脚加入生成时间、环境、应用版本和数据来源 ID,避免以后无法追溯。

给 Claude Code 的提示词

请实现 PDF 生成功能。

方向:
- 用 HTML 模板 + Playwright Chromium 输出 PDF。
- 不要把整份文档画成一张 canvas 图片。
- 使用 A4 纵向、14mm 页边距、打印背景色和 print CSS。
- 字体栈包含 Noto Sans CJK / Microsoft YaHei / sans-serif。
- 结构要能复用于发票、报告和证书。

实现:
- 新增 scripts/create-invoice-pdf.mjs。
- 从示例数据生成 out/invoice-CN-2026-0602.pdf。
- 金额用 Intl.NumberFormat 格式化。
- 用户输入必须 HTML 转义。
- page.pdf 使用 printBackground 和 preferCSSPageSize。

验证:
- 保留运行命令。
- 让打印 HTML 可以截图对比。
- 检查字体、分页、背景色和合计金额。

可复制运行的最小代码

npm init -y
npm pkg set type=module
npm i -D playwright
npx playwright install chromium
mkdir scripts out
node scripts/create-invoice-pdf.mjs
import { chromium } from "playwright";
import { mkdir } from "node:fs/promises";
import { dirname, resolve } from "node:path";

const outputPath = resolve("out/invoice-CN-2026-0602.pdf");
const money = new Intl.NumberFormat("zh-CN", { style: "currency", currency: "CNY" });

const invoice = {
  number: "CN-2026-0602",
  buyer: "上海示例科技有限公司",
  seller: "Masa Design Lab",
  issuedAt: "2026-06-02",
  items: [
    { name: "PDF 模板设计", quantity: 1, unitPrice: 5000 },
    { name: "Playwright 生成脚本", quantity: 1, unitPrice: 7000 },
    { name: "打印 CSS 与中文字体验证", quantity: 1, unitPrice: 3000 },
  ],
};

function escapeHtml(value) {
  return String(value).replace(/[&<>"']/g, (char) => ({
    "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;",
  })[char]);
}

function renderHtml(data) {
  const subtotal = data.items.reduce((sum, item) => sum + item.quantity * item.unitPrice, 0);
  const tax = Math.round(subtotal * 0.06);
  const rows = data.items.map((item) => `<tr>
    <td>${escapeHtml(item.name)}</td>
    <td class="num">${item.quantity}</td>
    <td class="num">${money.format(item.unitPrice)}</td>
    <td class="num">${money.format(item.quantity * item.unitPrice)}</td>
  </tr>`).join("");

  return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8">
  <style>
    @page { size: A4; margin: 14mm; }
    body { font-family: "Noto Sans CJK SC", "Microsoft YaHei", sans-serif; color: #202124; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
    header { display: flex; justify-content: space-between; border-bottom: 3px solid #1f5eff; padding-bottom: 14px; }
    h1 { margin: 0; font-size: 28px; }
    table { width: 100%; border-collapse: collapse; margin-top: 24px; }
    th { background: #eef3ff; text-align: left; }
    th, td { border-bottom: 1px solid #d7dce5; padding: 10px 8px; }
    .num { text-align: right; white-space: nowrap; }
    .total { margin-left: auto; width: 260px; margin-top: 20px; font-size: 18px; font-weight: 700; }
    .avoid-break { break-inside: avoid; page-break-inside: avoid; }
  </style></head><body>
  <header><h1>发票</h1><div>编号: ${escapeHtml(data.number)}<br>日期: ${escapeHtml(data.issuedAt)}</div></header>
  <p><strong>收票方:</strong> ${escapeHtml(data.buyer)}</p>
  <p><strong>开票方:</strong> ${escapeHtml(data.seller)}</p>
  <table><thead><tr><th>项目</th><th class="num">数量</th><th class="num">单价</th><th class="num">金额</th></tr></thead><tbody>${rows}</tbody></table>
  <div class="total avoid-break">合计: ${money.format(subtotal + tax)}</div>
  </body></html>`;
}

await mkdir(dirname(outputPath), { recursive: true });
const browser = await chromium.launch();
try {
  const page = await browser.newPage();
  await page.setContent(renderHtml(invoice), { waitUntil: "networkidle" });
  await page.evaluate(() => document.fonts.ready);
  await page.emulateMedia({ media: "print" });
  await page.pdf({ path: outputPath, printBackground: true, preferCSSPageSize: true, margin: { top: "0", right: "0", bottom: "0", left: "0" } });
  console.log(`Created ${outputPath}`);
} finally {
  await browser.close();
}

常见失败与检查

常见失败包括:把整份 PDF 做成图片、忘记 printBackground 导致表头背景消失、服务器缺少中文字体、外部 Logo 尚未加载就导出、长备注导致合计栏被挤到下一页、以及没有转义客户输入。用 Claude Code 修复时,要让它先复现失败,再给出最小修改,不要顺手重写整个发票系统。

视觉回归也很重要。PDF 文件存在不代表排版正确。实际项目中可以打开 /invoices/CN-2026-0602/print 这样的打印页,执行 page.emulateMedia({ media: "print" }),再用 Playwright 的截图比较确认表头、金额和分页没有漂移。金额计算应单独用单元测试验证,截图只负责布局证据。

变现 CTA 与实测结果

PDF 生成天然适合变现:发票模板、报告样例、证书工具、检查清单 PDF 都能连接到产品、咨询和培训。但文章必须提供真实代码、失败案例和验证证据,而不是只总结库名。你可以从免费 Claude Code 资料开始,进一步查看ClaudeCodeLab 教材,团队需要把 PDF、测试和发布流程接入仓库时可看培训与咨询

本文的流程按本地 Node.js、Playwright Chromium 和示例发票数据验证。最有价值的不是一行 page.pdf,而是把打印 HTML 当成可测试对象。Masa 在实际项目里更常遇到的是字体加载、背景色丢失、长文本分页和审查证据不足,而不是简单语法错误。因此让 Claude Code 同时交付可运行脚本和验证说明,质量会稳定很多。

#Claude Code #PDF generation #jsPDF #Puppeteer #reports
免费

免费 PDF: Claude Code 速查表

输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。

我们会妥善保护你的信息,不发送垃圾邮件。

把 Claude Code 变成真正能带来结果的工作流

先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。

Masa

关于作者

Masa

专注 Claude Code 实务流程、团队导入和内容转化的工程师。