From be836909c52f8586646fa6360df649288b5e2875 Mon Sep 17 00:00:00 2001 From: Guillaume Munch Date: Wed, 25 May 2016 13:16:51 +0100 Subject: [PATCH] Display properly math characters that behave like symbols * set up a replacement of *, -, and : by the adequate symbols (#9893) * fix the wrong character selection and operator spacing in \text mode * hide some internal symbols from the auto-completion. --- lib/symbols | 17 +++-- src/mathed/InsetMathChar.cpp | 126 +++++++++++++++++++++------------ src/mathed/InsetMathChar.h | 17 ++++- src/mathed/InsetMathSymbol.cpp | 49 ++----------- src/mathed/MathSupport.cpp | 48 +++++++++++++ src/mathed/MathSupport.h | 5 ++ 6 files changed, 165 insertions(+), 97 deletions(-) diff --git a/lib/symbols b/lib/symbols index 1905888887..481aa8186c 100644 --- a/lib/symbols +++ b/lib/symbols @@ -298,7 +298,7 @@ diamondsuit cmsy 125 168 mathord ♢ heartsuit cmsy 126 169 mathord ♡ spadesuit cmsy 127 170 mathord ♠ # We define lyxnot as mathrel in order to have proper alignment -lyxnot cmsy 54 47 mathrel / +lyxnot cmsy 54 47 mathrel / hiddensymbol iffont cmsy # 10mu is the extra space added to relation operators \def\not{\lyxnot\kern-20mu} @@ -669,11 +669,16 @@ hslash msb 125 0 mathord ℏ hbar msb 126 0 mathord ℏ backepsilon msb 127 0 mathrel ϶ -lyxbar cmsy 161 0 mathord — -lyxeq cmr 61 0 mathord = -lyxdabar msa 57 0 mathord – -lyxright msa 75 0 mathord → -lyxleft msa 76 0 mathord ← +lyxbar cmsy 161 0 mathord — hiddensymbol +lyxminus cmsy 161 0 mathbin — hiddensymbol +lyxplus cmr 43 43 mathbin + hiddensymbol +lyxeq cmr 61 61 mathord = hiddensymbol +lyxeqrel cmr 61 61 mathrel = hiddensymbol +lyxlt cmm 60 60 mathrel < hiddensymbol +lyxgt cmm 62 62 mathrel > hiddensymbol +lyxdabar msa 57 0 mathord – hiddensymbol +lyxright msa 75 0 mathord → hiddensymbol +lyxleft msa 76 0 mathord ← hiddensymbol male wasy 26 0 x ♂ female wasy 25 0 x ♀ diff --git a/src/mathed/InsetMathChar.cpp b/src/mathed/InsetMathChar.cpp index 813067e08a..ae843bf00d 100644 --- a/src/mathed/InsetMathChar.cpp +++ b/src/mathed/InsetMathChar.cpp @@ -13,6 +13,7 @@ #include "InsetMathChar.h" +#include "MathParser.h" #include "MathSupport.h" #include "MathStream.h" #include "MetricsInfo.h" @@ -34,6 +35,57 @@ namespace lyx { extern bool has_math_fonts; +namespace { + +latexkeys const * makeSubstitute(char_type c) +{ + std::string name; + switch (c) { + // Latex replaces ', *, -, and : with specific symbols. With unicode-math, + // these symbols are replaced respectively by ^U+2032, U+2217, U+2212 and + // U+2236 (the latter substitution can be turned off with a package + // option). Unicode-math also replaces ` with \backprime. + // prime needs to be placed in superscript unless an opentype font is used. + //case '\'': + //name = "prime"; + //break; + case '*': + name = "ast"; + break; + case '-': + name = "lyxminus";// unicode-math: "minus" + break; + case ':': + name = "ordinarycolon";// unicode-math: "mathratio" + break; + // The remaining replacements are not real character substitutions (from a + // unicode point of view) but are done here: 1. for cosmetic reasons, in the + // context of being stuck with CM fonts at the moment, to ensure consistency + // with related symbols: -, \leq, \geq, etc. 2. to get the proper spacing + // as defined in lib/symbols. + case '+': + name = "lyxplus";//unicode-math: "mathplus" + break; + case '>': + name = "lyxgt";//unicode-math: "greater" + break; + case '<': + name = "lyxlt";//unicode-math: "less" + break; + case '=': + name = "lyxeqrel";//unicode-math: "equal" + break; + //case ','://unicode-math: "mathcomma" + //case ';'://unicode-math: "mathsemicolon" + default: + return nullptr; + } + return in_word_set(from_ascii(name)); +} + +} //anonymous namespace + + static bool slanted(char_type c) { return isAlphaASCII(c) || Encodings::isMathAlpha(c); @@ -41,7 +93,7 @@ static bool slanted(char_type c) InsetMathChar::InsetMathChar(char_type c) - : char_(c), kerning_(0) + : char_(c), kerning_(0), subst_(makeSubstitute(c)) {} @@ -54,13 +106,13 @@ Inset * InsetMathChar::clone() const void InsetMathChar::metrics(MetricsInfo & mi, Dimension & dim) const { -#if 1 - if (char_ == '=' && has_math_fonts) { - Changer dummy = mi.base.changeFontSet("cmr"); - dim = theFontMetrics(mi.base.font).dimension(char_); - } else if ((char_ == '>' || char_ == '<') && has_math_fonts) { - Changer dummy = mi.base.changeFontSet("cmm"); - dim = theFontMetrics(mi.base.font).dimension(char_); + bool const mathfont = isMathFont(mi.base.fontname); + if (mathfont && subst_) { + // If the char has a substitute, draw the replacement symbol + // instead, but only in math mode. + mathedSymbolDim(mi, dim, subst_); + kerning_ = mathed_char_kerning(mi.base.font, *subst_->draw.rbegin()); + return; } else if (!slanted(char_) && mi.base.fontname == "mathnormal") { Changer dummy = mi.base.font.changeShape(UP_SHAPE); dim = theFontMetrics(mi.base.font).dimension(char_); @@ -69,50 +121,27 @@ void InsetMathChar::metrics(MetricsInfo & mi, Dimension & dim) const dim = fm.dimension(char_); kerning_ = fm.rbearing(char_) - dim.wid; } - if (isMathBin()) - dim.wid += 2 * mathed_medmuskip(mi.base.font); - else if (isMathRel()) - dim.wid += 2 * mathed_thickmuskip(mi.base.font); - else if (isMathPunct()) + if (mathfont && isMathPunct()) dim.wid += mathed_thinmuskip(mi.base.font); - else if (char_ == '\'') - // FIXME: don't know where this is coming from - dim.wid += mathed_thinmuskip(mi.base.font); -#else - whichFont(font_, code_, mi); - dim = theFontMetrics(font_).dimension(char_); - if (isBinaryOp(char_, code_)) - dim.wid += 2 * theFontMetrics(font_).width(' '); - lyxerr << "InsetMathChar::metrics: " << dim << endl; -#endif } void InsetMathChar::draw(PainterInfo & pi, int x, int y) const { //lyxerr << "drawing '" << char_ << "' font: " << pi.base.fontname << std::endl; - if (isMathBin()) - x += mathed_medmuskip(pi.base.font); - else if (isMathRel()) - x += mathed_thickmuskip(pi.base.font); - else if (char_ == '\'') - x += mathed_thinmuskip(pi.base.font) / 2; -#if 1 - if (char_ == '=' && has_math_fonts) { - Changer dummy = pi.base.changeFontSet("cmr"); - pi.draw(x, y, char_); - } else if ((char_ == '>' || char_ == '<') && has_math_fonts) { - Changer dummy = pi.base.changeFontSet("cmm"); - pi.draw(x, y, char_); - } else if (!slanted(char_) && pi.base.fontname == "mathnormal") { - Changer dummy = pi.base.font.changeShape(UP_SHAPE); - pi.draw(x, y, char_); - } else { - pi.draw(x, y, char_); + if (isMathFont(pi.base.fontname)) { + if (subst_) { + // If the char has a substitute, draw the replacement symbol + // instead, but only in math mode. + mathedSymbolDraw(pi, x, y, subst_); + return; + } else if (!slanted(char_) && pi.base.fontname == "mathnormal") { + Changer dummy = pi.base.font.changeShape(UP_SHAPE); + pi.draw(x, y, char_); + return; + } } -#else - drawChar(pain, font_, x, y, char_); -#endif + pi.draw(x, y, char_); } @@ -202,6 +231,8 @@ void InsetMathChar::mathmlize(MathStream & ms) const void InsetMathChar::htmlize(HtmlStream & ms) const { std::string entity; + // Not taking subst_ into account here because the MathML output of + // <>=+-* looks correct as it is. FIXME: ' is not output as ^\prime switch (char_) { case '<': entity = "<"; break; case '>': entity = ">"; break; @@ -237,19 +268,20 @@ void InsetMathChar::htmlize(HtmlStream & ms) const bool InsetMathChar::isMathBin() const { - return support::contains("+-*", static_cast(char_)); + return subst_ && subst_->extra == "mathbin"; } bool InsetMathChar::isMathRel() const { - return support::contains("<>=:", static_cast(char_)); + return subst_ && subst_->extra == "mathrel"; } bool InsetMathChar::isMathPunct() const { - return support::contains(",;", static_cast(char_)); + return support::contains(",;", static_cast(char_)) + || (subst_ && subst_->extra == "mathpunct"); } diff --git a/src/mathed/InsetMathChar.h b/src/mathed/InsetMathChar.h index 67bbc64e8c..ea190211b7 100644 --- a/src/mathed/InsetMathChar.h +++ b/src/mathed/InsetMathChar.h @@ -16,6 +16,8 @@ namespace lyx { +class latexkeys; + /// The base character inset. class InsetMathChar : public InsetMath { public: @@ -60,9 +62,22 @@ public: private: virtual Inset * clone() const; /// the character - char_type char_; + char_type const char_; /// cached kerning for superscript mutable int kerning_; + /// Inset to substitute char for, for on-screen display in math mode, as + /// performed by LaTeX (#9893): + /// * -> \ast (U+2217) + /// - -> \lyxminus (U+2212) + /// : -> \ordinarycolon (U+2236) + /// + /// For cosmetic reasons, +, >, <, and = are also substituted to force the + /// use of CM fonts for uniformity. If CM fonts are replaced with unicode + /// math fonts, this should be removed, and substitutions of "'", ",", and + /// ";" added. + /// + /// Null if there is no substitute. + latexkeys const * const subst_; }; } // namespace lyx diff --git a/src/mathed/InsetMathSymbol.cpp b/src/mathed/InsetMathSymbol.cpp index 68c8247429..620ca226a2 100644 --- a/src/mathed/InsetMathSymbol.cpp +++ b/src/mathed/InsetMathSymbol.cpp @@ -60,36 +60,17 @@ docstring InsetMathSymbol::name() const void InsetMathSymbol::metrics(MetricsInfo & mi, Dimension & dim) const { - //lyxerr << "metrics: symbol: '" << sym_->name - // << "' in font: '" << sym_->inset - // << "' drawn as: '" << sym_->draw - // << "'" << endl; - - bool const italic_upcase_greek = sym_->inset == "cmr" && - sym_->extra == "mathalpha" && - mi.base.fontname == "mathit"; - std::string const font = italic_upcase_greek ? "cmm" : sym_->inset; - Changer dummy = mi.base.changeFontSet(font); - mathed_string_dim(mi.base.font, sym_->draw, dim); - docstring::const_reverse_iterator rit = sym_->draw.rbegin(); - kerning_ = mathed_char_kerning(mi.base.font, *rit); + // set dim + mathedSymbolDim(mi, dim, sym_); + // set kerning_ + kerning_ = mathed_char_kerning(mi.base.font, *sym_->draw.rbegin()); // correct height for broken cmex and wasy font if (sym_->inset == "cmex" || sym_->inset == "wasy") { h_ = 4 * dim.des / 5; dim.asc += h_; dim.des -= h_; } - // seperate things a bit - if (isMathBin()) - dim.wid += 2 * mathed_medmuskip(mi.base.font); - else if (isMathRel()) - dim.wid += 2 * mathed_thickmuskip(mi.base.font); - else if (isMathPunct()) - dim.wid += mathed_thinmuskip(mi.base.font); - // FIXME: I see no reason for this - //else - // dim.wid += support::iround(0.1667 * em); - + // set striptable_ scriptable_ = false; if (mi.base.style == LM_ST_DISPLAY) if (sym_->inset == "cmex" || sym_->inset == "esint" || @@ -101,25 +82,7 @@ void InsetMathSymbol::metrics(MetricsInfo & mi, Dimension & dim) const void InsetMathSymbol::draw(PainterInfo & pi, int x, int y) const { - //lyxerr << "metrics: symbol: '" << sym_->name - // << "' in font: '" << sym_->inset - // << "' drawn as: '" << sym_->draw - // << "'" << endl; - - bool const italic_upcase_greek = sym_->inset == "cmr" && - sym_->extra == "mathalpha" && - pi.base.fontname == "mathit"; - std::string const font = italic_upcase_greek ? "cmm" : sym_->inset; - if (isMathBin()) - x += mathed_medmuskip(pi.base.font); - else if (isMathRel()) - x += mathed_thickmuskip(pi.base.font); - // FIXME: I see no reason for this - //else - // x += support::iround(0.0833 * em); - - Changer dummy = pi.base.changeFontSet(font); - pi.draw(x, y - h_, sym_->draw); + mathedSymbolDraw(pi, x, y - h_, sym_); } diff --git a/src/mathed/MathSupport.cpp b/src/mathed/MathSupport.cpp index 5c3e84f249..0c610b1e69 100644 --- a/src/mathed/MathSupport.cpp +++ b/src/mathed/MathSupport.cpp @@ -27,6 +27,7 @@ #include "support/debug.h" #include "support/docstream.h" +#include "support/lassert.h" #include "support/lyxlib.h" #include @@ -656,6 +657,52 @@ void mathed_draw_deco(PainterInfo & pi, int x, int y, int w, int h, } +void mathedSymbolDim(MetricsInfo & mi, Dimension & dim, latexkeys const * sym) +{ + LASSERT((bool)sym, return); + //lyxerr << "metrics: symbol: '" << sym->name + // << "' in font: '" << sym->inset + // << "' drawn as: '" << sym->draw + // << "'" << endl; + + bool const italic_upcase_greek = sym->inset == "cmr" && + sym->extra == "mathalpha" && + mi.base.fontname == "mathit"; + std::string const font = italic_upcase_greek ? "cmm" : sym->inset; + Changer dummy = mi.base.changeFontSet(font); + mathed_string_dim(mi.base.font, sym->draw, dim); + // seperate things a bit + if (sym->extra == "mathbin") + dim.wid += 2 * mathed_medmuskip(mi.base.font); + else if (sym->extra == "mathrel") + dim.wid += 2 * mathed_thickmuskip(mi.base.font); + else if (sym->extra == "mathpunct") + dim.wid += mathed_thinmuskip(mi.base.font); +} + + +void mathedSymbolDraw(PainterInfo & pi, int x, int y, latexkeys const * sym) +{ + LASSERT((bool)sym, return); + //lyxerr << "drawing: symbol: '" << sym->name + // << "' in font: '" << sym->inset + // << "' drawn as: '" << sym->draw + // << "'" << endl; + + bool const italic_upcase_greek = sym->inset == "cmr" && + sym->extra == "mathalpha" && + pi.base.fontname == "mathit"; + std::string const font = italic_upcase_greek ? "cmm" : sym->inset; + if (sym->extra == "mathbin") + x += mathed_medmuskip(pi.base.font); + else if (sym->extra == "mathrel") + x += mathed_thickmuskip(pi.base.font); + + Changer dummy = pi.base.changeFontSet(font); + pi.draw(x, y, sym->draw); +} + + void metricsStrRedBlack(MetricsInfo & mi, Dimension & dim, docstring const & str) { FontInfo font = mi.base.font; @@ -708,6 +755,7 @@ FontShape const inh_shape = INHERIT_SHAPE; // does not work fontinfo fontinfos[] = { // math fonts + // Color_math determines which fonts are math (see isMathFont) {"mathnormal", ROMAN_FAMILY, MEDIUM_SERIES, ITALIC_SHAPE, Color_math}, {"mathbf", inh_family, BOLD_SERIES, diff --git a/src/mathed/MathSupport.h b/src/mathed/MathSupport.h index 74b7465f10..003631b8ae 100644 --- a/src/mathed/MathSupport.h +++ b/src/mathed/MathSupport.h @@ -25,6 +25,7 @@ class Dimension; class MathData; class MathAtom; class InsetMath; +class latexkeys; int mathed_font_em(FontInfo const &); @@ -48,6 +49,10 @@ void mathed_string_dim(FontInfo const & font, int mathed_string_width(FontInfo const &, docstring const & s); +void mathedSymbolDim(MetricsInfo & mi, Dimension & dim, latexkeys const * sym); + +void mathedSymbolDraw(PainterInfo & pi, int x, int y, latexkeys const * sym); + void metricsStrRedBlack(MetricsInfo & mi, Dimension & dim, docstring const & s); void drawStrRed(PainterInfo & pi, int x, int y, docstring const & s); -- 2.39.2