Search code examples
openxmldocxequationmathml

OpenXML SDK and MathML


I use MathML to create some data blocks and I need to insert it throught OpenXML SDK into docx file. I've heard it is possible, but I didn't manage it. Could somebody help me with this problem?


Solution

  • As far as I know, the OpenXml SDK does not support presentation MathML out of the box.

    Instead, the OpenXml SDK supports Office MathML. So, to insert presentation MathML into a word document we first have to transform the presentation MathML into Office MathML.

    Fortunately, Microsoft provides a XSL file (called MML2OMML.xsl) to transform presentation MathML into Office MathML. The file MML2OMML.xsl is located under %ProgramFiles%\Microsoft Office\Office12. In conjunction with the .Net Framework class XslCompiledTransform we are able to transform presentation MathML into Office MathML.

    The next step is to create a OfficeMath object from the transformed MathML. The OfficeMath class represents a run containing WordprocessingML which shall be handled as though it was Office Open XML Math. For more info please refer to MSDN.

    The presentation MathML does not contain font information. To get a nice result we must add font information to the created OfficeMath object.

    In the last step we have to add the OfficeMath object to our word document. In the example below I simply search for the first Paragraph in a word document called template.docx and add the OfficeMath object to the found paragraph.

    XslCompiledTransform xslTransform = new XslCompiledTransform();
    
    // The MML2OMML.xsl file is located under 
    // %ProgramFiles%\Microsoft Office\Office12\
    xslTransform.Load("MML2OMML.xsl");
    
    // Load the file containing your MathML presentation markup.
    using (XmlReader reader = XmlReader.Create(File.Open("mathML.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);
    
        // Add the OfficeMath instance to our 
        // word template.
        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();
    
            RunFonts runFonts2 = new RunFonts() { Ascii = "Cambria Math", HighAnsi = "Cambria Math" };        
            runProperties2.Append(runFonts2);
    
            currentRun.InsertAt(runProperties2, 0);
          }
    
          par.Append(om);
        }
      }
    }