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

5.1 KiB
Raw Permalink Blame History

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 格式

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 中,由调用者负责清理