PDF-бібліотека для Node.js — PDF Oxide
PDF Oxide — найшвидша PDF-бібліотека для Node.js: 0,8 мс на сторінку в середньому, у 5× швидше за PyMuPDF, у 15× швидше за pypdf, 100 % успіху на 3 830 PDF. Один пакет для видобування, створення й редагування — з визначеннями TypeScript у комплекті. Ліцензія MIT / Apache-2.0.
Запускаєте у браузері, Deno, Bun чи Cloudflare Workers? Скористайтеся WASM-збіркою — той самий API, без нативних бінарників. N-API розширення з цієї сторінки — тільки для Node.js і Electron.
Встановлення
npm install pdf-oxide
Вимоги: Node.js 18 або новіше. Системні залежності та Rust-тулчейн не потрібні. Готові .node-розширення для Linux (glibc + musl) x64/arm64, macOS x64/arm64 і Windows x64/arm64 завантажуються автоматично через платформні 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) повертає PdfPage із кешованими width / height / rotation і методами видобування, що працюють для конкретної сторінки.
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();
}
}
Див. посібник з async — там є шаблони на кшталт Promise.all із розкиданням по сторінках.
Структуроване видобування
Дані на рівні символів і спанів із позиціями та метаданими шрифту:
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, Grayscale і 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) підтримується повністю — включно з підписами push-button-віджетів і коректно інвалідованими кешами об’єктів після пізньої автентифікації.
Створення 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}`);
}
Наступні кроки
- Швидкий старт Python — використання PDF Oxide з Python
- Швидкий старт WASM — браузер / Deno / Bun / edge-середовища
- Довідник API Node.js — повна документація нативного API
- Посібник з async — методи
*Asyncі шаблониPromise.all - Стріми Node.js —
SearchStreamта інші - Видобування тексту — докладні опції