# 公文数据生成与回取

业务系统提交一份公文 JSON，生成按 GB/T 9704 排版、可在线编辑的公文；编辑后读回同一格式的数据。

业务系统（例如表单加后端服务）只需要按本页的协议组装一份 JSON。版头、红线、标题、层次编号、署名、版记和页码都由
`@quirekit/official-document` 按 GB/T 9704—2012 排好；生成的文档在 Classic 中和普通文档一样编辑，并可导出 Word、PDF、OFD。
各要素按 GB/T 33476.2 标注公文域，编辑后可以读回同一格式的 JSON 交还业务系统。

`@quirekit/official-document` 属于商业组件，使用前先取得相应分发与授权，见[付费使用](/classic/commercial)。

## 1. 整体流程

1. 业务系统根据表单内容生成公文 JSON（第 2～8 节）。
2. 前端校验数据，编译成文档并载入 Classic。
3. 用户在线修改、导出；需要时把当前内容读回 JSON（第 10 节），交给业务系统保存。

```ts
import { validateOfficialDocumentData } from '@quirekit/official-document'
import { readOfficialDocumentData } from '@quirekit/official-document/classic'

const checked = validateOfficialDocumentData(await response.json())
if (!checked.ok) showIssues(checked.issues) // 每项都有 path 与中文说明
else instance.editor?.commands.composeOfficialDocument(checked.data) // 一次可撤销地替换整篇文档

// 编辑完成后，连同印章、图片一起读回
const extracted = readOfficialDocumentData(instance.editor!)
if (extracted.ok) await saveToBusinessSystem(extracted.data, extracted.warnings)
```

`composeOfficialDocument` 由公文扩展提供：装配了 `@quirekit/classic-ai` 时已随附，否则安装
`createClassicOfficialDocumentPlugin()`（`@quirekit/official-document/classic`）。它把数据中的印章、图片先登记到编辑器媒体，
再替换文档；数据不合法时返回 `false` 且不改动文档。不用命令时，先把 `collectOfficialDocumentImages(data)` 登记到媒体，
再载入 `compileOfficialDocument(data)` 的结果；回取用 `extractOfficialDocumentData(instance.getJSON(), { images })`。
文档的保存、版本与文件转换见[保存、打开与文件转换](/classic/files)。

也可以不经浏览器，在 Node 服务中编译并用 Classic 的 DOCX 服务导出 Word 文件。Node 中没有浏览器的字体测量，
偶数末页版记是否与正文同页按估算字宽判断，交付前应在 Word 中核对。

## 2. 完整示例

一份带份号、密级和紧急程度的通知：

```json
{
  "version": 1,
  "kind": "notice",
  "issuer": { "agency": "示例市政务服务管理局" },
  "copyNumber": "000123",
  "secrecy": { "level": "秘密", "term": "1年" },
  "urgency": "加急",
  "documentNumber": "示政发〔2026〕12号",
  "title": "示例市政务服务管理局关于2027年元旦放假安排的通知",
  "recipients": ["各处室", "各直属单位"],
  "body": [
    { "type": "paragraph", "text": "根据国务院办公厅通知精神，结合工作实际，现将2027年元旦放假安排通知如下。" },
    { "type": "heading", "level": 1, "title": "放假时间" },
    { "type": "paragraph", "text": "2027年1月1日至3日放假调休，共3天。" },
    { "type": "heading", "level": 1, "title": "工作要求" },
    { "type": "heading", "level": 2, "title": "严格值班值守。", "text": "各单位要安排好节日期间值班。" },
    { "type": "heading", "level": 2, "title": "做好安全检查。", "text": "节前开展一次安全隐患排查。" },
    { "type": "paragraph", "text": "特此通知。" }
  ],
  "attachments": ["值班安排表", "联系人名单"],
  "date": "2026-12-20",
  "note": "联系人：李某，电话：12345678",
  "imprint": { "cc": ["示例市人民政府办公室"], "office": "示例市政务服务管理局办公室", "date": "2026-12-20" }
}
```

## 3. 字段

只有 `kind`、`issuer.agency` 和 `body` 必填。**没有内容的字段不要输出**：可选字段写成 `null` 或空字符串会被拒绝。

| 字段 | 类型 | 含义与规则 |
| --- | --- | --- |
| `version` | `1` | 协议版本，可省略 |
| `kind` | 文种值 | 见第 5 节 |
| `format` | `"letter"` | 信函格式（第 6 节），通常用于函；不写时为文件格式 |
| `issuer.agency` | 文本 | 发文机关全称或规范化简称，用于署名；联合行文时为主办机关 |
| `issuer.joint` | 文本数组 | 联合行文的联署机关，按顺序排在主办机关之后 |
| `issuer.mark` | 文本 | 发文机关标志，默认“机关名称＋文件”（命令为“＋令”，纪要为“＋会议纪要”）；可用 `\n` 分行 |
| `copyNumber` | 数字串 | 份号，一般 6 位，如 `000123` |
| `secrecy.level` | 秘密、机密、绝密 | 密级 |
| `secrecy.term` | 文本 | 保密期限、解密时间或条件，如 `1年`；排成“机密★1年” |
| `urgency` | 特急、加急 | 紧急程度；电报另有特提、平急 |
| `documentNumber` | 文本 | 发文字号，年份用六角括号，如 `示政发〔2026〕12号`；命令写令号 `第1号`，纪要写期号 `第1期` |
| `signers` | 文本数组 | 签发人，只用于上行文（请示、报告），按发文机关顺序每个机关一人 |
| `title` | 文本 | 标题；过长时自动在“关于”前分行，也可以用 `\n` 指定分行；命令、纪要不写 |
| `adoption` | 文本 | 会议通过说明，如 `（2026年10月17日……会议通过）`，用于决议等 |
| `recipients` | 文本数组 | 主送机关，每项一个机关，不带顿号或冒号；公告、通告、公报不写 |
| `body` | 正文块数组 | 见第 4 节 |
| `attachments` | 文本数组 | 附件名称，末尾不加标点；多个附件自动编号 |
| `signature` | 落款 | 见第 7 节 |
| `date` | `YYYY-MM-DD` | 成文日期，输出为“2026年12月20日”；不写时留出“年 月 日”待填 |
| `note` | 文本 | 附注，不带括号 |
| `attendees` | 名单数组 | 纪要的出席、请假、列席名单：`{ "label": "出席", "names": ["张某", "李某"] }` |
| `imprint.cc` | 文本数组 | 抄送机关 |
| `imprint.office` | 文本 | 印发机关，通常是发文机关办公室；信函格式不写 |
| `imprint.date` | `YYYY-MM-DD` | 印发日期，信函格式不写；不需要版记时整个 `imprint` 不写 |

## 4. 正文

`body` 按阅读顺序排列，有四种块：

| 块 | 写法 | 排版 |
| --- | --- | --- |
| 段落 | `{ "type": "paragraph", "text": "……" }` | 三号仿宋，首行缩进两字；不要以空格开头 |
| 层次标题 | `{ "type": "heading", "level": 1, "title": "……" }` | 1～4 级依次编号为“一、”“（一）”“1.”“（1）”，下级随上级重新计数 |
| 表格 | `{ "type": "table", "title": "……", "header": [[…]], "rows": [[…]] }` | 版心全宽、0.5 pt 单线；表题在上方居中（黑体），表头黑体居中、跨页重复，每行不跨页 |
| 图片 | `{ "type": "image", "src": "data:image/png;base64,…", "caption": "……" }` | 单独成段、居中，图题在下方居中 |

- 标题**不要写序号**，序号由程序生成；第一个层次标题必须从第 1 级开始。
- 小标题后同一行接正文时，`title` 以句号结尾，正文写在 `text`：
  `{ "type": "heading", "level": 2, "title": "严格值班值守。", "text": "各单位要安排好节日期间值班。" }`。

表格：

```json
{
  "type": "table",
  "title": "表1 2027年培训安排",
  "header": [["时间", "内容", "对象"]],
  "rows": [["3月", "公文格式", "各处室"], ["4月", "公文处理\n办文流程", "办公室"]],
  "columns": [{ "width": 1, "align": "center" }, { "width": 3 }, { "width": 2, "align": "center" }],
  "fontSize": 14
}
```

| 字段 | 说明 |
| --- | --- |
| `title` | 表题，可省略 |
| `header` | 表头行，可省略；跨页时每页重复 |
| `rows` | 表体行；每行（含表头）的格数必须等于列数，不支持合并单元格；单元格中的 `\n` 另起一段 |
| `columns` | 每列一项：`width` 为相对宽度（缺省等分），`align` 为 `start`（缺省）、`center` 或 `end` |
| `fontSize` | 表格文字字号：16（三号，缺省）、14（四号）或 12（小四） |

图片：`src` 为 PNG 或 JPEG 的 data URL；`width` 为宽度（mm），缺省按图片 96 dpi 的尺寸、不超过版心宽度 156 mm，高度按比例；
`caption` 为图题，可省略。图片与印章一样保存在编辑器媒体中，见第 7 节。国标 §8 的横排表格暂不支持。

## 5. 文种

| `kind` | 文种 | 说明 |
| --- | --- | --- |
| `resolution` | 决议 | 一般无署名，常带 `adoption` |
| `decision` | 决定 | |
| `order` | 命令（令） | 令式版头，无标题、主送；`documentNumber` 写令号；落款写负责人 |
| `communique` | 公报 | 一般无主送、无署名 |
| `announcement` | 公告 | 一般无主送 |
| `public-notice` | 通告 | 一般无主送 |
| `opinion` | 意见 | |
| `notice` | 通知 | |
| `circular` | 通报 | |
| `report` | 报告 | 上行文，写 `signers` |
| `request` | 请示 | 上行文，写 `signers` |
| `reply` | 批复 | |
| `proposal` | 议案 | 落款写负责人 |
| `letter` | 函 | |
| `minutes` | 纪要 | 纪要式版头，无标题、主送；写 `attendees`，一般无署名 |

命令和纪要不支持联合行文；命令不排份号、密级和紧急程度。

## 6. 信函格式

写 `"format": "letter"` 时按 GB/T 9704 §10.1 编排：发文机关标志只用机关名称（不加“文件”），上边缘距上页边 30 mm，
其下 4 mm 为红色双线（上粗下细），距下页边 20 mm 为红色双线（上细下粗），线长 170 mm；份号、密级、紧急程度顶格居左，
发文字号顶格居右，标题在其下二行；首页不显示页码；版记只排抄送（`imprint` 只写 `cc`），位于最后一页版心最下方。
联合行文时标志只用主办机关，署名仍依次排列。信函格式不排签发人，命令和纪要不能使用。

```json
{
  "kind": "letter",
  "format": "letter",
  "issuer": { "agency": "示例市政务服务管理局" },
  "documentNumber": "示政函〔2026〕5号",
  "title": "关于商请协助开展公文规范培训的函",
  "recipients": ["示例市档案局"],
  "body": [{ "type": "paragraph", "text": "专此函达。" }],
  "date": "2026-12-20",
  "imprint": { "cc": ["示例市财政局"] }
}
```

## 7. 落款与联合行文

`signature` 不写时为机关署名（不加盖印章的版式），其他写法：

- 加盖印章：`{ "type": "sealed" }`，见下文“印章与签名章”
- 加盖签发人签名章：`{ "type": "stamp", "stamps": [...] }`，见下文
- 负责人署名（命令、议案）：`{ "type": "leader", "position": "市长", "name": "张某" }`
- 不署名（决议、公报、纪要）：`{ "type": "none" }`

联合行文在 `issuer.joint` 列出联署机关：版头各机关名称上下排列、“文件”居中排在右侧，署名按主办机关在前依次向下排。
上行文的签发人每行两个，按机关顺序排列：

```json
{
  "kind": "request",
  "issuer": { "agency": "示例市政务服务管理局", "joint": ["示例市财政局"] },
  "documentNumber": "示政请〔2026〕3号",
  "signers": ["张某", "李某"],
  "title": "关于联合开展公文规范培训的请示",
  "recipients": ["示例市人民政府"],
  "body": [
    { "type": "paragraph", "text": "为提高公文办理质量，拟联合开展公文规范培训。现将有关事项请示如下。" },
    { "type": "heading", "level": 1, "title": "培训安排" },
    { "type": "paragraph", "text": "拟于2027年3月组织一次专题培训。" },
    { "type": "paragraph", "text": "妥否，请批示。" }
  ],
  "date": "2026-12-20"
}
```

### 印章与签名章

按 GB/T 9704—2012 §7.3.5.1、§7.3.5.3 编排，两种用法任选：

- **接入电子签章系统**：不给图片，文档只排好落款并预留盖章位置（不可见、随分页移动），导出后由签章系统按
  `locateOfficialSeals` 给出的坐标盖章。
- **没有签章系统**：在数据中给出印章图片（透明底 PNG 的 data URL），直接盖在文档中，Word、PDF、OFD 导出都包含。

```json
{ "type": "sealed", "seals": ["data:image/png;base64,iVBORw0..."], "diameter": 42 }
```

| 字段 | 说明 |
| --- | --- |
| `seals` | 按发文机关顺序，每个机关一枚；不写时预留全部位置。枚数必须等于机关数 |
| `diameter` | 印章直径（mm），20～60，默认 42 |

加盖印章时成文日期右空四字，机关署名以日期为准居中，印章与署名、日期同心，顶端距正文半行。联合行文时印章按机关顺序成排，
最后一枚压在成文日期上，其余向左排列，枚间与排间留 2 mm；机关名称较长、一排放不下各自署名时，每排一枚、依次向下。

```json
{ "type": "stamp", "stamps": [{ "position": "示例市人民政府市长", "image": "data:image/png;base64,iVBORw0..." }], "height": 15 }
```

| 字段 | 说明 |
| --- | --- |
| `stamps` | 按发文机关顺序，每个机关一项：`position` 为签发人职务全称，`image` 为签名章图片，可省略以预留位置 |
| `height` | 签名章高度（mm），8～30，默认 15；宽度按图片比例，没有图片时取高度的 2.5 倍 |

签名章在正文下空二行、右空四字，职务在其左侧空二字并上下居中，成文日期在签名章下空一行、右空四字；联合行文时每行一个机关。

浏览器中，盖章位置由 `@quirekit/official-document/classic` 读出：

```ts
import { locateOfficialSeals } from "@quirekit/official-document/classic";

// [{ kind: "seal", number: 1, reserved: true, page: 1, x: 120.68, y: 215.62, width: 42, height: 42 }]
// page 从 1 起；x、y、width、height 为毫米，以页面左上角为原点，与 PDF 导出一致。
const positions = locateOfficialSeals(instance.editor!);
```

图片保存在编辑器的媒体中，不写入文档 JSON；`composeOfficialDocument` 会先登记图片再替换文档。

## 8. 后端校验

发行包内的 `schema/official-document.schema.json`（JSON Schema 2020-12）描述字段结构，后端可以用所用语言的
JSON Schema 2020-12 校验库先行校验。以下规则 Schema 表达不了，由 `validateOfficialDocumentData` 检查：

- 除命令、纪要外必须有标题；
- 日期必须是真实存在的日子；
- 第一个层次标题从第 1 级开始；
- 命令不排份号、密级、紧急程度，命令和纪要不支持联合行文；
- 签发人不能多于发文机关；`attendees` 只用于纪要；
- 信函格式不能用于命令、纪要，也不排签发人；非信函格式的版记必须有 `office` 和 `date`；
- 印章（`seals`）、签名章（`stamps`）的个数等于发文机关数；
- 表格每行（含表头）的格数等于列数。

校验失败时返回全部问题，每项含 JSON Pointer 路径和中文说明，可以直接提示到对应表单项：

```json
[
  { "path": "/issuer", "message": "缺少必填字段：agency。" },
  { "path": "/date", "message": "格式不对，日期请写成 YYYY-MM-DD。" }
]
```

## 9. 接入要点

我们提供的接口与语言无关：业务系统用任何语言生成符合本协议的 JSON，通过 HTTP 交给前端即可。组装数据时注意：

- 字段名使用协议的驼峰命名，如 `documentNumber`、`fontSize`；
- 没填的字段直接不输出，不要输出 `null` 或空字符串（段落的 `text` 不能为空）；
- 日期写成 `2026-12-20`；
- 层次标题不写“一、”“（一）”等序号，由程序编号；
- 表格每行（含表头）的格数等于列数；印章、签名章的个数等于发文机关数；
- 印章、签名章与正文图片写成 data URL：印章、签名章为透明底 PNG，正文图片可用 PNG 或 JPEG。

后端可先用发行包内的 `schema/official-document.schema.json` 校验结构（第 8 节）。回取得到的 JSON 结构相同，可以用生成时的同一组类型反序列化。

## 10. 回取

`extractOfficialDocumentData(document, options?)` 读取编辑器内容（`instance.getJSON()`）或导入的 DOCX，返回
`{ ok: true, data, warnings }` 或 `{ ok: false, issues }`：

- 只读取公文域内的文字；在域外另起的段落不回取，在正文内回车新增的段落正常回取。
- 待审修订按“全部接受”后的文字读取，不改变文档中的修订。
- 正文中的表格与图片读回为表格块和图片块；含合并单元格的表格等协议容纳不了的内容跳过，并在 `warnings` 中给出位置；
  无法识别的日期留空并给出警告。列宽读回为对应的 twips 宽度，比例不变。
- 联合行文的机关以署名为准；版头与署名中的机关名称不一致时给出警告。
- 在 Microsoft Word 中编辑后再导入也能回取（Mac Word 16.109.2 实测）。
- 在线编辑时，“视图”功能区的“公文要素”打开右侧面板，按版头、主体、版记列出文档中的要素，像填表一样修改，改动写入对应的
  公文域，可撤销；正文、附件说明只显示摘要并可定位。界面样式需引入 `@quirekit/official-document/styles.css`。
- 印章与签名章图片在编辑器媒体中，回取时传入 `{ images: 媒体解析器 }` 才能读回；浏览器中用
  `readOfficialDocumentData(editor)`（`@quirekit/official-document/classic`）自动读回。读不到时给出警告，按预留位置回取。

数据未修改时，回取结果再编译得到同一份文档。

## 11. 当前范围

已支持：上述十五种文种、文件格式与信函格式、份号、密级与保密期限、紧急程度、签发人、联合行文、四级层次、正文表格与图片、附件说明、机关署名、加盖印章（预留位置或图片）、签名章、负责人署名与不署名、附注、纪要名单、
抄送与印发、奇偶页码，以及 Word/PDF/OFD 导出。

尚未支持：横排表格、表格合并单元格。WPS 等其他公文系统能否识别公文域尚未验证；
PDF、OFD 导出只含版面内容，不含公文域。
