/MathML2Word

如何将MATHML[MATHML(Mathematical Markup Language,MathML)是一种基于XML的标准,用来描述数学符号和公式。它的目标是把数学公式集成到万维网和其他文档中。从2015年开始,MathML成为了HTML5的一部分和ISO标准]转为Word中包含各种数学公式的文档,有些时候,我们想要将MATHML导出到Word中方便查看,我们该怎样实现呢?这个时候我们还需要了解一下微软Office的OMML(Office math markup language)标记语言,它是一种在WORD里面进行公式表达的标记语法,是以XML结构来存储的。遗憾的是,MATHML并不能直接转换为Word文档,它需要先转换为OMML

Primary LanguageC#

如何将MATHML转为Word文档

MATHML(Mathematical Markup Language,MathML)是一种基于XML的标准,用来描述数学符号和公式。它的目标是把数学公式集成到万维网和其他文档中。从2015年开始,MathML成为了HTML5的一部分和ISO标准。

由于数学符号和公式的结构复杂且符号与符号之间存在多种逻辑关系,MathML的格式十分繁琐。因此,大多数人都不会去手写MathML,而是利用其它的工具来编写,其中包括TeX到MathML的转换器。

有些时候,我们想要将MATHML导出到Word中方便查看,我们该怎样实现呢?这个时候我们还需要了解一下微软Office的OMML(Office math markup language)标记语言,它是一种在WORD里面进行公式表达的标记语法,是以XML结构来存储的。遗憾的是,MATHML并不能直接转换为Word文档,它需要先转换为OMML。

那么如何将MathML转换为OMML?答案是使用一个转换文件——MML2OMML.xsl,这个文件是office自带的,位于目录:%ProgramFiles%\Microsoft Office\Office12\之下(若你用的是office 2016,则在%ProgramFiles%\Microsoft Office\Office16\目录)。

不知你是否知道,将MathML公式以文本的形式粘贴到Word中时,它会自动变成Word公式,这个操作的背后就是MML2OMML.xsl在起作用。同样的目录下还有一个文件OMML2MML.xsl,它的作用是反过来转换,我们这里用不到。

这个式子的MATHML的XML代码如下:

<math xmlns="http://www.w3.org/1998/Math/MathML" mathvariant='italic' display='inline'>
    <msub>
        <mi>R</mi>
        <mi>i</mi>
    </msub>
    <msup>
        <mtext></mtext>
        <mi>j</mi>
    </msup>
    <msub>
        <mtext></mtext>
        <mtext>kl</mtext>
    </msub>
    <mo>=</mo>
    <msup>
        <mi>g</mi>
        <mtext>jm</mtext>
    </msup>
    <msub>
        <mi>R</mi>
        <mtext>imkl</mtext>
    </msub>
    <mo>+</mo>
    <msqrt>
        <mn>1</mn>
        <mo>-</mo>
        <msup>
            <mi>g</mi>
            <mtext>jm</mtext>
        </msup>
        <msub>
            <mi>R</mi>
            <mtext>mikl</mtext>
        </msub>
    </msqrt>
</math>

具体的实现过程参考了https://stackoverflow.com/questions/10993621/openxml-sdk-and-mathml

我们还需要由微软开发的Open-XML-SDK来提供这些操作,你可以到微软的官网下载,当然,我这里也上传到了Github上,你可以在根目录下找到该安装包文件OpenXMLSDKV25.msi

为什么需要这个Open-XML-SDK呢?,我们首先需要了解一下Office Open XML.

Office Open XML(缩写:Open XML、OpenXML或OOXML),为由Microsoft开发的一种以XML为基础并以ZIP格式压缩的电子文件规范,支持文件、表格、备忘录、幻灯片等文件格式。

OOXML在2006年12月成为了ECMA规范的一部分,编号为ECMA-376;并于2008年4月通过国际标准化组织的表决,在两个月后公布为ISO/IEC 29500国际标准。微软推出这个格式,很多人认为是商业考量。许多专家指出,该标准并不是个完整的标准,使用上困难重重。

从Microsoft Office 2007开始,Office Open XML文件格式已经成为Microsoft Office默认的文件格式。Microsoft Office 2010支持对ECMA-376标准文档的读操作,ISO/IEC 29500 Transitional的读/写,ISO/IEC 29500 Strict的读取。Microsoft Office 2013同时支持ISO/IEC 29500 Strict的读写操作。

具体的实现把MATHML转换为Word文档的代码如下:

        public static void MathML2Word()
        {
            XslCompiledTransform xslTransform = new XslCompiledTransform();
            xslTransform.Load(@"C:\Program Files (x86)\Microsoft Office\Office14\MML2OMML.xsl");

            // Load the file containing your MathML presentation markup.
            using (XmlReader reader = XmlReader.Create(File.Open("../../../test1.xml", FileMode.Open)))
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    XmlWriterSettings settings = xslTransform.OutputSettings.Clone();

                    // Configure xml writer to omit xml declaration.
                    settings.ConformanceLevel = ConformanceLevel.Fragment;
                    settings.OmitXmlDeclaration = true;
                    XmlWriter xw = XmlWriter.Create(ms, settings);
                    // Transform our MathML to OfficeMathML
                    xslTransform.Transform(reader, xw);
                    ms.Seek(0, SeekOrigin.Begin);
                    StreamReader sr = new StreamReader(ms, Encoding.UTF8);
 
                    string officeML = sr.ReadToEnd();
                    Console.Out.WriteLine(officeML);

                    // Create a OfficeMath instance from the OfficeMathML xml.
                    DocumentFormat.OpenXml.Math.OfficeMath om = new DocumentFormat.OpenXml.Math.OfficeMath(officeML);

                    //创建Word文档(Microsoft.Office.Interop.Word)  
                    Microsoft.Office.Interop.Word._Application WordApp = new Application();
                    WordApp.Visible = true;
                    using (WordprocessingDocument package = WordprocessingDocument.Create("../../../template.docx", WordprocessingDocumentType.Document))
                    {
                        // Add a new main document part. 
                        package.AddMainDocumentPart();

                        // Create the Document DOM. 
                        package.MainDocumentPart.Document =
                          new DocumentFormat.OpenXml.Wordprocessing.Document(
                            new Body(
                              new DocumentFormat.OpenXml.Wordprocessing.Paragraph(
                                new Run(
                                  new Text("  ")))));
                         
                        // Save changes to the main document part. 
                        package.MainDocumentPart.Document.Save(); 
                    }
                    
                    using (WordprocessingDocument wordDoc = WordprocessingDocument.Open("../../../template.docx", true))
                    {
                        DocumentFormat.OpenXml.Wordprocessing.Paragraph par =
                          wordDoc.MainDocumentPart.Document.Body.Descendants<DocumentFormat.OpenXml.Wordprocessing.Paragraph>().FirstOrDefault();

                        foreach (var currentRun in om.Descendants<DocumentFormat.OpenXml.Math.Run>())
                        {
                            // Add font information to every run.
                            DocumentFormat.OpenXml.Wordprocessing.RunProperties runProperties2 =
                              new DocumentFormat.OpenXml.Wordprocessing.RunProperties();
                            currentRun.InsertAt(runProperties2, 0);
                        }
                        par.Append(om);
                    }
                }
            }
        }