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