X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Foutput_latex.cpp;h=783be726084195a10b6ec8c4f3d9f36b59865b6b;hb=7032b0374801d7e2621c221ce43f409855a901d6;hp=f461ba2faabb7df0580f0f6c1135f0117224801e;hpb=7d5b1fb8409a8c7ac3c75056ee80aec6cba13195;p=lyx.git diff --git a/src/output_latex.cpp b/src/output_latex.cpp index f461ba2faa..783be72608 100644 --- a/src/output_latex.cpp +++ b/src/output_latex.cpp @@ -35,6 +35,8 @@ #include "support/lstrings.h" #include "support/textutils.h" +#include + #include #include #include @@ -53,13 +55,28 @@ enum OpenEncoding { CJK }; -// FIXME THREAD -// There could easily be a conflict here, with the export process -// setting this one way, and a View>Source process (possbily for -// another Buffer) resetting it. -static int open_encoding_ = none; -static int cjk_inherited_ = 0; -Language const * prev_env_language_ = 0; + +struct OutputState +{ + OutputState() : open_encoding_(none), cjk_inherited_(0), + prev_env_language_(0) + { + } + int open_encoding_; + int cjk_inherited_; + Language const * prev_env_language_; +}; + + +OutputState * getOutputState() +{ + // FIXME An instance of OutputState should be kept around for each export + // instead of using local thread storage + static QThreadStorage outputstate; + if (!outputstate.hasLocalData()) + outputstate.setLocalData(new OutputState); + return outputstate.localData(); +} string const getPolyglossiaEnvName(Language const * lang) @@ -101,7 +118,8 @@ static TeXEnvironmentData prepareEnvironment(Buffer const & buf, ParagraphList::const_iterator const priorpit = pit == paragraphs.begin() ? pit : boost::prior(pit); - bool const use_prev_env_language = prev_env_language_ != 0 + OutputState * state = getOutputState(); + bool const use_prev_env_language = state->prev_env_language_ != 0 && priorpit->layout().isEnvironment() && (priorpit->getDepth() > pit->getDepth() || (priorpit->getDepth() == pit->getDepth() @@ -112,7 +130,7 @@ static TeXEnvironmentData prepareEnvironment(Buffer const & buf, Language const * const doc_language = bparams.language; Language const * const prev_par_language = (pit != paragraphs.begin()) - ? (use_prev_env_language ? prev_env_language_ + ? (use_prev_env_language ? state->prev_env_language_ : priorpit->getParLanguage(bparams)) : doc_language; @@ -191,11 +209,11 @@ static TeXEnvironmentData prepareEnvironment(Buffer const & buf, // in multilingual environments, the CJK tags have to be nested properly data.cjk_nested = false; if (data.par_language->encoding()->package() == Encoding::CJK && - open_encoding_ != CJK && pit->isMultiLingual(bparams)) { + state->open_encoding_ != CJK && pit->isMultiLingual(bparams)) { if (prev_par_language->encoding()->package() == Encoding::CJK) os << "\\begin{CJK}{" << from_ascii(data.par_language->encoding()->latexName()) << "}{" << from_ascii(bparams.fonts_cjk) << "}%\n"; - open_encoding_ = CJK; + state->open_encoding_ = CJK; data.cjk_nested = true; } return data; @@ -203,19 +221,20 @@ static TeXEnvironmentData prepareEnvironment(Buffer const & buf, static void finishEnvironment(otexstream & os, OutputParams const & runparams, - TeXEnvironmentData const & data) + TeXEnvironmentData const & data) { - if (open_encoding_ == CJK && data.cjk_nested) { + OutputState * state = getOutputState(); + if (state->open_encoding_ == CJK && data.cjk_nested) { // We need to close the encoding even if it does not change // to do correct environment nesting os << "\\end{CJK}\n"; - open_encoding_ = none; + state->open_encoding_ = none; } if (data.style->isEnvironment()) { os << breakln << "\\end{" << from_ascii(data.style->latexname()) << "}\n"; - prev_env_language_ = data.par_language; + state->prev_env_language_ = data.par_language; if (runparams.encoding != data.prev_encoding) { runparams.encoding = data.prev_encoding; if (!runparams.isFullUnicode()) @@ -225,13 +244,17 @@ static void finishEnvironment(otexstream & os, OutputParams const & runparams, if (data.leftindent_open) { os << breakln << "\\end{LyXParagraphLeftIndent}\n"; - prev_env_language_ = data.par_language; + state->prev_env_language_ = data.par_language; if (runparams.encoding != data.prev_encoding) { runparams.encoding = data.prev_encoding; if (!runparams.isFullUnicode()) os << setEncoding(data.prev_encoding->iconvName()); } } + + // Check whether we should output a blank line after the environment + if (!data.style->nextnoindent) + os << '\n'; } @@ -311,6 +334,18 @@ void getArgInsets(otexstream & os, OutputParams const & runparams, Layout::LaTeX if (argnr == 0) return; + // Default and preset args are always output, so if they require + // other arguments, consider this. + Layout::LaTeXArgMap::const_iterator lit = latexargs.begin(); + Layout::LaTeXArgMap::const_iterator const lend = latexargs.end(); + for (; lit != lend; ++lit) { + Layout::latexarg arg = (*lit).second; + if ((!arg.presetarg.empty() || !arg.defaultarg.empty()) && !arg.requires.empty()) { + vector req = getVectorFromString(arg.requires); + required.insert(required.end(), req.begin(), req.end()); + } + } + for (unsigned int i = 1; i <= argnr; ++i) { map::const_iterator lit = ilist.find(i); bool inserted = false; @@ -519,7 +554,7 @@ void TeXOnePar(Buffer const & buf, Paragraph const & par = paragraphs.at(pit); // FIXME This check should not really be needed. // Perhaps we should issue an error if it is. - Layout const style = text.inset().forcePlainLayout() ? + Layout const & style = text.inset().forcePlainLayout() ? bparams.documentClass().plainLayout() : par.layout(); if (style.inpreamble) @@ -539,10 +574,11 @@ void TeXOnePar(Buffer const & buf, bool const maintext = text.isMainText(); // we are at the beginning of an inset and CJK is already open; // we count inheritation levels to get the inset nesting right. + OutputState * state = getOutputState(); if (pit == 0 && !maintext - && (cjk_inherited_ > 0 || open_encoding_ == CJK)) { - cjk_inherited_ += 1; - open_encoding_ = none; + && (state->cjk_inherited_ > 0 || state->open_encoding_ == CJK)) { + state->cjk_inherited_ += 1; + state->open_encoding_ = none; } if (text.inset().isPassThru()) { @@ -605,7 +641,7 @@ void TeXOnePar(Buffer const & buf, // environment with nesting depth greater than (or equal to, but with // a different layout) the current one. If there is no previous // paragraph, the previous language is the outer language. - bool const use_prev_env_language = prev_env_language_ != 0 + bool const use_prev_env_language = state->prev_env_language_ != 0 && priorpar && priorpar->layout().isEnvironment() && (priorpar->getDepth() > par.getDepth() @@ -613,7 +649,7 @@ void TeXOnePar(Buffer const & buf, && priorpar->layout() != par.layout())); Language const * const prev_language = (pit != 0) - ? (use_prev_env_language ? prev_env_language_ + ? (use_prev_env_language ? state->prev_env_language_ : priorpar->getParLanguage(bparams)) : outer_language; @@ -636,7 +672,7 @@ void TeXOnePar(Buffer const & buf, // In some insets (such as Arguments), we cannot use \selectlanguage bool const localswitch = !use_polyglossia - && text.inset().getLayout().forcelocalfontswitch(); + && text.inset().forceLocalFontSwitch(); if (localswitch) { lang_begin_command = lyxrc.language_command_local; lang_end_command = "}"; @@ -754,10 +790,10 @@ void TeXOnePar(Buffer const & buf, // the following is necessary after a CJK environment in a multilingual // context (nesting issue). if (par_language->encoding()->package() == Encoding::CJK - && open_encoding_ != CJK && cjk_inherited_ == 0) { + && state->open_encoding_ != CJK && state->cjk_inherited_ == 0) { os << "\\begin{CJK}{" << from_ascii(par_language->encoding()->latexName()) << "}{" << from_ascii(bparams.fonts_cjk) << "}%\n"; - open_encoding_ = CJK; + state->open_encoding_ = CJK; } if (encoding->package() != Encoding::none && enc_switch.first) { if (enc_switch.second > 0) { @@ -785,7 +821,7 @@ void TeXOnePar(Buffer const & buf, bool const useSetSpace = bparams.documentClass().provides("SetSpace"); if (par.allowParagraphCustomization()) { if (par.params().startOfAppendix()) { - os << "\\appendix\n"; + os << "\n\\appendix\n"; } if (!par.params().spacing().isDefault() @@ -813,9 +849,9 @@ void TeXOnePar(Buffer const & buf, // calculates the space between the baselines according // to this font. (Matthias) // - // Is this really needed ? (Dekel) - // We do not need to use to change the font for the last paragraph - // or for a command. + // We must not change the font for the last paragraph + // of non-multipar insets, tabular cells or commands, + // since this produces unwanted whitespace. Font const font = par.empty() ? par.getLayoutFont(bparams, outerfont) @@ -824,7 +860,9 @@ void TeXOnePar(Buffer const & buf, bool const is_command = style.isCommand(); if (style.resfont.size() != font.fontInfo().size() - && nextpar + && (nextpar || maintext + || (text.inset().getLayout().isMultiPar() + && text.inset().lyxCode() != CELL_CODE)) && !is_command) { os << '{'; os << "\\" << from_ascii(font.latexSize()) << " \\par}"; @@ -844,9 +882,7 @@ void TeXOnePar(Buffer const & buf, switch (style.latextype) { case LATEX_ITEM_ENVIRONMENT: case LATEX_LIST_ENVIRONMENT: - if (nextpar - && (par.params().depth() < nextpar->params().depth()) - && !par.isEnvSeparator(par.size() - 1)) + if (nextpar && par.params().depth() < nextpar->params().depth()) pending_newline = true; break; case LATEX_ENVIRONMENT: { @@ -861,8 +897,7 @@ void TeXOnePar(Buffer const & buf, // fall through possible default: // we don't need it for the last paragraph!!! - // or if the last thing is an environment separator - if (nextpar && !par.isEnvSeparator(par.size() - 1)) + if (nextpar) pending_newline = true; } @@ -940,29 +975,33 @@ void TeXOnePar(Buffer const & buf, if (closing_rtl_ltr_environment) os << "}"; + bool const last_was_separator = + par.size() > 0 && par.isEnvSeparator(par.size() - 1); + if (pending_newline) { if (unskip_newline) // prevent unwanted whitespace os << '%'; - os << '\n'; + if (!os.afterParbreak() && !last_was_separator) + os << '\n'; } // if this is a CJK-paragraph and the next isn't, close CJK // also if the next paragraph is a multilingual environment (because of nesting) if (nextpar - && open_encoding_ == CJK + && state->open_encoding_ == CJK && (nextpar->getParLanguage(bparams)->encoding()->package() != Encoding::CJK || (nextpar->layout().isEnvironment() && nextpar->isMultiLingual(bparams))) // inbetween environments, CJK has to be closed later (nesting!) && (!style.isEnvironment() || !nextpar->layout().isEnvironment())) { os << "\\end{CJK}\n"; - open_encoding_ = none; + state->open_encoding_ = none; } // If this is the last paragraph, close the CJK environment // if necessary. If it's an environment, we'll have to \end that first. if (runparams.isLastPar && !style.isEnvironment()) { - switch (open_encoding_) { + switch (state->open_encoding_) { case CJK: { // do nothing at the end of child documents if (maintext && buf.masterBuffer() != &buf) @@ -973,12 +1012,12 @@ void TeXOnePar(Buffer const & buf, // end of an inset } else os << "\\end{CJK}"; - open_encoding_ = none; + state->open_encoding_ = none; break; } case inputenc: { os << "\\egroup"; - open_encoding_ = none; + state->open_encoding_ = none; break; } case none: @@ -1008,7 +1047,7 @@ void TeXOnePar(Buffer const & buf, // we don't need a newline for the last paragraph!!! // Note from JMarc: we will re-add a \n explicitly in // TeXEnvironment, because it is needed in this case - if (nextpar && !par.isEnvSeparator(par.size() - 1)) { + if (nextpar && !os.afterParbreak() && !last_was_separator) { // Make sure to start a new line os << breakln; Layout const & next_layout = nextpar->layout(); @@ -1025,9 +1064,13 @@ void TeXOnePar(Buffer const & buf, // unchanged, or (case 2) the following is a // non-environment paragraph whose depth is increased // but whose alignment is unchanged, or (case 3) the + // paragraph is not an environment and the next one is a + // non-itemize-like env at lower depth, or (case 4) the // paragraph is a command not followed by an environment // and the alignment of the current and next paragraph - // is unchanged. + // is unchanged, or (case 5) the current alignment is + // changed and a standard paragraph follows. + DocumentClass const & tclass = bparams.documentClass(); if ((style == next_layout && !style.parbreak_is_newline && style.latextype != LATEX_ITEM_ENVIRONMENT @@ -1038,10 +1081,15 @@ void TeXOnePar(Buffer const & buf, || (!next_layout.isEnvironment() && nextpar->getDepth() > par.getDepth() && nextpar->getAlign() == par.getAlign()) + || (!style.isEnvironment() + && next_layout.latextype == LATEX_ENVIRONMENT + && nextpar->getDepth() < par.getDepth()) || (style.isCommand() && !next_layout.isEnvironment() && style.align == par.getAlign() - && next_layout.align == nextpar->getAlign())) { + && next_layout.align == nextpar->getAlign()) + || (style.align != par.getAlign() + && tclass.isDefaultLayout(next_layout))) { os << '\n'; } } @@ -1072,11 +1120,12 @@ void latexParagraphs(Buffer const & buf, // Open a CJK environment at the beginning of the main buffer // if the document's language is a CJK language // (but not in child documents) + OutputState * state = getOutputState(); if (maintext && !is_child && bparams.encoding().package() == Encoding::CJK) { os << "\\begin{CJK}{" << from_ascii(bparams.encoding().latexName()) << "}{" << from_ascii(bparams.fonts_cjk) << "}%\n"; - open_encoding_ = CJK; + state->open_encoding_ = CJK; } // if "auto begin" is switched off, explicitly switch the // language on at start @@ -1198,16 +1247,16 @@ void latexParagraphs(Buffer const & buf, // If the last paragraph is an environment, we'll have to close // CJK at the very end to do proper nesting. - if (maintext && !is_child && open_encoding_ == CJK) { + if (maintext && !is_child && state->open_encoding_ == CJK) { os << "\\end{CJK}\n"; - open_encoding_ = none; + state->open_encoding_ = none; } // reset inherited encoding - if (cjk_inherited_ > 0) { - cjk_inherited_ -= 1; - if (cjk_inherited_ == 0) - open_encoding_ = CJK; + if (state->cjk_inherited_ > 0) { + state->cjk_inherited_ -= 1; + if (state->cjk_inherited_ == 0) + state->open_encoding_ = CJK; } } @@ -1246,6 +1295,7 @@ pair switchEncoding(odocstream & os, BufferParams const & bparams, return make_pair(true, 0); docstring const inputenc_arg(from_ascii(newEnc.latexName())); + OutputState * state = getOutputState(); switch (newEnc.package()) { case Encoding::none: case Encoding::japanese: @@ -1254,15 +1304,15 @@ pair switchEncoding(odocstream & os, BufferParams const & bparams, case Encoding::inputenc: { int count = inputenc_arg.length(); if (oldEnc.package() == Encoding::CJK && - open_encoding_ == CJK) { + state->open_encoding_ == CJK) { os << "\\end{CJK}"; - open_encoding_ = none; + state->open_encoding_ = none; count += 9; } else if (oldEnc.package() == Encoding::inputenc && - open_encoding_ == inputenc) { + state->open_encoding_ == inputenc) { os << "\\egroup"; - open_encoding_ = none; + state->open_encoding_ = none; count += 7; } if (runparams.local_font != 0 @@ -1272,7 +1322,7 @@ pair switchEncoding(odocstream & os, BufferParams const & bparams, // else CJK fails. os << "\\bgroup"; count += 7; - open_encoding_ = inputenc; + state->open_encoding_ = inputenc; } // with the japanese option, inputenc is omitted. if (runparams.use_japanese) @@ -1283,18 +1333,18 @@ pair switchEncoding(odocstream & os, BufferParams const & bparams, case Encoding::CJK: { int count = inputenc_arg.length(); if (oldEnc.package() == Encoding::CJK && - open_encoding_ == CJK) { + state->open_encoding_ == CJK) { os << "\\end{CJK}"; count += 9; } if (oldEnc.package() == Encoding::inputenc && - open_encoding_ == inputenc) { + state->open_encoding_ == inputenc) { os << "\\egroup"; count += 7; } os << "\\begin{CJK}{" << inputenc_arg << "}{" << from_ascii(bparams.fonts_cjk) << "}"; - open_encoding_ = CJK; + state->open_encoding_ = CJK; return make_pair(true, count + 15); } }