]> git.lyx.org Git - lyx.git/commitdiff
Fix crash when generating MathML with InsetMathBox
authorJean-Marc Lasgouttes <lasgouttes@lyx.org>
Sat, 20 Jul 2024 10:15:32 +0000 (12:15 +0200)
committerJean-Marc Lasgouttes <lasgouttes@lyx.org>
Mon, 22 Jul 2024 18:53:29 +0000 (20:53 +0200)
Instead of generating code and parsing it to add <mtext>...</mtext> at
the right spots, this commit honors the text mode setting that was
already present in the codebase to generate it automatically.

This is the work of two helper methods in MathMLStream:

* beforeText() notices when the stream is in text mode and that a
  <mtext> has not yet been generated. In this case it inserts it, so
  that raw text can be emitted afterwards.

* beforeTag() checks whether a <mtext> needs to be closed at this
  point, and does it if needed.

To make this work, the code now tracks the nesting level in the
stream, and compares it the what the level was when text mode has been
enabled using the SetMode helper function.

In order to avoid later bugs, member os() that allows to access the
underlying stream of MathMLStream is removed. This required many <<
operators to become friends of MathMLStream.

In InsetMathBox, rename splitAndWrapInMText() to mathmlizeHelper(),
which is not just a method that sets text mode inside a <mrow>
element.

In InsetMathFont and InsetMathHull, the explicit generation of nesting
in <mtext>...</mtext> can be removed now.

Fixes bug #13069.

src/mathed/InsetMath.cpp
src/mathed/InsetMathBox.cpp
src/mathed/InsetMathFont.cpp
src/mathed/InsetMathHull.cpp
src/mathed/MathStream.cpp
src/mathed/MathStream.h

index 5aea5716ce33c5e8b4be38dcab9a3488ec311856..5dee0c31ba4dd8c72f6b16ea6df3300a4b44e114 100644 (file)
@@ -204,10 +204,13 @@ void InsetMath::mathematica(MathematicaStream & os) const
 
 void InsetMath::mathmlize(MathMLStream & ms) const
 {
+       SetMode rawmode(ms, false);
        ms << "<!-- " << from_utf8(insetName(lyxCode())) << " -->";
        ms << MTagInline("mi");
-       NormalStream ns(ms.os());
+       odocstringstream ods;
+       NormalStream ns(ods);
        normalize(ns);
+       ms << ods.str();
        ms << ETagInline("mi");
 }
 
index 6feddf9cdd269578707d04e6decd145550cb2b94..f06ffcf95d747e29ab30a22500313108f694805b 100644 (file)
@@ -59,69 +59,19 @@ void InsetMathBox::normalize(NormalStream & os) const
 
 
 namespace {
-void splitAndWrapInMText(MathMLStream & ms, MathData const & cell,
-                                                const std::string & attributes)
+// Generate the MathML, making sure that anything that is outside of
+// any tag is wrapped in <mtext></mtext> tags, then wrap the whole thing in an
+// <mrow></mrow> tag with attributes
+void mathmlizeHelper(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();
-       }
-
-       // 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 (std::size_t i = 0; i < parts.size(); i += 2) {
-               ms << MTag("mtext")
-                  << parts[i]
-                  << ETag("mtext");
-               if (parts.size() > i + 1)
-                       ms << parts[i + 1];
+       {
+               SetMode textmode(ms, true);
+               ms << cell;
        }
        ms << ETag("mrow");
 }
+
 }
 
 
@@ -131,7 +81,7 @@ void InsetMathBox::mathmlize(MathMLStream & ms) const
        // Need to do something special for tags here.
        // Probably will have to involve deferring them, which
        // means returning something from this routine.
-       splitAndWrapInMText(ms, cell(0), "class='mathbox'");
+       mathmlizeHelper(ms, cell(0), "class='mathbox'");
 }
 
 
@@ -230,7 +180,7 @@ void InsetMathFBox::normalize(NormalStream & os) const
 
 void InsetMathFBox::mathmlize(MathMLStream & ms) const
 {
-       splitAndWrapInMText(ms, cell(0), "class='fbox'");
+       mathmlizeHelper(ms, cell(0), "class='fbox'");
 }
 
 
@@ -373,7 +323,7 @@ void InsetMathMakebox::mathmlize(MathMLStream & ms) const
 {
        // FIXME We could do something with the other arguments.
        std::string const cssclass = framebox_ ? "framebox" : "makebox";
-       splitAndWrapInMText(ms, cell(2), "class='" + cssclass + "'");
+       mathmlizeHelper(ms, cell(2), "class='" + cssclass + "'");
 }
 
 
@@ -452,7 +402,7 @@ void InsetMathBoxed::infoize(odocstream & os) const
 
 void InsetMathBoxed::mathmlize(MathMLStream & ms) const
 {
-       splitAndWrapInMText(ms, cell(0), "class='boxed'");
+       mathmlizeHelper(ms, cell(0), "class='boxed'");
 }
 
 
index 5687eed057165e1e7ed40e37ec35529568030ecb..25ee4ae53b625b2744557db6db6d6b3f851b067e 100644 (file)
@@ -220,9 +220,7 @@ void InsetMathFont::mathmlize(MathMLStream & ms) const
        if (tag == "text" || tag == "textnormal" || tag == "textrm" ||
                        tag == "textup" || tag == "textmd") {
                SetMode textmode(ms, true);
-               ms << MTagInline("mtext");
                ms << cell(0);
-               ms << ETagInline("mtext");
        } else if (!variant.empty()) {
                ms << MTag("mstyle", "mathvariant='" + variant + "'");
                ms << cell(0);
index 6bbd11ebaab5a1a3792ca16b6c8b0ad671defc4c..57c57f20eacd20e78deb940fcabc0508aa3823a2 100644 (file)
@@ -2565,8 +2565,10 @@ void InsetMathHull::mathmlize(MathMLStream & ms) const
                if (haveNumbers()) {
                        ms << MTag("mtd");
                        docstring const & num = numbers_[row];
-                       if (!num.empty())
-                               ms << MTagInline("mtext") << '(' << num << ')' << ETagInline("mtext");
+                       if (!num.empty()) {
+                               SetMode textmode(ms, true);
+                               ms << '(' << num << ')';
+                       }
                    ms << ETag("mtd");
                }
 
index dc4aa05df918656f87a8c2abc5cfd2b9773451f0..2951aeeefd03db5a6dc878b332dc7e88e28a139d 100644 (file)
@@ -291,7 +291,7 @@ TeXMathStream & operator<<(TeXMathStream & ws, unsigned int i)
 MathMLStream::MathMLStream(odocstream & os, std::string const & xmlns)
        : os_(os), xmlns_(xmlns)
 {
-       if (in_text_)
+       if (inText())
                font_math_style_ = TEXT_STYLE;
        else
                font_math_style_ = DISPLAY_STYLE;
@@ -300,9 +300,9 @@ MathMLStream::MathMLStream(odocstream & os, std::string const & xmlns)
 
 void MathMLStream::cr()
 {
-       os() << '\n';
+       os_ << '\n';
        for (int i = 0; i < tab(); ++i)
-               os() << ' ';
+               os_ << ' ';
 }
 
 
@@ -323,6 +323,23 @@ docstring MathMLStream::deferred() const
        return deferred_.str();
 }
 
+void MathMLStream::beforeText()
+{
+       if (!in_mtext_ && nesting_level_ == text_level_) {
+               *this << MTagInline("mtext");
+               in_mtext_ = true;
+       }
+}
+
+
+void MathMLStream::beforeTag()
+{
+       if (in_mtext_ && nesting_level_ == text_level_ + 1) {
+               in_mtext_ = false;
+               *this << ETagInline("mtext");
+       }
+}
+
 
 MathMLStream & operator<<(MathMLStream & ms, MathAtom const & at)
 {
@@ -340,7 +357,8 @@ MathMLStream & operator<<(MathMLStream & ms, MathData const & ar)
 
 MathMLStream & operator<<(MathMLStream & ms, docstring const & s)
 {
-       ms.os() << s;
+       ms.beforeText();
+       ms.os_ << s;
        return ms;
 }
 
@@ -368,51 +386,65 @@ MathMLStream & operator<<(MathMLStream & ms, char_type c)
 
 MathMLStream & operator<<(MathMLStream & ms, MTag const & t)
 {
+       ms.beforeTag();
+       SetMode rawmode(ms, false);
        ms.cr();
        ++ms.tab();
-       ms.os() << '<' << from_ascii(ms.namespacedTag(t.tag_));
+       ms.os_ << '<' << from_ascii(ms.namespacedTag(t.tag_));
        if (!t.attr_.empty())
-               ms.os() << " " << from_ascii(t.attr_);
+               ms.os_ << " " << from_ascii(t.attr_);
        ms << ">";
+       ++ms.nesting_level_;
        return ms;
 }
 
 
 MathMLStream & operator<<(MathMLStream & ms, MTagInline const & t)
 {
+       ms.beforeTag();
+       SetMode rawmode(ms, false);
        ms.cr();
-       ms.os() << '<' << from_ascii(ms.namespacedTag(t.tag_));
+       ms.os_ << '<' << from_ascii(ms.namespacedTag(t.tag_));
        if (!t.attr_.empty())
-               ms.os() << " " << from_ascii(t.attr_);
+               ms.os_ << " " << from_ascii(t.attr_);
        ms << ">";
+       ++ms.nesting_level_;
        return ms;
 }
 
 
 MathMLStream & operator<<(MathMLStream & ms, ETag const & t)
 {
+       ms.beforeTag();
+       SetMode rawmode(ms, false);
        if (ms.tab() > 0)
                --ms.tab();
        ms.cr();
-       ms.os() << "</" << from_ascii(ms.namespacedTag(t.tag_)) << ">";
+       ms.os_ << "</" << from_ascii(ms.namespacedTag(t.tag_)) << ">";
+       --ms.nesting_level_;
        return ms;
 }
 
 
 MathMLStream & operator<<(MathMLStream & ms, ETagInline const & t)
 {
-       ms.os() << "</" << from_ascii(ms.namespacedTag(t.tag_)) << ">";
+       ms.beforeTag();
+       SetMode rawmode(ms, false);
+       ms.os_ << "</" << from_ascii(ms.namespacedTag(t.tag_)) << ">";
+       --ms.nesting_level_;
        return ms;
 }
 
 
 MathMLStream & operator<<(MathMLStream & ms, CTag const & t)
 {
+       ms.beforeTag();
+       SetMode rawmode(ms, false);
        ms.cr();
-       ms.os() << "<" << from_ascii(ms.namespacedTag(t.tag_));
+       ms.os_ << "<" << from_ascii(ms.namespacedTag(t.tag_));
     if (!t.attr_.empty())
-        ms.os() << " " << from_utf8(t.attr_);
-    ms.os() << "/>";
+        ms.os_ << " " << from_utf8(t.attr_);
+    ms.os_ << "/>";
        return ms;
 }
 
@@ -508,14 +540,14 @@ HtmlStream & operator<<(HtmlStream & ms, docstring const & s)
 SetMode::SetMode(MathMLStream & ms, bool text)
        : ms_(ms)
 {
-       was_text_ = ms_.inText();
-       ms_.setTextMode(text);
+       old_text_level_ = ms_.text_level_;
+       ms_.text_level_ = text ? ms_.nesting_level_ : MathMLStream::nlevel;
 }
 
 
 SetMode::~SetMode()
 {
-       ms_.setTextMode(was_text_);
+       ms_.text_level_ = old_text_level_;
 }
 
 
index c8d684bc3844c3619f03b5354490353649ba0864..a9edae08c06cc5019f23a46ed8368044f3e70bfc 100644 (file)
@@ -380,8 +380,6 @@ public:
        explicit MathMLStream(odocstream & os, std::string const & xmlns = "");
        ///
        void cr();
-       ///
-       odocstream & os() { return os_; }
        /// Indentation when nesting tags
        int & tab() { return tab_; }
        ///
@@ -391,7 +389,7 @@ public:
        ///
        docstring deferred() const;
        ///
-       bool inText() const { return in_text_; }
+       bool inText() const { return text_level_ != nlevel; }
        ///
        std::string xmlns() const { return xmlns_; }
        /// Returns the tag name prefixed by the name space if needed.
@@ -403,14 +401,21 @@ public:
        /// Sets the current math style in the stream.
        void setFontMathStyle(const MathStyle style) { font_math_style_ = style; }
 private:
-       ///
-       void setTextMode(bool t) { in_text_ = t; }
+       /// Check whether it makes sense to start a <mtext>
+       void beforeText();
+       ///Check whether there is a <mtext> to close here
+       void beforeTag();
        ///
        odocstream & os_;
        ///
        int tab_ = 0;
        ///
-       bool in_text_ = false;
+       int nesting_level_ = 0;
+       static const int nlevel = -1000;
+       ///
+       int text_level_ = nlevel;
+       ///
+       bool in_mtext_ = false;
        ///
        odocstringstream deferred_;
        ///
@@ -419,6 +424,14 @@ private:
        MathStyle font_math_style_;
        ///
        friend class SetMode;
+       friend MathMLStream & operator<<(MathMLStream &, MathAtom const &);
+       friend MathMLStream & operator<<(MathMLStream &, MathData const &);
+       friend MathMLStream & operator<<(MathMLStream &, docstring const &);
+       friend MathMLStream & operator<<(MathMLStream &, MTag const &);
+       friend MathMLStream & operator<<(MathMLStream &, MTagInline const &);
+       friend MathMLStream & operator<<(MathMLStream &, ETag const &);
+       friend MathMLStream & operator<<(MathMLStream &, ETagInline const &);
+       friend MathMLStream & operator<<(MathMLStream &, CTag const &);
 };
 
 ///
@@ -456,7 +469,7 @@ private:
        ///
        MathMLStream & ms_;
        ///
-       bool was_text_;
+       bool old_text_level_;
 };