Skip to content

Insert MathML as plain text for Word 2010 to render it as an equation #320

Open

Description

@peepall

Hi everybody,

I am trying to insert some MathML code into a *.docx document. To do so, I do the following:

#!/bin/env python
# -*- coding: utf8 -*-

from docx import Document


doc = Document('template.docx')

# Word needs a specific XML namespace in the '<math>' tag in order to embed MathML text into a math area
# Also, the MathML expression must contain no newline or Word will create one distinct math area for each line
math_ml = '<math xmlns="http://www.w3.org/1998/Math/MathML"><mfrac><mn>1</mn><mn>2</mn></mfrac></math>'

# Insert the MathML text into Word
doc.add_paragraph().add_run(text=math_ml, style=None).font.math = True
doc.save('test.docx')

When I open the resulting document in Word 2010, this is what I see.
The math area has been correctly created, but the code itself has not been rendered.

The interesting thing is when I open the resulting document and then copy then paste the MathML code from/to the math area using "Paste as plain text" directly from Word UI, it gets properly rendered.

I have come to the conclusion that when a style is applied to the MathML text, then Word does not render it as an equation. And this assertion is verified by the fact that when you use another paste option like "Keep source formatting" or "Melt both formats", then the equation is not rendered using Word UI.

My question is: how can I insert the MathML text as plain text into Word without any style using python-docx lib ?
I tried those, but none of them worked:

doc.add_paragraph().add_run(text=math_ml, style=None).font.math = True
doc.add_paragraph().add_run(text=math_ml, style='').font.math = True
doc.add_paragraph().add_run(text=math_ml).font.math = True

Or maybe this is currently not supported by this library ?

Thanks for your help :)

Activity

  1. scizers commented on Mar 21, 2017

    @scizers

    Hello @peepall,

    Any success with inserting malthml ?

  2. peepall commented on Mar 21, 2017

    @peepall
    Author

    Hi @scizers ,

    I've managed to make it work. However, you must use the MathML to Office MathML stylesheet in order to transform the MathML data into Office-compatible MathML.

    The stylesheet is installed along with Microsoft Office and you can find it in:
    C:\Program Files (x86)\Microsoft Office\Office14\MML2OMML.XSL.

    #!/bin/env python
    # coding: utf-8
    
    from lxml import etree
    
    # Convert MathML (MML) into Office MathML (OMML) using a XSLT stylesheet
    tree = etree.fromstring(mathml_string)
    xslt = etree.parse(mml2omml_stylesheet_path)
    
    transform = etree.XSLT(xslt)
    new_dom = transform(tree)
    
    paragraph = doc.add_paragraph()
    paragraph._element.append(new_dom.getroot())
    

    You may also need to remove any namespace from the <math> tag before transforming it into Office MathML. Let me know if it works for you too.

  3. scizers commented on Mar 22, 2017

    @scizers

    Thanks @peepall,

    Worked like a charm !

  4. madphysicist commented on Nov 7, 2017

    @madphysicist

    @peepall It has been a while since you made this post, but I was hoping you could help me out. I am working with the following exact snippet:

    from docx import Document
    from lxml import etree
    
    mathml_string = '<math xmlns="http://www.w3.org/1998/Math/MathML"><mfrac><mn>1</mn><mn>2</mn></mfrac></math>'  # From the first post
    
    # Convert MathML (MML) into Office MathML (OMML) using a XSLT stylesheet
    tree = etree.fromstring(mathml_string)
    xslt = etree.parse(mml2omml_stylesheet_path)
    
    transform = etree.XSLT(xslt)
    new_dom = transform(tree)
    
    doc = Document()
    paragraph = doc.add_paragraph()
    paragraph._element.append(new_dom.getroot())
    doc.save('test.docx')
    

    The issue is that new_dom appears to be an empty result and so new_dom.getroot() returns None. The actual error is from append:

    TypeError: Argument 'element' has incorrect type (expected lxml.etree._Element, got NoneType)
    

    This is probably related to the style sheet I am using. I obtained my copy of mml2omml.xsl from https://github.com/MartinPaulEve/meTypeset/blob/af0b1ad33a3280150a53ba20afe97285121d1300/docx/utils/maths/mml2omml.xsl (this is the current master branch, I just wanted to fix it to a specific commit for stability in future referencing).

    I also tried to remove the xmlns like this: mathml_string = '<math><mfrac><mn>1</mn><mn>2</mn></mfrac></math>'.

    What am I doing wrong and how do I fix it?

  5. peepall commented on Nov 8, 2017

    @peepall
    Author

    Hi @madphysicist

    You will find the MML2OMML.XSL file from Office 14 (2010) below. The content of the file you are using is different and may cause an issue during data transformation.

    MML2OMML.zip

    Tell me if this solves your issue.
    Regards

  6. madphysicist commented on Nov 8, 2017

    @madphysicist

    @peepall It certainly did. Many thanks!

  7. requeijaum commented on Mar 14, 2018

    @requeijaum

    👀

  8. Daniel-Quistan commented on Jan 22, 2019

    @Daniel-Quistan

    Hello!

    Using the code provided by @peepall and some sympy(https://docs.sympy.org) funcionalities I made a simple function that makes it easier to output equations to word! Here it is:

    from lxml import etree
    from sympy.printing.mathml import mathml
    from sympy import *
    
    
    
    def math_to_word(eq, equal=None):
        """Transform a sympy equation to be printed in word document."""
        # Mathml uses sympy to transform equations to mathml format
        # eq = equation you want to add to Word using sympy format
        # The math_to_wrod functions also have an optitional equal argument
        # that prints an = sign and the number passed to the argument
    
        math_ml = mathml(eq, printer='presentation')
        # Creates mathml string
        if equal is None:
            mathml_string = '''
                <math xmlns="http://www.w3.org/1998/Math/MathML">
                    {}
                </math>
                '''.format(math_ml)
        else:
            mathml_string = '''
                <math xmlns="http://www.w3.org/1998/Math/MathML">
                    {0}
                    <mo>=</mo>
                    <mn>{1}</mn>
                 </math>
                '''.format(math_ml, equal)
        # Converts mathml string
        tree = etree.fromstring(mathml_string)
        xslt = etree.parse(
            mml2omml_stylesheet_path
            )
        transform = etree.XSLT(xslt)
        new_dom = transform(tree)
        return new_dom.getroot()
    

    A example of usage within a docx script is:

    from docx import Document
    from math_to_word import math_to_word
    from sympy.abc import x
    
    doc = Document()
    
    p = doc.add_paragraph()
    p._element.append(math_to_word(x+1, equal=10))
    
    doc.save('test1.docx')
    

    Hope it will be useful! Thanks @peepall

  9. kencomputes commented on Jul 11, 2019

    @kencomputes

    @peepall Thanks for providing this excellent solution! As others have stated, it works like a charm.

    I'm trying to perform a similar operation using python-pptx. I created the following as a test, and while it runs successfully, my resulting output doesn't contain any equation.

    from pptx import Presentation
    from pptx.util import Inches, Pt
    from lxml import etree
    
    prs = Presentation()
    blank_slide_layout = prs.slide_layouts[6]
    slide = prs.slides.add_slide(blank_slide_layout)
    
    left = top = width = height = Inches(1)
    txBox = slide.shapes.add_textbox(left, top, width, height)
    tf = txBox.text_frame
    
    tree = etree.fromstring('<math xmlns="http://www.w3.org/1998/Math/MathML"><mi>x</mi><mo>=</mo><mstyle displaystyle="true"><mfrac><mrow><mrow><mo>&#8722;</mo><mi>b</mi></mrow><mo>&#177;</mo><msqrt><msup><mi>b</mi><mn>2</mn></msup><mo>&#8722;</mo><mrow><mn>4</mn><mo>&#8290;</mo><mi>a</mi><mo>&#8290;</mo><mi>c</mi></mrow></msqrt></mrow><mrow><mn>2</mn><mo>&#8290;</mo><mi>a</mi></mrow></mfrac></mstyle></math>')
    xslt = etree.parse('C:/Program Files/Microsoft Office/root/Office16/MML2OMML.XSL')
    
    transform = etree.XSLT(xslt)
    new_dom = transform(tree)
    
    p = tf.add_paragraph()
    p._element.append(new_dom.getroot())
    
    prs.save('testDoc.pptx')
    

    @peepall Do you have any sage advice regarding performing this operation in python-pptx?

  10. kencomputes commented on Jul 15, 2019

    @kencomputes

    If anyone on a similar mission comes across this post, here's how I got things working.

    I was able to append the math element with the following code:

    from pptx import Presentation
    from pptx.util import Inches, Pt
    from lxml import etree
    
    prs = Presentation()
    blank_slide_layout = prs.slide_layouts[6]
    slide = prs.slides.add_slide(blank_slide_layout)
    
    left = top = width = height = Inches(1)
    txBox = slide.shapes.add_textbox(left, top, width, height)
    tf = txBox.text_frame
    
    # Convert MathML (MML) into Office MathML (OMML) using a XSLT stylesheet
    tree = etree.fromstring(
        "<math xmlns="http://www.w3.org/1998/Math/MathML">"
        "  <mi>x</mi>"
        "  <mo>=</mo>"
        "  <mstyle displaystyle="true">"
        "    <mfrac>"
        "      <mrow>"
        "        <mrow>"
        "          <mo>&#8722;</mo>"
        "          <mi>b</mi>"
        "        </mrow>"
        "        <mo>&#177;</mo>"
        "        <msqrt>"
        "          <msup>"
        "            <mi>b</mi>"
        "            <mn>2</mn>"
        "          </msup>"
        "          <mo>&#8722;</mo>"
        "          <mrow>"
        "            <mn>4</mn>"
        "            <mo>&#8290;</mo>"
        "            <mi>a</mi>"
        "            <mo>&#8290;</mo>"
        "            <mi>c</mi>"
        "          </mrow>"
        "        </msqrt>"
        "      </mrow>"
        "      <mrow>"
        "        <mn>2</mn>"
        "        <mo>&#8290;</mo>"
        "        <mi>a</mi>"
        "      </mrow>"
        "    </mfrac>"
        "  </mstyle>"
        "</math>')"
    )
    xslt = etree.parse('C:/Program Files/Microsoft Office/root/Office16/MML2OMML.XSL')
    
    wrapper = etree.fromstring(
        "<a14:m xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main">"
        "  <m:oMathPara xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">" 
        "  </m:oMathPara>"
        "</a14:m>"
    )
    
    transform = etree.XSLT(xslt)
    new_dom = transform(tree)
    
    wrapper.getchildren()[0].append(new_dom.getroot())
    
    p = tf.add_paragraph()
    p._element.append(wrapper)
    
    prs.save('testDoc.pptx')

    If there are text nodes to be inserted before/after the math content that can be accomplished like this:

    textWrapOpen = '<a:r xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:t>'
    textWrapClose = '</a:t></a:r>'
    string = "Some string "
    textTree = etree.fromstring(textWrapOpen + string + textWrapClose)
    p._element.append(textTree)

    I'm still working out some kinks, but this should be a good starting point for whoever might be attempting this in the future.

  11. Vaclav-Laurin commented on Jul 23, 2020

    @Vaclav-Laurin

    Hi @scizers ,

    I've managed to make it work. However, you must use the MathML to Office MathML stylesheet in order to transform the MathML data into Office-compatible MathML.

    The stylesheet is installed along with Microsoft Office and you can find it in:
    C:\Program Files (x86)\Microsoft Office\Office14\MML2OMML.XSL.

    #!/bin/env python
    # coding: utf-8
    
    from lxml import etree
    
    # Convert MathML (MML) into Office MathML (OMML) using a XSLT stylesheet
    tree = etree.fromstring(mathml_string)
    xslt = etree.parse(mml2omml_stylesheet_path)
    
    transform = etree.XSLT(xslt)
    new_dom = transform(tree)
    
    paragraph = doc.add_paragraph()
    paragraph._element.append(new_dom.getroot())
    

    You may also need to remove any namespace from the <math> tag before transforming it into Office MathML. Let me know if it works for you too.

    Hi!

    I'm also working on this issue and thanks to all you I got it, but there is another problem now!
    I can't increase font size of the MathML equations (that inserted in MS Word by Python-docx).
    Standart MathML attributes like mathsize or maxsize or scriptminsize don't work.
    There are no any problems with the running of code but MathML equation has CambriaMath font with font size 11 (I need to make it 14).
    Any others attributes for setup the math font (like fontweight or fontcolor others) don't work.

    What I do wrong?

    Here is an example of code that I used:

        import docx
        from sympy import*
        from sympy.abc import*
        from sympy.printing.mathml import mathml
        from lxml import etree    
        
        math_ml = '''
        <math xmlns="http://www.w3.org/1998/Math/MathML" mathsize='50pt' fontweight='bold'>
            <mstyle>
            <msub>
                <mi>t</mi>
                <mi>Н-Р</mi>
            </msub>
            <mi>=</mi>
            <mfrac>
            <msrow>
                <msub>
                    <mi>t</mi>
                    <mi>Н</mi>
                    </msub>
                    <mi>+</mi>
                    <msub>
                        <mi>t</mi>
                        <mi>Р</mi>
                    </msub>
                    <mi>+</mi>
                    <msub>
                        <mi>t</mi>
                        <mi>Д</mi>
                    </msub>
                </msrow>
                <mn>60</mn>
            </mfrac>
            </mstyle>
        </math>'''
    
        tree = etree.fromstring(math_ml)
        mml2omml_stylesheet_path = 'MML2OMML.XSL'
        xslt = etree.parse(mml2omml_stylesheet_path)
        transform = etree.XSLT(xslt)
        new_dom = transform(tree)
    
        doc = docx.Document ()
        p = doc.add_paragraph()
        p._element.append(new_dom.getroot())
    
        report.save('Report.docx')```
    
    
  12. zimakim commented on Mar 13, 2021

    @zimakim

    I solved this issue with the latex2mathml module. Please buy coffee for the man who wrote it

    from docx import Document
    from lxml import etree
    import latex2mathml.converter
    
    
    def latex_to_word(latex_input):
        mathml = latex2mathml.converter.convert(latex_input)
        tree = etree.fromstring(mathml)
        xslt = etree.parse(
            'MML2OMML.XSL'
            )
        transform = etree.XSLT(xslt)
        new_dom = transform(tree)
        return new_dom.getroot()
    
    
    document = Document()
    
    p = document.add_paragraph()
    word_math = latex_to_word(ur"\sum_{i=1}^{10}{\frac{\sigma_{zp,i}}{E_i} kN")
    p._element.append(word_math)
    
    p = document.add_paragraph()
    p.add_run('before formula ')
    p._element.append(word_math)
    p.add_run(' after formula ')
    
    p = document.add_paragraph()
    word_math = latex_to_word(ur"\sum_{n=1}^{10}{n^{2}}")
    p._element.append(word_math)
    
    p = document.add_paragraph()
    word_math = latex_to_word(ur"\sqrt{\frac{a}{b}}")
    p._element.append(word_math)
    
    
    p = document.add_paragraph()
    word_math = latex_to_word(ur"\begin{matrix*}[r]a & b \\ c & d \end{matrix*}")
    p._element.append(word_math)
    
    document.save('demo.docx')
  13. scanny commented on Mar 13, 2021

    @scanny
    Contributor

    Nice job Akim :)

  14. UHSA94 commented on Apr 26, 2021

    @UHSA94

    20210426114121
    20210426114205
    thanks,now i can add a math equation to docx , i can also set text's font and color,but how could i set font size and color for a math equation?

  15. AsadAmeer7 commented on Feb 18, 2022

    @AsadAmeer7

    Hi everybody,

    I am trying to read equation as linear text form(latex) and images from word document in python script using python-docx

    anyone here have any idea on it?

  16. retsyo commented on Apr 8, 2022

    @retsyo
  17. retsyo commented on Apr 8, 2022

    @retsyo

    some, for example \sum, \int and \prod, has flaw

    roniemartinez/latex2mathml#288

  18. fathoni commented on Aug 26, 2022

    @fathoni
    MML2OMML.XSL

    Great solution, thanks! The only question is now about the XSL file - I could not manage to find the redistribution policy for this file or any other Office-included XSL file. Does anyone know about open-licensed version of MML2OMML.XSL - or the redistribution policy of it?

  19. shashikulk commented on Mar 11, 2023

    @shashikulk

    Hi all, need some pointers with issues I am facing while trying to write math expressions/equations with a mix of text to Word document using solution approach given in zimakim's post (dated 14th March 2021). I am using latex2mathml and p._element.append(). The issues I am facing in the output are -

    1. The output is not wrapping to next line, but going beyond document page boundary
    2. The output is center justified, whereas I need it left aligned
    3. The output in one case is not inline and breaking to next line at "=" sign. This behavior is also not as seen in issue 1 above.

    I am not able to figure out the root cause of these issues. I am not able to locate much documentation on _element and how to use it in Python programs. I have attached the Word document output that is generated by my program. Any help to resolve these issues is highly appreciated.

    Trial_Word_Output.docx

  20. Gu-f commented on Sep 10, 2024

    @Gu-f

    Another alternative: LatexToWord
    A pure python library implemented by python3 for writing Latex formulas to word.

  21. StephanMeijer commented on Oct 1, 2024

    @StephanMeijer

    @shashikulk Can I use your file https://github.com/python-openxml/python-docx/files/10948322/Trial_Word_Output.docx for testing purposes in my project, and republish in my Git repository, in order to support conversions between specifications of mathematics?

  22. shashikulk commented on Oct 7, 2024

    @shashikulk

    @shashikulk Can I use your file https://github.com/python-openxml/python-docx/files/10948322/Trial_Word_Output.docx for testing purposes in my project, and republish in my Git repository, in order to support conversions between specifications of mathematics?

    @StephanMeijer sure you can.

  23. keh9mark commented on Jan 29, 2025

    @keh9mark

    To add a formula (equation) to a .docx document, you can use the math2docx library. Here is an example code that demonstrates how to do this:

    import math2docx from docx import Document

    document = Document() p = document.add_paragraph()

    #LaTeX Math Formula latex_ = r"\cos (2\theta) = \cos^2 \theta - \sin^2 \theta"

    math2docx.add_math(p, latex_) document.save("new_file.docx")

    This code creates a new .docx document, adds a paragraph to it, and inserts a mathematical formula specified in LaTeX format into that paragraph. The document is then saved as new_file.docx.

    https://github.com/keh9mark/math2docx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions