# PDF处理模块重构说明
## 📦 模块结构
PDF处理功能已重构为4个模块,便于维护和理解:
```
ocr_utils/
├── pdf_utils.py # 主入口 - 高级API和向后兼容包装
├── pdf_coordinate_transform.py # 坐标转换功能
├── pdf_text_extraction.py # 文本提取功能
├── pdf_image_rendering.py # 图像渲染功能
└── pdf_README.md # 本文档
```
## 🔧 各模块功能
### 1. pdf_utils.py (主入口,~400行)
**作用**: 高级API和统一入口,保持向后兼容性
**主要类**: `PDFUtils`
**核心功能**:
- `load_and_classify_document()`: 加载文档(PDF/图片)并分类
- `merge_cross_page_tables()`: 跨页表格合并(TODO)
- 所有子模块函数的重新导出(向后兼容)
**使用示例**:
```python
from ocr_utils.pdf_utils import PDFUtils
# 加载PDF并分类
images, pdf_type, pdf_doc, renderer = PDFUtils.load_and_classify_document(
document_path=Path("test.pdf"),
dpi=200,
page_range="1-5",
renderer="fitz"
)
```
### 2. pdf_coordinate_transform.py (~315行)
**作用**: PDF坐标系与图像坐标系的转换
**核心函数**:
- `pdf_rotation_to_image_rotation()`: **PDF旋转角度转换为图片旋转角度**
- `transform_bbox_for_rotation_fitz()`: fitz引擎的坐标变换(支持正视/旋转后坐标输出)
- `transform_bbox_for_rotation_pypdfium2()`: pypdfium2引擎的坐标变换(支持正视/旋转后坐标输出)
**⭐ 统一输出逻辑(重要)**:
两个引擎通过参数统一对外输出行为:
- **正视坐标**(`return_upright_coords=True`,默认,推荐):坐标在正视方向,不受PDF rotation影响
- **旋转后坐标**(`return_upright_coords=False`):坐标在旋转后坐标系,匹配PDF rotation
**坐标系说明**:
- **PDF坐标系**: 左下角原点 (0,0),X向右,Y向上
- **图像坐标系**: 左上角原点 (0,0),X向右,Y向下
- **正视坐标**: 视觉上保持upright(rotation=0)的坐标系
- **旋转后坐标**: 匹配PDF rotation属性的坐标系
**旋转定义(重要)**:
- **PDF rotation**: 0/90/180/270度(**顺时针旋转**,PDF规范)
- **图片rotation**: 0/90/180/270度(**逆时针旋转**,图像处理标准)
- **对外API统一返回图片rotation**(逆时针定义)
**旋转转换映射**:
| PDF Rotation (顺时针) | 图片 Rotation (逆时针) |
|---------------------|---------------------|
| 0° | 0° |
| 90° | 270° |
| 180° | 180° |
| 270° | 90° |
**引擎原始行为差异**:
| 特性 | fitz | pypdfium2 |
|------|------|-----------||
| 原生返回坐标 | **正视坐标**(总是upright) | **旋转后坐标**(匹配PDF rotation) |
| 坐标系类型 | 与rotation无关 | 与rotation相关 |
| 转换需求 | 需转换为旋转后(如需要) | 需转换为正视(如需要) |
**统一后的输出**:
| 参数 | fitz输出 | pypdfium2输出 | 说明 |
|------|---------|--------------|------|
| `to_rotated=False`
`to_upright=True` | 正视坐标 | 正视坐标 | **默认,推荐** |
| `to_rotated=True`
`to_upright=False` | 旋转后坐标 | 旋转后坐标 | 匹配渲染图像 |
**使用示例**:
```python
from ocr_utils.pdf_coordinate_transform import (
transform_bbox_for_rotation_fitz,
transform_bbox_for_rotation_pypdfium2
)
# fitz引擎: 返回正视坐标(默认,推荐)
upright_bbox = transform_bbox_for_rotation_fitz(
bbox=[100, 50, 200, 100],
rotation=90,
pdf_width=595,
pdf_height=842,
scale=2.778,
to_rotated=False # 默认值,返回正视坐标
)
# fitz引擎: 返回旋转后坐标
rotated_bbox = transform_bbox_for_rotation_fitz(
bbox=[100, 50, 200, 100],
rotation=90,
pdf_width=595,
pdf_height=842,
scale=2.778,
to_rotated=True # 返回旋转后坐标
)
# pypdfium2引擎: 返回正视坐标(默认,推荐)
upright_bbox = transform_bbox_for_rotation_pypdfium2(
bbox=[100, 50, 200, 100],
rotation=90,
pdf_width=595,
pdf_height=842,
scale=2.778,
to_upright=True # 默认值,返回正视坐标
)
```
### 3. pdf_text_extraction.py (~450行)
**作用**: 从PDF提取文本,支持rotation处理和坐标系选择
**核心函数**:
- `extract_text_from_pdf()`: 从指定区域提取文本(自动检测引擎)
- `extract_all_text_blocks()`: 提取页面所有文本块(自动检测引擎,支持坐标系选择)
- `detect_pdf_doc_type()`: 检测PDF文档类型(fitz/pypdfium2)
- `bbox_overlap()`: 检查bbox重叠
**⭐ 坐标系选择参数**:
- `return_upright_coords=True`(默认):返回正视坐标,所有rotation下坐标一致
- `return_upright_coords=False`:返回旋转后坐标,匹配渲染图像
- `with_rotation`(已废弃):保留向后兼容,使用时会警告
**支持引擎**:
- **pypdfium2**: MinerU标准引擎
- **fitz (PyMuPDF)**: 轻量级替代引擎
**使用示例**:
```python
from ocr_utils.pdf_text_extraction import extract_all_text_blocks
# 方式1: 提取正视坐标(默认,推荐)
text_blocks, rotation = extract_all_text_blocks(
pdf_doc=pdf_doc,
page_idx=0,
scale=2.778,
return_upright_coords=True # 默认值
)
# 方式2: 提取旋转后坐标(匹配渲染图像)
text_blocks, rotation = extract_all_text_blocks(
pdf_doc=pdf_doc,
page_idx=0,
scale=2.778,
return_upright_coords=False
)
# 返回格式:
# text_blocks = [
# {
# 'text': 'Hello',
# 'bbox': [x1, y1, x2, y2], # 转换后的坐标(正视或旋转后)
# 'origin_bbox': [x1, y1, x2, y2] # 原始坐标
# },
# ...
# ]
# rotation = 270 # 图片旋转角度:0/90/180/270(逆时针)
```
**⚠️ 坐标系说明**:
- `return_upright_coords=True`(推荐):bbox在正视坐标系,不同rotation下相同文本坐标一致
- `return_upright_coords=False`:bbox在旋转后坐标系,与渲染图像对齐,可直接绘制
- `rotation` 返回值采用**图片旋转定义**(逆时针),不是PDF rotation(顺时针)
- PDF rotation 90° → 返回 270°(图片需要逆时针旋转270°)
- PDF rotation 270° → 返回 90°(图片需要逆时针旋转90°)
### 4. pdf_image_rendering.py (~300行)
**作用**: PDF页面渲染为图像
**核心函数**:
- `load_images_from_pdf_unified()`: 统一的PDF图像加载接口
- `load_images_pypdfium2()`: 使用pypdfium2渲染
- `load_images_fitz()`: 使用fitz渲染
**渲染引擎对比**:
| 特性 | pypdfium2 | fitz |
|------|-----------|------|
| 渲染引擎 | Chrome PDFium | MuPDF |
| 多进程加速 | ✅ (非Windows) | ❌ |
| 超时控制 | ✅ | ❌ |
| 尺寸限制 | 3500px | 4500px |
| 超限处理 | 动态调整scale | 降到72 DPI |
| MinerU标准 | ✅ | ❌ |
**使用示例**:
```python
from ocr_utils.pdf_image_rendering import load_images_from_pdf_unified
# 使用pypdfium2(推荐)
images, doc = load_images_from_pdf_unified(
pdf_bytes=pdf_bytes,
dpi=200,
renderer="pypdfium2",
threads=4
)
# 使用fitz
images, doc = load_images_from_pdf_unified(
pdf_bytes=pdf_bytes,
dpi=200,
renderer="fitz"
)
# 返回格式:
# images = [
# {'img_pil': PIL.Image, 'scale': 2.778},
# ...
# ]
```
## 🔄 向后兼容性
**所有原有代码无需修改!**
`PDFUtils`类保留了所有原有方法作为包装函数,内部调用新模块的功能:
```python
# 旧代码继续工作
from ocr_utils.pdf_utils import PDFUtils
# 所有这些方法仍然可用:
PDFUtils.extract_text_from_pdf(...)
PDFUtils.extract_all_text_blocks(...)
PDFUtils.load_images_from_pdf_unified(...)
PDFUtils._transform_bbox_for_rotation_fitz(...)
# ... 等等
```
## 📝 最佳实践
### 1. **新代码**: 直接导入子模块
```python
# 推荐: 直接从子模块导入
from ocr_utils.pdf_text_extraction import extract_all_text_blocks
from ocr_utils.pdf_coordinate_transform import transform_bbox_for_rotation_fitz
text_blocks, rotation = extract_all_text_blocks(pdf_doc, 0, 2.778)
```
### 2. **旧代码**: 继续使用PDFUtils
```python
# 兼容: 通过PDFUtils类使用
from ocr_utils.pdf_utils import PDFUtils
text_blocks, rotation = PDFUtils.extract_all_text_blocks(pdf_doc, 0, 2.778)
```
### 3. **渲染引擎选择**
```python
# 生产环境推荐: pypdfium2 (MinerU标准)
renderer = "pypdfium2" # 多进程加速,更好的细节保留
# 开发/测试: fitz (简单轻量)
renderer = "fitz" # 无需额外依赖,单进程
```
## 🎯 重构优势
1. **代码组织**: 从单个984行文件 → 4个模块,每个200-450行
2. **职责清晰**: 坐标变换、文本提取、图像渲染各自独立
3. **易于测试**: 各模块可独立测试
4. **向后兼容**: 现有代码无需修改
5. **易于扩展**: 新功能可加入对应模块,不影响其他部分
## ✅ 测试验证
重构后通过完整测试验证:
- ✅ 所有24个测试通过 (4种rotation × 2种引擎 × 2种坐标模式 + 8个对比测试)
- ✅ fitz引擎: rotation 0°/90°/180°/270° 正视坐标正确
- ✅ pypdfium2引擎: rotation 0°/90°/180°/270° 正视坐标正确
- ✅ 两种坐标模式对比测试通过
- ✅ 坐标边界验证通过
- ✅ 向后兼容性验证通过
**修复的关键bug**:
- 🐛 修复pypdfium2在90°/270°转换时使用错误页面尺寸的问题
- 问题:使用原始PDF尺寸,导致负坐标
- 修复:使用旋转后页面尺寸(90°: height×width, 270°: height×width)
测试命令:
```bash
cd ocr_tools/universal_doc_parser/tests
python test_pdf_rotation.py
```
测试覆盖:
- 正视坐标模式 (return_upright_coords=True)
- 旋转后坐标模式 (return_upright_coords=False)
- 两种模式对比验证
- 可视化验证(生成带bbox标注的图像)
## 📚 相关文档
- [MinerU文档](https://github.com/opendatalab/MinerU)
- [PyMuPDF文档](https://pymupdf.readthedocs.io/)
- [pypdfium2文档](https://pypdfium2.readthedocs.io/)
## 🔧 维护指南
### 添加新功能
根据功能类型选择合适的模块:
1. **坐标转换相关** → `pdf_coordinate_transform.py`
2. **文本提取相关** → `pdf_text_extraction.py`
3. **图像渲染相关** → `pdf_image_rendering.py`
4. **高级API/工作流** → `pdf_utils.py`
### 修改现有功能
1. 在对应子模块中修改实现
2. 如果改变了函数签名,需在`pdf_utils.py`中更新包装函数
3. 运行测试验证: `python test_pdf_rotation.py`
---
**重构日期**: 2026-01-05
**API改进日期**: 2026-01-07
**重构原因**: pdf_utils.py文件过大(984行),难以维护
**重构目标**: 按功能层次拆分,提高可维护性,保持向后兼容性
**API改进**: 统一坐标系输出逻辑,使用语义明确的`return_upright_coords`参数替代`with_rotation`