commit 1254e249faac7e08f0318daabb0f6d2adcf787cf
Author: Thibaut Cuvelier <[email protected]>
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
[email protected]
https://lists.lyx.org/mailman/listinfo/lyx-cvs