Skip to content

Node.js PDF 库 — PDF Oxide

PDF Oxide 是 Node.js 上最快的 PDF 库:文本提取平均每页 0.8 ms,比 PyMuPDF 快 5 倍,比 pypdf 快 15 倍,在 3830 个 PDF 上保持 100% 通过率。一个包覆盖提取、生成、编辑三类需求,并自带 TypeScript 类型定义。MIT / Apache-2.0 双协议。

在浏览器、Deno、Bun 或 Cloudflare Workers 里运行? 请改用 WASM 构建版本——API 完全一致,无需原生二进制。本页的原生插件仅用于 Node.js 和 Electron。

安装

npm install pdf-oxide

**环境要求:**Node.js 18 及以上,无需系统依赖,也不需要 Rust 工具链。针对 Linux(glibc + musl)x64/arm64、macOS x64/arm64、Windows x64/arm64 的预编译 .node 插件通过平台专属的 optionalDependencies 自动下载,安装时不会进行任何编译。

打开 PDF

JavaScript

const { PdfDocument } = require("pdf-oxide");

const doc = new PdfDocument("research-paper.pdf");
console.log(`Pages: ${doc.getPageCount()}`);

const { major, minor } = doc.getVersion();
console.log(`PDF version: ${major}.${minor}`);

doc.close();

TypeScript

import { PdfDocument } from "pdf-oxide";

const doc: PdfDocument = new PdfDocument("research-paper.pdf");
const pageCount: number = doc.getPageCount();
const { major, minor }: { major: number; minor: number } = doc.getVersion();
console.log(`${pageCount} pages, PDF ${major}.${minor}`);
doc.close();

Node.js 22+ 可以使用 using 自动释放资源:

{
  using doc = new PdfDocument("report.pdf");
  const text = doc.extractText(0);
} // 自动调用 doc.close()

页面 API

自 v0.3.34 起,PdfDocument 可迭代;doc.page(i) 返回缓存了 width / height / rotationPdfPage,并提供以页面为单位的提取方法。

const { PdfDocument } = require("pdf-oxide");

const doc = new PdfDocument("paper.pdf");
for (const page of doc) {
  console.log(`Page ${page.index}: ${page.width}x${page.height} (rotation ${page.rotation})`);
  const md = page.markdown();
  const tables = page.tables();       // 行与单元格,含 bbox
}
doc.close();

索引方式:doc.page(0)doc.page(-1)(最后一页)。页面方法:text()markdown()html()plainText()words()lines()tables()images()paths()annotations()fonts()search(query, caseSensitive)

文本提取

单页

JavaScript

const { PdfDocument } = require("pdf-oxide");

const doc = new PdfDocument("report.pdf");
const text = doc.extractText(0);
console.log(text);
doc.close();

TypeScript

import { PdfDocument } from "pdf-oxide";

const doc: PdfDocument = new PdfDocument("report.pdf");
const text: string = doc.extractText(0);
console.log(text);
doc.close();

全部页面

const doc = new PdfDocument("book.pdf");
const pageCount = doc.getPageCount();

for (let i = 0; i < pageCount; i++) {
  console.log(`--- Page ${i + 1} ---`);
  console.log(doc.extractText(i));
}

doc.close();

异步提取

每个同步方法都有对应的 *Async 版本,运行在 libuv 线程池上。在 HTTP 处理器和其他并发服务端代码中请使用异步方法,避免阻塞事件循环。

const { PdfDocument } = require("pdf-oxide");

async function extract(path) {
  const doc = new PdfDocument(path);
  try {
    return await doc.extractTextAsync(0);
  } finally {
    doc.close();
  }
}

按页 Promise.all 分发等模式参见 异步指南

结构化提取

字符级和 span 级别的数据,含位置与字体元数据:

const chars = doc.extractChars(0);
for (const ch of chars.slice(0, 10)) {
  console.log(`'${ch.char}' at (${ch.x.toFixed(1)}, ${ch.y.toFixed(1)}) ` +
              `size=${ch.fontSize.toFixed(1)} font=${ch.fontName}`);
}

const spans = doc.extractSpans(0);
for (const span of spans) {
  console.log(`"${span.text}" font=${span.fontName} size=${span.fontSize}`);
}

可调分段阈值的单词与行提取:

const words = doc.extractWords(0);
const lines = doc.extractTextLines(0, { wordGapThreshold: 2.5, lineGapThreshold: 1.2 });

Markdown 转换

JavaScript

const md = doc.toMarkdown(0, { detectHeadings: true });
console.log(md);

// 全部页面
const allMd = doc.toMarkdownAll();

TypeScript

const md: string = doc.toMarkdown(0, { detectHeadings: true });
const allMd: string = doc.toMarkdownAll();

HTML 转换

const html = doc.toHtml(0);
const allHtml = doc.toHtmlAll();

图像提取

const { writeFileSync } = require("fs");

const doc = new PdfDocument("brochure.pdf");
const images = doc.extractImages(0);

for (const [i, img] of images.entries()) {
  console.log(`Image ${i}: ${img.width}x${img.height} ${img.format} (${img.data.length} bytes)`);
  writeFileSync(`image_${i}.${img.format}`, img.data);
}

doc.close();

从索引色 PDF 提取的图像会自动展开为 RGB,支持 1/2/4/8 bpc 的索引调色板,基础色空间可为 RGB、灰度或 CMYK。

从字节打开

从内存字节打开 PDF——适合从 S3、HTTP 或数据库下载的数据:

const { PdfDocument } = require("pdf-oxide");
const { readFileSync } = require("fs");

const bytes = readFileSync("document.pdf");
const doc = PdfDocument.openFromBytes(bytes);
const text = doc.extractText(0);
doc.close();

密码保护的 PDF

const doc = PdfDocument.openWithPassword("confidential.pdf", "secret");
const text = doc.extractText(0);
doc.close();

也可以在打开后再做认证:

const doc = new PdfDocument("confidential.pdf");
if (doc.authenticate("secret")) {
  console.log(doc.extractText(0));
}
doc.close();

完整支持 AES-256(V=5, R=6)PDF——包括推按式控件标题,以及延迟认证后正确失效的对象缓存。

创建 PDF

Pdf 类提供了从多种源格式创建 PDF 的工厂方法。

从 Markdown

const { Pdf } = require("pdf-oxide");
const { writeFileSync } = require("fs");

const pdf = Pdf.fromMarkdown("# Hello World\n\nThis is a PDF.");
writeFileSync("output.pdf", pdf.toBytes());

从 HTML

const pdf = Pdf.fromHtml("<h1>Invoice</h1><p>Amount due: $42.00</p>");
writeFileSync("invoice.pdf", pdf.toBytes());

从纯文本

const pdf = Pdf.fromText("Plain text document.\n\nSecond paragraph.");
writeFileSync("notes.pdf", pdf.toBytes());

从图像

const pdf = Pdf.fromImage("scan.jpg");
writeFileSync("scan.pdf", pdf.toBytes());

搜索

const doc = new PdfDocument("manual.pdf");

// 全部页面搜索
const results = doc.searchAll("configuration", { caseSensitive: false });
for (const r of results) {
  console.log(`Page ${r.page}: "${r.text}" at (${r.x.toFixed(0)}, ${r.y.toFixed(0)})`);
}

// 单页搜索
const pageResults = doc.searchPage(0, "configuration");
doc.close();

对大文档进行流式搜索时使用 SearchStream

const { PdfDocument, SearchStream, SearchManager } = require("pdf-oxide");

const doc = new PdfDocument("large.pdf");
const manager = new SearchManager(doc);
const stream = new SearchStream(manager, "invoice");

stream.on("data", (r) => console.log(`page ${r.pageIndex + 1}: ${r.text}`));
stream.on("end", () => doc.close());

详见 Node.js 流指南

编辑

元数据、页面操作、注释、表单字段等编辑功能请使用 DocumentEditor

const { DocumentEditor } = require("pdf-oxide");

const editor = DocumentEditor.open("document.pdf");

// 元数据
editor.setTitle("Updated Title");
editor.setAuthor("Jane Doe");

// 页面操作
editor.rotatePage(0, 90);
editor.deletePage(5);
editor.movePage(2, 0);

// 表单
editor.setFormFieldValue("employee.name", "Jane Doe");
editor.flattenForms();

editor.save("edited.pdf");
editor.close();

OCR

安装时启用 ocr feature 即可对扫描页面使用 OCR:

npm install pdf-oxide --build-from-source -- --features ocr
const { PdfDocument, OcrEngine } = require("pdf-oxide");

const doc = new PdfDocument("scanned.pdf");
const ocr = new OcrEngine();

if (ocr.pageNeedsOcr(doc, 0)) {
  const text = ocr.extractText(doc, 0);
  console.log(text);
}

ocr.close();
doc.close();

端到端的用法参考 OCR 指南

线程安全

PdfDocument 满足 Send + Sync,可以在多个工作线程之间共享同一个文档并并行读取页面。*Async 系列方法会通过 libuv 线程池自动完成这件事;手动的工作线程模式参见 并发指南

错误处理

所有方法在失败时都会抛出异常:

const { PdfDocument } = require("pdf-oxide");

try {
  const doc = new PdfDocument("document.pdf");
  const text = doc.extractText(0);
  doc.close();
} catch (err) {
  console.error(`Extraction failed: ${err.message}`);
}

下一步