PAYATHON 2026

如何将大型 MATLAB 符号表达式自动插入为 Word 原生公式?

技术老齐

结论

可以自动完成。建议采用以下处理流程:

MATLAB symbolic expression
        ↓ mathml()
Presentation MathML
        ↓ MML2OMML.XSL
Office Math Markup Language(OMML)
        ↓ Word COM Range.InsertXML
Word 原生公式

不要把 mathml() 的返回值直接作为文本粘贴到 Word。Word 文档使用 OMML 保存原生公式,而非普通 MathML。需要先用 Microsoft Office 附带的 MML2OMML.XSL 将 MathML 转换为 OMML,再通过 Word COM 的 Range.InsertXML 插入文档。

整个过程不使用剪贴板,也不依赖浏览器或 HTML 页面,适合批量处理大型表达式。但它并非完全不受长度限制。单个公式过于复杂时,仍会受到 Word 公式排版性能、内存和页面宽度的影响。

为什么 mathml() 的结果会变成纯文本

MathML 和 OMML 是两种不同的格式:

  • MathML 是通用的数学标记语言。
  • OMML 是 Microsoft Word 在 .docx 中使用的原生公式格式。

把 XML 字符串粘贴到 Word 时,Word 是否将其识别为公式,取决于剪贴板携带的数据格式和粘贴上下文。如果剪贴板中只有纯文本,Word 通常会直接插入 XML 文本。

从浏览器或 MathJax 复制公式有时能够成功,是因为浏览器可能同时向剪贴板写入 HTML、MathML 等格式,Word 会从中选择可以转换的内容。这种方式容易受到浏览器、剪贴板容量和 MathJax 输出方式的影响,不适合作为稳定的自动化接口。

使用 COM 的 Range.InsertXML,可以直接插入转换后的 OMML,完全绕过剪贴板。

准备工作

使用该方案需要满足以下条件:

  1. MATLAB 在 Windows 上运行。
  2. 计算机已安装桌面版 Microsoft Word。
  3. MATLAB 可以通过 actxserver 使用 ActiveX/COM。
  4. 能够找到 Office 安装目录中的 MML2OMML.XSL。

该文件常见的安装位置如下:

C:\Program Files\Microsoft Office\root\Office16\MML2OMML.XSL

实际路径会随 Office 的安装方式和目录结构而变化,请以本机文件为准。可以使用 PowerShell 查找:

Get-ChildItem "C:\Program Files\Microsoft Office" -Filter MML2OMML.XSL -Recurse -ErrorAction SilentlyContinue

如果安装的是 32 位 Office,还可以检查:

Get-ChildItem "C:\Program Files (x86)\Microsoft Office" -Filter MML2OMML.XSL -Recurse -ErrorAction SilentlyContinue

完整 MATLAB 示例

以下代码接收一组符号表达式,调用 mathml() 生成 MathML,再用 Office 的 XSLT 转换为 OMML,最后创建 Word 文档并插入原生公式。

function symbolic_to_word(expressions, output_file, xsl_file)
%SYMBOLIC_TO_WORD Insert symbolic expressions as native Word equations.
%
% expressions : cell array containing symbolic expressions
% output_file : output .docx path
% xsl_file    : full path to MML2OMML.XSL

    arguments
        expressions cell
        output_file char
        xsl_file char
    end

    if ~isfile(xsl_file)
        error('MML2OMML.XSL was not found: %s', xsl_file);
    end

    output_file = char(java.io.File(output_file).getCanonicalPath());

    word = [];
    doc = [];

    try
        word = actxserver('Word.Application');
        word.Visible = true;

        doc = word.Documents.Add();

        for k = 1:numel(expressions)
            expr = expressions{k};

            % MATLAB symbolic expression -> MathML
            mml = mathml(expr);

            % MathML -> Word OMML
            omml = mathml_to_omml(mml, xsl_file);

            % Insert immediately before Word's final paragraph mark
            pos = doc.Content.End - 1;
            rng = doc.Range(pos, pos);
            rng.InsertXML(omml);

            % Start a new paragraph for the next formula
            if k < numel(expressions)
                pos = doc.Content.End - 1;
                rng = doc.Range(pos, pos);
                rng.InsertAfter(char(13));
            end
        end

        % 16 = wdFormatDocumentDefault (.docx)
        doc.SaveAs2(output_file, 16);
        doc.Close(0);
        word.Quit();

        delete(word);
        fprintf('Saved Word document: %s\n', output_file);

    catch ME
        if ~isempty(doc)
            try
                doc.Close(0);
            catch
            end
        end

        if ~isempty(word)
            try
                word.Quit();
            catch
            end
            try
                delete(word);
            catch
            end
        end

        rethrow(ME);
    end
end


function omml = mathml_to_omml(mml, xsl_file)
%MATHML_TO_OMML Convert MathML to Office Math Markup Language.

    source = actxserver('Msxml2.DOMDocument.6.0');
    style  = actxserver('Msxml2.DOMDocument.6.0');

    source.async = false;
    source.validateOnParse = false;
    source.resolveExternals = false;

    style.async = false;
    style.validateOnParse = false;
    style.resolveExternals = false;

    cleanupObj = onCleanup(@() release_dom_objects(source, style));

    if ~source.loadXML(mml)
        reason = source.parseError.reason;
        error('Unable to parse MathML: %s', reason);
    end

    if ~style.load(xsl_file)
        reason = style.parseError.reason;
        error('Unable to load MML2OMML.XSL: %s', reason);
    end

    omml = source.transformNode(style);

    % InsertXML needs the XML element, not an XML declaration.
    omml = regexprep(omml, ...
        '^\s*<\?xml[^>]*\?>\s*', '', 'once');
end


function release_dom_objects(source, style)
    try
        delete(source);
    catch
    end

    try
        delete(style);
    catch
    end
end

调用示例:

clear;
clc;

syms epsilon_i y_i t q K_0 K_1
syms phi_i(y_i) Gamma(q)

expr1 = epsilon_i == phi_i(y_i);
expr2 = diff(epsilon_i, t) + K_1 * epsilon_i;
expr3 = diff(epsilon_i, t) ...
      + K_1 * epsilon_i ...
      + K_0 * int(epsilon_i, t) ...
      + Gamma(q);

expressions = {
    expr1
    expr2
    expr3
};

xsl_file = ...
    'C:\Program Files\Microsoft Office\root\Office16\MML2OMML.XSL';

output_file = fullfile(pwd, 'symbolic_equations.docx');

symbolic_to_word(expressions, output_file, xsl_file);

插入后的内容会作为 Word 原生公式对象保存,可以继续在公式编辑器中修改,不会变成图片或普通 XML 文本。

如果只有 LaTeX 字符串

如果输入来自 latex(expr),可以尝试让 Word 直接解析线性公式:

word = actxserver('Word.Application');
word.Visible = true;

doc = word.Documents.Add();

latex_text = '\frac{x^2+1}{x-1}';

pos = doc.Content.End - 1;
rng = doc.Range(pos, pos);
rng.Text = latex_text;

equation = doc.OMaths.Add(rng);
equation.BuildUp();

doc.SaveAs2(fullfile(pwd, 'latex_equation.docx'), 16);

这种方式步骤更少,但可靠性不如转换为 OMML,主要有以下限制:

  • Word 版本和公式输入设置会影响 LaTeX 解析。
  • Word 只支持其公式编辑器能够接受的 LaTeX 子集。
  • equation、align 等文档级环境不一定能直接解析。
  • MATLAB latex() 的输出不保证完全符合 Word 的 LaTeX 输入语法。
  • 多行公式、矩阵和复杂定界符更容易遇到兼容问题。

简单表达式可以使用 OMaths.Add 和 BuildUp。批量处理复杂的 MATLAB 符号表达式时,建议使用 mathml()、MML2OMML.XSL 和 InsertXML。

处理超大型表达式时的建议

能够自动插入公式,不代表 Word 可以高效排版任意大小的单个公式。表达式过大时,Word 可能响应缓慢,公式也可能超出页面宽度,编辑时还会占用大量内存。

遇到这类问题,应从 MATLAB 的表达式树中拆分公式,不要直接截断 LaTeX 或 MathML 字符串。例如,可以按等号、加法项、矩阵行或推导步骤拆成多个完整表达式,再分别转换为 OMML。在字符串中间随意截断,可能破坏 XML、括号配对或运算符结构。

还可以采用以下方式:

  • 将中间子表达式定义为单独变量。
  • 把长等式拆成连续的推导步骤。
  • 将大型矩阵按行或分块插入。
  • 每处理一批公式便保存一次文档。
  • 每个公式使用独立段落,减少公式对象之间的排版影响。

其他注意事项

  • actxserver('Word.Application') 仅适用于 Windows 桌面版 Word,不能直接用于 macOS、Linux 或 Word Online。
  • 不要让 MSXML 执行外部来源提供的任意 XSLT 文件。此处应使用本机 Office 安装附带的 MML2OMML.XSL。
  • 如果 loadXML 报错,请先检查 mathml() 输出是否完整,以及其中是否存在无法解析的实体或损坏的 XML。
  • Range.InsertXML 失败,通常是因为转换结果不是有效的 OMML、缺少命名空间,或者插入位置不适合放置公式。
  • 多行对齐公式需要在 MathML 或 OMML 中表示为表格结构。直接把 align 环境文本交给 Word,无法保证得到相同的对齐效果。
  • 在没有 Word 的环境中,也可以直接创建 .docx 包,并向 word/document.xml 写入 OMML。但这种方法还需要正确维护命名空间、关系文件和包结构,开发成本通常高于 COM 方案。

备注:内容仅供参考。