Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 69 additions & 29 deletions mathics/format/render/mathml.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,30 @@
Lower-level formatter of Mathics objects as MathML strings.

MathML formatting is usually initiated in Mathics via MathMLForm[].

For readability, and following WMA MathML generated code, tags \
containing sub-tags are split on several lines, one by
sub element. For example, the Box expression

>> FractionBox[RowBox[{"a", "+", SuperscriptBox["b", "c"]}], "d"]

produces
```
<mfrac>
<mrow>
<mi>a</mi>
<mo>+</mo>
<msup>
<mi>b</mi>
<mi>c</mi>
</msup>
</mrow>
<mi>d</mi>
</mfrac>
```
In WMA, each line would be also indented adding one space on each \
level of indentation.

"""

import base64
Expand Down Expand Up @@ -44,6 +68,7 @@ def encode_mathml(text: str) -> str:
return text


# "Operators" which are not in display_operators_set
extra_operators = {
",",
"(",
Expand All @@ -52,15 +77,15 @@ def encode_mathml(text: str) -> str:
"]",
"{",
"}",
"\u301a",
"\u301b",
"\u00d7",
"\u2032",
"\u2032\u2032",
" ",
"\u2062",
"\u222b",
"\u2146",
# TODO: check why the following characters are not in `operators`:
"\u301a", # [[
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recently, RightDoubleBracket and LeftDoubleBracket were tagged as operators. I suspect that these can now be picked up from the character tables.

"\u301b", # ]]
"\u00d7", # \[Times]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't this be picked up from character tables?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. I have to check if we are not doing already that.

"\u2032", # \[RawComma]
"\u2032\u2032", # \[RawComma]\[RawComma]
"\u2062", # \[InvisibleTimes]
"\u222b", # \[Integral]
"\u2146", # \[DifferentialD]
}


Expand All @@ -80,13 +105,10 @@ def render(format, string):
return format % encoded_text

if text.startswith('"') and text.endswith('"'):
if show_string_characters:
return render("<ms>%s</ms>", text[1:-1])
else:
outtext = ""
for line in text[1:-1].split("\n"):
outtext += render("<mtext>%s</mtext>", line)
return outtext
text = text[1:-1]
if not show_string_characters:
return render("<mtext>%s</mtext>", text)
return render("<ms>%s</ms>", text)
elif (
text
and (number_as_text is SymbolFalse)
Expand All @@ -101,29 +123,47 @@ def render(format, string):
# Mathics-Django:
if text == "":
return ""
if text == "\u2146":
if text == "\u2146": # DifferentialD
return render(
'<mo form="prefix" lspace="0.2em" rspace="0">%s</mo>', text
)
if text == "\u2062":
if text == "\u2062": # InvisibleTimes
return render(
'<mo form="prefix" lspace="0" rspace="0.2em">%s</mo>', text
)
return render("<mo>%s</mo>", text)
elif is_symbol_name(text):
return render("<mi>%s</mi>", text)
else:
outtext = ""
for line in text.split("\n"):
outtext += render("<mtext>%s</mtext>", line)
return outtext
return "".join(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where getting the proper indent level would be done.

render("<mtext>%s</mtext>", line) for line in text.split("\n")
)


add_conversion_fn(String, string)


def interpretation_box(self, **options):
return lookup_conversion_method(self.boxes, "mathml")(self.boxes, **options)
boxes = self.boxes
origin = self.expr
if origin.has_form("InputForm", None):
# InputForm produce outputs of the form
# InterpretationBox[Style[_String, ...], origin_InputForm, opts___]
assert isinstance(boxes, StyleBox), f"boxes={boxes} are not a StyleBox"
boxes = boxes.boxes
options["System`ShowStringCharacters"] = SymbolTrue
assert isinstance(boxes, String)
# Remove the outer quotes
elif origin.has_form("OutputForm", None):
# OutputForm produce outputs of the form
# InterpretationBox[PaneBox[_String, ...], origin_OutputForm, opts___]
assert boxes.has_form("PaneBox", 1, None)
boxes = boxes.boxes
assert isinstance(boxes, String)
# Remove the outer quotes
boxes = String(boxes.value)

return lookup_conversion_method(boxes, "mathml")(boxes, **options)


add_conversion_fn(InterpretationBox, interpretation_box)
Expand Down Expand Up @@ -171,7 +211,7 @@ def fractionbox(self, **options) -> str:
_options = self.box_options.copy()
_options.update(options)
options = _options
return "<mfrac>%s %s</mfrac>" % (
return "<mfrac>\n%s\n%s\n</mfrac>" % (
lookup_conversion_method(self.num, "mathml")(self.num, **options),
lookup_conversion_method(self.den, "mathml")(self.den, **options),
)
Expand Down Expand Up @@ -231,7 +271,7 @@ def sqrtbox(self, **options):
lookup_conversion_method(self.index, "mathml")(self.index, **options),
)

return "<msqrt> %s </msqrt>" % lookup_conversion_method(self.radicand, "mathml")(
return "<msqrt>\n%s\n</msqrt>" % lookup_conversion_method(self.radicand, "mathml")(
self.radicand, **options
)

Expand All @@ -243,7 +283,7 @@ def subscriptbox(self, **options):
_options = self.box_options.copy()
_options.update(options)
options = _options
return "<msub>%s %s</msub>" % (
return "<msub>\n%s\n%s\n</msub>" % (
lookup_conversion_method(self.base, "mathml")(self.base, **options),
lookup_conversion_method(self.subindex, "mathml")(self.subindex, **options),
)
Expand All @@ -256,7 +296,7 @@ def superscriptbox(self, **options):
_options = self.box_options.copy()
_options.update(options)
options = _options
return "<msup>%s %s</msup>" % (
return "<msup>\n%s\n%s\n</msup>" % (
lookup_conversion_method(self.base, "mathml")(self.base, **options),
lookup_conversion_method(self.superindex, "mathml")(self.superindex, **options),
)
Expand All @@ -270,7 +310,7 @@ def subsuperscriptbox(self, **options):
_options.update(options)
options = _options
options["inside_row"] = True
return "<msubsup>%s %s %s</msubsup>" % (
return "<msubsup>\n%s\n%s\n%s\n</msubsup>" % (
lookup_conversion_method(self.base, "mathml")(self.base, **options),
lookup_conversion_method(self.subindex, "mathml")(self.subindex, **options),
lookup_conversion_method(self.superindex, "mathml")(self.superindex, **options),
Expand Down Expand Up @@ -318,7 +358,7 @@ def is_list_interior(content):

# print(f"mrow: {result}")

return "<mrow>%s</mrow>" % " ".join(result)
return "<mrow>\n%s\n</mrow>" % "\n".join(result)


add_conversion_fn(RowBox, rowbox)
Expand Down
111 changes: 63 additions & 48 deletions test/format/format_tests-WMA.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
# because we use both in documentation and in the web interface.
#


'"-7.32"':
msg: A String with a number
latex:
Expand Down Expand Up @@ -402,36 +401,15 @@ Graphics[{}]:
Grid[{{a,b},{c,d}}]:
msg: GridBox
latex:
InputForm: \text{Grid[\{\{a, b\}, \{c, d\}\}]}
InputForm: '\text{Grid[$\{\{$a, b$\}$, $\{$c, d$\}\}$]}'
OutputForm: \begin{array}{cc} a & b\\ c & d\end{array}
StandardForm: \begin{array}{cc} a & b\\ c & d\end{array}
TraditionalForm: \begin{array}{cc} a & b\\ c & d\end{array}
StandardForm: "\\begin{array}{cc}\n a & b\\\\\n c & d\n\\end{array}"
TraditionalForm: "\\begin{array}{cc}\n a & b\\\\\n c & d\n\\end{array}"
mathml:
InputForm: <mrow><mi>Grid</mi> <mo>[</mo> <mrow><mo>{</mo> <mrow><mrow><mo>{</mo>
<mrow><mi>a</mi> <mtext>,&nbsp;</mtext> <mi>b</mi></mrow> <mo>}</mo></mrow>
<mtext>,&nbsp;</mtext> <mrow><mo>{</mo> <mrow><mi>c</mi> <mtext>,&nbsp;</mtext>
<mi>d</mi></mrow> <mo>}</mo></mrow></mrow> <mo>}</mo></mrow> <mo>]</mo></mrow>
OutputForm: '<mtable columnalign="center">

<mtr><mtd columnalign="center"><mi>a</mi></mtd><mtd columnalign="center"><mi>b</mi></mtd></mtr>

<mtr><mtd columnalign="center"><mi>c</mi></mtd><mtd columnalign="center"><mi>d</mi></mtd></mtr>

</mtable>'
StandardForm: '<mtable columnalign="center">

<mtr><mtd columnalign="center"><mi>a</mi></mtd><mtd columnalign="center"><mi>b</mi></mtd></mtr>

<mtr><mtd columnalign="center"><mi>c</mi></mtd><mtd columnalign="center"><mi>d</mi></mtd></mtr>

</mtable>'
TraditionalForm: '<mtable columnalign="center">

<mtr><mtd columnalign="center"><mi>a</mi></mtd><mtd columnalign="center"><mi>b</mi></mtd></mtr>

<mtr><mtd columnalign="center"><mi>c</mi></mtd><mtd columnalign="center"><mi>d</mi></mtd></mtr>

</mtable>'
InputForm: '<mrow><mi>Grid</mi> <mo>[</mo> <mrow><mo>{</mo> <mrow><mrow><mo>{</mo> <mrow><mi>a</mi> <mtext>,&nbsp;</mtext> <mi>b</mi></mrow> <mo>}</mo></mrow> <mtext>,&nbsp;</mtext> <mrow><mo>{</mo> <mrow><mi>c</mi> <mtext>,&nbsp;</mtext> <mi>d</mi></mrow> <mo>}</mo></mrow></mrow> <mo>}</mo></mrow> <mo>]</mo></mrow>'
StandardForm: "<mtable columnalign=\"center\">\n <mtr>\n <mtd columnalign=\"center\">\n <mi>a</mi>\n </mtd>\n <mtd columnalign=\"center\">\n <mi>b</mi>\n </mtd>\n </mtr>\n <mtr>\n <mtd columnalign=\"center\">\n <mi>c</mi>\n </mtd>\n <mtd columnalign=\"center\">\n <mi>d</mi>\n </mtd>\n </mtr>\n </mtable>"
TraditionalForm: "<mtable columnalign=\"center\">\n <mtr><mtd columnalign=\"center\"><mi>a</mi></mtd><mtd columnalign=\"center\"><mi>b</mi></mtd></mtr>\n
<mtr><mtd columnalign=\"center\"><mi>c</mi></mtd><mtd columnalign=\"center\"><mi>d</mi></mtd></mtr>\n </mtable>"
text:
InputForm: Grid[{{a, b}, {c, d}}]
OutputForm: 'a b
Expand Down Expand Up @@ -556,12 +534,8 @@ Subscript[a, 4]:
- <mrow><mi>Subscript</mi> <mo>[</mo> <mrow><mi>a</mi> <mtext>,&nbsp;</mtext>
<mtext>4</mtext></mrow> <mo>]</mo></mrow>
- Fragile!
OutputForm:
- <mrow><mi>Subscript</mi> <mo>[</mo> <mrow><mi>a</mi> <mtext>,&nbsp;</mtext>
<mn>4</mn></mrow> <mo>]</mo></mrow>
- Fragile!
StandardForm: <msub><mi>a</mi> <mn>4</mn></msub>
TraditionalForm: <msub><mi>a</mi> <mn>4</mn></msub>
StandardForm: "<msub>\n <mi>a</mi>\n <mn>4</mn>\n </msub>"
TraditionalForm: "<msub>\n <mi>a</mi>\n <mn>4</mn>\n </msub>"
text:
InputForm: Subscript[a, 4]
OutputForm: "a\n 4"
Expand All @@ -583,8 +557,8 @@ Subsuperscript[a, p, q]:
<mi>p</mi> <mtext>,&nbsp;</mtext> <mi>q</mi></mrow> <mo>]</mo></mrow>
OutputForm: <mrow><mi>Subsuperscript</mi> <mo>[</mo> <mrow><mi>a</mi> <mtext>,&nbsp;</mtext>
<mi>p</mi> <mtext>,&nbsp;</mtext> <mi>q</mi></mrow> <mo>]</mo></mrow>
StandardForm: <msubsup><mi>a</mi> <mi>p</mi> <mi>q</mi></msubsup>
TraditionalForm: <msubsup><mi>a</mi> <mi>p</mi> <mi>q</mi></msubsup>
StandardForm: "<msubsup>\n <mi>a</mi>\n <mi>p</mi>\n <mi>q</mi>\n </msubsup>"
TraditionalForm: "<msubsup>\n <mi>a</mi>\n <mi>p</mi>\n <mi>q</mi>\n </msubsup>"
text:
InputForm: Subsuperscript[a, p, q]
OutputForm: " q\na\n p"
Expand Down Expand Up @@ -703,15 +677,13 @@ a^(g[b]/c):
latex:
InputForm: \text{a${}^{\wedge}$(g[b]/c)}
OutputForm: a\text{ ${}^{\wedge}$ }\left(g(b)\text{ / }c\right)
StandardForm: a^{\frac{g(b)}{c}}
StandardForm: a^{\frac{g[b]}{c}}
TraditionalForm: a^{\frac{g(b)}{c}}
mathml:
InputForm: <mrow><mi>a</mi> <mo>^</mo> <mrow><mo>(</mo> <mrow><mi>b</mi>
<mtext>&nbsp;/&nbsp;</mtext> <mi>c</mi></mrow> <mo>)</mo></mrow></mrow>
OutputForm: <mrow><mi>a</mi> <mtext>&nbsp;^&nbsp;</mtext> <mrow><mo>(</mo>
<mrow><mi>b</mi> <mtext>&nbsp;/&nbsp;</mtext> <mi>c</mi></mrow> <mo>)</mo></mrow></mrow>
StandardForm: <msup><mi>a</mi> <mfrac><mi>b</mi> <mi>c</mi></mfrac></msup>
TraditionalForm: <msup><mi>a</mi> <mfrac><mrow><mi>g</mi> <mo>(</mo> <mi>b</mi> <mo>)</mo></mrow> <mi>c</mi></mfrac></msup>
InputForm: '<mtext>a^(g[b]/c)</mtext>'
OutputForm: '<><>'
StandardForm: "<msup>\n <mi>a</mi>\n <mfrac>\n <mrow>\n <mi>g</mi>\n <mo>[</mo>\n <mi>b</mi>\n <mo>]</mo>\n </mrow>\n <mi>c</mi>\n </mfrac>\n </msup>"
TraditionalForm: "<msup>\n <mi>a</mi>\n <mfrac>\n <mrow>\n <mi>g</mi>\n <mo>&#8289;</mo>\n <mo>(</mo>\n <mi>b</mi>\n <mo>)</mo>\n </mrow>\n <mi>c</mi>\n </mfrac>\n </msup>"
text:
InputForm: a^(g[b]/c)
OutputForm: " g[b]/c\na"
Expand All @@ -727,12 +699,55 @@ a^4:
StandardForm: a^4
TraditionalForm: a^4
mathml:
InputForm: <mrow><mi>a</mi> <mo>^</mo> <mtext>4</mtext></mrow>
OutputForm: <mrow><mi>a</mi> <mtext>&nbsp;^&nbsp;</mtext> <mn>4</mn></mrow>
StandardForm: <msup><mi>a</mi> <mn>4</mn></msup>
TraditionalForm: <msup><mi>a</mi> <mn>4</mn></msup>
InputForm: <mtext>a^4</mtext>
StandardForm: "<msup>\n <mi>a</mi>\n <mn>4</mn>\n </msup>"
TraditionalForm: "<msup>\n <mi>a</mi>\n <mn>4</mn>\n </msup>"
text:
InputForm: a^4
OutputForm: " 4\na"
StandardForm: "\\!\\(\\*SuperscriptBox[\"a\", \"4\"]\\)"
TraditionalForm: "\\!\\(\\*FormBox[SuperscriptBox[\"a\", \"4\"], TraditionalForm]\\)"


Optional[x__]:
msg: Optional with one argument
latex:
System`OutputForm: '\text{x$\_\_$.}'
System`StandardForm: '\text{x$\_\_$.}'
mathml:
System`OutputForm: '<mtext>x__.</mtext>'
System`StandardForm: '<mtext>x__.</mtext>'
text:
System`InputForm: 'x__.'
System`OutputForm: 'x__.'
System`StandardForm: '\!\(\*RowBox[{"Optional", "[", "x__", "]"}]\)'
System`TraditionalForm: '\!\(\*FormBox[RowBox[{"Optional", "[", "x__", "]"}], TraditionalForm]\)'


Optional[x__, a+b]:
msg: Optional with two arguments
latex:
System`OutputForm: '\text{x$\_\_$:a + b}'
System`StandardForm: '\text{x$\_\_$}:a+b'
mathml:
System`OutputForm: '<mtext>x__&nbsp;:&nbsp;a&nbsp;+&nbsp;b</mtext>'
text:
System`InputForm: 'x__:a + b'
System`OutputForm: 'x__:a + b'
System`StandardForm: '\!\(\*RowBox[{"x__", ":", RowBox[{"a", "+", "b"}]}]\)'
System`TraditionalForm: '\!\(\*FormBox[RowBox[{"x__", ":", RowBox[{"a", "+", "b"}]}], TraditionalForm]\)'


a+PrecedenceForm[b+c,10]:
msg: "PrecedenceForm"
latex:
System`OutputForm: '\text{a + (b + c)}'
System`StandardForm: 'a+(b+c)'
mathml:
System`OutputForm: '<mtext>a&nbsp;+&nbsp;(b&nbsp;+&nbsp;c)</mtext>'
System`StandardForm: "<mrow>\n <mi>a</mi>\n <mo>+</mo>\n <mrow>\n <mo>(</mo>\n <mrow>\n <mi>b</mi>\n <mo>+</mo>\n <mi>c</mi>\n </mrow>\n <mo>)</mo>\n </mrow>\n </mrow>"
text:
System`InputForm: 'a + PrecedenceForm[b + c, 10]'
System`OutputForm: 'a + (b + c)'
System`StandardForm: '\!\(\*RowBox[{"a", "+", RowBox[{"(", TagBox[RowBox[{"b", "+", "c"}], Function[PrecedenceForm[Slot[1], 10]]], ")"}]}]\)'
System`TraditionalForm: '\!\(\*FormBox[RowBox[{"a", "+", RowBox[{"(", TagBox[RowBox[{"b", "+", "c"}], Function[PrecedenceForm[Slot[1], 10]]], ")"}]}], TraditionalForm]\)'
Loading
Loading