# fsys.openXml.pptx：读取、生成与有限编辑

本页保留读取与媒体导出用法。0.6.0 新增 [PPTX 生成与常用动画](builder/index.html)、[有限编辑与另存](editor/index.html)。纯生成建议直接导入 builder；read 的原有返回字段与语义不变。

```aardio
import fsys.openXml.pptx;

var result,err = fsys.openXml.pptx.read("/input.pptx",{
    includeNotes = true;
});
if(!result) return null,err;

for i,page in result.pages {
    print("第 " + i + " 页",page.partName,page.text);
    if(page.notesPart) print("备注",page.notesText);
}
for i,warning in result.warnings {
    print(warning.partName,warning.message);
}
return result;
```

## 结果

| 字段 | 内容 |
|---|---|
| `path` / `mainPart` | 源文件完整路径 / 主文档部件名 |
| `slideCount` | 默认页面列表中的实际页数，包含空白页和隐藏页 |
| `slides` | 按 `sldIdLst` 顺序排列的页面文本数组 |
| `notes` | 开启备注时与 `slides` 一一对应；无备注页为空字符串。关闭时为 `[]` |
| `noteCount` | 含备注部件的页面数；空备注部件也计入 |
| `text` / `notesText` | 对应数组以两个 CRLF 连接 |
| `pages` | 页面明细数组 |
| `warnings` | 发现的未支持内容或兼容回退，元素含 `code,message,partName` |

`pages` 元素包含 `index,id,relationshipId,partName,hidden,text,paragraphs,tables`；开启备注时还有 `notesText`，存在备注部件时还有 `notesPart`。`id` 保留为字符串。结果都是普通表、数组或基础值，没有需要关闭的资源。

- `paragraphs` 保留源顺序和空段落，包含基本表格中的段落。
- `tables` 元素含 `rows`（二维单元格文本数组）、`rowCount`。空单元格保留为 `""`，文字 `"0"` 不丢失。
- 普通文本的 run 直接连接，段落和显式换行使用 CRLF；表格文本用 TAB 分列、CRLF 分行，单元格内段落也保留。
- 不重建合并单元格几何；需要明确行列边界时读取 `tables[].rows`，不要重新拆分展示用 `text`。
- 判断某页是否有备注部件用 `notesPart`；判断总量用 `noteCount`，不要依赖 `notesText` 是否为空。

## 支持范围

通过包关系及内容类型识别 PresentationML，支持普通演示文稿、放映、模板及宏格式的**文本只读路径**，不执行宏。支持 Transitional / Strict 核心命名空间、前缀变化、默认命名空间与重排后的页面关系。

支持常见文本框、分组形状内文本、基本 DrawingML 表格、页面备注。字段读取已保存的文字，不重算日期、页码等字段。

### 明确不做

- 不渲染、还原版式或推断视觉阅读顺序。页内按 XML 形状树顺序读取；默认页面顺序不是自定义放映顺序。
- 不读取母版/布局继承文本，不做图片 OCR、图表/SmartArt 语义、动画或嵌入对象分析。
- 备注提取其自身存储的形状文本，可能包括其自身存储的占位符文字。
- `AlternateContent` 只使用存在的 Fallback 并记录警告；没有 Fallback 时跳过并记录警告。不实现完整 Markup Compatibility 处理。
- 不承诺 `warnings=[]` 代表整份文稿无遗漏；已明确排除的母版、图片 OCR 等范围不会逐项产生警告。
- 不支持旧二进制 `.ppt`，不支持加密文档。

错误统一返回 `null,err`，空文稿返回成功结果及空数组；函数自动关闭包。读取限额见 [包层说明](../index.html)。

## 清单与媒体目录

```aardio
import fsys.openXml.pptx;
var entries,err = fsys.openXml.pptx.listEntries("/input.pptx");
if(!entries) return null,err;
var media,err = fsys.openXml.pptx.listMedia("/input.pptx");
if(!media) return null,err;
return {entries=entries;media=media};
```

- `listEntries(path,pattern,options)`：列出真实 ZIP 条目，数组项为 `name,isDir,size,compressedSize,contentType`。包含实际存在的目录项，不合成隐式目录；`pattern` 是可选 aardio 模式串。
- `listMedia(path,options)`：列出 `ppt/media/` 和主部件同级 `media/` 目录内的文件，含子目录，不包含目录项。数组项为 `partName,name,ext,size,compressedSize,contentType`；`name` 是末级文件名，`ext` 不含点。
- 这些入口会验证包及 PresentationML 主部件，不靠文件扩展名猜格式；但不解析所有幻灯片正文，也不读取媒体内容。**清单成功不是整包校验成功。**
- 媒体范围当前刻意限定为常见目录；目录中的未引用文件也会列出。不枚举任意路径的所有图片/音视频关系，不做逐页映射；外链不会下载。目录外资源可由调用者用包层的 `getRelationships/readPart/extractPart` 按需处理。

## 导出媒体

```aardio
import fsys.openXml.pptx;
var result,err,partial = fsys.openXml.pptx.extractMedia("/input.pptx","/media",{
    overwrite = false;
});
if(!result) return null,err,partial;
return result;
```

成功结果为 `{outputDir,files,skipped}`：

- `files` 是本次已提交文件数组；元素含 `name,partName,source,path,ext,size,compressedSize,contentType`。`source` 与 `partName` 是包内 URI，`path` 是完整输出路径。
- `skipped` 是默认不覆盖时已存在的普通文件；字段同 `files`。这些跳过的文件不会被读取或检查 CRC。
- `options.overwrite` 默认 `false`，传 `true` 才替换已有文件。空媒体目录返回成功结果及空数组。
- 按末级文件名平铺导出。来自不同子目录的同名文件（忽略大小写）在写入前报错，避免互相覆盖。URI 的百分号转义保留为文件名原文，不自动解码为分隔符；非法文件名或设备名拒绝。
- 每个资源都经通用包层的 `extractPart` 完成 CRC、长度、读取限额及文件提交检查；只复制字节，不播放、执行、转码或验证图像格式。
- 失败返回 `null,err,partial`；已经开始批量导出时，`partial` 记录已提交及跳过项。**不是整批事务**：此前成功文件保留，当前失败项不会留下半成品或提前清空旧文件。写入目录可能已经创建。
- `options` 接受包层限额；导出在内存中容纳单个完整部件。默认 16 MiB/件，更大媒体需要显式提高限额，不能把超限误认为空内容。

## PowerPoint 技能包兼容入口

0.3.0 起四个入口全部转发到本库，不再含重复的 ZIP 遍历、流读取或固定前缀 XML 正则：

| 技能包入口 | 本库入口 |
|---|---|
| `listPptxEntries(path,pattern,options)` | `listEntries(path,pattern,options)` |
| `listPptxMedia(path,options)` | `listMedia(path,options)` |
| `extractTextFromPptx(path,includeNotes=true,options)` | `read(path,options)` |
| `extractPptxMedia(path,outputDir,overwrite=true,options)` | `extractMedia(path,outputDir,options)` |

旧调用参数及常用结果字段保留，`options` 为新增的尾部可选参数。技能包清单继续提供 `path,isDir,size,formattedSize`；媒体列表继续提供 `path,name,ext,size,formattedSize`；导出继续返回文件数组。旧导出入口**默认允许覆盖**，这是兼容保留；新 `extractMedia` 默认不覆盖。显式 `includeNotes/overwrite` 参数优先于 `options` 同名字段，不修改调用者选项表。

有意修正：页面顺序按主文档而非部件编号；备注逐页配对，`noteCount` 为含备注部件的页面数；媒体列表排除目录项，子目录媒体现在也可导出；损坏、非 PresentationML、超限或写入失败不再伪装为空结果或成功。错误时第三个返回值可取得已完成的部分导出结果。

Office/WPS 创建、编辑与渲染代码不属于 OpenXML 读取链，本轮保持不变。不把复杂 Office 制作接口搬进基础库。
