Make sure to properly nest \begin{lang} and \end{lang} tags even
when no language package is selected. In this case, LyX assumes
that babel is being used, so the language names might be wrong
if the user arranged for using polyglossia in the preamble.
Nevertheless, we assure that the produced output is syntactically
correct, so that by adding proper preamble code a correct output
is still possible.
This commit is contained in:
Enrico Forestieri 2017-06-06 00:01:14 +02:00
parent 59c22bd7b6
commit d738151a9b
5 changed files with 95 additions and 73 deletions

View File

@ -286,7 +286,7 @@ int Font::latexWriteStartChanges(odocstream & os, BufferParams const & bparams,
tmp += "{"; tmp += "{";
os << from_ascii(tmp); os << from_ascii(tmp);
count += tmp.length(); count += tmp.length();
pushPolyglossiaLang(language()->polyglossia(), true); pushLanguageName(language()->polyglossia(), true);
} else if (language()->encoding()->package() != Encoding::CJK) { } else if (language()->encoding()->package() != Encoding::CJK) {
os << '{'; os << '{';
count += 1; count += 1;
@ -322,6 +322,8 @@ int Font::latexWriteStartChanges(odocstream & os, BufferParams const & bparams,
"$$lang", language()->babel()); "$$lang", language()->babel());
os << from_ascii(tmp); os << from_ascii(tmp);
count += tmp.length(); count += tmp.length();
if (!lyxrc.language_command_end.empty())
pushLanguageName(language()->babel(), true);
} else if (language()->encoding()->package() != Encoding::CJK) { } else if (language()->encoding()->package() != Encoding::CJK) {
os << '{'; os << '{';
count += 1; count += 1;
@ -551,8 +553,11 @@ int Font::latexWriteEndChanges(otexstream & os, BufferParams const & bparams,
&& language()->encoding()->package() != Encoding::CJK) { && language()->encoding()->package() != Encoding::CJK) {
os << '}'; os << '}';
++count; ++count;
if (runparams.use_polyglossia) bool const using_begin_end =
popPolyglossiaLang(); runparams.use_polyglossia ||
!lyxrc.language_command_end.empty();
if (using_begin_end)
popLanguageName();
} }
return count; return count;

View File

@ -2483,6 +2483,8 @@ void Paragraph::latex(BufferParams const & bparams,
// close babel's font environment before opening CJK. // close babel's font environment before opening CJK.
string const lang_end_command = runparams.use_polyglossia ? string const lang_end_command = runparams.use_polyglossia ?
"\\end{$$lang}" : lyxrc.language_command_end; "\\end{$$lang}" : lyxrc.language_command_end;
bool const using_begin_end = runparams.use_polyglossia ||
!lang_end_command.empty();
if (!running_lang.empty() && if (!running_lang.empty() &&
font.language()->encoding()->package() == Encoding::CJK) { font.language()->encoding()->package() == Encoding::CJK) {
string end_tag = subst(lang_end_command, string end_tag = subst(lang_end_command,
@ -2490,8 +2492,8 @@ void Paragraph::latex(BufferParams const & bparams,
running_lang); running_lang);
os << from_ascii(end_tag); os << from_ascii(end_tag);
column += end_tag.length(); column += end_tag.length();
if (runparams.use_polyglossia) if (using_begin_end)
popPolyglossiaLang(); popLanguageName();
} }
// Switch file encoding if necessary (and allowed) // Switch file encoding if necessary (and allowed)

View File

@ -84,11 +84,12 @@ OutputState * getOutputState()
} }
string const & openPolyglossiaLang(OutputState const * state) string const & openLanguageName(OutputState const * state)
{ {
// Return a reference to the last active language opened with // Return a reference to the last active language opened with
// polyglossia. If none or when using babel, return a reference // polyglossia or when using begin/end commands. If none or when
// to an empty string. // using babel with only a begin command, return a reference to
// an empty string.
static string const empty; static string const empty;
@ -101,8 +102,9 @@ string const & openPolyglossiaLang(OutputState const * state)
bool atSameLastLangSwitchDepth(OutputState const * state) bool atSameLastLangSwitchDepth(OutputState const * state)
{ {
// Return true if the actual nest level is the same at which the // Return true if the actual nest level is the same at which the
// language was switched when using polyglossia. Instead, return // language was switched when using polyglossia or begin/end
// always true when using babel. // commands. Instead, return always true when using babel with
// only a begin command.
return state->lang_switch_depth_.size() == 0 return state->lang_switch_depth_.size() == 0
? true ? true
@ -112,7 +114,7 @@ bool atSameLastLangSwitchDepth(OutputState const * state)
bool isLocalSwitch(OutputState const * state) bool isLocalSwitch(OutputState const * state)
{ {
// Return true if the language was opened by the \text<lang> command. // Return true if the language was opened by a local command switch.
return state->lang_switch_depth_.size() return state->lang_switch_depth_.size()
&& state->lang_switch_depth_.top() < 0; && state->lang_switch_depth_.top() < 0;
@ -208,10 +210,12 @@ static TeXEnvironmentData prepareEnvironment(Buffer const & buf,
"\\begin{$$lang}" : lyxrc.language_command_begin; "\\begin{$$lang}" : lyxrc.language_command_begin;
string const lang_end_command = use_polyglossia ? string const lang_end_command = use_polyglossia ?
"\\end{$$lang}" : lyxrc.language_command_end; "\\end{$$lang}" : lyxrc.language_command_end;
bool const using_begin_end = use_polyglossia ||
!lang_end_command.empty();
// For polyglossia, switch language outside of environment, if possible. // For polyglossia, switch language outside of environment, if possible.
if (par_lang != prev_par_lang) { if (par_lang != prev_par_lang) {
if ((!use_polyglossia || langOpenedAtThisLevel(state)) && if ((!using_begin_end || langOpenedAtThisLevel(state)) &&
!lang_end_command.empty() && !lang_end_command.empty() &&
prev_par_lang != doc_lang && prev_par_lang != doc_lang &&
!prev_par_lang.empty()) { !prev_par_lang.empty()) {
@ -221,21 +225,21 @@ static TeXEnvironmentData prepareEnvironment(Buffer const & buf,
prev_par_lang)) prev_par_lang))
// the '%' is necessary to prevent unwanted whitespace // the '%' is necessary to prevent unwanted whitespace
<< "%\n"; << "%\n";
if (use_polyglossia) if (using_begin_end)
popPolyglossiaLang(); popLanguageName();
} }
// If no language was explicitly opened and we are using // If no language was explicitly opened and we are using
// polyglossia, then the current polyglossia language is // polyglossia or begin/end commands, then the current
// the document language. // language is the document language.
string const & pol_lang = use_polyglossia string const & cur_lang = using_begin_end
&& state->lang_switch_depth_.size() && state->lang_switch_depth_.size()
? openPolyglossiaLang(state) ? openLanguageName(state)
: doc_lang; : doc_lang;
if ((lang_end_command.empty() || if ((lang_end_command.empty() ||
par_lang != doc_lang || par_lang != doc_lang ||
par_lang != pol_lang) && par_lang != cur_lang) &&
!par_lang.empty()) { !par_lang.empty()) {
string bc = use_polyglossia ? string bc = use_polyglossia ?
getPolyglossiaBegin(lang_begin_command, par_lang, getPolyglossiaBegin(lang_begin_command, par_lang,
@ -244,8 +248,8 @@ static TeXEnvironmentData prepareEnvironment(Buffer const & buf,
os << bc; os << bc;
// the '%' is necessary to prevent unwanted whitespace // the '%' is necessary to prevent unwanted whitespace
os << "%\n"; os << "%\n";
if (use_polyglossia) if (using_begin_end)
pushPolyglossiaLang(par_lang); pushLanguageName(par_lang);
} }
} }
@ -309,17 +313,20 @@ static void finishEnvironment(otexstream & os, OutputParams const & runparams,
if (data.style->isEnvironment()) { if (data.style->isEnvironment()) {
os << breakln; os << breakln;
// Close any polyglossia language opened at this nest level bool const using_begin_end =
if (runparams.use_polyglossia) { runparams.use_polyglossia ||
!lyxrc.language_command_end.empty();
// Close any language opened at this nest level
if (using_begin_end) {
while (langOpenedAtThisLevel(state)) { while (langOpenedAtThisLevel(state)) {
if (isLocalSwitch(state)) { if (isLocalSwitch(state)) {
os << "}"; os << "}";
} else { } else {
os << "\\end{" os << "\\end{"
<< openPolyglossiaLang(state) << openLanguageName(state)
<< "}%\n"; << "}%\n";
} }
popPolyglossiaLang(); popLanguageName();
} }
} }
state->nest_level_ -= 1; state->nest_level_ -= 1;
@ -501,7 +508,7 @@ void getArgInsets(otexstream & os, OutputParams const & runparams, Layout::LaTeX
} // namespace anon } // namespace anon
void pushPolyglossiaLang(string const & lang_name, bool localswitch) void pushLanguageName(string const & lang_name, bool localswitch)
{ {
OutputState * state = getOutputState(); OutputState * state = getOutputState();
@ -511,7 +518,7 @@ void pushPolyglossiaLang(string const & lang_name, bool localswitch)
} }
void popPolyglossiaLang() void popLanguageName()
{ {
OutputState * state = getOutputState(); OutputState * state = getOutputState();
@ -779,10 +786,12 @@ void TeXOnePar(Buffer const & buf,
"\\end{$$lang}" : lyxrc.language_command_end; "\\end{$$lang}" : lyxrc.language_command_end;
// the '%' is necessary to prevent unwanted whitespace // the '%' is necessary to prevent unwanted whitespace
string lang_command_termination = "%\n"; string lang_command_termination = "%\n";
bool const using_begin_end = use_polyglossia ||
!lang_end_command.empty();
// In some insets (such as Arguments), we cannot use \selectlanguage // In some insets (such as Arguments), we cannot use \selectlanguage
bool const localswitch = text.inset().forceLocalFontSwitch() bool const localswitch = text.inset().forceLocalFontSwitch()
|| (use_polyglossia && text.inset().forcePlainLayout()); || (using_begin_end && text.inset().forcePlainLayout());
if (localswitch) { if (localswitch) {
lang_begin_command = use_polyglossia ? lang_begin_command = use_polyglossia ?
"\\text$$lang$$opts{" : lyxrc.language_command_local; "\\text$$lang$$opts{" : lyxrc.language_command_local;
@ -797,18 +806,18 @@ void TeXOnePar(Buffer const & buf,
&& priorpar->getDepth() <= par.getDepth()) && priorpar->getDepth() <= par.getDepth())
|| priorpar->getDepth() < par.getDepth()))) || priorpar->getDepth() < par.getDepth())))
{ {
if ((!use_polyglossia || langOpenedAtThisLevel(state)) && if ((!using_begin_end || langOpenedAtThisLevel(state)) &&
!lang_end_command.empty() && !lang_end_command.empty() &&
prev_lang != outer_lang && prev_lang != outer_lang &&
!prev_lang.empty() && !prev_lang.empty() &&
(!use_polyglossia || !style.isEnvironment())) (!using_begin_end || !style.isEnvironment()))
{ {
os << from_ascii(subst(lang_end_command, os << from_ascii(subst(lang_end_command,
"$$lang", "$$lang",
prev_lang)) prev_lang))
<< lang_command_termination; << lang_command_termination;
if (use_polyglossia) if (using_begin_end)
popPolyglossiaLang(); popLanguageName();
} }
// We need to open a new language if we couldn't close the previous // We need to open a new language if we couldn't close the previous
@ -817,7 +826,7 @@ void TeXOnePar(Buffer const & buf,
// outer_language (which is currently in effect once the previous one // outer_language (which is currently in effect once the previous one
// is closed). // is closed).
if ((lang_end_command.empty() || par_lang != outer_lang if ((lang_end_command.empty() || par_lang != outer_lang
|| (!use_polyglossia || (!using_begin_end
|| (style.isEnvironment() && par_lang != prev_lang))) || (style.isEnvironment() && par_lang != prev_lang)))
&& !par_lang.empty()) { && !par_lang.empty()) {
// If we're inside an inset, and that inset is within an \L or \R // If we're inside an inset, and that inset is within an \L or \R
@ -825,7 +834,7 @@ void TeXOnePar(Buffer const & buf,
// language paragraph should appear within an \L or \R (in addition // language paragraph should appear within an \L or \R (in addition
// to, outside of, the normal language switch commands). // to, outside of, the normal language switch commands).
// This behavior is not correct for ArabTeX, though. // This behavior is not correct for ArabTeX, though.
if (!use_polyglossia if (!using_begin_end
// not for ArabTeX // not for ArabTeX
&& par_language->lang() != "arabic_arabtex" && par_language->lang() != "arabic_arabtex"
&& outer_language->lang() != "arabic_arabtex" && outer_language->lang() != "arabic_arabtex"
@ -858,15 +867,15 @@ void TeXOnePar(Buffer const & buf,
} }
// With CJK, the CJK tag has to be closed first (see below) // With CJK, the CJK tag has to be closed first (see below)
if (runparams.encoding->package() != Encoding::CJK if (runparams.encoding->package() != Encoding::CJK
&& par_lang != openPolyglossiaLang(state) && par_lang != openLanguageName(state)
&& !par_lang.empty()) { && !par_lang.empty()) {
string bc = use_polyglossia ? string bc = use_polyglossia ?
getPolyglossiaBegin(lang_begin_command, par_lang, par_language->polyglossiaOpts()) getPolyglossiaBegin(lang_begin_command, par_lang, par_language->polyglossiaOpts())
: subst(lang_begin_command, "$$lang", par_lang); : subst(lang_begin_command, "$$lang", par_lang);
os << bc; os << bc;
os << lang_command_termination; os << lang_command_termination;
if (use_polyglossia) if (using_begin_end)
pushPolyglossiaLang(par_lang, localswitch); pushLanguageName(par_lang, localswitch);
} }
} }
} }
@ -918,15 +927,15 @@ void TeXOnePar(Buffer const & buf,
} }
// With CJK, the CJK tag had to be closed first (see above) // With CJK, the CJK tag had to be closed first (see above)
if (runparams.encoding->package() == Encoding::CJK if (runparams.encoding->package() == Encoding::CJK
&& par_lang != openPolyglossiaLang(state) && par_lang != openLanguageName(state)
&& !par_lang.empty()) { && !par_lang.empty()) {
os << from_ascii(subst( os << from_ascii(subst(
lang_begin_command, lang_begin_command,
"$$lang", "$$lang",
par_lang)) par_lang))
<< lang_command_termination; << lang_command_termination;
if (use_polyglossia) if (using_begin_end)
pushPolyglossiaLang(par_lang, localswitch); pushLanguageName(par_lang, localswitch);
} }
runparams.encoding = encoding; runparams.encoding = encoding;
} }
@ -988,7 +997,7 @@ void TeXOnePar(Buffer const & buf,
&& nextpar->getDepth() == par.getDepth()) && nextpar->getDepth() == par.getDepth())
|| (atSameLastLangSwitchDepth(state) && nextpar || (atSameLastLangSwitchDepth(state) && nextpar
&& nextpar->getDepth() < par.getDepth())) && nextpar->getDepth() < par.getDepth()))
close_lang_switch = use_polyglossia; close_lang_switch = using_begin_end;
if (nextpar && par.params().depth() < nextpar->params().depth()) if (nextpar && par.params().depth() < nextpar->params().depth())
pending_newline = true; pending_newline = true;
break; break;
@ -998,9 +1007,9 @@ void TeXOnePar(Buffer const & buf,
if (nextpar if (nextpar
&& ((nextpar->layout() != par.layout() && ((nextpar->layout() != par.layout()
|| nextpar->params().depth() != par.params().depth()) || nextpar->params().depth() != par.params().depth())
|| (!use_polyglossia || par_lang != nextpar_lang))) || (!using_begin_end || par_lang != nextpar_lang)))
{ {
close_lang_switch = use_polyglossia; close_lang_switch = using_begin_end;
break; break;
} }
} }
@ -1031,7 +1040,7 @@ void TeXOnePar(Buffer const & buf,
// Closing the language is needed for the last paragraph; it is also // Closing the language is needed for the last paragraph; it is also
// needed if we're within an \L or \R that we may have opened above (not // needed if we're within an \L or \R that we may have opened above (not
// necessarily in this paragraph) and are about to close. // necessarily in this paragraph) and are about to close.
bool closing_rtl_ltr_environment = !use_polyglossia bool closing_rtl_ltr_environment = !using_begin_end
// not for ArabTeX // not for ArabTeX
&& (par_language->lang() != "arabic_arabtex" && (par_language->lang() != "arabic_arabtex"
&& outer_language->lang() != "arabic_arabtex") && outer_language->lang() != "arabic_arabtex")
@ -1044,7 +1053,7 @@ void TeXOnePar(Buffer const & buf,
if (closing_rtl_ltr_environment if (closing_rtl_ltr_environment
|| ((runparams.isLastPar || close_lang_switch) || ((runparams.isLastPar || close_lang_switch)
&& (par_lang != outer_lang || (use_polyglossia && (par_lang != outer_lang || (using_begin_end
&& style.isEnvironment() && style.isEnvironment()
&& par_lang != nextpar_lang)))) { && par_lang != nextpar_lang)))) {
// Since \selectlanguage write the language to the aux file, // Since \selectlanguage write the language to the aux file,
@ -1067,7 +1076,7 @@ void TeXOnePar(Buffer const & buf,
? getPolyglossiaEnvName(current_language) ? getPolyglossiaEnvName(current_language)
: current_language->babel(); : current_language->babel();
if (!current_lang.empty() if (!current_lang.empty()
&& current_lang != openPolyglossiaLang(state)) { && current_lang != openLanguageName(state)) {
string bc = use_polyglossia ? string bc = use_polyglossia ?
getPolyglossiaBegin(lang_begin_command, current_lang, getPolyglossiaBegin(lang_begin_command, current_lang,
current_language->polyglossiaOpts()) current_language->polyglossiaOpts())
@ -1075,28 +1084,28 @@ void TeXOnePar(Buffer const & buf,
os << bc; os << bc;
pending_newline = !localswitch; pending_newline = !localswitch;
unskip_newline = !localswitch; unskip_newline = !localswitch;
if (use_polyglossia) if (using_begin_end)
pushPolyglossiaLang(current_lang, localswitch); pushLanguageName(current_lang, localswitch);
} }
} else if ((!use_polyglossia || } else if ((!using_begin_end ||
langOpenedAtThisLevel(state)) && langOpenedAtThisLevel(state)) &&
!par_lang.empty()) { !par_lang.empty()) {
// If we are in an environment, we have to // If we are in an environment, we have to
// close the "outer" language afterwards // close the "outer" language afterwards
string const & pol_lang = openPolyglossiaLang(state); string const & cur_lang = openLanguageName(state);
if (!style.isEnvironment() if (!style.isEnvironment()
|| (close_lang_switch || (close_lang_switch
&& atSameLastLangSwitchDepth(state) && atSameLastLangSwitchDepth(state)
&& par_lang != outer_lang && par_lang != outer_lang
&& (par_lang != pol_lang && (par_lang != cur_lang
|| (pol_lang != outer_lang || (cur_lang != outer_lang
&& nextpar && nextpar
&& style != nextpar->layout()))) && style != nextpar->layout())))
|| (atSameLastLangSwitchDepth(state) || (atSameLastLangSwitchDepth(state)
&& state->lang_switch_depth_.size() && state->lang_switch_depth_.size()
&& pol_lang != par_lang)) && cur_lang != par_lang))
{ {
if (use_polyglossia && !localswitch) if (using_begin_end && !localswitch)
os << breakln; os << breakln;
os << from_ascii(subst( os << from_ascii(subst(
lang_end_command, lang_end_command,
@ -1104,8 +1113,8 @@ void TeXOnePar(Buffer const & buf,
par_lang)); par_lang));
pending_newline = !localswitch; pending_newline = !localswitch;
unskip_newline = !localswitch; unskip_newline = !localswitch;
if (use_polyglossia) if (using_begin_end)
popPolyglossiaLang(); popLanguageName();
} }
} }
} }
@ -1273,6 +1282,10 @@ void latexParagraphs(Buffer const & buf,
: bparams.language->babel(); : bparams.language->babel();
string const lang_begin_command = runparams.use_polyglossia ? string const lang_begin_command = runparams.use_polyglossia ?
"\\begin{$$lang}$$opts" : lyxrc.language_command_begin; "\\begin{$$lang}$$opts" : lyxrc.language_command_begin;
string const lang_end_command = runparams.use_polyglossia ?
"\\end{$$lang}" : lyxrc.language_command_end;
bool const using_begin_end = runparams.use_polyglossia ||
!lang_end_command.empty();
if (maintext && !lyxrc.language_auto_begin && if (maintext && !lyxrc.language_auto_begin &&
!mainlang.empty()) { !mainlang.empty()) {
@ -1283,8 +1296,8 @@ void latexParagraphs(Buffer const & buf,
: subst(lang_begin_command, "$$lang", mainlang); : subst(lang_begin_command, "$$lang", mainlang);
os << bc; os << bc;
os << '\n'; os << '\n';
if (runparams.use_polyglossia) if (using_begin_end)
pushPolyglossiaLang(mainlang); pushLanguageName(mainlang);
} }
ParagraphList const & paragraphs = text.paragraphs(); ParagraphList const & paragraphs = text.paragraphs();
@ -1381,16 +1394,14 @@ void latexParagraphs(Buffer const & buf,
// if "auto end" is switched off, explicitly close the language at the end // if "auto end" is switched off, explicitly close the language at the end
// but only if the last par is in a babel or polyglossia language // but only if the last par is in a babel or polyglossia language
string const lang_end_command = runparams.use_polyglossia ?
"\\end{$$lang}" : lyxrc.language_command_end;
if (maintext && !lyxrc.language_auto_end && !mainlang.empty() && if (maintext && !lyxrc.language_auto_end && !mainlang.empty() &&
paragraphs.at(lastpit).getParLanguage(bparams)->encoding()->package() != Encoding::CJK) { paragraphs.at(lastpit).getParLanguage(bparams)->encoding()->package() != Encoding::CJK) {
os << from_utf8(subst(lang_end_command, os << from_utf8(subst(lang_end_command,
"$$lang", "$$lang",
mainlang)) mainlang))
<< '\n'; << '\n';
if (runparams.use_polyglossia) if (using_begin_end)
popPolyglossiaLang(); popLanguageName();
} }
// If the last paragraph is an environment, we'll have to close // If the last paragraph is an environment, we'll have to close
@ -1399,15 +1410,15 @@ void latexParagraphs(Buffer const & buf,
os << "\\end{CJK}\n"; os << "\\end{CJK}\n";
state->open_encoding_ = none; state->open_encoding_ = none;
} }
// Likewise for polyglossia // Likewise for polyglossia or when using begin/end commands
string const & pol_lang = openPolyglossiaLang(state); string const & cur_lang = openLanguageName(state);
if (maintext && !is_child && !pol_lang.empty()) { if (maintext && !is_child && !cur_lang.empty()) {
os << from_utf8(subst(lang_end_command, os << from_utf8(subst(lang_end_command,
"$$lang", "$$lang",
pol_lang)) cur_lang))
<< '\n'; << '\n';
if (runparams.use_polyglossia) if (using_begin_end)
popPolyglossiaLang(); popLanguageName();
} }
// reset inherited encoding // reset inherited encoding

View File

@ -29,14 +29,15 @@ class Paragraph;
class OutputParams; class OutputParams;
class Text; class Text;
/** Register a language switch when using polyglossia. /** Register a language switch when using polyglossia or begin/end commands.
Set \p localswitch to true if the \text<lang> command is used. Set \p localswitch to true if a local command switch is used.
*/ */
void pushPolyglossiaLang(std::string const & lang, bool localswitch = false); void pushLanguageName(std::string const & lang, bool localswitch = false);
/** Unregister the last language switch when using polyglossia. /** Unregister the last language switch when using polyglossia
or begin/end commands.
*/ */
void popPolyglossiaLang(); void popLanguageName();
/** Export optional and required arguments of the paragraph \p par. /** Export optional and required arguments of the paragraph \p par.
Non-existing required arguments are output empty: {}. Non-existing required arguments are output empty: {}.

View File

@ -42,6 +42,9 @@ What's new
- Fix wrong spacing in output when the font size is changed inside - Fix wrong spacing in output when the font size is changed inside
a center environment or a greyed out note (bugs 9598 and 10650). a center environment or a greyed out note (bugs 9598 and 10650).
- Assure properly nested \begin{lang} and \end{lang} tags even when
no language package is selected (bug 10685).
* LYX2LYX * LYX2LYX