首页 > web前端 > js教程 > 正文

怎样使用Node.js生成PDF?

幻夢星雲
发布: 2025-08-31 12:57:01
原创
455人浏览过
Puppeteer适合HTML转PDF因能真实渲染网页内容,支持动态加载、高保真输出;pdf-lib适合代码直接生成或修改PDF,性能更高但布局需手动计算。

怎样使用node.js生成pdf?

要在Node.js中生成PDF,最直接有效的方式是利用现有的库。对于需要将HTML内容转换为PDF的场景,我个人通常会选择Puppeteer,它通过控制一个无头浏览器来渲染网页并将其保存为PDF,这能最大程度地保留原始网页的布局和样式。而如果你的需求是纯粹地从代码层面构建PDF,例如绘制图形、插入文本等,那么像

pdf-lib
登录后复制
这样的库会更合适。

解决方案

如果你想把一个网页或一段HTML代码转换成PDF,Puppeteer无疑是目前最强大、最灵活的选项之一。它本质上是Google Chrome或Chromium的Node.js API,这意味着你可以用它来做任何浏览器能做的事情,包括生成高质量的PDF。

以下是一个基本的Puppeteer生成PDF的流程和代码示例:

  1. 安装Puppeteer:

    npm install puppeteer
    登录后复制
  2. 编写生成PDF的代码:

    const puppeteer = require('puppeteer');
    const path = require('path');
    
    async function generatePdfFromHtml(htmlContent, outputPath) {
        let browser;
        try {
            browser = await puppeteer.launch({
                headless: true, // 在后台运行浏览器,不显示UI
                args: ['--no-sandbox', '--disable-setuid-sandbox'] // 某些环境可能需要
            });
            const page = await browser.newPage();
    
            // 设置页面内容为HTML字符串
            await page.setContent(htmlContent, {
                waitUntil: 'networkidle0' // 等待网络空闲,确保所有资源加载完成
            });
    
            // 或者,如果你想从一个URL生成PDF
            // await page.goto('https://example.com', {
            //     waitUntil: 'networkidle0'
            // });
    
            await page.pdf({
                path: outputPath,
                format: 'A4',
                printBackground: true, // 打印背景颜色和图片
                margin: {
                    top: '20mm',
                    right: '20mm',
                    bottom: '20mm',
                    left: '20mm'
                },
                // displayHeaderFooter: true, // 是否显示页眉页脚
                // headerTemplate: '<span>页眉</span>', // 页眉HTML模板
                // footerTemplate: '<span>页脚</span>', // 页脚HTML模板
            });
    
            console.log(`PDF已成功生成到: ${outputPath}`);
        } catch (error) {
            console.error('生成PDF时出错:', error);
        } finally {
            if (browser) {
                await browser.close();
            }
        }
    }
    
    // 示例用法
    const html = `
        <!DOCTYPE html>
        <html>
        <head>
            <title>我的报告</title>
            <style>
                body { font-family: 'Arial', sans-serif; margin: 0; padding: 0; color: #333; }
                .container { width: 800px; margin: 50px auto; border: 1px solid #eee; padding: 30px; box-shadow: 0 0 10px rgba(0,0,0,0.05); }
                h1 { color: #0056b3; text-align: center; }
                p { line-height: 1.6; }
                .footer { text-align: center; margin-top: 50px; font-size: 0.8em; color: #666; }
                /* 打印样式 */
                @media print {
                    .container { box-shadow: none; border: none; }
                    /* 强制在某个元素前分页 */
                    .new-page { page-break-before: always; }
                }
            </style>
        </head>
        <body>
            <div class="container">
                <h1>年度总结报告</h1>
                <p>这是一段关于今年工作成果的介绍性文字。我们取得了显著的进步,特别是在产品研发和市场拓展方面。</p>
                <p>本报告将详细阐述各项指标、挑战以及未来的展望。</p>
    
                <h2>关键成就</h2>
                <ul>
                    <li>用户增长率达到20%</li>
                    <li>新产品发布市场反响良好</li>
                    <li>团队协作效率提升15%</li>
                </ul>
    
                <div class="new-page"></div> <!-- 强制分页 -->
    
                <h2>数据分析</h2>
                <p>通过对用户行为数据的深入分析,我们发现了一些新的趋势,这将指导我们下一阶段的策略调整。</p>
                <img src="https://via.placeholder.com/600x200.png?text=Placeholder+Image" alt="示例图片" style="max-width: 100%; height: auto; display: block; margin: 20px 0;">
    
                <div class="footer">
                    <p>版权所有 &copy; 2023 我的公司</p>
                </div>
            </div>
        </body>
        </html>
    `;
    
    const outputPath = path.join(__dirname, 'my_report.pdf');
    generatePdfFromHtml(html, outputPath);
    登录后复制

    这段代码展示了如何启动一个无头浏览器实例,加载HTML内容,然后调用

    page.pdf()
    登录后复制
    方法将其保存为PDF文件。你可以通过
    page.pdf()
    登录后复制
    的选项来控制PDF的格式、边距、是否打印背景、页眉页脚等。

为什么选择Puppeteer来生成PDF?它有哪些优势?

对我来说,选择Puppeteer生成PDF,最核心的原因就是它的“所见即所得”能力。我们都知道,网页开发已经非常成熟,用HTML、CSS和JavaScript来构建复杂、美观的布局简直是家常便饭。如果一个PDF只是要把这些已经设计好的内容呈现出来,Puppeteer简直是完美的选择。

它的优势非常明显:

Elai.io
Elai.io

AI视频生成工具,可以使用文字、URL、PDF等生成视频

Elai.io 34
查看详情 Elai.io
  • 真实渲染环境:Puppeteer背后跑的是一个完整的Chromium浏览器,这意味着它能像用户浏览器一样解析和渲染HTML、CSS和JavaScript。那些复杂的CSS布局、动画效果(虽然PDF是静态的,但渲染时会执行JS)、外部字体、图片等,都能被准确地捕获并呈现在PDF中。这对于生成包含图表、复杂表格、动态内容报告的PDF特别有用。
  • 高保真度:与一些基于模板或DOM解析的PDF库不同,Puppeteer生成的PDF几乎就是网页的快照。你不需要担心CSS属性不被支持,或者JavaScript渲染的内容丢失。我个人在做一些客户报告、发票或者在线简历生成时,发现它能完美复刻网页版的设计,省去了大量的样式适配工作。
  • 动态内容支持:如果你的网页内容是动态加载的(比如通过AJAX获取数据后渲染),Puppeteer可以在这些内容加载并渲染完成后再进行截图或PDF生成。
    waitUntil: 'networkidle0'
    登录后复制
    这样的选项就是为此服务的。
  • 易于调试:由于它基于Chrome,你可以选择以非无头模式运行Puppeteer (
    headless: false
    登录后复制
    ),直接看到浏览器窗口,甚至打开开发者工具进行调试,这在排查布局问题时非常方便。
  • 功能强大:除了生成PDF,Puppeteer还能做很多其他事情,比如网页截图、自动化测试、爬虫等,这使得它成为一个多功能的工具。

当然,它也有一些缺点,比如启动一个浏览器实例会有一定的资源开销,但对于大多数服务器端生成PDF的场景,这点开销是完全可以接受的。

生成复杂PDF时,如何处理样式、分页和字体问题?

在实际操作中,尤其面对复杂的报告或文档,样式、分页和字体处理是生成高质量PDF的关键,也常常是让人头疼的地方。

样式处理: Puppeteer能够很好地处理CSS,但你需要记住,PDF是打印介质,所以要充分利用CSS的

@media print
登录后复制
规则。我通常会这样做:

  • 打印专用CSS:在你的样式表中,专门为
    @media print
    登录后复制
    定义一套规则。比如,隐藏掉页面上的导航栏、按钮等在打印时不需要的元素,调整字体大小以适应打印,或者为表格添加更清晰的边框。
  • 单位选择:在打印样式中,使用
    mm
    登录后复制
    cm
    登录后复制
    in
    登录后复制
    pt
    登录后复制
    等物理单位比
    px
    登录后复制
    更可靠,因为它们在打印时有固定的物理尺寸。
  • 避免绝对定位滥用:虽然绝对定位在网页布局中很常见,但在打印时可能会导致内容溢出或重叠。尽量使用流式布局或Flexbox/Grid来构建主要结构。
  • 背景打印:确保
    page.pdf()
    登录后复制
    选项中的
    printBackground: true
    登录后复制
    设置为真,否则背景颜色和图片可能不会被打印出来。

分页控制: 这是PDF生成中最常见的挑战之一。没人喜欢内容被截断在两页之间。CSS3提供了一些非常有用的属性来控制分页:

  • page-break-before: always;
    登录后复制
    :强制在元素之前分页。这对于确保每个新章节或大标题都从新页开始非常有用。
  • page-break-after: always;
    登录后复制
    :强制在元素之后分页。
  • page-break-inside: avoid;
    登录后复制
    :尽量避免在元素内部(例如一个完整的表格行、一个图片和其标题组成的
    div
    登录后复制
    )分页。这对于保持内容的完整性非常重要。
  • orphans
    登录后复制
    widows
    登录后复制
    :这两个CSS属性可以控制段落中,一页顶部或底部至少保留的行数,避免出现孤行或寡行。
  • Puppeteer的页眉页脚:如果你需要统一的页眉页脚(比如页码、公司Logo),Puppeteer的
    displayHeaderFooter
    登录后复制
    headerTemplate
    登录后复制
    footerTemplate
    登录后复制
    选项非常方便。它们接受HTML字符串,你可以用CSS来控制它们的样式和位置,甚至用
    <span>
    登录后复制
    标签配合
    pageNumber
    登录后复制
    totalPages
    登录后复制
    来显示页码。

字体问题: 字体是另一个可能导致PDF看起来与预期不符的问题。

  • Web字体嵌入:如果你使用了非系统默认字体(比如Google Fonts),确保它们被正确地加载和嵌入到HTML中。Puppeteer会等待这些字体加载完成。在CSS中,使用
    @font-face
    登录后复制
    规则并提供字体文件的URL,或者直接通过
    <link>
    登录后复制
    标签引入。
  • 字体子集化:在某些情况下,为了减小PDF文件大小,PDF生成器可能会对字体进行子集化(只嵌入实际使用的字符)。这通常是自动的,但如果遇到字符显示问题,需要检查字体文件是否完整或是否被正确引用。
  • Fallback字体:在CSS中定义一个字体栈,比如
    font-family: 'MyCustomFont', Arial, sans-serif;
    登录后复制
    ,这样即使自定义字体加载失败,也有一个可靠的系统字体作为备选。

处理这些问题时,我经常会采取迭代的方式:先生成一个初稿,然后根据PDF的实际效果调整HTML/CSS,再生成,直到满意为止。

除了Puppeteer,还有哪些Node.js库可以用于PDF生成?它们适用于哪些场景?

虽然Puppeteer是我处理HTML到PDF转换的首选,但在某些特定场景下,其他Node.js库可能更合适,或者说,它们解决的是不同类型的问题。

  1. pdf-lib
    登录后复制
    (或
    jsPDF
    登录后复制
    )

    • 特点:这些库不依赖浏览器渲染,而是直接通过JavaScript代码操作PDF文档的底层结构。你可以用它们来绘制图形、添加文本、插入图片、创建表单字段、修改现有PDF等。它们更像是PDF的“画板”和“编辑器”。
    • 适用场景
      • 纯代码生成PDF:当你不需要将HTML转换为PDF,而是希望完全通过编程来构建一个PDF时,比如生成简单的收据、证书、图表(数据驱动的图形绘制)、或者需要高度定制化PDF布局的场景。
      • 修改现有PDF:例如,你有一个PDF模板,需要动态地在特定位置填充文本或图片,
        pdf-lib
        登录后复制
        在这方面非常强大。
      • 性能敏感:对于非常简单的PDF生成,或者在资源受限的环境中,避免启动一个完整的浏览器实例可以节省大量内存和CPU。
    • 个人看法:我用
      pdf-lib
      登录后复制
      比较多,因为它在处理二进制PDF文件和更底层的PDF结构方面表现出色。比如,我曾用它来给用户上传的PDF文件添加水印,或者合并多个PDF文档。它提供了对PDF内容的精细控制,但缺点是,如果你想实现复杂的布局,需要手动计算每个元素的位置,这比写HTML/CSS要繁琐得多。
  2. html-pdf
    登录后复制

    • 特点:这是一个相对较老的库,它使用PhantomJS(一个无头Webkit浏览器)或Headless Chrome来将HTML转换为PDF。它的API比Puppeteer更简洁,但功能相对较少,且PhantomJS已经不再维护。
    • 适用场景
      • 简单HTML到PDF转换:对于非常简单的HTML结构,没有复杂JavaScript交互的页面,
        html-pdf
        登录后复制
        可能是一个轻量级的选择。
      • 遗留项目:如果你的项目已经在使用它,并且运行良好,可能没有必要立即迁移。
    • 个人看法:我个人现在很少推荐
      html-pdf
      登录后复制
      ,因为它依赖的PhantomJS已经过时,而其Headless Chrome的集成也不如Puppeteer那么完善和灵活。如果非要HTML转PDF,我几乎总是直接上Puppeteer,因为它更现代,社区活跃,且功能更强大。

总的来说,选择哪个库,关键在于你的具体需求。如果你的内容已经以HTML形式存在,并且需要高保真度的视觉呈现,Puppeteer是毋庸置疑的首选。而如果你需要从零开始构建PDF内容,或者对现有PDF进行操作,那么像

pdf-lib
登录后复制
这样的底层PDF操作库会更有效率。没有绝对的“最好”,只有最适合当前任务的工具。

以上就是怎样使用Node.js生成PDF?的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号