news 2026/9/7 18:07:28

Word 文档公式转换详解:以 Docling 的 equations.docx.md 基准文件剖析 OMML 到 LaTeX 的实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Word 文档公式转换详解:以 Docling 的 equations.docx.md 基准文件剖析 OMML 到 LaTeX 的实现

Word 文档公式转换详解:以 Docling 的 equations.docx.md 基准文件剖析 OMML 到 LaTeX 的实现

【免费下载链接】doclingGet your documents ready for gen AI项目地址: https://gitcode.com/GitHub_Trending/do/docling

Docling 将 Word(.docx)文档转换为结构化 DoclingDocument 与 Markdown 时,数学公式的处理是一条独立的实现链路:Word 内部用 OMML(Office Math Markup Language)存储公式,Docling 将其逐元素转换为 LaTeX,再按“行内公式 / 独立公式”两种形态嵌入正文。本文以转换基准文件tests/data/docx/groundtruth/equations.docx.md为主体,完整解析它所覆盖的公式形态、Docling 的转换输出结构(Markdown、DoclingDocument JSON、层级树),并深入源码说明 OMML→LaTeX 的实现与测试验证方式。读完后你将能够:读懂该基准文件的每一段内容对应哪种公式场景、定位公式转换在 docling/backend/msword_backend.py 与 docling/backend/docx/latex/omml.py 中的实现位置、并知道如何运行相关测试验证转换结果。

基准文件是什么:一次 Word 公式文档转换的期望输出

equations.docx.md 是 Docling 测试数据目录中的一份 groundtruth(基准输出)。它与以下三个文件构成同一文档的四种视图:

文件作用
equations.docx转换输入源(Word 文档,公式以 OMML 形式存储)
equations.docx.md期望的 Markdown 输出(本文主体)
equations.docx.json期望的 DoclingDocument JSON 结构
equations.docx.itxt期望的文档层级树(item 列表)表示

端对端转换测试会将源文档实际转换出的 Markdown 与该基准文件逐字比对,因此这份文件本身就是“公式转换能力”的可执行验收标准。下面先完整给出基准文件内容,再逐类解析其覆盖的公式场景。

This is a word document and this is an inline equation: $A= \pi r^{2}$ . - First item with inline equation: $A= \pi r^{2}$ is the area formula. - Second item with equations: $E=mc^{2}$ and $F=ma$ are physics formulas. - The formula $a^{2}+b^{2}=c^{2}$ is the Pythagorean theorem. If instead, I want an equation by line, I can do this: $$a^{2}+b^{2}=c^{2} \times 23$$ And that is an equation by itself. Cheers! This is another equation: $$f\left(x\right)=a_{0}+\sum_{n=1}^{ \infty }\left(a_{n}\cos(\frac{n \pi x}{L})+b_{n}\sin(\frac{n \pi x}{L})\right)$$ This is text. This is text. ...(连续重复的填充段落) This is a word document and this is an inline equation: $A= \pi r^{2}$ . If instead, I want an equation by line, I can do this: $$\left(x+a\right)^{n}=\sum_{k=0}^{n}\left(\genfrac{}{}{0pt}{}{n}{k}\right)x^{k}a^{n-k}$$ And that is an equation by itself. Cheers! This is another equation: $$\left(1+x\right)^{n}=1+\frac{nx}{1!}+\frac{n\left(n-1\right)x^{2}}{2!}+ \text{ \textellipsis }$$ This is text. ...(连续重复的填充段落) This is a word document and these are inline equations: $N_{s}^{H}$ / $N_{s}^{P}$ ​. If instead, I want an equation by line, I can do this: $$e^{x}=1+\frac{x}{1!}+\frac{x^{2}}{2!}+\frac{x^{3}}{3!}+ \text{ \textellipsis } , - \infty < x < \infty$$ And that is an equation by itself. Cheers! Large operators and integrals are represented with n-ary objects in OMML XML: $$\sum_{0}^{2}x$$ $$\bigcup_{n=1}^{m}\left(X_{n} \cap Y_{n}\right)$$ $$\prod_{k=1}^{n}A_{k}$$ $$\bigwedge_{}^{}x$$ $$\int_{}^{}(2x+1)dx$$ $$\iint_{0}^{1}xdx$$ $$\iiint_{}^{}ydy$$ $$\oint_{}^{}\frac{dy}{dx}$$ $$\oiint_{0}^{2 \pi }idt$$ $$\oiiint_{C}^{}\frac{1}{z}dz$$ Operators used with limits: $$\operatorname{argmax}_{ \epsilon}f(x), \lim_{n}{\left(1+\frac{1}{n}\right)}^{n} , \max_{0 \leq x \leq 1}xe^{-x^{2}}, unsupported_{n}{\left(1+\frac{1}{n}\right)}^{n}$$ Equations with the OMML group character object: $$P_{ x}=\underbrace{S \cdot T \cdot G \cdot (x+y+z)}_{group\ with\ underbraces}+e^{x}$$ $$Q_{ y}=\overset{group\ with\ overbraces}{\overbrace{G \cdot T \cdot S \cdot (x+y+z)}}+e^{y}$$ $$s\left\{max\right\}= A \times B$$

逐类解析:这份基准文件覆盖了哪些公式场景

基准文件并非随意堆砌公式,而是按 OMML 的构造类型逐段组织,每一段对应转换链路中的一个关键能力点。

行内公式(inline equation)

第一段正文与随后三个列表项展示的是“文字与公式混排”场景:

  • 普通段落中的行内公式:This is a word document and this is an inline equation: $A= \pi r^{2}$ .
  • 列表项中的行内公式:三条-列表项分别覆盖“单公式”“同一列表项内两个公式($E=mc^{2}$$F=ma$)“公式夹在文字中间”三种情况。
  • 同一列表项内公式与文本交替出现:如第二项中Second item with equations: $E=mc^{2}$ and $F=ma$ are physics formulas.,公式之间还夹着普通文本and

值得注意的是第三处混排段落:$N_{s}^{H}$ / $N_{s}^{P}$​中两个行内公式之间用斜杠分隔,且第二个公式后保留了一个零宽字符(基准文件第 31 行),这是对“公式紧贴标点/不可见字符”这类边角输入的输出保真验证——转换结果不能吞掉也不能错误改写这些字符。

独立公式(display equation)

独立成行的公式在 Markdown 输出中统一使用$$...$$包裹。基准文件包含多组:

  • 带乘积尾项的简单式:$$a^{2}+b^{2}=c^{2} \times 23$$
  • 傅里叶级数(求和、上下限、无穷大、分数嵌套):$$f\left(x\right)=a_{0}+\sum_{n=1}^{ \infty }\left(a_{n}\cos(\frac{n \pi x}{L})+b_{n}\sin(\frac{n \pi x}{L})\right)$$
  • 二项式定理,其中组合数被转换为\genfrac{}{}{0pt}{}{n}{k}形式,说明 Docling 对 OMML 中的 binomial 构造有专门映射
  • 展开式中的\text{ \textellipsis },即省略号经 OMML 的 text 对象转出为 LaTeX 的\textellipsis

每个$$公式段落前后都有空的 text 段落占位(见基准文件第 7、10 行的空段),这一点在 equations.docx.itxt 的层级树中可以一一对应(后文展开)。

n-ary 大算子与积分

基准文件中“Large operators and integrals are represented with n-ary objects in OMML XML”一节,集中列出了 10 个 n-ary 构造:

$$\sum_{0}^{2}x$$ $$\bigcup_{n=1}^{m}\left(X_{n} \cap Y_{n}\right)$$ $$\prod_{k=1}^{n}A_{k}$$ $$\bigwedge_{}^{}x$$ $$\int_{}^{}(2x+1)dx$$ $$\iint_{0}^{1}xdx$$ $$\iiint_{}^{}ydy$$ $$\oint_{}^{}\frac{dy}{dx}$$ $$\oiint_{0}^{2 \pi }idt$$ $$\oiiint_{C}^{}\frac{1}{z}dz$$

覆盖了求和、并集、连乘、逻辑与、单/双/三重积分、闭环积分、双/三重闭环积分,并且包含上、下界留空(\int_{}^{})的情况,验证转换器对空 limits 的降级输出不会报错。

带极限的算子与“unsupported”回退

Operators used with limits一节的单行公式同时包含四种算子:

$$\operatorname{argmax}_{ \epsilon}f(x), \lim_{n}{\left(1+\frac{1}{n}\right)}^{n} , \max_{0 \leq x \leq 1}xe^{-x^{2}}, unsupported_{n}{\left(1+\frac{1}{n}\right)}^{n}$$

前三个(argmax、lim、max)是算子与下标极限组合的标准写法;第四个unsupported_{n}{...}是故意使用的未知算子名——从源码结构看,docling/backend/docx/latex/latex_dict.py 中定义了 LIM_FUNC(极限类函数)、FUNC(函数类)等算子映射表,已知算子会被赋予极限排版,而unsupported这类不在映射表中的名字走原样输出的回退路径。把这两种行为放进同一个公式,可以在一条断言里同时验证“正确映射”与“未知算子不崩溃”。

OMML group 字符对象与特殊字符

结尾两节覆盖更细的构造:

$$P_{ x}=\underbrace{S \cdot T \cdot G \cdot (x+y+z)}_{group\ with\ underbraces}+e^{x}$$ $$Q_{ y}=\overset{group\ with\ overbraces}{\overbrace{G \cdot T \cdot S \cdot (x+y+z)}}+e^{y}$$ $$s\left\{max\right\}= A \times B$$

前两个分别是\underbrace\overset{...}{\overbrace{...}}的花括号分组构造,第三个\left\{max\right\}验证反斜杠转义字符在花括号中的输出。

源码链路:OMML 是如何变成这些 LaTeX 的

基准文件里每一行$$$都来自同一条转换链路,核心代码集中在两个文件。

OMML→LaTeX 转换器:docling/backend/docx/latex/omml.py

docling/backend/docx/latex/omml.py(约 864 行)的模块 docstring 说明了它的职责:将 Word 文档中的 OMML 元素转换为 LaTeX 格式,处理分数、上下标、矩阵、极限与特殊字符;文件头部还注明其实现改编自开源的 dwml 项目(2025-01-23 引入)。几个可直接观察到的实现事实:

  • 模块以OMML_NS = "{http://schemas.openxmlformats.org/officeDocument/2006/math}"作为 OMML 命名空间常量,所有 lxml 元素查找都基于该命名空间;
  • 大量转换规则以字典常量形式放在 docling/backend/docx/latex/latex_dict.py(LIM_FUNCFUNCGROUPING_FUNCSRADPOSMATH_CHARS等),omml.py 顶部一次性导入——基准文件中lim/max/argmaxunsupported的不同输出,正是查这张表的结果;
  • 模块对pylatexenc.latexencode.UnicodeToLatexEncoder做了带保护的可选导入(try: import ... except ImportError: pass,并注释说明该依赖由MsWordDocumentBackend.__init__统一保证),用于把公式中的 Unicode 数学字符编码为 LaTeX。

段落内公式混排:msword_backend.py 的 _handle_equations_in_text

docling/backend/msword_backend.py 是 .docx 后端主体(约 3794 行),公式相关的关键方法:

  • _handle_equations_in_text(约 L1925):遍历段落元素,把普通文本片段与公式分别收集,公式以带标记的形式(<eq>...</eq>)进入only_equations列表并插入原文本的对应位置,返回(output_text, only_equations)。方法内有一道防护:若解析得到的公式/文本子串与原段文本无法对齐(数量或内容不一致),会记录日志并放弃公式解析、原样返回原段落文本——宁可输出纯文本也不输出错位的公式。这正是基准文件中混排段落能逐字比对的前提。
  • _add_inline_equations_to_parent(约 L2410):把“文本 + 行内公式”作为子项挂到父元素下,注释明确说明它同时服务于普通段落与列表项两条路径;
  • _add_list_item_with_equations(约 L2639):专门处理含行内公式的列表项,与_handle_text_elements中“若列表项检测到len(equations) > 0则走特殊分支”的调用点对应——这就是基准文件三条-列表项在输出中仍保留列表语义、且公式以$...$嵌在项内文字中间的原因。
  • 表格单元格内同样会调用_handle_equations_in_text(约 L2793-L2797),表格公式的覆盖由table_with_equations.docx姊妹样本单独验证(见文末扩展阅读)。

输出结构印证:JSON 与层级树里的 FormulaItem

基准 Markdown 只是同一转换结果的投影,结构化的真相在 equations.docx.json 与 equations.docx.itxt 中。

DoclingDocument JSON

该 JSON 顶部声明:

{ "schema_name": "DoclingDocument", "version": "1.10.0", "name": "equations", "origin": { "mimetype": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "binary_hash": 14529667209329784536, "filename": "equations.docx" }, ... }

origin记录了输入文件类型与内容哈希;body.children通过$ref(如#/groups/0#/texts/17)引用正文元素。含行内公式的段落在结构中呈现为 group(inline 容器)挂多个子项(text 与 formula 交替),独立公式段落则直接作为顶层 formula 元素出现在 body 子项序列中。

层级树 .itxt 的逐项对应

equations.docx.itxt 用缩进树呈现了同一结构,例如基准文件开头的混排段落在树中是:

item-0 at level 0: unspecified: group _root_ item-1 at level 1: inline: group group item-2 at level 2: text: This is a word document and this is an inline equation: item-3 at level 2: formula: A= \pi r^{2} item-4 at level 2: text: . item-5 at level 1: list: group list item-6 at level 2: list_item: item-7 at level 3: inline: group group item-8 at level 4: text: First item with inline equation: item-9 at level 4: formula: A= \pi r^{2} item-10 at level 4: text: is the area formula.

可以清楚看到:含公式的段落被包装为inline: group,其下textformula子项交替排列;列表项为list -> list_item -> inline: group的三层嵌套。独立公式则直接挂在一级,如item-25 at level 1: formula: a^{2}+b^{2}=c^{2} \times 23。另外,.itxt 对超长公式行会做省略显示(如f\left(x\right)=a_{0}+\sum_{n=1} ... })+...一行),这是树视图的展示截断,完整公式仍以 JSON 与 Markdown 输出为准。

验证与复现:如何确认这份基准仍然成立

端到端测试比对

tests/test_backend_msword.py 中的test_e2e_docx_conversions(L100) 是核心验收用例:它对tests/data/docx/sources/下的 docx 样本执行转换,并将生成的 Markdown 与groundtruth/目录中同名.md基准逐一比对——equations.docx在其中。公式输出的任何一个字符变化(例如\bigwedge写成\bigwedge_{}^{}x的变体)都会使该测试失败。

公式混排逻辑的单元测试

同一测试文件中还有针对混排解析的定向用例:

  • test_handle_equations_in_text_returns_original_text_on_mismatch(L1022):当解析子串与原段文本不匹配时,方法必须返回原始文本、equations为空;
  • test_handle_equations_in_text_skips_empty_substrings(L1039):空子串应被跳过而公式仍被收录;
  • test_handle_text_elements_inline_equations_stop_when_text_is_consumed(L1110):文本被公式标记消费完毕后,循环应正确终止。

在仓库根目录下安装好开发依赖后运行:

pytest tests/test_backend_msword.py -k equations

即可只跑与公式相关的用例;跑全文件则覆盖列表、表格、代码块、页眉页脚等其他 docx 场景(同一文件约 1642 行、50+ 个测试函数)。

手动复现转换结果

不想跑测试时,可以直接对源文档做转换并查看输出:

docling convert tests/data/docx/sources/equations.docx

或使用 Python API(参考 docs/examples/minimal.py 的最简用法):

from docling.document_converter import DocumentConverter converter = DocumentConverter() result = converter.convert("tests/data/docx/sources/equations.docx") print(result.document.export_to_markdown())

把打印出的 Markdown 与 equations.docx.md 对照,即可手工验证本文解析的全部公式形态;调用result.document的 JSON 导出则可对照 equations.docx.json 检查 FormulaItem 的层级。

相关公式样本:这套机制的更宽覆盖

tests/data/docx/下还有一组聚焦 OMML 细节的姊妹样本,与equations.docx共同构成公式转换的完整测试面:

样本(sources 目录下)针对的 OMML 场景
omml_frac_superscript.docx分数与上下标组合构造
omml_func_log.docx函数类算子(log 等)映射
omml_multi_equation_paragraph.docx同一自然段内多段公式
omml_text_escapes_in_math.docx公式内 text 对象的转义字符
table_with_equations.docx表格单元格内的公式(对应 msword_backend.py 的单元格分支)

每个样本同样配有.md/.json/.itxt三种 groundtruth,验证方式与本文完全一致。

小结

equations.docx.md这份看似简单的基准文件,实际是 Docling “Word 公式 → LaTeX/Markdown” 转换能力的浓缩验收清单:行内与独立公式、n-ary 大算子与积分、极限算子及未知算子回退、花括号分组与特殊字符,全部以可逐字断言的形式固定下来。其背后的实现分两层——omml.py 负责 OMML 元素到 LaTeX 字符串的元素级翻译(映射表在 latex_dict.py),msword_backend.py 负责把翻译结果按段落、列表项、表格单元格的上下文安全地装配回文档结构,并在文本对齐失败时降级为纯文本输出。理解了这一文件与这条链路,你就掌握了在 Docling 中排查任何 docx 公式转换问题的完整路径:先看基准文件确定期望行为,再沿_handle_equations_in_text与 omml 转换器定位偏差。

【免费下载链接】doclingGet your documents ready for gen AI项目地址: https://gitcode.com/GitHub_Trending/do/docling

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 18:05:49

蓝牙音箱PCBA开发周期:揭秘“7天出样”背后的三大隐形耗时坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 18:03:30

Oracle表闪回(Flashback Table)原理、实战操作与常见错误排查

先唠个嗑。干过几年Oracle DBA的&#xff0c;谁手里还没几桩“手滑惨案”&#xff1f;UPDATE忘记带WHERE、 DELETE删错了条件、 TRUNCATE完发现要的是另一张表——那一瞬间的心跳骤停&#xff0c;我太熟了。别问我是怎么知道的&#xff0c;问就是曾在大半夜用表闪回救过一个差点…

作者头像 李华
网站建设 2026/9/7 18:01:06

12V逆变器开机报故障?从工作原理到元件级排查与修复全指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 18:00:44

单片机毕设项目:基于 STM32 的室内空气质量预警及蓝牙管控系统设计 基于 STM32 的多源环境数据监测与阈值联动系统设计(010307)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机&#xff0c;Java、小程序技术领域和毕业项目实战 ✌️…

作者头像 李华