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