From: Jürgen Spitzmüller Date: Sat, 8 Dec 2007 11:21:00 +0000 (+0000) Subject: Sanitize CJK support (fix bug 3561, 4349 and 4337): X-Git-Tag: 1.6.10~7017 X-Git-Url: https://git.lyx.org/gitweb/?a=commitdiff_plain;h=dc75c0f793202468c5102d18b235080409165433;p=features.git Sanitize CJK support (fix bug 3561, 4349 and 4337): * src/output_latex.{cpp, h}: - Many changes in order to allow CJK in a mulitlingual context. * src/Paragraph.cpp: - Fix file encoding switch and language nesting with CJK. * src/Buffer.cpp: - Move the opening and closing CJK and lanaguage tags to ouput_latex.cpp. * src/Font.cpp: - switchEncoding call now takes runparams as argument. git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@22011 a592a061-630c-0410-9148-cb99ea01b6c8 --- diff --git a/src/Buffer.cpp b/src/Buffer.cpp index f888232ddf..bf0a5dced8 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -1129,26 +1129,6 @@ void Buffer::writeLaTeXSource(odocstream & os, LYXERR(Debug::INFO, "preamble finished, now the body."); - if (!lyxrc.language_auto_begin && - !params().language->babel().empty()) { - // FIXME UNICODE - os << from_utf8(subst(lyxrc.language_command_begin, - "$$lang", - params().language->babel())) - << '\n'; - d->texrow.newline(); - } - - Encoding const & encoding = params().encoding(); - if (encoding.package() == Encoding::CJK) { - // Open a CJK environment, since in contrast to the encodings - // handled by inputenc the document encoding is not set in - // the preamble if it is handled by CJK.sty. - os << "\\begin{CJK}{" << from_ascii(encoding.latexName()) - << "}{}\n"; - d->texrow.newline(); - } - // if we are doing a real file with body, even if this is the // child of some other buffer, let's cut the link here. // This happens for example if only a child document is printed. @@ -1171,23 +1151,6 @@ void Buffer::writeLaTeXSource(odocstream & os, os << endl; d->texrow.newline(); - if (encoding.package() == Encoding::CJK) { - // Close the open CJK environment. - // latexParagraphs will have opened one even if the last text - // was not CJK. - os << "\\end{CJK}\n"; - d->texrow.newline(); - } - - if (!lyxrc.language_auto_end && - !params().language->babel().empty()) { - os << from_utf8(subst(lyxrc.language_command_end, - "$$lang", - params().language->babel())) - << '\n'; - d->texrow.newline(); - } - if (output_preamble) { os << "\\end{document}\n"; d->texrow.newline(); diff --git a/src/Font.cpp b/src/Font.cpp index ce59fd231b..7821c58e85 100644 --- a/src/Font.cpp +++ b/src/Font.cpp @@ -464,7 +464,7 @@ int Font::latexWriteStartChanges(odocstream & os, BufferParams const & bparams, if (language()->encoding()->package() == Encoding::CJK) { pair const c = switchEncoding(os, bparams, - runparams.moving_arg, *(runparams.encoding), + runparams, *(runparams.encoding), *(language()->encoding())); if (c.first) { open_encoding_ = true; @@ -635,7 +635,7 @@ int Font::latexWriteEndChanges(odocstream & os, BufferParams const & bparams, // to do correct environment nesting Encoding const * const ascii = encodings.getFromLyXName("ascii"); pair const c = switchEncoding(os, bparams, - runparams.moving_arg, *(runparams.encoding), + runparams, *(runparams.encoding), *ascii); BOOST_ASSERT(c.first); count += c.second; diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp index b28082f83e..42c4bec1cc 100644 --- a/src/Paragraph.cpp +++ b/src/Paragraph.cpp @@ -67,6 +67,7 @@ namespace lyx { using support::contains; using support::lowercase; using support::prefixIs; +using support::subst; using support::suffixIs; using support::rsplit; using support::uppercase; @@ -1962,12 +1963,22 @@ bool Paragraph::latex(Buffer const & buf, open_font = false; } + // close babel's font environment before opening CJK. + if (!running_font.language()->babel().empty() && + font.language()->encoding()->package() == Encoding::CJK) { + string end_tag = subst(lyxrc.language_command_end, + "$$lang", + running_font.language()->babel()); + os << from_ascii(end_tag); + column += end_tag.length(); + } + // Switch file encoding if necessary (and allowed) if (!runparams.verbatim && - runparams.encoding->package() == Encoding::inputenc && - font.language()->encoding()->package() == Encoding::inputenc) { + runparams.encoding->package() == Encoding::none && + font.language()->encoding()->package() == Encoding::none) { std::pair const enc_switch = switchEncoding(os, bparams, - runparams.moving_arg, *(runparams.encoding), + runparams, *(runparams.encoding), *(font.language()->encoding())); if (enc_switch.first) { column += enc_switch.second; diff --git a/src/output_latex.cpp b/src/output_latex.cpp index 47ae3f8be7..e953d91d9d 100644 --- a/src/output_latex.cpp +++ b/src/output_latex.cpp @@ -47,6 +47,17 @@ using std::make_pair; namespace { + +enum OpenEncoding { + none, + inputenc, + CJK + }; + +static int open_encoding_ = none; +static bool cjk_inherited_ = false; + + ParagraphList::const_iterator TeXEnvironment(Buffer const & buf, ParagraphList const & paragraphs, @@ -165,6 +176,18 @@ 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); @@ -203,6 +226,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(); @@ -282,6 +313,32 @@ TeXOnePar(Buffer const & buf, OutputParams runparams = runparams_in; runparams.moving_arg |= style->needprotect; + // we are at the beginning of an inset and CJK is already open. + if (pit == paragraphs.begin() && runparams.local_font != 0 && + open_encoding_ == CJK) { + cjk_inherited_ = true; + open_encoding_ = none; + } + + if (pit == paragraphs.begin() && runparams.local_font == 0) { + // Open a CJK environment at the beginning of the main buffer + // if the document's language is a CJK language + if (bparams.encoding().package() == Encoding::CJK) { + os << "\\begin{CJK}{" << from_ascii(bparams.encoding().latexName()) + << "}{}%\n"; + texrow.newline(); + open_encoding_ = CJK; + } + if (!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(); + } + } + // This paragraph's language Language const * const par_language = pit->getParLanguage(bparams); // The document's language @@ -363,13 +420,16 @@ TeXOnePar(Buffer const & buf, else os << "\\L{"; } - os << from_ascii(subst( - lyxrc.language_command_begin, - "$$lang", - par_language->babel())) - // the '%' is necessary to prevent unwanted whitespace - << "%\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(); + } } } @@ -377,14 +437,14 @@ 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) + if (runparams.encoding->package() == Encoding::inputenc && c < 0x80) continue; if (pit->isInset(i)) break; @@ -393,17 +453,44 @@ TeXOnePar(Buffer const & buf, // 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, + + // 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, *(runparams.encoding), *encoding); - if (encoding->package() == Encoding::inputenc && enc_switch.first) { - runparams.encoding = encoding; - if (enc_switch.second > 0) { - // the '%' is necessary to prevent unwanted whitespace - os << "%\n"; + 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; } } @@ -561,20 +648,23 @@ 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) @@ -585,6 +675,56 @@ TeXOnePar(Buffer const & buf, 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 (runparams.local_font == 0) { + 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; + } + // auto_end tag only if the last par is in a babel language + if (runparams.local_font == 0 && !lyxrc.language_auto_end && + !bparams.language->babel().empty() && + font.language()->encoding()->package() != Encoding::CJK) { + os << from_utf8(subst(lyxrc.language_command_end, + "$$lang", + bparams.language->babel())) + << '\n'; + texrow.newline(); + } + } + // 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 @@ -714,13 +854,26 @@ void latexParagraphs(Buffer const & buf, } texrow.newline(); } + // If the last paragraph is an environment, we'll have to close + // CJK at the very end to do proper nesting. + if (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, + OutputParams const & runparams, Encoding const & oldEnc, Encoding const & newEnc) { + bool moving_arg = runparams.moving_arg; if ((bparams.inputenc != "auto" && bparams.inputenc != "default") || moving_arg) return make_pair(false, 0); @@ -744,32 +897,55 @@ pair switchEncoding(odocstream & os, BufferParams const & bparams, 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 diff --git a/src/output_latex.h b/src/output_latex.h index 008564ed79..db7f4ebb2d 100644 --- a/src/output_latex.h +++ b/src/output_latex.h @@ -48,7 +48,7 @@ void latexParagraphs(Buffer const & buf, /// \return (did the encoding change?, number of characters written to \p os) std::pair switchEncoding(odocstream & os, BufferParams const & bparams, - bool moving_arg, Encoding const & oldEnc, + OutputParams const &, Encoding const & oldEnc, Encoding const & newEnc); } // namespace lyx