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