使用 PDF-Lib 库在 TypeScript 中生成 PDF 报告
在现代 Web 开发中,经常需要创建复杂的 PDF 文档:报告、发票、证书或分析仪表板。TypeScript 凭借其严格的类型检查,使这一过程变得可靠且可预测。在 Node.js 平台和浏览器中众多用于处理 PDF 的库中,PDF-Lib 脱颖而出——它是一个轻量级、功能强大且完全用 TypeScript 编写的工具。
PDF-Lib 无需安装额外的原生依赖项(与 Puppeteer 或 wkhtmltopdf 等重量级解决方案不同),既可在 Node.js 中运行,也可在浏览器中运行,并支持所有现代 PDF 功能:嵌入字体、图像、创建表单、注释等。
安装与第一个文档
开始使用只需通过 npm 安装该包:
npm install pdf-lib让我们创建一个包含文本的简单 PDF 文档:
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
async function createSimplePdf() { // 创建一个新的 PDF 文档 const pdfDoc = await PDFDocument.create();
// 添加一页(A4 格式) const page = pdfDoc.addPage([595.28, 841.89]); // A4 尺寸(点)
// 嵌入标准字体 const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
// 绘制文本 page.drawText('你好,世界!', { x: 50, y: 700, size: 30, font: font, color: rgb(0.2, 0.4, 0.8), });
// 将文档保存为字节 const pdfBytes = await pdfDoc.save(); return pdfBytes;}
// 使用示例createSimplePdf().then((bytes) => { // 在 Node.js 中可以写入文件 // require('fs').writeFileSync('hello.pdf', bytes); console.log('PDF 已创建!大小:', bytes.length, '字节');});处理俄语文本与自定义字体
生成俄语报告时的关键任务之一是正确显示西里尔字母。标准 PDF 字体(Helvetica、Times、Courier)不包含西里尔字符。解决方案是嵌入支持 Unicode 的 TrueType 字体。
import { PDFDocument, rgb } from 'pdf-lib';import fontkit from '@pdf-lib/fontkit';import fs from 'fs';
async function createRussianPdf() { const pdfDoc = await PDFDocument.create();
// 注册 fontkit 以使用自定义字体 pdfDoc.registerFontkit(fontkit);
// 加载包含西里尔字符的字体(例如 Roboto) const fontBytes = fs.readFileSync('Roboto-Regular.ttf'); const customFont = await pdfDoc.embedFont(fontBytes);
const page = pdfDoc.addPage([595.28, 841.89]);
page.drawText('在 TypeScript 中生成报告', { x: 50, y: 750, size: 24, font: customFont, color: rgb(0, 0, 0), });
page.drawText('日期:2024-01-15', { x: 50, y: 700, size: 14, font: customFont, color: rgb(0.3, 0.3, 0.3), });
return await pdfDoc.save();}创建表格与结构化报告
PDF-Lib 没有内置的表格模块,但可以通过绘制线条和定位文本来手动轻松实现。为了方便,可以创建一个辅助函数:
interface TableCell { text: string; width: number; height?: number; align?: 'left' | 'center' | 'right';}
async function drawTable( page: PDFPage, headers: TableCell[], rows: TableCell[][], startX: number, startY: number, font: PDFFont, fontSize: number = 12) { const cellPadding = 5; const rowHeight = fontSize + 2 * cellPadding; let currentY = startY;
// 绘制表头 let currentX = startX; for (const header of headers) { page.drawRectangle({ x: currentX, y: currentY - rowHeight, width: header.width, height: rowHeight, color: rgb(0.9, 0.9, 0.9),