# 客户端文档编辑 API

本文描述业务系统直接调用 `<word-editor>` 的稳定 JavaScript 接口。编辑器负责 DOCX 展示、编辑和序列化；文件存储、权限校验和版本管理由宿主业务系统负责。

## 最小接入

```html
<word-editor id="editor" mode="edit"></word-editor>
<script type="module">
  import "/packages/word-editor/dist/index.js";

  const editor = document.querySelector("#editor");
  await editor.importDocument(file, { name: file.name });
</script>
```

`mode="edit"` 可编辑，`mode="view"` 只读展示；也可调用 `editor.setMode("edit" | "view")` 动态切换。

## 1. 书签管理与位置

```js
const createdName = editor.addBookmark("CustomerSignature");
const exists = editor.hasBookmark(createdName);
const bookmark = editor.findBookmark(createdName);
const bookmarks = editor.listBookmarks();
const position = editor.getBookmarkPosition(createdName);
editor.gotoBookmark(createdName);
editor.deleteBookmark(createdName);
```

`findBookmark()` 和 `listBookmarks()` 返回的每个书签都包含 `position.start` 与 `position.end`：

```js
{
  name: "CustomerSignature",
  id: "12",
  type: "range",
  page: 2,
  text: "待签署",
  position: {
    start: {
      story: "body",
      page: 2,
      blockIndex: 8,
      blockId: "paragraph-8",
      offsetInBlock: 4,
      documentOffset: 126,
      pageOffset: { x: 112, y: 286 },
      viewportRect: { x: 420, y: 360, width: 0, height: 20, top: 360, right: 420, bottom: 380, left: 420 }
    },
    end: { /* 同样结构 */ }
  }
}
```

- `page` 从 1 开始。
- `blockIndex`、`offsetInBlock`、`documentOffset` 从 0 开始，偏移单位为 UTF-16 code unit。
- `documentOffset` 不包含页眉页脚，只统计正文。
- `pageOffset` 相对于当前渲染页；`viewportRect` 相对于浏览器视口。缩放、窗口尺寸或分页变化后应重新查询。

## 2. 在指定位置插入内容

统一使用 `insertContent(content, options)`。当前内容类型为安全的纯文本，换行会转换为文档换行，不解析 HTML。

```js
editor.insertContent("插入到当前光标或替换当前选区");
editor.insertContent("文档前缀", { at: "document-start" });
editor.insertContent("文档后缀", { at: "document-end" });
editor.insertContent("签署人：王宇", {
  at: "bookmark",
  bookmark: "CustomerSignature",
  placement: "start"
});
```

书签位置 `placement`：

| 值 | 语义 |
| --- | --- |
| `before` | 书签起点之前 |
| `start` | 书签内容起点，默认值 |
| `end` | 书签内容末尾 |
| `after` | 书签终点之后 |

兼容方法 `insertText(text)` 和 `insertTextAtBookmark(name, text)` 继续可用，它们内部调用同一套插入管线。

## 3. 保存

```js
const blob = await editor.saveDocument();
await businessApi.saveDocument(blob, {
  id: documentId,
  name: editor.documentName
});
```

`saveDocument()` 返回标准 DOCX `Blob`，并触发 `saved` 事件。`editor.dirty` 表示当前是否有未保存修改；宿主还可监听 `changed` 事件同步保存按钮状态。

## 4. 导入与导出

```js
// File / Blob / ArrayBuffer / Uint8Array
await editor.importDocument(input, { name: "contract.docx" });

const blob = await editor.exportDocument();
const bytes = await editor.exportDocument({ output: "array-buffer" });
```

`importDocument()` 是统一导入入口；原有 `load()` 保持兼容。`exportDocument()` 默认返回 DOCX `Blob`，需要二进制传输时可返回 `ArrayBuffer`。

## 完整宿主封装示例

```js
export function createDocumentEditorApi(editor) {
  return {
    setEditable(editable) {
      editor.setMode(editable ? "edit" : "view");
    },
    importDocument(input, name) {
      return editor.importDocument(input, { name });
    },
    save() {
      return editor.saveDocument();
    },
    exportDocument(options) {
      return editor.exportDocument(options);
    },
    bookmarks: {
      list: () => editor.listBookmarks(),
      find: (name) => editor.findBookmark(name),
      position: (name) => editor.getBookmarkPosition(name),
      create: (name) => editor.addBookmark(name),
      remove: (name) => editor.deleteBookmark(name),
      goto: (name) => editor.gotoBookmark(name)
    },
    insert(content, options) {
      return editor.insertContent(content, options);
    }
  };
}
```
