因为pdf-lib不解析原始图片二进制,只接受合法PNG/JPEG字节流(如Uint8Array),需用sharp等工具先校验格式、解码并转为RGBA Buffer再嵌入。

为什么直接用 pdf-lib 合并图片会失败
因为 pdf-lib 本身不解析图片二进制数据,它只接受已解码的 PNG 或 JPEG 字节(比如 Uint8Array),而你读进来的文件可能是原始 Buffer,没经过格式校验或解码。常见报错是 Invalid PNG signature 或 Unsupported JPEG format —— 实际上是你传了未处理的文件内容,不是它要的“图像字节流”。
- 别直接
fs.readFileSync('./a.png')后塞给pdfDoc.embedPng(),必须先确认格式合法 -
JPEG要带 SOI(0xFFD8)标记,PNG要以89 50 4E 47开头,否则pdf-lib会拒收 - 推荐用
sharp中转:它能自动识别格式、统一转为 RGBABuffer,再喂给pdf-lib
用 sharp + pdf-lib 实现单页多图 PDF 的最小可行代码
核心逻辑是:读图 → 统一缩放到 A4 尺寸(595×842 pt)→ 按行列排布 → 嵌入 PDF。关键参数不能硬编码,比如每张图留白、行高、列数得根据图数量动态算。
- 安装依赖:
npm install pdf-lib sharp - 确保输入图路径存在,且
sharp支持其格式(WebP、AVIF 也行,但需额外编译选项) - A4 宽高单位是
pt(1pt = 1/72 inch),不是像素;sharp输出时用.resize(595, 842, { fit: 'contain', background: 'white', embed: true })保证等比居中不拉伸 - 示例代码片段:
const { PDFDocument } = require('pdf-lib');
const sharp = require('sharp');
const fs = require('fs').promises;
<p>async function imagesToSinglePagePdf(imagePaths, outputPath) {
const pdfDoc = await PDFDocument.create();
const page = pdfDoc.addPage([595, 842]); // A4 size in pt</p><p>const margin = 20;
const cols = 2;
const rows = Math.ceil(imagePaths.length / cols);
const imgWidth = (595 - margin <em> 2) / cols;
const imgHeight = (842 - margin </em> 2) / rows;</p><p>for (let i = 0; i 2), // 高清屏适配,PDF 渲染更锐利
height: Math.round(imgHeight 2),
fit: 'contain',
background: 'white',
embed: true
}).png().toBuffer();</p><pre class="brush:php;toolbar:false;">const img = await pdfDoc.embedPng(resized);
const x = margin + (i % cols) * imgWidth;
const y = 842 - margin - Math.floor(i / cols) * imgHeight - imgHeight;
page.drawImage(img, { x, y, width: imgWidth, height: imgHeight });}
const pdfBytes = await pdfDoc.save(); await fs.writeFile(outputPath, pdfBytes); }
// 调用示例 imagesToSinglePagePdf(['./1.jpg', './2.png', './3.jpeg'], './output.pdf');
VSCode 中调试时容易卡住的三个地方
不是环境问题,而是 Node 运行时行为和 VSCode 调试器默认配置的冲突点。
-
sharp初始化慢:首次 require 会加载本地二进制,VSCode 的“自动附加调试器”可能误判为卡死,加"console": "integratedTerminal"到launch.json可避免假死提示 - 异步链断裂:忘记
await在pdfDoc.embedPng()或page.drawImage()后面,会导致 PDF 页面空白——这些方法返回 Promise,不 await 就不会真正嵌入 - 路径错误静默失败:VSCode 默认工作目录是打开的文件夹根目录,但脚本里写的是相对路径如
'./imgs/1.png',如果当前执行目录不对,fs.readFile报错但没 catch,程序直接退出。加一层try/catch并打印error.message很关键
合并效果不理想?优先检查这三项参数
多图排版不是“堆上去就行”,PDF 渲染引擎对坐标、缩放、DPI 敏感,肉眼觉得“差不多”往往就是模糊或错位的根源。
-
fit: 'contain'和'cover'效果差异极大:contain保证全图可见但可能留白多,cover填满但会裁剪边缘 —— 根据图内容选,别凭感觉 - PDF 页面坐标系 Y 轴向下为正,但原点在左下角,所以
y计算要用842 - ...,不是从上往下减 - 如果图太多导致文字或细节看不清,别盲目缩小尺寸,先用
sharp的.jpeg({ quality: 95 })或.png({ compressionLevel: 6 })控制输出体积,比压缩像素更有效
实际排版时,列数超过 3 张就很难保证可读性,A4 纸横向最多稳妥放 4 张图,再多就得换页或导出为 SVG 再嵌入 —— 这个限制不是工具问题,是物理纸面和人眼分辨率决定的。











