]> git.lyx.org Git - lyx.git/blob - src/output_latex.cpp
d281feac0a9e190b50e375aa0123f47d60fcd626
[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 "Language.h"
20 #include "LyXRC.h"
21 #include "OutputParams.h"
22 #include "Paragraph.h"
23 #include "paragraph_funcs.h"
24 #include "ParagraphParameters.h"
25 #include "TexRow.h"
26 #include "VSpace.h"
27
28 #include "insets/InsetBibitem.h"
29 #include "insets/InsetOptArg.h"
30
31 #include "support/lstrings.h"
32
33
34 namespace lyx {
35
36 using support::subst;
37
38 using std::endl;
39 using std::string;
40
41
42 namespace {
43
44 ParagraphList::const_iterator
45 TeXEnvironment(Buffer const & buf,
46                ParagraphList const & paragraphs,
47                ParagraphList::const_iterator pit,
48                odocstream & os, TexRow & texrow,
49                OutputParams const & runparams);
50
51 ParagraphList::const_iterator
52 TeXOnePar(Buffer const & buf,
53           ParagraphList const & paragraphs,
54           ParagraphList::const_iterator pit,
55           odocstream & os, TexRow & texrow,
56           OutputParams const & runparams,
57           string const & everypar = string());
58
59
60 ParagraphList::const_iterator
61 TeXDeeper(Buffer const & buf,
62           ParagraphList const & paragraphs,
63           ParagraphList::const_iterator pit,
64           odocstream & os, TexRow & texrow,
65           OutputParams const & runparams)
66 {
67         LYXERR(Debug::LATEX) << "TeXDeeper...     " << &*pit << endl;
68         ParagraphList::const_iterator par = pit;
69
70         while (par != paragraphs.end() &&
71                      par->params().depth() == pit->params().depth()) {
72                 if (par->layout()->isEnvironment()) {
73                         par = TeXEnvironment(buf, paragraphs, par,
74                                              os, texrow, runparams);
75                 } else {
76                         par = TeXOnePar(buf, paragraphs, par,
77                                              os, texrow, runparams);
78                 }
79         }
80         LYXERR(Debug::LATEX) << "TeXDeeper...done " << endl;
81
82         return par;
83 }
84
85
86 ParagraphList::const_iterator
87 TeXEnvironment(Buffer const & buf,
88                ParagraphList const & paragraphs,
89                ParagraphList::const_iterator pit,
90                odocstream & os, TexRow & texrow,
91                OutputParams const & runparams)
92 {
93         LYXERR(Debug::LATEX) << "TeXEnvironment...     " << &*pit << endl;
94
95         BufferParams const & bparams = buf.params();
96
97         Layout_ptr const & style = pit->layout();
98
99         Language const * const par_language = pit->getParLanguage(bparams);
100         Language const * const doc_language = bparams.language;
101         Language const * const prev_par_language =
102                 (pit != paragraphs.begin())
103                 ? boost::prior(pit)->getParLanguage(bparams)
104                 : doc_language;
105         if (par_language->babel() != prev_par_language->babel()) {
106
107                 if (!lyxrc.language_command_end.empty() &&
108                     prev_par_language->babel() != doc_language->babel() &&
109                     !prev_par_language->babel().empty()) {
110                         os << from_ascii(subst(
111                                 lyxrc.language_command_end,
112                                 "$$lang",
113                                 prev_par_language->babel()))
114                            << '\n';
115                         texrow.newline();
116                 }
117
118                 if (lyxrc.language_command_end.empty() ||
119                     par_language->babel() != doc_language->babel() &&
120                     !par_language->babel().empty()) {
121                         os << from_ascii(subst(
122                                 lyxrc.language_command_begin,
123                                 "$$lang",
124                                 par_language->babel()))
125                            << '\n';
126                         texrow.newline();
127                 }
128         }
129
130         bool leftindent_open = false;
131         if (!pit->params().leftIndent().zero()) {
132                 os << "\\begin{LyXParagraphLeftIndent}{"
133                    << from_ascii(pit->params().leftIndent().asLatexString())
134                    << "}\n";
135                 texrow.newline();
136                 leftindent_open = true;
137         }
138
139         if (style->isEnvironment()) {
140                 os << "\\begin{" << from_ascii(style->latexname()) << '}';
141                 if (style->optionalargs > 0) {
142                         int ret = latexOptArgInsets(buf, *pit, os, runparams,
143                                                     style->optionalargs);
144                         while (ret > 0) {
145                                 texrow.newline();
146                                 --ret;
147                         }
148                 }
149                 if (style->latextype == LATEX_LIST_ENVIRONMENT) {
150                         os << '{'
151                            << pit->params().labelWidthString()
152                            << "}\n";
153                 } else if (style->labeltype == LABEL_BIBLIO) {
154                         // ale970405
155                         os << '{' << bibitemWidest(buf) << "}\n";
156                 } else
157                         os << from_ascii(style->latexparam()) << '\n';
158                 texrow.newline();
159         }
160         ParagraphList::const_iterator par = pit;
161         do {
162                 par = TeXOnePar(buf, paragraphs, par, os, texrow, runparams);
163
164                 if (par == paragraphs.end()) {
165                         // Make sure that the last paragraph is
166                         // correctly terminated (because TeXOnePar does
167                         // not add a \n in this case)
168                         os << '\n';
169                         texrow.newline();
170                 } else if (par->params().depth() > pit->params().depth()) {
171                             if (par->layout()->isParagraph()) {
172
173                             // Thinko!
174                             // How to handle this? (Lgb)
175                             //&& !suffixIs(os, "\n\n")
176                                     //) {
177                                 // There should be at least one '\n' already
178                                 // but we need there to be two for Standard
179                                 // paragraphs that are depth-increment'ed to be
180                                 // output correctly.  However, tables can
181                                 // also be paragraphs so don't adjust them.
182                                 // ARRae
183                                 // Thinkee:
184                                 // Will it ever harm to have one '\n' too
185                                 // many? i.e. that we sometimes will have
186                                 // three in a row. (Lgb)
187                                 os << '\n';
188                                 texrow.newline();
189                         }
190                         par = TeXDeeper(buf, paragraphs, par, os, texrow,
191                                         runparams);
192                 }
193         } while (par != paragraphs.end()
194                  && par->layout() == pit->layout()
195                  && par->params().depth() == pit->params().depth()
196                  && par->params().leftIndent() == pit->params().leftIndent());
197
198         if (style->isEnvironment()) {
199                 os << "\\end{" << from_ascii(style->latexname()) << "}\n";
200                 texrow.newline();
201         }
202
203         if (leftindent_open) {
204                 os << "\\end{LyXParagraphLeftIndent}\n";
205                 texrow.newline();
206         }
207
208         if (par != paragraphs.end())
209                 LYXERR(Debug::LATEX) << "TeXEnvironment...done " << &*par << endl;
210         return par;
211 }
212
213 }
214
215
216 int latexOptArgInsets(Buffer const & buf, Paragraph const & par,
217                       odocstream & os, OutputParams const & runparams, int number)
218 {
219         int lines = 0;
220
221         InsetList::const_iterator it = par.insetlist.begin();
222         InsetList::const_iterator end = par.insetlist.end();
223         for (; it != end && number > 0 ; ++it) {
224                 if (it->inset->lyxCode() == Inset::OPTARG_CODE) {
225                         InsetOptArg * ins =
226                                 static_cast<InsetOptArg *>(it->inset);
227                         lines += ins->latexOptional(buf, os, runparams);
228                         --number;
229                 }
230         }
231         return lines;
232 }
233
234
235 namespace {
236
237 ParagraphList::const_iterator
238 TeXOnePar(Buffer const & buf,
239           ParagraphList const & paragraphs,
240           ParagraphList::const_iterator pit,
241           odocstream & os, TexRow & texrow,
242           OutputParams const & runparams_in,
243           string const & everypar)
244 {
245         LYXERR(Debug::LATEX) << "TeXOnePar...     " << &*pit << " '"
246                 << everypar << "'" << endl;
247         BufferParams const & bparams = buf.params();
248         Layout_ptr style;
249
250         // In an inset with unlimited length (all in one row),
251         // force layout to default
252         if (!pit->forceDefaultParagraphs())
253                 style = pit->layout();
254         else
255                 style = bparams.getTextClass().defaultLayout();
256
257         OutputParams runparams = runparams_in;
258         runparams.moving_arg |= style->needprotect;
259
260         Language const * const par_language = pit->getParLanguage(bparams);
261         Language const * const doc_language = bparams.language;
262         Language const * const prev_par_language =
263                 (pit != paragraphs.begin())
264                 ? boost::prior(pit)->getParLanguage(bparams)
265                 : doc_language;
266
267         if (par_language->babel() != prev_par_language->babel()
268             // check if we already put language command in TeXEnvironment()
269             && !(style->isEnvironment()
270                  && (pit == paragraphs.begin() ||
271                      (boost::prior(pit)->layout() != pit->layout() &&
272                       boost::prior(pit)->getDepth() <= pit->getDepth())
273                      || boost::prior(pit)->getDepth() < pit->getDepth())))
274         {
275                 if (!lyxrc.language_command_end.empty() &&
276                     prev_par_language->babel() != doc_language->babel() &&
277                     !prev_par_language->babel().empty())
278                 {
279                         os << from_ascii(subst(lyxrc.language_command_end,
280                                 "$$lang",
281                                 prev_par_language->babel()))
282                            << '\n';
283                         texrow.newline();
284                 }
285
286                 if (lyxrc.language_command_end.empty() ||
287                     par_language->babel() != doc_language->babel() &&
288                     !par_language->babel().empty())
289                 {
290                         os << from_ascii(subst(
291                                 lyxrc.language_command_begin,
292                                 "$$lang",
293                                 par_language->babel()))
294                            << '\n';
295                         texrow.newline();
296                 }
297         }
298
299         // Switch file encoding if necessary
300         if (bparams.inputenc == "auto") {
301                 // Look ahead for future encoding changes.
302                 // We try to output them at the beginning of the paragraph,
303                 // since the \inputencoding command is not allowed e.g. in
304                 // sections.
305                 for (pos_type i = 0; i < pit->size(); ++i) {
306                         char_type const c = pit->getChar(i);
307                         if (c < 0x80)
308                                 continue;
309                         if (pit->isInset(i))
310                                 break;
311                         // All characters before c are in the ASCII range, and
312                         // c is non-ASCII (but no inset), so change the
313                         // encoding to that required by the language of c.
314                         Encoding const * const encoding =
315                                 pit->getFontSettings(bparams, i).language()->encoding();
316                         if (switchEncoding(os, bparams, false,
317                                            *(runparams.encoding), *encoding) > 0) {
318                                 runparams.encoding = encoding;
319                                 os << '\n';
320                                 texrow.newline();
321                         }
322                         break;
323                 }
324         }
325
326         // In an inset with unlimited length (all in one row),
327         // don't allow any special options in the paragraph
328         if (!pit->forceDefaultParagraphs()) {
329                 if (pit->params().startOfAppendix()) {
330                         os << "\\appendix\n";
331                         texrow.newline();
332                 }
333
334                 if (!pit->params().spacing().isDefault()
335                         && (pit == paragraphs.begin()
336                             || !boost::prior(pit)->hasSameLayout(*pit)))
337                 {
338                         os << from_ascii(pit->params().spacing().writeEnvirBegin())
339                             << '\n';
340                         texrow.newline();
341                 }
342
343                 if (style->isCommand()) {
344                         os << '\n';
345                         texrow.newline();
346                 }
347         }
348
349         switch (style->latextype) {
350         case LATEX_COMMAND:
351                 os << '\\' << from_ascii(style->latexname());
352
353                 // Separate handling of optional argument inset.
354                 if (style->optionalargs > 0) {
355                         int ret = latexOptArgInsets(buf, *pit, os, runparams,
356                                                     style->optionalargs);
357                         while (ret > 0) {
358                                 texrow.newline();
359                                 --ret;
360                         }
361                 }
362                 else
363                         os << from_ascii(style->latexparam());
364                 break;
365         case LATEX_ITEM_ENVIRONMENT:
366         case LATEX_LIST_ENVIRONMENT:
367                 os << "\\item ";
368                 break;
369         case LATEX_BIB_ENVIRONMENT:
370                 // ignore this, the inset will write itself
371                 break;
372         default:
373                 break;
374         }
375
376         Font const outerfont =
377                 outerFont(std::distance(paragraphs.begin(), pit),
378                           paragraphs);
379
380         // FIXME UNICODE
381         os << from_utf8(everypar);
382         bool need_par = pit->simpleTeXOnePar(buf, bparams, outerfont,
383                                              os, texrow, runparams);
384
385         // Make sure that \\par is done with the font of the last
386         // character if this has another size as the default.
387         // This is necessary because LaTeX (and LyX on the screen)
388         // calculates the space between the baselines according
389         // to this font. (Matthias)
390         //
391         // Is this really needed ? (Dekel)
392         // We do not need to use to change the font for the last paragraph
393         // or for a command.
394
395         Font const font =
396                 (pit->empty()
397                  ? pit->getLayoutFont(bparams, outerfont)
398                  : pit->getFont(bparams, pit->size() - 1, outerfont));
399
400         bool is_command = style->isCommand();
401
402         if (style->resfont.size() != font.size()
403             && boost::next(pit) != paragraphs.end()
404             && !is_command) {
405                 if (!need_par)
406                         os << '{';
407                 os << "\\" << from_ascii(font.latexSize()) << " \\par}";
408         } else if (need_par) {
409                 os << "\\par}";
410         } else if (is_command)
411                 os << '}';
412
413         bool pending_newline = false;
414         switch (style->latextype) {
415         case LATEX_ITEM_ENVIRONMENT:
416         case LATEX_LIST_ENVIRONMENT:
417                 if (boost::next(pit) != paragraphs.end()
418                     && (pit->params().depth() < boost::next(pit)->params().depth()))
419                         pending_newline = true;
420                 break;
421         case LATEX_ENVIRONMENT: {
422                 // if its the last paragraph of the current environment
423                 // skip it otherwise fall through
424                 ParagraphList::const_iterator next = boost::next(pit);
425
426                 if (next != paragraphs.end()
427                     && (next->layout() != pit->layout()
428                         || next->params().depth() != pit->params().depth()))
429                         break;
430         }
431
432                 // fall through possible
433         default:
434                 // we don't need it for the last paragraph!!!
435                 if (boost::next(pit) != paragraphs.end())
436                         pending_newline = true;
437         }
438
439         if (!pit->forceDefaultParagraphs()) {
440                 if (!pit->params().spacing().isDefault()
441                         && (boost::next(pit) == paragraphs.end()
442                             || !boost::next(pit)->hasSameLayout(*pit)))
443                 {
444                         if (pending_newline) {
445                                 os << '\n';
446                                 texrow.newline();
447                         }
448                         os << from_ascii(pit->params().spacing().writeEnvirEnd());
449                         pending_newline = true;
450                 }
451         }
452
453         if (boost::next(pit) == paragraphs.end()
454             && par_language->babel() != doc_language->babel()) {
455                 // Since \selectlanguage write the language to the aux file,
456                 // we need to reset the language at the end of footnote or
457                 // float.
458
459                 if (pending_newline) {
460                         os << '\n';
461                         texrow.newline();
462                 }
463                 if (lyxrc.language_command_end.empty()) {
464                         if (!doc_language->babel().empty()) {
465                                 os << from_ascii(subst(
466                                         lyxrc.language_command_begin,
467                                         "$$lang",
468                                         doc_language->babel()));
469                                 pending_newline = true;
470                         }
471                 } else if (!par_language->babel().empty())
472                         os << from_ascii(subst(
473                                 lyxrc.language_command_end,
474                                 "$$lang",
475                                 par_language->babel()));
476                 pending_newline = true;
477         }
478
479         if (pending_newline) {
480                 os << '\n';
481                 texrow.newline();
482         }
483         runparams_in.encoding = runparams.encoding;
484
485         // we don't need it for the last paragraph!!!
486         // Note from JMarc: we will re-add a \n explicitely in
487         // TeXEnvironment, because it is needed in this case
488         if (boost::next(pit) != paragraphs.end()) {
489                 os << '\n';
490                 texrow.newline();
491         }
492
493         if (boost::next(pit) != paragraphs.end())
494                 LYXERR(Debug::LATEX) << "TeXOnePar...done " << &*boost::next(pit) << endl;
495
496         return ++pit;
497 }
498
499 } // anon namespace
500
501
502 // LaTeX all paragraphs
503 void latexParagraphs(Buffer const & buf,
504                      ParagraphList const & paragraphs,
505                      odocstream & os,
506                      TexRow & texrow,
507                      OutputParams const & runparams,
508                      string const & everypar)
509 {
510         bool was_title = false;
511         bool already_title = false;
512         TextClass const & tclass = buf.params().getTextClass();
513         ParagraphList::const_iterator par = paragraphs.begin();
514         ParagraphList::const_iterator endpar = paragraphs.end();
515
516         BOOST_ASSERT(runparams.par_begin <= runparams.par_end);
517         // if only part of the paragraphs will be outputed
518         if (runparams.par_begin !=  runparams.par_end) {
519                 par = boost::next(paragraphs.begin(), runparams.par_begin);
520                 endpar = boost::next(paragraphs.begin(), runparams.par_end);
521                 // runparams will be passed to nested paragraphs, so
522                 // we have to reset the range parameters.
523                 const_cast<OutputParams&>(runparams).par_begin = 0;
524                 const_cast<OutputParams&>(runparams).par_end = 0;
525         }
526
527         // if only_body
528         while (par != endpar) {
529                 ParagraphList::const_iterator lastpar = par;
530                 // well we have to check if we are in an inset with unlimited
531                 // length (all in one row) if that is true then we don't allow
532                 // any special options in the paragraph and also we don't allow
533                 // any environment other than the default layout of the
534                 // text class to be valid!
535                 if (!par->forceDefaultParagraphs()) {
536                         Layout_ptr const & layout = par->layout();
537
538                         if (layout->intitle) {
539                                 if (already_title) {
540                                         lyxerr << "Error in latexParagraphs: You"
541                                                 " should not mix title layouts"
542                                                 " with normal ones." << endl;
543                                 } else if (!was_title) {
544                                         was_title = true;
545                                         if (tclass.titletype() == TITLE_ENVIRONMENT) {
546                                                 os << "\\begin{"
547                                                     << from_ascii(tclass.titlename())
548                                                     << "}\n";
549                                                 texrow.newline();
550                                         }
551                                 }
552                         } else if (was_title && !already_title) {
553                                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
554                                         os << "\\end{" << from_ascii(tclass.titlename())
555                                             << "}\n";
556                                 }
557                                 else {
558                                         os << "\\" << from_ascii(tclass.titlename())
559                                             << "\n";
560                                 }
561                                 texrow.newline();
562                                 already_title = true;
563                                 was_title = false;
564                         }
565
566                         if (layout->is_environment) {
567                                 par = TeXOnePar(buf, paragraphs, par, os, texrow,
568                                                 runparams, everypar);
569                         } else if (layout->isEnvironment() ||
570                                    !par->params().leftIndent().zero()) {
571                                 par = TeXEnvironment(buf, paragraphs, par, os,
572                                                      texrow, runparams);
573                         } else {
574                                 par = TeXOnePar(buf, paragraphs, par, os, texrow,
575                                                 runparams, everypar);
576                         }
577                 } else {
578                         par = TeXOnePar(buf, paragraphs, par, os, texrow,
579                                         runparams, everypar);
580                 }
581                 if (std::distance(lastpar, par) >= std::distance(lastpar, endpar))
582                         break;
583         }
584         // It might be that we only have a title in this document
585         if (was_title && !already_title) {
586                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
587                         os << "\\end{" << from_ascii(tclass.titlename())
588                             << "}\n";
589                 }
590                 else {
591                         os << "\\" << from_ascii(tclass.titlename())
592                             << "\n";
593                                 }
594                 texrow.newline();
595         }
596 }
597
598
599 int switchEncoding(odocstream & os, BufferParams const & bparams,
600                    bool moving_arg, Encoding const & oldEnc,
601                    Encoding const & newEnc)
602 {
603         // FIXME thailatex does not support the inputenc package, so we
604         // ignore switches from/to tis620-0 encoding here. This does of
605         // course only work as long as the non-thai text contains ASCII
606         // only, but it is the best we can do.
607         // Since the \inputencoding command does not work inside sections
608         // we ignore the encoding switch also in moving arguments.
609         if (((bparams.inputenc == "auto" && !moving_arg) ||
610              bparams.inputenc == "default") &&
611             oldEnc.name() != newEnc.name() &&
612             oldEnc.name() != "ascii" && newEnc.name() != "ascii" &&
613             oldEnc.name() != "tis620-0" && newEnc.name() != "tis620-0") {
614                 LYXERR(Debug::LATEX) << "Changing LaTeX encoding from "
615                                      << oldEnc.name() << " to "
616                                      << newEnc.name() << endl;
617                 os << setEncoding(newEnc.iconvName());
618                 if (bparams.inputenc != "default") {
619                         docstring const inputenc(from_ascii(newEnc.latexName()));
620                         os << "\\inputencoding{" << inputenc << '}';
621                         return 16 + inputenc.length();
622                 }
623         }
624         return 0;
625 }
626
627 } // namespace lyx