Files
lanyuanxiaoyao a490b2642c feat: 新增 PPT 旧格式支持,重构 LibreOffice 转换工具
- 新增 PPT (旧格式) 解析器
- 重构 _utils.py,提取通用 convert_via_libreoffice 函数
- 更新依赖配置,添加 PPT 相关依赖
- 完善文档,更新 README 和 SKILL.md
- 添加 PPT 文件检测函数
- 新增 PPT 解析器测试用例
2026-03-16 22:49:04 +08:00

130 lines
5.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Purpose
提供 Reader 内部共享工具模块包含解析器包装函数、格式化工具、ZIP 安全处理和 unstructured 库集成。此模块仅供 readers 包内部使用,不作为公共 API。
## Requirements
### Requirement: 解析器包装函数
系统 SHALL 提供统一的解析器包装函数,封装第三方库的调用细节。
#### Scenario: 使用 MarkItDown 解析
- **WHEN** 调用 `parse_via_markitdown(file_path)`
- **THEN** 系统使用 MarkItDown 库解析文件
- **AND** 成功时返回 `(markdown_content, None)`
- **AND** 失败时返回 `(None, error_message)`
#### Scenario: 使用 docling 解析
- **WHEN** 调用 `parse_via_docling(file_path)`
- **THEN** 系统使用 docling 库解析文件
- **AND** 成功时返回 `(markdown_content, None)`
- **AND** 失败时返回 `(None, error_message)`
#### Scenario: 库未安装时返回友好错误
- **WHEN** 调用解析器包装函数但对应库未安装
- **THEN** 系统返回 `(None, "<库名> 库未安装")`
### Requirement: Markdown 表格格式化
系统 SHALL 提供将二维列表格式化为 Markdown 表格的工具函数。
#### Scenario: 格式化标准表格
- **WHEN** 调用 `build_markdown_table(rows_data)` 且 rows_data 包含表头和数据行
- **THEN** 系统生成标准 Markdown 表格格式
- **AND** 第一行前生成分隔行(`| --- | --- |`
#### Scenario: 空数据返回空字符串
- **WHEN** 调用 `build_markdown_table([])``build_markdown_table([[]])`
- **THEN** 系统返回空字符串
### Requirement: 列表堆栈处理
系统 SHALL 提供列表堆栈处理工具函数,用于处理嵌套列表的格式化输出。
#### Scenario: 刷新列表堆栈
- **WHEN** 调用 `flush_list_stack(list_stack, target)`
- **THEN** 系统将 list_stack 中所有非空项添加到 target 列表
- **AND** 每个项末尾添加换行符
- **AND** 清空 list_stack
#### Scenario: 跳过空项
- **WHEN** list_stack 中包含空字符串
- **THEN** 系统跳过空项,不添加到 target
### Requirement: ZIP 文件安全打开
系统 SHALL 提供安全的 ZIP 文件打开函数,防止路径遍历攻击。
#### Scenario: 打开合法文件
- **WHEN** 调用 `safe_open_zip(zip_file, "valid/file.txt")`
- **THEN** 系统返回对应的 ZipExtFile 对象
#### Scenario: 拒绝路径遍历攻击
- **WHEN** 路径包含 ".." 在 Path.parts 中
- **THEN** 系统返回 None
#### Scenario: 拒绝绝对路径
- **WHEN** 路径为绝对路径
- **THEN** 系统返回 None
#### Scenario: 处理路径异常
- **WHEN** Path() 抛出 ValueError 或 OSError
- **THEN** 系统捕获异常并返回 None
### Requirement: unstructured 元素转换
系统 SHALL 提供将 unstructured 库解析的元素转换为 Markdown 的工具函数。
#### Scenario: 转换标准元素
- **WHEN** 调用 `convert_unstructured_to_markdown(elements, trust_titles=True)`
- **THEN** 系统跳过 Header、Footer、PageBreak、PageNumber 元素
- **AND** 跳过 RGB 颜色值和页码噪声
- **AND** Table 元素转换为 Markdown 表格
- **AND** Title 元素转换为 # 标题(根据 category_depth 确定级别)
- **AND** ListItem 元素转换为 - 列表项
- **AND** Image 元素转换为 ![image](path) 格式
#### Scenario: 库未安装时回退
- **WHEN** markdownify 或 unstructured 库未安装
- **THEN** 系统提取所有元素的 text 属性并用双换行连接
### Requirement: 噪声模式匹配
系统 SHALL 定义 unstructured 库的噪声匹配模式。
#### Scenario: 匹配 RGB 颜色值
- **WHEN** 文本匹配 `_UNSTRUCTURED_RGB_PATTERN`(如 "R:255 G:128 B:0"
- **THEN** 系统将其识别为噪声并过滤
#### Scenario: 匹配页码
- **WHEN** 文本匹配 `_UNSTRUCTURED_PAGE_NUMBER_PATTERN`(如 "— 3 —"
- **THEN** 系统将其识别为噪声并过滤
### Requirement: 通用 LibreOffice 格式转换
系统 SHALL 提供通用的 LibreOffice 格式转换函数,支持在不同格式间转换。
#### Scenario: 转换文件到指定格式
- **WHEN** 调用 `convert_via_libreoffice(input_path, target_format, output_dir)`
- **THEN** 系统使用 soffice --headless --convert-to 进行转换
- **AND** 输出文件写入 output_dir
- **AND** 成功时返回 (output_path, None)
- **AND** 失败时返回 (None, error_message)
#### Scenario: LibreOffice 未安装
- **WHEN** soffice 未在 PATH 中
- **THEN** 系统返回 (None, "LibreOffice 未安装")
#### Scenario: 转换超时
- **WHEN** soffice 执行超过 timeout 秒(默认 60 秒)
- **THEN** 系统返回 (None, "LibreOffice 转换超时")
#### Scenario: 转换失败
- **WHEN** soffice 返回非零退出码
- **THEN** 系统返回 (None, "LibreOffice 转换失败 (code: {code})")
#### Scenario: 输出文件未生成
- **WHEN** soffice 执行成功但未生成输出文件
- **THEN** 系统返回 (None, "LibreOffice 未生成输出文件")
#### Scenario: 可自定义输出后缀
- **WHEN** 提供 output_suffix 参数
- **THEN** 系统使用该后缀作为输出文件后缀,而不是 target_format
#### Scenario: 调用者管理输出目录生命周期
- **WHEN** convert_via_libreoffice 执行完成
- **THEN** 输出文件保留在 output_dir 中,由调用者负责清理