# pptx-rs

Rust 实现的 PowerPoint `.pptx` / `.ppt` 读写库，对标 [python-pptx](https://github.com/scanny/python-pptx)。

> 当前状态：**0.4.0** —— MVP + 文件加密 + 图表 / SmartArt / 3D 效果 + 多母版 round-trip 保真。可创建 / 打开 / 保存 `.pptx`；支持幻灯片增删、文本框、段落、Run（带字体 / 字号 / 粗体 / 颜色 / 下划线）、图片插入、表格 / 连接器等基础形状；**支持母版 / 版式 / 主题读写与高阶编辑**；**支持 8 种图表类型**（Column / Bar / Line / Pie / Scatter / Area / Radar / Bubble，含 Excel 嵌入 + 数据标签 + 次坐标轴 + 系列样式 + 组合图 + 读路径）；**支持 SmartArt**（创建 + round-trip + 文本节点编辑）；**支持 3D 效果**（旋转 / 挤出 / 斜面 / 材质）；**支持 OOXML Agile Encryption 文件加密**（AES-256-CBC + SHA512，WPS/PowerPoint 兼容）；支持水印注入。**支持 .ppt（PowerPoint 97-2003 二进制格式）文件的 RC4 CryptoAPI 加密和水印注入**。
>
> **已知限制**：打开已有 `.pptx` 再保存时，**多母版/版式/主题场景已完整保真**（from_opc 解析存储 + to_opc_package 循环透传）；**notesMaster 已支持写路径**（读写 round-trip 保真）。

## 路线图

- [x] OPC 包容器：zip 读写、`[Content_Types].xml`、Part 关系链
- [x] Presentation / Slide / SlideLayout / SlideMaster XML 模型
- [x] 形状树：AutoShape、Picture、Group、Connector
- [x] TextFrame / Paragraph / Run / 字体/颜色
- [x] 常用 DrawingML 几何（`<a:prstGeom>` preset shapes）
- [x] 表格
- [x] 水印（向 spTree 注入半透明文本框）
- [x] **文件加密（OOXML Agile Encryption：AES-256-CBC + SHA512）**
- [x] **水印+加密一步到位**
- [x] **.ppt 文件加密（RC4 CryptoAPI，MS-OFFCRYPTO 规范）**
- [x] **.ppt 文件水印注入（Escher OfficeArt SpContainer）**
- [x] **.ppt 水印+加密合并**
- [x] **母版 / 版式 / 主题**（读写 + 高阶编辑 API；round-trip 保真仍有限制，见"已知限制"）
- [x] **Chart**（8 种类型：Column / Bar / Line / Pie / Scatter / Area / Radar / Bubble + Excel 嵌入 + 数据标签 / 次坐标轴 + 系列样式 + 组合图 + 读路径）
- [x] **SmartArt**（创建 + round-trip + 文本节点编辑）
- [x] **3D 效果**（旋转 / 挤出 / 斜面 / 材质 / backdrop 背景元素）
- [x] **音视频嵌入**（`add_video` / `add_audio` + 海报帧）
- [x] **OLE 对象嵌入**（`add_ole_object`）
- [~] 完整 round-trip 保真（多母版/版式/主题已支持；notesMaster 读写已支持）
- [ ] 跨版本大文件兼容性测试（PowerPoint / WPS / Keynote）

## 安装

```bash
cargo add pptx-rs2
```

> **注意**：crates.io 上的 crate 名为 `pptx-rs2`（原名 `pptx-rs` 已被他人占坑），
> 但代码中导入名为 `pptx_rs`（通过 `[lib] name = "pptx_rs"` 解耦）。使用时 `use pptx_rs::...`。

或在 `Cargo.toml` 中手动添加：

```toml
[dependencies]
pptx-rs2 = "0.4"
```

## 快速开始

```rust
use pptx_rs::{Presentation, Pt, RGBColor};

fn main() -> pptx_rs::Result<()> {
    // 1) 全新创建
    let mut prs = Presentation::new()?;
    let counter = prs.id_counter();
    let slide = prs.slides_mut().add_slide(counter)?;
    slide.shapes_mut().add_textbox(
        pptx_rs::Inches(1.0), pptx_rs::Inches(1.0),
        pptx_rs::Inches(8.0), pptx_rs::Inches(1.0),
    )?
    .text_frame_mut()
    .set_text("Hello, rust-pptx!")
    .paragraphs_mut()
    .first_mut()
    .unwrap()
    .runs_mut()
    .first_mut()
    .unwrap()
    .font_mut()
    .set_size(Pt(40.0))
    .set_bold(true)
    .set_color(RGBColor(0x1F, 0x4E, 0x79));

    prs.save("hello.pptx")?;

    // 2) 打开已存在
    let prs = Presentation::open("hello.pptx")?;
    for (i, slide) in prs.slides().iter().enumerate() {
        println!("slide #{} has {} shape(s)", i, slide.shapes().len());
    }
    Ok(())
}
```

## .ppt 97-2003 格式支持（库 API）

`.ppt`（PowerPoint 97-2003 二进制格式）与 `.pptx`（ZIP+XML）是**完全不同**的两种格式。
本库通过 `pptx_rs::ppt97` 模块提供 `.ppt` 文件的**水印注入**与 **RC4 CryptoAPI 加密**能力，
填补 python-pptx 不支持 .ppt 二进制格式的空白。

### 模块组织

```
src/ppt97/
├── mod.rs          # 模块入口 + 公共 API
├── record.rs       # PPT record 树解析（MS-PPT 规范）
├── ole.rs          # OLE2/CFB 容器操作（基于 cfb crate）
├── watermark.rs    # 水印注入（Escher OfficeArt SpContainer）
└── crypto.rs       # RC4 CryptoAPI 加密（MS-OFFCRYPTO 规范）
```

### 公共 API

| API | 功能 |
| --- | --- |
| `pptx_rs::ppt97::add_watermark(path, &config)` | 为 .ppt 注入水印（不可编辑背景层） |
| `pptx_rs::ppt97::encrypt(path, password)` | 为 .ppt 设置 RC4 CryptoAPI 加密 |
| `pptx_rs::ppt97::add_watermark_and_encrypt(path, &config, password)` | 同时注入水印和加密（先水印后加密） |
| `pptx_rs::ppt97::WatermarkConfig` | 水印配置（文本、字号、颜色、旋转角度） |

### 库 API 示例

```rust
use pptx_rs::ppt97::{add_watermark, encrypt, add_watermark_and_encrypt, WatermarkConfig};
use std::path::Path;

fn main() -> pptx_rs::Result<()> {
    let input = Path::new("input.ppt");
    let config = WatermarkConfig {
        text: "机密".to_string(),
        ..Default::default()
    };

    // 1) 仅加水印
    let watermarked = add_watermark(input, &config)?;
    std::fs::write("watermarked.ppt", &watermarked)?;

    // 2) 仅加密
    let encrypted = encrypt(input, "my-password")?;
    std::fs::write("encrypted.ppt", &encrypted)?;

    // 3) 水印 + 加密（推荐用法）
    let both = add_watermark_and_encrypt(input, &config, "my-password")?;
    std::fs::write("both.ppt", &both)?;

    Ok(())
}
```

### 水印特性

- 注入到 **MainMaster** 的 PPDrawing（覆盖所有幻灯片，不只是单张）
- 作为 SpgrContainer 中"组形状本身"之后的**第一个子形状**（z-order 最低）
- FOPT 保护位 0x01C2 锁定，普通视图下**不可选中/不可编辑**
- 全屏覆盖（ClientAnchor 0,0 → 5760,4320）
- 默认中灰色、可配置旋转角度、可配置文本/字号/颜色

### 加密特性

- RC4 CryptoAPI 加密（MS-OFFCRYPTO 规范 2.3.5）
- 密钥派生：H₀=SHA1(salt+password_utf16le)，Hfinal=SHA1(H₀+LE32(block))
- 每个 persist 对象独立加密（block=persistId，分段加密）
- **不加密**：UserEditAtom、PersistDirectoryAtom、Pictures stream（WPS 兼容）
- 加密标记：CurrentUserAtom.headerToken=0xF3D1C4DF，UserEditAtom.recLen=28→32
- 默认密码：`pptx-rs-secret`（可在调用时自定义）

## 文件加密

### .pptx 文件加密（OOXML Agile Encryption）

使用 OOXML Agile Encryption（MS-OFFCRYPTO 规范），输出可被 WPS / PowerPoint / msoffcrypto-python 正确解密：

```bash
# 仅加密
cargo run --example protect_pptx

# 水印+加密
cargo run --example watermark_and_protect
```

加密参数：
- 算法：AES-256-CBC + SHA512
- 密钥派生：PBKDF2（100000 次迭代）
- 容器：OLE2/CFB（DataSpaces + EncryptionInfo + EncryptedPackage）
- 默认密码：`pptx-rs-secret`

### .ppt 文件加密（RC4 CryptoAPI）

支持 PowerPoint 97-2003 二进制格式（`.ppt`）的 RC4 CryptoAPI 加密，输出可被 msoffcrypto-python 正确解密。
**推荐使用 [`pptx_rs::ppt97`](#ppt-97-2003-格式支持库-api) 库 API**，examples 仅作为命令行封装：

```bash
# 仅加密（库 API: pptx_rs::ppt97::encrypt）
cargo run --example protect_ppt

# 水印+加密（库 API: pptx_rs::ppt97::add_watermark_and_encrypt）
cargo run --example watermark_and_protect_ppt
```

加密参数：
- 算法：RC4 + SHA1 密钥派生（MS-OFFCRYPTO 规范）
- 密钥位数：128 bit
- 容器：OLE2/CFB（PowerPoint Document + Current User stream）
- 加密结构：CryptSession10Container + PersistDirectoryAtom 更新
- 每个 persist 对象独立加密（block=persistId，分段加密）
- 默认密码：`pptx-rs-secret`

## 水印

### .pptx 水印

```bash
cargo run --example watermark_pptx
```

水印特性：
- 浅灰色半透明 40pt 粗体文字 "pptx-rs WATERMARK"
- 旋转 -45°，位于幻灯片中央
- 不重新声明 xmlns（WPS 兼容）

### .ppt 水印

```bash
# 库 API: pptx_rs::ppt97::add_watermark
cargo run --example watermark_ppt
```

水印特性：
- 在每个 **MainMaster** 的 PPDrawing 中注入水印 SpContainer（不可编辑背景层）
- Escher OfficeArt 二进制结构（FSP / FOPT / ClientAnchor / ClientTextbox）
- TextCharsAtom（UTF-16LE）水印文本 "pptx-rs 水印"
- 加水印后正确更新所有 persist 对象的 offset
- 作为 SpgrContainer 中"组形状本身"之后的第一个子形状（z-order 最低）
- FOPT 保护位 0x01C2 锁定，普通视图下不可选中/不可编辑

## 与 python-pptx 的对应关系

| python-pptx | pptx-rs |
| --- | --- |
| `Presentation()` | `Presentation::new()` |
| `Presentation(path)` | `Presentation::open(path)` |
| `prs.save(path)` | `prs.save(path)` |
| `prs.slides` | `prs.slides()` / `prs.slides_mut()` |
| `prs.slide_width` / `slide_height` | `Emu` 类型字段 |
| `prs.slide_masters` / `slide_master` | `prs.slide_masters()` / `prs.slide_master(idx)` |
| `prs.slide_layouts` | `prs.slide_layouts()` / `prs.slide_layout(idx)` |
| `slide.shapes` | `slide.shapes()` / `slide.shapes_mut()` |
| `shapes.add_textbox(l, t, w, h)` | `shapes_mut().add_textbox(l, t, w, h)` |
| `shapes.add_picture(path, l, t, w, h)` | `shapes_mut().add_picture(path, l, t, w, h)` |
| `shapes.add_chart(chart_type, data, x, y, cx, cy)` | `shapes_mut().add_chart(chart_type, data, l, t, w, h)` |
| `shapes.add_ole_object(...)` | `shapes_mut().add_ole_object(path, prog_id, name, l, t, w, h)` |
| `shape.text_frame.text = "…"` | `shape.text_frame_mut().set_text("…")` |
| `run.font.size = Pt(24)` | `run.font_mut().set_size(Pt(24.0))` |
| `run.font.bold = True` | `run.font_mut().set_bold(true)` |
| `chart.plots[0].has_data_labels` | `chart_shape.set_data_labels(...)` / `series_data_labels(...)` |

> 注：python-pptx 当前**不支持** SmartArt 创建 / 编辑，本库通过 `shapes_mut().add_smartart(...)` 提供此能力。

## 命名

| 类型 | 说明 |
| --- | --- |
| `Emu(i64)` | English Metric Unit，1 inch = 914400 EMU |
| `Pt(f64)` | 磅，1 pt = 12700 EMU |
| `Inches(f64)` | 英寸 |
| `RGBColor(u8, u8, u8)` | sRGB 颜色 |

## 示例

| 示例 | 说明 |
| --- | --- |
| `hello_pptx` | 创建包含文本框的演示文稿 |
| `watermark_pptx` | 为现有 .pptx 添加水印 |
| `protect_pptx` | 为现有 .pptx 设置文件加密 |
| `watermark_and_protect` | 同时添加水印和加密（.pptx） |
| `protect_ppt` | 为现有 .ppt 设置 RC4 CryptoAPI 加密 |
| `watermark_ppt` | 为现有 .ppt 添加水印 |
| `watermark_and_protect_ppt` | 同时添加水印和加密（.ppt） |
| `text_format_demo` | 文本格式化演示 |
| `shapes_demo` | 各种形状演示 |
| `connector_demo` | 连接器演示 |
| `chart_demo` | 图表演示（柱 / 线 / 饼 / 散点 / 面积 5 种） |
| `media_demo` | 音视频嵌入演示（`add_video` / `add_audio`） |
| `ole_demo` | OLE 对象嵌入演示（`add_ole_object`） |
| `test_pictures_encryption` | 图片加密验证 |
| `test_copy_ppt` | .ppt 复制验证 |

## 文档

- [架构总览](docs/ARCHITECTURE.md) — 三层模型、调用链、关键类型
- [开发指南](docs/DEVELOPMENT.md) — 环境、构建、调试、发布
- [测试规范](docs/TESTING.md) — 覆盖目标、断言策略
- [OOXML 参考](docs/OOXML_REFERENCE.md) — 命名空间、元素顺序
- [贡献指南](docs/CONTRIBUTING.md) — fork / PR / 评审
- [更新日志](docs/CHANGELOG.md) — 版本变更

## 许可

MIT
