# Word 受控编辑

`fsys.openXml.docx.editor` 为已有 DOCX/DOCM/DOTX/DOTM 提供不启动 Office/WPS 的受控编辑。它基于公共 `fsys.openXml.editor`，只提交明确修改的主文档部件；未修改部件保留原始解压字节，未知内容不会被重建或删除。

它不是完整 Word 编辑器：不计算域、页码或统计属性，不生成修订，不处理版式、图片、页眉页脚、文本框、内容控件、合并/嵌套表格，也不执行宏、外部关系或嵌入对象。

## 快速使用

```aardio
import fsys.openXml.docx.editor;

var ed,err = fsys.openXml.docx.editor.open("/input.docx");
if(!ed) return null,err;

var count,err,detail = ed.replaceText("{{客户}}","张三",{expectedCount=1});
if(count===null){ ed.close(); return null,err; }

var written,err = ed.setTableCells(1,[["张三",0],[false,""]],{row=2;column=1});
if(written===null){ ed.close(); return null,err; }

var output,err = ed.save("/output/report.docx",{overwrite=true});
ed.close();
if(!output) return null,err;
return output,{replaced=count;written=written;detail=detail};
```

编辑会话只读打开源文件；`save` 总是另存，默认不覆盖。`overwrite=true` 也不能覆盖源件或指向同一文件的硬链接别名。用完显式调用 `close`，失败路径也应关闭会话。

## 预览结构

```aardio
var paragraphs,err,warnings = ed.listParagraphs();
var tables,err,warnings = ed.listTables();
var table,err,warnings = ed.getTable(1);
```

- `listParagraphs()` 返回正文及直接普通表格内的段落：`index,text,tableIndex,row,column,editable,reason`。序号从 1 开始，只是编辑器当前扫描顺序，不等同于 Word COM 的 `Paragraphs` 序号。
- `listTables()` 返回 `index,depth,rowCount,columnCount,editable,reason`。表级 `editable` 只表示结构检查通过；具体单元格能力以 `getTable().cells` 为准。
- `getTable(index)` 返回物理单元格二维 `rows`，以及同坐标的 `cells[row][column] = {text,editable,reason}`。不展开合并格，外层格预览不包括嵌套表的内部文本。
- `warnings` 是扫描过程中发现的已跳过结构，不是错误。含复杂域时编辑操作会直接失败，因为无法安全确定域范围。

## 文字替换

```aardio
var count,err,detail = ed.replaceText("旧值","新值",{
    paragraphs=[1,4];
    expectedCount=2;
});
```

替换是区分大小写的字面匹配：

- `search` 必须是非空字符串；`replacement` 可以是空串，但不能包含 TAB、CR/LF、VT 或分页符。
- 可以跨同一段落中相邻的普通 `w:r/w:t`，新文字继承首个命中运行的格式；不跨段落、TAB、软换行、超链接边界或其他标记。
- `paragraphs` 是 `listParagraphs()` 的 1 基序号数组。省略时扫描全部普通段落；默认跳过受保护段落并放入 `detail.warnings`，显式选中受保护段落则失败。
- `expectedCount` 可为 0。次数不符时不提交任何修改；返回成功时 `count` 为 0 也合法。一次调用最多替换 100000 处。
- `detail` 目前含 `warnings,scope="body",matched`。它不是 Word 页码或布局统计。

## 表格填值

```aardio
var written,err = ed.setTableCells(1,[
    ["项目", "数值"],
    ["完成", 0],
    ["通过", false]
],{row=1;column=1});

var one,err = ed.setTableCell(1,2,2,"已完成");
```

- 表格索引、行列坐标均从 1 开始；不增删行列，只接受普通矩形、未合并且无省略格的表。
- `values` 必须是矩形纯数组。`null` 跳过，空串清空，`0`、`false` 和首尾空白保留。尾部 `null` 或全空行请显式声明数组长度，例如 `row@={length=3}`。
- 字符串中的 CR/LF 按段落处理，VT 写成软换行，TAB 写成制表符。目标格继承首段落属性和首个运行属性，保留表格、行、格的格式与尺寸。
- 目标格含域、书签、权限/批注标记、修订、内容控件、链接、嵌套表或未知对象时拒绝填值，不丢弃这些对象。
- 整次批量操作先检查所有目标和所有值，再提交；越界、非矩形、非法类型或受保护目标失败时不提交半张表。返回写入格数，0 合法。
- 可填值为字符串、有限数值、布尔值或 `null`；其他类型失败。

## 受保护边界

打开时拒绝数字签名、文档编辑保护和修订跟踪。扫描到域、扩展内容、结构修订、合并表或复杂块时，编辑器保留原 XML，并在预览中标出 `reason`；不会为了“尽量修改”而丢弃未知节点。

`save` 只修改主文档部件，不更新域缓存、页数、字数、缩略图、应用属性或其他部件。需要完整 Word/WPS 对象模型、域更新、修订、页眉页脚、复杂表格或 PDF 导出时，改用 `autos.skills.word` 的 COM 入口。

## Word 技能入口

```aardio
import autos.skills.word;
var wd = autos.skills.word;

var ed,err = wd.editDocx("/input.docx");
var path,err,report = wd.writeDocx("/output/new.docx",{blocks=[{text="正文"}]},{overwrite=true});
```

`editDocx` 直接返回本会话；`writeDocx` 直接返回 `path,err,report`，分别转发 `fsys.openXml.docx.editor.open` 与 `fsys.openXml.docx.builder.write`。
