pdf_oxide giờ đã hỗ trợ Go, C#/.NET và Node.js/TypeScript — cùng Rust core, 0.8ms, MIT
Gần nửa năm trước mình đã phát hành pdf_oxide — một thư viện PDF viết bằng Rust, open source theo MIT / Apache-2.0. Mục tiêu lúc đó chỉ là có một engine nhanh, ổn định, không dính AGPL. Suốt mấy tháng qua mình nhận feedback gần như mỗi ngày — bug reports, PDF bị lỗi parsing, CJK edge case, column detection trên layout phức tạp, ICC color management, kerning. Từ v0.3.5 đến v0.3.37 fix được rất nhiều thứ. Core giờ đã đủ ổn để dùng thật trong production. Vì core ổn định rồi nên 2 tháng gần đây mình viết thêm bindings cho Go, C#/.NET và JavaScript / TypeScript — cùng Rust core, cùng tốc độ 0.8ms trung bình, cùng giấy phép MIT.
Cài đặt:
# JavaScript / TypeScript
npm install pdf-oxide
# C# / .NET
dotnet add package PdfOxide
# Go
go get github.com/yfedoseev/pdf_oxide/go
go run github.com/yfedoseev/pdf_oxide/go/cmd/install@latest
Ví dụ Node.js:
const { PdfDocument } = require("pdf-oxide");
const doc = new PdfDocument("paper.pdf");
const text = doc.extractText(0);
doc.close();
GitHub: https://github.com/yfedoseev/pdf_oxide Docs: https://oxide.fyi
Làm bindings nghe tưởng dễ — chỉ cần FFI wrapper. Thực tế mất 2 tháng để mỗi binding thấy tự nhiên với ngôn ngữ đó.
Ví dụ phần khó của Go binding: thư viện static của Rust compile ra có kích thước ~310MB mỗi platform × mỗi release. Commit vào Go module thì không ổn (user clone sẽ mất vài GB). Giải pháp là dùng go run github.com/yfedoseev/pdf_oxide/go/cmd/install@latest để tải binary cần thiết từ GitHub Releases asset lúc setup, chứ không ship trong module.
Với Node.js, vấn đề khác: prebuild file .node phải được compile với glibc cũ (centos7-era) thì mới chạy được trên Alpine Kubernetes và AWS Lambda. Build trên GitHub runner mặc định thì production báo GLIBC_2.34 not found. Một tuần CI iteration mới ra baseline tương thích.
Benchmark trên 3.830 PDF thực tế (veraPDF, Mozilla pdf.js, DARPA SafeDocs) — mọi bindings dùng chung Rust core nên số liệu như nhau:
| Thư viện | Trung bình | p99 | Pass rate | Giấy phép |
|---|---|---|---|---|
| pdf_oxide | 0.8ms | 9ms | 100% | MIT / Apache-2.0 |
| PyMuPDF | 4.6ms | 28ms | 99.3% | AGPL-3.0 |
| pypdfium2 | 4.1ms | 42ms | 99.2% | Apache-2.0 |
| pdftext | 7.3ms | 82ms | 99.0% | GPL-3.0 |
| pypdf | 12.1ms | 97ms | 98.4% | BSD-3 |
| pdfminer | 16.8ms | 124ms | 98.8% | MIT |
| pdfplumber | 23.2ms | 189ms | 98.8% | MIT |
Node binding overhead ~25% so với Rust trực tiếp. Go ~15%. C# do P/Invoke skip được mutex nên đôi khi còn nhanh hơn cả Rust gốc trên file nhỏ. PDF mã hoá AES-256 vẫn còn vài edge case đang xử lý. Trích xuất bảng cơ bản hơn pdfplumber. Ngoài ra stable cho production.
Mình rất muốn nghe ý kiến mọi người — API có tự nhiên với Go / C# / Node.js không, có ai đang gặp pain point nào mà chưa giải quyết được với thư viện hiện có không. Bug report và feedback qua GitHub issues hay comment dưới post đều welcome.
All rights reserved