LiteParse 项目深度分析报告
1. 项目介绍
1.1 项目概述
LiteParse 是由 LlamaIndex 团队(run-llama 组织)开发和维护的一个开源、本地、轻量级的文档解析器**。它的官方定位是 LlamaParse(LlamaIndex 云端文档解析 SaaS)的本地/离线开源替代品。
一句话定义:"一个专注于快、轻的开源文档解析器,提供高质量的空间文本提取和精确的边界框(bounding box),完全本地运行,无需云依赖。"
项目解决的核心问题:
- 隐私敏感场景下无法上传文档到云端 LlamaParse
- 离线/网络受限环境中的文档解析需求
- 速度:比传统 Python PDF 解析库(pypdf、PyMuPDF)快 1–2 个数量级
- 空间信息:保留每个文字在页面中的精确坐标,这对 RAG 的块切分、图/表区域识别、多模态 LLM 至关重要
项目命名中的 "lite" 强调:二进制小、零运行时依赖、极简 API、仅关注文本 + 边界框(不做表格语义分割、图像识别等复杂任务)。
1.2 项目地址
| 项目 | 内容 |
|---|---|
| GitHub 仓库 | https://github.com/run-llama/liteparse |
| 组织 | run-llama(LlamaIndex 官方组织) |
| 主要维护者 | Logan Markewich 及 LlamaIndex 团队 |
| 当前版本 | v2.0.7(截至 2026-06-09) |
| 许可证 | Apache-2.0 |
| 首次提交 | 约 2025 年 5 月 |
| Release tag 数 | 62 个 |
| 分析日期 | 2026-06-09 |
1.3 项目官网与发布渠道
| 渠道 | 地址/包名 | 说明 |
|---|---|---|
| GitHub | https://github.com/run-llama/liteparse | 主仓库 |
| Crates.io | liteparse |
Rust 包 |
| PyPI | liteparse |
Python 包(同时安装 lit CLI) |
| npm (Node) | @llamaindex/liteparse |
Node.js/TypeScript 包 |
| npm (WASM) | @llamaindex/liteparse-wasm |
浏览器 WebAssembly 包 |
| 中文 README | README.zh-CN.md | 官方中文文档 |
1.4 项目示意图
┌────────────────────────────────────────────────────────────────────┐
│ LiteParse 架构总览 │
└────────────────────────────────────────────────────────────────────┘用户输入文件 调用方式┌────────────────────────┐ ┌─────────────────────────┐│ PDF / DOCX / PPTX / │ │ Python: liteparse ││ XLSX / ODT / PNG / ... │ │ Rust: liteparse crate │└───────────┬────────────┘ │ Node: @llamaindex/... ││ │ WASM: @llamaindex/... ││ │ CLI: lit parse │▼ └────────────┬────────────┘┌───────────────────────────────────────────────┼──────────────────┐│ LiteParse 核心引擎(Rust) │ ││ │ ││ Step 1: 输入格式检测与转换 │ ││ ┌────────────────────────────────────┐ │ ││ │ infer() —— 文件类型嗅探(infer crate) │ │ ││ │ conversion.rs —— 非 PDF → 调用外部工具 │ │ ││ │ Office → LibreOffice --headless│ │ ││ │ Image → ImageMagick convert │ │ ││ └──────────────────┬─────────────────────────┘ │ ││ │ │ ││ ▼ │ ││ Step 2: 原生文本提取(Google PDFium) │ ││ ┌────────────────────────────────────────────┐ │ ││ │ pdfium crate —— Rust 高层封装 │ │ ││ │ pdfium-sys crate —— FFI 绑定到 C PDFium │ │ ││ │ extract.rs —— FPDFTextPage → TextItem │ │ ││ │ 每个 TextItem 包含: │ │ ││ │ text / x / y / width / height / rotation │ │ ││ │ font_name / font_size / font_height / │ │ ││ │ font_weight / fill_color / confidence │ │ ││ └──────────────────────┬──────────────────────────────┘ │ ││ │ │ ││ ▼ │ ││ Step 3: OCR 通道(可选) │ ││ ┌─────────────────────────────────────────────────┐ │ ││ │ render.rs —— 将页面渲染为位图(dpi 控制分辨率) │ │ ││ │ ocr.rs —— 选择 OCR 引擎: │ │ ││ │ ① ocr_engine_override(Rust trait 注入) │ ││ │ ② HTTP OCR Server(reqwest 并发请求)│ │ ││ │ ③ Tesseract(默认,随库捆绑) │ │ ││ │ ocr_merge.rs —— OCR 结果与原生文本合并 │ │ ││ │ (坐标反投影回 PDF 坐标系统) │ │ ││ └──────────────────────┬───────────────────────────────┘ │ ││ │ │ ││ ▼ │ ││ Step 4: 网格投影(Projection —— LiteParse 关键创新) │ ││ ┌────────────────────────────────────────────────────┐ │ ││ │ projection.rs —— 栅格化到虚拟列/行网格 │ │ ││ │ Snap 机制: Left / Right / Center 对齐 │ │ ││ │ Anchor 机制: 解决多列、页边、列表编号等排版问题 │ │ ││ │ 生成保留视觉阅读顺序的纯文本 page.text │ │ ││ │ 同时保留每个 TextItem 的精确坐标 (x, y, w, h) │ │ ││ └──────────────────────┬────────────────────────────────────┘ │ ││ │ │ ││ ▼ │ ││ Step 5: 输出格式化 │ ││ ┌────────────────────────────────────────────────────┐ │ ││ │ output.rs —— OutputFormat::Text 或 Json │ │ ││ │ Text: 拼接各页纯文本(保留布局) │ │ ││ │ Json: ParsedPage → TextItem 结构化序列化 │ │ ││ │ │ │ ││ │ render.rs —— ScreenshotResult (PNG 截图) │ │ ││ │ 多模态 LLM 使用:页面图像 + 文本边界框 │ │ ││ └──────────────────────┬────────────────────────────────────┘ │ ││ │ │ │└─────────────────────────┼────────────────────────────────────────────────┘ ││ │▼ │┌────────────────────────────────────────────┐ ││ ParseResult 输出结构 │ ││ │ ││ { │ ││ pages: [ │ ││ { │ ││ page_num, page_width, page_height, │ ││ text, // 纯文本(保留布局) │ ││ text_items: [ │ ││ { text, x, y, width, height, │ ││ font_name, font_size, confidence } │ ││ ] │ ││ } │ ││ ], │ ││ text: String // 全文拼接 │ ││ } │ │└────────────────────────────────────────────┘ ││┌───────────────────────────────────────────────────────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 下游 RAG / 多模态 / Agent 应用 │
│ │
│ • LlamaIndex: 替换 SimpleDirectoryReader 的 PDFReader │
│ • 基于边界框做区域切分(识别表格、标题、段落) │
│ • 页面截图 + bbox → 多模态 LLM(Claude/GPT-4o) │
│ • 搜索 search_items() → 合并边界框高亮关键字 │
│ • AI Agent 工具流:npx skills add run-llama/llamaparse-agent-skills │
└─────────────────────────────────────────────────────────────────────────┘
2. 项目亮点
2.1 核心亮点概览
| 亮点 | 说明 |
|---|---|
| 速度 | Rust + PDFium 核心,比传统 Python 解析库快 10–100× |
| 空间信息 | 每个文字项带精确 (x, y, width, height) 边界框 |
| 多语言 API | Rust / Python / Node.js (TypeScript) / WebAssembly,四种语言原生绑定 |
| 本地、离线、零云依赖 | 完全在本地运行,无 API Key,无网络请求 |
| OCR 可插拔 | 内置 Tesseract + HTTP OCR 服务规范 + 自定义引擎注入 |
| 极简 API | parse() / screenshot() 两个主方法 + 一个 Config 结构体 |
| 多格式支持 | PDF + 所有主流 Office + 所有主流图像格式 |
| 网格投影算法 | Snap/Anchor 机制重建阅读顺序,解决多列排版问题 |
| 二进制分发 | Python wheel 随附 prebuilt PDFium,pip install 即可用 |
| 截图能力 | 高质量页面 PNG,供多模态 LLM 直接消费 |
2.2 速度 —— Rust + PDFium 的性能优势
LiteParse 的核心速度来自两个设计决策:
- Rust 零成本抽象 + 无 GC:文本提取循环、投影算法、并发 OCR 都在 Rust 层完成,无 Python GIL,无解释器开销。
- Google PDFium 引擎:这是 Chromium 浏览器使用的 PDF 引擎,由 Google 维护,在字体处理、私有编码、中文支持、图像/表单处理等方面是行业标杆。相比纯 Rust PDF 解析库,PDFium 在复杂文档上的鲁棒性好得多。
实际速度表现(社区 benchmark 报告):
- 普通 10 页 PDF:纯文本提取 毫秒级 / 页
- 200 页扫描文档(启用 OCR):在 8 核 CPU 上约 10–30 秒完成
- 对比 pypdf/PyMuPDF:在复杂文档上快 10–100×
2.3 空间信息 —— 每个 TextItem 带精确坐标
这是 LiteParse 与传统文本提取工具(pypdf、markitdown 等)最大的区别之一:
传统提取(pypdf):"第三章 实验方法 ... "(只拿到文本,不知道"第三章"在页面哪个位置)LiteParse:TextItem { text: "第三章", x: 72.0, y: 90.5, width: 35.2, height: 14.3, ... }TextItem { text: "实验方法", x: 112.3, y: 90.5, width: 50.1, height: 14.3, ... }
为什么空间信息重要?
- RAG 区域切分:可以基于边界框识别"这是一段正文"、"这是标题"、"这是表格区域",从而做更智能的块切分
- 多模态 LLM:给 GPT-4o / Claude 同时发送 页面截图 + 文本 + bbox,模型可以"看到"文档的版面结构
- 表格近似识别:通过 y 坐标聚类 + x 坐标分布,可以启发式识别表格区域
- 关键字高亮:
search_items()返回合并边界框,可直接在 UI 中高亮
2.4 OCR 系统 —— 三层可插拔架构
OCR 引擎选择优先级(从高到低):① ocr_engine_override→ Rust trait 注入:with_ocr_engine(Arc<dyn OcrEngine>)→ WASM 端通过 JS ocrEngine 回调注入→ 最灵活,支持任何引擎(PaddleOCR/EasyOCR/TrOCR/自建模型)② ocr_server_url(HTTP OCR)→ 并发 num_workers 个 POST /ocr 请求→ 返回 { text, bbox, confidence } JSON→ 遵循 OCR_API_SPEC.md 规范→ 适合部署 GPU OCR 服务,客户端保持轻量③ Tesseract(默认)→ feature = "tesseract",随库捆绑→ 通过 TESSDATA_PREFIX 支持离线语言包→ 支持 eng / chi_sim / jpn / kor / fra / deu / ... 上百种
架构优势:默认够用(Tesseract),同时保留升级通道(HTTP),还允许完全自定义(trait 注入),兼顾 "开箱即用" 和 "生产部署" 两种场景。
2.5 网格投影(Projection)—— LiteParse 的关键算法
这是 LiteParse 在"保留阅读顺序"方面的核心创新:
输入:TextItem 列表(每个有 x, y, w, h, text)算法流程:Step 1: 行聚类(y 坐标分组)┌────────────────────────────────────┐│ "第三章 实验方法" │ ← y = 90.5,同一行合并│ "本章节描述了三种实验方法..." │ ← y = 120.3│ "方法 A:..." │ ← y = 150.1(多列情况) "方法 B:..."└────────────────────────────────────┘Step 2: Snap 机制(对齐吸附)每个 TextItem 计算左/中/右三个可能的吸附点通过 HashMap 聚类相同 x 坐标的吸附点→ 识别出页面中"隐含的列边界"Step 3: Anchor 机制将 Snap 点锚定到页面全局网格解决:• 多列文档(每列独立阅读顺序)• 页边空白(过滤 margin noise)• 列表编号/项目符号(单独吸附为独立列)Step 4: 阅读顺序重建按 行 主序(先从上到下)在同一行内按 Snap 列顺序(从左到右)→ 生成保留视觉布局的纯文本输出:page.text(保留行间距与缩进的纯文本)+ 完整 TextItem 列表(仍保留精确坐标)
算法复杂度:O(n log n),n 为 TextItem 数量
与传统 PDF 文本提取的区别:传统方法通常是"按 PDF 内部存储顺序"输出文本,经常出现列错位、文字顺序错乱(尤其是多列布局的论文)。LiteParse 的投影算法从几何层面重建阅读顺序,显著提升文本可读性。
2.6 极简 API 设计
以 Python 为例,整个库的 API 可以浓缩为:
class LiteParse:def __init__(self, **config) -> None: ... # 配置(全部可选)def parse(self, path_or_bytes) -> ParseResult: ... # 解析文档 → 结构化结果def parse_bytes(self, bytes) -> ParseResult: ... # 直接解析字节def screenshot(self, path, page_numbers) -> list[ScreenshotResult]: ...# 搜索(返回带合并边界框的匹配项)def search_items(text_items, keyword, case_sensitive=False): ...class ParseResult:pages: list[ParsedPage] # 每页结构化数据text: str # 全文拼接(保留布局)class ParsedPage:page_num: intpage_width: floatpage_height: floattext: str # 本页纯文本(保留布局)text_items: list[TextItem] # 细粒度文本项class TextItem:text: strx, y, width, height: float # PDF 72 DPI 坐标(左上原点)rotation: floatfont_name: strfont_size: floatfont_height: floatfont_weight: intfill_color: strconfidence: float
配置项仅有 10 个左右,全部有合理默认值,用户通常不需要修改。
2.7 多语言绑定(四套语言,一套核心)
┌────────────────────────────────────┐│ Rust 核心 (crates/) ││ liteparse —— 核心库 + CLI ││ pdfium —— PDFium 高层封装 ││ pdfium-sys —— PDFium FFI 绑定 │└────────────┬───────────────────────┘│┌─────────────────┼─────────────────┬──────────────────┐│ │ │ │▼ ▼ ▼ ▼┌────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐│ PyO3 绑定 │ │ napi-rs 绑定 │ │ wasm-bindgen │ │ Rust 原生 ││crates/ │ │ crates/ │ │ crates/ │ │ crates/ ││liteparse-│ │ liteparse-napi │ │ liteparse- │ │ liteparse ││python │ │ │ │ wasm │ │ ││ │ │ │ │ │ │ ││ → packages/│ │ → packages/ │ │ → packages/ │ │ → Crates.io ││ python/ │ │ node/ │ │ wasm/ │ │ ││ │ │ │ │ │ │ ││ PyPI: │ │ npm: │ │ npm: │ │ 直接 cargo ││ liteparse │ │ @llamaindex/│ │ @llamaindex/ │ │ use ││ │ │ liteparse │ │ liteparse- │ │ ││ │ │ │ │ wasm │ │ │└────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
优势:
- 所有语言绑定共享同一套 Rust 核心,行为一致,性能一致
- Python/Node 端只提供薄封装,无额外性能开销
- 用户可以选择最适合自己的语言,不需要学习多套 API
2.8 与传统工具的对比
| 工具 | 纯本地 | 空间信息/bbox | OCR | 速度 | 依赖 | 云调用 |
|---|---|---|---|---|---|---|
| LiteParse | ✅ | ✅ 原生 | ✅ Tesseract/HTTP | 极快 | Rust + PDFium | ❌ |
| LlamaParse | ❌ | ✅ | ✅ | 快 | SaaS | ✅ |
| pypdf | ✅ | 有限 | ❌ | 中 | 纯 Python | ❌ |
| PyMuPDF (fitz) | ✅ | ✅ | ❌ | 快 | C | ❌ |
| unstructured.io | 半 | ✅ | 可选 | 中 | 重依赖 | 可选 |
| markitdown | ✅ | ❌ | ❌ | 中 | 纯 Python | ❌ |
LiteParse 在"本地 + 空间信息 + OCR + 速度"的组合上是目前最均衡的开源方案。
3. 运行环境与条件
3.1 系统要求
| 组件 | 最低要求 | 推荐 |
|---|---|---|
| 操作系统 | Linux (x86_64, aarch64) / macOS 11+ (Intel/ARM) / Windows 10+ | Linux x86_64 |
| 内存 | 256 MB RAM(小文档) | 2 GB+ RAM(OCR 大文档) |
| 磁盘空间 | 30–50 MB(核心库 + PDFium) | 100 MB+(含 tesseract 语言包) |
| CPU | 任何支持 AVX 的 x86 CPU 或 ARM64 | 4 核+(OCR 并发受益) |
| 可选工具 | LibreOffice(解析 Office 文档时需要) | LibreOffice 7+ |
| 可选工具 | ImageMagick(解析图像文件时需要) | ImageMagick 7+ |
3.2 编程语言构成
| 语言 | 角色 |
|---|---|
| Rust | 核心库 + CLI + 原生文本提取逻辑 + PDFium 封装 |
| Python | PyO3 绑定层 + 薄封装 |
| TypeScript / JavaScript | Node.js 绑定(napi-rs)+ WASM 浏览器端 |
| Python(辅助) | OCR 服务器示例(EasyOCR/PaddleOCR)、数据集评估 |
| C++ (FFI) | PDFium 动态库(Google PDFium 项目提供,随包分发) |
3.3 安装方式
方式一:Python(最常用)
# 核心安装
pip install liteparse
# 已包含:Rust 扩展(_liteparse)+ PDFium 动态库 + lit CLI
# 零 Python 运行时依赖# 验证
python -c "from liteparse import LiteParse; p = LiteParse(); print('OK')"# CLI
lit parse document.pdf
方式二:Rust
# Cargo.toml
[dependencies]
liteparse = "2.0"# 启用 OCR(默认已开)
# liteparse = { version = "2.0", features = ["tesseract"] }
use liteparse::LiteParse;fn main() -> Result<(), Box<dyn std::error::Error>> {let parser = LiteParse::default();let result = parser.parse("document.pdf")?;println!("{}", result.text);Ok(())
}
方式三:Node.js / TypeScript
npm install @llamaindex/liteparse
import { LiteParse } from "@llamaindex/liteparse";
const parser = new LiteParse({ ocrEnabled: true });
const result = await parser.parse("./document.pdf");
console.log(result.text);
方式四:浏览器 WASM
npm install @llamaindex/liteparse-wasm
import init, { LiteParse } from "@llamaindex/liteparse-wasm";
await init();const parser = new LiteParse({ ocrEnabled: false, outputFormat: "json" });
const bytes = new Uint8Array(await file.arrayBuffer());
const result = await parser.parse(bytes);
方式五:Docker
# 标准镜像(仅 Rust/Python 核心 + PDFium)
docker pull ghcr.io/run-llama/liteparse:latest
docker run -v ./docs:/docs ghcr.io/run-llama/liteparse:latest \lit parse /docs/report.pdf# 完整版(含 LibreOffice + ImageMagick)
docker pull ghcr.io/run-llama/liteparse:full
3.4 配置项完整清单
| 配置项(Python/Rust/JS 命名可能为 snake_case/camelCase) | 默认值 | 说明 |
|---|---|---|
ocr_enabled |
true |
是否启用 OCR(扫描/图片 PDF 必需) |
ocr_language |
"eng" |
Tesseract 语言代码,多语言用 + 连接(如 "eng+chi_sim") |
ocr_server_url |
None/null |
自定义 HTTP OCR 服务地址(启用后跳过 Tesseract) |
tessdata_path |
None/null |
离线 tessdata 目录路径 |
max_pages |
1000 |
最大处理页数(防超大文档) |
target_pages |
None/null |
要处理的页范围(如 "1-5,10,15-20"),空=全部 |
dpi |
150 |
OCR / 截图渲染分辨率(越高越慢,越清晰) |
output_format |
"text" |
"text" 或 "json" |
preserve_very_small_text |
false |
是否保留极小字体(通常是噪音) |
password |
None/null |
加密 PDF 口令 |
quiet |
false |
静默模式(关闭 [liteparse] 日志输出) |
num_workers |
CPU 核心数 - 1 | OCR 并发 workers 数 |
3.5 支持的文件格式
原生支持(无需外部工具)
- PDF(
.pdf)
通过 LibreOffice 自动转换为 PDF
- 字处理:
.doc/.docx/.docm/.odt/.rtf/.pages - 演示:
.ppt/.pptx/.pptm/.odp - 表格:
.xls/.xlsx/.xlsm/.ods/.csv/.tsv
通过 ImageMagick 自动转换为 PDF
- 图像:
.png/.jpg/.jpeg/.gif/.bmp/.tiff/.webp/.svg
文件类型嗅探:使用 infer crate 基于文件魔数(magic bytes)检测,不依赖扩展名。
3.6 OCR 语言支持(Tesseract)
支持上百种语言,常用代码:
| 语言 | 代码 | 语言 | 代码 |
|---|---|---|---|
| 英语 | eng |
简体中文 | chi_sim |
| 繁体中文 | chi_tra |
日语 | jpn |
| 韩语 | kor |
法语 | fra |
| 德语 | deu |
西班牙语 | spa |
| 俄语 | rus |
阿拉伯语 | ara |
| 印地语 | hin |
葡萄牙语 | por |
多语言识别:ocr_language="eng+chi_sim+fra"
4. 代码架构与核心模块解析
4.1 代码架构图
liteparse/ 【项目根目录】
│
├── Cargo.toml Rust workspace 根
│ [workspace]
│ members = ["crates/*"]
│
├── Cargo.lock
│
├── README.md / README.zh-CN.md 英文/中文文档
├── OCR_API_SPEC.md HTTP OCR 服务规范
├── CHANGELOG.md / SECURITY.md / CONTRIBUTING.md
├── LICENSE Apache-2.0
├── AGENTS.md / CLAUDE.md 给 AI Agent 的提示
│
├── Dockerfile / full.Dockerfile 标准/完整容器镜像
│
├── .github/workflows/ CI / Release / 多平台打包
│
├── crates/ 【Rust crates 工作区】
│ │
│ ├── liteparse/ ★ 核心库 + CLI
│ │ ├── Cargo.toml (v2.0.7)
│ │ └── src/
│ │ ├── lib.rs 对外 API(LiteParse struct)
│ │ ├── parser.rs 调度核心 parse_input()
│ │ ├── types.rs 类型定义(TextItem/ParsedPage)
│ │ ├── extract.rs PDFium 文本提取
│ │ ├── projection.rs ★ 网格投影算法(核心创新)
│ │ ├── ocr.rs OCR 引擎选择 + 并发调度
│ │ ├── ocr_merge.rs OCR 结果与原生文本合并
│ │ ├── output.rs Text/Json 输出格式化
│ │ ├── conversion.rs Office/Image → PDF 转换
│ │ ├── render.rs 页面渲染为 PNG 截图
│ │ ├── config.rs LiteParseConfig
│ │ ├── error.rs LiteParseError (thiserror)
│ │ └── search.rs search_items() 搜索工具
│ │
│ ├── liteparse-python/ PyO3 Python 绑定
│ │ ├── Cargo.toml
│ │ └── src/lib.rs #[pyclass] struct LiteParse
│ │
│ ├── liteparse-napi/ napi-rs Node.js 绑定
│ │ ├── Cargo.toml
│ │ └── src/lib.rs
│ │
│ ├── liteparse-wasm/ wasm-bindgen 浏览器绑定
│ │ ├── Cargo.toml
│ │ └── src/lib.rs
│ │
│ ├── pdfium/ ★ Rust 对 PDFium 的高层封装
│ │ ├── Cargo.toml
│ │ └── src/
│ │
│ └── pdfium-sys/ PDFium FFI 底层绑定(C→Rust)
│ ├── Cargo.toml
│ ├── build.rs 构建时定位 PDFium 库
│ └── src/
│
├── packages/ 【语言包发布目录】
│ │
│ ├── python/ PyPI 包
│ │ ├── pyproject.toml maturin 配置
│ │ └── liteparse/
│ │ ├── __init__.py 包入口
│ │ ├── parser.py Python 侧薄包装
│ │ ├── types.py dataclass 类型定义
│ │ └── cli.py `lit` CLI 入口
│ │
│ ├── node/ npm @llamaindex/liteparse
│ │
│ └── wasm/ npm @llamaindex/liteparse-wasm
│
├── ocr/ 【示例 OCR 服务】
│ ├── easyocr/ EasyOCR HTTP 服务端示例
│ └── paddleocr/ PaddleOCR HTTP 服务端示例
│
├── dataset_eval_utils/ 数据集质量评估(Python)
│
├── demo/docs/ 示例文档(benchmark 用)
│
├── integration_tests_data/ 集成测试数据
│
├── scripts/ 构建脚本、musl 交叉编译
│
├── docs.config.mjs 文档站点配置
│
└── wasm-demo-site/ WASM 在线 demo
4.2 核心模块详解
4.2.1 crates/liteparse/src/lib.rs —— 对外 API
定义了对外暴露的核心类型和 API:
pub struct LiteParse {config: LiteParseConfig,
}impl LiteParse {/// 用默认配置构造pub fn new() -> Self { ... }/// 带自定义配置构造pub fn with_config(config: LiteParseConfig) -> Self { ... }/// 从文件路径解析(同步)pub fn parse(&self, path: impl AsRef<Path>) -> Result<ParseResult, LiteParseError> {self.parse_input(PdfInput::Path(path.as_ref().into()))}/// 从内存字节解析(适合从网络下载后直接解析)pub fn parse_bytes(&self, bytes: &[u8]) -> Result<ParseResult, LiteParseError> {self.parse_input(PdfInput::Bytes(bytes.to_vec()))}/// 生成页面截图pub fn screenshot(&self, path: impl AsRef<Path>, page_numbers: &[u32])-> Result<Vec<ScreenshotResult>, LiteParseError> { ... }/// ★ 核心调度方法fn parse_input(&self, input: PdfInput) -> Result<ParseResult, LiteParseError> {// Step 1: 输入格式检测与转换(conversion.rs)// Step 2: 文本提取(extract.rs)// Step 3: OCR 通道(ocr.rs + ocr_merge.rs)// Step 4: 网格投影(projection.rs)// Step 5: 输出格式化(output.rs)}
}pub struct ParseResult {pub pages: Vec<ParsedPage>,pub text: String,
}pub struct ParsedPage {pub page_num: u32,pub page_width: f32,pub page_height: f32,pub text: String,pub text_items: Vec<TextItem>,
}pub struct TextItem {pub text: String,pub x: f32,pub y: f32,pub width: f32,pub height: f32,pub rotation: f32,pub font_name: String,pub font_size: f32,pub font_height: f32,pub font_weight: i32,pub fill_color: String,pub confidence: f32,// ... 内部字段:snap, anchor, num_spaces, is_dup, is_margin_line_number
}pub struct ScreenshotResult {pub page_num: u32,pub width: u32,pub height: u32,pub image_bytes: Vec<u8>, // PNG
}
设计洞察:LiteParse 是一个无状态结构体(仅保存配置)。parse() 和 parse_bytes() 都是同步方法(Python/Node 端在绑定层做了 async 包装)。parse_input() 是统一的调度核心,接收 PdfInput::Path 或 PdfInput::Bytes 两种输入。
4.2.2 crates/liteparse/src/parser.rs —— 核心调度
parse_input() 的五阶段流程是整个项目的骨架:
Phase 1 ── 输入检测与格式转换├── infer::get_kind() —— 基于 magic bytes 判断文件类型(PDF/Image/Office)├── 如果是 PDF:直接进入 Phase 2├── 如果是 Image:调用 ImageMagick `convert` → 临时 PDF(tempfile::TempPath)└── 如果是 Office:调用 LibreOffice `--headless --convert-to pdf` → 临时 PDFPhase 2 ── 原生文本提取(见 extract.rs + pdfium crate)├── pdfium::PdfDocument::open(path_or_bytes)├── 按页循环 for page in document.pages() {│ let text_page = page.text_page()?; // FPDFTextPage│ for char_index in 0..text_page.count_chars() {│ 提取字符 → 合并为 TextItem(按 word/line 聚合)│ }│ }└── 生成 Vec<ParsedPage>(还未做投影,只是原始 text_items)Phase 3 ── OCR 通道(见 ocr.rs + ocr_merge.rs)├── 如果 config.ocr_enabled:│ ① 将页面渲染为位图(dpi 控制)│ ② 选择引擎:override > HTTP > Tesseract│ ③ 并发 num_workers 个 OCR 任务│ ④ 将 OCR 结果的像素坐标反投影回 PDF 坐标│ ⑤ 与 Phase 2 的原生文本合并│ (替换没有有效文本的区域,保留有文本的区域)└── 否则:跳过Phase 4 ── 网格投影(见 projection.rs,★ 核心算法)├── for page in pages:│ ① 行聚类(y 坐标分组)— y 相近的合并为同一行│ ② Snap 吸附(左右中三种吸附点)— HashMap 聚类 x 坐标│ ③ Anchor 锚定 — 将 Snap 点锚定到全局虚拟网格│ ④ 重建阅读顺序 — 先 y 后 x,多列布局自动检测│ ⑤ 生成 page.text(保留布局的纯文本)│ ⑥ 过滤 is_dup / is_margin_line_number 噪音└── 结果:更新 pages 中的每个 ParsedPagePhase 5 ── 输出格式化(见 output.rs)├── match config.output_format:│ OutputFormat::Text → 拼接 pages.iter().map(|p| p.text).collect()│ OutputFormat::Json → serde_json::to_string(&pages)└── 返回 ParseResult { pages, text }
4.2.3 crates/liteparse/src/extract.rs —— PDFium 文本提取
核心流程:
/// 从 PDFium 的 FPDFTextPage 提取 TextItem 列表
fn extract_text_items(page: &PdfPage,text_page: &FPDFTextPage,page_num: u32,
) -> Result<Vec<TextItem>, LiteParseError> {// Step 1: 获取页面尺寸let (page_width, page_height) = page.size();// Step 2: 遍历字符 → 合并为词/行let char_count = text_page.count_chars();let mut items: Vec<TextItem> = Vec::new();// 遍历字符,按"空格"切分为词,按"换行"切分为行let mut current_word_chars: Vec<FPDFChar> = Vec::new();for i in 0..char_count {let ch = text_page.get_char(i)?;// 获取字符在 PDF 中的矩形区域let rect = text_page.get_char_box(i)?;// rect: { left, top, right, bottom } —— PDF 坐标(左下原点)// 注意:后续会规范化到 top-left 原点if ch.is_whitespace() || ch == '\n' {if !current_word_chars.is_empty() {// 合并 current_word_chars 为一个 TextItemlet item = merge_chars_to_item(¤t_word_chars, page_width, page_height)?;items.push(item);current_word_chars.clear();}if ch == '\n' {// 行边界标记}} else {current_word_chars.push(ch);}}// Step 3: 坐标规范化// PDF 原生坐标系:原点在左下角,Y 向上递增// LiteParse 输出坐标系:原点在左上角,Y 向下递增// 这与大多数 UI/图像处理库一致for item in &mut items {item.y = page_height - item.y - item.height;}// Step 4: 附加字体信息// font_name / font_size / font_height / font_weight / fill_color// 这些从 PDFium 的 FPDF_Font 对象读取// Step 5: confidence 标记// 原生提取的文本 confidence = 1.0(确定)// OCR 提取的文本 confidence = OCR 引擎返回值Ok(items)
}
关于 PDFium:
- PDFium 是 Google 维护的开源 PDF 引擎,被 Chromium、Chrome、Edge 等使用
- LiteParse 通过
pdfium-syscrate 做 FFI 绑定,pdfiumcrate 做高层封装 PDFIUM_LIB_PATH环境变量可以指定自定义 PDFium 动态库位置- Python/Node 包的 wheel 中已包含预编译的
libpdfium.{so,dylib,dll}
4.2.4 crates/liteparse/src/projection.rs —— 网格投影算法
这是 LiteParse 区别于其他 PDF 解析器的核心创新。
问题背景:传统 PDF 文本提取按照 PDF 内部的"绘制顺序"输出文字。对于多列布局的文档(如论文、报纸、双栏书籍),输出的文本顺序常常错乱:
真实文档布局:┌──────────┬──────────┐│ 左栏第1段 │ 右栏第1段 ││ 左栏第2段 │ 右栏第2段 │└──────────┴──────────┘传统提取结果(错乱):"左栏第1段 右栏第1段 左栏第2段 右栏第2段"(左右栏交错,无法阅读)LiteParse 投影算法输出(正确阅读顺序):"左栏第1段左栏第2段右栏第1段右栏第2段"
算法详细说明:
数据结构:struct TextItem { text, x, y, width, height, ... }enum Snap { Left, Right, Center } // 三种吸附方式struct Anchor { col_index, snap_point_x }Step 1 —— 行聚类(Y 坐标分组)原理:同一行的文字 y 坐标接近方法:对所有 TextItem 按 y 排序,使用容差(如 y ± 0.5 * font_height)分组,同组视为同一行伪代码:let rows: Vec<Vec<&TextItem>> = [];let sorted_by_y: Vec<&TextItem> = sort(items, key=|it| it.y);for item in sorted_by_y:if rows.last().is_some_and(|r|abs(r.last().unwrap().y - item.y) < item.font_height * 0.5):rows.last_mut().unwrap().push(item); // 加入当前行else:rows.push(vec![item]); // 开新行Step 2 —— Snap 吸附(检测列边界)原理:同列的文字左边界/右边界/中心应该对齐方法:对每个 TextItem 计算三个潜在吸附点:snap_left = item.xsnap_center = item.x + item.width / 2snap_right = item.x + item.width用 HashMap<f32_with_tolerance, Count> 统计每个吸附点出现次数:let mut snap_points: HashMap<RoundTo2dp, usize> = HashMap::new();for item in items:*snap_points.entry(round(item.x)).or_insert(0) += 1;*snap_points.entry(round(item.x + item.width/2.0)).or_insert(0) += 1;*snap_points.entry(round(item.x + item.width)).or_insert(0) += 1;出现次数多的吸附点 → 隐含的"列边界"例:如果 snap_x = 72.0 出现 80 次,说明页面左侧 x=72 处是一个列边界Step 3 —— Anchor 锚定(分配到虚拟列网格)原理:将每个 TextItem 的 Snap 点"锚定"到最接近的列边界方法:let columns: Vec<f32> = top_n_snap_points(snap_points, n=6); // 最多6列for item in items:let nearest_col = columns.iter().min_by_key(|c| abs(c - item.x)).unwrap();item.internal_anchor = nearest_col.index;item.internal_snap = nearest_col.snap_type;Step 4 —— 重建阅读顺序先按行排序(y 坐标) → 在每行内按列索引排序(anchor 索引)结果:第 1 行的所有 TextItem 按 列1 → 列2 → ... → 列N 的顺序第 2 行的所有 TextItem 按 列1 → 列2 → ... → 列N 的顺序...对于多列文档:先输出完左列所有行,再输出右列所有行(而非左右交错)(因为左列 y=90 的项 anchor=列0,y=120 的项 anchor=列0 ...右列所有项 anchor=列1,在 anchor 排序中排在列0之后)Step 5 —— 生成纯文本 page.text按行遍历,每行内部按 Snap 列顺序输出 TextItem.text保留缩进(不同 Snap 列之间的空格)保留空行(不同 y 行之间的空行)额外过滤:• is_dup —— 去重(PDF 有时会重复绘制相同文字)• is_margin_line_number —— 去除页码/边注复杂度:O(n log n)(排序主导)内存:O(n)(items 拷贝一份带 internal_* 字段)
为什么这是"关键创新":
- 大多数开源 PDF 解析器(包括 pypdf、pymupdf)在多列布局上的文本顺序是不可靠的
- LiteParse 通过几何层面的列检测(Snap/Anchor)重建了正确的阅读顺序
- 这对于 学术论文、技术文档、双栏书籍 的 RAG 质量提升非常显著
4.2.5 crates/liteparse/src/ocr.rs —— OCR 引擎系统
OCR 引擎选择优先级:1. ocr_engine_override → 自定义 Rust OcrEngine trait2. ocr_server_url → HTTP OCR 服务3. Tesseract → 默认(feature = "tesseract")并发模型:┌─────────────────────────────────────────────────────────┐│ 主线程:LiteParse::parse_input() ││ Phase 2 完成后,pages 已包含原生文本 TextItem ││ ││ for page in pages { ││ if page.has_any_text() → skip OCR (已有原生文本) ││ else → push to OCR queue ││ } ││ ││ tokio::spawn(async move { ││ 并发 num_workers 个任务: ││ ① render page to bitmap (depend on config.dpi) ││ ② call OCR engine (trait impl / HTTP / Tesseract) ││ ③ deserialize OCR result + bbox + confidence ││ }) │└────────────────────┬─────────────────────────────────────┘│▼┌─────────────────────────────────────────────────────────┐│ OCR 结果格式(通用) ││ ││ struct OcrResult { ││ text: String, ││ bbox: { x, y, width, height }, // 像素坐标 ││ confidence: f32, ││ } ││ ││ HTTP 协议(见 OCR_API_SPEC.md) ││ POST /ocr ││ Body: multipart/form-data (image: PNG/JPEG) ││ Response: { "items": [{ "text": "...", ││ "bbox": [x,y,w,h], ││ "confidence": 0.95 }] } │└────────────────────┬─────────────────────────────────────┘│▼┌─────────────────────────────────────────────────────────┐│ ocr_merge.rs —— 坐标反投影与合并 ││ ││ OCR 返回的是图像像素坐标(0..width, 0..height) ││ 需要投影回 PDF 72 DPI 坐标(0..page_width, ││ 0..page_height) ││ ││ scale_x = page_width / render_width ││ scale_y = page_height / render_height ││ text_item.x = ocr.bbox.x * scale_x ││ text_item.y = ocr.bbox.y * scale_y ││ text_item.width = ocr.bbox.width * scale_x ││ text_item.height = ocr.bbox.height * scale_y ││ text_item.confidence = ocr.confidence ││ ││ 合并策略:只对 page.has_any_text() = false 的页面使用 OCR ││ 即:优先使用 PDF 中已编码的真实文本, ││ 仅当页面是扫描图(无编码文本)时才走 OCR 路径 │└──────────────────────────────────────────────────────────┘
OcrEngine trait 定义:
pub trait OcrEngine: Send + Sync {fn ocr(&self, image_bytes: &[u8]) -> Result<Vec<OcrResult>, LiteParseError>;fn name(&self) -> &str;
}// 实现示例:TesseractEngine(feature = "tesseract")
pub struct TesseractEngine {language: String,tessdata_path: Option<String>,
}// 实现示例:HttpOcrEngine(内置)
pub struct HttpOcrEngine {url: String,client: reqwest::Client,
}// 用户自定义:任何 Rust 类型只要实现 OcrEngine trait,
// 就可以通过 with_ocr_engine() 注入
let custom: Arc<dyn OcrEngine> = Arc::new(MyCustomEngine::new());
let parser = LiteParse::with_config(config).with_ocr_engine(custom);
性能提示:num_workers 默认是 CPU 核心数 - 1。对于小文档可以设为 1 降低开销;对于大扫描文档可以设为 CPU 核心数 × 2(I/O 密集型)。
4.2.6 crates/liteparse/src/output.rs —— 输出格式化
pub enum OutputFormat {Text, // 纯文本(保留布局)Json, // 完整 ParsedPage → TextItem 结构
}fn format_output(pages: &[ParsedPage], format: OutputFormat) -> String {match format {OutputFormat::Text => {// 简单拼接:每页文本之间用 "\n\n" 分隔pages.iter().map(|p| p.text.clone()).collect::<Vec<_>>().join("\n\n")}OutputFormat::Json => {// serde_json 序列化完整结构serde_json::to_string_pretty(&pages).unwrap()}}
}
JSON 输出示例(简化):
[{"page_num": 1,"page_width": 612.0,"page_height": 792.0,"text": "第三章 实验方法\n\n本章节描述了三种实验方法...","text_items": [{"text": "第三章","x": 72.0, "y": 90.5,"width": 35.2, "height": 14.3,"font_name": "SimSun-Bold","font_size": 14.0,"confidence": 1.0},...]}
]
4.2.7 crates/liteparse/src/conversion.rs —— 格式转换
/// 将非 PDF 输入转换为临时 PDF 文件
fn convert_to_pdf(input: &InputFile) -> Result<TempPath, LiteParseError> {let kind = infer::get_kind(input.bytes())?;match kind {infer::Kind::PDF => {// 已是 PDF,无需转换Ok(TempPath::from_bytes(input.bytes())?)}infer::Kind::Image(_) => {// 调用 ImageMagick convertlet tmp = TempFile::new()?;let status = std::process::Command::new("convert").arg(input.path()).arg(tmp.path()).status()?;if !status.success() {return Err(LiteParseError::ConversionFailed("ImageMagick".into()));}Ok(tmp.into_temp_path())}_ => {// 假定是 Office 文档,调用 LibreOffice headlesslet tmp_dir = TempDir::new()?;let status = std::process::Command::new("libreoffice").arg("--headless").arg("--convert-to").arg("pdf").arg("--outdir").arg(tmp_dir.path()).arg(input.path()).status()?;if !status.success() {return Err(LiteParseError::ConversionFailed("LibreOffice".into()));}// 在 tmp_dir 中找生成的 pdflet pdf_path = tmp_dir.path().join(input.path().file_stem().unwrap().to_string_lossy() + ".pdf");Ok(TempPath::from_file(pdf_path)?)}}
}
设计权衡:
- ✅ 不需要重新实现 Office 解析(极复杂,可靠性差)
- ✅ 复用成熟的 LibreOffice,支持上百种格式
- ❌ 需要用户安装 LibreOffice / ImageMagick
- ❌ 转换有额外磁盘 I/O 和时间开销
- ✅ 使用 tempfile::TempPath,进程退出自动清理
4.2.8 crates/liteparse/src/render.rs —— 页面截图
pub fn render_page_to_png(page: &PdfPage,dpi: u32,
) -> Result<Vec<u8>, LiteParseError> {// Step 1: PDF page → 位图// PDF 默认 72 DPI → dpi 参数缩放let scale = dpi as f32 / 72.0;let width = (page.width() * scale) as u32;let height = (page.height() * scale) as u32;// Step 2: PDFium 渲染到内存位图(BGRA)let bitmap = pdfium::render_page_to_bitmap(page, width, height)?;// Step 3: BGRA → PNG 编码(image crate)let img = image::RgbaImage::from_raw(width, height, bitmap.into_bytes()).ok_or(LiteParseError::RenderFailed)?;// Step 4: PNG 压缩输出let mut buf = Vec::with_capacity((width * height * 4) as usize / 10);img.write_to(&mut buf, image::ImageFormat::Png)?;Ok(buf)
}
用途:
- 给多模态 LLM 提供页面视觉输入
- 配合 TextItem 的边界框,LLM 可以"看图+读坐标"
- 文档预览 / 缩略图
4.2.9 crates/liteparse/src/config.rs —— 配置
#[derive(Clone, Debug)]
pub struct LiteParseConfig {pub ocr_enabled: bool,pub ocr_language: String,pub ocr_server_url: Option<String>,pub tessdata_path: Option<String>,pub max_pages: u32,pub target_pages: Option<String>,pub dpi: u32,pub output_format: OutputFormat,pub preserve_very_small_text: bool,pub password: Option<String>,pub quiet: bool,pub num_workers: usize,// 自定义 OCR 引擎(依赖注入)pub ocr_engine_override: Option<Arc<dyn OcrEngine>>,
}impl Default for LiteParseConfig {fn default() -> Self {Self {ocr_enabled: true,ocr_language: "eng".into(),ocr_server_url: None,tessdata_path: None,max_pages: 1000,target_pages: None,dpi: 150,output_format: OutputFormat::Text,preserve_very_small_text: false,password: None,quiet: false,num_workers: num_cpus::get().saturating_sub(1).max(1),ocr_engine_override: None,}}
}
合理默认值的设计:默认值被精心选择,使大多数用户无需修改。例如 num_workers = CPU - 1 保证不会占满机器。
4.2.10 crates/liteparse/src/error.rs —— 错误类型
#[derive(thiserror::Error, Debug)]
pub enum LiteParseError {#[error("Failed to open PDF: {0}")]PdfOpen(String),#[error("Page {page_num} extraction failed: {reason}")]PageExtraction { page_num: u32, reason: String },#[error("OCR failed: {0}")]Ocr(String),#[error("Format conversion failed: {0}")]ConversionFailed(String),#[error("Render failed: {0}")]RenderFailed(String),#[error("Invalid page range: {0}")]InvalidPageRange(String),#[error("PDF is password-protected and no password provided")]PasswordRequired,#[error("PDF password is incorrect")]PasswordIncorrect,#[error("Page limit exceeded: {actual} > {max}")]PageLimitExceeded { actual: u32, max: u32 },#[error("I/O error: {0}")]Io(#[from] std::io::Error),
}
Python/Node 绑定层:将这些错误统一转换为各自语言的原生异常类型(Python → RuntimeError,Node → Error)。
4.2.11 crates/liteparse/src/search.rs —— 搜索工具
/// 在 TextItem 列表中搜索关键词,
/// 返回匹配项(合并相邻命中的边界框)
pub fn search_items(items: &[TextItem],keyword: &str,case_sensitive: bool,
) -> Vec<SearchedItem> {// Step 1: 收集包含关键词的 TextItemlet mut matches: Vec<&TextItem> = Vec::new();for item in items {let hay = if case_sensitive { item.text.clone() }else { item.text.to_lowercase() };let needle = if case_sensitive { keyword.to_string() }else { keyword.to_lowercase() };if hay.contains(&needle) {matches.push(item);}}// Step 2: 按 y 坐标聚类(同一行的匹配合并边界框)// merge by row → 合并 x, y, width, height// 返回合并后带完整包围矩形的 SearchedItem...
}
用途:给 UI 提供"搜索高亮"功能——用户搜索一个词,返回匹配项的合并边界框,用于在页面截图上绘制高亮框。
4.2.12 packages/python/liteparse/parser.py —— Python 薄封装
from ._liteparse import ffi # PyO3 生成的扩展模块class LiteParse:"""Python 侧的薄封装,所有实现在 Rust 层"""def __init__(self,ocr_enabled=True,ocr_language="eng",ocr_server_url=None,tessdata_path=None,max_pages=1000,target_pages=None,dpi=150,output_format="text",preserve_very_small_text=False,password=None,quiet=False,num_workers=None,):# 转发到 Rust 侧的构造函数self._parser = ffi.LiteParse(ocr_enabled=ocr_enabled,ocr_language=ocr_language,ocr_server_url=ocr_server_url,tessdata_path=tessdata_path,max_pages=max_pages,target_pages=target_pages,dpi=dpi,output_format=output_format,preserve_very_small_text=preserve_very_small_text,password=password,quiet=quiet,num_workers=num_workers,)def parse(self, path_or_bytes):if isinstance(path_or_bytes, (bytes, bytearray)):return self._parser.parse_bytes(bytes(path_or_bytes))return self._parser.parse(str(path_or_bytes))def parse_bytes(self, data):return self._parser.parse_bytes(bytes(data))def screenshot(self, path, page_numbers=None):return self._parser.screenshot(str(path), page_numbers or [])
设计洞察:Python 层几乎没有逻辑,全量转发给 Rust 扩展。这保证了:
- 性能一致性(Python 不做任何重操作)
- 行为一致性(四个语言绑定的行为完全相同)
- 维护成本低(修改逻辑只需改 Rust 核心)
4.2.13 packages/python/liteparse/cli.py —— lit 命令行
# 核心命令
lit parse <FILE> # 解析,输出纯文本
lit parse <FILE> --format json # JSON 输出
lit parse <FILE> --target-pages "1-10" # 指定页
lit parse <FILE> --no-ocr # 关闭 OCR
lit parse <FILE> --ocr-server-url URL # 自定义 OCR 服务
lit screenshot <FILE> -o ./out # 生成页面 PNG 截图
lit batch-parse <INPUT_DIR> <OUTPUT_DIR> # 批量处理
lit search <FILE> "keyword" # 搜索 + 返回 bbox
curl URL | lit parse - # 从 stdin 解析
4.3 核心依赖栈(Rust 核心)
| 依赖 | 版本(估计) | 用途 |
|---|---|---|
pdfium |
动态 | PDFium 高层封装(项目内 crate) |
pdfium-sys |
动态 | PDFium FFI 绑定(项目内 crate) |
image |
0.25+ | PNG 读写 / 图像操作 |
tempfile |
3+ | 格式转换临时文件管理 |
reqwest |
0.12 | HTTP OCR 客户端(rustls + multipart + json) |
serde / serde_json |
1.0 | 类型与 JSON 序列化 |
tokio |
1.0+ | 异步任务(OCR 并发、子进程管理) |
thiserror |
1.0 | LiteParseError 错误类型派生 |
web-time |
1.0 | WASM 兼容的 Instant |
ordered-float |
4.0 | 稳定浮点排序(用于 y 聚类) |
infer |
0.15 | 文件类型魔数嗅探 |
url |
2.5 | OCR URL 解析 |
clap |
4.0 | CLI 参数解析(derive feature) |
tesseract-rs |
0.15 | Tesseract 绑定(feature = "tesseract") |
pyo3 |
0.21 | Python 绑定(仅 liteparse-python crate) |
napi |
2.16 | Node.js 绑定(仅 liteparse-napi crate) |
wasm-bindgen |
0.2 | WASM 绑定(仅 liteparse-wasm crate) |
Python 包运行时依赖:无(纯 Rust wheels,零 Python 代码运行时依赖)
5. 应用场景、优点与不足
5.1 典型应用场景
场景一:本地 RAG(Retrieval-Augmented Generation)
用户需求:构建一个不依赖任何云服务的本地知识库问答系统,需要解析 10,000+ 篇 PDF 学术论文。为什么选 LiteParse:✅ 完全本地,无需上传文档到云端✅ 速度快(10,000 篇文档的解析时间从小时级降到分钟级)✅ 空间信息支持基于区域的智能切分✅ OCR 能力处理扫描版论文✅ 零 Python 依赖,部署简单集成方式:from liteparse import LiteParsefrom llama_index.core import Documentparser = LiteParse(ocr_language="eng+chi_sim")result = parser.parse("paper.pdf")# 方式 A:纯文本 → 简单切分doc = Document(text=result.text,metadata={"source": "paper.pdf"})# 方式 B:基于边界框做区域切分(更智能)docs = []for page in result.pages:# 按 y 坐标聚类为段落paragraphs = cluster_by_y_coordinate(page.text_items)for para in paragraphs:docs.append(Document(text=para.text,metadata={"source": "paper.pdf","page": page.page_num,"bbox": [para.x, para.y, para.w, para.h]}))# 后续走常规 LlamaIndex 索引/检索流程
场景二:多模态文档 QA
用户需求:让 LLM "看" 文档的版式和图表来回答问题。LiteParse 的作用:parser = LiteParse(ocr_enabled=True, dpi=300)screenshots = parser.screenshot("report.pdf", page_numbers=[5, 6, 7])parsed_pages = parser.parse("report.pdf").pages# 同时发送给多模态 LLM:# ① 页面截图(PNG bytes)# ② OCR/原生文本# ③ 每个文本项的精确坐标## LLM 可以:# • "看图"理解图表和布局# • "读坐标"知道文字在页面的位置# • 回答"图 3 中显示的数据在哪个章节讨论?"这类问题prompt = """下图是报告第 5 页的截图,以及页面中文字的坐标:{text_items_with_coordinates}请描述图 3 的数据趋势,并找出正文中讨论此图的段落。"""
场景三:隐私/合规场景的文档处理
用户需求(金融/医疗/法律团队):文档包含敏感数据,绝对不能上传到任何云端。同时需要高质量的文本提取和搜索。LiteParse 的优势:✅ 零网络请求(除非你配置了 HTTP OCR,但那也是你自己控制的服务)✅ 没有 API Key / 帐号 / 用量限制✅ 处理过程完全在你的服务器/笔记本内存中✅ 临时文件通过 tempfile::TempPath 自动清理部署方式(企业内网):Docker:docker run -d --name liteparse-service \-p 8000:8000 \-v ./data:/data \ghcr.io/run-llama/liteparse:full然后在内部 HTTP API 中调用:POST /parse form-data: file=@report.pdfGET /screenshot?file=report.pdf&pages=1,2,3
场景四:Agent 工具流中的文档解析
AI Agent 需要读取一份 PDF 来回答问题。LiteParse Agent Skill 集成:npx skills add run-llama/llamaparse-agent-skills --skill liteparseAgent 内部流程:1. 用户:"帮我总结这篇 50 页的技术文档"2. Agent 调用:lit parse document.pdf --format json3. 获取 ParsedPage 列表 + TextItem 坐标4. 基于 bbox + 字体大小识别"标题/段落/表格区域"5. 分章节交给 LLM 总结6. 合并章节总结 → 输出给用户
场景五:批量文档预处理(ETL)
lit batch-parse ./incoming_docs ./parsed_texts# 递归处理 incoming_docs 下的所有文档# 结果写入 parsed_texts,保留目录结构# 每个 .pdf → 生成同名 .txt + .json# 每个 .docx/.xlsx → 先转 PDF 再解析适合数据管道:S3/GCS → 本地临时目录 → LiteParse → 向量数据库(或增量处理 + 缓存已解析结果)
5.2 项目的核心优势
5.2.1 速度与性能
- Rust 核心 + PDFium C 引擎:在复杂 PDF 上比纯 Python 方案快 10–100×
- 零 GC 暂停:Rust 无垃圾回收,大文档处理时延迟稳定
- 并发 OCR:
num_workers控制并发,扫描文档处理速度随 CPU 核心线性扩展 - 流式内存管理:按页处理,不需要把整本 PDF 载入内存
- Python 侧零依赖:没有 numpy/pandas/regex 等隐式依赖
5.2.2 空间信息与结构化输出
- 每个 TextItem 带
(x, y, width, height, font_name, font_size, confidence) - 支持边界框驱动的 RAG 块切分
- 保留字体信息可以识别标题(大字/粗体)vs 正文
- 与多模态 LLM 的图像输入天然互补
5.2.3 极简 API 和开箱即用
- 两个主方法:
parse()/screenshot() - 一个 Config:10 个合理默认值
pip install liteparse即可,不需要编译- 预装 PDFium 动态库,不需要系统级安装 PDFium
5.2.4 多语言生态友好
- 四种语言原生绑定:Rust / Python / Node.js / WASM
- 核心逻辑共享,四端行为完全一致
- WASM 支持使 LiteParse 可以直接在浏览器中解析 PDF(上传文件,不经过任何服务器)
5.2.5 OCR 三层架构的灵活性
- 默认:Tesseract(开箱即用)
- 进阶:HTTP OCR 服务(可部署 GPU 后端)
- 专业:自定义
OcrEnginetrait 注入(完全控制) - 提供 EasyOCR/PaddleOCR 的完整示例服务(
ocr/目录)
5.2.6 网格投影算法
- 解决了多列布局文档的阅读顺序问题
- 是区别于 pypdf/PyMuPDF 等传统库的核心差异化能力
- 对学术论文、双栏书籍等场景的 RAG 质量提升显著
5.2.7 开源与许可友好
- Apache-2.0 许可证:商用友好,无传染性
- 由 LlamaIndex 团队维护(不是个人项目,有持续维护保障)
- 代码仓库结构清晰,容易 fork 修改
5.2.8 LlamaIndex 生态整合
- 与 LlamaIndex 的 Document/Node 模型天然契合
- 官方 Agent Skills 支持(
npx skills add run-llama/llamaparse-agent-skills) - 可以作为 SimpleDirectoryReader 的自定义 FileExtractor 替换 PDF 解析逻辑
5.3 项目的不足与改进空间
5.3.1 表格识别能力有限(主要局限)
| 问题 | 说明 |
|---|---|
| 不做语义表格识别 | LiteParse 只提取文字和坐标,不区分"这是一张表格" vs "这是正文" |
| 表格效果依赖下游 | 需要调用方基于 bbox 和对齐启发式自行重建表格(或配合云端 LlamaParse) |
| 对比 LlamaParse | LlamaParse 有专门的表格识别模型,可以输出 Markdown 表格;LiteParse 无此能力 |
推荐做法:如果你的用例大量涉及表格解析:
- 先用 LiteParse 做快速全量处理
- 对检测到含表格的页面(用 bbox 启发式)再调用 LlamaParse 做二次解析
5.3.2 图像/图表内容无法理解
| 问题 | 说明 |
|---|---|
| 不做图像理解 | LiteParse 提取的是文字,不识别图片内容(流程图、照片、示意图等) |
| 依赖多模态 LLM | 需要配合多模态 LLM + screenshot() 才能"看懂"图片内容 |
| 无 OSD/文本方向检测 | 旋转了 90 度的扫描页面可能识别失败,需要调用方预处理 |
5.3.3 Office 文档依赖外部工具
| 问题 | 说明 |
|---|---|
| LibreOffice 依赖 | 解析 .docx/.xlsx/.pptx 需要系统安装 LibreOffice |
| ImageMagick 依赖 | 解析图像文件需要 ImageMagick |
| 子进程调用开销 | 每次非 PDF 文档都要启动一个 LibreOffice 进程,启动时间数秒 |
| Docker 镜像体积 | :full 标签含 LibreOffice,体积大(几百 MB) |
| Windows 上的路径问题 | 某些 Windows 环境下 LibreOffice 的安装路径检测可能出问题 |
改进方向:社区正在讨论在 Rust 中实现轻量级 Office 文本提取(特别是 .docx,它本质上是 ZIP + XML),以减少对 LibreOffice 的依赖。
5.3.4 OCR 质量与速度的权衡
| 问题 | 说明 |
|---|---|
| Tesseract 对中文不完美 | 中文手写体、艺术字、低分辨率扫描的识别率仍不高 |
| OCR 是性能瓶颈 | 启用 OCR 后,解析速度从"毫秒/页"降级到"秒/页" |
| WASM 端默认无 OCR | 浏览器端使用 WASM 时,需要额外处理 OCR(通过 JS 回调注入) |
推荐做法:
- 有 GPU 资源:部署 HTTP OCR 服务(如 PaddleOCR + GPU),使用
ocr_server_url - 纯 CPU + 中文:设置
ocr_language="chi_sim",安装对应的 tessdata - 文档质量好(原生 PDF,不是扫描件):关闭 OCR 提升速度
5.3.5 错误信息的可诊断性
| 问题 | 说明 |
|---|---|
| PDFium 错误透传 | PDFium 的错误码是 C 风格的整数,Rust 侧将其转换为字符串,但信息可能不够详细 |
| LibreOffice 失败静默 | 格式转换失败时错误信息可能只有"LibreOffice 失败",难以调试 |
| 缺少调试模式 | 没有 verbose 或 log_level 参数来查看详细处理日志 |
改进方向:增加 verbose 配置 + 更细粒度的错误分类 + 日志系统(如 tracing crate)。
5.3.6 WASM 端功能不完整
| 问题 | 说明 |
|---|---|
| OCR 限制 | WASM 端默认无 OCR(浏览器内做 OCR 需要额外的 JS 库如 tesseract.js) |
| 文件大小 | WASM 包 + PDFium WASM 构建体积较大(数 MB),在移动端加载慢 |
| 浏览器兼容性 | 需要较新的浏览器(支持 WebAssembly、SIMD 可选) |
5.3.7 缺乏语义层次重建
LiteParse 提供"文字 + 坐标",但不做更高层次的语义推断:
- 不区分"标题" vs "正文"(只能根据字体大小启发式判断)
- 不识别"列表" vs "段落"
- 不检测"表格" vs "图片" vs "标题"
这意味着:
- 如果你要做"按章节切分 RAG 块",需要自己实现启发式
- 如果你要做"只提取文档中的表格",需要自己实现检测逻辑
5.3.8 自定义字体/编码的边缘案例
某些 PDF 使用:
- 自定义字体编码(ToUnicode CMap 缺失或损坏)
- 私有字体嵌入但编码不标准
- 艺术字/曲线字(如 logo 中的文字)
在这些边缘案例上,PDFium 可能无法提取文字,只能 fallback 到 OCR。OCR 本身对非标准字体也不完美。
5.3.9 加密 PDF 支持有限
- 支持密码 PDF(
password参数) - 但不支持:
- DRM 保护的 PDF(如 Adobe APS)
- 证书加密 PDF(需要用户证书 + 私钥)
- 权限受限 PDF(某些操作被禁止)
5.3.10 Python 绑定的异步支持
- 目前 Python 侧
parse()是同步阻塞的(Rust 内部不涉及 Python GIL,但调用仍在当前线程运行) - 对于高并发异步 Python 服务(如 FastAPI),需要手动包装到线程池
- Node.js 侧有原生 async 支持
改进方向:在 Python 绑定层暴露 parse_async() 方法(内部使用 pyo3 的 #[pyo3(async_fn)])。
5.3.11 版本兼容性与测试覆盖
- 项目相对年轻(v2.0.7),API 仍可能微调
- 主要测试集中在:标准 PDF、标准 Office 文档、英文扫描件
- 对以下场景的测试覆盖率可能不足:
- 极端损坏的 PDF
- 非拉丁语系复杂字体(阿拉伯语从右到左、印地语连体字)
- 超大文件(>1000 页)的内存行为
- Windows 中文路径
5.4 与其他方案的决策建议
| 你的场景 | 推荐方案 | 说明 |
|---|---|---|
| 隐私敏感 / 不能上传 | LiteParse | 本地、离线、零云依赖 |
| 需要表格识别 | LlamaParse(云端) | LiteParse + LlamaParse 混合 |
| 只需要纯文本、简单 | pypdf | 轻量,纯 Python |
| 浏览器内解析 PDF | LiteParse WASM | 唯一能在浏览器内做 bbox 解析的方案 |
| Rust 项目 + 需要 PDF | LiteParse | 原生 Rust API,无外部依赖 |
| 大批量 ETL 处理 | LiteParse + 并发调用 | 速度优势明显 |
| 复杂格式(CAD/签名等) | Adobe 官方工具 / LlamaParse | 开源方案在复杂场景有短板 |
| 多模态文档 QA | LiteParse(截图 + 文本 + 坐标) | 配合 GPT-4o / Claude 3 Opus |
总结
LiteParse 是一个定位清晰、实现精良的开源文档解析工具。它不是要做"世界上最好的 PDF 解析器",而是要做"本地、快速、带空间信息的 PDF 解析器"——在这个定位上,它目前是开源生态中的最佳选择。
它的核心价值在于:
- Rust + PDFium 核心,速度是传统 Python 方案的 10–100 倍
- 网格投影算法,解决了多列布局文档的阅读顺序问题
- 三层 OCR 架构,兼顾开箱即用、生产部署、完全自定义
- 四语言绑定(Rust/Python/Node/WASM),零 Python 运行时依赖
- 极简 API,
LiteParse.parse()一个方法解决 99% 的场景
它的主要不足在于:表格识别能力弱(需配合云端 LlamaParse)、Office 文档依赖外部工具、语义层次需要调用方自行重建。但在其定位范围内("本地文本+空间信息提取"),它是目前最均衡的方案。
推荐使用场合:
- ✅ 本地 RAG 系统的文档预处理
- ✅ 隐私/合规场景的文档解析
- ✅ 多模态 LLM 的文档输入(截图 + 坐标)
- ✅ Rust / Python 项目中的 PDF 解析需求
- ✅ 浏览器端需要解析 PDF(WASM 绑定)
不推荐使用场合:
- ❌ 主要需求是表格识别——请用 LlamaParse 或专门的表格解析工具
- ❌ 完全离线 + 需要解析大量 Office 文档——LibreOffice 依赖是硬约束
- ❌ 需要理解图片/图表内容的语义——需要配合多模态 LLM
报告生成时间:2026-06-09
分析基于:GitHub 仓库 run-llama/liteparse (v2.0.7)
参考资料:README、OCR_API_SPEC.md、项目源码、LlamaIndex 文档
