← 全部文档
实时 OOXML 约定

DOCX 保真度与安全

Next.js 导出端点在本地生成原生的 WordprocessingML 包。这个对照表与代码仓库中实时运行的包级保真度测试相关联;它并不是声称任意浏览器 CSS 都有一一对应的 Word 等价物。有两种结构另有专文介绍:经得起 Word 往返的公式和.docx 里面到底有什么。

经过测试的映射

方面原生 DOCX 输出
文档结构标题、段落、引用块、嵌套的项目符号列表和十进制编号列表、固定布局的表格
内联格式粗体、斜体、下划线、删除线、代码、字体和字号、文字/背景颜色、下标和上标
段落布局对齐、外边距、行高、首行缩进、page-break-before 和显式分页符
页面布局Letter 或 A4、纵向或横向、有范围限制的页边距、默认页眉和页脚、当前页/总页数域
表格精确的 DXA 网格、重复的表头行、列合并、行合并、单元格底纹和垂直对齐
审阅语义原生 DOCX 插入、删除、批注、脚注和尾注
公式LaTeX 转换为原生 OMML——分数、上下标、根式、n 元运算符、定界符、函数、极限、重音符号、横线和方框;可在 Word 的公式编辑器中编辑,而不是图片
链接和媒体HTTP/HTTPS/mailto 链接;PNG、JPEG、GIF 和 BMP data URI;由服务器解析的可信图片映射

请求约定

POST /api/export/docx
{
  "html": "<h1>Report</h1><p>Body</p>",
  "title": "Report",
  "fileName": "report.docx",
  "headerHtml": "<p>Private report</p>",
  "footerHtml": "<p>Page <span data-page-number="current"></span></p>",
  "page": { "size": "a4", "orientation": "landscape",
            "margin": { "top": 1080, "right": 900, "bottom": 1080, "left": 900 } },
  "imageMap": { "https://trusted.example/chart.png": "data:image/png;base64,..." }
}

页边距以二十分之一磅(twip)为单位,范围为 360 到 2880。请求大小上限为 1 MiB。图片映射最多接受八个 HTTP(S) 键,值只能是 data 图片,解码后总计不超过 768 KiB。

语义化 HTML 约定

<ins data-author="Ada" data-date="2026-07-16T12:00:00Z">added</ins>
<del data-author="Ada">removed</del>
<span data-comment="Check the source" data-comment-author="Reviewer">claim</span>
<sup data-footnote="Source citation"></sup>
<span data-endnote="Appendix detail"></span>
<hr class="page-break">

导入 DOCX

导入完全在浏览器中运行,不使用任何库,也不需要与服务器往返。.docx 是一个由 XML 组成的 ZIP 包:直接读取 ZIP 中央目录,由 DecompressionStream("deflate-raw") 负责解压。不会向任何地方上传任何内容。

导入的内容处理方式,以及简单的转换器会丢失什么
超链接 URLw:hyperlink 只携带一个 r:id;地址存放在 word/_rels/document.xml.rels 中。我们会解析它——否则每个链接导入后都会变成纯文本,URL 被丢弃。
图片w:drawing → a:blip → word/media/*,以 data URI 形式内联。对于图片很多的文件,可设置 documentImportImages: false 跳过图片。
编号列表与项目符号列表document.xml 以完全相同的方式存储所有列表;只有 word/numbering.xml 能区分它们。我们会读取它,因此编号列表仍然是 <ol>,而不会变成项目符号列表。
列表嵌套w:ilvl 会生成真正的嵌套,子列表放在其父级 <li> 的内部,使标记有效。
脚注和尾注Word 把注释正文保存在单独的 word/footnotes.xml 和 word/endnotes.xml 部件中;document.xml 只保存一个 id。我们会读取全部注释,并重建为实时注释——在导入的注释上方插入一条新注释,所有编号都会重新排列。这两个部件共用一种架构,但元素名称不同(w:footnote 与 w:endnote),因此为其中一种编写的解析器对另一种会悄无声息地返回空结果。Word 的分隔符条目会被跳过。
文本框文本框内容嵌套在 run 之下六层(w:pict → v:shape → v:textbox → w:txbxContent),因此只读取直接子元素的转换器会丢掉其中的每一个字。我们按文档顺序把它还原为一个 .rte-imported-textbox 块,段落和表格保持完整。mc:AlternateContent 块会携带同一个文本框两次——一个 DrawingML 版本和一个 VML 备选版本——只会输出其中一个。
修订和批注w:ins 和 w:del 会带着作者和日期导入为 <ins>/<del>——与导出时使用的约定相同,因此审阅标记可以往返。被删除的文本位于 w:delText 而不是 w:t 中。批注范围会变成 data-comment 片段,正文取自 word/comments.xml。
公式Word 把公式存储为自己 math 命名空间中的 m:oMath 树,夹在各个 run 之间——只遍历 w:r 元素的转换器永远看不到它,结果公式消失了,而它周围的句子却正常导入。我们把它转换为 LaTeX,并输出为编辑器自己的 rte-math-inline 片段,因此可以在公式对话框中编辑,并通过页面加载的任何渲染器进行渲染。分数、上下标、根式、n 元运算符(运算符是一个属性,而不是文本)、定界符、函数、极限、重音符号、横线、矩阵和对齐数组都能映射;任何无法识别的内容都会保留其字面文本,而不会消失。
SmartArtdocument.xml 中的绘图只保存关系 id;图示中的每一个字都位于另一个命名空间下的 word/diagrams/dataN.xml 中,因此寻找 a:blip 的读取程序什么也找不到,会把整个图形丢掉。布局无法在 HTML 中重现,但内容会被还原为一个 .rte-imported-smartart 大纲——导入为列表的组织结构图仍然可用;导入为空白则是一个漏洞。Word 的呈现点和连接点(pres、parTrans、sibTrans)不包含内容,会被跳过。
WMF/EMF 图片Windows 图元文件没有浏览器可以渲染的形式,因此无法内联。它们不会无声无息地消失,而是各自留下一个带有 data-source 的具名占位符,让读者知道那里原本有一张图,以及应该在原文件中查找哪一张。
字符格式颜色(w:color)、突出显示、上标/下标和字号——w:sz 以半磅为单位,因此 36 会变成 18pt 而不是 36pt。
结构和格式来自 w:pStyle 的标题、表格、段落对齐、粗体、斜体、下划线、删除线、制表符和换行。

Markdown、HTML、纯文本以及 Word 的 HTML 风格 .doc 也以同样的方式处理。对于超出这些范围的需求——或者要通过您自己的服务进行转换——请提供 config.documentImportResolver(file),它对所有格式都优先生效。

已知限制——这些是形式上的限制,而不是内容上的限制。.docx 中的任何内容现在都不会被悄悄丢弃。HTML 无法重现的部分会以可见的方式降级,并明确标示出来:文本框以内容而不是浮动框架的形式还原(浏览器 HTML 没有与 Word 的绝对页面锚定等价的机制),SmartArt 以大纲而不是排好版的图形还原,WMF/EMF 图片则以具名占位符还原。公式以 LaTeX 形式到达,页面上需要有 KaTeX 或 MathJax 才能把它们渲染为数学公式,而不是显示源代码。这些是导入方面的不足,而不是导出方面的不足——上表涵盖的是目前可以往返的内容。公式经过了真正的往返验证,而不是作为两个独立的功能分别验证:十五个公式导出为 OOXML 再重新导入后,结果逐字节相同,包括不同生成器输出的、外观完全相同的三种 Unicode 尖括号。

安全边界

转换器从不发起网络请求。img 元素中的 URL 会被忽略,除非调用方服务器在 imageMap 中提供了从该 URL 精确映射到 data URI 的条目。这样,DNS、重定向、私有地址、凭据和租户授权都被排除在转换器的信任边界之外。

有意设定的限制

  • Word 是分页的,而浏览器 HTML 是流式布局。浮动、CSS 网格、滤镜、脚本、表单、动画和任意样式都不会被转换。
  • 只提供默认的节页眉和页脚;首页/偶数页变体以及由 HTML 定义的多个节需要特定于应用的模板。
  • 自动化测试套件会检查生成的 OOXML 部件和关系。对于品牌模板,在 Microsoft Word 或 LibreOffice 中进行视觉确认仍然是发布流程中的一步。