165 lines
6.2 KiB
Markdown
165 lines
6.2 KiB
Markdown
# v0.6.0 DOCX 字体嵌入与代表主题验收
|
||
|
||
最后更新:2026-07-31
|
||
|
||
## 1. 目标与边界
|
||
|
||
阶段 12D-R2 解决 DOCX 在目标机器没有安装主题字体时发生字体回退的问题。
|
||
实现必须同时满足:
|
||
|
||
- 纯 Node.js 运行,不增加 Python 环境;
|
||
- 接受主题现有的 OTF、TTF、WOFF 和 WOFF2 资源;
|
||
- 只嵌入明确声明且许可证位允许编辑的字体;
|
||
- 使用 Word 原生嵌入字体部件,正文、标题、表格和代码仍可编辑;
|
||
- Word 与 WPS 均可打开、编辑和互存;
|
||
- 转换、关系、内容类型和字体部件继续通过严格 OOXML 校验;
|
||
- 外部主题可以通过清单接入,不依赖主题 ID 分支。
|
||
|
||
字体采用完整字形嵌入,不做子集化。这样用户在 Word 或 WPS 中输入原文
|
||
没有出现过的新字符时,仍可继续使用同一字体。代价是生成文件明显增大。
|
||
|
||
## 2. 通用实现
|
||
|
||
主题清单新增可选的 `docxFonts.faces`:
|
||
|
||
```json
|
||
{
|
||
"family": "FandolFang",
|
||
"aliases": ["FangSong", "STFangsong"],
|
||
"source": "theme-shared:official-fonts/FandolFang-Regular.woff2",
|
||
"weight": 400,
|
||
"style": "normal",
|
||
"license": "GPL-3.0-or-later WITH Font-exception-2.0"
|
||
}
|
||
```
|
||
|
||
处理链路如下:
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
A["主题字体清单"] --> B["受限资源读取"]
|
||
B --> C["WOFF/WOFF2 转 SFNT"]
|
||
C --> D["解析名称、字重与 fsType"]
|
||
D --> E["许可证和体积门禁"]
|
||
E --> F["ODTTF 混淆"]
|
||
F --> G["写入 fontTable 与关系"]
|
||
G --> H["重写 Word 字体引用"]
|
||
H --> I["严格 OOXML 校验"]
|
||
```
|
||
|
||
核心约束:
|
||
|
||
- 单主题最多 16 个字形面;
|
||
- 单个源字体最多 8 MiB,源字体总量最多 32 MiB;
|
||
- 单个解码后 SFNT 最多 16 MiB,总量最多 48 MiB;
|
||
- 接受 `fsType=0x0000` 的可安装嵌入和 `fsType=0x0008` 的可编辑嵌入;
|
||
- 拒绝受限、仅预览打印、位图嵌入及无效权限组合;
|
||
- 字体键和 ODTTF 部件名由内容指纹稳定生成;
|
||
- WOFF2 WASM 解码采用进程内串行调度,避免底层非重入导致批量损坏;
|
||
- 同一字体内容按 SHA-256 合并并使用有界缓存;
|
||
- 内置主题可引用 `_shared` 字体,本地主题不能越权引用内置共享资源;
|
||
- 最终校验器会解混淆字体,重新检查 SFNT、内部字体族、权限位、关系和
|
||
孤立部件。
|
||
|
||
`themes/_shared/official-fonts/fonts.css` 会统一前置到内置主题 CSS,使网页、
|
||
PDF 和 DOCX 使用同一字体资产。字体选择和别名替换按字体族工作,没有
|
||
主题 ID 判断。
|
||
|
||
## 3. 许可证边界
|
||
|
||
Fandol 字体:
|
||
|
||
- 使用仓库已有的 Fandol WOFF2;
|
||
- 字体内 `fsType=0x0008`,允许可编辑嵌入;
|
||
- 许可证为 GPL-3.0-or-later with Font Exception;
|
||
- 完整许可证已位于
|
||
`themes/_shared/official-fonts/LICENSE.txt`。
|
||
|
||
Open Sans:
|
||
|
||
- `typora-github` 当前包含的是 `v17` 历史字体文件;
|
||
- 该批二进制的字体元数据和历史发行信息对应 Apache License 2.0;
|
||
- 历史版权声明和完整许可证随字体放在
|
||
`themes/typora-github/github/OPEN-SANS-LICENSE.txt`;
|
||
- 当前上游 Open Sans 已迁移到 SIL Open Font License 1.1。未来替换字体
|
||
二进制时必须重新核对许可证、版权声明和嵌入权限,不能沿用本阶段清单。
|
||
|
||
字体清单中的 `license` 是审计字段,不替代随包许可证文件。
|
||
|
||
## 4. 代表主题结果
|
||
|
||
测试机未安装 FandolSong、FandolFang、FandolHei、FandolKai 或
|
||
Open Sans,能够真实检验嵌入字体而不是本机字体回退。
|
||
|
||
| 主题 | 嵌入字形面 | 原始 DOCX 大小 | Word 页数 | WPS 页数 |
|
||
| --- | ---: | ---: | ---: | ---: |
|
||
| 政企正式·工作报告 | 2 | 9,932,187 字节 | 2 | 2 |
|
||
| 政企红头·标准文件 | 5 | 23,920,588 字节 | 2 | 3 |
|
||
| Typora Github | 5 | 4,680,418 字节 | 2 | 2 |
|
||
|
||
三份文件均满足:
|
||
|
||
- 内置校验器确认嵌入字体部件、关系、内容类型和字体键完整;
|
||
- Word 使用 `OpenNoRepairDialog` 正常打开并导出 PDF;
|
||
- WPS 正常打开并导出 PDF;
|
||
- 未发现方框、缺字或明显系统字体回退;
|
||
- Word 追加编辑标记后,WPS 可以读取并继续编辑;
|
||
- WPS 另存后,Word 仍可禁止修复打开;
|
||
- 最终文件同时保留 Word 和 WPS 两段编辑文本。
|
||
|
||
14 套主题完整矩阵也已通过。未声明 `docxFonts` 的其余 11 套主题继续产生
|
||
零嵌入字体,不会被共享 CSS 隐式扩大 DOCX。
|
||
|
||
## 5. 已知客户端差异
|
||
|
||
### 5.1 WPS 红头主题分页
|
||
|
||
同一份红头 DOCX 在 Word 中为 2 页,在 WPS 中为 3 页。WPS 将版记整体
|
||
移动到近空白第 3 页。字体显示正确,内容没有丢失,但尚未达到跨客户端
|
||
视觉一致门禁。
|
||
|
||
该问题进入阶段 12D-R3,由 PDF 与 DOCX 自动视觉差异引擎量化后处理。
|
||
当前不通过主题 ID 特判或缩小正文来掩盖差异,避免在没有 PDF 基线的情况
|
||
下破坏 Word 与 Chromium 的一致性。
|
||
|
||
### 5.2 WPS 互存策略
|
||
|
||
WPS 默认另存会移除嵌入字体,三份文件会缩小到约 20 KiB。设置文档属性
|
||
“嵌入 TrueType 字体”并禁用子集后可以保留字体,但 WPS 会重新生成字体
|
||
部件,互存文件增大到约 14~45 MiB。
|
||
|
||
WPS 还会重排 `styles.xml` 的 `uiPriority`,并可能删除未使用的
|
||
`numbering.xml`。Microsoft Open XML SDK 会报告 WPS 写回文件自身产生的
|
||
顺序问题,但 Word 仍能禁止修复打开。项目不会因此放宽对自身生成文件的
|
||
严格 OOXML 门禁。
|
||
|
||
## 6. 验证结果
|
||
|
||
- `npm run verify:docx-themes`:14/14 主题通过;
|
||
- `npm test`:全项目通过;
|
||
- `npm run typecheck`:全项目通过;
|
||
- `npm run build`:全项目通过;
|
||
- `git diff --check`:通过;
|
||
- Word 原始文档:3/3 禁止修复打开并导出 PDF;
|
||
- WPS 原始文档:3/3 打开并导出 PDF;
|
||
- Word → WPS → Word 双向编辑:3/3 通过;
|
||
- Word/WPS 原始渲染:共 13 页逐页检查完成。
|
||
|
||
本地验收产物位于被 Git 忽略的:
|
||
|
||
```text
|
||
output/docx-theme-matrix/
|
||
```
|
||
|
||
## 7. 下一步
|
||
|
||
阶段 12D-R3 建立 PDF 与 DOCX 自动视觉差异引擎,优先量化:
|
||
|
||
- 页面数和分页边界;
|
||
- 文本块位置和尺寸;
|
||
- 字体、字号、颜色和字重;
|
||
- 封面、版头、版记、表格及代码块边界;
|
||
- Word、WPS 与 Chromium PDF 的差异。
|
||
|
||
红头主题的 WPS 版记溢出作为首个固定回归样例。
|