X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FFont.cpp;h=a6580fda216f3fd51ad175f6dd3061f4f60c7409;hb=38c2cde0d8695ac5287bae218c4a33a2acf18ef8;hp=bf71d73c4c89e5fb3184d9659f79efcaa538e1f0;hpb=fc6ce7cd08562fd7bab4427880b46390bb7d2f07;p=lyx.git diff --git a/src/Font.cpp b/src/Font.cpp index bf71d73c4c..a6580fda21 100644 --- a/src/Font.cpp +++ b/src/Font.cpp @@ -3,10 +3,10 @@ * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * - * \author Lars Gullik Bjønnes + * \author Lars Gullik Bjønnes * \author Jean-Marc Lasgouttes * \author Angus Leeming - * \author André Pönitz + * \author André Pönitz * \author Dekel Tsur * * Full author contact details are available in file CREDITS. @@ -17,346 +17,87 @@ #include "Font.h" #include "BufferParams.h" // stateText -#include "debug.h" -#include "gettext.h" +#include "ColorSet.h" +#include "Encoding.h" #include "Language.h" -#include "Color.h" +#include "LaTeXFeatures.h" #include "Lexer.h" #include "LyXRC.h" - +#include "output_latex.h" +#include "OutputParams.h" +#include "texstream.h" + +#include "support/lassert.h" +#include "support/convert.h" +#include "support/debug.h" +#include "support/gettext.h" #include "support/lstrings.h" +#include -namespace lyx { - -using support::ascii_lowercase; -using support::bformat; -using support::rtrim; -using support::subst; - -using std::endl; -using std::string; -using std::ostream; - -#ifndef CXX_GLOBAL_CSTD -using std::strlen; -#endif - -// -// Names for the GUI -// - -namespace { - -char const * GUIFamilyNames[Font::NUM_FAMILIES + 2 /* default & error */] = -{ N_("Roman"), N_("Sans Serif"), N_("Typewriter"), N_("Symbol"), - "cmr", "cmsy", "cmm", "cmex", "msa", "msb", "eufrak", "wasy", "esint", - N_("Inherit"), N_("Ignore") }; - -char const * GUISeriesNames[4] = -{ N_("Medium"), N_("Bold"), N_("Inherit"), N_("Ignore") }; - -char const * GUIShapeNames[6] = -{ N_("Upright"), N_("Italic"), N_("Slanted"), N_("Smallcaps"), N_("Inherit"), - N_("Ignore") }; - -char const * GUISizeNames[14] = -{ N_("Tiny"), N_("Smallest"), N_("Smaller"), N_("Small"), N_("Normal"), N_("Large"), - N_("Larger"), N_("Largest"), N_("Huge"), N_("Huger"), N_("Increase"), N_("Decrease"), - N_("Inherit"), N_("Ignore") }; - -char const * GUIMiscNames[5] = -{ N_("Off"), N_("On"), N_("Toggle"), N_("Inherit"), N_("Ignore") }; +using namespace std; +using namespace lyx::support; +namespace lyx { // // Strings used to read and write .lyx format files // -char const * LyXFamilyNames[Font::NUM_FAMILIES + 2 /* default & error */] = -{ "roman", "sans", "typewriter", "symbol", - "cmr", "cmsy", "cmm", "cmex", "msa", "msb", "eufrak", "wasy", "esint", - "default", "error" }; +// These are defined in FontInfo.cpp +extern char const * LyXFamilyNames[NUM_FAMILIES + 2]; +extern char const * LyXSeriesNames[NUM_SERIES + 2]; +extern char const * LyXShapeNames[NUM_SHAPE + 2]; +extern char const * LyXSizeNames[NUM_SIZE + 4]; +extern char const * LyXMiscNames[5]; +extern char const * GUIMiscNames[5]; -char const * LyXSeriesNames[4] = -{ "medium", "bold", "default", "error" }; - -char const * LyXShapeNames[6] = -{ "up", "italic", "slanted", "smallcaps", "default", "error" }; - -char const * LyXSizeNames[14] = -{ "tiny", "scriptsize", "footnotesize", "small", "normal", "large", - "larger", "largest", "huge", "giant", - "increase", "decrease", "default", "error" }; - -char const * LyXMiscNames[5] = -{ "off", "on", "toggle", "default", "error" }; +namespace { // // Strings used to write LaTeX files // -char const * LaTeXFamilyNames[6] = -{ "textrm", "textsf", "texttt", "error1", "error2", "error3" }; +char const * LaTeXFamilyNames[NUM_FAMILIES + 2] = +{ "textrm", "textsf", "texttt", "error1", "error2", "error3", "error4", + "error5", "error6", "error7", "error8", "error9", "error10", "error11", + "error12", "error13" }; -char const * LaTeXSeriesNames[4] = +char const * LaTeXSeriesNames[NUM_SERIES + 2] = { "textmd", "textbf", "error4", "error5" }; -char const * LaTeXShapeNames[6] = +char const * LaTeXShapeNames[NUM_SHAPE + 2] = { "textup", "textit", "textsl", "textsc", "error6", "error7" }; -char const * LaTeXSizeNames[14] = +char const * LaTeXSizeNames[NUM_SIZE + 4] = { "tiny", "scriptsize", "footnotesize", "small", "normalsize", "large", "Large", "LARGE", "huge", "Huge", "error8", "error9", "error10", "error11" }; -} // namespace anon - - -// Initialize static member -Font::FontBits Font::sane = { - ROMAN_FAMILY, - MEDIUM_SERIES, - UP_SHAPE, - SIZE_NORMAL, - Color::none, - OFF, - OFF, - OFF, - OFF }; - -// Initialize static member -Font::FontBits Font::inherit = { - INHERIT_FAMILY, - INHERIT_SERIES, - INHERIT_SHAPE, - INHERIT_SIZE, - Color::inherit, - INHERIT, - INHERIT, - INHERIT, - OFF }; - -// Initialize static member -Font::FontBits Font::ignore = { - IGNORE_FAMILY, - IGNORE_SERIES, - IGNORE_SHAPE, - IGNORE_SIZE, - Color::ignore, - IGNORE, - IGNORE, - IGNORE, - IGNORE }; - - -bool operator==(Font::FontBits const & lhs, - Font::FontBits const & rhs) -{ - return lhs.family == rhs.family && - lhs.series == rhs.series && - lhs.shape == rhs.shape && - lhs.size == rhs.size && - lhs.color == rhs.color && - lhs.emph == rhs.emph && - lhs.underbar == rhs.underbar && - lhs.noun == rhs.noun && - lhs.number == rhs.number; -} - - -Font::Font() - : bits(sane), lang(default_language) -{} - - -Font::Font(Font::FONT_INIT1) - : bits(inherit), lang(default_language) -{} - - -Font::Font(Font::FONT_INIT2) - : bits(ignore), lang(ignore_language) -{} - - -Font::Font(Font::FONT_INIT3) - : bits(sane), lang(default_language) -{} - +} // namespace -Font::Font(Font::FONT_INIT1, Language const * l) - : bits(inherit), lang(l) -{} - -Font::Font(Font::FONT_INIT2, Language const * l) - : bits(ignore), lang(l) -{} - - -Font::Font(Font::FONT_INIT3, Language const * l) - : bits(sane), lang(l) -{} - - - -Color_color Font::color() const +Font::Font(FontInfo bits, Language const * l) + : bits_(bits), lang_(l), open_encoding_(false) { - return Color::color(bits.color); + if (!lang_) + lang_ = default_language; } bool Font::isRightToLeft() const { - return lang->rightToLeft(); + return lang_->rightToLeft(); } bool Font::isVisibleRightToLeft() const { - return (lang->rightToLeft() && - number() != ON); -} - - -void Font::setFamily(Font::FONT_FAMILY f) -{ - bits.family = f; -} - - -void Font::setSeries(Font::FONT_SERIES s) -{ - bits.series = s; -} - - -void Font::setShape(Font::FONT_SHAPE s) -{ - bits.shape = s; -} - - -void Font::setSize(Font::FONT_SIZE s) -{ - bits.size = s; -} - - -void Font::setEmph(Font::FONT_MISC_STATE e) -{ - bits.emph = e; -} - - -void Font::setUnderbar(Font::FONT_MISC_STATE u) -{ - bits.underbar = u; -} - - -void Font::setNoun(Font::FONT_MISC_STATE n) -{ - bits.noun = n; -} - - -void Font::setColor(Color_color c) -{ - bits.color = int(c); + return (lang_->rightToLeft() && + bits_.number() != FONT_ON); } void Font::setLanguage(Language const * l) { - lang = l; -} - - -void Font::setNumber(Font::FONT_MISC_STATE n) -{ - bits.number = n; -} - - -/// Decreases font size by one -Font & Font::decSize() -{ - switch (size()) { - case SIZE_HUGER: setSize(SIZE_HUGE); break; - case SIZE_HUGE: setSize(SIZE_LARGEST); break; - case SIZE_LARGEST: setSize(SIZE_LARGER); break; - case SIZE_LARGER: setSize(SIZE_LARGE); break; - case SIZE_LARGE: setSize(SIZE_NORMAL); break; - case SIZE_NORMAL: setSize(SIZE_SMALL); break; - case SIZE_SMALL: setSize(SIZE_FOOTNOTE); break; - case SIZE_FOOTNOTE: setSize(SIZE_SCRIPT); break; - case SIZE_SCRIPT: setSize(SIZE_TINY); break; - case SIZE_TINY: break; - case INCREASE_SIZE: - lyxerr << "Can't Font::decSize on INCREASE_SIZE" << endl; - break; - case DECREASE_SIZE: - lyxerr <<"Can't Font::decSize on DECREASE_SIZE" << endl; - break; - case INHERIT_SIZE: - lyxerr <<"Can't Font::decSize on INHERIT_SIZE" << endl; - break; - case IGNORE_SIZE: - lyxerr <<"Can't Font::decSize on IGNORE_SIZE" << endl; - break; - } - return *this; -} - - -/// Increases font size by one -Font & Font::incSize() -{ - switch (size()) { - case SIZE_HUGER: break; - case SIZE_HUGE: setSize(SIZE_HUGER); break; - case SIZE_LARGEST: setSize(SIZE_HUGE); break; - case SIZE_LARGER: setSize(SIZE_LARGEST); break; - case SIZE_LARGE: setSize(SIZE_LARGER); break; - case SIZE_NORMAL: setSize(SIZE_LARGE); break; - case SIZE_SMALL: setSize(SIZE_NORMAL); break; - case SIZE_FOOTNOTE: setSize(SIZE_SMALL); break; - case SIZE_SCRIPT: setSize(SIZE_FOOTNOTE); break; - case SIZE_TINY: setSize(SIZE_SCRIPT); break; - case INCREASE_SIZE: - lyxerr <<"Can't Font::incSize on INCREASE_SIZE" << endl; - break; - case DECREASE_SIZE: - lyxerr <<"Can't Font::incSize on DECREASE_SIZE" << endl; - break; - case INHERIT_SIZE: - lyxerr <<"Can't Font::incSize on INHERIT_SIZE" << endl; - break; - case IGNORE_SIZE: - lyxerr <<"Can't Font::incSize on IGNORE_SIZE" << endl; - break; - } - return *this; -} - - -/// Updates a misc setting according to request -Font::FONT_MISC_STATE Font::setMisc(FONT_MISC_STATE newfont, - FONT_MISC_STATE org) -{ - if (newfont == TOGGLE) { - if (org == ON) - return OFF; - else if (org == OFF) - return ON; - else { - lyxerr <<"Font::setMisc: Need state" - " ON or OFF to toggle. Setting to ON" << endl; - return ON; - } - } else if (newfont == IGNORE) - return org; - else - return newfont; + lang_ = l; } @@ -365,324 +106,42 @@ void Font::update(Font const & newfont, Language const * document_language, bool toggleall) { - if (newfont.family() == family() && toggleall) - setFamily(INHERIT_FAMILY); // toggle 'back' - else if (newfont.family() != IGNORE_FAMILY) - setFamily(newfont.family()); - // else it's IGNORE_SHAPE - - // "Old" behaviour: "Setting" bold will toggle bold on/off. - switch (newfont.series()) { - case BOLD_SERIES: - // We toggle... - if (series() == BOLD_SERIES && toggleall) - setSeries(MEDIUM_SERIES); - else - setSeries(BOLD_SERIES); - break; - case MEDIUM_SERIES: - case INHERIT_SERIES: - setSeries(newfont.series()); - break; - case IGNORE_SERIES: - break; - } - - if (newfont.shape() == shape() && toggleall) - setShape(INHERIT_SHAPE); // toggle 'back' - else if (newfont.shape() != IGNORE_SHAPE) - setShape(newfont.shape()); - // else it's IGNORE_SHAPE - - if (newfont.size() != IGNORE_SIZE) { - if (newfont.size() == INCREASE_SIZE) - incSize(); - else if (newfont.size() == DECREASE_SIZE) - decSize(); - else - setSize(newfont.size()); - } - - setEmph(setMisc(newfont.emph(), emph())); - setUnderbar(setMisc(newfont.underbar(), underbar())); - setNoun(setMisc(newfont.noun(), noun())); + bits_.update(newfont.fontInfo(), toggleall); - setNumber(setMisc(newfont.number(), number())); if (newfont.language() == language() && toggleall) if (language() == document_language) setLanguage(default_language); else setLanguage(document_language); + else if (newfont.language() == reset_language) + setLanguage(document_language); else if (newfont.language() != ignore_language) setLanguage(newfont.language()); - - if (newfont.color() == color() && toggleall) - setColor(Color::inherit); // toggle 'back' - else if (newfont.color() != Color::ignore) - setColor(newfont.color()); -} - - -/// Reduce font to fall back to template where possible -void Font::reduce(Font const & tmplt) -{ - if (family() == tmplt.family()) - setFamily(INHERIT_FAMILY); - if (series() == tmplt.series()) - setSeries(INHERIT_SERIES); - if (shape() == tmplt.shape()) - setShape(INHERIT_SHAPE); - if (size() == tmplt.size()) - setSize(INHERIT_SIZE); - if (emph() == tmplt.emph()) - setEmph(INHERIT); - if (underbar() == tmplt.underbar()) - setUnderbar(INHERIT); - if (noun() == tmplt.noun()) - setNoun(INHERIT); - if (color() == tmplt.color()) - setColor(Color::inherit); } -/// Realize font from a template -Font & Font::realize(Font const & tmplt) -{ - if (bits == inherit) { - bits = tmplt.bits; - return *this; - } - - if (bits.family == INHERIT_FAMILY) - bits.family = tmplt.bits.family; - - if (bits.series == INHERIT_SERIES) - bits.series = tmplt.bits.series; - - if (bits.shape == INHERIT_SHAPE) - bits.shape = tmplt.bits.shape; - - if (bits.size == INHERIT_SIZE) - bits.size = tmplt.bits.size; - - if (bits.emph == INHERIT) - bits.emph = tmplt.bits.emph; - - if (bits.underbar == INHERIT) - bits.underbar = tmplt.bits.underbar; - - if (bits.noun == INHERIT) - bits.noun = tmplt.bits.noun; - - if (bits.color == Color::inherit) - bits.color = tmplt.bits.color; - - return *this; -} - - -/// Is font resolved? -bool Font::resolved() const -{ - return (family() != INHERIT_FAMILY && series() != INHERIT_SERIES && - shape() != INHERIT_SHAPE && size() != INHERIT_SIZE && - emph() != INHERIT && underbar() != INHERIT && - noun() != INHERIT && - color() != Color::inherit); -} - - -docstring const Font::stateText(BufferParams * params) const +docstring const Font::stateText(BufferParams * params, bool const terse) const { odocstringstream os; - if (family() != INHERIT_FAMILY) - os << _(GUIFamilyNames[family()]) << ", "; - if (series() != INHERIT_SERIES) - os << _(GUISeriesNames[series()]) << ", "; - if (shape() != INHERIT_SHAPE) - os << _(GUIShapeNames[shape()]) << ", "; - if (size() != INHERIT_SIZE) - os << _(GUISizeNames[size()]) << ", "; - if (color() != Color::inherit) - os << lcolor.getGUIName(color()) << ", "; - if (emph() != INHERIT) - os << bformat(_("Emphasis %1$s, "), - _(GUIMiscNames[emph()])); - if (underbar() != INHERIT) - os << bformat(_("Underline %1$s, "), - _(GUIMiscNames[underbar()])); - if (noun() != INHERIT) - os << bformat(_("Noun %1$s, "), - _(GUIMiscNames[noun()])); - if (bits == inherit) - os << _("Default") << ", "; - if (!params || (language() != params->language)) + os << bits_.stateText(terse); + if ((!params || (language() != params->language)) + && (!terse || language() != ignore_language)) { + // reset_language is a null pointer! os << bformat(_("Language: %1$s, "), - _(language()->display())); - if (number() != OFF) - os << bformat(_(" Number %1$s"), - _(GUIMiscNames[number()])); + (language() == reset_language) ? _("Default") + : _(language()->display())); + } + if (bits_.number() != FONT_OFF) + os << " " << bformat(_("Number %1$s"), + _(GUIMiscNames[bits_.number()])); return rtrim(os.str(), ", "); } -// Set family according to lyx format string -Font & Font::setLyXFamily(string const & fam) -{ - string const s = ascii_lowercase(fam); - - int i = 0; - while (LyXFamilyNames[i] != s && - LyXFamilyNames[i] != string("error")) - ++i; - if (s == LyXFamilyNames[i]) - setFamily(Font::FONT_FAMILY(i)); - else - lyxerr << "Font::setLyXFamily: Unknown family `" - << s << '\'' << endl; - return *this; -} - - -// Set series according to lyx format string -Font & Font::setLyXSeries(string const & ser) -{ - string const s = ascii_lowercase(ser); - - int i = 0; - while (LyXSeriesNames[i] != s && - LyXSeriesNames[i] != string("error")) ++i; - if (s == LyXSeriesNames[i]) { - setSeries(Font::FONT_SERIES(i)); - } else - lyxerr << "Font::setLyXSeries: Unknown series `" - << s << '\'' << endl; - return *this; -} - - -// Set shape according to lyx format string -Font & Font::setLyXShape(string const & sha) -{ - string const s = ascii_lowercase(sha); - - int i = 0; - while (LyXShapeNames[i] != s && LyXShapeNames[i] != string("error")) - ++i; - if (s == LyXShapeNames[i]) - setShape(Font::FONT_SHAPE(i)); - else - lyxerr << "Font::setLyXShape: Unknown shape `" - << s << '\'' << endl; - return *this; -} - - -// Set size according to lyx format string -Font & Font::setLyXSize(string const & siz) -{ - string const s = ascii_lowercase(siz); - int i = 0; - while (LyXSizeNames[i] != s && LyXSizeNames[i] != string("error")) - ++i; - if (s == LyXSizeNames[i]) { - setSize(Font::FONT_SIZE(i)); - } else - lyxerr << "Font::setLyXSize: Unknown size `" - << s << '\'' << endl; - return *this; -} - - -// Set size according to lyx format string -Font::FONT_MISC_STATE Font::setLyXMisc(string const & siz) -{ - string const s = ascii_lowercase(siz); - int i = 0; - while (LyXMiscNames[i] != s && - LyXMiscNames[i] != string("error")) ++i; - if (s == LyXMiscNames[i]) - return FONT_MISC_STATE(i); - lyxerr << "Font::setLyXMisc: Unknown misc flag `" - << s << '\'' << endl; - return OFF; -} - - -/// Sets color after LyX text format -Font & Font::setLyXColor(string const & col) -{ - setColor(lcolor.getFromLyXName(col)); - return *this; -} - - // Returns size in latex format string const Font::latexSize() const { - return LaTeXSizeNames[size()]; -} - - -// Read a font definition from given file in lyx format -// Used for layouts -Font & Font::lyxRead(Lexer & lex) -{ - bool error = false; - bool finished = false; - while (!finished && lex.isOK() && !error) { - lex.next(); - string const tok = ascii_lowercase(lex.getString()); - - if (tok.empty()) { - continue; - } else if (tok == "endfont") { - finished = true; - } else if (tok == "family") { - lex.next(); - string const ttok = lex.getString(); - setLyXFamily(ttok); - } else if (tok == "series") { - lex.next(); - string const ttok = lex.getString(); - setLyXSeries(ttok); - } else if (tok == "shape") { - lex.next(); - string const ttok = lex.getString(); - setLyXShape(ttok); - } else if (tok == "size") { - lex.next(); - string const ttok = lex.getString(); - setLyXSize(ttok); - } else if (tok == "misc") { - lex.next(); - string const ttok = ascii_lowercase(lex.getString()); - - if (ttok == "no_bar") { - setUnderbar(OFF); - } else if (ttok == "no_emph") { - setEmph(OFF); - } else if (ttok == "no_noun") { - setNoun(OFF); - } else if (ttok == "emph") { - setEmph(ON); - } else if (ttok == "underbar") { - setUnderbar(ON); - } else if (ttok == "noun") { - setNoun(ON); - } else { - lex.printError("Illegal misc type `$$Token´"); - } - } else if (tok == "color") { - lex.next(); - string const ttok = lex.getString(); - setLyXColor(ttok); - } else { - lex.printError("Unknown tag `$$Token'"); - error = true; - } - } - return *this; + return LaTeXSizeNames[bits_.size()]; } @@ -691,39 +150,57 @@ void Font::lyxWriteChanges(Font const & orgfont, ostream & os) const { os << "\n"; - if (orgfont.family() != family()) - os << "\\family " << LyXFamilyNames[family()] << "\n"; - if (orgfont.series() != series()) - os << "\\series " << LyXSeriesNames[series()] << "\n"; - if (orgfont.shape() != shape()) - os << "\\shape " << LyXShapeNames[shape()] << "\n"; - if (orgfont.size() != size()) - os << "\\size " << LyXSizeNames[size()] << "\n"; - if (orgfont.emph() != emph()) - os << "\\emph " << LyXMiscNames[emph()] << "\n"; - if (orgfont.number() != number()) - os << "\\numeric " << LyXMiscNames[number()] << "\n"; - if (orgfont.underbar() != underbar()) { + if (orgfont.fontInfo().family() != bits_.family()) + os << "\\family " << LyXFamilyNames[bits_.family()] << "\n"; + if (orgfont.fontInfo().series() != bits_.series()) + os << "\\series " << LyXSeriesNames[bits_.series()] << "\n"; + if (orgfont.fontInfo().shape() != bits_.shape()) + os << "\\shape " << LyXShapeNames[bits_.shape()] << "\n"; + if (orgfont.fontInfo().size() != bits_.size()) + os << "\\size " << LyXSizeNames[bits_.size()] << "\n"; + // FIXME: shall style be handled there? Probably not. + if (orgfont.fontInfo().emph() != bits_.emph()) + os << "\\emph " << LyXMiscNames[bits_.emph()] << "\n"; + if (orgfont.fontInfo().number() != bits_.number()) + os << "\\numeric " << LyXMiscNames[bits_.number()] << "\n"; + if (orgfont.fontInfo().nospellcheck() != bits_.nospellcheck()) + os << "\\nospellcheck " << LyXMiscNames[bits_.nospellcheck()] << "\n"; + if (orgfont.fontInfo().underbar() != bits_.underbar()) { // This is only for backwards compatibility - switch (underbar()) { - case OFF: os << "\\bar no\n"; break; - case ON: os << "\\bar under\n"; break; - case TOGGLE: lyxerr << "Font::lyxWriteFontChanges: " - "TOGGLE should not appear here!" + switch (bits_.underbar()) { + case FONT_OFF: os << "\\bar no\n"; break; + case FONT_ON: os << "\\bar under\n"; break; + case FONT_TOGGLE: lyxerr << "Font::lyxWriteFontChanges: " + "FONT_TOGGLE should not appear here!" << endl; break; - case INHERIT: os << "\\bar default\n"; break; - case IGNORE: lyxerr << "Font::lyxWriteFontChanges: " + case FONT_INHERIT: os << "\\bar default\n"; break; + case FONT_IGNORE: lyxerr << "Font::lyxWriteFontChanges: " "IGNORE should not appear here!" << endl; break; } } - if (orgfont.noun() != noun()) { - os << "\\noun " << LyXMiscNames[noun()] << "\n"; + if (orgfont.fontInfo().strikeout() != bits_.strikeout()) { + os << "\\strikeout " << LyXMiscNames[bits_.strikeout()] << "\n"; + } + if (orgfont.fontInfo().xout() != bits_.xout()) { + os << "\\xout " << LyXMiscNames[bits_.xout()] << "\n"; + } + if (orgfont.fontInfo().uuline() != bits_.uuline()) { + os << "\\uuline " << LyXMiscNames[bits_.uuline()] << "\n"; + } + if (orgfont.fontInfo().uwave() != bits_.uwave()) { + os << "\\uwave " << LyXMiscNames[bits_.uwave()] << "\n"; } - if (orgfont.color() != color()) - os << "\\color " << lcolor.getLyXName(color()) << '\n'; + if (orgfont.fontInfo().noun() != bits_.noun()) { + os << "\\noun " << LyXMiscNames[bits_.noun()] << "\n"; + } + if (orgfont.fontInfo().color() != bits_.color()) + os << "\\color " << lcolor.getLyXName(bits_.color()) << '\n'; + // FIXME: uncomment this when we support background. + //if (orgfont.fontInfo().background() != bits_.background()) + // os << "\\color " << lcolor.getLyXName(bits_.background()) << '\n'; if (orgfont.language() != language() && language() != latex_language) { if (language()) @@ -736,15 +213,49 @@ void Font::lyxWriteChanges(Font const & orgfont, /// Writes the head of the LaTeX needed to impose this font // Returns number of chars written. -int Font::latexWriteStartChanges(odocstream & os, Font const & base, - Font const & prev) const +int Font::latexWriteStartChanges(odocstream & os, BufferParams const & bparams, + OutputParams const & runparams, + Font const & base, + Font const & prev) const { bool env = false; int count = 0; - if (language()->babel() != base.language()->babel() && + + // polyglossia or babel? + if (runparams.use_polyglossia + && language()->lang() != base.language()->lang() + && language() != prev.language()) { + if (!language()->polyglossia().empty()) { + string tmp = "\\text" + language()->polyglossia(); + if (!language()->polyglossiaOpts().empty()) + tmp += "[" + language()->polyglossiaOpts() + "]"; + tmp += "{"; + os << from_ascii(tmp); + count += tmp.length(); + pushLanguageName(language()->polyglossia(), true); + } else if (language()->encoding()->package() != Encoding::CJK) { + os << '{'; + count += 1; + } + } else if (language()->babel() != base.language()->babel() && language() != prev.language()) { - if (isRightToLeft() != prev.isRightToLeft()) { + if (language()->lang() == "farsi") { + os << "\\textFR{"; + count += 8; + } else if (!isRightToLeft() && + base.language()->lang() == "farsi") { + os << "\\textLR{"; + count += 8; + } else if (language()->lang() == "arabic_arabi") { + os << "\\textAR{"; + count += 8; + } else if (!isRightToLeft() && + base.language()->lang() == "arabic_arabi") { + os << "\\textLR{"; + count += 8; + // currently the remaining RTL languages are arabic_arabtex and hebrew + } else if (isRightToLeft() != prev.isRightToLeft()) { if (isRightToLeft()) { os << "\\R{"; count += 3; @@ -752,23 +263,34 @@ int Font::latexWriteStartChanges(odocstream & os, Font const & base, os << "\\L{"; count += 3; } - } else { + } else if (!language()->babel().empty()) { string const tmp = subst(lyxrc.language_command_local, "$$lang", language()->babel()); os << from_ascii(tmp); count += tmp.length(); + if (!lyxrc.language_command_end.empty()) + pushLanguageName(language()->babel(), true); + } else if (language()->encoding()->package() != Encoding::CJK) { + os << '{'; + count += 1; } } - if (number() == ON && prev.number() != ON && - language()->lang() == "hebrew") { - os << "{\\beginL "; - count += 9; + if (language()->encoding()->package() == Encoding::CJK) { + pair const c = switchEncoding(os, bparams, + runparams, *(language()->encoding())); + if (c.first) { + open_encoding_ = true; + count += c.second; + runparams.encoding = language()->encoding(); + } } - Font f = *this; - f.reduce(base); + FontInfo f = bits_; + f.reduce(base.bits_); + FontInfo p = bits_; + p.reduce(prev.bits_); if (f.family() != INHERIT_FAMILY) { os << '\\' @@ -791,25 +313,56 @@ int Font::latexWriteStartChanges(odocstream & os, Font const & base, count += strlen(LaTeXShapeNames[f.shape()]) + 2; env = true; //We have opened a new environment } - if (f.color() != Color::inherit && f.color() != Color::ignore) { + if (f.color() != Color_inherit && f.color() != Color_ignore) { + if (f.color() == Color_none && p.color() != Color_none) { + // Color none: Close previous color, if any + os << '}'; + ++count; + } else if (f.color() != Color_none) { + os << "\\textcolor{" + << from_ascii(lcolor.getLaTeXName(f.color())) + << "}{"; + count += lcolor.getLaTeXName(f.color()).length() + 13; + } + env = true; //We have opened a new environment + } + // FIXME: uncomment this when we support background. + /* + if (f.background() != Color_inherit && f.background() != Color_ignore) { os << "\\textcolor{" - << from_ascii(lcolor.getLaTeXName(f.color())) + << from_ascii(lcolor.getLaTeXName(f.background())) << "}{"; - count += lcolor.getLaTeXName(f.color()).length() + 13; + count += lcolor.getLaTeXName(f.background()).length() + 13; env = true; //We have opened a new environment } - if (f.emph() == ON) { + */ + // If the current language is Hebrew, Arabic, or Farsi + // the numbers are written Left-to-Right. ArabTeX package + // and bidi (polyglossia with XeTeX) reorder the number automatically + // but the packages used for Hebrew and Farsi (Arabi) do not. + if (!runparams.useBidiPackage() + && !runparams.pass_thru + && bits_.number() == FONT_ON + && prev.fontInfo().number() != FONT_ON + && (language()->lang() == "hebrew" + || language()->lang() == "farsi" + || language()->lang() == "arabic_arabi")) { + if (runparams.use_polyglossia) { + // LuaTeX/luabidi + os << "\\LR{"; + count += 5; + } else { + os << "{\\beginL "; + count += 9; + } + } + if (f.emph() == FONT_ON) { os << "\\emph{"; count += 6; env = true; //We have opened a new environment } - if (f.underbar() == ON) { - os << "\\underbar{"; - count += 10; - env = true; //We have opened a new environment - } // \noun{} is a LyX special macro - if (f.noun() == ON) { + if (f.noun() == FONT_ON) { os << "\\noun{"; count += 6; env = true; //We have opened a new environment @@ -822,8 +375,42 @@ int Font::latexWriteStartChanges(odocstream & os, Font const & base, } os << '\\' << LaTeXSizeNames[f.size()] - << ' '; - count += strlen(LaTeXSizeNames[f.size()]) + 2; + << "{}"; + count += strlen(LaTeXSizeNames[f.size()]) + 3; + } + // The ulem commands need to be on the deepest nesting level + // because ulem puts every nested group or macro in a box, + // which prevents linebreaks (#8424, #8733) + if (f.underbar() == FONT_ON) { + os << "\\uline{"; + count += 10; + ++runparams.inulemcmd; + } + if (f.uuline() == FONT_ON) { + os << "\\uuline{"; + count += 11; + ++runparams.inulemcmd; + } + if (f.strikeout() == FONT_ON) { + os << "\\sout{"; + count += 9; + ++runparams.inulemcmd; + } + if (f.xout() == FONT_ON) { + os << "\\xout{"; + count += 9; + ++runparams.inulemcmd; + } + if (f.uwave() == FONT_ON) { + if (runparams.inulemcmd) { + // needed with nested uwave in xout + // see https://tex.stackexchange.com/a/263042 + os << "\\ULdepth=1000pt"; + count += 15; + } + os << "\\uwave{"; + count += 10; + ++runparams.inulemcmd; } return count; } @@ -832,8 +419,12 @@ int Font::latexWriteStartChanges(odocstream & os, Font const & base, /// Writes ending block of LaTeX needed to close use of this font // Returns number of chars written // This one corresponds to latexWriteStartChanges(). (Asger) -int Font::latexWriteEndChanges(odocstream & os, Font const & base, - Font const & next) const +int Font::latexWriteEndChanges(otexstream & os, BufferParams const & bparams, + OutputParams const & runparams, + Font const & base, + Font const & next, + bool & needPar, + bool const & closeLanguage) const { int count = 0; bool env = false; @@ -841,8 +432,8 @@ int Font::latexWriteEndChanges(odocstream & os, Font const & base, // reduce the current font to changes against the base // font (of the layout). We use a temporary for this to // avoid changing this font instance, as that would break - Font f = *this; - f.reduce(base); + FontInfo f = bits_; + f.reduce(base.bits_); if (f.family() != INHERIT_FAMILY) { os << '}'; @@ -859,22 +450,17 @@ int Font::latexWriteEndChanges(odocstream & os, Font const & base, ++count; env = true; // Size change need not bother about closing env. } - if (f.color() != Color::inherit && f.color() != Color::ignore) { - os << '}'; - ++count; - env = true; // Size change need not bother about closing env. - } - if (f.emph() == ON) { + if (f.color() != Color_inherit && f.color() != Color_ignore && f.color() != Color_none) { os << '}'; ++count; env = true; // Size change need not bother about closing env. } - if (f.underbar() == ON) { + if (f.emph() == FONT_ON) { os << '}'; ++count; env = true; // Size change need not bother about closing env. } - if (f.noun() == ON) { + if (f.noun() == FONT_ON) { os << '}'; ++count; env = true; // Size change need not bother about closing env. @@ -882,53 +468,312 @@ int Font::latexWriteEndChanges(odocstream & os, Font const & base, if (f.size() != INHERIT_SIZE) { // We only have to close if only size changed if (!env) { + if (needPar && !closeLanguage) { + os << "\\par"; + count += 4; + needPar = false; + } os << '}'; ++count; } } + if (f.underbar() == FONT_ON) { + os << '}'; + ++count; + --runparams.inulemcmd; + } + if (f.strikeout() == FONT_ON) { + os << '}'; + ++count; + --runparams.inulemcmd; + } + if (f.xout() == FONT_ON) { + os << '}'; + ++count; + --runparams.inulemcmd; + } + if (f.uuline() == FONT_ON) { + os << '}'; + ++count; + --runparams.inulemcmd; + } + if (f.uwave() == FONT_ON) { + os << '}'; + ++count; + --runparams.inulemcmd; + } + + // If the current language is Hebrew, Arabic, or Farsi + // the numbers are written Left-to-Right. ArabTeX package + // and bidi (polyglossia with XeTeX) reorder the number automatically + // but the packages used for Hebrew and Farsi (Arabi) do not. + if (!runparams.useBidiPackage() + && !runparams.pass_thru + && bits_.number() == FONT_ON + && next.fontInfo().number() != FONT_ON + && (language()->lang() == "hebrew" + || language()->lang() == "farsi" + || language()->lang() == "arabic_arabi")) { + if (runparams.use_polyglossia) { + // LuaTeX/luabidi + os << "}"; + count += 1; + } else { + os << "\\endL}"; + count += 6; + } + } - if (number() == ON && next.number() != ON && - language()->lang() == "hebrew") { - os << "\\endL}"; - count += 6; + if (open_encoding_) { + // We need to close the encoding even if it does not change + // to do correct environment nesting + Encoding const * const ascii = encodings.fromLyXName("ascii"); + pair const c = switchEncoding(os.os(), bparams, + runparams, *ascii); + LATTEST(c.first); + count += c.second; + runparams.encoding = ascii; + open_encoding_ = false; } - if (language() != base.language() && language() != next.language()) { + if (closeLanguage + && language() != base.language() && language() != next.language() + && (language()->encoding()->package() != Encoding::CJK)) { os << '}'; ++count; + bool const using_begin_end = + runparams.use_polyglossia || + !lyxrc.language_command_end.empty(); + if (using_begin_end) + popLanguageName(); } return count; } -Color_color Font::realColor() const +string Font::toString(bool const toggle) const +{ + string const lang = (language() == reset_language) + ? "reset" : language()->lang(); + + ostringstream os; + os << "family " << bits_.family() << '\n' + << "series " << bits_.series() << '\n' + << "shape " << bits_.shape() << '\n' + << "size " << bits_.size() << '\n' + << "emph " << bits_.emph() << '\n' + << "underbar " << bits_.underbar() << '\n' + << "strikeout " << bits_.strikeout() << '\n' + << "xout " << bits_.xout() << '\n' + << "uuline " << bits_.uuline() << '\n' + << "uwave " << bits_.uwave() << '\n' + << "noun " << bits_.noun() << '\n' + << "number " << bits_.number() << '\n' + << "nospellcheck " << bits_.nospellcheck() << '\n' + << "color " << bits_.color() << '\n' + << "language " << lang << '\n' + << "toggleall " << convert(toggle); + return os.str(); +} + + +bool Font::fromString(string const & data, bool & toggle) +{ + istringstream is(data); + Lexer lex; + lex.setStream(is); + + int nset = 0; + while (lex.isOK()) { + string token; + if (lex.next()) + token = lex.getString(); + + if (token.empty() || !lex.next()) + break; + + if (token == "family") { + int const next = lex.getInteger(); + bits_.setFamily(FontFamily(next)); + + } else if (token == "series") { + int const next = lex.getInteger(); + bits_.setSeries(FontSeries(next)); + + } else if (token == "shape") { + int const next = lex.getInteger(); + bits_.setShape(FontShape(next)); + + } else if (token == "size") { + int const next = lex.getInteger(); + bits_.setSize(FontSize(next)); + // FIXME: shall style be handled there? Probably not. + } else if (token == "emph" || token == "underbar" + || token == "noun" || token == "number" + || token == "uuline" || token == "uwave" + || token == "strikeout" || token == "xout" + || token == "nospellcheck") { + + int const next = lex.getInteger(); + FontState const misc = FontState(next); + + if (token == "emph") + bits_.setEmph(misc); + else if (token == "underbar") + bits_.setUnderbar(misc); + else if (token == "strikeout") + bits_.setStrikeout(misc); + else if (token == "xout") + bits_.setXout(misc); + else if (token == "uuline") + bits_.setUuline(misc); + else if (token == "uwave") + bits_.setUwave(misc); + else if (token == "noun") + bits_.setNoun(misc); + else if (token == "number") + bits_.setNumber(misc); + else if (token == "nospellcheck") + bits_.setNoSpellcheck(misc); + + } else if (token == "color") { + int const next = lex.getInteger(); + bits_.setColor(ColorCode(next)); + + /** + } else if (token == "background") { + int const next = lex.getInteger(); + bits_.setBackground(ColorCode(next)); + */ + + } else if (token == "language") { + string const next = lex.getString(); + setLanguage(languages.getLanguage(next)); + + } else if (token == "toggleall") { + toggle = lex.getBool(); + + } else { + // Unrecognised token + break; + } + + ++nset; + } + return (nset > 0); +} + + +void Font::validate(LaTeXFeatures & features) const { - if (color() == Color::none) - return Color::foreground; - return color(); + BufferParams const & bparams = features.bufferParams(); + Language const * doc_language = bparams.language; + + if (bits_.noun() == FONT_ON) { + LYXERR(Debug::LATEX, "font.noun: " << bits_.noun()); + features.require("noun"); + LYXERR(Debug::LATEX, "Noun enabled. Font: " << to_utf8(stateText())); + } + if (bits_.underbar() == FONT_ON) { + LYXERR(Debug::LATEX, "font.underline: " << bits_.underbar()); + features.require("ulem"); + LYXERR(Debug::LATEX, "Underline enabled. Font: " << to_utf8(stateText())); + } + if (bits_.strikeout() == FONT_ON) { + LYXERR(Debug::LATEX, "font.strikeout: " << bits_.strikeout()); + features.require("ulem"); + LYXERR(Debug::LATEX, "Strike out enabled. Font: " << to_utf8(stateText())); + } + if (bits_.xout() == FONT_ON) { + LYXERR(Debug::LATEX, "font.xout: " << bits_.xout()); + features.require("ulem"); + LYXERR(Debug::LATEX, "Cross out enabled. Font: " << to_utf8(stateText())); + } + if (bits_.uuline() == FONT_ON) { + LYXERR(Debug::LATEX, "font.uuline: " << bits_.uuline()); + features.require("ulem"); + LYXERR(Debug::LATEX, "Double underline enabled. Font: " << to_utf8(stateText())); + } + if (bits_.uwave() == FONT_ON) { + LYXERR(Debug::LATEX, "font.uwave: " << bits_.uwave()); + features.require("ulem"); + LYXERR(Debug::LATEX, "Wavy underline enabled. Font: " << to_utf8(stateText())); + } + switch (bits_.color()) { + case Color_none: + case Color_inherit: + case Color_ignore: + // probably we should put here all interface colors used for + // font displaying! For now I just add this ones I know of (Jug) + case Color_latex: + case Color_notelabel: + break; + case Color_brown: + case Color_darkgray: + case Color_gray: + case Color_lightgray: + case Color_lime: + case Color_olive: + case Color_orange: + case Color_pink: + case Color_purple: + case Color_teal: + case Color_violet: + features.require("xcolor"); + break; + default: + features.require("color"); + LYXERR(Debug::LATEX, "Color enabled. Font: " << to_utf8(stateText())); + } + + // FIXME: Do something for background and soul package? + + if (((features.usePolyglossia() && lang_->polyglossia() != doc_language->polyglossia()) + || (features.useBabel() && lang_->babel() != doc_language->babel()) + || (doc_language->encoding()->package() == Encoding::CJK && lang_ != doc_language)) + && lang_ != ignore_language + && lang_ != latex_language) + { + features.useLanguage(lang_); + LYXERR(Debug::LATEX, "Found language " << lang_->lang()); + } } -ostream & operator<<(ostream & os, Font::FONT_MISC_STATE fms) +ostream & operator<<(ostream & os, FontState fms) { return os << int(fms); } -std::ostream & operator<<(std::ostream & os, Font const & font) +ostream & operator<<(ostream & os, FontInfo const & f) { return os << "font:" - << " family " << font.bits.family - << " series " << font.bits.series - << " shape " << font.bits.shape - << " size " << font.bits.size - << " color " << font.bits.color - << " emph " << font.bits.emph - << " underbar " << font.bits.underbar - << " noun " << font.bits.noun - << " number " << font.bits.number - << " lang: " << (font.lang ? font.lang->lang() : 0); + << " family " << f.family() + << " series " << f.series() + << " shape " << f.shape() + << " size " << f.size() + << " style " << f.style() + << " color " << f.color() + // FIXME: uncomment this when we support background. + //<< " background " << f.background() + << " emph " << f.emph() + << " underbar " << f.underbar() + << " strikeout " << f.strikeout() + << " xout " << f.xout() + << " uuline " << f.uuline() + << " uwave " << f.uwave() + << " noun " << f.noun() + << " number " << f.number() + << " nospellcheck " << f.nospellcheck(); +} + + +ostream & operator<<(ostream & os, Font const & font) +{ + return os << font.bits_ + << " lang: " << (font.lang_ ? font.lang_->lang() : nullptr); }