]> git.lyx.org Git - features.git/blob - src/output_latex.cpp
forgot this in my last commit
[features.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
480         if (pending_newline) {
481                 os << '\n';
482                 texrow.newline();
483         }
484         runparams_in.encoding = runparams.encoding;
485
486         // we don't need it for the last paragraph!!!
487         // Note from JMarc: we will re-add a \n explicitely in
488         // TeXEnvironment, because it is needed in this case
489         if (boost::next(pit) != paragraphs.end()) {
490                 os << '\n';
491                 texrow.newline();
492         }
493
494         if (boost::next(pit) != paragraphs.end())
495                 LYXERR(Debug::LATEX) << "TeXOnePar...done " << &*boost::next(pit) << endl;
496
497         return ++pit;
498 }
499
500 } // anon namespace
501
502
503 // LaTeX all paragraphs
504 void latexParagraphs(Buffer const & buf,
505                      ParagraphList const & paragraphs,
506                      odocstream & os,
507                      TexRow & texrow,
508                      OutputParams const & runparams,
509                      string const & everypar)
510 {
511         bool was_title = false;
512         bool already_title = false;
513         TextClass const & tclass = buf.params().getTextClass();
514         ParagraphList::const_iterator par = paragraphs.begin();
515         ParagraphList::const_iterator endpar = paragraphs.end();
516
517         BOOST_ASSERT(runparams.par_begin <= runparams.par_end);
518         // if only part of the paragraphs will be outputed
519         if (runparams.par_begin !=  runparams.par_end) {
520                 par = boost::next(paragraphs.begin(), runparams.par_begin);
521                 endpar = boost::next(paragraphs.begin(), runparams.par_end);
522                 // runparams will be passed to nested paragraphs, so
523                 // we have to reset the range parameters.
524                 const_cast<OutputParams&>(runparams).par_begin = 0;
525                 const_cast<OutputParams&>(runparams).par_end = 0;
526         }
527
528         // if only_body
529         while (par != endpar) {
530                 ParagraphList::const_iterator lastpar = par;
531                 // well we have to check if we are in an inset with unlimited
532                 // length (all in one row) if that is true then we don't allow
533                 // any special options in the paragraph and also we don't allow
534                 // any environment other than the default layout of the
535                 // text class to be valid!
536                 if (!par->forceDefaultParagraphs()) {
537                         Layout_ptr const & layout = par->layout();
538
539                         if (layout->intitle) {
540                                 if (already_title) {
541                                         lyxerr << "Error in latexParagraphs: You"
542                                                 " should not mix title layouts"
543                                                 " with normal ones." << endl;
544                                 } else if (!was_title) {
545                                         was_title = true;
546                                         if (tclass.titletype() == TITLE_ENVIRONMENT) {
547                                                 os << "\\begin{"
548                                                     << from_ascii(tclass.titlename())
549                                                     << "}\n";
550                                                 texrow.newline();
551                                         }
552                                 }
553                         } else if (was_title && !already_title) {
554                                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
555                                         os << "\\end{" << from_ascii(tclass.titlename())
556                                             << "}\n";
557                                 }
558                                 else {
559                                         os << "\\" << from_ascii(tclass.titlename())
560                                             << "\n";
561                                 }
562                                 texrow.newline();
563                                 already_title = true;
564                                 was_title = false;
565                         }
566
567                         if (layout->is_environment) {
568                                 par = TeXOnePar(buf, paragraphs, par, os, texrow,
569                                                 runparams, everypar);
570                         } else if (layout->isEnvironment() ||
571                                    !par->params().leftIndent().zero()) {
572                                 par = TeXEnvironment(buf, paragraphs, par, os,
573                                                      texrow, runparams);
574                         } else {
575                                 par = TeXOnePar(buf, paragraphs, par, os, texrow,
576                                                 runparams, everypar);
577                         }
578                 } else {
579                         par = TeXOnePar(buf, paragraphs, par, os, texrow,
580                                         runparams, everypar);
581                 }
582                 if (std::distance(lastpar, par) >= std::distance(lastpar, endpar))
583                         break;
584         }
585         // It might be that we only have a title in this document
586         if (was_title && !already_title) {
587                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
588                         os << "\\end{" << from_ascii(tclass.titlename())
589                             << "}\n";
590                 }
591                 else {
592                         os << "\\" << from_ascii(tclass.titlename())
593                             << "\n";
594                                 }
595                 texrow.newline();
596         }
597 }
598
599
600 int switchEncoding(odocstream & os, BufferParams const & bparams,
601                    bool moving_arg, Encoding const & oldEnc,
602                    Encoding const & newEnc)
603 {
604         // FIXME thailatex does not support the inputenc package, so we
605         // ignore switches from/to tis620-0 encoding here. This does of
606         // course only work as long as the non-thai text contains ASCII
607         // only, but it is the best we can do.
608         // Since the \inputencoding command does not work inside sections
609         // we ignore the encoding switch also in moving arguments.
610         if (((bparams.inputenc == "auto" && !moving_arg) ||
611              bparams.inputenc == "default") &&
612             oldEnc.name() != newEnc.name() &&
613             oldEnc.name() != "ascii" && newEnc.name() != "ascii" &&
614             oldEnc.name() != "tis620-0" && newEnc.name() != "tis620-0") {
615                 LYXERR(Debug::LATEX) << "Changing LaTeX encoding from "
616                                      << oldEnc.name() << " to "
617                                      << newEnc.name() << endl;
618                 os << setEncoding(newEnc.iconvName());
619                 if (bparams.inputenc != "default") {
620                         docstring const inputenc(from_ascii(newEnc.latexName()));
621                         os << "\\inputencoding{" << inputenc << '}';
622                         return 16 + inputenc.length();
623                 }
624         }
625         return 0;
626 }
627
628 } // namespace lyx