commit 1254e249faac7e08f0318daabb0f6d2adcf787cf
Author: Thibaut Cuvelier <tcuvel...@lyx.org>
Date:   Sun Nov 3 05:30:37 2024 +0100

    Add math font into HtmlStream/MathMLStream.
    
    At the same time, implement font nesting for maths.
    
    MathFontInfo also moves to its final resting place, along with the math 
streams.
---
 src/mathed/InsetMathFont.cpp | 200 ++++---------------------------------------
 src/mathed/MathStream.cpp    | 158 ++++++++++++++++++++++++++++++++++
 src/mathed/MathStream.h      |  74 ++++++++++++++++
 3 files changed, 248 insertions(+), 184 deletions(-)

diff --git a/src/mathed/InsetMathFont.cpp b/src/mathed/InsetMathFont.cpp
index 120a8cf6da..91c5ed517e 100644
--- a/src/mathed/InsetMathFont.cpp
+++ b/src/mathed/InsetMathFont.cpp
@@ -29,178 +29,6 @@ using namespace lyx::support;
 
 namespace lyx {
 
-namespace {
-// Similar to FontInfo and its related enums, but specifically for the math
-// mode.
-//
-// All types have enumerations, like FontEnums.h, even though there are
-// sometimes only two cases: this design ensures some future-proofness and
-// ensures that you cannot inadvertently swap two values.
-class MathFontInfo {
-public:
-       enum MathFontFamily {
-               MATH_NORMAL_FAMILY = 0, // Default value in MathML.
-               MATH_FRAKTUR_FAMILY,
-               MATH_SANS_FAMILY,
-               MATH_MONOSPACE_FAMILY,
-               MATH_DOUBLE_STRUCK_FAMILY,
-               MATH_SCRIPT_FAMILY,
-               MATH_SMALL_CAPS // Not natively supported in any version of 
MathML.
-       };
-
-       enum MathFontSeries {
-               MATH_MEDIUM_SERIES = 0, // Default value in MathML. // Default 
value in MathML.
-               MATH_BOLD_SERIES
-       };
-
-       enum MathFontShape {
-               MATH_UP_SHAPE = 0,
-               MATH_ITALIC_SHAPE // Default value in MathML mi, not outside.
-       };
-
-       MathFontInfo() :
-               family_(MATH_NORMAL_FAMILY), series_(MATH_MEDIUM_SERIES), 
shape_(MATH_UP_SHAPE) {}
-       MathFontInfo(const MathFontFamily family, const MathFontSeries series, 
const MathFontShape shape) :
-               family_(family), series_(series), shape_(shape) {}
-
-       static MathFontInfo fromMacro(const docstring& tag)
-       {
-               MathFontInfo font;
-               if (tag == "mathnormal" || tag == "mathrm"
-                               || tag == "text" || tag == "textnormal"
-                               || tag == "textrm" || tag == "textup"
-                               || tag == "textmd")
-                       font.shape_ = MATH_UP_SHAPE;
-               else if (tag == "frak" || tag == "mathfrak")
-                       font.family_ = MATH_FRAKTUR_FAMILY;
-               else if (tag == "mathbf" || tag == "textbf")
-                       font.series_ = MATH_BOLD_SERIES;
-               else if (tag == "mathbb" || tag == "mathbbm"
-                                || tag == "mathds")
-                       font.family_ = MATH_DOUBLE_STRUCK_FAMILY;
-               else if (tag == "mathcal")
-                       font.family_ = MATH_SCRIPT_FAMILY;
-               else if (tag == "mathit" || tag == "textsl"
-                                || tag == "emph" || tag == "textit")
-                       font.shape_ = MATH_ITALIC_SHAPE;
-               else if (tag == "mathsf" || tag == "textsf")
-                       font.family_ = MATH_SANS_FAMILY;
-               else if (tag == "mathtt" || tag == "texttt")
-                       font.family_ = MATH_MONOSPACE_FAMILY;
-               else if (tag == "textipa" || tag == "textsc" || tag == "noun")
-                       font.family_ = MATH_SMALL_CAPS;
-               // Otherwise, the tag is not recognised, use the default font.
-
-               return font;
-       }
-
-       MathFontFamily family() const { return family_; }
-       MathFontSeries series() const { return series_; }
-       MathFontShape shape() const { return shape_; }
-
-       std::string toMathMLMathVariant(MathMLStream::MathMLVersion 
mathml_version) const
-       {
-               return mathml_version == MathMLStream::MathMLVersion::mathml3 ?
-                       toMathVariantForMathML3() : 
toMathVariantForMathMLCore();
-       }
-
-       std::string toHTMLSpanClass() const
-       {
-               std::string span_class;
-               switch (family_) {
-               case MATH_NORMAL_FAMILY:
-                       break;
-               case MATH_FRAKTUR_FAMILY:
-                       span_class = "fraktur";
-                       break;
-               case MATH_SANS_FAMILY:
-                       span_class = "sans";
-                       break;
-               case MATH_MONOSPACE_FAMILY:
-                       span_class = "monospace";
-                       break;
-               case MATH_DOUBLE_STRUCK_FAMILY:
-                       // This style does not exist in HTML and cannot be 
implemented in CSS.
-                       break;
-               case MATH_SCRIPT_FAMILY:
-                       span_class = "script";
-                       break;
-               case MATH_SMALL_CAPS:
-                       span_class = "noun";
-                       break;
-               }
-               // Explicitly match the cases with an empty output. This 
ensures that we catch at runtime
-               // invalid values for the enum while keeping compile-time 
warnings.
-               if (span_class.empty() && (family_ == MATH_NORMAL_FAMILY || 
family_ == MATH_DOUBLE_STRUCK_FAMILY)) {
-                       LYXERR(Debug::MATHED,
-                               "Unexpected case in 
MathFontInfo::toHTMLSpanClass: family_ = " << family_
-                                       << ", series = " << series_ << ", shape 
= " << shape_);
-               }
-
-               if (series_ == MATH_BOLD_SERIES) {
-                       if (!span_class.empty()) span_class += "-";
-                       span_class += "bold";
-               }
-
-               if (shape_ == MATH_ITALIC_SHAPE) {
-                       if (!span_class.empty()) span_class += "-";
-                       span_class += "italic";
-               }
-
-               return span_class;
-       }
-
-private:
-       MathFontFamily family_;
-       MathFontSeries series_;
-       MathFontShape shape_;
-
-       std::string toMathVariantForMathML3() const
-       {
-               // mathvariant is the way MathML 3 encodes fonts.
-               // Not all combinations are supported. Official list:
-               // https://www.w3.org/TR/MathML3/chapter3.html#presm.commatt
-               // "initial", "tailed", "looped", and "stretched" are not 
implemented,
-               // as they are only useful for Arabic characters (for which LyX 
has no
-               // support right now).
-               switch (family_) {
-               case MATH_MONOSPACE_FAMILY:
-                       return "monospace";
-               case MATH_DOUBLE_STRUCK_FAMILY:
-                       return "double-struck";
-               case MATH_FRAKTUR_FAMILY:
-                       return series_ == MATH_BOLD_SERIES ? "bold-fraktur" : 
"fraktur";
-               case MATH_SCRIPT_FAMILY:
-                       return series_ == MATH_BOLD_SERIES ? "bold-script" : 
"script";
-               case MATH_SANS_FAMILY:
-                       if (series_ == MATH_MEDIUM_SERIES) {
-                               return shape_ == MATH_UP_SHAPE ? "sans-serif" : 
"sans-serif-italic";
-                       }
-                       return shape_ == MATH_UP_SHAPE ? "bold-sans-serif" : 
"sans-serif-bold-italic";
-               case MATH_NORMAL_FAMILY:
-                       if (series_ == MATH_MEDIUM_SERIES) {
-                               return shape_ == MATH_UP_SHAPE ? "normal" : 
"italic";
-                       }
-                       return shape_ == MATH_UP_SHAPE ? "bold" : "bold-italic";
-               case MATH_SMALL_CAPS:
-                       // No valid value...
-                       return "";
-               }
-
-               // Better safe than sorry.
-               LYXERR(Debug::MATHED,
-                       "Unexpected case in 
MathFontInfo::toMathVariantForMathML3: family_ = " << family_
-                               << ", series = " << series_ << ", shape = " << 
shape_);
-               return "";
-       }
-
-       std::string toMathVariantForMathMLCore() const
-       {
-               return shape_ == MATH_UP_SHAPE ? "normal" : "";
-       }
-};
-}
-
 InsetMathFont::InsetMathFont(Buffer * buf, latexkeys const * key)
        : InsetMathNest(buf, 1), key_(key)
 {}
@@ -340,31 +168,33 @@ void InsetMathFont::validate(LaTeXFeatures & features) 
const
 // The fonts we want to support are listed in lib/symbols
 void InsetMathFont::htmlize(HtmlStream & os) const
 {
-       // FIXME These are not quite right, because they do not nest
-       // correctly. A proper fix would presumably involve tracking
-       // the fonts already in effect.
-       const MathFontInfo font = MathFontInfo::fromMacro(key_->name);
-       const std::string span_class = font.toHTMLSpanClass();
+       MathFontInfo old_font = 
os.fontInfo().mergeWith(MathFontInfo::fromMacro(key_->name));
+       const std::string span_class = os.fontInfo().toHTMLSpanClass();
 
+       // TODO: with this implementation, variants are output several times. 
See mathmlize.
        if (!span_class.empty()) {
                os << MTag("span", "class='" + span_class + "'")
                   << cell(0)
                   << ETag("span");
        } else
                os << cell(0);
+
+       os.fontInfo().replaceBy(old_font);
 }
 
 
 // The fonts we want to support are listed in lib/symbols
 void InsetMathFont::mathmlize(MathMLStream & ms) const
 {
-       // FIXME These are not quite right, because they do not nest
-       // correctly. A proper fix would presumably involve tracking
-       // the fonts already in effect.
-       const MathFontInfo font = MathFontInfo::fromMacro(key_->name);
-       const std::string variant = font.toMathMLMathVariant(ms.version());
-
-       if (font.shape() == MathFontInfo::MATH_UP_SHAPE) {
+       MathFontInfo old_font = 
ms.fontInfo().mergeWith(MathFontInfo::fromMacro(key_->name));
+       const std::string variant = 
ms.fontInfo().toMathMLMathVariant(ms.version());
+
+       // TODO: with this implementation, variants are output several times 
(e.g.,
+       // for \textit{a\textbf{b}}, italics will be present twice in the 
output,
+       // <mstyle mathvariant="italic">a<mstyle mathvariant="italic 
bold">b</mstyle></mstyle>
+       // To do better, we'd need more logic for (un)toggling, like in the 
code for text
+       // (for instance, computeDocBookFontSwitch).
+       if (ms.fontInfo().shape() == MathFontInfo::MATH_UP_SHAPE) {
                SetMode textmode(ms, true);
                ms << cell(0);
        } else if (!variant.empty()) {
@@ -374,6 +204,8 @@ void InsetMathFont::mathmlize(MathMLStream & ms) const
        } else {
                ms << cell(0);
        }
+
+       ms.fontInfo().replaceBy(old_font);
 }
 
 
diff --git a/src/mathed/MathStream.cpp b/src/mathed/MathStream.cpp
index 085c8310d4..c6f78f72c1 100644
--- a/src/mathed/MathStream.cpp
+++ b/src/mathed/MathStream.cpp
@@ -35,6 +35,164 @@ namespace lyx {
 //////////////////////////////////////////////////////////////////////
 
 
+MathFontInfo MathFontInfo::mergeWith(const MathFontInfo& other)
+{
+       MathFontInfo old = *this;
+
+       if (other.family_ != family_ && other.family_ != MATH_INHERIT_FAMILY) {
+               family_ = other.family_;
+       }
+       if (other.series_ != series_ && other.series_ != MATH_INHERIT_SERIES) {
+               series_ = other.series_;
+       }
+       if (other.shape_ != shape_ && other.shape_ != MATH_INHERIT_SHAPE) {
+               shape_ = other.shape_;
+       }
+
+       return old;
+}
+
+
+void MathFontInfo::replaceBy(const MathFontInfo& other)
+{
+       *this = other;
+}
+
+
+MathFontInfo MathFontInfo::fromMacro(const docstring& tag)
+{
+       MathFontInfo font;
+       if (tag == "mathnormal" || tag == "mathrm"
+                       || tag == "text" || tag == "textnormal"
+                       || tag == "textrm" || tag == "textup"
+                       || tag == "textmd")
+               font.shape_ = MATH_UP_SHAPE;
+       else if (tag == "frak" || tag == "mathfrak")
+               font.family_ = MATH_FRAKTUR_FAMILY;
+       else if (tag == "mathbf" || tag == "textbf")
+               font.series_ = MATH_BOLD_SERIES;
+       else if (tag == "mathbb" || tag == "mathbbm"
+                        || tag == "mathds")
+               font.family_ = MATH_DOUBLE_STRUCK_FAMILY;
+       else if (tag == "mathcal")
+               font.family_ = MATH_SCRIPT_FAMILY;
+       else if (tag == "mathit" || tag == "textsl"
+                        || tag == "emph" || tag == "textit")
+               font.shape_ = MATH_ITALIC_SHAPE;
+       else if (tag == "mathsf" || tag == "textsf")
+               font.family_ = MATH_SANS_FAMILY;
+       else if (tag == "mathtt" || tag == "texttt")
+               font.family_ = MATH_MONOSPACE_FAMILY;
+       else if (tag == "textipa" || tag == "textsc" || tag == "noun")
+               font.family_ = MATH_SMALL_CAPS;
+       // Otherwise, the tag is not recognised, use the default font.
+
+       return font;
+}
+
+
+std::string MathFontInfo::toMathMLMathVariant(MathMLVersion mathml_version) 
const
+{
+       return mathml_version == MathMLVersion::mathml3 ?
+               toMathVariantForMathML3() : toMathVariantForMathMLCore();
+}
+
+
+std::string MathFontInfo::toMathVariantForMathML3() const
+{
+       // mathvariant is the way MathML 3 encodes fonts.
+       // Not all combinations are supported. Official list:
+       // https://www.w3.org/TR/MathML3/chapter3.html#presm.commatt
+       // "initial", "tailed", "looped", and "stretched" are not implemented,
+       // as they are only useful for Arabic characters (for which LyX has no
+       // support right now).
+       switch (family_) {
+       case MATH_MONOSPACE_FAMILY:
+               return "monospace";
+       case MATH_DOUBLE_STRUCK_FAMILY:
+               return "double-struck";
+       case MATH_FRAKTUR_FAMILY:
+               return series_ == MATH_BOLD_SERIES ? "bold-fraktur" : "fraktur";
+       case MATH_SCRIPT_FAMILY:
+               return series_ == MATH_BOLD_SERIES ? "bold-script" : "script";
+       case MATH_SANS_FAMILY:
+               if (series_ == MATH_BOLD_SERIES) {
+                       return shape_ == MATH_UP_SHAPE ? "bold-sans-serif" : 
"sans-serif-bold-italic";
+               }
+               return shape_ == MATH_UP_SHAPE ? "sans-serif" : 
"sans-serif-italic";
+       case MATH_NORMAL_FAMILY:
+       case MATH_INHERIT_FAMILY: // Only consider the other two attributes.
+               if (series_ == MATH_BOLD_SERIES) {
+                       return shape_ == MATH_UP_SHAPE ? "bold" : "bold-italic";
+               }
+               return shape_ == MATH_UP_SHAPE ? "normal" : "italic";
+       case MATH_SMALL_CAPS:
+               // No valid value to return.
+               return "";
+       }
+
+       // Better safe than sorry.
+       LYXERR(Debug::MATHED,
+               "Unexpected case in MathFontInfo::toMathVariantForMathML3: 
family_ = " << family_
+                       << ", series = " << series_ << ", shape = " << shape_);
+       return "";
+}
+
+
+std::string MathFontInfo::toMathVariantForMathMLCore() const
+{
+       return shape_ == MATH_UP_SHAPE ? "normal" : "";
+}
+
+
+std::string MathFontInfo::toHTMLSpanClass() const
+{
+       std::string span_class;
+       switch (family_) {
+       case MATH_INHERIT_FAMILY:
+       case MATH_NORMAL_FAMILY:
+               break;
+       case MATH_FRAKTUR_FAMILY:
+               span_class = "fraktur";
+               break;
+       case MATH_SANS_FAMILY:
+               span_class = "sans";
+               break;
+       case MATH_MONOSPACE_FAMILY:
+               span_class = "monospace";
+               break;
+       case MATH_DOUBLE_STRUCK_FAMILY:
+               // This style does not exist in HTML and cannot be implemented 
in CSS.
+               break;
+       case MATH_SCRIPT_FAMILY:
+               span_class = "script";
+               break;
+       case MATH_SMALL_CAPS:
+               span_class = "noun";
+               break;
+       }
+       // Explicitly match the cases with an empty output. This ensures that 
we catch at runtime
+       // invalid values for the enum while keeping compile-time warnings.
+       if (span_class.empty() && (family_ == MATH_INHERIT_FAMILY || family_ == 
MATH_NORMAL_FAMILY || family_ == MATH_DOUBLE_STRUCK_FAMILY)) {
+               LYXERR(Debug::MATHED,
+                       "Unexpected case in MathFontInfo::toHTMLSpanClass: 
family_ = " << family_
+                               << ", series = " << series_ << ", shape = " << 
shape_);
+       }
+
+       if (series_ == MATH_BOLD_SERIES) {
+               if (!span_class.empty()) span_class += "-";
+               span_class += "bold";
+       }
+
+       if (shape_ == MATH_ITALIC_SHAPE) {
+               if (!span_class.empty()) span_class += "-";
+               span_class += "italic";
+       }
+
+       return span_class;
+}
+
+
 NormalStream & operator<<(NormalStream & ns, MathAtom const & at)
 {
        at->normalize(ns);
diff --git a/src/mathed/MathStream.h b/src/mathed/MathStream.h
index 9b76ff5344..d611ec8c7c 100644
--- a/src/mathed/MathStream.h
+++ b/src/mathed/MathStream.h
@@ -33,6 +33,72 @@ enum class MathMLVersion : int {
        mathmlCore
 };
 
+// Similar to FontInfo and its related enums, but specifically for the math
+// mode.
+//
+// All types have enumerations, like FontEnums.h, even though there are
+// sometimes only two cases: this design ensures some future-proofness and
+// ensures that you cannot inadvertently swap two values.
+class MathFontInfo {
+public:
+       enum MathFontFamily {
+               MATH_INHERIT_FAMILY = 0,
+               MATH_NORMAL_FAMILY, // Default value in MathML.
+               MATH_FRAKTUR_FAMILY,
+               MATH_SANS_FAMILY,
+               MATH_MONOSPACE_FAMILY,
+               MATH_DOUBLE_STRUCK_FAMILY,
+               MATH_SCRIPT_FAMILY,
+               MATH_SMALL_CAPS // Not natively supported in any version of 
MathML.
+       };
+
+       enum MathFontSeries {
+               MATH_INHERIT_SERIES = 0,
+               MATH_MEDIUM_SERIES, // Default value in MathML. // Default 
value in MathML.
+               MATH_BOLD_SERIES
+       };
+
+       enum MathFontShape {
+               MATH_INHERIT_SHAPE = 0,
+               MATH_UP_SHAPE,
+               MATH_ITALIC_SHAPE // Default value in MathML mi, not outside.
+       };
+
+       MathFontInfo() :
+               family_(MATH_INHERIT_FAMILY), series_(MATH_INHERIT_SERIES), 
shape_(MATH_INHERIT_SHAPE) {}
+       MathFontInfo(const MathFontFamily family, const MathFontSeries series, 
const MathFontShape shape) :
+               family_(family), series_(series), shape_(shape) {}
+
+       /// Merges this font with another one, replacing all fields in this font
+    /// by the ones in the argument if they are not "inherit".
+       MathFontInfo mergeWith(const MathFontInfo& other);
+       /// Replaces this font by the given one.
+       void replaceBy(const MathFontInfo& other);
+       /// Parses a LaTeX font macro into a MathFontInfo object (builder 
method).
+       static MathFontInfo fromMacro(const docstring& tag);
+
+       MathFontFamily family() const { return family_; }
+       MathFontSeries series() const { return series_; }
+       MathFontShape shape() const { return shape_; }
+
+       /// Transforms this font into the mathvariant attribute for MathML.
+       /// For MathML 3, all fonts are output as mathvariants; for MathML Core,
+       /// almost all fonts are supposed to be output as characters.
+       std::string toMathMLMathVariant(MathMLVersion mathml_version) const;
+       /// Transforms this font into a class attribute for the HTML span tag.
+       std::string toHTMLSpanClass() const;
+
+private:
+       MathFontFamily family_;
+       MathFontSeries series_;
+       MathFontShape shape_;
+
+       /// Transforms this font into the mathvariant attribute for MathML 3.
+       std::string toMathVariantForMathML3() const;
+       /// Transforms this font into the mathvariant attribute for MathML Core.
+       std::string toMathVariantForMathMLCore() const;
+};
+
 //
 // LaTeX/LyX
 //
@@ -409,6 +475,8 @@ public:
        const MathStyle & getFontMathStyle() const { return font_math_style_; }
        /// Sets the current math style in the stream.
        void setFontMathStyle(const MathStyle style) { font_math_style_ = 
style; }
+       /// Gets a mutable reference to the stream's current font.
+       MathFontInfo& fontInfo() { return current_font_; }
 private:
        /// Check whether it makes sense to start a <mtext>
        void beforeText();
@@ -433,6 +501,8 @@ private:
        MathMLVersion version_;
        /// The only important part of a FontInfo object.
        MathStyle font_math_style_;
+       /// Current font (which might be nested).
+       MathFontInfo current_font_;
        ///
        friend class SetMode;
        friend MathMLStream & operator<<(MathMLStream &, MathAtom const &);
@@ -506,6 +576,8 @@ public:
        docstring deferred() const;
        ///
        bool inText() const { return in_text_; }
+       /// Gets a mutable reference to the stream's current font.
+       MathFontInfo& fontInfo() { return current_font_; }
 private:
        ///
        void setTextMode(bool t) { in_text_ = t; }
@@ -519,6 +591,8 @@ private:
        bool in_text_;
        ///
        odocstringstream deferred_;
+       /// Current font (which might be nested).
+       MathFontInfo current_font_;
        ///
        friend class SetHTMLMode;
 };
-- 
lyx-cvs mailing list
lyx-cvs@lists.lyx.org
https://lists.lyx.org/mailman/listinfo/lyx-cvs

Reply via email to