]> git.lyx.org Git - features.git/commitdiff
InsetMathBox: split the boxed text into <mtext> and other tags, while boxing the...
authorThibaut Cuvelier <tcuvelier@lyx.org>
Sun, 8 Jan 2023 01:27:36 +0000 (02:27 +0100)
committerThibaut Cuvelier <tcuvelier@lyx.org>
Sun, 8 Jan 2023 01:36:48 +0000 (02:36 +0100)
Rationales:
- previously, <mstyle> was used, but it's being deprecated for MathML 4 Core in favour of CSS and <mrow> (not a big deal in itself)
- the whole box cannot be hosted within the same tag, because neither <mstyle> nor <mrow> can have text, they need an intermediate container, <mtext> (which cannot hold anything else that pure text)
- new behaviour: always output a container for the whole box that has the right attributes, i.e. an <mrow>; split the content of the cell to have text and other tags set apart (text in <mtext>, other tags left as they were)

Old behaviour, invalid MathML (2 to 4):
<mstyle XXX>text<mn>.</mn></mstyle>

New behaviour, valid MathML:
<mrow XXX><mtext>text</mtext><mn>.</mn></mrow>

src/mathed/InsetMathBox.cpp

index f3ba7a7cb526ee6b555c66cf48ec2e8a4f28c52b..30cbd14356e2a7ad9ce49dbe10e8892325307dbc 100644 (file)
@@ -25,6 +25,7 @@
 #include "frontends/Painter.h"
 
 #include <algorithm>
+#include <iostream>
 #include <ostream>
 
 using namespace lyx::support;
@@ -57,16 +58,82 @@ void InsetMathBox::normalize(NormalStream & os) const
 }
 
 
+namespace {
+void splitAndWrapInMText(MathMLStream & ms, MathData const & cell,
+                                                const std::string & attributes)
+{
+       // First, generate the inset into a string of its own.
+       docstring inset_contents;
+       {
+               odocstringstream ostmp;
+               MathMLStream mstmp(ostmp, ms.xmlns());
+
+               SetMode textmode(mstmp, true);
+               mstmp << cell;
+
+               inset_contents = ostmp.str();
+       }
+
+       std::cout << '"' << to_ascii(inset_contents) << '"' << std::endl;
+
+       // No tags are allowed within <m:mtext>: split the string if there are tags.
+       std::vector<docstring> parts;
+       while (true) {
+               std::size_t angle_pos = inset_contents.find('<');
+               if (angle_pos == docstring::npos)
+                       break;
+
+               // String structure:
+               // - prefix: pure text, no tag
+               // - tag to split: something like <m:mn>1</m:mn> or more complicated
+               //   (like nested tags), with or without name space
+               // - rest to be taken care of in the next iteration
+
+               // Push the part before the tag.
+               parts.emplace_back(inset_contents.substr(0, angle_pos));
+               inset_contents = inset_contents.substr(angle_pos);
+               // Now, inset_contents starts with the tag to isolate, so that
+               //     inset_contents[0] == '<'
+
+               // Push the tag, up to its end. Process: find the tag name (either
+               // before > or the first attribute of the tag), then the matching end
+               // tag, then proceed with pushing.
+               const std::size_t tag_name_end =
+                               std::min(inset_contents.find(' ', 1), inset_contents.find('>', 1));
+               const std::size_t tag_name_length = tag_name_end - 1;
+               const docstring tag_name = inset_contents.substr(1, tag_name_length);
+
+               const std::size_t end_tag_start =
+                               inset_contents.find(tag_name, tag_name_end + 1);
+               const std::size_t end_tag = inset_contents.find('>', end_tag_start);
+
+               parts.emplace_back(inset_contents.substr(0, end_tag + 1));
+               inset_contents = inset_contents.substr(end_tag + 1);
+       }
+       parts.emplace_back(inset_contents);
+
+       // Finally, output the complete inset: escape the test in <m:mtext>, leave
+       // the other tags untouched.
+       ms << MTag("mrow", attributes);
+       for (int i = 0; i < parts.size(); i += 2) {
+               ms << MTag("mtext")
+                  << parts[i]
+                  << ETag("mtext");
+               if (parts.size() > i + 1)
+                       ms << parts[i + 1];
+       }
+       ms << ETag("mrow");
+}
+}
+
+
 void InsetMathBox::mathmlize(MathMLStream & ms) const
 {
        // FIXME XHTML
        // Need to do something special for tags here.
        // Probably will have to involve deferring them, which
        // means returning something from this routine.
-       SetMode textmode(ms, true);
-       ms << MTag("mtext", "class='mathbox'")
-          << cell(0)
-          << ETag("mtext");
+       splitAndWrapInMText(ms, cell(0), "class='mathbox'");
 }
 
 
@@ -165,10 +232,7 @@ void InsetMathFBox::normalize(NormalStream & os) const
 
 void InsetMathFBox::mathmlize(MathMLStream & ms) const
 {
-       SetMode textmode(ms, true);
-       ms << MTag("mtext", "class='fbox'")
-          << cell(0)
-          << ETag("mtext");
+       splitAndWrapInMText(ms, cell(0), "class='fbox'");
 }
 
 
@@ -311,10 +375,7 @@ void InsetMathMakebox::mathmlize(MathMLStream & ms) const
 {
        // FIXME We could do something with the other arguments.
        std::string const cssclass = framebox_ ? "framebox" : "makebox";
-       SetMode textmode(ms, true);
-       ms << MTag("mtext", "class='" + cssclass + "'")
-          << cell(2)
-          << ETag("mtext");
+       splitAndWrapInMText(ms, cell(2), "class='" + cssclass + "'");
 }
 
 
@@ -393,9 +454,7 @@ void InsetMathBoxed::infoize(odocstream & os) const
 
 void InsetMathBoxed::mathmlize(MathMLStream & ms) const
 {
-       ms << MTag("mtext", "class='boxed'")
-          << cell(0)
-          << ETag("mtext");
+       splitAndWrapInMText(ms, cell(0), "class='boxed'");
 }