]> git.lyx.org Git - lyx.git/blob - src/output_latex.cpp
Remove 3rdparty/boost/Makefile.am
[lyx.git] / src / output_latex.cpp
1 /**
2  * \file output_latex.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "output_latex.h"
14
15 #include "BiblioInfo.h"
16 #include "Buffer.h"
17 #include "BufferParams.h"
18 #include "Encoding.h"
19 #include "Font.h"
20 #include "InsetList.h"
21 #include "Language.h"
22 #include "LyXRC.h"
23 #include "OutputParams.h"
24 #include "Paragraph.h"
25 #include "ParagraphParameters.h"
26 #include "texstream.h"
27 #include "TextClass.h"
28
29 #include "insets/InsetBibitem.h"
30 #include "insets/InsetArgument.h"
31
32 #include "frontends/alert.h"
33
34 #include "support/lassert.h"
35 #include "support/convert.h"
36 #include "support/debug.h"
37 #include "support/lstrings.h"
38 #include "support/textutils.h"
39 #include "support/gettext.h"
40
41 #include <QThreadStorage>
42
43 #include <list>
44 #include <stack>
45
46 using namespace std;
47 using namespace lyx::support;
48
49
50 namespace lyx {
51
52 namespace {
53
54 enum OpenEncoding {
55         none,
56         inputenc,
57         CJK
58 };
59
60
61 struct OutputState
62 {
63         OutputState() : prev_env_language_(nullptr), open_encoding_(none),
64                 cjk_inherited_(0), nest_level_(0)
65         {
66         }
67         Language const * prev_env_language_;
68         stack<int> lang_switch_depth_;          // Both are always empty when
69         stack<string> open_polyglossia_lang_;   // not using polyglossia
70         OpenEncoding open_encoding_;
71         int cjk_inherited_;
72         int nest_level_;
73 };
74
75
76 OutputState * getOutputState()
77 {
78         // FIXME An instance of OutputState should be kept around for each export
79         //       instead of using local thread storage
80         static QThreadStorage<OutputState *> outputstate;
81         if (!outputstate.hasLocalData())
82                 outputstate.setLocalData(new OutputState);
83         return outputstate.localData();
84 }
85
86
87 string const & openLanguageName(OutputState const * state)
88 {
89         // Return a reference to the last active language opened with
90         // polyglossia or when using begin/end commands. If none or when
91         // using babel with only a begin command, return a reference to
92         // an empty string.
93
94         static string const empty;
95
96         return state->open_polyglossia_lang_.empty()
97                 ? empty
98                 : state->open_polyglossia_lang_.top();
99 }
100
101
102 bool atSameLastLangSwitchDepth(OutputState const * state)
103 {
104         // Return true if the actual nest level is the same at which the
105         // language was switched when using polyglossia or begin/end
106         // commands. Instead, return always true when using babel with
107         // only a begin command.
108
109         return state->lang_switch_depth_.empty()
110                         ? true
111                         : abs(state->lang_switch_depth_.top()) == state->nest_level_;
112 }
113
114
115 bool isLocalSwitch(OutputState const * state)
116 {
117         // Return true if the language was opened by a local command switch.
118
119         return !state->lang_switch_depth_.empty()
120                 && state->lang_switch_depth_.top() < 0;
121 }
122
123
124 bool langOpenedAtThisLevel(OutputState const * state)
125 {
126         // Return true if the language was opened at the current nesting level.
127
128         return !state->lang_switch_depth_.empty()
129                 && abs(state->lang_switch_depth_.top()) == state->nest_level_;
130 }
131
132
133 string const getPolyglossiaEnvName(Language const * lang)
134 {
135         string result = lang->polyglossia();
136         if (result == "arabic")
137                 // exceptional spelling; see polyglossia docs.
138                 result = "Arabic";
139         return result;
140 }
141
142
143 string const getPolyglossiaBegin(string const & lang_begin_command,
144                                  string const & lang, string const & opts,
145                                  bool const localswitch = false)
146 {
147         string result;
148         if (!lang.empty()) {
149                 // we need to revert the upcasing done in getPolyglossiaEnvName()
150                 // in case we have a local polyglossia command (\textarabic).
151                 string language = localswitch ? ascii_lowercase(lang) : lang;
152                 result = subst(lang_begin_command, "$$lang", language);
153         }
154         string options = opts.empty() ?
155                     string() : "[" + opts + "]";
156         result = subst(result, "$$opts", options);
157
158         return result;
159 }
160
161
162 struct TeXEnvironmentData
163 {
164         Layout const * style;
165         Language const * par_language;
166         Encoding const * prev_encoding;
167         bool cjk_nested;
168         bool leftindent_open;
169 };
170
171
172 static TeXEnvironmentData prepareEnvironment(Buffer const & buf,
173                                         Text const & text,
174                                         ParagraphList::const_iterator pit,
175                                         otexstream & os,
176                                         OutputParams const & runparams)
177 {
178         TeXEnvironmentData data;
179
180         BufferParams const & bparams = buf.params();
181
182         // FIXME This test should not be necessary.
183         // We should perhaps issue an error if it is.
184         Layout const & style = text.inset().forcePlainLayout() ?
185                 bparams.documentClass().plainLayout() : pit->layout();
186
187         ParagraphList const & paragraphs = text.paragraphs();
188         ParagraphList::const_iterator const priorpit =
189                 pit == paragraphs.begin() ? pit : prev(pit, 1);
190
191         OutputState * state = getOutputState();
192         bool const use_prev_env_language = state->prev_env_language_ != nullptr
193                         && priorpit->layout().isEnvironment()
194                         && (priorpit->getDepth() > pit->getDepth()
195                             || (priorpit->getDepth() == pit->getDepth()
196                                 && priorpit->layout() != pit->layout()));
197
198         data.prev_encoding = runparams.encoding;
199         data.par_language = pit->getParLanguage(bparams);
200         Language const * const doc_language = bparams.language;
201         Language const * const prev_par_language =
202                 (pit != paragraphs.begin())
203                 ? (use_prev_env_language ? state->prev_env_language_
204                                          : priorpit->getParLanguage(bparams))
205                 : doc_language;
206
207         bool const use_polyglossia = runparams.use_polyglossia;
208         string const par_lang = use_polyglossia ?
209                 getPolyglossiaEnvName(data.par_language) : data.par_language->babel();
210         string const prev_par_lang = use_polyglossia ?
211                 getPolyglossiaEnvName(prev_par_language) : prev_par_language->babel();
212         string const doc_lang = use_polyglossia ?
213                 getPolyglossiaEnvName(doc_language) : doc_language->babel();
214         string const lang_begin_command = use_polyglossia ?
215                 "\\begin{$$lang}" : lyxrc.language_command_begin;
216         string const lang_end_command = use_polyglossia ?
217                 "\\end{$$lang}" : lyxrc.language_command_end;
218         bool const using_begin_end = use_polyglossia ||
219                                         !lang_end_command.empty();
220
221         // For polyglossia, switch language outside of environment, if possible.
222         if (par_lang != prev_par_lang) {
223                 if ((!using_begin_end || langOpenedAtThisLevel(state)) &&
224                     !lang_end_command.empty() &&
225                     prev_par_lang != doc_lang &&
226                     !prev_par_lang.empty()) {
227                         os << from_ascii(subst(
228                                 lang_end_command,
229                                 "$$lang",
230                                 prev_par_lang))
231                           // the '%' is necessary to prevent unwanted whitespace
232                           << "%\n";
233                         if (using_begin_end)
234                                 popLanguageName();
235                 }
236
237                 // If no language was explicitly opened and we are using
238                 // polyglossia or begin/end commands, then the current
239                 // language is the document language.
240                 string const & cur_lang = using_begin_end
241                                           && !state->lang_switch_depth_.empty()
242                                                   ? openLanguageName(state)
243                                                   : doc_lang;
244
245                 if ((lang_end_command.empty() ||
246                     par_lang != doc_lang ||
247                     par_lang != cur_lang) &&
248                     !par_lang.empty()) {
249                             string bc = use_polyglossia ?
250                                         getPolyglossiaBegin(lang_begin_command, par_lang,
251                                                             data.par_language->polyglossiaOpts())
252                                       : subst(lang_begin_command, "$$lang", par_lang);
253                             os << bc;
254                             // the '%' is necessary to prevent unwanted whitespace
255                             os << "%\n";
256                             if (using_begin_end)
257                                     pushLanguageName(par_lang);
258                 }
259         }
260
261         data.leftindent_open = false;
262         if (!pit->params().leftIndent().zero()) {
263                 os << "\\begin{LyXParagraphLeftIndent}{"
264                    << from_ascii(pit->params().leftIndent().asLatexString())
265                    << "}\n";
266                 data.leftindent_open = true;
267         }
268
269         if (style.isEnvironment())
270                 state->nest_level_ += 1;
271
272         if (style.isEnvironment() && !style.latexname().empty()) {
273                 os << "\\begin{" << from_ascii(style.latexname()) << '}';
274                 if (!style.latexargs().empty()) {
275                         OutputParams rp = runparams;
276                         rp.local_font = &pit->getFirstFontSettings(bparams);
277                         latexArgInsets(paragraphs, pit, os, rp, style.latexargs());
278                 }
279                 if (style.latextype == LATEX_LIST_ENVIRONMENT) {
280                         os << '{'
281                            << pit->params().labelWidthString()
282                            << "}\n";
283                 } else if (style.labeltype == LABEL_BIBLIO) {
284                         if (pit->params().labelWidthString().empty())
285                                 os << '{' << bibitemWidest(buf, runparams) << "}\n";
286                         else
287                                 os << '{'
288                                   << pit->params().labelWidthString()
289                                   << "}\n";
290                 } else
291                         os << from_ascii(style.latexparam()) << '\n';
292                 if (style.latextype == LATEX_BIB_ENVIRONMENT
293                     || style.latextype == LATEX_ITEM_ENVIRONMENT
294                     || style.latextype ==  LATEX_LIST_ENVIRONMENT) {
295                         OutputParams rp = runparams;
296                         rp.local_font = &pit->getFirstFontSettings(bparams);
297                         latexArgInsets(paragraphs, pit, os, rp, style.listpreamble(),
298                                        "listpreamble:");
299                 }
300         }
301         data.style = &style;
302
303         // in multilingual environments, the CJK tags have to be nested properly
304         data.cjk_nested = false;
305         if (!bparams.useNonTeXFonts
306             && (bparams.inputenc == "auto-legacy"
307                         || bparams.inputenc == "auto-legacy-plain")
308             && data.par_language->encoding()->package() == Encoding::CJK
309             && state->open_encoding_ != CJK && pit->isMultiLingual(bparams)) {
310                 if (prev_par_language->encoding()->package() == Encoding::CJK) {
311                         os << "\\begin{CJK}{"
312                            << from_ascii(data.par_language->encoding()->latexName())
313                            << "}{" << from_ascii(bparams.fonts_cjk) << "}%\n";
314                 }
315                 state->open_encoding_ = CJK;
316                 data.cjk_nested = true;
317         }
318         return data;
319 }
320
321
322 static void finishEnvironment(otexstream & os, OutputParams const & runparams,
323                               TeXEnvironmentData const & data)
324 {
325         OutputState * state = getOutputState();
326         // BufferParams const & bparams = buf.params(); // FIXME: for speedup shortcut below, would require passing of "buf" as argument
327         if (state->open_encoding_ == CJK && data.cjk_nested) {
328                 // We need to close the encoding even if it does not change
329                 // to do correct environment nesting
330                 os << "\\end{CJK}\n";
331                 state->open_encoding_ = none;
332         }
333
334         if (data.style->isEnvironment()) {
335                 os << breakln;
336                 bool const using_begin_end =
337                         runparams.use_polyglossia ||
338                                 !lyxrc.language_command_end.empty();
339                 // Close any language opened at this nest level
340                 if (using_begin_end) {
341                         while (langOpenedAtThisLevel(state)) {
342                                 if (isLocalSwitch(state)) {
343                                         os << "}";
344                                 } else {
345                                         os << "\\end{"
346                                            << openLanguageName(state)
347                                            << "}%\n";
348                                 }
349                                 popLanguageName();
350                         }
351                 }
352                 state->nest_level_ -= 1;
353                 string const & name = data.style->latexname();
354                 if (!name.empty())
355                         os << "\\end{" << from_ascii(name) << "}\n";
356                 state->prev_env_language_ = data.par_language;
357                 if (runparams.encoding != data.prev_encoding) {
358                         runparams.encoding = data.prev_encoding;
359                         os << setEncoding(data.prev_encoding->iconvName());
360                 }
361         }
362
363         if (data.leftindent_open) {
364                 os << breakln << "\\end{LyXParagraphLeftIndent}\n";
365                 state->prev_env_language_ = data.par_language;
366                 if (runparams.encoding != data.prev_encoding) {
367                         runparams.encoding = data.prev_encoding;
368                         os << setEncoding(data.prev_encoding->iconvName());
369                 }
370         }
371
372         // Check whether we should output a blank line after the environment
373         if (!data.style->nextnoindent)
374                 os << '\n';
375 }
376
377
378 void TeXEnvironment(Buffer const & buf, Text const & text,
379                     OutputParams const & runparams,
380                     pit_type & pit, otexstream & os)
381 {
382         ParagraphList const & paragraphs = text.paragraphs();
383         ParagraphList::const_iterator ipar = paragraphs.iterator_at(pit);
384         LYXERR(Debug::LATEX, "TeXEnvironment for paragraph " << pit);
385
386         Layout const & current_layout = ipar->layout();
387         depth_type const current_depth = ipar->params().depth();
388         Length const & current_left_indent = ipar->params().leftIndent();
389
390         // This is for debugging purpose at the end.
391         pit_type const par_begin = pit;
392         for (; pit < runparams.par_end; ++pit) {
393                 ParagraphList::const_iterator par = paragraphs.iterator_at(pit);
394
395                 // check first if this is an higher depth paragraph.
396                 bool go_out = (par->params().depth() < current_depth);
397                 if (par->params().depth() == current_depth) {
398                         // This environment is finished.
399                         go_out |= (par->layout() != current_layout);
400                         go_out |= (par->params().leftIndent() != current_left_indent);
401                 }
402                 if (go_out) {
403                         // nothing to do here, restore pit and go out.
404                         pit--;
405                         break;
406                 }
407
408                 if (par->layout() == current_layout
409                         && par->params().depth() == current_depth
410                         && par->params().leftIndent() == current_left_indent) {
411                         // We are still in the same environment so TeXOnePar and continue;
412                         TeXOnePar(buf, text, pit, os, runparams);
413                         continue;
414                 }
415
416                 // We are now in a deeper environment.
417                 // Either par->layout() != current_layout
418                 // Or     par->params().depth() > current_depth
419                 // Or     par->params().leftIndent() != current_left_indent)
420
421                 // FIXME This test should not be necessary.
422                 // We should perhaps issue an error if it is.
423                 bool const force_plain_layout = text.inset().forcePlainLayout();
424                 Layout const & style = force_plain_layout
425                         ? buf.params().documentClass().plainLayout()
426                         : par->layout();
427
428                 if (!style.isEnvironment()) {
429                         // This is a standard paragraph, no need to call TeXEnvironment.
430                         TeXOnePar(buf, text, pit, os, runparams);
431                         continue;
432                 }
433
434                 // Do not output empty environments if the whole paragraph has
435                 // been deleted with ct and changes are not output.
436                 if (size_t(pit + 1) < paragraphs.size()) {
437                         ParagraphList::const_iterator nextpar = paragraphs.iterator_at(pit + 1);
438                         Paragraph const & cpar = paragraphs.at(pit);
439                         if ((par->layout() != nextpar->layout()
440                              || par->params().depth() == nextpar->params().depth()
441                              || par->params().leftIndent() == nextpar->params().leftIndent())
442                             && !runparams.for_search && !cpar.empty()
443                             && cpar.isDeleted(0, cpar.size()) && !buf.params().output_changes) {
444                                 if (!buf.params().output_changes && !cpar.parEndChange().deleted())
445                                         os << '\n' << '\n';
446                                 continue;
447                         }
448                 }
449
450                 // This is a new environment.
451                 TeXEnvironmentData const data =
452                         prepareEnvironment(buf, text, par, os, runparams);
453                 // Recursive call to TeXEnvironment!
454                 TeXEnvironment(buf, text, runparams, pit, os);
455                 finishEnvironment(os, runparams, data);
456         }
457
458         if (pit != runparams.par_end)
459                 LYXERR(Debug::LATEX, "TeXEnvironment for paragraph " << par_begin << " done.");
460 }
461
462
463 void getArgInsets(otexstream & os, OutputParams const & runparams, Layout::LaTeXArgMap const & latexargs,
464                   map<size_t, lyx::InsetArgument const *> ilist, vector<string> required, string const & prefix)
465 {
466         size_t const argnr = latexargs.size();
467         if (argnr == 0)
468                 return;
469
470         // Default and preset args are always output, so if they require
471         // other arguments, consider this.
472         for (auto const & larg : latexargs) {
473                 Layout::latexarg const & arg = larg.second;
474                 if ((!arg.presetarg.empty() || !arg.defaultarg.empty()) && !arg.required.empty()) {
475                                 vector<string> req = getVectorFromString(arg.required);
476                                 required.insert(required.end(), req.begin(), req.end());
477                         }
478         }
479
480         for (size_t i = 1; i <= argnr; ++i) {
481                 map<size_t, InsetArgument const *>::const_iterator lit = ilist.find(i);
482                 bool inserted = false;
483                 if (lit != ilist.end()) {
484                         InsetArgument const * ins = lit->second;
485                         if (ins) {
486                                 Layout::LaTeXArgMap::const_iterator const lait =
487                                                 latexargs.find(ins->name());
488                                 if (lait != latexargs.end()) {
489                                         Layout::latexarg arg = lait->second;
490                                         docstring ldelim;
491                                         docstring rdelim;
492                                         if (!arg.nodelims) {
493                                                 ldelim = arg.mandatory ?
494                                                         from_ascii("{") : from_ascii("[");
495                                                 rdelim = arg.mandatory ?
496                                                         from_ascii("}") : from_ascii("]");
497                                         }
498                                         if (!arg.ldelim.empty())
499                                                 ldelim = arg.ldelim;
500                                         if (!arg.rdelim.empty())
501                                                 rdelim = arg.rdelim;
502                                         ins->latexArgument(os, runparams, ldelim, rdelim, arg.presetarg);
503                                         if (prefix == "listpreamble:")
504                                                 os << breakln;
505                                         inserted = true;
506                                 }
507                         }
508                 }
509                 if (!inserted) {
510                         Layout::LaTeXArgMap::const_iterator lait = latexargs.begin();
511                         Layout::LaTeXArgMap::const_iterator const laend = latexargs.end();
512                         for (; lait != laend; ++lait) {
513                                 string const name = prefix + convert<string>(i);
514                                 if ((*lait).first == name) {
515                                         Layout::latexarg arg = (*lait).second;
516                                         docstring preset = arg.presetarg;
517                                         if (!arg.defaultarg.empty()) {
518                                                 if (!preset.empty())
519                                                         preset += ",";
520                                                 preset += arg.defaultarg;
521                                         }
522                                         if (arg.mandatory) {
523                                                 docstring ldelim = arg.ldelim.empty() ?
524                                                                 from_ascii("{") : arg.ldelim;
525                                                 docstring rdelim = arg.rdelim.empty() ?
526                                                                 from_ascii("}") : arg.rdelim;
527                                                 os << ldelim << preset << rdelim;
528                                         } else if (!preset.empty()) {
529                                                 docstring ldelim = arg.ldelim.empty() ?
530                                                                 from_ascii("[") : arg.ldelim;
531                                                 docstring rdelim = arg.rdelim.empty() ?
532                                                                 from_ascii("]") : arg.rdelim;
533                                                 os << ldelim << preset << rdelim;
534                                         } else if (find(required.begin(), required.end(),
535                                                    (*lait).first) != required.end()) {
536                                                 docstring ldelim = arg.ldelim.empty() ?
537                                                                 from_ascii("[") : arg.ldelim;
538                                                 docstring rdelim = arg.rdelim.empty() ?
539                                                                 from_ascii("]") : arg.rdelim;
540                                                 os << ldelim << rdelim;
541                                         } else
542                                                 break;
543                                 }
544                         }
545                 }
546         }
547         if (runparams.for_search) {
548                 // Mark end of arguments for findadv() only
549                 os << "\\endarguments{}";
550         }
551 }
552
553
554 } // namespace
555
556
557 void pushLanguageName(string const & lang_name, bool localswitch)
558 {
559         OutputState * state = getOutputState();
560
561         int nest_level = localswitch ? -state->nest_level_ : state->nest_level_;
562         state->lang_switch_depth_.push(nest_level);
563         state->open_polyglossia_lang_.push(lang_name);
564 }
565
566
567 void popLanguageName()
568 {
569         OutputState * state = getOutputState();
570
571         state->lang_switch_depth_.pop();
572         state->open_polyglossia_lang_.pop();
573 }
574
575
576 string const & openLanguageName()
577 {
578         OutputState * state = getOutputState();
579
580         return openLanguageName(state);
581 }
582
583
584 namespace {
585
586 void addArgInsets(Paragraph const & par, string const & prefix,
587                  Layout::LaTeXArgMap const & latexargs,
588                  map<size_t, InsetArgument const *> & ilist,
589                  vector<string> & required)
590 {
591         for (auto const & table : par.insetList()) {
592                 InsetArgument const * arg = table.inset->asInsetArgument();
593                 if (!arg)
594                         continue;
595                 if (arg->name().empty()) {
596                         LYXERR0("Error: Unnamed argument inset!");
597                         continue;
598                 }
599                 string const name = prefix.empty() ?
600                         arg->name() : split(arg->name(), ':');
601                 size_t const nr = convert<size_t>(name);
602                 ilist.insert({nr, arg});
603                 Layout::LaTeXArgMap::const_iterator const lit =
604                         latexargs.find(arg->name());
605                 if (lit != latexargs.end()) {
606                         Layout::latexarg const & larg = lit->second;
607                         vector<string> req = getVectorFromString(larg.required);
608                         move(req.begin(), req.end(), back_inserter(required));
609                 }
610         }
611 }
612
613 } // namespace
614
615
616 void latexArgInsets(Paragraph const & par, otexstream & os,
617                     OutputParams const & runparams,
618                     Layout::LaTeXArgMap const & latexargs,
619                     string const & prefix)
620 {
621         map<size_t, InsetArgument const *> ilist;
622         vector<string> required;
623         addArgInsets(par, prefix, latexargs, ilist, required);
624         getArgInsets(os, runparams, latexargs, ilist, required, prefix);
625 }
626
627
628 void latexArgInsets(ParagraphList const & pars,
629                     ParagraphList::const_iterator pit,
630                     otexstream & os, OutputParams const & runparams,
631                     Layout::LaTeXArgMap const & latexargs,
632                     string const & prefix)
633 {
634         map<size_t, InsetArgument const *> ilist;
635         vector<string> required;
636
637         depth_type const current_depth = pit->params().depth();
638         Layout const current_layout = pit->layout();
639
640         // get the first paragraph in sequence with this layout and depth
641         ptrdiff_t offset = 0;
642         while (true) {
643                 if (prev(pit, offset) == pars.begin())
644                         break;
645                 ParagraphList::const_iterator priorpit = prev(pit, offset + 1);
646                 if (priorpit->layout() == current_layout
647                     && priorpit->params().depth() == current_depth)
648                         ++offset;
649                 else
650                         break;
651         }
652
653         ParagraphList::const_iterator spit = prev(pit, offset);
654         for (; spit != pars.end(); ++spit) {
655                 if (spit->layout() != current_layout ||
656                     spit->params().depth() < current_depth)
657                         break;
658                 if (spit->params().depth() > current_depth)
659                         continue;
660                 addArgInsets(*spit, prefix, latexargs, ilist, required);
661         }
662         getArgInsets(os, runparams, latexargs, ilist, required, prefix);
663 }
664
665
666 void latexArgInsetsForParent(ParagraphList const & pars, otexstream & os,
667                              OutputParams const & runparams,
668                              Layout::LaTeXArgMap const & latexargs,
669                              string const & prefix)
670 {
671         map<size_t, InsetArgument const *> ilist;
672         vector<string> required;
673
674         for (Paragraph const & par : pars) {
675                 if (par.layout().hasArgs())
676                         // The InsetArguments inside this paragraph refer to this paragraph
677                         continue;
678                 addArgInsets(par, prefix, latexargs, ilist, required);
679         }
680         getArgInsets(os, runparams, latexargs, ilist, required, prefix);
681 }
682
683
684 namespace {
685
686 // output the proper paragraph start according to latextype.
687 void parStartCommand(Paragraph const & par, otexstream & os,
688                      OutputParams const & runparams, Layout const & style)
689 {
690         switch (style.latextype) {
691         case LATEX_COMMAND:
692                 if (par.needsCProtection(runparams.moving_arg)) {
693                         if (contains(runparams.active_chars, '^'))
694                                 // cprotect relies on ^ being on catcode 7
695                                 os << "\\begingroup\\catcode`\\^=7";
696                         os << "\\cprotect";
697                 }
698                 os << '\\' << from_ascii(style.latexname());
699
700                 // Command arguments
701                 if (!style.latexargs().empty())
702                         latexArgInsets(par, os, runparams, style.latexargs());
703                 os << from_ascii(style.latexparam());
704                 break;
705         case LATEX_ITEM_ENVIRONMENT:
706         case LATEX_LIST_ENVIRONMENT:
707                 if (runparams.for_search) {
708                         os << "\\" + style.itemcommand() << "{" << style.latexname() << "}";
709                 }
710                 else {
711                         os << "\\" + style.itemcommand();
712                         // Item arguments
713                         if (!style.itemargs().empty())
714                                 latexArgInsets(par, os, runparams, style.itemargs(), "item:");
715                         os << " ";
716                 }
717                 break;
718         case LATEX_ENVIRONMENT:
719                 if (runparams.for_search) {
720                         os << "\\latexenvironment{" << style.latexname() << "}{";
721                 }
722                 break;
723         case LATEX_BIB_ENVIRONMENT:
724                 // ignore this, the inset will write itself
725                 break;
726         default:
727                 break;
728         }
729 }
730
731 } // namespace
732
733 // FIXME: this should be anonymous
734 void TeXOnePar(Buffer const & buf,
735                Text const & text,
736                pit_type pit,
737                otexstream & os,
738                OutputParams const & runparams_in,
739                string const & everypar,
740                int start_pos, int end_pos,
741                bool const force)
742 {
743         BufferParams const & bparams = runparams_in.is_child
744                 ? buf.masterParams() : buf.params();
745         ParagraphList const & paragraphs = text.paragraphs();
746         Paragraph const & par = paragraphs.at(pit);
747         // FIXME This check should not really be needed.
748         // Perhaps we should issue an error if it is.
749         Layout const & style = text.inset().forcePlainLayout() ?
750                 bparams.documentClass().plainLayout() : par.layout();
751
752         if (style.inpreamble && !force)
753                 return;
754
755         // Do not output empty commands if the whole paragraph has
756         // been deleted with ct and changes are not output.
757         if (!runparams_in.for_search && style.latextype != LATEX_ENVIRONMENT
758             && !par.empty() && par.isDeleted(0, par.size()) && !bparams.output_changes)
759                 return;
760
761         LYXERR(Debug::LATEX, "TeXOnePar for paragraph " << pit << " ptr " << &par << " '"
762                 << everypar << "'");
763
764         OutputParams runparams = runparams_in;
765         runparams.isLastPar = (pit == pit_type(paragraphs.size() - 1));
766         // We reinitialize par begin and end to be on the safe side
767         // with embedded inset as we don't know if they set those
768         // value correctly.
769         runparams.par_begin = 0;
770         runparams.par_end = 0;
771
772         bool const maintext = text.isMainText();
773         // we are at the beginning of an inset and CJK is already open;
774         // we count inheritation levels to get the inset nesting right.
775         OutputState * state = getOutputState();
776         if (pit == 0 && !maintext
777             && (state->cjk_inherited_ > 0 || state->open_encoding_ == CJK)) {
778                 state->cjk_inherited_ += 1;
779                 state->open_encoding_ = none;
780         }
781
782         // This paragraph is merged and we do not show changes in the output
783         bool const merged_par = !bparams.output_changes && par.parEndChange().deleted();
784
785         if (text.inset().isPassThru()) {
786                 Font const outerfont = text.outerFont(pit);
787
788                 // No newline before first paragraph in this lyxtext
789                 if (pit > 0 && !text.inset().getLayout().parbreakIgnored() && !merged_par) {
790                         os << '\n';
791                         if (!text.inset().getLayout().parbreakIsNewline())
792                                 os << '\n';
793                 }
794
795                 par.latex(bparams, outerfont, os, runparams, start_pos, end_pos, force);
796                 return;
797         }
798
799         Paragraph const * nextpar = runparams.isLastPar
800                 ? nullptr : &paragraphs.at(pit + 1);
801
802         bool const intitle_command = style.intitle && style.isCommand();
803         // Intitle commands switch languages locally, thus increase
804         // language nesting level
805         if (intitle_command)
806                 state->nest_level_ += 1;
807
808         if (style.pass_thru) {
809                 Font const outerfont = text.outerFont(pit);
810                 parStartCommand(par, os, runparams, style);
811                 if (style.isCommand() && style.needprotect)
812                         // Due to the moving argument, some fragile
813                         // commands (labels, index entries)
814                         // are output after this command (#2154)
815                         runparams.postpone_fragile_stuff =
816                                 bparams.postpone_fragile_content;
817                 if (intitle_command)
818                         os << '{';
819
820                 par.latex(bparams, outerfont, os, runparams, start_pos, end_pos, force);
821
822                 // I did not create a parEndCommand for this minuscule
823                 // task because in the other user of parStartCommand
824                 // the code is different (JMarc)
825                 if (style.isCommand()) {
826                         os << "}";
827                         if (par.needsCProtection(runparams.moving_arg)
828                             && contains(runparams.active_chars, '^'))
829                                 os << "\\endgroup";
830                         if (merged_par)
831                                 os << "{}";
832                         else
833                                 os << "\n";
834                 }
835                 else if (!merged_par)
836                         os << '\n';
837                 if (!style.parbreak_is_newline && !merged_par) {
838                         os << '\n';
839                 } else if (nextpar && !style.isEnvironment()) {
840                         Layout const nextstyle = text.inset().forcePlainLayout()
841                                 ? bparams.documentClass().plainLayout()
842                                 : nextpar->layout();
843                         if (nextstyle.name() != style.name() && !merged_par)
844                                 os << '\n';
845                 }
846
847                 return;
848         }
849
850         // This paragraph's language
851         Language const * const par_language = par.getParLanguage(bparams);
852         Language const * const nextpar_language = nextpar ?
853                 nextpar->getParLanguage(bparams) : nullptr;
854         // The document's language
855         Language const * const doc_language = bparams.language;
856         // The language that was in effect when the environment this paragraph is
857         // inside of was opened
858         Language const * const outer_language =
859                 (runparams.local_font != nullptr) ?
860                         runparams.local_font->language() : doc_language;
861
862         Paragraph const * priorpar = (pit == 0) ? nullptr : &paragraphs.at(pit - 1);
863
864         // The previous language that was in effect is the language of the
865         // previous paragraph, unless the previous paragraph is inside an
866         // environment with nesting depth greater than (or equal to, but with
867         // a different layout) the current one. If there is no previous
868         // paragraph, the previous language is the outer language.
869         // Note further that we take the outer language also if the prior par
870         // is PassThru, since in that case it has latex_language, and all secondary
871         // languages have been closed (#10793).
872         bool const use_prev_env_language = state->prev_env_language_ != nullptr
873                         && priorpar
874                         && priorpar->layout().isEnvironment()
875                         && (priorpar->getDepth() > par.getDepth()
876                             || (priorpar->getDepth() == par.getDepth()
877                                 && priorpar->layout() != par.layout()));
878
879         // We need to ignore previous intitle commands since languages
880         // are switched locally there (# 11514)
881         // There might be paragraphs before the title, so we check this.
882         Paragraph * prior_nontitle_par = nullptr;
883         if (!intitle_command) {
884                 pit_type ppit = pit;
885                 while (ppit > 0) {
886                         --ppit;
887                         Paragraph const * tmppar = &paragraphs.at(ppit);
888                         if (tmppar->layout().intitle && tmppar->layout().isCommand())
889                                 continue;
890                         prior_nontitle_par = const_cast<Paragraph*>(tmppar);
891                         break;
892                 }
893         }
894         Language const * const prev_language =
895                 runparams_in.for_search 
896                         ? languages.getLanguage("ignore")
897                         : (prior_nontitle_par && !prior_nontitle_par->isPassThru())
898                                 ? (use_prev_env_language 
899                                         ? state->prev_env_language_
900                                         : prior_nontitle_par->getParLanguage(bparams))
901                                 : outer_language;
902
903         bool const use_polyglossia = runparams.use_polyglossia;
904         string const par_lang = use_polyglossia ?
905                 getPolyglossiaEnvName(par_language): par_language->babel();
906         string const prev_lang = use_polyglossia ?
907                 getPolyglossiaEnvName(prev_language) : prev_language->babel();
908         string const outer_lang = use_polyglossia ?
909                 getPolyglossiaEnvName(outer_language) : outer_language->babel();
910         string const nextpar_lang = nextpar_language ? (use_polyglossia ?
911                 getPolyglossiaEnvName(nextpar_language) :
912                 nextpar_language->babel()) : string();
913         string lang_begin_command = use_polyglossia ?
914                 "\\begin{$$lang}$$opts" : lyxrc.language_command_begin;
915         string lang_end_command = use_polyglossia ?
916                 "\\end{$$lang}" : lyxrc.language_command_end;
917         // the '%' is necessary to prevent unwanted whitespace
918         string lang_command_termination = "%\n";
919         bool const using_begin_end = use_polyglossia ||
920                                         !lang_end_command.empty();
921
922         // For InTitle commands, we need to switch the language inside the command
923         // (see #10849); thus open the command here.
924         if (intitle_command) {
925                 parStartCommand(par, os, runparams, style);
926                 if (style.isCommand() && style.needprotect)
927                         // Due to the moving argument, some fragile
928                         // commands (labels, index entries)
929                         // are output after this command (#2154)
930                         runparams.postpone_fragile_stuff =
931                                 bparams.postpone_fragile_content;
932                 os << '{';
933         }
934
935         // In some insets (such as Arguments), we cannot use \selectlanguage.
936         // Also, if an RTL language is set via environment in polyglossia,
937         // only a nested \\text<lang> command will reset the direction for LTR
938         // languages (see # 10111).
939         bool const in_polyglossia_rtl_env =
940                 use_polyglossia
941                 && runparams.local_font != nullptr
942                 && outer_language->rightToLeft()
943                 && !par_language->rightToLeft();
944         bool const localswitch =
945                         (runparams_in.for_search
946                         || text.inset().forceLocalFontSwitch()
947                         || (using_begin_end && text.inset().forcePlainLayout())
948                         || in_polyglossia_rtl_env)
949                         && !text.inset().forceParDirectionSwitch();
950         if (localswitch) {
951                 lang_begin_command = use_polyglossia ?
952                             "\\text$$lang$$opts{" : lyxrc.language_command_local;
953                 lang_end_command = "}";
954                 lang_command_termination.clear();
955         }
956
957         bool const localswitch_needed = localswitch && par_lang != outer_lang;
958
959         // localswitches need to be closed and reopened at each par
960         if (runparams_in.for_search || ((par_lang != prev_lang || localswitch_needed)
961              // check if we already put language command in TeXEnvironment()
962              && !(style.isEnvironment()
963                   && (pit == 0 || (priorpar->layout() != par.layout()
964                                    && priorpar->getDepth() <= par.getDepth())
965                       || priorpar->getDepth() < par.getDepth())))) {
966                 if (!localswitch
967                     && (!using_begin_end || langOpenedAtThisLevel(state))
968                     && !lang_end_command.empty()
969                     && prev_lang != outer_lang
970                     && !prev_lang.empty()
971                     && (!using_begin_end || !style.isEnvironment())) {
972                         os << from_ascii(subst(lang_end_command,
973                                                "$$lang",
974                                                prev_lang))
975                            << lang_command_termination;
976                         if (using_begin_end)
977                                 popLanguageName();
978                 }
979
980                 // We need to open a new language if we couldn't close the previous
981                 // one (because there's no language_command_end); and even if we closed
982                 // the previous one, if the current language is different than the
983                 // outer_language (which is currently in effect once the previous one
984                 // is closed).
985                 if ((lang_end_command.empty() || par_lang != outer_lang
986                      || (!using_begin_end
987                          || (style.isEnvironment() && par_lang != prev_lang)))
988                         && !par_lang.empty()) {
989                         // If we're inside an inset, and that inset is within an \L or \R
990                         // (or equivalents), then within the inset, too, any opposite
991                         // language paragraph should appear within an \L or \R (in addition
992                         // to, outside of, the normal language switch commands).
993                         // This behavior is not correct for ArabTeX, though.
994                         if (!using_begin_end
995                             // not for ArabTeX
996                             && par_language->lang() != "arabic_arabtex"
997                             && outer_language->lang() != "arabic_arabtex"
998                             // are we in an inset?
999                             && runparams.local_font != nullptr
1000                             // is the inset within an \L or \R?
1001                             //
1002                             // FIXME: currently, we don't check this; this means that
1003                             // we'll have unnnecessary \L and \R commands, but that
1004                             // doesn't seem to hurt (though latex will complain)
1005                             //
1006                             // is this paragraph in the opposite direction?
1007                             && runparams.local_font->isRightToLeft() != par_language->rightToLeft()) {
1008                                 // FIXME: I don't have a working copy of the Arabi package, so
1009                                 // I'm not sure if the farsi and arabic_arabi stuff is correct
1010                                 // or not...
1011                                 if (par_language->lang() == "farsi")
1012                                         os << "\\textFR{";
1013                                 else if (outer_language->lang() == "farsi")
1014                                         os << "\\textLR{";
1015                                 else if (par_language->lang() == "arabic_arabi")
1016                                         os << "\\textAR{";
1017                                 else if (outer_language->lang() == "arabic_arabi")
1018                                         os << "\\textLR{";
1019                                 // remaining RTL languages currently is hebrew
1020                                 else if (par_language->rightToLeft())
1021                                         os << "\\R{";
1022                                 else
1023                                         os << "\\L{";
1024                         }
1025                         // With CJK, the CJK tag has to be closed first (see below)
1026                         if ((runparams.encoding->package() != Encoding::CJK
1027                                  || bparams.useNonTeXFonts
1028                                  || runparams.for_search)
1029                             && (par_lang != openLanguageName(state) || localswitch || intitle_command)
1030                             && !par_lang.empty()) {
1031                                 string bc = use_polyglossia ?
1032                                           getPolyglossiaBegin(lang_begin_command, par_lang,
1033                                                               par_language->polyglossiaOpts(),
1034                                                               localswitch)
1035                                           : subst(lang_begin_command, "$$lang", par_lang);
1036                                 os << bc;
1037                                 os << lang_command_termination;
1038                                 if (using_begin_end)
1039                                         pushLanguageName(par_lang, localswitch);
1040                         }
1041                 }
1042         }
1043
1044         // Switch file encoding if necessary; no need to do this for "auto-legacy-plain"
1045         // encoding, since this only affects the position of the outputted
1046         // \inputencoding command; the encoding switch will occur when necessary
1047         if (bparams.inputenc == "auto-legacy"
1048                 && !runparams.isFullUnicode() // Xe/LuaTeX use one document-wide encoding  (see also switchEncoding())
1049                 && runparams.encoding->package() != Encoding::japanese
1050                 && runparams.encoding->package() != Encoding::none) {
1051                 // Look ahead for future encoding changes.
1052                 // We try to output them at the beginning of the paragraph,
1053                 // since the \inputencoding command is not allowed e.g. in
1054                 // sections. For this reason we only set runparams.moving_arg
1055                 // after checking for the encoding change, otherwise the
1056                 // change would be always avoided by switchEncoding().
1057                 for (pos_type i = 0; i < par.size(); ++i) {
1058                         char_type const c = par.getChar(i);
1059                         Encoding const * const encoding =
1060                                 par.getFontSettings(bparams, i).language()->encoding();
1061                         if (encoding->package() != Encoding::CJK
1062                                 && runparams.encoding->package() == Encoding::inputenc
1063                                 && isASCII(c))
1064                                 continue;
1065                         if (par.isInset(i))
1066                                 break;
1067                         // All characters before c are in the ASCII range, and
1068                         // c is non-ASCII (but no inset), so change the
1069                         // encoding to that required by the language of c.
1070                         // With CJK, only add switch if we have CJK content at the beginning
1071                         // of the paragraph
1072                         if (i != 0 && encoding->package() == Encoding::CJK)
1073                                 continue;
1074
1075                         pair<bool, int> enc_switch = switchEncoding(os.os(),
1076                                                 bparams, runparams, *encoding);
1077                         // the following is necessary after a CJK environment in a multilingual
1078                         // context (nesting issue).
1079                         if (par_language->encoding()->package() == Encoding::CJK
1080                                 && state->open_encoding_ != CJK && state->cjk_inherited_ == 0) {
1081                                 os << "\\begin{CJK}{"
1082                                    << from_ascii(par_language->encoding()->latexName())
1083                                    << "}{" << from_ascii(bparams.fonts_cjk) << "}%\n";
1084                                 state->open_encoding_ = CJK;
1085                         }
1086                         if (encoding->package() != Encoding::none && enc_switch.first) {
1087                                 if (enc_switch.second > 0) {
1088                                         // the '%' is necessary to prevent unwanted whitespace
1089                                         os << "%\n";
1090                                 }
1091                                 // With CJK, the CJK tag had to be closed first (see above)
1092                                 if (runparams.encoding->package() == Encoding::CJK
1093                                     && par_lang != openLanguageName(state)
1094                                     && !par_lang.empty()) {
1095                                         os << subst(lang_begin_command, "$$lang", par_lang)
1096                                            << lang_command_termination;
1097                                         if (using_begin_end)
1098                                                 pushLanguageName(par_lang, localswitch);
1099                                 }
1100                                 runparams.encoding = encoding;
1101                         }
1102                         break;
1103                 }
1104         }
1105
1106         runparams.moving_arg |= style.needprotect;
1107         if (style.needmboxprotect)
1108                 ++runparams.inulemcmd;
1109         Encoding const * const prev_encoding = runparams.encoding;
1110
1111         bool const useSetSpace = bparams.documentClass().provides("SetSpace");
1112         if (par.allowParagraphCustomization()) {
1113                 if (par.params().startOfAppendix()) {
1114                         os << "\n\\appendix\n";
1115                 }
1116
1117                 // InTitle commands must use switches (not environments)
1118                 // inside the commands (see #9332)
1119                 if (style.intitle) {
1120                         if (!par.params().spacing().isDefault())
1121                         {
1122                                 if (runparams.moving_arg)
1123                                         os << "\\protect";
1124                                 os << from_ascii(par.params().spacing().writeCmd(useSetSpace));
1125                         }
1126                 } else {
1127                         if (!par.params().spacing().isDefault()
1128                                 && (pit == 0 || !priorpar->hasSameLayout(par)))
1129                         {
1130                                 os << from_ascii(par.params().spacing().writeEnvirBegin(useSetSpace))
1131                                     << '\n';
1132                         }
1133
1134                         if (style.isCommand()) {
1135                                 os << '\n';
1136                         }
1137                 }
1138         }
1139
1140         // For InTitle commands, we already started the command before
1141         // the language switch
1142         if (!intitle_command) {
1143                 parStartCommand(par, os, runparams, style);
1144                 if (style.isCommand() && style.needprotect)
1145                         // Due to the moving argument, some fragile
1146                         // commands (labels, index entries)
1147                         // are output after this command (#2154)
1148                         runparams.postpone_fragile_stuff =
1149                                 bparams.postpone_fragile_content;
1150         }
1151
1152         Font const outerfont = text.outerFont(pit);
1153
1154         // FIXME UNICODE
1155         os << from_utf8(everypar);
1156         par.latex(bparams, outerfont, os, runparams, start_pos, end_pos, force);
1157
1158         Font const font = par.empty()
1159                  ? par.getLayoutFont(bparams, outerfont)
1160                  : par.getFont(bparams, par.size() - 1, outerfont);
1161
1162         bool const is_command = style.isCommand();
1163
1164         // InTitle commands need to be closed after the language has been closed.
1165         if (!intitle_command) {
1166                 if (is_command) {
1167                         os << '}';
1168                         if (!style.postcommandargs().empty())
1169                                 latexArgInsets(par, os, runparams, style.postcommandargs(), "post:");
1170                         if (!runparams.post_macro.empty()) {
1171                                 // Output the stored fragile commands (labels, indices etc.)
1172                                 // that need to be output after the command with moving argument.
1173                                 os << runparams.post_macro;
1174                                 runparams.post_macro.clear();
1175                         }
1176                         if (par.needsCProtection(runparams.moving_arg)
1177                             && contains(runparams.active_chars, '^'))
1178                                 os << "\\endgroup";
1179                         if (runparams.encoding != prev_encoding) {
1180                                 runparams.encoding = prev_encoding;
1181                                 os << setEncoding(prev_encoding->iconvName());
1182                         }
1183                 }
1184         }
1185
1186         bool pending_newline = false;
1187         bool unskip_newline = false;
1188         bool close_lang_switch = false;
1189         switch (style.latextype) {
1190         case LATEX_ITEM_ENVIRONMENT:
1191         case LATEX_LIST_ENVIRONMENT:
1192                 if ((nextpar && par_lang != nextpar_lang
1193                              && nextpar->getDepth() == par.getDepth())
1194                     || (atSameLastLangSwitchDepth(state) && nextpar
1195                             && nextpar->getDepth() < par.getDepth()))
1196                         close_lang_switch = using_begin_end;
1197                 if (nextpar && par.params().depth() < nextpar->params().depth())
1198                         pending_newline = !text.inset().getLayout().parbreakIgnored() && !merged_par;
1199                 break;
1200         case LATEX_ENVIRONMENT: {
1201                 // if it's the last paragraph of the current environment
1202                 // skip it otherwise fall through
1203                 if (nextpar
1204                     && ((nextpar->layout() != par.layout()
1205                            || nextpar->params().depth() != par.params().depth())
1206                         || (!using_begin_end || par_lang != nextpar_lang)))
1207                 {
1208                         close_lang_switch = using_begin_end;
1209                         break;
1210                 }
1211         }
1212         // possible
1213         // fall through
1214         default:
1215                 // we don't need it for the last paragraph and in InTitle commands!!!
1216                 if (nextpar && !intitle_command)
1217                         pending_newline = !text.inset().getLayout().parbreakIgnored() && !merged_par;
1218         }
1219
1220         // InTitle commands use switches (not environments) for space settings
1221         if (par.allowParagraphCustomization() && !style.intitle) {
1222                 if (!par.params().spacing().isDefault()
1223                         && (runparams.isLastPar || !nextpar->hasSameLayout(par))) {
1224                         if (pending_newline)
1225                                 os << '\n';
1226
1227                         string const endtag =
1228                                 par.params().spacing().writeEnvirEnd(useSetSpace);
1229                         if (prefixIs(endtag, "\\end{"))
1230                                 os << breakln;
1231
1232                         os << from_ascii(endtag);
1233                         pending_newline = true;
1234                 }
1235         }
1236
1237         // Closing the language is needed for the last paragraph in a given language
1238         // as well as for any InTitleCommand (since these set the language locally);
1239         // it is also needed if we're within an \L or \R that we may have opened above
1240         // (not necessarily in this paragraph) and are about to close.
1241         bool closing_rtl_ltr_environment = !using_begin_end
1242                 // not for ArabTeX
1243                 && (par_language->lang() != "arabic_arabtex"
1244                     && outer_language->lang() != "arabic_arabtex")
1245                 // have we opened an \L or \R environment?
1246                 && runparams.local_font != nullptr
1247                 && runparams.local_font->isRightToLeft() != par_language->rightToLeft()
1248                 // are we about to close the language?
1249                 &&((nextpar && par_lang != nextpar_lang)
1250                    || (runparams.isLastPar && par_lang != outer_lang));
1251
1252         if (localswitch_needed
1253             || (intitle_command && using_begin_end)
1254             || closing_rtl_ltr_environment
1255             || (((runparams.isLastPar && !runparams.inbranch) || close_lang_switch)
1256                 && (par_lang != outer_lang || (using_begin_end
1257                                                 && style.isEnvironment()
1258                                                 && par_lang != nextpar_lang)))) {
1259                 // Since \selectlanguage write the language to the aux file,
1260                 // we need to reset the language at the end of footnote or
1261                 // float.
1262
1263                 if (!localswitch && (pending_newline || close_lang_switch))
1264                         os << '\n';
1265
1266                 // when the paragraph uses CJK, the language has to be closed earlier
1267                 if ((font.language()->encoding()->package() != Encoding::CJK)
1268                         || bparams.useNonTeXFonts
1269                         || runparams_in.for_search) {
1270                         if (lang_end_command.empty()) {
1271                                 // If this is a child, we should restore the
1272                                 // master language after the last paragraph.
1273                                 Language const * const current_language =
1274                                         (runparams.isLastPar && runparams.master_language)
1275                                                 ? runparams.master_language
1276                                                 : outer_language;
1277                                 string const current_lang = use_polyglossia
1278                                         ? getPolyglossiaEnvName(current_language)
1279                                         : current_language->babel();
1280                                 if (!current_lang.empty()
1281                                     && current_lang != openLanguageName(state)) {
1282                                         string bc = use_polyglossia ?
1283                                                     getPolyglossiaBegin(lang_begin_command, current_lang,
1284                                                                         current_language->polyglossiaOpts(),
1285                                                                         localswitch)
1286                                                   : subst(lang_begin_command, "$$lang", current_lang);
1287                                         os << bc;
1288                                         pending_newline = !localswitch
1289                                                         && !text.inset().getLayout().parbreakIgnored();
1290                                         unskip_newline = !localswitch;
1291                                         if (using_begin_end)
1292                                                 pushLanguageName(current_lang, localswitch);
1293                                 }
1294                         } else if ((!using_begin_end ||
1295                                     langOpenedAtThisLevel(state)) &&
1296                                    !par_lang.empty()) {
1297                                 // If we are in an environment, we have to
1298                                 // close the "outer" language afterwards
1299                                 string const & cur_lang = openLanguageName(state);
1300                                 if (!style.isEnvironment()
1301                                     || (close_lang_switch
1302                                         && atSameLastLangSwitchDepth(state)
1303                                         && par_lang != outer_lang
1304                                         && (par_lang != cur_lang
1305                                             || (cur_lang != outer_lang
1306                                                 && nextpar
1307                                                 && style != nextpar->layout())))
1308                                     || (atSameLastLangSwitchDepth(state)
1309                                         && !state->lang_switch_depth_.empty()
1310                                         && cur_lang != par_lang)
1311                                     || in_polyglossia_rtl_env)
1312                                 {
1313                                         if (using_begin_end && !localswitch)
1314                                                 os << breakln;
1315                                         os << from_ascii(subst(
1316                                                 lang_end_command,
1317                                                 "$$lang",
1318                                                 par_lang));
1319                                         pending_newline = !localswitch
1320                                                         && !text.inset().getLayout().parbreakIgnored();
1321                                         unskip_newline = !localswitch;
1322                                         if (using_begin_end)
1323                                                 popLanguageName();
1324                                 }
1325                         }
1326                 }
1327         }
1328         if (closing_rtl_ltr_environment)
1329                 os << "}";
1330
1331         // InTitle commands need to be closed after the language has been closed.
1332         if (intitle_command) {
1333                 os << '}';
1334                 if (!style.postcommandargs().empty())
1335                         latexArgInsets(par, os, runparams, style.postcommandargs(), "post:");
1336                 if (!runparams.post_macro.empty()) {
1337                         // Output the stored fragile commands (labels, indices etc.)
1338                         // that need to be output after the command with moving argument.
1339                         os << runparams.post_macro;
1340                         runparams.post_macro.clear();
1341                 }
1342                 if (par.needsCProtection(runparams.moving_arg)
1343                     && contains(runparams.active_chars, '^'))
1344                         os << "\\endgroup";
1345                 if (runparams.encoding != prev_encoding) {
1346                         runparams.encoding = prev_encoding;
1347                         os << setEncoding(prev_encoding->iconvName());
1348                 }
1349         }
1350
1351         bool const last_was_separator =
1352                 !par.empty() && par.isEnvSeparator(par.size() - 1);
1353
1354         // Signify added/deleted par break in output if show changes in output
1355         if (nextpar && !os.afterParbreak() && !last_was_separator
1356             && bparams.output_changes && par.parEndChange().changed()) {
1357                 Changes::latexMarkChange(os, bparams, Change(Change::UNCHANGED),
1358                                          par.parEndChange(), runparams);
1359                 os << bparams.encoding().latexString(docstring(1, 0x00b6)).first << "}";
1360         }
1361
1362         if (pending_newline) {
1363                 if (unskip_newline)
1364                         // prevent unwanted whitespace
1365                         os << '%';
1366                 if (!os.afterParbreak() && !last_was_separator)
1367                         os << '\n';
1368         }
1369
1370         // if this is a CJK-paragraph and the next isn't, close CJK
1371         // also if the next paragraph is a multilingual environment (because of nesting)
1372         if (nextpar && state->open_encoding_ == CJK
1373                 && bparams.encoding().iconvName() != "UTF-8"
1374                 && bparams.encoding().package() != Encoding::CJK
1375                 && ((nextpar_language &&
1376                         nextpar_language->encoding()->package() != Encoding::CJK)
1377                         || (nextpar->layout().isEnvironment() && nextpar->isMultiLingual(bparams)))
1378                 // inbetween environments, CJK has to be closed later (nesting!)
1379                 && (!style.isEnvironment() || !nextpar->layout().isEnvironment())) {
1380                 os << "\\end{CJK}\n";
1381                 state->open_encoding_ = none;
1382         }
1383
1384         // If this is the last paragraph, close the CJK environment
1385         // if necessary. If it's an environment or nested in an environment,
1386         // we'll have to \end that first.
1387         if (runparams.isLastPar && !style.isEnvironment()
1388                 && par.params().depth() < 1) {
1389                 switch (state->open_encoding_) {
1390                         case CJK: {
1391                                 // do nothing at the end of child documents
1392                                 if (maintext && buf.masterBuffer() != &buf)
1393                                         break;
1394                                 // end of main text: also insert a \clearpage (see #5386)
1395                                 if (maintext) {
1396                                         os << "\n\\clearpage\n\\end{CJK}\n";
1397                                 // end of an inset
1398                                 } else
1399                                         os << "\\end{CJK}";
1400                                 state->open_encoding_ = none;
1401                                 break;
1402                         }
1403                         case inputenc: {
1404                                 // FIXME: If we are in an inset and the switch happened outside this inset,
1405                                 // do not switch back at the end of the inset (bug #8479)
1406                                 // The following attempt does not help with listings-caption in a CJK document:
1407                                 // if (runparams_in.local_font != 0
1408                                 //    && runparams_in.encoding == runparams_in.local_font->language()->encoding())
1409                                 //      break;
1410                                 os << "\\egroup";
1411                                 state->open_encoding_ = none;
1412                                 break;
1413                         }
1414                         case none:
1415                         default:
1416                                 // do nothing
1417                                 break;
1418                 }
1419         }
1420
1421         // Information about local language is stored as a font feature.
1422         // If this is the last paragraph of the inset and a local_font was set upon entering
1423         // and we are mixing encodings ("auto-legacy" or "auto-legacy-plain" and no XeTeX or LuaTeX),
1424         // ensure the encoding is set back to the default encoding of the local language.
1425         if (runparams.isLastPar && runparams_in.local_font != nullptr
1426             && runparams_in.encoding != runparams_in.local_font->language()->encoding()
1427             && (bparams.inputenc == "auto-legacy" || bparams.inputenc == "auto-legacy-plain")
1428                 && !runparams.isFullUnicode()
1429            ) {
1430                 runparams_in.encoding = runparams_in.local_font->language()->encoding();
1431                 os << setEncoding(runparams_in.encoding->iconvName());
1432         }
1433         // Otherwise, the current encoding should be set for the next paragraph.
1434         else
1435                 runparams_in.encoding = runparams.encoding;
1436
1437         // Also pass the post_macros upstream
1438         runparams_in.post_macro = runparams.post_macro;
1439         // These need to be passed upstream as well
1440         runparams_in.need_maketitle = runparams.need_maketitle;
1441         runparams_in.have_maketitle = runparams.have_maketitle;
1442
1443
1444         // we don't need a newline for the last paragraph!!!
1445         // Note from JMarc: we will re-add a \n explicitly in
1446         // TeXEnvironment, because it is needed in this case
1447         if (nextpar && !os.afterParbreak() && !last_was_separator) {
1448                 Layout const & next_layout = nextpar->layout();
1449                 if (!text.inset().getLayout().parbreakIgnored() && !merged_par)
1450                         // Make sure to start a new line
1451                         os << breakln;
1452                 // A newline '\n' is always output before a command,
1453                 // so avoid doubling it.
1454                 if (!next_layout.isCommand()) {
1455                         // Here we now try to avoid spurious empty lines by
1456                         // outputting a paragraph break only if: (case 1) the
1457                         // paragraph style allows parbreaks and no \begin, \end
1458                         // or \item tags are going to follow (i.e., if the next
1459                         // isn't the first or the current isn't the last
1460                         // paragraph of an environment or itemize) and the
1461                         // depth and alignment of the following paragraph is
1462                         // unchanged, or (case 2) the following is a
1463                         // non-environment paragraph whose depth is increased
1464                         // but whose alignment is unchanged, or (case 3) the
1465                         // paragraph is not an environment and the next one is a
1466                         // non-itemize-like env at lower depth, or (case 4) the
1467                         // paragraph is a command not followed by an environment
1468                         // and the alignment of the current and next paragraph
1469                         // is unchanged, or (case 5) the current alignment is
1470                         // changed and a standard paragraph follows.
1471                         DocumentClass const & tclass = bparams.documentClass();
1472                         if ((style == next_layout
1473                              && !style.parbreak_is_newline
1474                              && !text.inset().getLayout().parbreakIsNewline()
1475                              && !text.inset().getLayout().parbreakIgnored()
1476                              && style.latextype != LATEX_ITEM_ENVIRONMENT
1477                              && style.latextype != LATEX_LIST_ENVIRONMENT
1478                              && style.align == par.getAlign(bparams)
1479                              && nextpar->getDepth() == par.getDepth()
1480                              && nextpar->getAlign(bparams) == par.getAlign(bparams))
1481                             || (!next_layout.isEnvironment()
1482                                 && nextpar->getDepth() > par.getDepth()
1483                                 && nextpar->getAlign(bparams) == next_layout.align)
1484                             || (!style.isEnvironment()
1485                                 && next_layout.latextype == LATEX_ENVIRONMENT
1486                                 && nextpar->getDepth() < par.getDepth())
1487                             || (style.isCommand()
1488                                 && !next_layout.isEnvironment()
1489                                 && style.align == par.getAlign(bparams)
1490                                 && next_layout.align == nextpar->getAlign(bparams))
1491                             || (style.align != par.getAlign(bparams)
1492                                 && tclass.isDefaultLayout(next_layout))) {
1493                                 // and omit paragraph break if it has been deleted with ct
1494                                 // and changes are not shown in output
1495                                 if (!merged_par)
1496                                         os << '\n';
1497                         }
1498                 }
1499         }
1500
1501         // Reset language nesting level after intitle command
1502         if (intitle_command)
1503                 state->nest_level_ -= 1;
1504
1505         LYXERR(Debug::LATEX, "TeXOnePar for paragraph " << pit << " done; ptr "
1506                 << &par << " next " << nextpar);
1507
1508         return;
1509 }
1510
1511
1512 // LaTeX all paragraphs
1513 void latexParagraphs(Buffer const & buf,
1514                      Text const & text,
1515                      otexstream & os,
1516                      OutputParams const & runparams,
1517                      string const & everypar)
1518 {
1519         LASSERT(runparams.par_begin <= runparams.par_end,
1520                 { os << "% LaTeX Output Error\n"; return; } );
1521
1522         BufferParams const & bparams = buf.params();
1523         BufferParams const & mparams = buf.masterParams();
1524
1525         bool const maintext = text.isMainText();
1526         bool const is_child = buf.masterBuffer() != &buf;
1527         bool const multibib_child = maintext && is_child
1528                         && mparams.multibib == "child";
1529
1530         if (multibib_child && mparams.useBiblatex())
1531                 os << "\\newrefsection";
1532         else if (multibib_child && mparams.useBibtopic()
1533                  && !buf.masterBibInfo().empty()) {
1534                 os << "\\begin{btUnit}\n";
1535                 runparams.openbtUnit = true;
1536         }
1537
1538         // Open a CJK environment at the beginning of the main buffer
1539         // if the document's main encoding requires the CJK package
1540         // or the document encoding is utf8 and the CJK package is required
1541         // (but not in child documents or documents using system fonts):
1542         OutputState * state = getOutputState();
1543         if (maintext && !is_child && !bparams.useNonTeXFonts
1544             && (bparams.encoding().package() == Encoding::CJK
1545                         || (bparams.encoding().name() == "utf8"
1546                                 && runparams.use_CJK))
1547            ) {
1548                 docstring const cjkenc = bparams.encoding().iconvName() == "UTF-8"
1549                                                                  ? from_ascii("UTF8")
1550                                                                  : from_ascii(bparams.encoding().latexName());
1551                 os << "\\begin{CJK}{" << cjkenc
1552                    << "}{" << from_ascii(bparams.fonts_cjk) << "}%\n";
1553                 state->open_encoding_ = CJK;
1554         }
1555         // if "auto begin" is switched off, explicitly switch the
1556         // language on at start
1557         string const mainlang = runparams.use_polyglossia
1558                 ? getPolyglossiaEnvName(bparams.language)
1559                 : bparams.language->babel();
1560         string const lang_begin_command = runparams.use_polyglossia ?
1561                 "\\begin{$$lang}$$opts" : lyxrc.language_command_begin;
1562         string const lang_end_command = runparams.use_polyglossia ?
1563                 "\\end{$$lang}" : lyxrc.language_command_end;
1564         bool const using_begin_end = runparams.use_polyglossia ||
1565                                         !lang_end_command.empty();
1566
1567         if (maintext && !lyxrc.language_auto_begin &&
1568             !mainlang.empty()) {
1569                 // FIXME UNICODE
1570                 string bc = runparams.use_polyglossia ?
1571                             getPolyglossiaBegin(lang_begin_command, mainlang,
1572                                                 bparams.language->polyglossiaOpts())
1573                           : subst(lang_begin_command, "$$lang", mainlang);
1574                 os << bc;
1575                 os << '\n';
1576                 if (using_begin_end)
1577                         pushLanguageName(mainlang);
1578         }
1579
1580         ParagraphList const & paragraphs = text.paragraphs();
1581
1582         if (runparams.par_begin == runparams.par_end) {
1583                 // The full doc will be exported but it is easier to just rely on
1584                 // runparams range parameters that will be passed TeXEnvironment.
1585                 runparams.par_begin = 0;
1586                 runparams.par_end = static_cast<int>(paragraphs.size());
1587         }
1588
1589         pit_type pit = runparams.par_begin;
1590         // lastpit is for the language check after the loop.
1591         pit_type lastpit = pit;
1592         DocumentClass const & tclass = bparams.documentClass();
1593
1594         // Did we already warn about inTitle layout mixing? (we only warn once)
1595         bool gave_layout_warning = false;
1596         for (; pit < runparams.par_end; ++pit) {
1597                 lastpit = pit;
1598                 ParagraphList::const_iterator par = paragraphs.iterator_at(pit);
1599
1600                 // FIXME This check should not be needed. We should
1601                 // perhaps issue an error if it is.
1602                 Layout const & layout = text.inset().forcePlainLayout() ?
1603                                 tclass.plainLayout() : par->layout();
1604
1605                 if (layout.intitle) {
1606                         if (runparams.have_maketitle) {
1607                                 if (!gave_layout_warning && !runparams.dryrun) {
1608                                         gave_layout_warning = true;
1609                                         frontend::Alert::warning(_("Error in latexParagraphs"),
1610                                                         bformat(_("You are using at least one "
1611                                                           "layout (%1$s) intended for the title, "
1612                                                           "after using non-title layouts. This "
1613                                                           "could lead to missing or incorrect output."
1614                                                           ), layout.name()));
1615                                 }
1616                         } else if (!runparams.need_maketitle) {
1617                                 runparams.need_maketitle = true;
1618                                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
1619                                         os << "\\begin{"
1620                                                         << from_ascii(tclass.titlename())
1621                                                         << "}\n";
1622                                 }
1623                         }
1624                 } else if (runparams.need_maketitle && !runparams.have_maketitle
1625                            && !layout.inpreamble && !text.inset().isInTitle()) {
1626                         if (tclass.titletype() == TITLE_ENVIRONMENT) {
1627                                 os << "\\end{" << from_ascii(tclass.titlename())
1628                                                 << "}\n";
1629                         }
1630                         else {
1631                                 os << "\\" << from_ascii(tclass.titlename())
1632                                                 << "\n";
1633                         }
1634                         runparams.have_maketitle = true;
1635                         runparams.need_maketitle = false;
1636                 }
1637
1638                 if (layout.isCommand() && !layout.latexname().empty()
1639                     && layout.latexname() == bparams.multibib) {
1640                         if (runparams.openbtUnit)
1641                                 os << "\\end{btUnit}\n";
1642                         if (!bparams.useBiblatex()
1643                             && !buf.masterBibInfo().empty()) {
1644                                 os << '\n' << "\\begin{btUnit}\n";
1645                                 runparams.openbtUnit = true;
1646                         }
1647                 }
1648
1649                 if (!layout.isEnvironment() && par->params().leftIndent().zero()) {
1650                         // This is a standard top level paragraph, TeX it and continue.
1651                         TeXOnePar(buf, text, pit, os, runparams, everypar);
1652                         continue;
1653                 }
1654
1655                 // Do not output empty environments if the whole paragraph has
1656                 // been deleted with ct and changes are not output.
1657                 if (size_t(pit + 1) < paragraphs.size()) {
1658                         ParagraphList::const_iterator nextpar = paragraphs.iterator_at(pit + 1);
1659                         Paragraph const & cpar = paragraphs.at(pit);
1660                         if ((par->layout() != nextpar->layout()
1661                              || par->params().depth() == nextpar->params().depth()
1662                              || par->params().leftIndent() == nextpar->params().leftIndent())
1663                             && !runparams.for_search && !cpar.empty()
1664                             && cpar.isDeleted(0, cpar.size()) && !bparams.output_changes) {
1665                                 if (!cpar.parEndChange().deleted())
1666                                         os << '\n' << '\n';
1667                                 continue;
1668                         }
1669                 }
1670
1671                 TeXEnvironmentData const data =
1672                         prepareEnvironment(buf, text, par, os, runparams);
1673                 // pit can be changed in TeXEnvironment.
1674                 TeXEnvironment(buf, text, runparams, pit, os);
1675                 finishEnvironment(os, runparams, data);
1676         }
1677
1678         // FIXME: uncomment the content or remove this block
1679         if (pit == runparams.par_end) {
1680                         // Make sure that the last paragraph is
1681                         // correctly terminated (because TeXOnePar does
1682                         // not add a \n in this case)
1683                         //os << '\n';
1684         }
1685
1686         // It might be that we only have a title in this document.
1687         // But if we're in an inset, this is not the end of
1688         // the document. (There may be some other checks of this
1689         // kind that are needed.)
1690         if (runparams.need_maketitle && !runparams.have_maketitle && maintext) {
1691                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
1692                         os << "\\end{" << from_ascii(tclass.titlename())
1693                            << "}\n";
1694                 } else {
1695                         os << "\\" << from_ascii(tclass.titlename())
1696                            << "\n";
1697                 }
1698         }
1699
1700         if (maintext && !is_child && runparams.openbtUnit)
1701                 os << "\\end{btUnit}\n";
1702
1703         // if "auto end" is switched off, explicitly close the language at the end
1704         // but only if the last par is in a babel or polyglossia language
1705         Language const * const lastpar_language =
1706                         paragraphs.at(lastpit).getParLanguage(bparams);
1707         if (maintext && !lyxrc.language_auto_end && !mainlang.empty() &&
1708                 lastpar_language->encoding()->package() != Encoding::CJK) {
1709                 os << from_utf8(subst(lang_end_command,
1710                                         "$$lang",
1711                                         mainlang))
1712                         << '\n';
1713                 // If we have language_auto_begin, the stack will
1714                 // already be empty, nothing to pop()
1715                 if (using_begin_end && !lyxrc.language_auto_begin)
1716                         popLanguageName();
1717         }
1718
1719         // If the last paragraph is an environment, we'll have to close
1720         // CJK at the very end to do proper nesting.
1721         if (maintext && !is_child && state->open_encoding_ == CJK) {
1722                 os << "\\clearpage\n\\end{CJK}\n";
1723                 state->open_encoding_ = none;
1724         }
1725         // Likewise for polyglossia or when using begin/end commands
1726         // or at the very end of an active branch inset with a language switch
1727         Language const * const outer_language = (runparams.local_font != nullptr)
1728                         ? runparams.local_font->language() : bparams.language;
1729         string const & prev_lang = runparams.use_polyglossia
1730                         ? getPolyglossiaEnvName(outer_language)
1731                         : outer_language->babel();
1732         string const lastpar_lang = runparams.use_polyglossia ?
1733                 getPolyglossiaEnvName(lastpar_language): lastpar_language->babel();
1734         string const & cur_lang = openLanguageName(state);
1735         if (((runparams.inbranch && langOpenedAtThisLevel(state) && prev_lang != cur_lang)
1736              || (maintext && !is_child)) && !cur_lang.empty()) {
1737                 os << from_utf8(subst(lang_end_command,
1738                                         "$$lang",
1739                                         cur_lang))
1740                    << '\n';
1741                 if (using_begin_end)
1742                         popLanguageName();
1743         } else if (runparams.inbranch && !using_begin_end
1744                    && prev_lang != lastpar_lang && !lastpar_lang.empty()) {
1745                 // with !using_begin_end, cur_lang is empty, so we need to
1746                 // compare against the paragraph language (and we are in the
1747                 // last paragraph at this point)
1748                 os << subst(lang_begin_command, "$$lang", prev_lang) << '\n';
1749         }
1750
1751         // reset inherited encoding
1752         if (state->cjk_inherited_ > 0) {
1753                 state->cjk_inherited_ -= 1;
1754                 if (state->cjk_inherited_ == 0)
1755                         state->open_encoding_ = CJK;
1756         }
1757
1758         if (multibib_child && mparams.useBibtopic()) {
1759                 os << "\\end{btUnit}\n";
1760                 runparams.openbtUnit = false;
1761         }
1762 }
1763
1764 // Switch the input encoding for some part(s) of the document.
1765 pair<bool, int> switchEncoding(odocstream & os, BufferParams const & bparams,
1766                    OutputParams const & runparams, Encoding const & newEnc,
1767                    bool force, bool noswitchmacro)
1768 {
1769         // Never switch encoding with XeTeX/LuaTeX
1770         // or if we're in a moving argument or inherit the outer encoding.
1771         if (runparams.isFullUnicode() || newEnc.name() == "inherit")
1772                 return make_pair(false, 0);     
1773
1774         // Only switch for auto-selected legacy encodings (inputenc setting
1775         // "auto-legacy" or "auto-legacy-plain").
1776         // The "listings" environment can force a switch also with other
1777         // encoding settings (it does not support variable width encodings
1778         // (utf8, jis, ...) under 8-bit latex engines).
1779         if (!force && ((bparams.inputenc != "auto-legacy" && bparams.inputenc != "auto-legacy-plain")
1780                                    || runparams.moving_arg))
1781                 return make_pair(false, 0);
1782
1783         Encoding const & oldEnc = *runparams.encoding;
1784         // Do not switch, if the encoding is unchanged or switching is not supported.
1785         if (oldEnc.name() == newEnc.name()
1786                 || oldEnc.package() == Encoding::japanese
1787                 || oldEnc.package() == Encoding::none
1788                 || newEnc.package() == Encoding::none
1789                 || runparams.for_search)
1790                 return make_pair(false, 0);
1791         // FIXME We ignore encoding switches from/to encodings that do
1792         // neither support the inputenc package nor the CJK package here.
1793         // This may fail for characters not supported by "unicodesymbols"
1794         // or for non-ASCII characters in "listings"
1795         // but it is the best we can do.
1796
1797         // change encoding
1798         LYXERR(Debug::LATEX, "Changing LaTeX encoding from "
1799                    << oldEnc.name() << " to " << newEnc.name());
1800         os << setEncoding(newEnc.iconvName());
1801         if (bparams.inputenc == "auto-legacy-plain")
1802           return make_pair(true, 0);
1803
1804         docstring const inputenc_arg(from_ascii(newEnc.latexName()));
1805         OutputState * state = getOutputState();
1806         switch (newEnc.package()) {
1807                 case Encoding::none:
1808                 case Encoding::japanese:
1809                         // shouldn't ever reach here (see above) but avoids warning.
1810                         return make_pair(true, 0);
1811                 case Encoding::inputenc: {
1812                         size_t count = inputenc_arg.length();
1813                         if (oldEnc.package() == Encoding::CJK &&
1814                             state->open_encoding_ == CJK) {
1815                                 os << "\\end{CJK}";
1816                                 state->open_encoding_ = none;
1817                                 count += 9;
1818                         }
1819                         else if (oldEnc.package() == Encoding::inputenc &&
1820                                  state->open_encoding_ == inputenc) {
1821                                 os << "\\egroup";
1822                                 state->open_encoding_ = none;
1823                                 count += 7;
1824                         }
1825                         if (runparams.local_font != nullptr
1826                             &&  oldEnc.package() == Encoding::CJK) {
1827                                 // within insets, \inputenc switches need
1828                                 // to be embraced within \bgroup...\egroup;
1829                                 // else CJK fails.
1830                                 os << "\\bgroup";
1831                                 count += 7;
1832                                 state->open_encoding_ = inputenc;
1833                         }
1834                         if (noswitchmacro)
1835                                 return make_pair(true, count);
1836                         os << "\\inputencoding{" << inputenc_arg << '}';
1837                         return make_pair(true, count + 16);
1838                 }
1839                 case Encoding::CJK: {
1840                         size_t count = inputenc_arg.length();
1841                         if (oldEnc.package() == Encoding::CJK &&
1842                             state->open_encoding_ == CJK) {
1843                                 os << "\\end{CJK}";
1844                                 count += 9;
1845                         }
1846                         if (oldEnc.package() == Encoding::inputenc &&
1847                             state->open_encoding_ == inputenc) {
1848                                 os << "\\egroup";
1849                                 count += 7;
1850                         }
1851                         os << "\\begin{CJK}{"
1852                            << from_ascii(newEnc.latexName()) << "}{"
1853                            << from_ascii(bparams.fonts_cjk) << "}";
1854                         state->open_encoding_ = CJK;
1855                         return make_pair(true, count + 15);
1856                 }
1857         }
1858         // Dead code to avoid a warning:
1859         return make_pair(true, 0);
1860 }
1861
1862 } // namespace lyx