]> git.lyx.org Git - lyx.git/blob - src/output_latex.cpp
* LaTeXFeatures.cpp (useLanguage):
[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 "Buffer.h"
16 #include "BufferParams.h"
17 #include "debug.h"
18 #include "Encoding.h"
19 #include "InsetList.h"
20 #include "Language.h"
21 #include "Layout.h"
22 #include "LyXRC.h"
23 #include "OutputParams.h"
24 #include "Paragraph.h"
25 #include "paragraph_funcs.h"
26 #include "ParagraphParameters.h"
27 #include "TextClass.h"
28 #include "TexRow.h"
29 #include "VSpace.h"
30
31 #include "insets/InsetBibitem.h"
32 #include "insets/InsetOptArg.h"
33
34 #include "support/lstrings.h"
35
36 #include <boost/next_prior.hpp>
37
38 namespace lyx {
39
40 using support::subst;
41
42 using std::endl;
43 using std::string;
44 using std::pair;
45 using std::make_pair;
46
47
48 namespace {
49
50 ParagraphList::const_iterator
51 TeXEnvironment(Buffer const & buf,
52                ParagraphList const & paragraphs,
53                ParagraphList::const_iterator pit,
54                odocstream & os, TexRow & texrow,
55                OutputParams const & runparams);
56
57 ParagraphList::const_iterator
58 TeXOnePar(Buffer const & buf,
59           ParagraphList const & paragraphs,
60           ParagraphList::const_iterator pit,
61           odocstream & os, TexRow & texrow,
62           OutputParams const & runparams,
63           string const & everypar = string());
64
65
66 ParagraphList::const_iterator
67 TeXDeeper(Buffer const & buf,
68           ParagraphList const & paragraphs,
69           ParagraphList::const_iterator pit,
70           odocstream & os, TexRow & texrow,
71           OutputParams const & runparams)
72 {
73         LYXERR(Debug::LATEX, "TeXDeeper...     " << &*pit);
74         ParagraphList::const_iterator par = pit;
75
76         while (par != paragraphs.end() &&
77                      par->params().depth() == pit->params().depth()) {
78                 if (par->layout()->isEnvironment()) {
79                         par = TeXEnvironment(buf, paragraphs, par,
80                                              os, texrow, runparams);
81                 } else {
82                         par = TeXOnePar(buf, paragraphs, par,
83                                              os, texrow, runparams);
84                 }
85         }
86         LYXERR(Debug::LATEX, "TeXDeeper...done ");
87
88         return par;
89 }
90
91
92 ParagraphList::const_iterator
93 TeXEnvironment(Buffer const & buf,
94                ParagraphList const & paragraphs,
95                ParagraphList::const_iterator pit,
96                odocstream & os, TexRow & texrow,
97                OutputParams const & runparams)
98 {
99         LYXERR(Debug::LATEX, "TeXEnvironment...     " << &*pit);
100
101         BufferParams const & bparams = buf.params();
102
103         LayoutPtr const & style = pit->layout();
104
105         Language const * const par_language = pit->getParLanguage(bparams);
106         Language const * const doc_language = bparams.language;
107         Language const * const prev_par_language =
108                 (pit != paragraphs.begin())
109                 ? boost::prior(pit)->getParLanguage(bparams)
110                 : doc_language;
111         if (par_language->babel() != prev_par_language->babel()) {
112
113                 if (!lyxrc.language_command_end.empty() &&
114                     prev_par_language->babel() != doc_language->babel() &&
115                     !prev_par_language->babel().empty()) {
116                         os << from_ascii(subst(
117                                 lyxrc.language_command_end,
118                                 "$$lang",
119                                 prev_par_language->babel()))
120                            << '\n';
121                         texrow.newline();
122                 }
123
124                 if ((lyxrc.language_command_end.empty() ||
125                      par_language->babel() != doc_language->babel()) &&
126                     !par_language->babel().empty()) {
127                         os << from_ascii(subst(
128                                 lyxrc.language_command_begin,
129                                 "$$lang",
130                                 par_language->babel()))
131                            << '\n';
132                         texrow.newline();
133                 }
134         }
135
136         bool leftindent_open = false;
137         if (!pit->params().leftIndent().zero()) {
138                 os << "\\begin{LyXParagraphLeftIndent}{"
139                    << from_ascii(pit->params().leftIndent().asLatexString())
140                    << "}\n";
141                 texrow.newline();
142                 leftindent_open = true;
143         }
144
145         if (style->isEnvironment()) {
146                 os << "\\begin{" << from_ascii(style->latexname()) << '}';
147                 if (style->optionalargs > 0) {
148                         int ret = latexOptArgInsets(buf, *pit, os, runparams,
149                                                     style->optionalargs);
150                         while (ret > 0) {
151                                 texrow.newline();
152                                 --ret;
153                         }
154                 }
155                 if (style->latextype == LATEX_LIST_ENVIRONMENT) {
156                         os << '{'
157                            << pit->params().labelWidthString()
158                            << "}\n";
159                 } else if (style->labeltype == LABEL_BIBLIO) {
160                         // ale970405
161                         os << '{' << bibitemWidest(buf) << "}\n";
162                 } else
163                         os << from_ascii(style->latexparam()) << '\n';
164                 texrow.newline();
165         }
166         ParagraphList::const_iterator par = pit;
167         do {
168                 par = TeXOnePar(buf, paragraphs, par, os, texrow, runparams);
169
170                 if (par == paragraphs.end()) {
171                         // Make sure that the last paragraph is
172                         // correctly terminated (because TeXOnePar does
173                         // not add a \n in this case)
174                         os << '\n';
175                         texrow.newline();
176                 } else if (par->params().depth() > pit->params().depth()) {
177                         if (par->layout()->isParagraph()) {
178                           // Thinko!
179                           // How to handle this? (Lgb)
180                           //&& !suffixIs(os, "\n\n")
181                                   //) {
182
183                                 // There should be at least one '\n' already
184                                 // but we need there to be two for Standard
185                                 // paragraphs that are depth-increment'ed to be
186                                 // output correctly.  However, tables can
187                                 // also be paragraphs so don't adjust them.
188                                 // ARRae
189                                 // Thinkee:
190                                 // Will it ever harm to have one '\n' too
191                                 // many? i.e. that we sometimes will have
192                                 // three in a row. (Lgb)
193                                 os << '\n';
194                                 texrow.newline();
195                         }
196                         par = TeXDeeper(buf, paragraphs, par, os, texrow,
197                                         runparams);
198                 }
199         } while (par != paragraphs.end()
200                  && par->layout() == pit->layout()
201                  && par->params().depth() == pit->params().depth()
202                  && par->params().leftIndent() == pit->params().leftIndent());
203
204         if (style->isEnvironment()) {
205                 os << "\\end{" << from_ascii(style->latexname()) << "}\n";
206                 texrow.newline();
207         }
208
209         if (leftindent_open) {
210                 os << "\\end{LyXParagraphLeftIndent}\n";
211                 texrow.newline();
212         }
213
214         if (par != paragraphs.end())
215                 LYXERR(Debug::LATEX, "TeXEnvironment...done " << &*par);
216
217         return par;
218 }
219
220 }
221
222
223 int latexOptArgInsets(Buffer const & buf, Paragraph const & par,
224                       odocstream & os, OutputParams const & runparams, int number)
225 {
226         int lines = 0;
227
228         InsetList::const_iterator it = par.insetList().begin();
229         InsetList::const_iterator end = par.insetList().end();
230         for (; it != end && number > 0 ; ++it) {
231                 if (it->inset->lyxCode() == OPTARG_CODE) {
232                         InsetOptArg * ins =
233                                 static_cast<InsetOptArg *>(it->inset);
234                         lines += ins->latexOptional(buf, os, runparams);
235                         --number;
236                 }
237         }
238         return lines;
239 }
240
241
242 namespace {
243
244 ParagraphList::const_iterator
245 TeXOnePar(Buffer const & buf,
246           ParagraphList const & paragraphs,
247           ParagraphList::const_iterator pit,
248           odocstream & os, TexRow & texrow,
249           OutputParams const & runparams_in,
250           string const & everypar)
251 {
252         LYXERR(Debug::LATEX, "TeXOnePar...     " << &*pit << " '"
253                 << everypar << "'");
254         BufferParams const & bparams = buf.params();
255         LayoutPtr style;
256
257         if (runparams_in.verbatim) {
258                 int const dist = std::distance(paragraphs.begin(), pit);
259                 Font const outerfont = outerFont(dist, paragraphs);
260
261                 // No newline if only one paragraph in this lyxtext
262                 if (dist > 0) {
263                         os << '\n';
264                         texrow.newline();
265                 }
266
267                 /*bool need_par = */ pit->latex(buf, bparams, outerfont,
268                         os, texrow, runparams_in);
269
270                 return ++pit;
271         }
272
273         // In an inset with unlimited length (all in one row),
274         // force layout to default
275         if (!pit->forceDefaultParagraphs())
276                 style = pit->layout();
277         else
278                 style = bparams.getTextClass().defaultLayout();
279
280         OutputParams runparams = runparams_in;
281         runparams.moving_arg |= style->needprotect;
282
283         // This paragraph's language
284         Language const * const par_language = pit->getParLanguage(bparams);
285         // The document's language
286         Language const * const doc_language = bparams.language;
287         // The language that was in effect when the environemnt this paragraph is 
288         // inside of was opened
289         Language const * const outer_language = 
290                 (runparams.local_font != 0) ?
291                         runparams.local_font->language() : doc_language;
292         // The previous language that was in effect is either the language of
293         // the previous paragraph, if there is one, or else the outer language
294         // if there is no previous paragraph
295         Language const * const prev_language =
296                 (pit != paragraphs.begin()) ?
297                         boost::prior(pit)->getParLanguage(bparams) : outer_language;
298
299         if (par_language->babel() != prev_language->babel()
300             // check if we already put language command in TeXEnvironment()
301             && !(style->isEnvironment()
302                  && (pit == paragraphs.begin() ||
303                      (boost::prior(pit)->layout() != pit->layout() &&
304                       boost::prior(pit)->getDepth() <= pit->getDepth())
305                      || boost::prior(pit)->getDepth() < pit->getDepth())))
306         {
307                 if (!lyxrc.language_command_end.empty() &&
308                     prev_language->babel() != outer_language->babel() &&
309                     !prev_language->babel().empty())
310                 {
311                         os << from_ascii(subst(lyxrc.language_command_end,
312                                 "$$lang",
313                                 prev_language->babel()))
314                            << '\n';
315                         texrow.newline();
316                 }
317
318                 // We need to open a new language if we couldn't close the previous 
319                 // one (because there's no language_command_end); and even if we closed
320                 // the previous one, if the current language is different than the
321                 // outer_language (which is currently in effect once the previous one
322                 // is closed).
323                 if ((lyxrc.language_command_end.empty() ||
324                      par_language->babel() != outer_language->babel()) &&
325                     !par_language->babel().empty()) {
326                         // If we're inside an inset, and that inset is within an \L or \R
327                         // (or equivalents), then within the inset, too, any opposite
328                         // language paragraph should appear within an \L or \R (in addition
329                         // to, outside of, the normal language switch commands).
330                         // This behavior is not correct for ArabTeX, though.
331                         if (    // not for ArabTeX
332                                         (par_language->lang() != "arabic_arabtex" &&
333                                          outer_language->lang() != "arabic_arabtex") &&
334                                         // are we in an inset?
335                                         runparams.local_font != 0 &&
336                                         // is the inset within an \L or \R?
337                                         // 
338                                         // FIXME: currently, we don't check this; this means that
339                                         // we'll have unnnecessary \L and \R commands, but that 
340                                         // doesn't seem to hurt (though latex will complain)
341                                         // 
342                                         // is this paragraph in the opposite direction?
343                                         runparams.local_font->isRightToLeft() !=
344                                                 par_language->rightToLeft()
345                                 ) {
346                                 // FIXME: I don't have a working copy of the Arabi package, so
347                                 // I'm not sure if the farsi and arabic_arabi stuff is correct
348                                 // or not...
349                                 if (par_language->lang() == "farsi")
350                                         os << "\\textFR{";
351                                 else if (outer_language->lang() == "farsi")
352                                         os << "\\textLR{";
353                                 else if (par_language->lang() == "arabic_arabi")
354                                         os << "\\textAR{";
355                                 else if (outer_language->lang() == "arabic_arabi")
356                                         os << "\\textLR{";
357                                 // remaining RTL languages currently is hebrew
358                                 else if (par_language->rightToLeft())
359                                         os << "\\R{";
360                                 else
361                                         os << "\\L{";
362                         }
363                         os << from_ascii(subst(
364                                 lyxrc.language_command_begin,
365                                 "$$lang",
366                                 par_language->babel()))
367                            << '\n';
368                         texrow.newline();
369                 }
370         }
371
372         // Switch file encoding if necessary; no need to do this for "default"
373         // encoding, since this only affects the position of the outputted
374         // \inputencoding command; the encoding switch will occur when necessary
375         if (bparams.inputenc == "auto" &&
376             runparams.encoding->package() == Encoding::inputenc) {
377                 // Look ahead for future encoding changes.
378                 // We try to output them at the beginning of the paragraph,
379                 // since the \inputencoding command is not allowed e.g. in
380                 // sections.
381                 for (pos_type i = 0; i < pit->size(); ++i) {
382                         char_type const c = pit->getChar(i);
383                         if (c < 0x80)
384                                 continue;
385                         if (pit->isInset(i))
386                                 break;
387                         // All characters before c are in the ASCII range, and
388                         // c is non-ASCII (but no inset), so change the
389                         // encoding to that required by the language of c.
390                         Encoding const * const encoding =
391                                 pit->getFontSettings(bparams, i).language()->encoding();
392                         pair<bool, int> enc_switch = switchEncoding(os, bparams, false,
393                                         *(runparams.encoding), *encoding);
394                         if (encoding->package() == Encoding::inputenc && enc_switch.first) {
395                                 runparams.encoding = encoding;
396                                 if (enc_switch.second > 0) {
397                                         os << '\n';
398                                         texrow.newline();
399                                 }
400                         }
401                         break;
402                 }
403         }
404
405         // In an inset with unlimited length (all in one row),
406         // don't allow any special options in the paragraph
407         if (!pit->forceDefaultParagraphs()) {
408                 if (pit->params().startOfAppendix()) {
409                         os << "\\appendix\n";
410                         texrow.newline();
411                 }
412
413                 if (!pit->params().spacing().isDefault()
414                         && (pit == paragraphs.begin()
415                             || !boost::prior(pit)->hasSameLayout(*pit)))
416                 {
417                         os << from_ascii(pit->params().spacing().writeEnvirBegin())
418                             << '\n';
419                         texrow.newline();
420                 }
421
422                 if (style->isCommand()) {
423                         os << '\n';
424                         texrow.newline();
425                 }
426         }
427
428         switch (style->latextype) {
429         case LATEX_COMMAND:
430                 os << '\\' << from_ascii(style->latexname());
431
432                 // Separate handling of optional argument inset.
433                 if (style->optionalargs > 0) {
434                         int ret = latexOptArgInsets(buf, *pit, os, runparams,
435                                                     style->optionalargs);
436                         while (ret > 0) {
437                                 texrow.newline();
438                                 --ret;
439                         }
440                 }
441                 else
442                         os << from_ascii(style->latexparam());
443                 break;
444         case LATEX_ITEM_ENVIRONMENT:
445         case LATEX_LIST_ENVIRONMENT:
446                 os << "\\item ";
447                 break;
448         case LATEX_BIB_ENVIRONMENT:
449                 // ignore this, the inset will write itself
450                 break;
451         default:
452                 break;
453         }
454
455         Font const outerfont =
456                 outerFont(std::distance(paragraphs.begin(), pit),
457                           paragraphs);
458
459         // FIXME UNICODE
460         os << from_utf8(everypar);
461         bool need_par = pit->latex(buf, bparams, outerfont,
462                                              os, texrow, runparams);
463
464         // Make sure that \\par is done with the font of the last
465         // character if this has another size as the default.
466         // This is necessary because LaTeX (and LyX on the screen)
467         // calculates the space between the baselines according
468         // to this font. (Matthias)
469         //
470         // Is this really needed ? (Dekel)
471         // We do not need to use to change the font for the last paragraph
472         // or for a command.
473
474         Font const font =
475                 (pit->empty()
476                  ? pit->getLayoutFont(bparams, outerfont)
477                  : pit->getFont(bparams, pit->size() - 1, outerfont));
478
479         bool is_command = style->isCommand();
480
481         if (style->resfont.size() != font.fontInfo().size()
482             && boost::next(pit) != paragraphs.end()
483             && !is_command) {
484                 if (!need_par)
485                         os << '{';
486                 os << "\\" << from_ascii(font.latexSize()) << " \\par}";
487         } else if (need_par) {
488                 os << "\\par}";
489         } else if (is_command)
490                 os << '}';
491
492         bool pending_newline = false;
493         switch (style->latextype) {
494         case LATEX_ITEM_ENVIRONMENT:
495         case LATEX_LIST_ENVIRONMENT:
496                 if (boost::next(pit) != paragraphs.end()
497                     && (pit->params().depth() < boost::next(pit)->params().depth()))
498                         pending_newline = true;
499                 break;
500         case LATEX_ENVIRONMENT: {
501                 // if its the last paragraph of the current environment
502                 // skip it otherwise fall through
503                 ParagraphList::const_iterator next = boost::next(pit);
504
505                 if (next != paragraphs.end()
506                     && (next->layout() != pit->layout()
507                         || next->params().depth() != pit->params().depth()))
508                         break;
509         }
510
511                 // fall through possible
512         default:
513                 // we don't need it for the last paragraph!!!
514                 if (boost::next(pit) != paragraphs.end())
515                         pending_newline = true;
516         }
517
518         if (!pit->forceDefaultParagraphs()) {
519                 if (!pit->params().spacing().isDefault()
520                         && (boost::next(pit) == paragraphs.end()
521                             || !boost::next(pit)->hasSameLayout(*pit)))
522                 {
523                         if (pending_newline) {
524                                 os << '\n';
525                                 texrow.newline();
526                         }
527                         os << from_ascii(pit->params().spacing().writeEnvirEnd());
528                         pending_newline = true;
529                 }
530         }
531
532         // Closing the language is needed for the last paragraph; it is also
533         // needed if we're within an \L or \R that we may have opened above (not
534         // necessarily in this paragraph) and are about to close.
535         bool closing_rtl_ltr_environment = 
536                 // not for ArabTeX
537                 (par_language->lang() != "arabic_arabtex" &&
538                  outer_language->lang() != "arabic_arabtex") &&
539                 // have we opened and \L or \R environment?
540                 runparams.local_font != 0 &&
541                 runparams.local_font->isRightToLeft() != par_language->rightToLeft() &&
542                 // are we about to close the language?
543                 ((boost::next(pit) != paragraphs.end() &&
544                   par_language->babel() != 
545                         (boost::next(pit)->getParLanguage(bparams))->babel()) ||
546                  (boost::next(pit) == paragraphs.end() &&
547                   par_language->babel() != outer_language->babel()));
548
549         if (closing_rtl_ltr_environment || (boost::next(pit) == paragraphs.end()
550             && par_language->babel() != outer_language->babel())) {
551                 // Since \selectlanguage write the language to the aux file,
552                 // we need to reset the language at the end of footnote or
553                 // float.
554
555                 if (pending_newline) {
556                         os << '\n';
557                         texrow.newline();
558                 }
559                 if (lyxrc.language_command_end.empty()) {
560                         if (!prev_language->babel().empty()) {
561                                 os << from_ascii(subst(
562                                         lyxrc.language_command_begin,
563                                         "$$lang",
564                                         prev_language->babel()));
565                                 pending_newline = true;
566                         }
567                 } else if (!par_language->babel().empty()) {
568                         os << from_ascii(subst(
569                                 lyxrc.language_command_end,
570                                 "$$lang",
571                                 par_language->babel()));
572                         pending_newline = true;
573                 }
574         }
575         if (closing_rtl_ltr_environment)
576                 os << "}";
577
578         if (pending_newline) {
579                 os << '\n';
580                 texrow.newline();
581         }
582
583         // If this is the last paragraph, and a local_font was set upon entering
584         // the inset, the encoding should be set back to that local_font's 
585         // encoding. We don't use switchEncoding(), because no explicit encoding
586         // switch command is needed, since latex will automatically revert to it
587         // when this inset closes.
588         // This switch is only necessary if we're using "auto" or "default" 
589         // encoding. 
590         if (boost::next(pit) == paragraphs.end() && runparams_in.local_font != 0) {
591                 runparams_in.encoding = runparams_in.local_font->language()->encoding();
592                 if (bparams.inputenc == "auto" || bparams.inputenc == "default")
593                         os << setEncoding(runparams_in.encoding->iconvName());
594
595         }
596         // Otherwise, the current encoding should be set for the next paragraph.
597         else
598                 runparams_in.encoding = runparams.encoding;
599
600
601         // we don't need it for the last paragraph!!!
602         // Note from JMarc: we will re-add a \n explicitely in
603         // TeXEnvironment, because it is needed in this case
604         if (boost::next(pit) != paragraphs.end()) {
605                 os << '\n';
606                 texrow.newline();
607         }
608
609         if (boost::next(pit) != paragraphs.end())
610                 LYXERR(Debug::LATEX, "TeXOnePar...done " << &*boost::next(pit));
611
612         return ++pit;
613 }
614
615 } // anon namespace
616
617
618 // LaTeX all paragraphs
619 void latexParagraphs(Buffer const & buf,
620                      ParagraphList const & paragraphs,
621                      odocstream & os,
622                      TexRow & texrow,
623                      OutputParams const & runparams,
624                      string const & everypar)
625 {
626         bool was_title = false;
627         bool already_title = false;
628         TextClass const & tclass = buf.params().getTextClass();
629         ParagraphList::const_iterator par = paragraphs.begin();
630         ParagraphList::const_iterator endpar = paragraphs.end();
631
632         BOOST_ASSERT(runparams.par_begin <= runparams.par_end);
633         // if only part of the paragraphs will be outputed
634         if (runparams.par_begin !=  runparams.par_end) {
635                 par = boost::next(paragraphs.begin(), runparams.par_begin);
636                 endpar = boost::next(paragraphs.begin(), runparams.par_end);
637                 // runparams will be passed to nested paragraphs, so
638                 // we have to reset the range parameters.
639                 const_cast<OutputParams&>(runparams).par_begin = 0;
640                 const_cast<OutputParams&>(runparams).par_end = 0;
641         }
642
643         // if only_body
644         while (par != endpar) {
645                 ParagraphList::const_iterator lastpar = par;
646                 // well we have to check if we are in an inset with unlimited
647                 // length (all in one row) if that is true then we don't allow
648                 // any special options in the paragraph and also we don't allow
649                 // any environment other than the default layout of the
650                 // text class to be valid!
651                 if (!par->forceDefaultParagraphs()) {
652                         LayoutPtr const & layout = par->layout();
653
654                         if (layout->intitle) {
655                                 if (already_title) {
656                                         lyxerr << "Error in latexParagraphs: You"
657                                                 " should not mix title layouts"
658                                                 " with normal ones." << endl;
659                                 } else if (!was_title) {
660                                         was_title = true;
661                                         if (tclass.titletype() == TITLE_ENVIRONMENT) {
662                                                 os << "\\begin{"
663                                                     << from_ascii(tclass.titlename())
664                                                     << "}\n";
665                                                 texrow.newline();
666                                         }
667                                 }
668                         } else if (was_title && !already_title) {
669                                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
670                                         os << "\\end{" << from_ascii(tclass.titlename())
671                                             << "}\n";
672                                 }
673                                 else {
674                                         os << "\\" << from_ascii(tclass.titlename())
675                                             << "\n";
676                                 }
677                                 texrow.newline();
678                                 already_title = true;
679                                 was_title = false;
680                         }
681
682                         if (layout->is_environment) {
683                                 par = TeXOnePar(buf, paragraphs, par, os, texrow,
684                                                 runparams, everypar);
685                         } else if (layout->isEnvironment() ||
686                                    !par->params().leftIndent().zero()) {
687                                 par = TeXEnvironment(buf, paragraphs, par, os,
688                                                      texrow, runparams);
689                         } else {
690                                 par = TeXOnePar(buf, paragraphs, par, os, texrow,
691                                                 runparams, everypar);
692                         }
693                 } else {
694                         par = TeXOnePar(buf, paragraphs, par, os, texrow,
695                                         runparams, everypar);
696                 }
697                 if (std::distance(lastpar, par) >= std::distance(lastpar, endpar))
698                         break;
699         }
700         // It might be that we only have a title in this document
701         if (was_title && !already_title) {
702                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
703                         os << "\\end{" << from_ascii(tclass.titlename())
704                             << "}\n";
705                 }
706                 else {
707                         os << "\\" << from_ascii(tclass.titlename())
708                             << "\n";
709                                 }
710                 texrow.newline();
711         }
712 }
713
714
715 pair<bool, int> switchEncoding(odocstream & os, BufferParams const & bparams,
716                    bool moving_arg, Encoding const & oldEnc,
717                    Encoding const & newEnc)
718 {
719         if ((bparams.inputenc != "auto" && bparams.inputenc != "default")
720                 || moving_arg)
721                 return make_pair(false, 0);
722
723         // Do nothing if the encoding is unchanged.
724         if (oldEnc.name() == newEnc.name())
725                 return make_pair(false, 0);
726
727         // FIXME We ignore encoding switches from/to encodings that do
728         // neither support the inputenc package nor the CJK package here.
729         // This does of course only work in special cases (e.g. switch from
730         // tis620-0 to latin1, but the text in latin1 contains ASCII only),
731         // but it is the best we can do
732         if (oldEnc.package() == Encoding::none
733                 || newEnc.package() == Encoding::none)
734                 return make_pair(false, 0);
735
736         LYXERR(Debug::LATEX, "Changing LaTeX encoding from "
737                 << oldEnc.name() << " to " << newEnc.name());
738         os << setEncoding(newEnc.iconvName());
739         if (bparams.inputenc == "default")
740                 return make_pair(true, 0);
741
742         docstring const inputenc = from_ascii(newEnc.latexName());
743         switch (newEnc.package()) {
744                 case Encoding::none:
745                         // shouldn't ever reach here, see above
746                         return make_pair(true, 0);
747                 case Encoding::inputenc: {
748                         int count = inputenc.length();
749                         if (oldEnc.package() == Encoding::CJK) {
750                                 os << "\\end{CJK}";
751                                 count += 9;
752                         }
753                         os << "\\inputencoding{" << inputenc << '}';
754                         return make_pair(true, count + 16);
755                 }
756                 case Encoding::CJK: {
757                         int count = inputenc.length();
758                         if (oldEnc.package() == Encoding::CJK) {
759                                 os << "\\end{CJK}";
760                                 count += 9;
761                         }
762                         os << "\\begin{CJK}{" << inputenc << "}{}";
763                         return make_pair(true, count + 15);
764                 }
765         }
766         // Dead code to avoid a warning:
767         return make_pair(true, 0);
768 }
769
770 } // namespace lyx