使用Puppeteer生成PDF时,能否为第一页和第二页设置不同页眉页脚?
实现Puppeteer PDF第一页与第二页不同页眉页脚的方案
我之前做项目时刚好碰到过一模一样的需求!Puppeteer默认的页眉页脚是全局生效的,但通过CSS配合一些小技巧,完全可以实现第一页和其他页面(比如第二页)的差异化设置,下面分享两种我亲测有效的方案:
方案一:利用CSS @page 伪类(最简洁)
这个方案直接通过CSS打印样式来控制不同页面的页眉页脚,不需要复杂的JS逻辑,适合静态内容的场景。
核心思路
使用CSS的 @page 规则定义全局页眉页脚,再用 @page :first 伪类单独覆盖第一页的样式,同时通过调整页面边距避免内容和页眉页脚重叠。
代码示例
const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); // 加载自定义HTML内容 await page.setContent(` <!DOCTYPE html> <html> <head> <style> /* 全局页面样式(除第一页外) */ @page { margin: 80px 40px; /* 给页眉页脚留足空间 */ @top-center { content: "全局通用页眉"; font-size: 12px; color: #666; } @bottom-center { content: "第 " counter(page) " 页 / 共 " counter(pages) " 页"; font-size: 12px; color: #666; } } /* 第一页专属样式 */ @page :first { margin: 60px 40px; /* 调整第一页边距,避免和全局冲突 */ @top-center { content: "第一页(介绍页)专属页眉"; font-size: 14px; font-weight: bold; color: #333; } @bottom-center { content: "介绍页 - 第1页"; font-size: 12px; color: #333; } } body { font-family: "Arial", sans-serif; line-height: 1.6; } /* 强制分页,确保第一页和第二页分离 */ .page-break { page-break-after: always; } </style> </head> <body> <div> <h1>欢迎来到介绍页</h1> <p>这里是PDF的第一页内容,展示项目介绍、目录等信息...</p> </div> <div class="page-break"></div> <div> <h1>第二页内容</h1> <p>这里是PDF的第二页,使用全局通用的页眉页脚...</p> </div> <!-- 更多页面可以继续添加 --> </body> </html> `); // 生成PDF,开启页眉页脚(模板留空,用CSS控制) await page.pdf({ path: 'custom-pdf.pdf', format: 'A4', displayHeaderFooter: true, headerTemplate: '', footerTemplate: '' }); await browser.close(); })();
方案二:动态DOM + Puppeteer模板(适合动态内容)
如果你的页眉页脚需要动态数据(比如从后端获取的标题、用户信息),可以结合Puppeteer的页眉页脚模板和页面DOM的类控制,实现更灵活的自定义。
核心思路
- 在页面中给第一页和其他页面添加不同的类标识
- 定义全局的页眉页脚模板,通过CSS控制第一页隐藏全局模板,显示自定义的DOM元素
- 其他页面则隐藏第一页的自定义元素,显示全局模板
代码示例
const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); // 假设从后端获取的动态数据 const firstPageHeader = "项目介绍专属页眉"; const globalHeader = "文档通用页眉"; await page.setContent(` <!DOCTYPE html> <html> <head> <style> body { font-family: "Arial", sans-serif; line-height: 1.6; } .page-break { page-break-after: always; } /* 打印时的样式控制 */ @media print { /* 第一页隐藏全局页眉页脚模板 */ .first-page .global-header, .first-page .global-footer { display: none; } /* 其他页面隐藏第一页专属页眉页脚 */ .non-first-page .first-header, .non-first-page .first-footer { display: none; } /* 第一页专属页眉页脚样式 */ .first-header, .first-footer { position: fixed; width: 100%; font-size: 12px; text-align: center; color: #333; } .first-header { top: 20px; font-weight: bold; font-size: 14px; } .first-footer { bottom: 20px; } } /* 屏幕上隐藏页眉页脚,只在打印时显示 */ @media screen { .first-header, .first-footer, .global-header, .global-footer { display: none; } } </style> </head> <body> <div class="first-page"> <div class="first-header">${firstPageHeader}</div> <div class="first-footer">介绍页 - 第1页</div> <h1>项目介绍</h1> <p>这里是第一页的动态内容...</p> </div> <div class="page-break"></div> <div class="non-first-page"> <h1>第二页内容</h1> <p>这里是第二页的内容,使用全局页眉页脚...</p> </div> </body> </html> `); // 生成PDF,配置全局页眉页脚模板 await page.pdf({ path: 'dynamic-pdf.pdf', format: 'A4', displayHeaderFooter: true, headerTemplate: `<div class="global-header">${globalHeader}</div>`, footerTemplate: '<div class="global-footer">第 <span class="pageNumber"></span> 页 / 共 <span class="totalPages"></span> 页</div>', margin: { top: '60px', bottom: '40px' } // 给页眉页脚留空间 }); await browser.close(); })();
注意事项
- 边距调整:一定要给页眉页脚留出足够的边距,避免内容和页眉页脚重叠,建议先在Chrome打印预览中调试边距和样式
- 测试技巧:开发时可以设置
puppeteer.launch({ headless: false }),打开Chrome的打印预览功能,实时查看样式效果 - 动态页码:如果需要使用页码变量,Puppeteer的模板支持
pageNumber和totalPages两个内置变量,直接在模板中使用即可
内容的提问来源于stack exchange,提问作者Guru
相关产品推荐
相关产品推荐

