diff --git a/mathics/builtin/box/compilation.py b/mathics/builtin/box/compilation.py index ce2a2fe7a..4a45a9b1f 100644 --- a/mathics/builtin/box/compilation.py +++ b/mathics/builtin/box/compilation.py @@ -21,7 +21,7 @@ class CompiledCodeBox(BoxExpression): def init(self, *args, **kwargs): self._elements = args - self.box_options = kwargs + self.box_attributes = kwargs def boxes_to_text(self, elements=None, **options): if elements is None: diff --git a/mathics/builtin/box/layout.py b/mathics/builtin/box/layout.py index 23423eb8d..e34b6c3d8 100644 --- a/mathics/builtin/box/layout.py +++ b/mathics/builtin/box/layout.py @@ -122,7 +122,7 @@ class FormBox(BoxExpression): summary_text = "wrap boxes with an association to a particular form" def init(self, *elems, **kwargs): - self.box_options = kwargs + self.box_attributes = kwargs self.form = elems[1] self.boxes = elems[0] assert isinstance(self.boxes, BoxElementMixin), f"{type(self.boxes)}" @@ -136,7 +136,7 @@ def elements(self): self.boxes, self.form, ), - self.box_options, + self.box_attributes, ) return self._elements @@ -178,7 +178,7 @@ def elements(self): self.num, self.den, ), - self.box_options, + self.box_attributes, ) return self._elements @@ -193,7 +193,7 @@ def eval(self, num, den, evaluation: Evaluation, options: dict): def init(self, num, den, **options): self.num = num self.den = den - self.box_options = options + self.box_attributes = options class GridBox(BoxExpression): @@ -228,11 +228,13 @@ class GridBox(BoxExpression): @property def elements(self): if self._elements is None: - self._elements = elements_to_expressions(self, self.items, self.box_options) + self._elements = elements_to_expressions( + self, self.items, self.box_attributes + ) return self._elements def init(self, *elems, **kwargs): - self.box_options = kwargs + self.box_attributes = kwargs self.items = elems self._elements = elems @@ -240,7 +242,7 @@ def get_array(self, elements, evaluation): if not elements: raise BoxConstructError - options = self.box_options + options = self.box_attributes expr = elements[0] if not expr.has_form("List", None): @@ -293,13 +295,13 @@ class InterpretationBox(BoxExpression): def __repr__(self): result = "InterpretationBox\n " + repr(self.boxes) - result += f"\n {self.box_options}" + result += f"\n {self.box_attributes}" return result def init(self, *expr, **options): self.boxes = expr[0] self.expr = expr[1] - self.box_options = options + self.box_attributes = options @property def elements(self): @@ -310,7 +312,7 @@ def elements(self): self.boxes, self.expr, ), - self.box_options, + self.box_attributes, ) return self._elements @@ -365,13 +367,13 @@ class PaneBox(BoxExpression): def elements(self): if self._elements is None: self._elements = elements_to_expressions( - self, (self.boxes,), self.box_options + self, (self.boxes,), self.box_attributes ) return self._elements def init(self, expr, **options): self.boxes = expr - self.box_options = options + self.box_attributes = options def eval_panebox1(self, expr, evaluation, options): "PaneBox[expr_String, OptionsPattern[]]" @@ -409,6 +411,16 @@ class RowBox(BoxExpression): def __repr__(self): return f"RowBox[{self.elements[0].__repr__()}]" + def __init__(self, *args, **kwargs): + # indent level nesting level used in rendering MathML boxes + self.indent_level: int = 0 + + # Consider adding. + # self.width: int = 0 + + # Pass all arguments to the parent class. + super().__init__(*args, **kwargs) + @property def elements(self): if self._elements is None: @@ -433,7 +445,7 @@ def eval_list(self, boxes, evaluation): def init(self, *items, **kwargs): # TODO: check that each element is an string or a BoxElementMixin - self.box_options = {} + self.box_attributes = {} if len(items) == 0: self.items = tuple() return @@ -509,13 +521,13 @@ def elements(self): if self._elements is None: index = self.index if index is None: - # self.box_options + # self.box_attributes self._elements = elements_to_expressions( - self, (self.radicand,), self.box_options + self, (self.radicand,), self.box_attributes ) else: self._elements = elements_to_expressions( - self, (self.radicand, index), self.box_options + self, (self.radicand, index), self.box_attributes ) return self._elements @@ -535,7 +547,7 @@ def eval(self, radicand, evaluation: Evaluation, options: dict): def init(self, radicand, index=None, **options): self.radicand = radicand self.index = index - self.box_options = options + self.box_attributes = options class StyleBox(BoxExpression): @@ -567,11 +579,11 @@ def elements(self): boxes = self.boxes if style: self._elements = elements_to_expressions( - self, (boxes, style), self.box_options + self, (boxes, style), self.box_attributes ) else: self._elements = elements_to_expressions( - self, (boxes,), self.box_options + self, (boxes,), self.box_attributes ) return self._elements @@ -596,10 +608,10 @@ def get_string_value(self) -> str: def init(self, boxes, style=None, **options): # This implementation supersedes Expression.process_style_box if isinstance(boxes, StyleBox): - options.update(boxes.box_options) + options.update(boxes.box_attributes) boxes = boxes.boxes self.style = style - self.box_options = options + self.box_attributes = options assert options is not None self.boxes = boxes assert isinstance( @@ -635,7 +647,7 @@ class SubscriptBox(BoxExpression): def elements(self): if self._elements is None: self._elements = elements_to_expressions( - self, (self.base, self.subindex), self.box_options + self, (self.base, self.subindex), self.box_attributes ) return self._elements @@ -648,7 +660,7 @@ def eval(self, a, b, evaluation: Evaluation, options: dict): return SubscriptBox(a_box, b_box, **options) def init(self, a, b, **options): - self.box_options = options.copy() + self.box_attributes = options.copy() if not (isinstance(a, BoxElementMixin) and isinstance(b, BoxElementMixin)): raise Exception((a, b), "are not boxes") self.base = a @@ -674,7 +686,7 @@ class SubsuperscriptBox(BoxExpression): @property def elements(self): if self._elements is None: - # self.box_options + # self.box_attributes self._elements = elements_to_expressions( ( self, @@ -682,7 +694,7 @@ def elements(self): self.subindex, self.superindex, ), - self.box_options, + self.box_attributes, ) return self._elements @@ -696,7 +708,7 @@ def eval(self, a, b, c, evaluation: Evaluation, options: dict): return SubsuperscriptBox(a_box, b_box, c_box, **options) def init(self, a, b, c, **options): - self.box_options = options.copy() + self.box_attributes = options.copy() if not all(isinstance(x, BoxElementMixin) for x in (a, b, c)): raise Exception((a, b, c), "are not boxes") self.base = a @@ -729,7 +741,7 @@ def elements(self): self.base, self.superindex, ), - self.box_options, + self.box_attributes, ) return self._elements @@ -742,7 +754,7 @@ def eval(self, a, b, evaluation: Evaluation, options: dict): return SuperscriptBox(a_box, b_box, **options) def init(self, a, b, **options): - self.box_options = options.copy() + self.box_attributes = options.copy() if not all(isinstance(x, BoxElementMixin) for x in (a, b)): raise Exception((a, b), "are not boxes") self.base = a @@ -766,7 +778,7 @@ class TagBox(BoxExpression): summary_text = "box tag with a head" def init(self, *elems, **kwargs): - self.box_options = kwargs + self.box_attributes = kwargs self.form = elems[1] self.boxes = elems[0] assert isinstance(self.boxes, BoxElementMixin), f"{type(self.boxes)}" @@ -780,7 +792,7 @@ def elements(self): self.boxes, self.form, ), - self.box_options, + self.box_attributes, ) return self._elements diff --git a/mathics/format/box/outputforms.py b/mathics/format/box/outputforms.py index 60d187f4d..09de275d9 100644 --- a/mathics/format/box/outputforms.py +++ b/mathics/format/box/outputforms.py @@ -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", @@ -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 = '%s' % mathml + if is_a_picture: + usesansserif = False + elif not isinstance(usesansserif, bool): + usesansserif = False + + if usesansserif: + mathml = '\n%s\n' % mathml + else: + mathml = "\n%s\n" % mathml mathml = '%s' % mathml # convert_box(boxes) return InterpretationBox( diff --git a/mathics/format/render/latex.py b/mathics/format/render/latex.py index 8cf27c403..a936ad587 100644 --- a/mathics/format/render/latex.py +++ b/mathics/format/render/latex.py @@ -174,7 +174,7 @@ def interpretation_box(self, **options): def pane_box(self, **options): content = lookup_conversion_method(self.boxes, "latex")(self.boxes, **options) - options = self.box_options + options = self.box_attributes size = options.get("System`ImageSize", SymbolAutomatic).to_python() if size == "System`Automatic": @@ -211,7 +211,7 @@ def pane_box(self, **options): def fractionbox(self, **options) -> str: - _options = self.box_options.copy() + _options = self.box_attributes.copy() _options.update(options) options = _options return "\\frac{%s}{%s}" % ( @@ -223,17 +223,17 @@ def fractionbox(self, **options) -> str: add_conversion_fn(FractionBox, fractionbox) -def gridbox(self, elements=None, **box_options) -> str: +def gridbox(self, elements=None, **box_attributes) -> str: def boxes_to_tex(box, **options): return lookup_conversion_method(box, "latex")(box, **options) if not elements: elements = self._elements - evaluation = box_options.get("evaluation") + evaluation = box_attributes.get("evaluation") items, options = self.get_array(elements, evaluation) - box_options.update(options) - box_options["inside_list"] = True - column_alignments = box_options["System`ColumnAlignments"].get_name() + box_attributes.update(options) + box_attributes["inside_list"] = True + column_alignments = box_attributes["System`ColumnAlignments"].get_name() try: column_alignments = { "System`Center": "c", @@ -251,12 +251,12 @@ def boxes_to_tex(box, **options): result = r"\begin{array}{%s} " % (column_alignments * column_count) for index, row in enumerate(items): if isinstance(row, tuple): - result += " & ".join(boxes_to_tex(item, **box_options) for item in row) + result += " & ".join(boxes_to_tex(item, **box_attributes) for item in row) else: result += r"\multicolumn{%s}{%s}{%s}" % ( str(column_count), column_alignments, - boxes_to_tex(row, **box_options), + boxes_to_tex(row, **box_attributes), ) if index != len(items) - 1: result += "\\\\ " @@ -268,7 +268,7 @@ def boxes_to_tex(box, **options): def sqrtbox(self, **options): - _options = self.box_options.copy() + _options = self.box_attributes.copy() _options.update(options) options = _options if self.index: @@ -285,7 +285,7 @@ def sqrtbox(self, **options): def superscriptbox(self, **options): - _options = self.box_options.copy() + _options = self.box_attributes.copy() _options.update(options) options = _options base_to_tex = lookup_conversion_method(self.base, "latex") @@ -315,7 +315,7 @@ def superscriptbox(self, **options): def subscriptbox(self, **options): - _options = self.box_options.copy() + _options = self.box_attributes.copy() _options.update(options) options = _options base_to_tex = lookup_conversion_method(self.base, "latex") @@ -330,7 +330,7 @@ def subscriptbox(self, **options): def subsuperscriptbox(self, **options): - _options = self.box_options.copy() + _options = self.box_attributes.copy() _options.update(options) options = _options base_to_tex = lookup_conversion_method(self.base, "latex") @@ -348,7 +348,7 @@ def subsuperscriptbox(self, **options): def rowbox(self, **options) -> str: - _options = self.box_options.copy() + _options = self.box_attributes.copy() _options.update(options) options = _options parts_str = [ @@ -382,7 +382,7 @@ def rowbox(self, **options) -> str: def stylebox(self, **options) -> str: - _options = self.box_options.copy() + _options = self.box_attributes.copy() _options.update(options) options = _options return lookup_conversion_method(self.boxes, "latex")(self.boxes, **options) diff --git a/mathics/format/render/mathml.py b/mathics/format/render/mathml.py index a9ecb2297..5a943e1d7 100644 --- a/mathics/format/render/mathml.py +++ b/mathics/format/render/mathml.py @@ -1,31 +1,32 @@ # -*- coding: utf-8 -*- """ -Lower-level formatter of Mathics objects as MathML strings. +Lower-level formatter of Mathics3 into MathML strings. MathML formatting is usually initiated in Mathics via MathMLForm[]. -For readability, and following WMA MathML generated code, tags \ +Following WMA MathML generated text, and for readability, MathML tags containing sub-tags are split on several lines, one by -sub element. For example, the Box expression +sub element, and indented according to nesting level. The +indentation step size is one space. + +For example, the Box expression >> FractionBox[RowBox[{"a", "+", SuperscriptBox["b", "c"]}], "d"] produces ``` - -a -+ - -b -c - - -d + + a + + + + b + c + + + d ``` -In WMA, each line would be also indented adding one space on each \ -level of indentation. """ @@ -90,20 +91,22 @@ def encode_mathml(text: str) -> str: } -def string(self, **options) -> str: - text = self.value +def string(s: String, **options) -> str: + text = s.value number_as_text = options.get("number_as_text", None) show_string_characters = ( options.get("System`ShowStringCharacters", None) is SymbolTrue ) - if isinstance(self, BoxElementMixin): - if number_as_text is None: - number_as_text = SymbolFalse + 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 + def render(format, s): + encoded_text = encode_mathml(s) + return indent_spaces + format % encoded_text if text.startswith('"') and text.endswith('"'): text = text[1:-1] @@ -144,9 +147,9 @@ def render(format, string): add_conversion_fn(String, string) -def interpretation_box(self, **options): - boxes = self.boxes - origin = self.expr +def interpretation_box(box: InterpretationBox, **options): + boxes = box.boxes + origin = box.expr if origin.has_form("InputForm", None): # InputForm produce outputs of the form # InterpretationBox[Style[_String, ...], origin_InputForm, opts___] @@ -170,9 +173,13 @@ def interpretation_box(self, **options): add_conversion_fn(InterpretationBox, interpretation_box) -def pane_box(self, **options): - content = lookup_conversion_method(self.boxes, "mathml")(self.boxes, **options) - options = self.box_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_attributes size = options.get("System`ImageSize", SymbolAutomatic).to_python() if size is SymbolAutomatic: width = "" @@ -201,34 +208,49 @@ def pane_box(self, **options): dims += "overflow:hidden;" dims = f' style="{dims}" ' if dims: - return f"\n{content}\n" - return content + return f"{indent_spaces}\n{content}\n{indent_spaces}" + return f"{indent_spaces}{content}" add_conversion_fn(PaneBox, pane_box) -def fractionbox(self, **options) -> str: - _options = self.box_options.copy() - _options.update(options) - options = _options - return "\n%s\n%s\n" % ( - lookup_conversion_method(self.num, "mathml")(self.num, **options), - lookup_conversion_method(self.den, "mathml")(self.den, **options), +def fractionbox(box: FractionBox, **options) -> str: + indent_level = box.box_attributes.get( + "indent_level", options.get("indent_level", 0) + ) + indent_spaces = " " * indent_level + indent_level += 1 + has_nonbox_children = False + + for child_box in (box.num, box.den): + if hasattr(child_box, "box_attributes"): + child_box.box_attributes["indent_level"] = indent_level + else: + has_nonbox_children = True + + if has_nonbox_children: + # non_boxed children have to get indent_level information passed down + # via a parameter. Here it is the "options" variable. (Which is a bad name). + child_options = {**options, "indent_level": indent_level} + + return f"{indent_spaces}\n%s\n%s\n{indent_spaces}" % ( + lookup_conversion_method(box.num, "mathml")(box.num, **child_options), + lookup_conversion_method(box.den, "mathml")(box.den, **child_options), ) add_conversion_fn(FractionBox, fractionbox) -def gridbox(self, elements=None, **box_options) -> str: +def gridbox(box: GridBox, elements=None, **box_attributes) -> str: def boxes_to_mathml(box, **options): return lookup_conversion_method(box, "mathml")(box, **options) if not elements: - elements = self._elements - evaluation = box_options.get("evaluation") - items, options = self.get_array(elements, evaluation) + elements = box._elements + evaluation = box_attributes.get("evaluation") + items, options = box.get_array(elements, evaluation) num_fields = max(len(item) if isinstance(item, tuple) else 1 for item in items) attrs = {} @@ -243,18 +265,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"\n" - new_box_options = box_options.copy() - new_box_options["inside_list"] = True + indent_level = options.get("indent_level", 0) + indent_spaces = " " * indent_level + result = f"{indent_spaces}\n" + new_box_attributes = box_attributes.copy() + new_box_attributes["inside_list"] = True + new_box_attributes["indent_level"] = indent_level + 3 + for row in items: - result += "" + result += f"{indent_spaces} " if isinstance(row, tuple): for item in row: - result += f"{boxes_to_mathml(item, **new_box_options)}" + new_box_attributes["indent_level"] = indent_level + 4 + result += f"\n{indent_spaces} \n{boxes_to_mathml(item, **new_box_attributes)}\n{indent_spaces} " else: - result += f"{boxes_to_mathml(row, **new_box_options)}" - result += "\n" - result += "" + result += f"\n{indent_spaces} \n{boxes_to_mathml(row, **new_box_attributes)}\n{indent_spaces} " + result += f"\n{indent_spaces} \n" + result += f"{indent_spaces}" # print(f"gridbox: {result}") return result @@ -262,67 +289,84 @@ def boxes_to_mathml(box, **options): add_conversion_fn(GridBox, gridbox) -def sqrtbox(self, **options): - _options = self.box_options.copy() +def sqrtbox(box: SqrtBox, **options): + _options = box.box_attributes.copy() _options.update(options) options = _options - if self.index: - return " %s %s " % ( - lookup_conversion_method(self.radicand, "mathml")(self.radicand, **options), - lookup_conversion_method(self.index, "mathml")(self.index, **options), + indent_level = options.get("indent_level", 0) + indent_spaces = " " * indent_level + options["indent_level"] = indent_level + 1 + + if box.index: + return f"{indent_spaces}\n%s\n%s\n{indent_spaces}" % ( + lookup_conversion_method(box.radicand, "mathml")(box.radicand, **options), + lookup_conversion_method(box.index, "mathml")(box.index, **options), ) - return "\n%s\n" % lookup_conversion_method(self.radicand, "mathml")( - self.radicand, **options + return ( + f"{indent_spaces}\n%s\n{indent_spaces}" + % lookup_conversion_method(box.radicand, "mathml")(box.radicand, **options) ) add_conversion_fn(SqrtBox, sqrtbox) -def subscriptbox(self, **options): - _options = self.box_options.copy() +def subscriptbox(box: SqrtBox, **options): + _options = box.box_attributes.copy() _options.update(options) options = _options - return "\n%s\n%s\n" % ( - lookup_conversion_method(self.base, "mathml")(self.base, **options), - lookup_conversion_method(self.subindex, "mathml")(self.subindex, **options), + indent_level = options.get("indent_level", 0) + indent_spaces = " " * indent_level + options["indent_level"] = indent_level + 1 + return f"{indent_spaces}\n%s\n%s\n{indent_spaces}" % ( + lookup_conversion_method(box.base, "mathml")(box.base, **options), + lookup_conversion_method(box.subindex, "mathml")(box.subindex, **options), ) add_conversion_fn(SubscriptBox, subscriptbox) -def superscriptbox(self, **options): - _options = self.box_options.copy() +def superscriptbox(box: SuperscriptBox, **options): + _options = box.box_attributes.copy() _options.update(options) options = _options - return "\n%s\n%s\n" % ( - lookup_conversion_method(self.base, "mathml")(self.base, **options), - lookup_conversion_method(self.superindex, "mathml")(self.superindex, **options), + indent_level = options.get("indent_level", 0) + indent_spaces = " " * indent_level + options["indent_level"] = indent_level + 1 + + return f"{indent_spaces}\n%s\n%s\n{indent_spaces}" % ( + lookup_conversion_method(box.base, "mathml")(box.base, **options), + lookup_conversion_method(box.superindex, "mathml")(box.superindex, **options), ) add_conversion_fn(SuperscriptBox, superscriptbox) -def subsuperscriptbox(self, **options): - _options = self.box_options.copy() +def subsuperscriptbox(box: SubscriptBox, **options): + _options = box.box_attributes.copy() _options.update(options) options = _options options["inside_row"] = True - return "\n%s\n%s\n%s\n" % ( - 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), + + indent_level = options.get("indent_level", 0) + indent_spaces = " " * indent_level + options["indent_level"] = indent_level + 1 + + return f"{indent_spaces}\n%s\n%s\n%s\n{indent_spaces}" % ( + 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), ) add_conversion_fn(SubsuperscriptBox, subsuperscriptbox) -def rowbox(self, **options) -> str: - _options = self.box_options.copy() +def rowbox(box: RowBox, **options) -> str: + _options = box.box_attributes.copy() _options.update(options) options = _options result = [] @@ -337,16 +381,16 @@ def is_list_interior(content): is_list_row = False if ( - len(self.items) >= 3 - and self.items[0].get_string_value() == "{" - and self.items[2].get_string_value() == "}" - and self.items[1].has_form("RowBox", 1, None) + len(box.items) >= 3 + and box.items[0].get_string_value() == "{" + and box.items[2].get_string_value() == "}" + and box.items[1].has_form("RowBox", 1, None) ): - content = self.items[1].items + content = box.items[1].items if is_list_interior(content): is_list_row = True - if not inside_row and is_list_interior(self.items): + if not inside_row and is_list_interior(box.items): is_list_row = True if is_list_row: @@ -354,31 +398,36 @@ def is_list_interior(content): else: options["inside_row"] = True - for element in self.items: + indent_level = options.get("indent_level", 0) + indent_spaces = " " * indent_level + options["indent_level"] = indent_level + 1 + + for element in box.items: + # Propagate properties down to box children. + # The below test could also be done via isinstance of of Box. result.append(lookup_conversion_method(element, "mathml")(element, **options)) # print(f"mrow: {result}") - - return "\n%s\n" % "\n".join(result) + return f"{indent_spaces}\n%s\n{indent_spaces}" % ("\n".join(result),) add_conversion_fn(RowBox, rowbox) -def stylebox(self, **options) -> str: - _options = self.box_options.copy() +def stylebox(box: StyleBox, **options) -> str: + _options = box.box_attributes.copy() _options.update(options) options = _options - return lookup_conversion_method(self.boxes, "mathml")(self.boxes, **options) + return lookup_conversion_method(box.boxes, "mathml")(box.boxes, **options) add_conversion_fn(StyleBox, stylebox) -def graphicsbox(self, elements=None, **options) -> str: +def graphicsbox(box: GraphicsBox, elements=None, **options) -> str: # FIXME: SVG is the only thing we can convert MathML into. # Handle other graphics formats. - svg_body = self.boxes_to_svg(elements, **options) + svg_body = box.boxes_to_svg(elements, **options) # mglyph, which is what we have been using, is bad because MathML standard changed. # metext does not work because the way in which we produce the svg images is also based on this outdated mglyph @@ -390,10 +439,14 @@ def graphicsbox(self, elements=None, **options) -> str: ) # print(svg_body) mathml = template % ( - int(self.width), - int(self.height), + int(box.width), + int(box.height), 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 @@ -401,19 +454,22 @@ def graphicsbox(self, elements=None, **options) -> str: add_conversion_fn(GraphicsBox, graphicsbox) -def graphics3dbox(self, elements=None, **options) -> str: +def graphics3dbox(box: Graphics3DBox, elements=None, **options) -> str: """Turn the Graphics3DBox into a MathML string""" - json_repr = self.boxes_to_json(elements, **options) + json_repr = box.boxes_to_json(elements, **options) mathml = f'' mathml = f"\n\n\n{mathml}\n\n\n" + indent_level = options.get("indent_level", 0) + if indent_level: + mathml = " " * indent_level + mathml return mathml add_conversion_fn(Graphics3DBox, graphics3dbox) -def tag_and_form_box(self, **options): - return lookup_conversion_method(self.boxes, "mathml")(self.boxes, **options) +def tag_and_form_box(box, **options): + return lookup_conversion_method(box.boxes, "mathml")(box.boxes, **options) add_conversion_fn(FormBox, tag_and_form_box) diff --git a/mathics/format/render/text.py b/mathics/format/render/text.py index 249a64dcb..fcd28dfc0 100644 --- a/mathics/format/render/text.py +++ b/mathics/format/render/text.py @@ -61,7 +61,7 @@ def pane_box(self, **options): def fractionbox(self, **options) -> str: - _options = self.box_options.copy() + _options = self.box_attributes.copy() _options.update(options) options = _options num_text = boxes_to_text(self.num, **options) @@ -77,13 +77,13 @@ def fractionbox(self, **options) -> str: add_conversion_fn(FractionBox, fractionbox) -def gridbox(self, elements=None, **box_options) -> str: +def gridbox(self, elements=None, **box_attributes) -> str: if not elements: elements = self.items - evaluation = box_options.get("evaluation", None) + evaluation = box_attributes.get("evaluation", None) items, options = self.get_array(elements, evaluation) - box_options.update(self.options) + box_attributes.update(self.options) if not items: return "" @@ -92,11 +92,11 @@ def gridbox(self, elements=None, **box_options) -> str: ( [ # TODO: check if this evaluation is necessary. - boxes_to_text(item, **box_options) + boxes_to_text(item, **box_attributes) for item in row ] if isinstance(row, tuple) - else boxes_to_text(row, **box_options) + else boxes_to_text(row, **box_attributes) ) for row in items ] @@ -111,7 +111,7 @@ def gridbox(self, elements=None, **box_options) -> str: def sqrtbox(self, **options) -> str: - _options = self.box_options.copy() + _options = self.box_attributes.copy() _options.update(options) options = _options if self.index: @@ -126,7 +126,7 @@ def sqrtbox(self, **options) -> str: def superscriptbox(self, **options) -> str: - _options = self.box_options.copy() + _options = self.box_attributes.copy() _options.update(options) options = _options no_parenthesize = True @@ -150,7 +150,7 @@ def superscriptbox(self, **options) -> str: def subscriptbox(self, **options) -> str: - _options = self.box_options.copy() + _options = self.box_attributes.copy() _options.update(options) options = _options return "Subscript[%s, %s]" % ( @@ -163,7 +163,7 @@ def subscriptbox(self, **options) -> str: def subsuperscriptbox(self, **options) -> str: - _options = self.box_options.copy() + _options = self.box_attributes.copy() _options.update(options) options = _options return "Subsuperscript[%s, %s, %s]" % ( @@ -177,7 +177,7 @@ def subsuperscriptbox(self, **options) -> str: def rowbox(self, elements=None, **options) -> str: - _options = self.box_options.copy() + _options = self.box_attributes.copy() _options.update(options) options = _options parts_str = [boxes_to_text(element, **options) for element in self.items] @@ -209,7 +209,7 @@ def rowbox(self, elements=None, **options) -> str: def stylebox(self, **options) -> str: options.pop("evaluation", None) - _options = self.box_options.copy() + _options = self.box_attributes.copy() _options.update(options) options = _options return boxes_to_text(self.boxes, **options) diff --git a/test/format/format_tests.yaml b/test/format/format_tests.yaml index 6254bbd02..b4bcd921d 100644 --- a/test/format/format_tests.yaml +++ b/test/format/format_tests.yaml @@ -139,8 +139,8 @@ mathml: System`InputForm: -4 System`OutputForm: -4 - System`StandardForm: "\n-\n4\n" - System`TraditionalForm: "\n-\n4\n" + System`StandardForm: "\n -\n 4\n" + System`TraditionalForm: "\n -\n 4\n" text: System`InputForm: '-4' System`OutputForm: '-4' @@ -158,8 +158,8 @@ mathml: System`InputForm: -4.32 System`OutputForm: -4.32 - System`StandardForm: "\n-\n4.32\n" - System`TraditionalForm: "\n-\n4.32\n" + System`StandardForm: "\n -\n 4.32\n" + System`TraditionalForm: "\n -\n 4.32\n" text: System`InputForm: '-4.32' System`OutputForm: '-4.32' @@ -177,8 +177,8 @@ mathml: System`InputForm: -4.33 System`OutputForm: -4.3 - System`StandardForm: "\n-\n4.33\n" - System`TraditionalForm: "\n-\n4.33\n" + System`StandardForm: "\n -\n 4.33\n" + System`TraditionalForm: "\n -\n 4.33\n" text: System`InputForm: -4.33`2. System`OutputForm: '-4.3' @@ -196,8 +196,8 @@ mathml: System`InputForm: -4.32 System`OutputForm: -4.320 - System`StandardForm: "\n-\n4.32\n" - System`TraditionalForm: "\n-\n4.32\n" + System`StandardForm: "\n -\n 4.32\n" + System`TraditionalForm: "\n -\n 4.32\n" text: System`InputForm: -4.32`4. System`OutputForm: '-4.320' @@ -271,10 +271,12 @@ System`OutputForm: - '1 / (1 + 1 / (1 + 1 / a))' - Fragile! - System`StandardForm: &id001 - - "\n1\n\n1\n+\n\n1\n\n1\n+\n\n1\na\n\n\n\n\n" + System`StandardForm: + - "\n 1\n \n 1\n +\n \n 1\n \n 1\n +\n \n 1\n a\n \n \n \n \n" + - Fragile! + System`TraditionalForm: + - "\n 1\n \n 1\n +\n \n 1\n \n 1\n +\n \n 1\n a\n \n \n \n \n" - Fragile! - System`TraditionalForm: *id001 text: System`InputForm: 1/(1 + 1/(1 + 1/a)) System`OutputForm: 1 / (1 + 1 / (1 + 1 / a)) @@ -292,8 +294,8 @@ mathml: System`InputForm: <|a -> x, b -> y, c -> <|d -> t|>|> System`OutputForm: '<|a -> x, b -> y, c -> <|d -> t|>|>' - System`StandardForm: "\n<|\n\n\na\n->\nx\n\n,\n\nb\n->\ny\n\n,\n\nc\n->\n\n<|\n\nd\n->\nt\n\n|>\n\n\n\n|>\n" - System`TraditionalForm: "\n<|\n\n\na\n->\nx\n\n,\n\nb\n->\ny\n\n,\n\nc\n->\n\n<|\n\nd\n->\nt\n\n|>\n\n\n\n|>\n" + System`StandardForm: "\n <|\n \n \n a\n ->\n x\n \n ,\n \n b\n ->\n y\n \n ,\n \n c\n ->\n \n <|\n \n d\n ->\n t\n \n |>\n \n \n \n |>\n" + System`TraditionalForm: "\n <|\n \n \n a\n ->\n x\n \n ,\n \n b\n ->\n y\n \n ,\n \n c\n ->\n \n <|\n \n d\n ->\n t\n \n |>\n \n \n \n |>\n" text: System`InputForm: <|a -> x, b -> y, c -> <|d -> t|>|> System`OutputForm: <|a -> x, b -> y, c -> <|d -> t|>|> @@ -311,8 +313,8 @@ Association[a -> x, b -> y, c -> Association[d -> t, Association[e -> u]]]: mathml: System`InputForm: "<|a -> x, b -> y, c -> <|d -> t, e -> u|>|>" System`OutputForm: '<|a -> x, b -> y, c -> <|d -> t, e -> u|>|>' - System`StandardForm: "\n<|\n\n\na\n->\nx\n\n,\n\nb\n->\ny\n\n,\n\nc\n->\n\n<|\n\n\nd\n->\nt\n\n,\n\ne\n->\nu\n\n\n|>\n\n\n\n|>\n" - System`TraditionalForm: "\n<|\n\n\na\n->\nx\n\n,\n\nb\n->\ny\n\n,\n\nc\n->\n\n<|\n\n\nd\n->\nt\n\n,\n\ne\n->\nu\n\n\n|>\n\n\n\n|>\n" + System`StandardForm: "\n <|\n \n \n a\n ->\n x\n \n ,\n \n b\n ->\n y\n \n ,\n \n c\n ->\n \n <|\n \n \n d\n ->\n t\n \n ,\n \n e\n ->\n u\n \n \n |>\n \n \n \n |>\n" + System`TraditionalForm: "\n <|\n \n \n a\n ->\n x\n \n ,\n \n b\n ->\n y\n \n ,\n \n c\n ->\n \n <|\n \n \n d\n ->\n t\n \n ,\n \n e\n ->\n u\n \n \n |>\n \n \n \n |>\n" text: System`InputForm: <|a -> x, b -> y, c -> <|d -> t, e -> u|>|> System`OutputForm: <|a -> x, b -> y, c -> <|d -> t, e -> u|>|> @@ -330,8 +332,8 @@ Complex[1.09*^12, 3.]: mathml: System`InputForm: 1.09*^12 + 3.*I System`OutputForm: '1.09×10^12 + 3. I' - System`StandardForm: "\n\n1.09\n*^\n12\n\n+\n\n3.\n \nI\n\n" - System`TraditionalForm: "\n\n1.09\n×\n\n10\n12\n\n\n+\n\n3.\n\nI\n\n" + System`StandardForm: "\n \n 1.09\n *^\n 12\n \n +\n \n 3.\n  \n I\n \n" + System`TraditionalForm: "\n \n 1.09\n ×\n \n 10\n 12\n \n \n +\n \n 3.\n \n I\n \n" text: System`InputForm: 1.09*^12 + 3.*I System`OutputForm: "1.09\xD710^12 + 3. I" @@ -458,18 +460,8 @@ Graphics[{}]: mathml: System`InputForm: "Grid[{{"Spanish", "Hola!"}, {"Portuguese", "Olà!"}, {"English", "Hi!"}}]" System`OutputForm: 'Spanish      Hola!Portuguese   Olà!English      Hi!' - System`StandardForm: "\nSpanishHola!\n\ - PortugueseOl\xE0!\nEnglishHi!\n\ - " - System`TraditionalForm: "\nSpanishHola!\n\ - PortugueseOl\xE0!\nEnglishHi!\n\ - " + System`StandardForm: "\n \n \n Spanish\n \n \n Hola!\n \n \n \n \n Portuguese\n \n \n Olà!\n \n \n \n \n English\n \n \n Hi!\n \n \n" + System`TraditionalForm: "\n \n \n Spanish\n \n \n Hola!\n \n \n \n \n Portuguese\n \n \n Olà!\n \n \n \n \n English\n \n \n Hi!\n \n \n" text: System`InputForm: "Grid[{{\"Spanish\", \"Hola!\"}, {\"Portuguese\", \"Ol\xE0!\"\ }, {\"English\", \"Hi!\"}}]" @@ -497,20 +489,8 @@ Grid[{{a,b},{c,d}}]: mathml: System`InputForm: Grid[{{a, b}, {c, d}}] System`OutputForm: 'a   bc   d' - System`StandardForm: ' - - ab - - cd - - ' - System`TraditionalForm: ' - - ab - - cd - - ' + System`StandardForm: "\n \n \n a\n \n \n b\n \n \n \n \n c\n \n \n d\n \n \n" + System`TraditionalForm: "\n \n \n a\n \n \n b\n \n \n \n \n c\n \n \n d\n \n \n" text: System`InputForm: Grid[{{a, b}, {c, d}}] System`OutputForm: 'a b @@ -543,8 +523,8 @@ Integrate[F[x], {x, a, g[b]}]: mathml: System`InputForm: 'Integrate[F[x], {x, a, g[b]}]' System`OutputForm: 'Integrate[F[x], {x, a, g[b]}]' - System`StandardForm: "\n\n\na\n\ng\n[\nb\n]\n\n\n\n\nF\n[\nx\n]\n\n\n\n𝑑\nx\n\n" - System`TraditionalForm: "\n\n\na\n\ng\n(\nb\n)\n\n\n\n\nF\n(\nx\n)\n\n\n\n𝑑\nx\n\n" + System`StandardForm: "\n \n \n a\n \n g\n [\n b\n ]\n \n \n \u2062\n \n F\n [\n x\n ]\n \n \u2062\n \n 𝑑\n x\n \n" + System`TraditionalForm: "\n \n \n a\n \n g\n (\n b\n )\n \n \n \u2062\n \n F\n (\n x\n )\n \n \u2062\n \n 𝑑\n x\n \n" text: System`InputForm: Integrate[F[x], {x, a, g[b]}] System`OutputForm: Integrate[F[x], {x, a, g[b]}] @@ -568,8 +548,8 @@ MatrixForm[{{a,b},{c,d}}]: mathml: System`InputForm: MatrixForm[{{a, b}, {c, d}}] System`OutputForm: 'a   bc   d' - System`StandardForm: "\n(\n\nab\ncd\n\n)\n" - System`TraditionalForm: "\n(\n\nab\ncd\n\n)\n" + System`StandardForm: "\n (\n\n \n \n a\n \n \n b\n \n \n \n \n c\n \n \n d\n \n \n\n )\n" + System`TraditionalForm: "\n (\n\n \n \n a\n \n \n b\n \n \n \n \n c\n \n \n d\n \n \n\n )\n" text: System`InputForm: MatrixForm[{{a, b}, {c, d}}] System`OutputForm: 'a b @@ -607,10 +587,10 @@ Sqrt[1/(1+1/(1+1/a))]: - 'Sqrt[1 / (1 + 1 / (1 + 1 / a))]' - Fragile! System`StandardForm: - - "\n\n1\n\n1\n+\n\n1\n\n1\n+\n\n1\na\n\n\n\n\n\n" + - "\n \n 1\n \n 1\n +\n \n 1\n \n 1\n +\n \n 1\n a\n \n \n \n \n \n" - Fragile! System`TraditionalForm: - - "\n\n1\n\n1\n+\n\n1\n\n1\n+\n\n1\na\n\n\n\n\n\n" + - "\n \n 1\n \n 1\n +\n \n 1\n \n 1\n +\n \n 1\n a\n \n \n \n \n \n" - Fragile! text: System`InputForm: Sqrt[1/(1 + 1/(1 + 1/a))] @@ -633,8 +613,8 @@ Subscript[a, 4]: System`OutputForm: - 'Subscript[a, 4]' - Fragile! - System`StandardForm: "\na\n4\n" - System`TraditionalForm: "\na\n4\n" + System`StandardForm: "\n a\n 4\n" + System`TraditionalForm: "\n a\n 4\n" text: System`InputForm: Subscript[a, 4] System`OutputForm: Subscript[a, 4] @@ -654,8 +634,8 @@ Subsuperscript[a, p, q]: mathml: System`InputForm: Subsuperscript[a, p, q] System`OutputForm: 'Subsuperscript[a, p, q]' - System`StandardForm: "\na\np\nq\n" - System`TraditionalForm: "\na\np\nq\n" + System`StandardForm: "\n a\n p\n q\n" + System`TraditionalForm: "\n a\n p\n q\n" text: System`InputForm: Subsuperscript[a, p, q] System`OutputForm: Subsuperscript[a, p, q] @@ -716,8 +696,8 @@ TableForm[{{a,b},{c,d}}]: mathml: System`InputForm: TableForm[{{a, b}, {c, d}}] System`OutputForm: 'a   bc   d' - System`StandardForm: "\nab\ncd\n" - System`TraditionalForm: "\nab\ncd\n" + System`StandardForm: "\n \n \n a\n \n \n b\n \n \n \n \n c\n \n \n d\n \n \n" + System`TraditionalForm: "\n \n \n a\n \n \n b\n \n \n \n \n c\n \n \n d\n \n \n" text: System`InputForm: TableForm[{{a, b}, {c, d}}] System`OutputForm: 'a b @@ -807,7 +787,7 @@ a^(g[b]/c): mathml: System`InputForm: a^(g[b]/c) System`OutputForm: 'a ^ (g[b] / c)' - System`TraditionalForm: "\na\n\n\ng\n(\nb\n)\n\nc\n\n" + System`TraditionalForm: "\n a\n \n \n g\n (\n b\n )\n \n c\n \n" text: System`InputForm: a^(g[b]/c) System`OutputForm: a ^ (g[b] / c) @@ -825,8 +805,8 @@ a^4: mathml: System`InputForm: 'a^4' System`OutputForm: 'a ^ 4' - System`StandardForm: "\na\n4\n" - System`TraditionalForm: "\na\n4\n" + System`StandardForm: "\n a\n 4\n" + System`TraditionalForm: "\n a\n 4\n" text: System`InputForm: a^4 System`OutputForm: a ^ 4 @@ -870,7 +850,7 @@ a+PrecedenceForm[b+c,10]: System`StandardForm: 'a+\left(b+c\right)' mathml: System`OutputForm: 'a + (b + c)' - System`StandardForm: "\na\n+\n\n(\n\nb\n+\nc\n\n)\n\n" + System`StandardForm: "\n a\n +\n \n (\n \n b\n +\n c\n \n )\n \n" text: System`InputForm: 'a + (PrecedenceForm[b + c, 10])' System`OutputForm: 'a + (b + c)' diff --git a/test/format/test_format.py b/test/format/test_format.py index ba469dafc..adf27bbc9 100644 --- a/test/format/test_format.py +++ b/test/format/test_format.py @@ -50,8 +50,6 @@ def load_tests(key): of the final formats ("text", "latex", "mathml") and produce two list: the first with the mandatory tests, and the other with "fragile" tests """ - global all_tests - global MATHML_STRICT def is_fragile(assert_msg: str) -> bool: """ diff --git a/test/test_session.py b/test/test_session.py index 4f04fb0df..08d779a1f 100644 --- a/test/test_session.py +++ b/test/test_session.py @@ -55,7 +55,7 @@ def test_session_format_evaluation(): assert session.format_result(form="text") == "a / b" assert session.format_result(form="latex") == "\\frac{a}{b}" assert session.format_result(form="xml") == ( - '\na\nb\n' + '\n \n a\n b\n \n' )