commit e70cdfd3a222f12ab25a8c7296b180018bc06381
Author: Thibaut Cuvelier <tcuvel...@lyx.org>
Date:   Sun Jan 8 02:27:36 2023 +0100

    InsetMathBox: split the boxed text into <mtext> and other tags, while 
boxing the whole inset within an <mrow>.
    
    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 |   89 +++++++++++++++++++++++++++++++++++-------
 1 files changed, 74 insertions(+), 15 deletions(-)

diff --git a/src/mathed/InsetMathBox.cpp b/src/mathed/InsetMathBox.cpp
index f3ba7a7..30cbd14 100644
--- a/src/mathed/InsetMathBox.cpp
+++ b/src/mathed/InsetMathBox.cpp
@@ -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'");
 }
 
 
-- 
lyx-cvs mailing list
lyx-cvs@lists.lyx.org
http://lists.lyx.org/mailman/listinfo/lyx-cvs

Reply via email to