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 :)
Hello @peepall,
Any success with inserting malthml ?
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.
Thanks @peepall,
Worked like a charm !
@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?
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.
Tell me if this solves your issue.
Regards
@peepall It certainly did. Many thanks!
馃憖
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
@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>−</mo><mi>b</mi></mrow><mo>±</mo><msqrt><msup><mi>b</mi><mn>2</mn></msup><mo>−</mo><mrow><mn>4</mn><mo>⁢</mo><mi>a</mi><mo>⁢</mo><mi>c</mi></mrow></msqrt></mrow><mrow><mn>2</mn><mo>⁢</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?
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>−</mo>"
" <mi>b</mi>"
" </mrow>"
" <mo>±</mo>"
" <msqrt>"
" <msup>"
" <mi>b</mi>"
" <mn>2</mn>"
" </msup>"
" <mo>−</mo>"
" <mrow>"
" <mn>4</mn>"
" <mo>⁢</mo>"
" <mi>a</mi>"
" <mo>⁢</mo>"
" <mi>c</mi>"
" </mrow>"
" </msqrt>"
" </mrow>"
" <mrow>"
" <mn>2</mn>"
" <mo>⁢</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.
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')```
Most helpful comment
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.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.