X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Foutput_latex.cpp;h=ac906835ed7537055843e34e089c00f0bc65b98f;hb=0362c6aae73c293d1c20277c12d362acfe0b2ef6;hp=bf04a243c35e73af5ccdd1e063dbdf791a444859;hpb=5ab6d5bf653166934d1f5b6ed52eb67d489a6743;p=lyx.git diff --git a/src/output_latex.cpp b/src/output_latex.cpp index bf04a243c3..ac906835ed 100644 --- a/src/output_latex.cpp +++ b/src/output_latex.cpp @@ -14,7 +14,6 @@ #include "Buffer.h" #include "BufferParams.h" -#include "debug.h" #include "Encoding.h" #include "InsetList.h" #include "Language.h" @@ -24,38 +23,46 @@ #include "Paragraph.h" #include "paragraph_funcs.h" #include "ParagraphParameters.h" +#include "TextClass.h" #include "TexRow.h" #include "VSpace.h" #include "insets/InsetBibitem.h" #include "insets/InsetOptArg.h" +#include "support/debug.h" #include "support/lstrings.h" #include +using namespace std; +using namespace lyx::support; + namespace lyx { -using support::subst; +namespace { -using std::endl; -using std::string; -using std::pair; -using std::make_pair; +enum OpenEncoding { + none, + inputenc, + CJK + }; + +static int open_encoding_ = none; +static bool cjk_inherited_ = false; -namespace { ParagraphList::const_iterator TeXEnvironment(Buffer const & buf, - ParagraphList const & paragraphs, + Text const & text, ParagraphList::const_iterator pit, odocstream & os, TexRow & texrow, OutputParams const & runparams); ParagraphList::const_iterator TeXOnePar(Buffer const & buf, - ParagraphList const & paragraphs, + Text const & text, ParagraphList::const_iterator pit, odocstream & os, TexRow & texrow, OutputParams const & runparams, @@ -64,25 +71,27 @@ TeXOnePar(Buffer const & buf, ParagraphList::const_iterator TeXDeeper(Buffer const & buf, - ParagraphList const & paragraphs, + Text const & text, ParagraphList::const_iterator pit, odocstream & os, TexRow & texrow, OutputParams const & runparams) { - LYXERR(Debug::LATEX) << "TeXDeeper... " << &*pit << endl; + LYXERR(Debug::LATEX, "TeXDeeper... " << &*pit); ParagraphList::const_iterator par = pit; + ParagraphList const & paragraphs = text.paragraphs(); + while (par != paragraphs.end() && par->params().depth() == pit->params().depth()) { if (par->layout()->isEnvironment()) { - par = TeXEnvironment(buf, paragraphs, par, + par = TeXEnvironment(buf, text, par, os, texrow, runparams); } else { - par = TeXOnePar(buf, paragraphs, par, + par = TeXOnePar(buf, text, par, os, texrow, runparams); } } - LYXERR(Debug::LATEX) << "TeXDeeper...done " << endl; + LYXERR(Debug::LATEX, "TeXDeeper...done "); return par; } @@ -90,16 +99,20 @@ TeXDeeper(Buffer const & buf, ParagraphList::const_iterator TeXEnvironment(Buffer const & buf, - ParagraphList const & paragraphs, + Text const & text, ParagraphList::const_iterator pit, odocstream & os, TexRow & texrow, OutputParams const & runparams) { - LYXERR(Debug::LATEX) << "TeXEnvironment... " << &*pit << endl; + LYXERR(Debug::LATEX, "TeXEnvironment... " << &*pit); BufferParams const & bparams = buf.params(); - LayoutPtr const & style = pit->layout(); + LayoutPtr const & style = pit->forceEmptyLayout() ? + bparams.textClass().emptyLayout() : + pit->layout(); + + ParagraphList const & paragraphs = text.paragraphs(); Language const * const par_language = pit->getParLanguage(bparams); Language const * const doc_language = bparams.language; @@ -116,7 +129,8 @@ TeXEnvironment(Buffer const & buf, lyxrc.language_command_end, "$$lang", prev_par_language->babel())) - << '\n'; + // the '%' is necessary to prevent unwanted whitespace + << "%\n"; texrow.newline(); } @@ -127,7 +141,8 @@ TeXEnvironment(Buffer const & buf, lyxrc.language_command_begin, "$$lang", par_language->babel())) - << '\n'; + // the '%' is necessary to prevent unwanted whitespace + << "%\n"; texrow.newline(); } } @@ -162,9 +177,21 @@ TeXEnvironment(Buffer const & buf, os << from_ascii(style->latexparam()) << '\n'; texrow.newline(); } + + // in multilingual environments, the CJK tags have to be nested properly + bool cjk_nested = false; + if (par_language->encoding()->package() == Encoding::CJK && + open_encoding_ != CJK && pit->isMultiLingual(bparams)) { + os << "\\begin{CJK}{" << from_ascii(par_language->encoding()->latexName()) + << "}{}%\n"; + open_encoding_ = CJK; + cjk_nested = true; + texrow.newline(); + } + ParagraphList::const_iterator par = pit; do { - par = TeXOnePar(buf, paragraphs, par, os, texrow, runparams); + par = TeXOnePar(buf, text, par, os, texrow, runparams); if (par == paragraphs.end()) { // Make sure that the last paragraph is @@ -192,7 +219,7 @@ TeXEnvironment(Buffer const & buf, os << '\n'; texrow.newline(); } - par = TeXDeeper(buf, paragraphs, par, os, texrow, + par = TeXDeeper(buf, text, par, os, texrow, runparams); } } while (par != paragraphs.end() @@ -200,6 +227,14 @@ TeXEnvironment(Buffer const & buf, && par->params().depth() == pit->params().depth() && par->params().leftIndent() == pit->params().leftIndent()); + if (open_encoding_ == CJK && cjk_nested) { + // We need to close the encoding even if it does not change + // to do correct environment nesting + os << "\\end{CJK}\n"; + texrow.newline(); + open_encoding_ = none; + } + if (style->isEnvironment()) { os << "\\end{" << from_ascii(style->latexname()) << "}\n"; texrow.newline(); @@ -210,9 +245,9 @@ TeXEnvironment(Buffer const & buf, texrow.newline(); } - if (par != paragraphs.end()) { - LYXERR(Debug::LATEX) << "TeXEnvironment...done " << &*par << endl; - } + if (par != paragraphs.end()) + LYXERR(Debug::LATEX, "TeXEnvironment...done " << &*par); + return par; } @@ -242,32 +277,57 @@ namespace { ParagraphList::const_iterator TeXOnePar(Buffer const & buf, - ParagraphList const & paragraphs, + Text const & text, ParagraphList::const_iterator pit, odocstream & os, TexRow & texrow, OutputParams const & runparams_in, string const & everypar) { - LYXERR(Debug::LATEX) << "TeXOnePar... " << &*pit << " '" - << everypar << "'" << endl; + LYXERR(Debug::LATEX, "TeXOnePar... " << &*pit << " '" + << everypar << "'"); BufferParams const & bparams = buf.params(); - LayoutPtr style; + ParagraphList const & paragraphs = text.paragraphs(); + + if (runparams_in.verbatim) { + int const dist = distance(paragraphs.begin(), pit); + Font const outerfont = outerFont(dist, paragraphs); + + // No newline if only one paragraph in this lyxtext + if (dist > 0) { + os << '\n'; + texrow.newline(); + } + + /*bool need_par = */ pit->latex(buf, bparams, outerfont, + os, texrow, runparams_in); + + return ++pit; + } + // FIXME This comment doesn't make sense. What's the + // length got to do with forceEmptyLayout()? I.e., what + // was forceDefaultParagraphs()? // In an inset with unlimited length (all in one row), // force layout to default - if (!pit->forceDefaultParagraphs()) - style = pit->layout(); - else - style = bparams.getTextClass().defaultLayout(); + LayoutPtr const style = pit->forceEmptyLayout() ? + bparams.textClass().emptyLayout() : + pit->layout(); OutputParams runparams = runparams_in; runparams.moving_arg |= style->needprotect; + bool const maintext = text.isMainText(buf); + // we are at the beginning of an inset and CJK is already open. + if (pit == paragraphs.begin() && !maintext && open_encoding_ == CJK) { + cjk_inherited_ = true; + open_encoding_ = none; + } + // This paragraph's language Language const * const par_language = pit->getParLanguage(bparams); // The document's language Language const * const doc_language = bparams.language; - // The language that was in effect when the environemnt this paragraph is + // The language that was in effect when the environment this paragraph is // inside of was opened Language const * const outer_language = (runparams.local_font != 0) ? @@ -294,7 +354,8 @@ TeXOnePar(Buffer const & buf, os << from_ascii(subst(lyxrc.language_command_end, "$$lang", prev_language->babel())) - << '\n'; + // the '%' is necessary to prevent unwanted whitespace + << "%\n"; texrow.newline(); } @@ -343,12 +404,16 @@ TeXOnePar(Buffer const & buf, else os << "\\L{"; } - os << from_ascii(subst( - lyxrc.language_command_begin, - "$$lang", - par_language->babel())) - << '\n'; - texrow.newline(); + // With CJK, the CJK tag has to be closed first (see below) + if (runparams.encoding->package() != Encoding::CJK) { + os << from_ascii(subst( + lyxrc.language_command_begin, + "$$lang", + par_language->babel())) + // the '%' is necessary to prevent unwanted whitespace + << "%\n"; + texrow.newline(); + } } } @@ -356,38 +421,66 @@ TeXOnePar(Buffer const & buf, // encoding, since this only affects the position of the outputted // \inputencoding command; the encoding switch will occur when necessary if (bparams.inputenc == "auto" && - runparams.encoding->package() == Encoding::inputenc) { + runparams.encoding->package() != Encoding::none) { // Look ahead for future encoding changes. // We try to output them at the beginning of the paragraph, // since the \inputencoding command is not allowed e.g. in // sections. for (pos_type i = 0; i < pit->size(); ++i) { char_type const c = pit->getChar(i); - if (c < 0x80) + Encoding const * const encoding = + pit->getFontSettings(bparams, i).language()->encoding(); + if (encoding->package() != Encoding::CJK && + runparams.encoding->package() == Encoding::inputenc && + c < 0x80) continue; if (pit->isInset(i)) break; // All characters before c are in the ASCII range, and // c is non-ASCII (but no inset), so change the // encoding to that required by the language of c. - Encoding const * const encoding = - pit->getFontSettings(bparams, i).language()->encoding(); - pair enc_switch = switchEncoding(os, bparams, false, - *(runparams.encoding), *encoding); - if (encoding->package() == Encoding::inputenc && enc_switch.first) { - runparams.encoding = encoding; - if (enc_switch.second > 0) { - os << '\n'; + // With CJK, only add switch if we have CJK content at the beginning + // of the paragraph + if (encoding->package() != Encoding::CJK || i == 0) { + OutputParams tmp_rp = runparams; + runparams.moving_arg = false; + pair enc_switch = switchEncoding(os, bparams, runparams, + *encoding); + runparams = tmp_rp; + // 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_) { + os << "\\begin{CJK}{" << from_ascii(par_language->encoding()->latexName()) + << "}{}%\n"; + open_encoding_ = CJK; texrow.newline(); } + if (encoding->package() != Encoding::none && enc_switch.first) { + if (enc_switch.second > 0) { + // the '%' is necessary to prevent unwanted whitespace + os << "%\n"; + texrow.newline(); + } + // With CJK, the CJK tag had to be closed first (see above) + if (runparams.encoding->package() == Encoding::CJK) { + os << from_ascii(subst( + lyxrc.language_command_begin, + "$$lang", + par_language->babel())) + // the '%' is necessary to prevent unwanted whitespace + << "%\n"; + texrow.newline(); + } + runparams.encoding = encoding; + } + break; } - break; } } - // In an inset with unlimited length (all in one row), - // don't allow any special options in the paragraph - if (!pit->forceDefaultParagraphs()) { + bool const useSetSpace = bparams.textClass().provides("SetSpace"); + if (pit->allowParagraphCustomization()) { if (pit->params().startOfAppendix()) { os << "\\appendix\n"; texrow.newline(); @@ -397,7 +490,7 @@ TeXOnePar(Buffer const & buf, && (pit == paragraphs.begin() || !boost::prior(pit)->hasSameLayout(*pit))) { - os << from_ascii(pit->params().spacing().writeEnvirBegin()) + os << from_ascii(pit->params().spacing().writeEnvirBegin(useSetSpace)) << '\n'; texrow.newline(); } @@ -436,7 +529,7 @@ TeXOnePar(Buffer const & buf, } Font const outerfont = - outerFont(std::distance(paragraphs.begin(), pit), + outerFont(distance(paragraphs.begin(), pit), paragraphs); // FIXME UNICODE @@ -461,7 +554,7 @@ TeXOnePar(Buffer const & buf, bool is_command = style->isCommand(); - if (style->resfont.size() != font.size() + if (style->resfont.size() != font.fontInfo().size() && boost::next(pit) != paragraphs.end() && !is_command) { if (!need_par) @@ -498,7 +591,7 @@ TeXOnePar(Buffer const & buf, pending_newline = true; } - if (!pit->forceDefaultParagraphs()) { + if (pit->allowParagraphCustomization()) { if (!pit->params().spacing().isDefault() && (boost::next(pit) == paragraphs.end() || !boost::next(pit)->hasSameLayout(*pit))) @@ -507,7 +600,7 @@ TeXOnePar(Buffer const & buf, os << '\n'; texrow.newline(); } - os << from_ascii(pit->params().spacing().writeEnvirEnd()); + os << from_ascii(pit->params().spacing().writeEnvirEnd(useSetSpace)); pending_newline = true; } } @@ -539,30 +632,73 @@ TeXOnePar(Buffer const & buf, os << '\n'; texrow.newline(); } - if (lyxrc.language_command_end.empty()) { - if (!prev_language->babel().empty()) { + // when the paragraph uses CJK, the language has to be closed earlier + if (font.language()->encoding()->package() != Encoding::CJK) { + if (lyxrc.language_command_end.empty()) { + if (!prev_language->babel().empty()) { + os << from_ascii(subst( + lyxrc.language_command_begin, + "$$lang", + prev_language->babel())); + pending_newline = true; + } + } else if (!par_language->babel().empty()) { os << from_ascii(subst( - lyxrc.language_command_begin, + lyxrc.language_command_end, "$$lang", - prev_language->babel())); + par_language->babel())); pending_newline = true; } - } else if (!par_language->babel().empty()) { - os << from_ascii(subst( - lyxrc.language_command_end, - "$$lang", - par_language->babel())); - pending_newline = true; } } if (closing_rtl_ltr_environment) os << "}"; - if (pending_newline && !runparams.verbatim) { + if (pending_newline) { os << '\n'; texrow.newline(); } + // 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 (boost::next(pit) != paragraphs.end() && open_encoding_ == CJK && + (boost::next(pit)->getParLanguage(bparams)->encoding()->package() != Encoding::CJK || + boost::next(pit)->layout()->isEnvironment() && boost::next(pit)->isMultiLingual(bparams)) + // in environments, CJK has to be closed later (nesting!) + && !style->isEnvironment()) { + os << "\\end{CJK}\n"; + 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 (boost::next(pit) == paragraphs.end() && !style->isEnvironment()) { + switch (open_encoding_) { + case CJK: { + // end of main text + if (maintext) { + os << '\n'; + texrow.newline(); + os << "\\end{CJK}\n"; + texrow.newline(); + // end of an inset + } else + os << "\\end{CJK}"; + open_encoding_ = none; + break; + } + case inputenc: { + os << "\\egroup"; + open_encoding_ = none; + break; + } + case none: + default: + // do nothing + break; + } + } + // If this is the last paragraph, and a local_font was set upon entering // the inset, the encoding should be set back to that local_font's // encoding. We don't use switchEncoding(), because no explicit encoding @@ -589,9 +725,8 @@ TeXOnePar(Buffer const & buf, texrow.newline(); } - if (boost::next(pit) != paragraphs.end()) { - LYXERR(Debug::LATEX) << "TeXOnePar...done " << &*boost::next(pit) << endl; - } + if (boost::next(pit) != paragraphs.end()) + LYXERR(Debug::LATEX, "TeXOnePar...done " << &*boost::next(pit)); return ++pit; } @@ -601,7 +736,7 @@ TeXOnePar(Buffer const & buf, // LaTeX all paragraphs void latexParagraphs(Buffer const & buf, - ParagraphList const & paragraphs, + Text const & text, odocstream & os, TexRow & texrow, OutputParams const & runparams, @@ -609,7 +744,9 @@ void latexParagraphs(Buffer const & buf, { bool was_title = false; bool already_title = false; - TextClass const & tclass = buf.params().getTextClass(); + BufferParams const & bparams = buf.params(); + TextClass const & tclass = bparams.textClass(); + ParagraphList const & paragraphs = text.paragraphs(); ParagraphList::const_iterator par = paragraphs.begin(); ParagraphList::const_iterator endpar = paragraphs.end(); @@ -624,16 +761,41 @@ void latexParagraphs(Buffer const & buf, const_cast(runparams).par_end = 0; } + bool const maintext = text.isMainText(buf); + + // Open a CJK environment at the beginning of the main buffer + // if the document's language is a CJK language + if (maintext && bparams.encoding().package() == Encoding::CJK) { + os << "\\begin{CJK}{" << from_ascii(bparams.encoding().latexName()) + << "}{}%\n"; + texrow.newline(); + open_encoding_ = CJK; + } + // if "auto begin" is switched off, explicitely switch the + // language on at start + if (maintext && !lyxrc.language_auto_begin && + !bparams.language->babel().empty()) { + // FIXME UNICODE + os << from_utf8(subst(lyxrc.language_command_begin, + "$$lang", + bparams.language->babel())) + << '\n'; + texrow.newline(); + } + + ParagraphList::const_iterator lastpar; // if only_body while (par != endpar) { - ParagraphList::const_iterator lastpar = par; + lastpar = par; // well we have to check if we are in an inset with unlimited // length (all in one row) if that is true then we don't allow // any special options in the paragraph and also we don't allow // any environment other than the default layout of the // text class to be valid! - if (!par->forceDefaultParagraphs()) { - LayoutPtr const & layout = par->layout(); + if (par->allowParagraphCustomization()) { + LayoutPtr const & layout = par->forceEmptyLayout() ? + tclass.emptyLayout() : + par->layout(); if (layout->intitle) { if (already_title) { @@ -664,21 +826,21 @@ void latexParagraphs(Buffer const & buf, } if (layout->is_environment) { - par = TeXOnePar(buf, paragraphs, par, os, texrow, + par = TeXOnePar(buf, text, par, os, texrow, runparams, everypar); } else if (layout->isEnvironment() || !par->params().leftIndent().zero()) { - par = TeXEnvironment(buf, paragraphs, par, os, + par = TeXEnvironment(buf, text, par, os, texrow, runparams); } else { - par = TeXOnePar(buf, paragraphs, par, os, texrow, + par = TeXOnePar(buf, text, par, os, texrow, runparams, everypar); } } else { - par = TeXOnePar(buf, paragraphs, par, os, texrow, + par = TeXOnePar(buf, text, par, os, texrow, runparams, everypar); } - if (std::distance(lastpar, par) >= std::distance(lastpar, endpar)) + if (distance(lastpar, par) >= distance(lastpar, endpar)) break; } // It might be that we only have a title in this document @@ -693,13 +855,36 @@ void latexParagraphs(Buffer const & buf, } texrow.newline(); } + // if "auto end" is switched off, explicitely close the language at the end + // but only if the last par is in a babel language + if (maintext && !lyxrc.language_auto_end && !bparams.language->babel().empty() && + lastpar->getParLanguage(bparams)->encoding()->package() != Encoding::CJK) { + os << from_utf8(subst(lyxrc.language_command_end, + "$$lang", + bparams.language->babel())) + << '\n'; + texrow.newline(); + } + // If the last paragraph is an environment, we'll have to close + // CJK at the very end to do proper nesting. + if (maintext && open_encoding_ == CJK) { + os << "\\end{CJK}\n"; + texrow.newline(); + open_encoding_ = none; + } + // reset inherited encoding + if (cjk_inherited_) { + open_encoding_ = CJK; + cjk_inherited_ = false; + } } pair switchEncoding(odocstream & os, BufferParams const & bparams, - bool moving_arg, Encoding const & oldEnc, - Encoding const & newEnc) + OutputParams const & runparams, Encoding const & newEnc) { + Encoding const oldEnc = *runparams.encoding; + bool moving_arg = runparams.moving_arg; if ((bparams.inputenc != "auto" && bparams.inputenc != "default") || moving_arg) return make_pair(false, 0); @@ -717,39 +902,61 @@ pair switchEncoding(odocstream & os, BufferParams const & bparams, || newEnc.package() == Encoding::none) return make_pair(false, 0); - LYXERR(Debug::LATEX) << "Changing LaTeX encoding from " - << oldEnc.name() << " to " - << newEnc.name() << endl; + LYXERR(Debug::LATEX, "Changing LaTeX encoding from " + << oldEnc.name() << " to " << newEnc.name()); os << setEncoding(newEnc.iconvName()); if (bparams.inputenc == "default") return make_pair(true, 0); - docstring const inputenc(from_ascii(newEnc.latexName())); + docstring const inputenc_arg(from_ascii(newEnc.latexName())); switch (newEnc.package()) { case Encoding::none: // shouldn't ever reach here, see above return make_pair(true, 0); case Encoding::inputenc: { - int count = inputenc.length(); - if (oldEnc.package() == Encoding::CJK) { + int count = inputenc_arg.length(); + if (oldEnc.package() == Encoding::CJK && + open_encoding_ == CJK) { os << "\\end{CJK}"; + open_encoding_ = none; count += 9; } - os << "\\inputencoding{" << inputenc << '}'; + else if (oldEnc.package() == Encoding::inputenc && + open_encoding_ == inputenc) { + os << "\\egroup"; + open_encoding_ = none; + count += 7; + } + if (runparams.local_font != 0 && oldEnc.package() == Encoding::CJK) { + // within insets, \inputenc switches need to be + // embraced within \bgroup ... \egroup; else CJK fails. + os << "\\bgroup"; + count += 7; + open_encoding_ = inputenc; + } + os << "\\inputencoding{" << inputenc_arg << '}'; return make_pair(true, count + 16); - } + } case Encoding::CJK: { - int count = inputenc.length(); - if (oldEnc.package() == Encoding::CJK) { + int count = inputenc_arg.length(); + if (oldEnc.package() == Encoding::CJK && + open_encoding_ == CJK) { os << "\\end{CJK}"; count += 9; } - os << "\\begin{CJK}{" << inputenc << "}{}"; + if (oldEnc.package() == Encoding::inputenc && + open_encoding_ == inputenc) { + os << "\\egroup"; + count += 7; + } + os << "\\begin{CJK}{" << inputenc_arg << "}{}"; + open_encoding_ = CJK; return make_pair(true, count + 15); } } // Dead code to avoid a warning: return make_pair(true, 0); + } } // namespace lyx