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