国产系统麒麟OS全功能PDF工具箱,什么编辑、转换、OCR、去水印都欧克了
Windows上的时候还有个acrobat可以转换和编辑PDF使用,到了麒麟上就啥也没有,有的都得要银子,本身每月就碎银几辆而已,实在穷的没办法,就自己搞一个吧。
一开始只搞了转换,分割,合并版本的,实在有失工具箱的名头,而且这种基础功能的软件一抓一大把,最主要的OCR识别和编辑,去水印这种全功能的才是刚需。
概述
PDF 全能工具是一款类似 Adobe Acrobat 的全功能 PDF 处理软件,面向国产操作系统(统信 UOS、银河麒麟等)和 Windows 平台,完全离线运行,无需联网。


核心特性
- **完全离线**:所有功能本地运行,无需上传文件,保障文档安全
- **全功能 PDF 编辑**:内容流级真实编辑,向 Acrobat 看齐
- **智能 OCR**:基于 PaddleOCR,支持中、英、日等 100+ 语言
- **版面分析**:自动识别标题、段落、表格、公式、图表,恢复阅读顺序
- **多格式转换**:支持 DOCX、TXT、HTML、Markdown、Excel、PPT、图片
- **国产系统适配**:支持 fcitx/ibus 输入法、中文字体、aarch64 架构
功能总览
| 类别 | 功能 |
| **编辑** | 文字编辑、添加文字、图片替换、OCR 识别、高亮/矩形标注、水印处理、页面管理、撤销 |
| **转换** | PDF → DOCX / TXT / HTML / Markdown / Excel / PPT / 图片 |
| **合并** | 多 PDF 合并、顺序调整 |
| **分割** | 每页一文件 / 按页数 / 按页面范围 |
| **OCR** | 单进程 / 多进程识别、页面范围选择、结果导出 |
| **版面分析** | 版面检测、表格识别、公式识别、阅读顺序恢复、图表分析 |
| **工具** | 页面旋转、加密、解密、添加水印、去除水印、清除密码 |
整体架构图

模块职责划分
| 模块 | 路径 | 职责 |
| **入口** | `pdftools/main.py` | QApplication 初始化、环境变量、字体、启动主窗口 |
| **配置** | `pdftools/config/settings.py` | 全局配置单例,读写 `~/.pdftools/config.json` |
| **引擎适配** | `pdftools/engines/` | 隔离 PaddleOCR / PP-Structure API 变化 |
| **服务层** | `pdftools/services/` | 业务逻辑(PDF 操作、转换、OCR、版面、编辑器) |
| **GUI** | `pdftools/gui/` | 用户界面、交互逻辑、异步 Worker |
| **模型管理** | `pdftools/models/model_manager.py` | 模型检查、下载、镜像源 |
| **工具** | `pdftools/utils/` | 文件、日志、内存、PDF 工具函数 |
双平台依赖矩阵
项目支持 **Windows(PySide2/Qt5)** 和 **Linux(PySide6/Qt6)** 两套依赖,针对国产系统优化。
#### Windows 依赖(`pdftools/requirements.txt`,Python 3.8 + Win7 兼容)
| 库 | 版本 | 用途 |
|-----|------|------|
| PyMuPDF | 1.23.8 | PDF 渲染、提取、编辑 |
| pypdf | 3.17.4 | PDF 合并/拆分/加密 |
| paddlepaddle | 2.6.2 | OCR 推理运行时 |
| paddleocr | 2.8.1 | OCR 文字识别 |
| pdf2docx | 0.5.8 | PDF 转 DOCX |
| python-docx | 1.1.2 | DOCX 生成 |
| python-pptx | 0.6.23 | PPT 生成 |
| Pillow | 9.5.0 | 图像处理 |
| **PySide2** | **5.15.2.1** | **GUI 框架(Qt5)** |
| markdown | 3.5.2 | Markdown 处理 |
| openpyxl | 3.1.5 | Excel 文件 |
| numpy | 1.22.4 | 数值计算 |
| pandas | 1.5.3 | 数据处理 |
| pypdfium2 | 4.30.0 | PDF 渲染备用 |
| opencv-python-headless | 4.8.1.78 | 图像处理(OCR) |
| shapely | 1.8.5 | 几何计算(表格) |
| lxml | 4.9.4 | XML 处理 |
| pyclipper | 1.3.0.post6 | 多边形裁剪 |
| PyYAML | 6.0.2 | YAML 配置 |
| tqdm | 4.66.5 | 进度条 |
| pyinstaller | 5.13.2 | 打包 |
#### Linux 依赖(`pdftools/requirements_linux.txt`,Python 3.10+,麒麟/UOS)
| 库 | 版本 | 用途 |
|-----|------|------|
| PyMuPDF | >=1.24.0 | PDF 渲染 |
| pypdf | >=5.0.0 | PDF 操作 |
| paddlepaddle | >=2.6.0 | OCR 运行时 |
| paddleocr | >=2.9.0 | OCR 识别 |
| pdf2docx | >=0.5.0 | PDF 转 DOCX |
| python-docx | >=1.1.0 | DOCX 生成 |
| Pillow | >=10.0.0 | 图像处理 |
| **PySide6** | **>=6.5.0** | **GUI 框架(Qt6)** |
| markdown | >=3.5.0 | Markdown 处理 |
| openpyxl | >=3.1.0 | Excel 文件 |
| numpy | >=1.24.0 | 数值计算 |
| pandas | >=2.0.0 | 数据处理 |
| pypdfium2 | >=4.0.0 | PDF 渲染备用 |
主要说一下OCR和编辑部分
OCRService(OCR 识别服务)
**文件**: `pdftools/services/ocr_service.py`
封装 `PaddleOCREngineAdapter`,支持单进程和多进程模式。
#### 核心方法
```python
class OCRService:
def __init__(self, lang: str = 'ch', use_gpu: bool = None)
@property
def engine_adapter(self) -> OCREngineAdapter
def is_available(self) -> bool
def ocr_image(self, image_path: str) -> List[Dict]
def ocr_pdf(
self,
pdf_path: str,
page_numbers: List[int] = None # None = 全部
) -> List[Dict] # [{'page': i, 'result': [...]}]
def ocr_pdf_page_by_page(
self,
pdf_path: str,
page_numbers: List[int] = None,
progress_callback=None, # callback(current, total)
status_callback=None # callback(str)
) -> List[Dict]
def ocr_pdf_multiprocess(
self,
pdf_path: str,
page_numbers: List[int] = None,
progress_callback=None
) -> List[Dict] # 多进程加速版
def extract_text_from_ocr_result(
self,
ocr_result: List
) -> str # 提取纯文本
```
LayoutService(版面分析服务)
**文件**: `pdftools/services/layout_service.py`
基于 `PPStructureEngineAdapter`,支持版面检测、表格识别、公式识别、图表分析、阅读顺序恢复。
#### 核心方法
```python
class LayoutService:
def __init__(self, lang: str = 'ch', use_gpu: bool = None)
@property
def engine_adapter(self) -> StructureEngineAdapter
def is_available(self) -> bool
def detect_layout(
self,
pdf_path: str,
page_numbers: List[int] = None,
progress_callback=None
) -> List[Dict] # [{'page': i, 'regions': [...]}]
def detect_layout_fast(
self,
pdf_path: str,
page_numbers: List[int] = None,
progress_callback=None
) -> List[Dict] # PyMuPDF 内置快速版(精度较低)
def recognize_tables(
self,
pdf_path: str,
page_numbers: List[int] = None
) -> List[Dict] # [{'page': i, 'tables': [...]}]
def export_tables_to_excel(
self,
pdf_path: str,
output_path: str = None,
progress_callback=None
) -> str
def export_tables_to_markdown(
self,
pdf_path: str,
output_path: str = None
) -> str
def recognize_formulas(
self,
pdf_path: str,
page_numbers: List[int] = None
) -> List[Dict] # [{'page': i, 'formulas': [{'content': 'LaTeX', ...}]}]
def restore_reading_order(
self,
pdf_path: str,
page_numbers: List[int] = None
) -> List[Dict] # [{'page': i, 'column_count': N, 'elements': [...]}]
def analyze_charts(
self,
pdf_path: str,
output_dir: str = None, # 图表截图保存目录
page_numbers: List[int] = None
) -> List[Dict] # [{'page': i, 'caption': ..., 'image_path': ...}]
def pdf_to_markdown(
self,
pdf_path: str,
output_path: str = None,
use_ocr: bool = False
) -> str
```
EditorService(PDF 编辑器服务)
**文件**: `pdftools/services/editor_service.py`
**核心服务**,基于 PyMuPDF 实现内容流级真实编辑。
#### 类属性与初始化
```python
class EditorService:
def __init__(self):
self._doc = None # PyMuPDF 文档对象
self._history = [] # 撤销历史
self._max_history = 20 # 最多 20 步
self._ocr_cache: Dict[int, List[Dict]] = {} # OCR 缓存 {page_num: [results]}
self._analyzed_pages: set = set() # 已分析的页面
self._needs_full_save: bool = False # 完整保存标记
@property
def is_open(self) -> bool
@property
def page_count(self) -> int
@property
def pdf_path(self) -> str
@property
def history_count(self) -> int # 可撤销步数
```
页面分析与 OCR
```python
def is_page_analyzed(self, page_num: int) -> bool
def analyze_page(self, page_num: int, progress_callback=None) -> Dict
# 返回:{'ocr_items': N, 'text_blocks': N, 'image_regions': N}
def get_all_text_blocks(self, page_num: int) -> List[Dict]
# 返回所有文字块(PDF原生 + OCR缓存,含水印标记)
def get_editable_text_blocks(self, page_num: int) -> List[Dict]
# 排除水印的文字块
def get_watermark_blocks(self, page_num: int) -> List[Dict]
# 仅水印文字块
def clear_analysis_cache(self, page_num: int = None) -> None
```
#### 文字块结构
```python
{
"bbox": (x0, y0, x1, y1), # PDF 坐标系
"text": str,
"size": float, # 字号
"font": str, # 字体名
"color": int, # sRGB int
"source": "pdf" | "ocr" | "watermark",
"is_watermark": bool,
"watermark_score": int, # 水印评分
}
```
#### 文字编辑
```python
def replace_text(
self,
page_num: int,
bbox: Tuple[float, float, float, float],
new_text: str,
font_size: float = None, # None = 自动估算
font_name: str = None, # 系统显示名或 PyMuPDF 内置名
color: tuple = (0, 0, 0), # RGB 0-1
font_file: str = None # 字体文件路径(优先使用,避免冲突)
) -> bool
# 内容流级真实替换:redaction 删除原文字 → 相同位置写入新文字
def move_text(
self,
page_num: int,
bbox: Tuple[float, float, float, float],
new_x: float,
new_y: float,
text: str,
font_size: float = None,
font_name: str = None,
color: tuple = (0, 0, 0),
font_file: str = None,
source: str = "pdf" # "pdf" 或 "ocr"
) -> bool
# 真实移动:原位置删除 → 新坐标插入,保留格式属性
def add_text(
self,
page_num: int,
point: Tuple[float, float], # (x, y)
text: str,
font_size: float = 14,
font_name: str = None,
color: tuple = (0, 0, 0),
font_file: str = None
) -> bool
```
#### 图片操作
```python
def get_images(self, page_num: int) -> List[Dict]
# [{'bbox': ..., 'width': ..., 'height': ..., 'xref': ...}]
def replace_image(self, page_num: int, img_index: int, new_image_path: str) -> bool
# 删除原图 XRef → 同位置插入新图
def add_image(
self,
page_num: int,
bbox: Tuple[float, float, float, float],
image_path: str
) -> bool
```
#### OCR 识别
```python
def ocr_region(
self,
page_num: int,
bbox: Tuple[float, float, float, float]
) -> List[Dict]
# 区域 OCR,坐标转换回 PDF 坐标系
def ocr_page(self, page_num: int) -> List[Dict]
# 整页 OCR
def ocr_to_text_layer(self, page_num: int) -> int
# OCR 并写入可编辑文字层
# 自动选择中文字体(china-s)或西文字体(helv)
```
#### 标注
```python
def add_highlight(
self,
page_num: int,
bbox: Tuple[float, float, float, float],
color: tuple = (1, 0.9, 0) # 默认黄色
) -> bool
def add_rect_annotation(
self,
page_num: int,
bbox: Tuple[float, float, float, float],
color: tuple = (1, 0, 0), # 默认红色
width: float = 2,
fill: tuple = None # 填充色
) -> bool
def add_underline(
self,
page_num: int,
bbox: Tuple[float, float, float, float],
color: tuple = (0, 0, 1) # 默认蓝色
) -> bool
```
水印处理
水印检测与处理
```python
def detect_watermarks(self, page_num: int = None) -> List[Dict]
# page_num=None 检测全部页
# 返回水印对象列表,类型:artifact/xobject/repeated_text/repeated_image
def remove_watermark(self, page_num: int, watermark: Dict) -> bool
# 对象级删除(Artifact/XObject/redact)
def remove_all_watermarks(self, page_num: int = None) -> int
# 删除所有水印,返回数量
def remove_watermark_block(
self,
page_num: int,
bbox: Tuple[float, float, float, float],
watermark_text: str = None,
watermark_size: float = None,
is_rotated: bool = None
) -> bool
# 文字块级内容流级精确移除(三重特征验证)
def remove_watermark_blocks_batch(
self,
page_num: int,
blocks: List[Dict]
) -> int
# 批量删除多个水印文字块(单次内容流扫描)
def extract_watermark(self, page_num: int, watermark: Dict) -> bytes | None
# 提取水印为 PNG 字节
```
#### 水印检测策略详解
```python
# === 对象级水印检测 (detect_watermarks) ===
# _detect_artifact_watermarks() - Artifact 标记水印
# _detect_xobject_watermarks() - Form XObject 水印
# _detect_repeated_content() - 跨页重复内容(≥3页)
# _extract_bbox_from_stream() - 从内容流提取 bbox
# _get_xobject_bbox_on_page() - 获取 XObject 在页面上的 bbox
# === 文字块级水印识别 (_identify_watermark_spans) ===
# 评分制(总分 >= 3 判定为水印):
# 旋转文字 (+3) - line_dir 的 dy != 0
# 浅色文字 (+2) - RGB 在 180-240 之间且三通道相近
# 异常大字号 (+1)- 字号 > 中位数×2 且 > 40
# 跨页重复 (+2) - 相同文字在 3 页以上出现
# Artifact标记(+5)- 内容流中明确标记
# 任一策略命中即标记 is_watermark=True, source="watermark"
# _build_watermark_text_cache() - 构建跨页重复文字缓存
# _get_artifact_text_set() - 获取 Artifact 标记的文字集合
```
#### 水印删除策略详解
```python
# === 对象级删除 ===
# _remove_artifact_watermark() - 正则匹配并移除 BDC...EMC 块
# _remove_xobject_watermark() - 移除 /Name Do 引用块
# === 文字块级删除 (_remove_text_by_features) ===
# 核心原理:在内容流中通过三重特征验证定位 BT...ET 块,
# 将文字替换为空格(绝不使用 redact,避免误删重叠正文)
#
# 三重验证(全部通过才执行移除):
# 1. 旋转特征:Tm 矩阵的 b/c 分量 ≠ 0
# 2. 字号匹配:Tf 操作符的字号与水印一致(容差 2pt)
# 3. 坐标匹配:Tm 的 (e,f) 转换到页面坐标系后在水印 bbox 内
# page_y = page_height - pdf_y
# 位置容差:max(字号×0.5, 10)pt
#
# 文字编码支持:
# - 字面字符串: (text) Tj
# - 十六进制字符串: <hex> Tj(中文水印最常见)
# - 数组形式: [(str) <hex>] TJ
# - 兜底机制:三重验证通过但未找到 Tj/TJ 时,整块替换
#
# 多水印处理:
# - 同一 BT...ET 块内可能包含多个 Tm+Tj 对
# - 逐个 Tm 独立做三重验证,只替换匹配 Tm 对应的文字段
#
# 批量删除 (remove_watermark_blocks_batch):
# - 所有水印特征一次性传入 _remove_text_by_features
# - 单次 clean_contents + 单次扫描 + 单次 update_stream
# - 避免逐个删除时内容流重组导致后续水印匹配失败
```
引擎适配层
OCREngineAdapter(`pdftools/engines/ocr_engine_adapter.py`)
```python
class OCREngineAdapter(ABC):
@abstractmethod
def ocr(self, image_path: str) -> List
@abstractmethod
def ocr_pdf(self, pdf_path: str, page_numbers: List[int] = None) -> List
@abstractmethod
def is_available(self) -> bool
class PaddleOCREngineAdapter(OCREngineAdapter):
def __init__(self, lang: str = None, use_gpu: bool = None)
# - 自动从 settings 读取默认值
# - _init_engine() 初始化 PaddleOCR
# - _filter_valid_params() 过滤函数签名中不存在的参数
# (兼容不同版本 PaddleOCR API)
# - 模型路径从 settings 读取,不存在则自动创建
def ocr(self, image_path: str) -> List
def ocr_pdf(self, pdf_path: str, page_numbers: List[int] = None) -> List
# PDF OCR 流程:
# 1. fitz.open() 打开 PDF
# 2. 逐页 get_pixmap(dpi=200) 渲染为图片
# 3. 保存为临时 PNG
# 4. 调用 ocr() 识别
# 5. 清理临时文件
def is_available(self) -> bool
```
StructureEngineAdapter(`pdftools/engines/structure_engine_adapter.py`)
```python
class StructureEngineAdapter(ABC):
@abstractmethod
def predict(self, pdf_path: str) -> List
@abstractmethod
def is_available(self) -> bool
class PPStructureEngineAdapter(StructureEngineAdapter):
def __init__(self, lang: str = None, use_gpu: bool = None)
def predict_fast(self, pdf_path: str, progress_callback=None) -> List
# 快速版面分析(PyMuPDF 内置):
# - page.get_text('blocks') 获取文字/图片块
# - page.find_tables() 查找表格
# - 速度快但精度较低,作为 PP-Structure 回退方案
def predict(self, pdf_path: str, progress_callback=None) -> List
# PP-Structure 版面分析:
# - ≤3 页:一次性分析
# - >3 页:逐页分析(单页导出临时 PDF → 分析 → 清理)
# - 实时报告进度
def _init_engine(self)
# 初始化 PPStructure
# - 配置参数:lang, use_gpu, table, show_log
# - 模型路径:det/rec/cls/layout/table/formula
# - _filter_valid_params() 过滤无效参数
def is_available(self) -> bool
```
关于模型
模型文件结构
```
models/
├── ocr/
│ ├── ch_PP-OCRv4_det_infer/ # 文字检测模型
│ ├── ch_PP-OCRv4_rec_infer/ # 文字识别模型
│ └── ch_ppocr_mobile_v2.0_cls_infer/ # 方向分类模型
└── layout/
├── picodet_lcnet_x1_0_fgd_layout_cdla_infer/ # 版面检测模型
├── ch_ppstructure_mobile_v2.0_SLANet_infer/ # 表格识别模型
└── rec_latex_ocr_infer/ # 公式识别模型
```
核心依赖文档
| 库 | 文档地址 |
|-----|---------|
| PyMuPDF | https://pymupdf.readthedocs.io/ |
| pypdf | https://pypdf.readthedocs.io/ |
| PaddleOCR | https://www.paddleocr.ai/ |
| PP-Structure | https://github.com/PaddlePaddle/PaddleOCR |
| pdf2docx | https://github.com/ArtifexSoftware/pdf2docx |
| python-docx | https://python-docx.readthedocs.io/ |
| openpyxl | https://openpyxl.readthedocs.io/ |
| python-pptx | https://python-pptx.readthedocs.io/ |
| PySide2 | https://doc.qt.io/qtforpython-5/ |
| PySide6 | https://doc.qt.io/qtforpython-6/ |
系统要求
| 平台 | 最低配置 | 推荐配置 |
| **Windows** | Win7 SP1 x64, 4GB RAM | Win10+, 8GB RAM, SSD |
| **Linux** | 麒麟/UOS, 4GB RAM | 8GB RAM, SSD, aarch64/x86_64 |
| **磁盘空间** | 2GB(含模型) | 5GB(含缓存空间) |
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)