pdf-lib npm 模块是使用Node.js创建和编辑PDF的好工具。 Puppeteer 是可以从HTML生成PDF的出色工具,但是不幸的是,以我的经验,浏览器对CSS中的打印布局的支持不是很好。 pdf-lib 模块为你提供了对PDF的非常精细的控制,它可以用来合并PDF,添加页码、水印,拆分PDF,以及你可能会使用 ILovePDF API 来进行的其它任何处理PDF文件的功能。
入门
让我们使用 pdf-lib 创建一个简单的PDF文档。 这个PDF文档只有1页,页面中间会显示 Mastering JS 的图标。
const { PDFDocument } = require('pdf-lib');
const fs = require('fs');
run().catch(err => console.log(err));
async function run() {
// Create a new document and add a new page
const doc = await PDFDocument.create();
const page = doc.addPage();
// Load the image and store it as a Node.js buffer in memory
let img = fs.readFileSync('./logo.png');
img = await doc.embedPng(img);
// Draw the image on the center of the page
const { width, height } = img.scale(1);
page.drawImage(img, {
x: page.getWidth() / 2 - width / 2,
y: page.getHeight() / 2 - height / 2
});
// Write the PDF to a file
fs.writeFileSync('./test.pdf', await doc.save());
}
运行上面的脚本会生成下面的PDF。 使用 pdf-lib 非常简单,只有一些陷阱:注意PDFDocument#embedPng()
和PDFDocument#save()
返回Promise
,因此你需要使用await
。
合并PDF
pdf-lib 的杀手级功能是你可以修改已存在的PDF,而不仅仅是创建新的PDF。 例如,假设您有两个PDF:一个包含电子书的封面,另一个包含电子书的内容。 如何合并两个PDF呢? 我在上一本电子书(Mastering Async/Await)中使用了 ILovePDF API,但是 pdf-lib 使这个任务在 Node.js 中变得很容易。
有两个PDF文件:cover.pdf
和page-30-31.pdf
。 下面的脚本,使用 pdf-lib 将这两个PDF合并为了一个 test.pdf
文件。
const { PDFDocument } = require('pdf-lib');
const fs = require('fs');
run().catch(err => console.log(err));
async function run() {
// Load cover and content pdfs
const cover = await PDFDocument.load(fs.readFileSync('./cover.pdf'));
const content = await PDFDocument.load(fs.readFileSync('./page-30-31.pdf'));
// Create a new document
const doc = await PDFDocument.create();
// Add the cover to the new doc
const [coverPage] = await doc.copyPages(cover, [0]);
doc.addPage(coverPage);
// Add individual content pages
const contentPages = await doc.copyPages(content, content.getPageIndices());
for (const page of contentPages) {
doc.addPage(page);
}
// Write the PDF to a file
fs.writeFileSync('./test.pdf', await doc.save());
}
合并后效果可见下图。
添加页码
使用 Puppeteer 从HTML生成PDF的最大难点之一就是添加页码十分痛苦。 添加页码虽然看起来很简单,但是CSS打印布局却无法正确实现这个功能。 可以看一下我写的一个for循环用了 hard-code 像素偏移量才能使页码可以正确地显示。
例如,Mastering Async / Await 这个PDF的前4页没有页码:./content.pdf
。 下面的脚本,将给PDF中的每一页添加页面。
const { PDFDocument, StandardFonts, rgb } = require('pdf-lib');
const fs = require('fs');
run().catch(err => console.log(err));
async function run() {
const content = await PDFDocument.load(fs.readFileSync('./content.pdf'));
// Add a font to the doc
const helveticaFont = await content.embedFont(StandardFonts.Helvetica);
// Draw a number at the bottom of each page.
// Note that the bottom of the page is `y = 0`, not the top
const pages = await content.getPages();
for (const [i, page] of Object.entries(pages)) {
page.drawText(`${+i + 1}`, {
x: page.getWidth() / 2,
y: 10,
size: 15,
font: helveticaFont,
color: rgb(0, 0, 0)
});
}
// Write the PDF to a file
fs.writeFileSync('./test.pdf', await content.save());
}
添加页码后的效果可见下图
继续
Node.js生态系统中有很多着出色的库,可以解决你几乎能想到的任何问题。 pdf-lib 模块可让你处理PDF,sharp 可让您处理几乎所有带有图像的东西,pkg 将Node项目捆绑到独立的可执行文件中,等等。 在你开始寻找线上 API 来解决你遇到的问题之前,如果先尝试搜索 npm,你可能会找到一个更好的解决方案。