惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threat Research - Cisco Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
V
Vulnerabilities – Threatpost
GbyAI
GbyAI
P
Proofpoint News Feed
L
LINUX DO - 热门话题
P
Palo Alto Networks Blog
A
About on SuperTechFans
T
Tenable Blog
M
MIT News - Artificial intelligence
IT之家
IT之家
I
Intezer
D
DataBreaches.Net
爱范儿
爱范儿
T
Threatpost
C
CERT Recently Published Vulnerability Notes
云风的 BLOG
云风的 BLOG
博客园 - 三生石上(FineUI控件)
WordPress大学
WordPress大学
K
Kaspersky official blog
大猫的无限游戏
大猫的无限游戏
A
Arctic Wolf
Y
Y Combinator Blog
Cyberwarzone
Cyberwarzone
酷 壳 – CoolShell
酷 壳 – CoolShell
D
Darknet – Hacking Tools, Hacker News & Cyber Security
H
Help Net Security
Microsoft Security Blog
Microsoft Security Blog
Spread Privacy
Spread Privacy
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
AWS News Blog
AWS News Blog
博客园 - 聂微东
C
Check Point Blog
S
Securelist
有赞技术团队
有赞技术团队
雷峰网
雷峰网
aimingoo的专栏
aimingoo的专栏
Last Week in AI
Last Week in AI
Stack Overflow Blog
Stack Overflow Blog
MongoDB | Blog
MongoDB | Blog
D
Docker
G
GRAHAM CLULEY
T
The Exploit Database - CXSecurity.com
C
Cybersecurity and Infrastructure Security Agency CISA
T
Tailwind CSS Blog
L
Lohrmann on Cybersecurity
G
Google Developers Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
L
LangChain Blog

博客园 - zhang-yd

今日开源[第16期]soxoj/maigret 论文解读-《Dual-Kernel Graph Community Contrastive Learning》 今日开源[第15期]agent-skills 论文解读-《Hyperbolic Continuous Structural Entropy for Hierarchical Clustering》 今日开源[第14期]google/skills 今日开源[第13期]turbovec 今日开源[第11期]OmniVoice-Studio 今日开源[第10期]ds4(DwarfStar) 今日开源[第9期]graphify 今日开源[第8期]open-notebook 今日开源[第7期]spec-kit 今日开源[第6期]Production Agentic RAG Course 今日开源[第5期]Headroom 今日开源[第4期]OpenTalking 今日开源[第3期]train-llm-from-scratch 今日开源[第2期]Project N.O.M.A.D. 今日开源[第1期]MoneyPrinterTurbo LearningCell代码解读 论文解读-《It Takes a Graph to Know a Graph Rewiring for Homophily with a Reference Graph》 论文解读-《Mitigating Over-Squashing in Graph Neural Networks by Spectrum-Preserving Sparsification》 论文解读-《Make Heterophily Graphs Better Fit GNN A Graph Rewiring Approach》 论文解读-《Temporal Graph Rewiring with Expander Graphs 》 论文解读-《Understanding Oversquashing in GNNs through the Lens of Effective Resistance》 论文解读-《Homophily-oriented Heterogeneous Graph Rewiring》 论文-Deep appearance modeling: A survey 代码阅读笔记-nanoclaw 代码阅读笔记-OpenManus 论文解读-《An Empirical Evaluation of Rewiring Approaches in Graph Neural Networks》 论文解读-《Probabilistic Graph Rewiring via Virtual Nodes》 论文解读-《Probabilistically Rewired Message-Passing Neural Networks》 论文解读-《Joint Graph Rewiring and Feature Denoising via Spectral Resonance》 代码阅读笔记-nanobot 论文解读-《Oversquashing in GNNs through the lens of information contraction and graph expansion》 论文解读-《GNNs Getting ComFy Community and Feature Similarity Guided Rewiring》 - zhang-yd 论文解读-《PANDA Expanded Width-Aware Message Passing Beyond Rewiring》 代码阅读笔记-AiPyApp 论文解读-《Deep Graph Contrastive Representation Learning》 论文解读-《Community-Invariant Graph Contrastive Learning》 论文解读-《DiffWire Inductive Graph Rewiring via the Lovász Bound》 论文解读-《The Effectiveness of Curvature-Based Rewiring and the Role of Hyperparameters in GNNs Revisited》 论文解读-《Over-Squashing in GNNs and Causal Inference of Rewiring Strategies》 论文解读-《Uncertainty-Aware Graph Structure Learning》
今日开源[第12期]LiteParse
zhang-yd · 2026-06-09 · via 博客园 - zhang-yd

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 的核心速度来自两个设计决策:

  1. Rust 零成本抽象 + 无 GC:文本提取循环、投影算法、并发 OCR 都在 Rust 层完成,无 Python GIL,无解释器开销。
  2. 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: int
    page_width: float
    page_height: float
    text: str                       # 本页纯文本(保留布局)
    text_items: list[TextItem]      # 细粒度文本项

class TextItem:
    text: str
    x, y, width, height: float     # PDF 72 DPI 坐标(左上原点)
    rotation: float
    font_name: str
    font_size: float
    font_height: float
    font_weight: int
    fill_color: str
    confidence: 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::PathPdfInput::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` → 临时 PDF

Phase 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 中的每个 ParsedPage

Phase 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 }

核心流程:

/// 从 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 为一个 TextItem
                let item = merge_chars_to_item(&current_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-sys crate 做 FFI 绑定,pdfium crate 做高层封装
  • 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.x
    snap_center = item.x + item.width / 2
    snap_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 trait
  2. 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 convert
            let 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 headless
            let 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 中找生成的 pdf
            let 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: 收集包含关键词的 TextItem
    let 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 扩展。这保证了:

  1. 性能一致性(Python 不做任何重操作)
  2. 行为一致性(四个语言绑定的行为完全相同)
  3. 维护成本低(修改逻辑只需改 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 LiteParse
  from llama_index.core import Document

  parser = 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.pdf
    GET  /screenshot?file=report.pdf&pages=1,2,3

场景四:Agent 工具流中的文档解析

AI Agent 需要读取一份 PDF 来回答问题。

LiteParse Agent Skill 集成:
  npx skills add run-llama/llamaparse-agent-skills --skill liteparse

  Agent 内部流程:
    1. 用户:"帮我总结这篇 50 页的技术文档"
    2. Agent 调用:lit parse document.pdf --format json
    3. 获取 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 无垃圾回收,大文档处理时延迟稳定
  • 并发 OCRnum_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 后端)
  • 专业:自定义 OcrEngine trait 注入(完全控制)
  • 提供 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 失败",难以调试
缺少调试模式 没有 verboselog_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 解析器"——在这个定位上,它目前是开源生态中的最佳选择。

它的核心价值在于:

  1. Rust + PDFium 核心,速度是传统 Python 方案的 10–100 倍
  2. 网格投影算法,解决了多列布局文档的阅读顺序问题
  3. 三层 OCR 架构,兼顾开箱即用、生产部署、完全自定义
  4. 四语言绑定(Rust/Python/Node/WASM),零 Python 运行时依赖
  5. 极简 APILiteParse.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 文档