4
$\begingroup$

Prior to v12.1 Row was not handled well by MathML export:

enter image description here

Now it is ok:

ExportString[Row[{1}], "MathML"]

 <math xmlns='http://www.w3.org/1998/Math/MathML'>
 <semantics>
  <mrow>
   <mn>1</mn>
  </mrow>
  <annotation encoding='Mathematica'>TemplateBox[List[&quot;1&quot;], &quot;RowDefault&quot;]</annotation>
 </semantics>
</math>

But I need to fix that issue for projects where earlier versions are used.

Is there a general way to add conversion rules or at least a fix for Row?

$\endgroup$
  • 3
    $\begingroup$ Looks like the relevant function is System`Convert`MathMLDump`BoxesToSMML- if you want to look at it with GeneralUtiities`PrintDefinitions, you'll probably need to remove the limit on the number of definitions with DownValues@GeneralUtilities`PrintDefinitions = DownValues@GeneralUtilities`PrintDefinitions /. 256 -> \[Infinity]; or similar $\endgroup$ – Lukas Lang Jul 31 at 15:42
4
$\begingroup$

Expanding my comment into a concrete answer: Using GeneralUtilities`PrintDefinitions to look at the functions responsible for the "MathML" export1, it looks like the core function is System`Convert`MathMLDump`BoxesToSMML:

(* remove limit on number of definitions *)
DownValues@GeneralUtilities`PrintDefinitions =  
  DownValues@GeneralUtilities`PrintDefinitions /. 256 -> Infinity;
(* print definitions of the function *)
GeneralUtilities`PrintDefinitions@System`Convert`MathMLDump`BoxesToSMML;

The following sections outline how to fix the following three issues:

  1. ExportString[Row[{1}], "MathML"] doesn't work before version 12.1 due to missing transformation rules for TemplateBox
  2. ExportString[ToBoxes[a==b],"MathML"] exports the == as &#10869;, since the character mappings are not set properly
  3. ExportString[a==b,"MathML"] exports the == as &#63449; (even with the fix for 2.), since there is no mapping for \[LongEqual] (the TraditionalForm version of ==) in the default mappings

Adding rules for box structures

In version 12.1, there are two definitions that seem to be responsible for the conversion of TemplateBox expressions (which is what Row is converted to by ToBoxes) - extracting them and adding them in e.g. 11.3 yields the following result:

Begin["System`Convert`MathMLDump`"];

BoxesToSMML[whole : TemplateBox[{__}, _, ___]] /; ! alreadyWrapped := Block[
   {alreadyWrapped = True},
   XMLElement[
    "semantics",
    {},
    {
     BoxesToSMML@BoxForm`TemplateBoxToDisplayBoxes@whole,
     BoxesToSMML@annotationWrapper@TagBox[whole, "MathMLMathematicaTag"]
     }
    ]
   ];

BoxesToSMML[whole : TemplateBox[{___}, _, ___]] := 
  BoxesToSMML@BoxForm`TemplateBoxToDisplayBoxes@whole;

End[];

ExportString[Row[{1}], "MathML"]
<math xmlns='http://www.w3.org/1998/Math/MathML'>
 <semantics>
  <mrow>
   <mn>1</mn>
  </mrow>
  <annotation encoding='Mathematica'>TemplateBox[List[&quot;1&quot;], &quot;RowDefault&quot;]</annotation>
 </semantics>
</math>

The definitions effectively use BoxForm`TemplateBoxToDisplayBoxes to convert arbitrary TemplateBox expressions to their resulting boxes, so this should work for (almost) all TemplateBox expressions, not just Row.

Specifying the mappings for special characters

It looks like special characters are not exported properly, even though there is a list for special character mappings for MathML2:

ExportString[ToBoxes[a == b], "MathML"]
<math xmlns='http://www.w3.org/1998/Math/MathML'>
 <mrow>
  <mi>a</mi>
  <mo>&#10869;</mo>
  <mi>b</mi>
 </mrow>
</math>

The list of mappings (which is not used by default for some reason) does include a case for \[Equal]:

System`Convert`MLStringDataDump`$UnicodeToMathML2Entities
(* {..., "\[Equal]" -> "&Equal;" ,...} *)

Luckily, the necessary option for System`Convert`XMLDump`WriteSymbolicXML is passed through from the top-level, so we can just add it:

ExportString[ToBoxes[a == b], "MathML", "Entities" -> "MathML"]
<math xmlns='http://www.w3.org/1998/Math/MathML'>
 <mrow>
  <mi>a</mi>
  <mo>&Equal;</mo>
  <mi>b</mi>
 </mrow>
</math>

Possible values for "Entities" can be found in the definition of System`Convert`XMLDump`addEntitiesToExportFunction:

"HTML"
"MathML"
"MathML1"
"MathML2"
_String->_String
_String:>_String (* equivalent to the previous, the delayed evaluation doesn't work *)
{...} (* list of allowed values *)

Adding custom rules to the character mappings

Unfortunately, the above fix is not enough for a==b:

ExportString[a == b, "MathML", "Entities" -> "MathML"]

<math xmlns='http://www.w3.org/1998/Math/MathML'>
 <mrow>
  <mi>a</mi>
  <mo>&#63449;</mo>
  <mi>b</mi>
 </mrow>
</math>

The issue is that by default, the boxes are created using MakeBoxes[expr,TraditionalForm], which formats == as \[LongEqual], which is not in the list of mappings. Since System`Convert`XMLDump`determineEntityExportFunction memoizes the conversion functions and since the first call to the MathML exporter resets the list of mappings, we have to be a bit creative to get the mapping in there:

(* needs fresh kernel session *)
(* load the MathML exporter, but without setting "Entities" to avoid memoization *)
ExportString[a == b, "MathML", "Entities" -> {"x" -> "x"}];

(* add custom character mappings *)
System`Convert`MLStringDataDump`$UnicodeToMathML2Entities = 
  System`Convert`MLStringDataDump`$UnicodeToMathML2Entities~Union~{
    "\[LongEqual]" -> "&Equal;"
};

(* now it works *)
ExportString[a == b, "MathML", "Entities" -> "MathML"]
<math xmlns='http://www.w3.org/1998/Math/MathML'>
 <mrow>
  <mi>a</mi>
  <mo>&Equal;</mo>
  <mi>b</mi>
 </mrow>
</math>

1For the curious: The call tree essentially looks like this:

  • ExportString to Export (which is used to export to a temporary file):
    • ExportString
    • System`ConvertersDump`ExportStringTest
    • Export
  • Export to System`ConvertersDump`export (this is the function which calls the correct function for a given format)
    • Export
    • System`ConvertersDump`ExportTest
    • System`ConvertersDump`ExportInternal
    • System`ConvertersDump`export
  • System`ConvertersDump`export to System`Convert`MathMLDump`BoxesToSMML (which is the function we're interested in)
    • System`ConvertersDump`export
    • System`Convert`MathMLDump`mathMLExport
    • System`Convert`MathMLDump`exportMathMLAux
    • System`Convert`MathMLDump`exportFunction
    • XML`MathML`BoxesToSymbolicMathML
    • System`Convert`MathMLDump`includeFormat
    • System`Convert`MathMLDump`BoxesToSMML

2The relevant part of the call tree here is (starting from above):

  • see above until System`Convert`MathMLDump`exportMathMLAux
  • System`Convert`XMLDump`WriteSymbolicXML
  • System`Convert`XMLDump`determineEntityExportFunction
  • System`Convert`XMLDump`addEntitiesToExportFunction
| improve this answer | |
$\endgroup$
  • 1
    $\begingroup$ Thanks for your time! Can you add a piece for the part where XMLElements are converted to the final string? I think I need to adjust that part to solve and issue I have with == being exported as a non standard unicode character: ExportString[a == b, "MathML"] $\endgroup$ – Kuba Aug 4 at 10:53
  • 2
    $\begingroup$ @Kuba It took some trying, but I found an ok-ish workaround (added to the post). Instead of modifying the "MathML"mappings as in the third part, you could probably also specify them manually as "Entities"->{"MathML", "\[LongEqual]"->...} (not tested though), but I felt it's cleaner to modify the mappings themselves instead to include the missing rules $\endgroup$ – Lukas Lang Aug 4 at 17:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.