From 2aa930bbe8f2bf07f665b5cda70842748add271a Mon Sep 17 00:00:00 2001 From: Enrico Forestieri Date: Sat, 10 Sep 2016 18:32:44 +0200 Subject: [PATCH] Fix display and export of some latex macros Fixes #9742. --- lib/symbols | 44 +++++++++++++++++----------------- src/BufferParams.cpp | 1 + src/LaTeXFeatures.cpp | 20 ++++++++++++++++ src/LaTeXFeatures.h | 2 ++ src/mathed/InsetMathFont.cpp | 18 ++++++++++++++ src/mathed/InsetMathFont.h | 2 ++ src/mathed/InsetMathSpace.cpp | 4 +++- src/mathed/InsetMathSymbol.cpp | 2 ++ src/mathed/MacroTable.h | 2 ++ src/mathed/MathFactory.cpp | 36 +++++++++++++++++++++------- src/mathed/MathMacro.cpp | 11 ++++++++- src/mathed/MathStream.h | 43 ++++++++++++++++++++++----------- 12 files changed, 138 insertions(+), 47 deletions(-) diff --git a/lib/symbols b/lib/symbols index 2d13250a48..ba2b6d330d 100644 --- a/lib/symbols +++ b/lib/symbols @@ -667,18 +667,18 @@ lyxleft msa 76 0 mathord ← male wasy 26 0 x ♂ female wasy 25 0 x ♀ -currency wasy 27 0 textmode ¤ wasysym # works in text mode only (produces \oe in math mode) -phone wasy 7 0 textmode ☎ wasysym # works in text mode only (produces \Upsilon in math mode) -recorder wasy 6 0 textmode ⌕ wasysym # works in text mode only (produces \Sigma in math mode) -clock wasy 28 0 textmode &clock; wasysym # works in text mode only (produces \o in math mode) -lightning wasy 18 0 textmode ☇ wasysym # works in text mode only (produces ` in math mode) +currency wasy 27 0 textmode ¤ wasysym,amstext,lyxmathsym # works in text mode only (produces \oe in math mode) +phone wasy 7 0 textmode ☎ wasysym,amstext,lyxmathsym # works in text mode only (produces \Upsilon in math mode) +recorder wasy 6 0 textmode ⌕ wasysym,amstext,lyxmathsym # works in text mode only (produces \Sigma in math mode) +clock wasy 28 0 textmode &clock; wasysym,amstext,lyxmathsym # works in text mode only (produces \o in math mode) +lightning wasy 18 0 textmode ☇ wasysym,amstext,lyxmathsym # works in text mode only (produces ` in math mode) # FIXME Display is wrong (bug 8493): qt does not display characters at the \t position (0x09) -pointer wasy 9 0 textmode x wasysym # works in text mode only (produces \Psi in math mode) -RIGHTarrow wasy 17 0 textmode ▶ wasysym # works in text mode only (produces \jmath in math mode) -LEFTarrow wasy 16 0 textmode ◀ wasysym # works in text mode only (produces \imath in math mode) -UParrow wasy 75 0 textmode ▲ wasysym # works in text mode only (produces K in math mode) -DOWNarrow wasy 76 0 textmode ▼ wasysym # works in text mode only (produces L in math mode) -AC wasy 58 0 textmode ∿ wasysym # works in text mode only +pointer wasy 9 0 textmode x wasysym,amstext,lyxmathsym # works in text mode only (produces \Psi in math mode) +RIGHTarrow wasy 17 0 textmode ▶ wasysym,amstext,lyxmathsym # works in text mode only (produces \jmath in math mode) +LEFTarrow wasy 16 0 textmode ◀ wasysym,amstext,lyxmathsym # works in text mode only (produces \imath in math mode) +UParrow wasy 75 0 textmode ▲ wasysym,amstext,lyxmathsym # works in text mode only (produces K in math mode) +DOWNarrow wasy 76 0 textmode ▼ wasysym,amstext,lyxmathsym # works in text mode only (produces L in math mode) +AC wasy 58 0 textmode ∿ wasysym,amstext,lyxmathsym # works in text mode only \def\HF{\approx} wasysym VHF wasy 64 0 x ≋ # triple tilde Square wasy 50 0 x □ @@ -770,8 +770,8 @@ sagittarius wasy 99 0 x ♐ capricornus wasy 100 0 x ♑ aquarius wasy 101 0 x ♒ pisces wasy 102 0 x ♓ -conjunction wasy 86 0 textmode ☌ wasysym # works in text mode only (produces V in math mode) -opposition wasy 87 0 textmode ☍ wasysym # works in text mode only (produces W in math mode) +conjunction wasy 86 0 textmode ☌ wasysym,amstext,lyxmathsym # works in text mode only (produces V in math mode) +opposition wasy 87 0 textmode ☍ wasysym,amstext,lyxmathsym # works in text mode only (produces W in math mode) # APL characters APLstar wasy 69 0 x ⍟ APLlog wasy 22 0 x x @@ -799,11 +799,11 @@ lyxgluoneelement wasy 82 0 x x notexisting # special characters cent wasy 103 0 x �A2; permil wasy 104 0 x ‰ -agemO wasy 48 0 textmode ℧ wasysym # works in text mode only (produces 0 in math mode) -thorn wasy 105 0 textmode þ wasysym # works in text mode only (produces i in math mode) -Thorn wasy 106 0 textmode Þ wasysym # works in text mode only (produces j in math mode) -openo wasy 108 0 textmode ɔ wasysym # works in text mode only (produces l in math mode) -inve wasy 85 0 textmode ə wasysym # works in text mode only (produces U in math mode) +agemO wasy 48 0 textmode ℧ wasysym,amstext,lyxmathsym # works in text mode only (produces 0 in math mode) +thorn wasy 105 0 textmode þ wasysym,amstext,lyxmathsym # works in text mode only (produces i in math mode) +Thorn wasy 106 0 textmode Þ wasysym,amstext,lyxmathsym # works in text mode only (produces j in math mode) +openo wasy 108 0 textmode ɔ wasysym,amstext,lyxmathsym # works in text mode only (produces l in math mode) +inve wasy 85 0 textmode ə wasysym,amstext,lyxmathsym # works in text mode only (produces U in math mode) #mho wasy 48 0 mathord ℧ wasysym # already in amsfonts #Join wasy 49 0 mathrel x wasysym # already in amsfonts @@ -1166,21 +1166,21 @@ else \def\Join{|x|} amssymb endif # Fixme: latin-1 chars in text file -\def\AA{\AA}{Å} -\def\O{\O}{Ø} +\def\AA{\AA}{Å} textmode Å amstext,lyxmathsym +\def\O{\O}{Ø} textmode Ø amstext,lyxmathsym iffont cmsy # The \sim is placed too high... \def\cong{\stackrel{_\sim}{=}} mathrel ≅ lyxsurd cmsy 112 0 mathord √ \def\surd{^\lyxsurd} mathord √ -\def\textdegree{\kern-1mu^{\circ}\kern-4mu} mathord ° +\def\textdegree{\kern-1mu^{\circ}\kern-4mu} textmode ° textcomp,amstext,lyxmathsym else # FIXME: These don't work on OS X, since the Symbol font uses a different # encoding and is therefore disabled in FontLoader::available(). cong lyxsymbol 64 0 mathrel ≅ surd lyxsymbol 214 0 mathord √ -textdegree lyxsymbol 176 0 mathord ° +textdegree lyxsymbol 176 0 mathord ° textcomp,amstext,lyxmathsym endif #"{\hat{}} diff --git a/src/BufferParams.cpp b/src/BufferParams.cpp index caa2dcb4f4..fa95e6a8f5 100644 --- a/src/BufferParams.cpp +++ b/src/BufferParams.cpp @@ -1866,6 +1866,7 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features, // Now insert the LyX specific LaTeX commands... docstring lyxpreamble; features.resolveAlternatives(); + features.expandMultiples(); if (output_sync) { if (!output_sync_macro.empty()) diff --git a/src/LaTeXFeatures.cpp b/src/LaTeXFeatures.cpp index 1e9d9e6c2e..e405b24848 100644 --- a/src/LaTeXFeatures.cpp +++ b/src/LaTeXFeatures.cpp @@ -1885,4 +1885,24 @@ void LaTeXFeatures::resolveAlternatives() } +void LaTeXFeatures::expandMultiples() +{ + for (Features::iterator it = features_.begin(); it != features_.end();) { + if (contains(*it, ',')) { + vector const multiples = getVectorFromString(*it, ","); + vector::const_iterator const end = multiples.end(); + vector::const_iterator itm = multiples.begin(); + // Do nothing if any multiple is already required + for (; itm != end; ++itm) { + if (!isRequired(*itm)) + require(*itm); + } + features_.erase(it); + it = features_.begin(); + } else + ++it; + } +} + + } // namespace lyx diff --git a/src/LaTeXFeatures.h b/src/LaTeXFeatures.h index 38273e4fa2..6b1febf28b 100644 --- a/src/LaTeXFeatures.h +++ b/src/LaTeXFeatures.h @@ -156,6 +156,8 @@ public: OutputParams const & runparams() const { return runparams_; } /// Resolve alternatives like "esint|amsmath|wasysym" void resolveAlternatives(); + /// Expand multiple requirements like "textcomp,lyxmathsym,amstext" + void expandMultiples(); /// void setHTMLTitle(docstring const & t) { htmltitle_ = t; } /// diff --git a/src/mathed/InsetMathFont.cpp b/src/mathed/InsetMathFont.cpp index 020509ddf2..40623f6ec6 100644 --- a/src/mathed/InsetMathFont.cpp +++ b/src/mathed/InsetMathFont.cpp @@ -64,6 +64,24 @@ bool InsetMathFont::lockedMode() const } +void InsetMathFont::write(WriteStream & os) const +{ + // Close the mode changing command inserted during export if + // we are going to output another mode changing command that + // actually doesn't change mode. This avoids exporting things + // such as \ensuremath{a\mathit{b}} or \textit{a\text{b}} and + // produce instead \ensuremath{a}\mathit{b} and \textit{a}\text{b}. + if (os.pendingBrace() + && ((currentMode() == TEXT_MODE && os.textMode()) + || (currentMode() == MATH_MODE && !os.textMode()))) { + os.os() << '}'; + os.pendingBrace(false); + os.textMode(!os.textMode()); + } + InsetMathNest::write(os); +} + + void InsetMathFont::metrics(MetricsInfo & mi, Dimension & dim) const { Changer dummy = mi.base.changeFontSet(font()); diff --git a/src/mathed/InsetMathFont.h b/src/mathed/InsetMathFont.h index 945dcf7f34..2dc524177e 100644 --- a/src/mathed/InsetMathFont.h +++ b/src/mathed/InsetMathFont.h @@ -34,6 +34,8 @@ public: /// do we allow changing mode during latex export? bool lockedMode() const; /// + void write(WriteStream & os) const; + /// docstring name() const; /// void metrics(MetricsInfo & mi, Dimension & dim) const; diff --git a/src/mathed/InsetMathSpace.cpp b/src/mathed/InsetMathSpace.cpp index e6aa834ea8..4cf1b944f8 100644 --- a/src/mathed/InsetMathSpace.cpp +++ b/src/mathed/InsetMathSpace.cpp @@ -266,7 +266,9 @@ void InsetMathSpace::normalize(NormalStream & os) const void InsetMathSpace::write(WriteStream & os) const { - // no MathEnsurer - all kinds work in text and math mode + // All kinds work in text and math mode, so simply suspend + // writing a possibly pending mode closing brace. + MathEnsurer ensurer(os, false); if (space_info[space_].escape) os << '\\'; os << space_info[space_].name.c_str(); diff --git a/src/mathed/InsetMathSymbol.cpp b/src/mathed/InsetMathSymbol.cpp index a4dc36abf0..68c8247429 100644 --- a/src/mathed/InsetMathSymbol.cpp +++ b/src/mathed/InsetMathSymbol.cpp @@ -260,6 +260,8 @@ void InsetMathSymbol::write(WriteStream & os) const unique_ptr ensurer; if (currentMode() != TEXT_MODE) ensurer = make_unique(os); + else + ensurer = make_unique(os, false, true, true); os << '\\' << name(); // $,#, etc. In theory the restriction based on catcodes, but then diff --git a/src/mathed/MacroTable.h b/src/mathed/MacroTable.h index 030e6f74ca..bc445d4397 100644 --- a/src/mathed/MacroTable.h +++ b/src/mathed/MacroTable.h @@ -68,6 +68,8 @@ public: /// char const * MathMLtype() const; /// + latexkeys const * symbol() const { return sym_; } + /// void setSymbol(latexkeys const * sym) { sym_ = sym; } /// DocIterator const & pos() { return pos_; } diff --git a/src/mathed/MathFactory.cpp b/src/mathed/MathFactory.cpp index f7c21e805e..0814167612 100644 --- a/src/mathed/MathFactory.cpp +++ b/src/mathed/MathFactory.cpp @@ -360,23 +360,41 @@ void initMath() } -bool ensureMath(WriteStream & os, bool needs_math_mode, bool macro) +bool ensureMath(WriteStream & os, bool needs_mathmode, bool macro, + bool textmode_macro) { bool brace = os.pendingBrace(); os.pendingBrace(false); if (!os.latex()) return brace; - if (os.textMode() && needs_math_mode) { - os << "\\ensuremath{"; + if (os.textMode() && needs_mathmode) { + if (brace) { + // close \lyxmathsym + os << '}'; + brace = false; + } else { + os << "\\ensuremath{"; + brace = true; + } os.textMode(false); - brace = true; - } else if (macro && brace && !needs_math_mode) { - // This is a user defined macro, but not a MathMacro, so we - // cannot be sure what mode is needed. As it was entered in - // a text box, we restore the text mode. - os << '}'; + } else if (macro && textmode_macro && !os.textMode()) { + if (brace) { + // close \ensuremath + os << '}'; + brace = false; + } else { + os << "\\lyxmathsym{"; + brace = true; + } os.textMode(true); + } else if (macro && brace && !textmode_macro) { + // This is a user defined macro, not a MathMacro, so we + // cannot be sure what mode is needed. We leave it in the + // same environment it was entered by closing either \lyxmathsym + // or \ensuremath, whichever was opened. + os << '}'; brace = false; + os.textMode(!os.textMode()); } return brace; } diff --git a/src/mathed/MathMacro.cpp b/src/mathed/MathMacro.cpp index 3def7f9f14..4529cf9280 100644 --- a/src/mathed/MathMacro.cpp +++ b/src/mathed/MathMacro.cpp @@ -614,6 +614,10 @@ void MathMacro::draw(PainterInfo & pi, int x, int y) const drawMarkers2(pi, expx, expy); } else { bool drawBox = lyxrc.macro_edit_style == LyXRC::MACRO_EDIT_INLINE_BOX; + bool upshape = d->macro_ && d->macro_->symbol() + && d->macro_->symbol()->extra == "textmode"; + Changer dummy = pi.base.font.changeShape(upshape ? UP_SHAPE + : pi.base.font.shape()); // warm up cells for (size_t i = 0; i < nargs(); ++i) @@ -925,7 +929,12 @@ bool MathMacro::folded() const void MathMacro::write(WriteStream & os) const { - MathEnsurer ensurer(os, d->macro_ != 0, true); + bool const textmode_macro = d->macro_ && d->macro_->symbol() + && d->macro_->symbol()->extra == "textmode"; + bool const needs_mathmode = d->macro_ && (!d->macro_->symbol() + || d->macro_->symbol()->extra != "textmode"); + + MathEnsurer ensurer(os, needs_mathmode, true, textmode_macro); // non-normal mode if (d->displayMode_ != DISPLAY_NORMAL) { diff --git a/src/mathed/MathStream.h b/src/mathed/MathStream.h index 3116ab43d0..fe5c259a93 100644 --- a/src/mathed/MathStream.h +++ b/src/mathed/MathStream.h @@ -141,10 +141,11 @@ WriteStream & operator<<(WriteStream &, int); /// WriteStream & operator<<(WriteStream &, unsigned int); -/// ensure math mode, possibly by opening \ensuremath -bool ensureMath(WriteStream & os, bool needs_math_mode = true, bool macro = false); +/// ensure correct mode, possibly by opening \ensuremath or \lyxmathsym +bool ensureMath(WriteStream & os, bool needs_mathmode = true, + bool macro = false, bool textmode_macro = false); -/// ensure the requested mode, possibly by closing \ensuremath +/// ensure the requested mode, possibly by closing \ensuremath or \lyxmathsym int ensureMode(WriteStream & os, InsetMath::mode_type mode, bool locked, bool ascii); @@ -153,6 +154,8 @@ int ensureMode(WriteStream & os, InsetMath::mode_type mode, bool locked, bool as * * A local variable of this type can be used to either ensure math mode * or delay the writing of a pending brace when outputting LaTeX. + * A LyX MathMacro is always assumed needing a math mode environment, while + * no assumption is made for macros defined through \newcommand or \def. * * Example 1: * @@ -171,23 +174,35 @@ int ensureMode(WriteStream & os, InsetMath::mode_type mode, bool locked, bool as * * Example 3: * - * MathEnsurer ensurer(os, needs_math_mode, true); + * MathEnsurer ensurer(os, needs_mathmode, true, textmode_macro); * - * The third parameter is set to true only for a user defined macro, which - * needs special handling. When it is a MathMacro, the needs_math_mode - * parameter is true and the behavior is as in Example 1. When the - * needs_math_mode parameter is false (not a MathMacro) and the macro - * was entered in a text box and we are in math mode, the mode is reset - * to text. This is because the macro was probably designed for text mode - * (given that it was entered in text mode and we have no way to tell the - * contrary). + * This form is mainly used for math macros as they are treated specially. + * In essence, the macros defined in the lib/symbols file and tagged as + * textmode will be enclosed in \lyxmathsym if they appear in a math mode + * environment, while macros defined in the preamble or ERT are left as is. + * The third parameter must be set to true and the fourth parameter has also + * to be specified. Only the following 3 different cases are handled. + * + * When the needs_mathmode parameter is true the behavior is as in Example 1. + * This is the case for a LyX MathMacro or a macro not tagged as textmode. + * + * When the needs_mathmode and textmode_macro parameters are both false the + * macro is left in the same (text or math mode) environment it was entered. + * This is because it is assumed that the macro was designed for that mode + * and we have no way to tell the contrary. + * This is the case for macros defined by using \newcommand or \def in ERT. + * + * When the needs_mathmode parameter is false while textmode_macro is true the + * macro will be enclosed in \lyxmathsym if it appears in a math mode environment. + * This is the case for the macros tagged as textmode in lib/symbols. */ class MathEnsurer { public: /// - explicit MathEnsurer(WriteStream & os, bool needs_math_mode = true, bool macro = false) - : os_(os), brace_(ensureMath(os, needs_math_mode, macro)) {} + explicit MathEnsurer(WriteStream & os, bool needs_mathmode = true, + bool macro = false, bool textmode_macro = false) + : os_(os), brace_(ensureMath(os, needs_mathmode, macro, textmode_macro)) {} /// ~MathEnsurer() { os_.pendingBrace(brace_); } private: -- 2.39.5