]> git.lyx.org Git - lyx.git/blob - src/output_latex.cpp
152fd89e09b242b471d9210f5448572e39926a61
[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             runparams.encoding->package() == Encoding::inputenc) {
302                 // Look ahead for future encoding changes.
303                 // We try to output them at the beginning of the paragraph,
304                 // since the \inputencoding command is not allowed e.g. in
305                 // sections.
306                 for (pos_type i = 0; i < pit->size(); ++i) {
307                         char_type const c = pit->getChar(i);
308                         if (c < 0x80)
309                                 continue;
310                         if (pit->isInset(i))
311                                 break;
312                         // All characters before c are in the ASCII range, and
313                         // c is non-ASCII (but no inset), so change the
314                         // encoding to that required by the language of c.
315                         Encoding const * const encoding =
316                                 pit->getFontSettings(bparams, i).language()->encoding();
317                         if (encoding->package() == Encoding::inputenc &&
318                             switchEncoding(os, bparams, false,
319                                            *(runparams.encoding), *encoding) > 0) {
320                                 runparams.encoding = encoding;
321                                 os << '\n';
322                                 texrow.newline();
323                         }
324                         break;
325                 }
326         }
327
328         // In an inset with unlimited length (all in one row),
329         // don't allow any special options in the paragraph
330         if (!pit->forceDefaultParagraphs()) {
331                 if (pit->params().startOfAppendix()) {
332                         os << "\\appendix\n";
333                         texrow.newline();
334                 }
335
336                 if (!pit->params().spacing().isDefault()
337                         && (pit == paragraphs.begin()
338                             || !boost::prior(pit)->hasSameLayout(*pit)))
339                 {
340                         os << from_ascii(pit->params().spacing().writeEnvirBegin())
341                             << '\n';
342                         texrow.newline();
343                 }
344
345                 if (style->isCommand()) {
346                         os << '\n';
347                         texrow.newline();
348                 }
349         }
350
351         switch (style->latextype) {
352         case LATEX_COMMAND:
353                 os << '\\' << from_ascii(style->latexname());
354
355                 // Separate handling of optional argument inset.
356                 if (style->optionalargs > 0) {
357                         int ret = latexOptArgInsets(buf, *pit, os, runparams,
358                                                     style->optionalargs);
359                         while (ret > 0) {
360                                 texrow.newline();
361                                 --ret;
362                         }
363                 }
364                 else
365                         os << from_ascii(style->latexparam());
366                 break;
367         case LATEX_ITEM_ENVIRONMENT:
368         case LATEX_LIST_ENVIRONMENT:
369                 os << "\\item ";
370                 break;
371         case LATEX_BIB_ENVIRONMENT:
372                 // ignore this, the inset will write itself
373                 break;
374         default:
375                 break;
376         }
377
378         Font const outerfont =
379                 outerFont(std::distance(paragraphs.begin(), pit),
380                           paragraphs);
381
382         // FIXME UNICODE
383         os << from_utf8(everypar);
384         bool need_par = pit->simpleTeXOnePar(buf, bparams, outerfont,
385                                              os, texrow, runparams);
386
387         // Make sure that \\par is done with the font of the last
388         // character if this has another size as the default.
389         // This is necessary because LaTeX (and LyX on the screen)
390         // calculates the space between the baselines according
391         // to this font. (Matthias)
392         //
393         // Is this really needed ? (Dekel)
394         // We do not need to use to change the font for the last paragraph
395         // or for a command.
396
397         Font const font =
398                 (pit->empty()
399                  ? pit->getLayoutFont(bparams, outerfont)
400                  : pit->getFont(bparams, pit->size() - 1, outerfont));
401
402         bool is_command = style->isCommand();
403
404         if (style->resfont.size() != font.size()
405             && boost::next(pit) != paragraphs.end()
406             && !is_command) {
407                 if (!need_par)
408                         os << '{';
409                 os << "\\" << from_ascii(font.latexSize()) << " \\par}";
410         } else if (need_par) {
411                 os << "\\par}";
412         } else if (is_command)
413                 os << '}';
414
415         bool pending_newline = false;
416         switch (style->latextype) {
417         case LATEX_ITEM_ENVIRONMENT:
418         case LATEX_LIST_ENVIRONMENT:
419                 if (boost::next(pit) != paragraphs.end()
420                     && (pit->params().depth() < boost::next(pit)->params().depth()))
421                         pending_newline = true;
422                 break;
423         case LATEX_ENVIRONMENT: {
424                 // if its the last paragraph of the current environment
425                 // skip it otherwise fall through
426                 ParagraphList::const_iterator next = boost::next(pit);
427
428                 if (next != paragraphs.end()
429                     && (next->layout() != pit->layout()
430                         || next->params().depth() != pit->params().depth()))
431                         break;
432         }
433
434                 // fall through possible
435         default:
436                 // we don't need it for the last paragraph!!!
437                 if (boost::next(pit) != paragraphs.end())
438                         pending_newline = true;
439         }
440
441         if (!pit->forceDefaultParagraphs()) {
442                 if (!pit->params().spacing().isDefault()
443                         && (boost::next(pit) == paragraphs.end()
444                             || !boost::next(pit)->hasSameLayout(*pit)))
445                 {
446                         if (pending_newline) {
447                                 os << '\n';
448                                 texrow.newline();
449                         }
450                         os << from_ascii(pit->params().spacing().writeEnvirEnd());
451                         pending_newline = true;
452                 }
453         }
454
455         if (boost::next(pit) == paragraphs.end()
456             && par_language->babel() != doc_language->babel()) {
457                 // Since \selectlanguage write the language to the aux file,
458                 // we need to reset the language at the end of footnote or
459                 // float.
460
461                 if (pending_newline) {
462                         os << '\n';
463                         texrow.newline();
464                 }
465                 if (lyxrc.language_command_end.empty()) {
466                         if (!doc_language->babel().empty()) {
467                                 os << from_ascii(subst(
468                                         lyxrc.language_command_begin,
469                                         "$$lang",
470                                         doc_language->babel()));
471                                 pending_newline = true;
472                         }
473                 } else if (!par_language->babel().empty()) {
474                         os << from_ascii(subst(
475                                 lyxrc.language_command_end,
476                                 "$$lang",
477                                 par_language->babel()));
478                         pending_newline = true;
479                 }
480         }
481
482         if (pending_newline) {
483                 os << '\n';
484                 texrow.newline();
485         }
486         runparams_in.encoding = runparams.encoding;
487
488         // we don't need it for the last paragraph!!!
489         // Note from JMarc: we will re-add a \n explicitely in
490         // TeXEnvironment, because it is needed in this case
491         if (boost::next(pit) != paragraphs.end()) {
492                 os << '\n';
493                 texrow.newline();
494         }
495
496         if (boost::next(pit) != paragraphs.end())
497                 LYXERR(Debug::LATEX) << "TeXOnePar...done " << &*boost::next(pit) << endl;
498
499         return ++pit;
500 }
501
502 } // anon namespace
503
504
505 // LaTeX all paragraphs
506 void latexParagraphs(Buffer const & buf,
507                      ParagraphList const & paragraphs,
508                      odocstream & os,
509                      TexRow & texrow,
510                      OutputParams const & runparams,
511                      string const & everypar)
512 {
513         bool was_title = false;
514         bool already_title = false;
515         TextClass const & tclass = buf.params().getTextClass();
516         ParagraphList::const_iterator par = paragraphs.begin();
517         ParagraphList::const_iterator endpar = paragraphs.end();
518
519         BOOST_ASSERT(runparams.par_begin <= runparams.par_end);
520         // if only part of the paragraphs will be outputed
521         if (runparams.par_begin !=  runparams.par_end) {
522                 par = boost::next(paragraphs.begin(), runparams.par_begin);
523                 endpar = boost::next(paragraphs.begin(), runparams.par_end);
524                 // runparams will be passed to nested paragraphs, so
525                 // we have to reset the range parameters.
526                 const_cast<OutputParams&>(runparams).par_begin = 0;
527                 const_cast<OutputParams&>(runparams).par_end = 0;
528         }
529
530         // if only_body
531         while (par != endpar) {
532                 ParagraphList::const_iterator lastpar = par;
533                 // well we have to check if we are in an inset with unlimited
534                 // length (all in one row) if that is true then we don't allow
535                 // any special options in the paragraph and also we don't allow
536                 // any environment other than the default layout of the
537                 // text class to be valid!
538                 if (!par->forceDefaultParagraphs()) {
539                         Layout_ptr const & layout = par->layout();
540
541                         if (layout->intitle) {
542                                 if (already_title) {
543                                         lyxerr << "Error in latexParagraphs: You"
544                                                 " should not mix title layouts"
545                                                 " with normal ones." << endl;
546                                 } else if (!was_title) {
547                                         was_title = true;
548                                         if (tclass.titletype() == TITLE_ENVIRONMENT) {
549                                                 os << "\\begin{"
550                                                     << from_ascii(tclass.titlename())
551                                                     << "}\n";
552                                                 texrow.newline();
553                                         }
554                                 }
555                         } else if (was_title && !already_title) {
556                                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
557                                         os << "\\end{" << from_ascii(tclass.titlename())
558                                             << "}\n";
559                                 }
560                                 else {
561                                         os << "\\" << from_ascii(tclass.titlename())
562                                             << "\n";
563                                 }
564                                 texrow.newline();
565                                 already_title = true;
566                                 was_title = false;
567                         }
568
569                         if (layout->is_environment) {
570                                 par = TeXOnePar(buf, paragraphs, par, os, texrow,
571                                                 runparams, everypar);
572                         } else if (layout->isEnvironment() ||
573                                    !par->params().leftIndent().zero()) {
574                                 par = TeXEnvironment(buf, paragraphs, par, os,
575                                                      texrow, runparams);
576                         } else {
577                                 par = TeXOnePar(buf, paragraphs, par, os, texrow,
578                                                 runparams, everypar);
579                         }
580                 } else {
581                         par = TeXOnePar(buf, paragraphs, par, os, texrow,
582                                         runparams, everypar);
583                 }
584                 if (std::distance(lastpar, par) >= std::distance(lastpar, endpar))
585                         break;
586         }
587         // It might be that we only have a title in this document
588         if (was_title && !already_title) {
589                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
590                         os << "\\end{" << from_ascii(tclass.titlename())
591                             << "}\n";
592                 }
593                 else {
594                         os << "\\" << from_ascii(tclass.titlename())
595                             << "\n";
596                                 }
597                 texrow.newline();
598         }
599 }
600
601
602 int switchEncoding(odocstream & os, BufferParams const & bparams,
603                    bool moving_arg, Encoding const & oldEnc,
604                    Encoding const & newEnc)
605 {
606         if ((bparams.inputenc != "auto" || moving_arg)
607                 && bparams.inputenc != "default")
608                 return 0;
609
610         // Do nothing if the encoding is unchanged.
611         if (oldEnc.name() == newEnc.name())
612                 return 0;
613
614         // FIXME We ignore encoding switches from/to encodings that do
615         // neither support the inputenc package nor the CJK package here.
616         // This does of course only work in special cases (e.g. switch from
617         // tis620-0 to latin1, but the text in latin1 contains ASCII only,
618         // but it is the best we can do
619         if (oldEnc.package() == Encoding::none
620                 || newEnc.package() == Encoding::none)
621                 return 0;
622
623         LYXERR(Debug::LATEX) << "Changing LaTeX encoding from "
624                 << oldEnc.name() << " to "
625                 << newEnc.name() << endl;
626         os << setEncoding(newEnc.iconvName());
627         if (bparams.inputenc == "default")
628                 return 0;
629
630         docstring const inputenc(from_ascii(newEnc.latexName()));
631         switch (newEnc.package()) {
632                 case Encoding::none:
633                         return 0;
634                 case Encoding::inputenc: {
635                         int count = inputenc.length();
636                         if (oldEnc.package() == Encoding::CJK) {
637                                 os << "\\end{CJK}";
638                                 count += 9;
639                         }
640                         os << "\\inputencoding{" << inputenc << '}';
641                         return count + 16;
642                  }
643                 case Encoding::CJK: {
644                         int count = inputenc.length();
645                         if (oldEnc.package() == Encoding::CJK) {
646                                 os << "\\end{CJK}";
647                                 count += 9;
648                         }
649                         os << "\\begin{CJK}{" << inputenc << "}{}";
650                         return count + 15;
651                 }
652         }
653         // Dead code to avoid a warning:
654         return 0;
655 }
656
657 } // namespace lyx