Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
95d09f2
Improve processing of MathMLForm for Input and Output Form
mmatera Jan 29, 2026
842fc58
remove trailing print
mmatera Jan 29, 2026
466bc6e
remove space from extra_operators, and add a comment
mmatera Jan 29, 2026
8f0f52b
add a comment about mathml multiline output. Give a more consistent o…
mmatera Jan 29, 2026
2d98ca7
Merge remote-tracking branch 'origin/master' into mathml_tweaks
Jan 30, 2026
f513083
indentation
Jan 30, 2026
d26de0d
mathml indent
Jan 30, 2026
cbceb63
update docs
Jan 30, 2026
e556ec9
update tests. adjust more indentation
Jan 30, 2026
9e816d2
Merge branch 'master' into mathml_tweaks
mmatera Jan 30, 2026
ada2900
Merge branch 'mathml_tweaks' into mathml_tweaks_indent
mmatera Jan 30, 2026
2951a17
restore Makefile
mmatera Jan 30, 2026
81689de
Merge branch 'master' into mathml_tweaks
mmatera Jan 31, 2026
af48b42
Merge branch 'mathml_tweaks' into mathml_tweaks_indent
mmatera Jan 31, 2026
a5e3104
improve Expression.sameQ to deal with BoxExpressions. Add tests for B…
mmatera Feb 2, 2026
270f82d
add a test involving FractionBox
mmatera Feb 2, 2026
cb3ff7a
Merge branch 'improve_Expression_sameQ' into mathml_tweaks
mmatera Feb 2, 2026
fb15ee6
Merge branch 'mathml_tweaks' into mathml_tweaks_indent
mmatera Feb 2, 2026
b46ade2
Merge branch 'mathml_tweaks' into mathml_tweaks_indent
rocky Feb 3, 2026
940f921
merge
Feb 3, 2026
dd7d2a9
merge
Feb 3, 2026
483ecf6
merge
Feb 3, 2026
46d942a
self->box
rocky Feb 3, 2026
994551e
Merge branch 'mathml_tweaks_indent' of github.com:Mathics3/mathics-co…
rocky Feb 3, 2026
d2383b9
Merge branch 'mathml_tweaks' into mathml_tweaks_indent
mmatera Feb 3, 2026
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
1 change: 0 additions & 1 deletion mathics/builtin/forms/print.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ class OutputForm(FormBaseClass):
in_outputforms = True
in_printforms = True

formats = {"OutputForm[s_String]": "s"}
summary_text = "format expression in plain text"


Expand Down
1 change: 0 additions & 1 deletion mathics/doc/doc_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import logging
import re
from abc import ABC
from os import getenv
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Sequence, Tuple

from mathics.core.evaluation import Message, Print, _Out
Expand Down
14 changes: 10 additions & 4 deletions mathics/format/box/outputforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def eval_mathmlform(expr: BaseElement, evaluation: Evaluation) -> BoxElementMixi

boxes = format_element(expr, evaluation, SymbolTraditionalForm)
try:
mathml = boxes.boxes_to_mathml(evaluation=evaluation)
mathml = boxes.boxes_to_mathml(evaluation=evaluation, _indent_level=1)
except BoxError:
evaluation.message(
"General",
Expand All @@ -44,9 +44,15 @@ def eval_mathmlform(expr: BaseElement, evaluation: Evaluation) -> BoxElementMixi
# #convert_box(boxes)
query = evaluation.parse("Settings`$UseSansSerif")
usesansserif = query.evaluate(evaluation).to_python()
if not is_a_picture:
if isinstance(usesansserif, bool) and usesansserif:
mathml = '<mstyle mathvariant="sans-serif">%s</mstyle>' % mathml
if is_a_picture:
usesansserif = False
elif not isinstance(usesansserif, bool):
usesansserif = False

if usesansserif:
mathml = '<mstyle mathvariant="sans-serif">\n%s\n</mstyle>' % mathml
else:
mathml = "\n%s\n" % mathml

mathml = '<math display="block">%s</math>' % mathml # convert_box(boxes)
return InterpretationBox(
Expand Down
111 changes: 77 additions & 34 deletions mathics/format/render/mathml.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,30 @@

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
sub element, and indented according to the level of the part. \
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>
<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
import html

from mathics_scanner.tokeniser import is_symbol_name

Expand Down Expand Up @@ -101,9 +101,12 @@ def string(s: String, **options) -> str:
if number_as_text is None:
number_as_text = SymbolFalse

indent_level = options.get("_indent_level", 0)
indent_spaces = " " * indent_level

def render(format, string):
encoded_text = encode_mathml(string)
return format % encoded_text
return indent_spaces + format % encoded_text

if text.startswith('"') and text.endswith('"'):
text = text[1:-1]
Expand Down Expand Up @@ -170,7 +173,11 @@ def interpretation_box(box: InterpretationBox, **options):
add_conversion_fn(InterpretationBox, interpretation_box)


def pane_box(box: PaneBox, **options):
def pane_box(box, **options):
indent_level = options.get("_indent_level", 0)
indent_spaces = " " * indent_level
options["_indent_level"] = indent_level + 1

content = lookup_conversion_method(box.boxes, "mathml")(box.boxes, **options)
options = box.box_options
size = options.get("System`ImageSize", SymbolAutomatic).to_python()
Expand Down Expand Up @@ -201,8 +208,8 @@ def pane_box(box: PaneBox, **options):
dims += "overflow:hidden;"
dims = f' style="{dims}" '
if dims:
return f"<mstyle {dims}>\n{content}\n</mstyle>"
return content
return f"{indent_spaces}<mstyle {dims}>\n{content}\n{indent_spaces}</mstyle>"
return f"{indent_spaces}{content}"


add_conversion_fn(PaneBox, pane_box)
Expand All @@ -212,7 +219,10 @@ def fractionbox(box: FractionBox, **options) -> str:
_options = box.box_options.copy()
_options.update(options)
options = _options
return "<mfrac>\n%s\n%s\n</mfrac>" % (
indent_level = options.get("_indent_level", 0)
indent_spaces = " " * indent_level
options["_indent_level"] = indent_level + 1
return f"{indent_spaces}<mfrac>\n%s\n%s\n{indent_spaces}</mfrac>" % (
lookup_conversion_method(box.num, "mathml")(box.num, **options),
lookup_conversion_method(box.den, "mathml")(box.den, **options),
)
Expand Down Expand Up @@ -243,18 +253,23 @@ def boxes_to_mathml(box, **options):
# invalid column alignment
raise BoxConstructError
joined_attrs = " ".join(f'{name}="{value}"' for name, value in attrs.items())
result = f"<mtable {joined_attrs}>\n"
indent_level = options.get("_indent_level", 0)
indent_spaces = " " * indent_level
result = f"{indent_spaces}<mtable {joined_attrs}>\n"
new_box_options = box_options.copy()
new_box_options["inside_list"] = True
new_box_options["_indent_level"] = indent_level + 3

for row in items:
result += "<mtr>"
result += f"{indent_spaces} <mtr>"
if isinstance(row, tuple):
for item in row:
result += f"<mtd {joined_attrs}>{boxes_to_mathml(item, **new_box_options)}</mtd>"
new_box_options["_indent_level"] = indent_level + 4
result += f"\n{indent_spaces} <mtd {joined_attrs}>\n{boxes_to_mathml(item, **new_box_options)}\n{indent_spaces} </mtd>"
else:
result += f"<mtd {joined_attrs} columnspan={num_fields}>{boxes_to_mathml(row, **new_box_options)}</mtd>"
result += "</mtr>\n"
result += "</mtable>"
result += f"\n{indent_spaces} <mtd {joined_attrs} columnspan={num_fields}>\n{boxes_to_mathml(row, **new_box_options)}\n{indent_spaces} </mtd>"
result += f"\n{indent_spaces} </mtr>\n"
result += f"{indent_spaces}</mtable>"
# print(f"gridbox: {result}")
return result

Expand All @@ -266,14 +281,19 @@ def sqrtbox(box: SqrtBox, **options):
_options = box.box_options.copy()
_options.update(options)
options = _options
indent_level = options.get("_indent_level", 0)
indent_spaces = " " * indent_level
options["_indent_level"] = indent_level + 1

if box.index:
return "<mroot> %s %s </mroot>" % (
return f"{indent_spaces}<mroot>\n%s\n%s\n{indent_spaces}</mroot>" % (
lookup_conversion_method(box.radicand, "mathml")(box.radicand, **options),
lookup_conversion_method(box.index, "mathml")(box.index, **options),
)

return "<msqrt>\n%s\n</msqrt>" % lookup_conversion_method(box.radicand, "mathml")(
box.radicand, **options
return (
f"{indent_spaces}<msqrt>\n%s\n{indent_spaces}</msqrt>"
% lookup_conversion_method(box.radicand, "mathml")(box.radicand, **options)
)


Expand All @@ -284,7 +304,10 @@ def subscriptbox(box: SubscriptBox, **options):
_options = box.box_options.copy()
_options.update(options)
options = _options
return "<msub>\n%s\n%s\n</msub>" % (
indent_level = options.get("_indent_level", 0)
indent_spaces = " " * indent_level
options["_indent_level"] = indent_level + 1
return f"{indent_spaces}<msub>\n%s\n%s\n{indent_spaces}</msub>" % (
lookup_conversion_method(box.base, "mathml")(box.base, **options),
lookup_conversion_method(box.subindex, "mathml")(box.subindex, **options),
)
Expand All @@ -297,7 +320,11 @@ def superscriptbox(box: SuperscriptBox, **options):
_options = box.box_options.copy()
_options.update(options)
options = _options
return "<msup>\n%s\n%s\n</msup>" % (
indent_level = options.get("_indent_level", 0)
indent_spaces = " " * indent_level
options["_indent_level"] = indent_level + 1

return f"{indent_spaces}<msup>\n%s\n%s\n{indent_spaces}</msup>" % (
lookup_conversion_method(box.base, "mathml")(box.base, **options),
lookup_conversion_method(box.superindex, "mathml")(box.superindex, **options),
)
Expand All @@ -311,7 +338,12 @@ def subsuperscriptbox(box: SubsuperscriptBox, **options):
_options.update(options)
options = _options
options["inside_row"] = True
return "<msubsup>\n%s\n%s\n%s\n</msubsup>" % (

indent_level = options.get("_indent_level", 0)
indent_spaces = " " * indent_level
options["_indent_level"] = indent_level + 1

return f"{indent_spaces}<msubsup>\n%s\n%s\n%s\n{indent_spaces}</msubsup>" % (
lookup_conversion_method(box.base, "mathml")(box.base, **options),
lookup_conversion_method(box.subindex, "mathml")(box.subindex, **options),
lookup_conversion_method(box.superindex, "mathml")(box.superindex, **options),
Expand Down Expand Up @@ -354,12 +386,15 @@ def is_list_interior(content):
else:
options["inside_row"] = True

indent_level = options.get("_indent_level", 0)
indent_spaces = " " * indent_level
options["_indent_level"] = indent_level + 1

for element in box.items:
result.append(lookup_conversion_method(element, "mathml")(element, **options))

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

return "<mrow>\n%s\n</mrow>" % "\n".join(result)
return f"{indent_spaces}<mrow>\n%s\n{indent_spaces}</mrow>" % ("\n".join(result),)


add_conversion_fn(RowBox, rowbox)
Expand Down Expand Up @@ -394,6 +429,10 @@ def graphicsbox(box: GraphicsBox, elements=None, **options) -> str:
int(box.boxheight),
base64.b64encode(svg_body.encode("utf8")).decode("utf8"),
)
indent_level = options.get("_indent_level", 0)
if indent_level:
mathml = " " * indent_level + mathml

# print("boxes_to_mathml", mathml)
return mathml

Expand All @@ -403,9 +442,13 @@ def graphicsbox(box: GraphicsBox, elements=None, **options) -> str:

def graphics3dbox(box, elements=None, **options) -> str:
"""Turn the Graphics3DBox into a MathML string"""
result = box.boxes_to_js(**options)
result = f"<mtable><mtr><mtd>{result}</mtd></mtr></mtable>"
return result
json_repr = box.boxes_to_json(elements, **options)
mathml = f'<graphics3d data="{html.escape(json_repr)}" />'
mathml = f"<mtable>\n<mtr>\n<mtd>\n{mathml}\n</mtd>\n</mtr>\n</mtable>"
indent_level = options.get("_indent_level", 0)
if indent_level:
mathml = " " * indent_level + mathml
return mathml


add_conversion_fn(Graphics3DBox, graphics3dbox)
Expand Down
Loading