]> git.lyx.org Git - lyx.git/blob - src/output_latex.cpp
Refine fix for bug #5096: Language switch fails in environments.
[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 "Encoding.h"
18 #include "InsetList.h"
19 #include "Language.h"
20 #include "Layout.h"
21 #include "LyXRC.h"
22 #include "OutputParams.h"
23 #include "Paragraph.h"
24 #include "paragraph_funcs.h"
25 #include "ParagraphParameters.h"
26 #include "TextClass.h"
27 #include "TexRow.h"
28 #include "VSpace.h"
29
30 #include "insets/InsetBibitem.h"
31 #include "insets/InsetOptArg.h"
32
33 #include "support/lassert.h"
34 #include "support/debug.h"
35 #include "support/lstrings.h"
36
37 #include <boost/next_prior.hpp>
38
39 using namespace std;
40 using namespace lyx::support;
41
42
43 namespace lyx {
44
45 namespace {
46
47 enum OpenEncoding {
48                 none,
49                 inputenc,
50                 CJK
51 };
52
53 static int open_encoding_ = none;
54 static int cjk_inherited_ = 0;
55
56
57 ParagraphList::const_iterator
58 TeXEnvironment(Buffer const & buf,
59                Text const & text,
60                ParagraphList::const_iterator pit,
61                odocstream & os, TexRow & texrow,
62                OutputParams const & runparams);
63
64
65 ParagraphList::const_iterator
66 TeXDeeper(Buffer const & buf,
67           Text const & text,
68           ParagraphList::const_iterator pit,
69           odocstream & os, TexRow & texrow,
70           OutputParams const & runparams)
71 {
72         LYXERR(Debug::LATEX, "TeXDeeper...     " << &*pit);
73         ParagraphList::const_iterator par = pit;
74
75         ParagraphList const & paragraphs = text.paragraphs();
76
77         while (par != paragraphs.end() &&
78                                         par->params().depth() == pit->params().depth()) {
79                 // FIXME This test should not be necessary.
80                 // We should perhaps issue an error if it is.
81                 Layout const & style = par->forcePlainLayout() ?
82                         buf.params().documentClass().plainLayout() : par->layout();
83                 if (style.isEnvironment()) {
84                         par = TeXEnvironment(buf, text, par,
85                                              os, texrow, runparams);
86                 } else {
87                         par = TeXOnePar(buf, text, par,
88                                              os, texrow, runparams);
89                 }
90         }
91         LYXERR(Debug::LATEX, "TeXDeeper...done ");
92
93         return par;
94 }
95
96
97 Language const *
98 priorLanguage(ParagraphList::const_iterator const pit,
99               ParagraphList::const_iterator const priorpit,
100               ParagraphList::const_iterator const pars_begin,
101               BufferParams const & bparams)
102 {
103         ParagraphList::const_iterator outpit = priorpit;
104         ParagraphList::const_iterator refpit = priorpit;
105
106         // We go back until the depth kept increasing or the current layout
107         // matches the reference one. When the depth decreases, we should stop
108         // only if we started from inside the enclosing environment, otherwise
109         // we should continue to go back.
110
111         bool topped = false;
112         depth_type const refdepth =
113                 priorpit->getDepth() ? priorpit->getDepth() - 1 : 0;
114         while (outpit != pars_begin
115                && (outpit->hasSameLayout(*refpit)
116                    || outpit->getDepth() > pit->getDepth())) {
117                 outpit = boost::prior(outpit);
118                 if (outpit->getDepth() == pit->getDepth()
119                     && outpit->layout() == pit->layout()
120                     && (priorpit->getAlign() != priorpit->layout().align
121                             || priorpit->getDepth() > pit->getDepth())) {
122                         // After skipping either a nested or non-default
123                         // aligned environment, we ended at an environment with
124                         // same layout and nesting depth as the current one.
125                         break;
126                 }
127                 if (outpit->getDepth() == refdepth) {
128                         if (outpit->getDepth() >= pit->getDepth()
129                             && (priorpit->getDepth() > pit->getDepth()
130                                 || outpit->layout() == refpit->layout())) {
131                                 // The prior environment was nested in
132                                 // another one, or was enclosing some other
133                                 // one, or we are still in it. In any case,
134                                 // we still have to go back.
135                                 refpit = outpit;
136                         } else {
137                                 topped = true;
138                                 break;
139                         }
140                 }
141         }
142         ParagraphList::const_iterator const inpit =
143                                         topped ? refpit : boost::next(outpit);
144         // Check if we stopped at the first paragraph,
145         // but we should still go back.
146         topped = !topped && (outpit == pars_begin
147                              && (outpit->hasSameLayout(*refpit)
148                                  || outpit->getDepth() > pit->getDepth())
149                              && !(outpit->getDepth() == pit->getDepth()
150                                   && outpit->layout() == pit->layout()
151                                   && (priorpit->getAlign() != priorpit->layout().align
152                                         || priorpit->getDepth() > pit->getDepth())));
153         Language const * const outenv_language =
154                          topped ? bparams.language
155                                 : outpit->getParLanguage(bparams);
156         Language const * const inenv_language =
157                          topped ? outpit->getParLanguage(bparams)
158                                 : inpit->getParLanguage(bparams);
159         if (outenv_language->babel() != inenv_language->babel())
160                 return inenv_language;
161         return outenv_language;
162 }
163
164
165 ParagraphList::const_iterator
166 TeXEnvironment(Buffer const & buf,
167                Text const & text,
168                ParagraphList::const_iterator pit,
169                odocstream & os, TexRow & texrow,
170                OutputParams const & runparams)
171 {
172         LYXERR(Debug::LATEX, "TeXEnvironment...     " << &*pit);
173
174         BufferParams const & bparams = buf.params();
175
176         // FIXME This test should not be necessary.
177         // We should perhaps issue an error if it is.
178         Layout const & style = pit->forcePlainLayout() ?
179                 bparams.documentClass().plainLayout() : pit->layout();
180
181         ParagraphList const & paragraphs = text.paragraphs();
182         ParagraphList::const_iterator const priorpit = 
183                 pit == paragraphs.begin() ? pit : boost::prior(pit);
184
185         Language const * const par_language = pit->getParLanguage(bparams);
186         Language const * const doc_language = bparams.language;
187         Language const * const prior_language =
188                 (pit != paragraphs.begin() && priorpit != paragraphs.begin()
189                  && priorpit->getDepth() >= pit->getDepth()
190                  && !priorpit->hasSameLayout(*pit)
191                  && !priorpit->layout().isParagraph())
192                 ? priorLanguage(pit, priorpit, paragraphs.begin(), bparams)
193                 : priorpit->getParLanguage(bparams);
194         Language const * const prev_par_language =
195                 (pit != paragraphs.begin()) ? prior_language : doc_language;
196
197         if (par_language->babel() != prev_par_language->babel()) {
198
199                 if (!lyxrc.language_command_end.empty() &&
200                     prev_par_language->babel() != doc_language->babel() &&
201                     !prev_par_language->babel().empty()) {
202                         os << from_ascii(subst(
203                                 lyxrc.language_command_end,
204                                 "$$lang",
205                                 prev_par_language->babel()))
206                            // the '%' is necessary to prevent unwanted whitespace
207                            << "%\n";
208                         texrow.newline();
209                 }
210
211                 if ((lyxrc.language_command_end.empty() ||
212                      par_language->babel() != doc_language->babel()) &&
213                     !par_language->babel().empty()) {
214                         os << from_ascii(subst(
215                                 lyxrc.language_command_begin,
216                                 "$$lang",
217                                 par_language->babel()))
218                            // the '%' is necessary to prevent unwanted whitespace
219                            << "%\n";
220                         texrow.newline();
221                 }
222         }
223
224         bool leftindent_open = false;
225         if (!pit->params().leftIndent().zero()) {
226                 os << "\\begin{LyXParagraphLeftIndent}{"
227                    << from_ascii(pit->params().leftIndent().asLatexString())
228                    << "}\n";
229                 texrow.newline();
230                 leftindent_open = true;
231         }
232
233         if (style.isEnvironment()) {
234                 os << "\\begin{" << from_ascii(style.latexname()) << '}';
235                 if (style.optionalargs > 0) {
236                         int ret = latexOptArgInsets(*pit, os, runparams,
237                                                     style.optionalargs);
238                         while (ret > 0) {
239                                 texrow.newline();
240                                 --ret;
241                         }
242                 }
243                 if (style.latextype == LATEX_LIST_ENVIRONMENT) {
244                         os << '{'
245                            << pit->params().labelWidthString()
246                            << "}\n";
247                 } else if (style.labeltype == LABEL_BIBLIO) {
248                         if (pit->params().labelWidthString().empty())
249                                 os << '{' << bibitemWidest(buf) << "}\n";
250                         else
251                                 os << '{'
252                                   << pit->params().labelWidthString()
253                                   << "}\n";
254                 } else
255                         os << from_ascii(style.latexparam()) << '\n';
256                 texrow.newline();
257         }
258
259         // in multilingual environments, the CJK tags have to be nested properly
260         bool cjk_nested = false;
261         if (par_language->encoding()->package() == Encoding::CJK &&
262             open_encoding_ != CJK && pit->isMultiLingual(bparams)) {
263                 if (prev_par_language->encoding()->package() == Encoding::CJK)
264                         os << "\\begin{CJK}{" << from_ascii(par_language->encoding()->latexName())
265                            << "}{" << from_ascii(bparams.fontsCJK) << "}%\n";
266                 open_encoding_ = CJK;
267                 cjk_nested = true;
268                 texrow.newline();
269         }
270
271         ParagraphList::const_iterator par = pit;
272         do {
273                 par = TeXOnePar(buf, text, par, os, texrow, runparams);
274
275                 if (par == paragraphs.end()) {
276                         // Make sure that the last paragraph is
277                         // correctly terminated (because TeXOnePar does
278                         // not add a \n in this case)
279                         os << '\n';
280                         texrow.newline();
281                 } else if (par->params().depth() > pit->params().depth()) {
282                         if (par->layout().isParagraph()) {
283                           // Thinko!
284                           // How to handle this? (Lgb)
285                           //&& !suffixIs(os, "\n\n")
286
287                                 // There should be at least one '\n' already
288                                 // but we need there to be two for Standard
289                                 // paragraphs that are depth-increment'ed to be
290                                 // output correctly.  However, tables can
291                                 // also be paragraphs so don't adjust them.
292                                 // ARRae
293                                 // Thinkee:
294                                 // Will it ever harm to have one '\n' too
295                                 // many? i.e. that we sometimes will have
296                                 // three in a row. (Lgb)
297                                 os << '\n';
298                                 texrow.newline();
299                         }
300                         par = TeXDeeper(buf, text, par, os, texrow,
301                                         runparams);
302                 }
303         } while (par != paragraphs.end()
304                  && par->layout() == pit->layout()
305                  && par->params().depth() == pit->params().depth()
306                  && par->params().leftIndent() == pit->params().leftIndent());
307
308         if (open_encoding_ == CJK && cjk_nested) {
309                 // We need to close the encoding even if it does not change
310                 // to do correct environment nesting
311                 os << "\\end{CJK}\n";
312                 texrow.newline();
313                 open_encoding_ = none;
314         }
315
316         if (style.isEnvironment()) {
317                 os << "\\end{" << from_ascii(style.latexname()) << "}\n";
318                 texrow.newline();
319         }
320
321         if (leftindent_open) {
322                 os << "\\end{LyXParagraphLeftIndent}\n";
323                 texrow.newline();
324         }
325
326         if (par != paragraphs.end())
327                 LYXERR(Debug::LATEX, "TeXEnvironment...done " << &*par);
328
329         return par;
330 }
331
332 } // namespace anon
333
334
335 int latexOptArgInsets(Paragraph const & par, odocstream & os,
336         OutputParams const & runparams, int number)
337 {
338         int lines = 0;
339
340         InsetList::const_iterator it = par.insetList().begin();
341         InsetList::const_iterator end = par.insetList().end();
342         for (; it != end && number > 0 ; ++it) {
343                 if (it->inset->lyxCode() == OPTARG_CODE) {
344                         InsetOptArg * ins =
345                                 static_cast<InsetOptArg *>(it->inset);
346                         lines += ins->latexOptional(os, runparams);
347                         --number;
348                 }
349         }
350         return lines;
351 }
352
353 // FIXME: this should be anonymous
354 ParagraphList::const_iterator TeXOnePar(Buffer const & buf,
355           Text const & text,
356           ParagraphList::const_iterator const pit,
357           odocstream & os, TexRow & texrow,
358           OutputParams const & runparams_in,
359           string const & everypar,
360           int start_pos, int end_pos)
361 {
362         LYXERR(Debug::LATEX, "TeXOnePar...     " << &*pit << " '"
363                 << everypar << "'");
364         BufferParams const & bparams = buf.params();
365         ParagraphList const & paragraphs = text.paragraphs();
366
367         ParagraphList::const_iterator const priorpit = 
368                 pit == paragraphs.begin() ? pit : boost::prior(pit);
369         ParagraphList::const_iterator const nextpit = 
370                 pit == paragraphs.end() ? pit : boost::next(pit);
371
372         OutputParams runparams = runparams_in;
373         runparams.isLastPar = nextpit == paragraphs.end();
374
375         if (runparams.verbatim) {
376                 int const dist = distance(paragraphs.begin(), pit);
377                 Font const outerfont = outerFont(dist, paragraphs);
378
379                 // No newline if only one paragraph in this lyxtext
380                 if (dist > 0) {
381                         os << '\n';
382                         texrow.newline();
383                 }
384
385                 /*bool need_par = */ pit->latex(bparams, outerfont,
386                         os, texrow, runparams, start_pos, end_pos);
387                 return nextpit;
388         }
389
390         // FIXME This check should not really be needed.
391         // Perhaps we should issue an error if it is.
392         Layout const style = pit->forcePlainLayout() ?
393                 bparams.documentClass().plainLayout() : pit->layout();
394
395         runparams.moving_arg |= style.needprotect;
396
397         bool const maintext = text.isMainText(buf);
398         // we are at the beginning of an inset and CJK is already open;
399         // we count inheritation levels to get the inset nesting right.
400         if (pit == paragraphs.begin() && !maintext
401             && (cjk_inherited_ > 0 || open_encoding_ == CJK)) {
402                 cjk_inherited_ += 1;
403                 open_encoding_ = none;
404         }
405
406         // This paragraph's language
407         Language const * const par_language = pit->getParLanguage(bparams);
408         // The document's language
409         Language const * const doc_language = bparams.language;
410         // The language that was in effect when the environment this paragraph is
411         // inside of was opened
412         Language const * const outer_language =
413                 (runparams.local_font != 0) ?
414                         runparams.local_font->language() : doc_language;
415
416         // The language that was in effect before the current paragraph.
417         Language const * const prior_language =
418                 (pit != paragraphs.begin() && priorpit != paragraphs.begin()
419                  && priorpit->getDepth() >= pit->getDepth()
420                  && !priorpit->hasSameLayout(*pit)
421                  && !priorpit->layout().isParagraph())
422                 ? priorLanguage(pit, priorpit, paragraphs.begin(), bparams)
423                 : priorpit->getParLanguage(bparams);
424
425         // The previous language that was in effect is the language of the
426         // previous paragraph, unless the previous paragraph is inside an
427         // environment and the current one is nested in it but with a lower
428         // depth. If there is no previous paragraph, the previous language
429         // is the outer language.
430         Language const * const prev_language =
431                 (pit != paragraphs.begin()) ? prior_language : outer_language;
432
433         if (par_language->babel() != prev_language->babel()
434             // check if we already put language command in TeXEnvironment()
435             && !(style.isEnvironment()
436                  && (pit == paragraphs.begin() ||
437                      (priorpit->layout() != pit->layout() &&
438                       priorpit->getDepth() <= pit->getDepth())
439                      || priorpit->getDepth() < pit->getDepth())))
440         {
441                 if (!lyxrc.language_command_end.empty() &&
442                     prev_language->babel() != outer_language->babel() &&
443                     !prev_language->babel().empty())
444                 {
445                         os << from_ascii(subst(lyxrc.language_command_end,
446                                 "$$lang",
447                                 prev_language->babel()))
448                            // the '%' is necessary to prevent unwanted whitespace
449                            << "%\n";
450                         texrow.newline();
451                 }
452
453                 // We need to open a new language if we couldn't close the previous
454                 // one (because there's no language_command_end); and even if we closed
455                 // the previous one, if the current language is different than the
456                 // outer_language (which is currently in effect once the previous one
457                 // is closed).
458                 if ((lyxrc.language_command_end.empty() ||
459                      par_language->babel() != outer_language->babel()) &&
460                     !par_language->babel().empty()) {
461                         // If we're inside an inset, and that inset is within an \L or \R
462                         // (or equivalents), then within the inset, too, any opposite
463                         // language paragraph should appear within an \L or \R (in addition
464                         // to, outside of, the normal language switch commands).
465                         // This behavior is not correct for ArabTeX, though.
466                         if (    // not for ArabTeX
467                                         (par_language->lang() != "arabic_arabtex" &&
468                                          outer_language->lang() != "arabic_arabtex") &&
469                                         // are we in an inset?
470                                         runparams.local_font != 0 &&
471                                         // is the inset within an \L or \R?
472                                         //
473                                         // FIXME: currently, we don't check this; this means that
474                                         // we'll have unnnecessary \L and \R commands, but that
475                                         // doesn't seem to hurt (though latex will complain)
476                                         //
477                                         // is this paragraph in the opposite direction?
478                                         runparams.local_font->isRightToLeft() !=
479                                                 par_language->rightToLeft()
480                                 ) {
481                                 // FIXME: I don't have a working copy of the Arabi package, so
482                                 // I'm not sure if the farsi and arabic_arabi stuff is correct
483                                 // or not...
484                                 if (par_language->lang() == "farsi")
485                                         os << "\\textFR{";
486                                 else if (outer_language->lang() == "farsi")
487                                         os << "\\textLR{";
488                                 else if (par_language->lang() == "arabic_arabi")
489                                         os << "\\textAR{";
490                                 else if (outer_language->lang() == "arabic_arabi")
491                                         os << "\\textLR{";
492                                 // remaining RTL languages currently is hebrew
493                                 else if (par_language->rightToLeft())
494                                         os << "\\R{";
495                                 else
496                                         os << "\\L{";
497                         }
498                         // With CJK, the CJK tag has to be closed first (see below)
499                         if (runparams.encoding->package() != Encoding::CJK) {
500                                 os << from_ascii(subst(
501                                         lyxrc.language_command_begin,
502                                         "$$lang",
503                                         par_language->babel()))
504                                    // the '%' is necessary to prevent unwanted whitespace
505                                    << "%\n";
506                                 texrow.newline();
507                         }
508                 }
509         }
510
511         // Switch file encoding if necessary; no need to do this for "default"
512         // encoding, since this only affects the position of the outputted
513         // \inputencoding command; the encoding switch will occur when necessary
514         if (bparams.inputenc == "auto" &&
515             runparams.encoding->package() != Encoding::none) {
516                 // Look ahead for future encoding changes.
517                 // We try to output them at the beginning of the paragraph,
518                 // since the \inputencoding command is not allowed e.g. in
519                 // sections.
520                 for (pos_type i = 0; i < pit->size(); ++i) {
521                         char_type const c = pit->getChar(i);
522                         Encoding const * const encoding =
523                                 pit->getFontSettings(bparams, i).language()->encoding();
524                         if (encoding->package() != Encoding::CJK &&
525                             runparams.encoding->package() == Encoding::inputenc &&
526                             c < 0x80)
527                                 continue;
528                         if (pit->isInset(i))
529                                 break;
530                         // All characters before c are in the ASCII range, and
531                         // c is non-ASCII (but no inset), so change the
532                         // encoding to that required by the language of c.
533                         // With CJK, only add switch if we have CJK content at the beginning
534                         // of the paragraph
535                         if (encoding->package() != Encoding::CJK || i == 0) {
536                                 pair<bool, int> enc_switch = switchEncoding(os, bparams, runparams,
537                                         *encoding);
538                                 // the following is necessary after a CJK environment in a multilingual
539                                 // context (nesting issue).
540                                 if (par_language->encoding()->package() == Encoding::CJK &&
541                                     open_encoding_ != CJK && cjk_inherited_ == 0) {
542                                         os << "\\begin{CJK}{" << from_ascii(par_language->encoding()->latexName())
543                                            << "}{" << from_ascii(bparams.fontsCJK) << "}%\n";
544                                         open_encoding_ = CJK;
545                                         texrow.newline();
546                                 }
547                                 if (encoding->package() != Encoding::none && enc_switch.first) {
548                                         if (enc_switch.second > 0) {
549                                                 // the '%' is necessary to prevent unwanted whitespace
550                                                 os << "%\n";
551                                                 texrow.newline();
552                                         }
553                                         // With CJK, the CJK tag had to be closed first (see above)
554                                         if (runparams.encoding->package() == Encoding::CJK) {
555                                                 os << from_ascii(subst(
556                                                         lyxrc.language_command_begin,
557                                                         "$$lang",
558                                                         par_language->babel()))
559                                                 // the '%' is necessary to prevent unwanted whitespace
560                                                 << "%\n";
561                                                 texrow.newline();
562                                         }
563                                         runparams.encoding = encoding;
564                                 }
565                                 break;
566                         }
567                 }
568         }
569
570         bool const useSetSpace = bparams.documentClass().provides("SetSpace");
571         if (pit->allowParagraphCustomization()) {
572                 if (pit->params().startOfAppendix()) {
573                         os << "\\appendix\n";
574                         texrow.newline();
575                 }
576
577                 if (!pit->params().spacing().isDefault()
578                         && (pit == paragraphs.begin()
579                             || !priorpit->hasSameLayout(*pit)))
580                 {
581                         os << from_ascii(pit->params().spacing().writeEnvirBegin(useSetSpace))
582                             << '\n';
583                         texrow.newline();
584                 }
585
586                 if (style.isCommand()) {
587                         os << '\n';
588                         texrow.newline();
589                 }
590         }
591
592         switch (style.latextype) {
593         case LATEX_COMMAND:
594                 os << '\\' << from_ascii(style.latexname());
595
596                 // Separate handling of optional argument inset.
597                 if (style.optionalargs > 0) {
598                         int ret = latexOptArgInsets(*pit, os, runparams,
599                                                     style.optionalargs);
600                         while (ret > 0) {
601                                 texrow.newline();
602                                 --ret;
603                         }
604                 }
605                 else
606                         os << from_ascii(style.latexparam());
607                 break;
608         case LATEX_ITEM_ENVIRONMENT:
609         case LATEX_LIST_ENVIRONMENT:
610                 os << "\\item ";
611                 break;
612         case LATEX_BIB_ENVIRONMENT:
613                 // ignore this, the inset will write itself
614                 break;
615         default:
616                 break;
617         }
618
619         Font const outerfont = outerFont(distance(paragraphs.begin(), pit),
620                           paragraphs);
621
622         // FIXME UNICODE
623         os << from_utf8(everypar);
624         bool need_par = pit->latex(bparams, outerfont,
625                                              os, texrow, runparams, start_pos, end_pos);
626
627         // Make sure that \\par is done with the font of the last
628         // character if this has another size as the default.
629         // This is necessary because LaTeX (and LyX on the screen)
630         // calculates the space between the baselines according
631         // to this font. (Matthias)
632         //
633         // Is this really needed ? (Dekel)
634         // We do not need to use to change the font for the last paragraph
635         // or for a command.
636
637         Font const font = pit->empty()
638                  ? pit->getLayoutFont(bparams, outerfont)
639                  : pit->getFont(bparams, pit->size() - 1, outerfont);
640
641         bool const is_command = style.isCommand();
642
643         if (style.resfont.size() != font.fontInfo().size()
644             && nextpit != paragraphs.end()
645             && !is_command) {
646                 if (!need_par)
647                         os << '{';
648                 os << "\\" << from_ascii(font.latexSize()) << " \\par}";
649         } else if (need_par) {
650                 os << "\\par}";
651         } else if (is_command)
652                 os << '}';
653
654         bool pending_newline = false;
655         switch (style.latextype) {
656         case LATEX_ITEM_ENVIRONMENT:
657         case LATEX_LIST_ENVIRONMENT:
658                 if (nextpit != paragraphs.end()
659                     && (pit->params().depth() < nextpit->params().depth()))
660                         pending_newline = true;
661                 break;
662         case LATEX_ENVIRONMENT: {
663                 // if its the last paragraph of the current environment
664                 // skip it otherwise fall through
665                 if (nextpit != paragraphs.end() && 
666                     (nextpit->layout() != pit->layout()
667                      || nextpit->params().depth() != pit->params().depth()))
668                         break;
669         }
670
671         // fall through possible
672         default:
673                 // we don't need it for the last paragraph!!!
674                 if (nextpit != paragraphs.end())
675                         pending_newline = true;
676         }
677
678         if (pit->allowParagraphCustomization()) {
679                 if (!pit->params().spacing().isDefault()
680                         && (nextpit == paragraphs.end() || !nextpit->hasSameLayout(*pit)))
681                 {
682                         if (pending_newline) {
683                                 os << '\n';
684                                 texrow.newline();
685                         }
686                         os << from_ascii(pit->params().spacing().writeEnvirEnd(useSetSpace));
687                         pending_newline = true;
688                 }
689         }
690
691         // Closing the language is needed for the last paragraph; it is also
692         // needed if we're within an \L or \R that we may have opened above (not
693         // necessarily in this paragraph) and are about to close.
694         bool closing_rtl_ltr_environment =
695                 // not for ArabTeX
696                 (par_language->lang() != "arabic_arabtex" &&
697                  outer_language->lang() != "arabic_arabtex") &&
698                 // have we opened and \L or \R environment?
699                 runparams.local_font != 0 &&
700                 runparams.local_font->isRightToLeft() != par_language->rightToLeft() &&
701                 // are we about to close the language?
702                 ((nextpit != paragraphs.end() &&
703                   par_language->babel() !=
704                     (nextpit->getParLanguage(bparams))->babel()) ||
705                   (nextpit == paragraphs.end() &&
706                     par_language->babel() != outer_language->babel()));
707
708         if (closing_rtl_ltr_environment || (nextpit == paragraphs.end()
709             && par_language->babel() != outer_language->babel())) {
710                 // Since \selectlanguage write the language to the aux file,
711                 // we need to reset the language at the end of footnote or
712                 // float.
713
714                 if (pending_newline) {
715                         os << '\n';
716                         texrow.newline();
717                 }
718                 // when the paragraph uses CJK, the language has to be closed earlier
719                 if (font.language()->encoding()->package() != Encoding::CJK) {
720                         if (lyxrc.language_command_end.empty()) {
721                                 if (!prev_language->babel().empty()) {
722                                         os << from_ascii(subst(
723                                                 lyxrc.language_command_begin,
724                                                 "$$lang",
725                                                 prev_language->babel()));
726                                         pending_newline = true;
727                                 }
728                         } else if (!par_language->babel().empty()) {
729                                 os << from_ascii(subst(
730                                         lyxrc.language_command_end,
731                                         "$$lang",
732                                         par_language->babel()));
733                                 pending_newline = true;
734                         }
735                 }
736         }
737         if (closing_rtl_ltr_environment)
738                 os << "}";
739
740         if (pending_newline) {
741                 os << '\n';
742                 texrow.newline();
743         }
744
745         // if this is a CJK-paragraph and the next isn't, close CJK
746         // also if the next paragraph is a multilingual environment (because of nesting)
747         if (nextpit != paragraphs.end() && open_encoding_ == CJK &&
748             (nextpit->getParLanguage(bparams)->encoding()->package() != Encoding::CJK ||
749              (nextpit->layout().isEnvironment() && nextpit->isMultiLingual(bparams)))
750              // inbetween environments, CJK has to be closed later (nesting!)
751              && (!style.isEnvironment() || !nextpit->layout().isEnvironment())) {
752                 os << "\\end{CJK}\n";
753                 open_encoding_ = none;
754         }
755
756         // If this is the last paragraph, close the CJK environment
757         // if necessary. If it's an environment, we'll have to \end that first.
758         if (nextpit == paragraphs.end() && !style.isEnvironment()) {
759                 switch (open_encoding_) {
760                         case CJK: {
761                                 // do nothing at the end of child documents
762                                 if (maintext && buf.masterBuffer() != &buf)
763                                         break;
764                                 // end of main text
765                                 if (maintext) {
766                                         os << '\n';
767                                         texrow.newline();
768                                         os << "\\end{CJK}\n";
769                                         texrow.newline();
770                                 // end of an inset
771                                 } else
772                                         os << "\\end{CJK}";
773                                 open_encoding_ = none;
774                                 break;
775                         }
776                         case inputenc: {
777                                 os << "\\egroup";
778                                 open_encoding_ = none;
779                                 break;
780                         }
781                         case none:
782                         default:
783                                 // do nothing
784                                 break;
785                 }
786         }
787
788         // If this is the last paragraph, and a local_font was set upon entering
789         // the inset, and we're using "auto" or "default" encoding, the encoding
790         // should be set back to that local_font's encoding.
791         // However, do not change the encoding when XeTeX is used.
792         if (nextpit == paragraphs.end() && runparams_in.local_font != 0
793             && runparams_in.encoding != runparams_in.local_font->language()->encoding()
794             && (bparams.inputenc == "auto" || bparams.inputenc == "default")
795             && (!bparams.useXetex)) {
796                 runparams_in.encoding = runparams_in.local_font->language()->encoding();
797                 os << setEncoding(runparams_in.encoding->iconvName());
798         }
799         // Otherwise, the current encoding should be set for the next paragraph.
800         else
801                 runparams_in.encoding = runparams.encoding;
802
803
804         // we don't need a newline for the last paragraph!!!
805         // Note from JMarc: we will re-add a \n explicitly in
806         // TeXEnvironment, because it is needed in this case
807         if (nextpit != paragraphs.end()) {
808                 Layout const & next_layout = nextpit->layout();
809                 if (style == next_layout
810                     // no blank lines before environments!
811                     || !next_layout.isEnvironment()
812                     // unless there's a depth change
813                     // FIXME What we really want to do here is put every \begin and \end
814                     // tag on a new line (which was not the case with nested environments).
815                     // But in the present state of play, we don't have access to the
816                     // information whether the current TeX row is empty or not.
817                     // For some ideas about how to fix this, see this thread:
818                     // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg145787.html
819                     || nextpit->params().depth() != pit->params().depth()) {
820                         os << '\n';
821                         texrow.newline();
822                 }
823         }
824
825         if (nextpit != paragraphs.end())
826                 LYXERR(Debug::LATEX, "TeXOnePar...done " << &*nextpit);
827
828         return nextpit;
829 }
830
831
832 // LaTeX all paragraphs
833 void latexParagraphs(Buffer const & buf,
834                      Text const & text,
835                      odocstream & os,
836                      TexRow & texrow,
837                      OutputParams const & runparams,
838                      string const & everypar)
839 {
840         bool was_title = false;
841         bool already_title = false;
842         BufferParams const & bparams = buf.params();
843         DocumentClass const & tclass = bparams.documentClass();
844         ParagraphList const & paragraphs = text.paragraphs();
845         ParagraphList::const_iterator par = paragraphs.begin();
846         ParagraphList::const_iterator endpar = paragraphs.end();
847
848         LASSERT(runparams.par_begin <= runparams.par_end, /**/);
849         // if only part of the paragraphs will be outputed
850         if (runparams.par_begin !=  runparams.par_end) {
851                 par = boost::next(paragraphs.begin(), runparams.par_begin);
852                 endpar = boost::next(paragraphs.begin(), runparams.par_end);
853                 // runparams will be passed to nested paragraphs, so
854                 // we have to reset the range parameters.
855                 const_cast<OutputParams&>(runparams).par_begin = 0;
856                 const_cast<OutputParams&>(runparams).par_end = 0;
857         }
858
859         bool const maintext = text.isMainText(buf);
860         bool const is_child = buf.masterBuffer() != &buf;
861
862         // Open a CJK environment at the beginning of the main buffer
863         // if the document's language is a CJK language
864         // (but not in child documents)
865         if (maintext && !is_child
866             && bparams.encoding().package() == Encoding::CJK) {
867                 os << "\\begin{CJK}{" << from_ascii(bparams.encoding().latexName())
868                 << "}{" << from_ascii(bparams.fontsCJK) << "}%\n";
869                 texrow.newline();
870                 open_encoding_ = CJK;
871         }
872         // if "auto begin" is switched off, explicitely switch the
873         // language on at start
874         if (maintext && !lyxrc.language_auto_begin &&
875             !bparams.language->babel().empty()) {
876                 // FIXME UNICODE
877                 os << from_utf8(subst(lyxrc.language_command_begin,
878                                         "$$lang",
879                                         bparams.language->babel()))
880                         << '\n';
881         texrow.newline();
882         }
883
884         ParagraphList::const_iterator lastpar;
885         // if only_body
886         while (par != endpar) {
887                 lastpar = par;
888                 // FIXME This check should not be needed. We should
889                 // perhaps issue an error if it is.
890                 Layout const & layout = par->forcePlainLayout() ?
891                                 tclass.plainLayout() : par->layout();
892
893                 if (layout.intitle) {
894                         if (already_title) {
895                                 lyxerr << "Error in latexParagraphs: You"
896                                         " should not mix title layouts"
897                                         " with normal ones." << endl;
898                         } else if (!was_title) {
899                                 was_title = true;
900                                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
901                                         os << "\\begin{"
902                                                         << from_ascii(tclass.titlename())
903                                                         << "}\n";
904                                         texrow.newline();
905                                 }
906                         }
907                 } else if (was_title && !already_title) {
908                         if (tclass.titletype() == TITLE_ENVIRONMENT) {
909                                 os << "\\end{" << from_ascii(tclass.titlename())
910                                                 << "}\n";
911                         }
912                         else {
913                                 os << "\\" << from_ascii(tclass.titlename())
914                                                 << "\n";
915                         }
916                         texrow.newline();
917                         already_title = true;
918                         was_title = false;
919                 }
920
921                 if (layout.isEnvironment() ||
922                                         !par->params().leftIndent().zero()) {
923                         par = TeXEnvironment(buf, text, par, os,
924                                                                 texrow, runparams);
925                 } else {
926                         par = TeXOnePar(buf, text, par, os, texrow,
927                                         runparams, everypar);
928                 }
929                 if (distance(lastpar, par) >= distance(lastpar, endpar))
930                         break;
931         }
932
933         // It might be that we only have a title in this document
934         if (was_title && !already_title) {
935                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
936                         os << "\\end{" << from_ascii(tclass.titlename())
937                             << "}\n";
938                 }
939                 else {
940                         os << "\\" << from_ascii(tclass.titlename())
941                             << "\n";
942                                 }
943                 texrow.newline();
944         }
945
946         // if "auto end" is switched off, explicitely close the language at the end
947         // but only if the last par is in a babel language
948         if (maintext && !lyxrc.language_auto_end && !bparams.language->babel().empty() &&
949                 lastpar->getParLanguage(bparams)->encoding()->package() != Encoding::CJK) {
950                 os << from_utf8(subst(lyxrc.language_command_end,
951                                         "$$lang",
952                                         bparams.language->babel()))
953                         << '\n';
954                 texrow.newline();
955         }
956
957         // If the last paragraph is an environment, we'll have to close
958         // CJK at the very end to do proper nesting.
959         if (maintext && !is_child && open_encoding_ == CJK) {
960                 os << "\\end{CJK}\n";
961                 texrow.newline();
962                 open_encoding_ = none;
963         }
964
965         // reset inherited encoding
966         if (cjk_inherited_ > 0) {
967                 cjk_inherited_ -= 1;
968                 if (cjk_inherited_ == 0)
969                         open_encoding_ = CJK;
970         }
971 }
972
973
974 pair<bool, int> switchEncoding(odocstream & os, BufferParams const & bparams,
975                    OutputParams const & runparams, Encoding const & newEnc,
976                    bool force)
977 {
978         Encoding const & oldEnc = *runparams.encoding;
979         bool moving_arg = runparams.moving_arg;
980         if (!force && ((bparams.inputenc != "auto" && bparams.inputenc != "default")
981                 || moving_arg))
982                 return make_pair(false, 0);
983
984         // Do nothing if the encoding is unchanged.
985         if (oldEnc.name() == newEnc.name())
986                 return make_pair(false, 0);
987
988         // FIXME We ignore encoding switches from/to encodings that do
989         // neither support the inputenc package nor the CJK package here.
990         // This does of course only work in special cases (e.g. switch from
991         // tis620-0 to latin1, but the text in latin1 contains ASCII only),
992         // but it is the best we can do
993         if (oldEnc.package() == Encoding::none
994                 || newEnc.package() == Encoding::none)
995                 return make_pair(false, 0);
996
997         LYXERR(Debug::LATEX, "Changing LaTeX encoding from "
998                 << oldEnc.name() << " to " << newEnc.name());
999         os << setEncoding(newEnc.iconvName());
1000         if (bparams.inputenc == "default")
1001                 return make_pair(true, 0);
1002
1003         docstring const inputenc_arg(from_ascii(newEnc.latexName()));
1004         switch (newEnc.package()) {
1005                 case Encoding::none:
1006                 case Encoding::japanese:
1007                         // shouldn't ever reach here, see above
1008                         return make_pair(true, 0);
1009                 case Encoding::inputenc: {
1010                         int count = inputenc_arg.length();
1011                         if (oldEnc.package() == Encoding::CJK &&
1012                             open_encoding_ == CJK) {
1013                                 os << "\\end{CJK}";
1014                                 open_encoding_ = none;
1015                                 count += 9;
1016                         }
1017                         else if (oldEnc.package() == Encoding::inputenc &&
1018                                  open_encoding_ == inputenc) {
1019                                 os << "\\egroup";
1020                                 open_encoding_ = none;
1021                                 count += 7;
1022                         }
1023                         if (runparams.local_font != 0
1024                             &&  oldEnc.package() == Encoding::CJK) {
1025                                 // within insets, \inputenc switches need
1026                                 // to be embraced within \bgroup...\egroup;
1027                                 // else CJK fails.
1028                                 os << "\\bgroup";
1029                                 count += 7;
1030                                 open_encoding_ = inputenc;
1031                         }
1032                         // with the japanese option, inputenc is omitted.
1033                         if (runparams.use_japanese)
1034                                 return make_pair(true, count);
1035                         os << "\\inputencoding{" << inputenc_arg << '}';
1036                         return make_pair(true, count + 16);
1037                 }
1038                 case Encoding::CJK: {
1039                         int count = inputenc_arg.length();
1040                         if (oldEnc.package() == Encoding::CJK &&
1041                             open_encoding_ == CJK) {
1042                                 os << "\\end{CJK}";
1043                                 count += 9;
1044                         }
1045                         if (oldEnc.package() == Encoding::inputenc &&
1046                             open_encoding_ == inputenc) {
1047                                 os << "\\egroup";
1048                                 count += 7;
1049                         }
1050                         os << "\\begin{CJK}{" << inputenc_arg << "}{"
1051                            << from_ascii(bparams.fontsCJK) << "}";
1052                         open_encoding_ = CJK;
1053                         return make_pair(true, count + 15);
1054                 }
1055         }
1056         // Dead code to avoid a warning:
1057         return make_pair(true, 0);
1058
1059 }
1060
1061 } // namespace lyx