【office2pdf】office2pdf – 产品需求文档 (PRD.md)

AI7小时前发布 beixibaobao
2 0 0

1. 概述

office2pdf 是一个库和命令行工具,使用纯 Rust 将 DOCX、XLSX 和 PPTX 文件转换为 PDF。
它无需外部运行时(LibreOffice、Chromium、Docker)即可独立运行,使用 Typst 引擎作为布局/PDF 后端。

核心价值

  • 零依赖:无需外部二进制文件/服务,作为单个可执行文件运行
  • 高质量输出:以 95% 的保真度再现原始文档的布局/样式
  • 库优先:可嵌入到其他 Rust 项目中,CLI 只是一个轻量封装

2. 目标用户

用户 使用场景
后端开发人员 服务端文档 → PDF 转换(报告、发票、打印输出)
DevOps/基础设施 无需 LibreOffice/Docker 的轻量级转换流水线
CLI 用户 从终端快速批量转换
Rust 开发人员 作为 crate 嵌入到项目中

3. 功能需求

3.1 输入格式

DOCX (Word)
优先级 功能 描述
P0 文本 段落、换行、分页
P0 行内格式 粗体、斜体、下划线、删除线、字体、字号、颜色
P0 段落格式 对齐(左/右/居中/两端对齐)、缩进、行距
P0 表格 基本表格、单元格合并、边框、背景色
P0 图片 内嵌图片、基本尺寸调整
P1 列表 编号列表、项目符号列表(多级)
P1 页眉/页脚 文本、页码
P1 页面设置 纸张大小、页边距、方向(纵向/横向)
P1 样式 应用文档样式表(标题 1~6 等)
P2 目录 目录渲染
P2 超链接 PDF 中的可点击链接
P2 脚注/尾注 脚注、尾注渲染
P3 文字环绕 图片周围的文字流动
P3 公式 OMML 数学公式
P3 图表 内嵌图表渲染
PPTX (PowerPoint)
优先级 功能 描述
P0 幻灯片 → 页面 1 张幻灯片 = 1 页的映射
P0 文本框 位置、大小、文本内容、格式
P0 基本形状 矩形、圆形、线条等
P0 图片 幻灯片内的图片放置
P1 背景 纯色、渐变、图片背景
P1 母版/版式 幻灯片母版 → 版式 → 幻灯片继承链
P1 主题 主题颜色、主题字体解析
P1 表格 幻灯片内的表格渲染
P2 组合形状 组合形状的处理
P2 形状样式 阴影、反射、旋转、透明度
P3 SmartArt SmartArt 图形
P3 图表 内嵌图表
XLSX (Excel)
优先级 功能 描述
P0 单元格数据 文本、数字、日期值的输出
P0 基本表格布局 行/列 → PDF 表格转换
P0 单元格合并 合并单元格的处理
P1 单元格格式 字体、颜色、背景色、边框
P1 列宽/行高 反映原始尺寸
P1 数字格式 货币、百分比、日期格式字符串
P1 工作表选择 转换特定工作表或所有工作表
P2 打印区域 反映配置的打印区域
P2 页眉/页脚 工作表的页眉/页脚
P2 分页符 手动分页符的处理
P3 条件格式 条件格式渲染
P3 图表 内嵌图表渲染

3.2 输出

功能 描述
PDF 输出 生成有效的 PDF 文件
PDF 版本 默认 PDF 1.7,可选 PDF/A
字体嵌入 在 PDF 中嵌入使用的字体
元数据 标题、作者、创建日期等

4. 非功能需求

类别 需求
性能 10 页文档 < 1 秒,100 页 < 5 秒
内存 处理 100 页文档时 < 500MB
二进制大小 CLI < 50MB(包含字体)
平台 Windows、macOS、Linux
错误处理 跳过无法解析的元素并发出警告,继续整体转换
字体回退 系统字体发现 + 内置默认字体

5. 架构

5.1 转换流水线

输入文件 (.docx/.pptx/.xlsx)
    │
    ▼
[1. 解析器] ─── docx-rs / ppt-rs / umya-spreadsheet
    │
    ▼
[2. IR (中间表示)]  ← 格式无关的文档模型
    │
    ▼
[3. Typst 代码生成] ─── IR → Typst 标记生成
    │
    ▼
[4. Typst 编译] ─── typst-as-lib / typst crate
    │
    ▼
[5. PDF 导出] ─── typst-pdf
    │
    ▼
output.pdf

5.2 IR (中间表示) 设计

所有输入格式在输出到 Typst 之前都转换为统一的 IR:

pub struct Document {
    pub metadata: Metadata,
    pub pages: Vec<Page>,
    pub styles: StyleSheet,
}
pub enum Page {
    Flow(FlowPage),     // DOCX:流式文本页面
    Fixed(FixedPage),   // PPTX:固定坐标页面
    Table(TablePage),   // XLSX:基于表格的页面
}
pub struct FlowPage {
    pub size: PageSize,
    pub margins: Margins,
    pub header: Option<HeaderFooter>,
    pub footer: Option<HeaderFooter>,
    pub content: Vec<Block>,
}
pub enum Block {
    Paragraph(Paragraph),
    Table(Table),
    Image(Image),
    PageBreak,
    List(List),
}

5.3 项目结构

office2pdf/
├── Cargo.toml                    # 工作区根目录
├── crates/
│   ├── office2pdf/                  # 库 crate
│   │   ├── Cargo.toml
│   │   └── src/
│   │       ├── lib.rs            # 公共 API
│   │       ├── error.rs          # thiserror 错误类型
│   │       ├── ir/               # 中间表示
│   │       │   ├── mod.rs
│   │       │   ├── document.rs   # Document, Page, Block 等
│   │       │   ├── style.rs      # 样式/格式模型
│   │       │   └── elements.rs   # Paragraph, Table, Image 等
│   │       ├── parser/           # 输入格式解析器
│   │       │   ├── mod.rs        # Parser trait
│   │       │   ├── docx.rs       # DOCX → IR
│   │       │   ├── pptx.rs       # PPTX → IR
│   │       │   └── xlsx.rs       # XLSX → IR
│   │       ├── render/           # IR → PDF 渲染
│   │       │   ├── mod.rs
│   │       │   ├── typst_gen.rs  # IR → Typst 标记生成
│   │       │   └── pdf.rs        # Typst 编译 + PDF 输出
│   │       └── config.rs         # 转换选项
│   └── office2pdf-cli/              # CLI crate
│       ├── Cargo.toml
│       └── src/
│           └── main.rs           # 基于 clap 的 CLI
├── tests/                        # 集成测试
│   ├── fixtures/                 # 测试文档文件
│   └── integration_tests.rs
└── fonts/                        # 内置默认字体

5.4 关键依赖

Crate 用途 备注
typst 布局引擎 typst-as-lib
typst-pdf PDF 输出
typst-kit 字体发现
docx-rs DOCX 解析 v0.4.19,活跃维护中
umya-spreadsheet XLSX 解析(含格式) 支持样式/格式提取
ppt-rs PPTX 解析 python-pptx 的 Rust 移植版
clap CLI 参数解析 v4 derive
thiserror 库错误
anyhow CLI 错误

6. 公共 API(库)

use office2pdf::{Document, ConvertOptions, Format};
// 简单用法
let pdf_bytes = office2pdf::convert("input.docx")?;
std::fs::write("output.pdf", pdf_bytes)?;
// 带选项
let options = ConvertOptions::builder()
    .paper_size(PaperSize::A4)
    .font_paths(vec!["./fonts"])
    .pdf_standard(PdfStandard::PdfA2b)
    .build();
let pdf_bytes = office2pdf::convert_with_options("input.xlsx", &options)?;
// 从字节转换
let docx_bytes = std::fs::read("input.docx")?;
let pdf_bytes = office2pdf::convert_bytes(&docx_bytes, Format::Docx, &options)?;
// 仅转换特定工作表/幻灯片
let options = ConvertOptions::builder()
    .sheet_names(vec!["Sheet1"])  // XLSX:仅特定工作表
    .slide_range(1..=5)          // PPTX:仅幻灯片 1-5
    .build();

7. CLI 接口

# 基本转换
office2pdf input.docx                     # → input.pdf
office2pdf input.pptx -o output.pdf       # 指定输出路径
# 选项
office2pdf input.xlsx --sheets "Sheet1,Sheet2"   # 仅特定工作表
office2pdf input.pptx --slides 1-5               # 仅特定幻灯片
office2pdf input.docx --paper a4 --landscape     # 纸张设置
office2pdf input.docx --font-path ./fonts        # 字体路径
# 批量转换
office2pdf *.docx --outdir ./pdfs/               # 批量转换多个文件
# 信息
office2pdf --version
office2pdf --help

8. 实施阶段

阶段 1:MVP(基础文本 + 图片)

  • 项目结构搭建(工作区、CI)
  • IR 定义
  • DOCX P0 功能 → IR → Typst → PDF
  • PPTX P0 功能 → IR → Typst → PDF
  • XLSX P0 功能 → IR → Typst → PDF
  • 基础 CLI 功能

阶段 2:格式 + 样式

  • 所有 P1 功能实现
  • 字体嵌入/回退
  • 增强错误处理

阶段 3:高级功能

  • P2 功能实现
  • PDF/A 支持
  • 性能优化
  • 批量转换

阶段 4:完善

  • P3 功能(在可行范围内)
  • 边缘情况处理
  • 文档、示例、crates.io 发布

9. 验证方法

方法 描述
Golden 测试 测试文档 → PDF → 截图对比
往返测试 转换已知文档并通过文本提取验证
手动对比 MS Office PDF 输出与 office2pdf 输出对比
CI 每次提交时自动转换测试文档集

10. 风险

风险 影响 缓解措施
OOXML 规范复杂性 解析 crate 可能不支持所有元素 跳过不支持的元素 + 警告日志
Typst IR 转换限制 某些布局可能无法在 Typst 中表达 在 Typst 能力范围内进行最佳近似
字体兼容性 原始字体可能不可用 系统字体发现 + 回退字体映射
解析 crate 维护 依赖 crate 可能被弃用 准备分支,考虑为核心解析器自行实现
© 版权声明

相关文章