]> git.lyx.org Git - lyx.git/blob - src/output_latex.cpp
remove unused CursorSlice::invalidate & isValid methods
[lyx.git] / src / output_latex.cpp
1 /**
2  * \file output_latex.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "output_latex.h"
14
15 #include "Buffer.h"
16 #include "BufferParams.h"
17 #include "debug.h"
18 #include "Encoding.h"
19 #include "Language.h"
20 #include "LyXRC.h"
21 #include "OutputParams.h"
22 #include "Paragraph.h"
23 #include "paragraph_funcs.h"
24 #include "ParagraphParameters.h"
25 #include "TexRow.h"
26 #include "VSpace.h"
27
28 #include "insets/InsetBibitem.h"
29 #include "insets/InsetOptArg.h"
30
31 #include "support/lstrings.h"
32
33
34 namespace lyx {
35
36 using support::subst;
37
38 using std::endl;
39 using std::string;
40
41
42 namespace {
43
44 ParagraphList::const_iterator
45 TeXEnvironment(Buffer const & buf,
46                ParagraphList const & paragraphs,
47                ParagraphList::const_iterator pit,
48                odocstream & os, TexRow & texrow,
49                OutputParams const & runparams);
50
51 ParagraphList::const_iterator
52 TeXOnePar(Buffer const & buf,
53           ParagraphList const & paragraphs,
54           ParagraphList::const_iterator pit,
55           odocstream & os, TexRow & texrow,
56           OutputParams const & runparams,
57           string const & everypar = string());
58
59
60 ParagraphList::const_iterator
61 TeXDeeper(Buffer const & buf,
62           ParagraphList const & paragraphs,
63           ParagraphList::const_iterator pit,
64           odocstream & os, TexRow & texrow,
65           OutputParams const & runparams)
66 {
67         LYXERR(Debug::LATEX) << "TeXDeeper...     " << &*pit << endl;
68         ParagraphList::const_iterator par = pit;
69
70         while (par != paragraphs.end() &&
71                      par->params().depth() == pit->params().depth()) {
72                 if (par->layout()->isEnvironment()) {
73                         par = TeXEnvironment(buf, paragraphs, par,
74                                              os, texrow, runparams);
75                 } else {
76                         par = TeXOnePar(buf, paragraphs, par,
77                                              os, texrow, runparams);
78                 }
79         }
80         LYXERR(Debug::LATEX) << "TeXDeeper...done " << endl;
81
82         return par;
83 }
84
85
86 ParagraphList::const_iterator
87 TeXEnvironment(Buffer const & buf,
88                ParagraphList const & paragraphs,
89                ParagraphList::const_iterator pit,
90                odocstream & os, TexRow & texrow,
91                OutputParams const & runparams)
92 {
93         LYXERR(Debug::LATEX) << "TeXEnvironment...     " << &*pit << endl;
94
95         BufferParams const & bparams = buf.params();
96
97         Layout_ptr const & style = pit->layout();
98
99         Language const * const par_language = pit->getParLanguage(bparams);
100         Language const * const doc_language = bparams.language;
101         Language const * const prev_par_language =
102                 (pit != paragraphs.begin())
103                 ? boost::prior(pit)->getParLanguage(bparams)
104                 : doc_language;
105         if (par_language->babel() != prev_par_language->babel()) {
106
107                 if (!lyxrc.language_command_end.empty() &&
108                     prev_par_language->babel() != doc_language->babel() &&
109                     !prev_par_language->babel().empty()) {
110                         os << from_ascii(subst(
111                                 lyxrc.language_command_end,
112                                 "$$lang",
113                                 prev_par_language->babel()))
114                            << '\n';
115                         texrow.newline();
116                 }
117
118                 if ((lyxrc.language_command_end.empty() ||
119                      par_language->babel() != doc_language->babel()) &&
120                     !par_language->babel().empty()) {
121                         os << from_ascii(subst(
122                                 lyxrc.language_command_begin,
123                                 "$$lang",
124                                 par_language->babel()))
125                            << '\n';
126                         texrow.newline();
127                 }
128         }
129
130         bool leftindent_open = false;
131         if (!pit->params().leftIndent().zero()) {
132                 os << "\\begin{LyXParagraphLeftIndent}{"
133                    << from_ascii(pit->params().leftIndent().asLatexString())
134                    << "}\n";
135                 texrow.newline();
136                 leftindent_open = true;
137         }
138
139         if (style->isEnvironment()) {
140                 os << "\\begin{" << from_ascii(style->latexname()) << '}';
141                 if (style->optionalargs > 0) {
142                         int ret = latexOptArgInsets(buf, *pit, os, runparams,
143                                                     style->optionalargs);
144                         while (ret > 0) {
145                                 texrow.newline();
146                                 --ret;
147                         }
148                 }
149                 if (style->latextype == LATEX_LIST_ENVIRONMENT) {
150                         os << '{'
151                            << pit->params().labelWidthString()
152                            << "}\n";
153                 } else if (style->labeltype == LABEL_BIBLIO) {
154                         // ale970405
155                         os << '{' << bibitemWidest(buf) << "}\n";
156                 } else
157                         os << from_ascii(style->latexparam()) << '\n';
158                 texrow.newline();
159         }
160         ParagraphList::const_iterator par = pit;
161         do {
162                 par = TeXOnePar(buf, paragraphs, par, os, texrow, runparams);
163
164                 if (par == paragraphs.end()) {
165                         // Make sure that the last paragraph is
166                         // correctly terminated (because TeXOnePar does
167                         // not add a \n in this case)
168                         os << '\n';
169                         texrow.newline();
170                 } else if (par->params().depth() > pit->params().depth()) {
171                             if (par->layout()->isParagraph()) {
172
173                             // Thinko!
174                             // How to handle this? (Lgb)
175                             //&& !suffixIs(os, "\n\n")
176                                     //) {
177                                 // There should be at least one '\n' already
178                                 // but we need there to be two for Standard
179                                 // paragraphs that are depth-increment'ed to be
180                                 // output correctly.  However, tables can
181                                 // also be paragraphs so don't adjust them.
182                                 // ARRae
183                                 // Thinkee:
184                                 // Will it ever harm to have one '\n' too
185                                 // many? i.e. that we sometimes will have
186                                 // three in a row. (Lgb)
187                                 os << '\n';
188                                 texrow.newline();
189                         }
190                         par = TeXDeeper(buf, paragraphs, par, os, texrow,
191                                         runparams);
192                 }
193         } while (par != paragraphs.end()
194                  && par->layout() == pit->layout()
195                  && par->params().depth() == pit->params().depth()
196                  && par->params().leftIndent() == pit->params().leftIndent());
197
198         if (style->isEnvironment()) {
199                 os << "\\end{" << from_ascii(style->latexname()) << "}\n";
200                 texrow.newline();
201         }
202
203         if (leftindent_open) {
204                 os << "\\end{LyXParagraphLeftIndent}\n";
205                 texrow.newline();
206         }
207
208         if (par != paragraphs.end())
209                 LYXERR(Debug::LATEX) << "TeXEnvironment...done " << &*par << endl;
210         return par;
211 }
212
213 }
214
215
216 int latexOptArgInsets(Buffer const & buf, Paragraph const & par,
217                       odocstream & os, OutputParams const & runparams, int number)
218 {
219         int lines = 0;
220
221         InsetList::const_iterator it = par.insetlist.begin();
222         InsetList::const_iterator end = par.insetlist.end();
223         for (; it != end && number > 0 ; ++it) {
224                 if (it->inset->lyxCode() == Inset::OPTARG_CODE) {
225                         InsetOptArg * ins =
226                                 static_cast<InsetOptArg *>(it->inset);
227                         lines += ins->latexOptional(buf, os, runparams);
228                         --number;
229                 }
230         }
231         return lines;
232 }
233
234
235 namespace {
236
237 ParagraphList::const_iterator
238 TeXOnePar(Buffer const & buf,
239           ParagraphList const & paragraphs,
240           ParagraphList::const_iterator pit,
241           odocstream & os, TexRow & texrow,
242           OutputParams const & runparams_in,
243           string const & everypar)
244 {
245         LYXERR(Debug::LATEX) << "TeXOnePar...     " << &*pit << " '"
246                 << everypar << "'" << endl;
247         BufferParams const & bparams = buf.params();
248         Layout_ptr style;
249
250         // In an inset with unlimited length (all in one row),
251         // force layout to default
252         if (!pit->forceDefaultParagraphs())
253                 style = pit->layout();
254         else
255                 style = bparams.getTextClass().defaultLayout();
256
257         OutputParams runparams = runparams_in;
258         runparams.moving_arg |= style->needprotect;
259
260         Language const * const par_language = pit->getParLanguage(bparams);
261         Language const * const doc_language = bparams.language;
262         Language const * const prev_par_language =
263                 (pit != paragraphs.begin())
264                 ? boost::prior(pit)->getParLanguage(bparams)
265                 : doc_language;
266
267         if (par_language->babel() != prev_par_language->babel()
268             // check if we already put language command in TeXEnvironment()
269             && !(style->isEnvironment()
270                  && (pit == paragraphs.begin() ||
271                      (boost::prior(pit)->layout() != pit->layout() &&
272                       boost::prior(pit)->getDepth() <= pit->getDepth())
273                      || boost::prior(pit)->getDepth() < pit->getDepth())))
274         {
275                 if (!lyxrc.language_command_end.empty() &&
276                     prev_par_language->babel() != doc_language->babel() &&
277                     !prev_par_language->babel().empty())
278                 {
279                         os << from_ascii(subst(lyxrc.language_command_end,
280                                 "$$lang",
281                                 prev_par_language->babel()))
282                            << '\n';
283                         texrow.newline();
284                 }
285
286                 if ((lyxrc.language_command_end.empty() ||
287                      par_language->babel() != doc_language->babel()) &&
288                     !par_language->babel().empty()) {
289                         os << from_ascii(subst(
290                                 lyxrc.language_command_begin,
291                                 "$$lang",
292                                 par_language->babel()))
293                            << '\n';
294                         texrow.newline();
295                 }
296         }
297
298         // Switch file encoding if necessary
299         if (bparams.inputenc == "auto" &&
300             runparams.encoding->package() == Encoding::inputenc) {
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 (encoding->package() == Encoding::inputenc &&
317                             switchEncoding(os, bparams, false,
318                                            *(runparams.encoding), *encoding) > 0) {
319                                 runparams.encoding = encoding;
320                                 os << '\n';
321                                 texrow.newline();
322                         }
323                         break;
324                 }
325         }
326
327         // In an inset with unlimited length (all in one row),
328         // don't allow any special options in the paragraph
329         if (!pit->forceDefaultParagraphs()) {
330                 if (pit->params().startOfAppendix()) {
331                         os << "\\appendix\n";
332                         texrow.newline();
333                 }
334
335                 if (!pit->params().spacing().isDefault()
336                         && (pit == paragraphs.begin()
337                             || !boost::prior(pit)->hasSameLayout(*pit)))
338                 {
339                         os << from_ascii(pit->params().spacing().writeEnvirBegin())
340                             << '\n';
341                         texrow.newline();
342                 }
343
344                 if (style->isCommand()) {
345                         os << '\n';
346                         texrow.newline();
347                 }
348         }
349
350         switch (style->latextype) {
351         case LATEX_COMMAND:
352                 os << '\\' << from_ascii(style->latexname());
353
354                 // Separate handling of optional argument inset.
355                 if (style->optionalargs > 0) {
356                         int ret = latexOptArgInsets(buf, *pit, os, runparams,
357                                                     style->optionalargs);
358                         while (ret > 0) {
359                                 texrow.newline();
360                                 --ret;
361                         }
362                 }
363                 else
364                         os << from_ascii(style->latexparam());
365                 break;
366         case LATEX_ITEM_ENVIRONMENT:
367         case LATEX_LIST_ENVIRONMENT:
368                 os << "\\item ";
369                 break;
370         case LATEX_BIB_ENVIRONMENT:
371                 // ignore this, the inset will write itself
372                 break;
373         default:
374                 break;
375         }
376
377         Font const outerfont =
378                 outerFont(std::distance(paragraphs.begin(), pit),
379                           paragraphs);
380
381         // FIXME UNICODE
382         os << from_utf8(everypar);
383         bool need_par = pit->simpleTeXOnePar(buf, bparams, outerfont,
384                                              os, texrow, runparams);
385
386         // Make sure that \\par is done with the font of the last
387         // character if this has another size as the default.
388         // This is necessary because LaTeX (and LyX on the screen)
389         // calculates the space between the baselines according
390         // to this font. (Matthias)
391         //
392         // Is this really needed ? (Dekel)
393         // We do not need to use to change the font for the last paragraph
394         // or for a command.
395
396         Font const font =
397                 (pit->empty()
398                  ? pit->getLayoutFont(bparams, outerfont)
399                  : pit->getFont(bparams, pit->size() - 1, outerfont));
400
401         bool is_command = style->isCommand();
402
403         if (style->resfont.size() != font.size()
404             && boost::next(pit) != paragraphs.end()
405             && !is_command) {
406                 if (!need_par)
407                         os << '{';
408                 os << "\\" << from_ascii(font.latexSize()) << " \\par}";
409         } else if (need_par) {
410                 os << "\\par}";
411         } else if (is_command)
412                 os << '}';
413
414         bool pending_newline = false;
415         switch (style->latextype) {
416         case LATEX_ITEM_ENVIRONMENT:
417         case LATEX_LIST_ENVIRONMENT:
418                 if (boost::next(pit) != paragraphs.end()
419                     && (pit->params().depth() < boost::next(pit)->params().depth()))
420                         pending_newline = true;
421                 break;
422         case LATEX_ENVIRONMENT: {
423                 // if its the last paragraph of the current environment
424                 // skip it otherwise fall through
425                 ParagraphList::const_iterator next = boost::next(pit);
426
427                 if (next != paragraphs.end()
428                     && (next->layout() != pit->layout()
429                         || next->params().depth() != pit->params().depth()))
430                         break;
431         }
432
433                 // fall through possible
434         default:
435                 // we don't need it for the last paragraph!!!
436                 if (boost::next(pit) != paragraphs.end())
437                         pending_newline = true;
438         }
439
440         if (!pit->forceDefaultParagraphs()) {
441                 if (!pit->params().spacing().isDefault()
442                         && (boost::next(pit) == paragraphs.end()
443                             || !boost::next(pit)->hasSameLayout(*pit)))
444                 {
445                         if (pending_newline) {
446                                 os << '\n';
447                                 texrow.newline();
448                         }
449                         os << from_ascii(pit->params().spacing().writeEnvirEnd());
450                         pending_newline = true;
451                 }
452         }
453
454         if (boost::next(pit) == paragraphs.end()
455             && par_language->babel() != doc_language->babel()) {
456                 // Since \selectlanguage write the language to the aux file,
457                 // we need to reset the language at the end of footnote or
458                 // float.
459
460                 if (pending_newline) {
461                         os << '\n';
462                         texrow.newline();
463                 }
464                 if (lyxrc.language_command_end.empty()) {
465                         if (!doc_language->babel().empty()) {
466                                 os << from_ascii(subst(
467                                         lyxrc.language_command_begin,
468                                         "$$lang",
469                                         doc_language->babel()));
470                                 pending_newline = true;
471                         }
472                 } else if (!par_language->babel().empty()) {
473                         os << from_ascii(subst(
474                                 lyxrc.language_command_end,
475                                 "$$lang",
476                                 par_language->babel()));
477                         pending_newline = true;
478                 }
479         }
480
481         if (pending_newline) {
482                 os << '\n';
483                 texrow.newline();
484         }
485         runparams_in.encoding = runparams.encoding;
486
487         // we don't need it for the last paragraph!!!
488         // Note from JMarc: we will re-add a \n explicitely in
489         // TeXEnvironment, because it is needed in this case
490         if (boost::next(pit) != paragraphs.end()) {
491                 os << '\n';
492                 texrow.newline();
493         }
494
495         if (boost::next(pit) != paragraphs.end())
496                 LYXERR(Debug::LATEX) << "TeXOnePar...done " << &*boost::next(pit) << endl;
497
498         return ++pit;
499 }
500
501 } // anon namespace
502
503
504 // LaTeX all paragraphs
505 void latexParagraphs(Buffer const & buf,
506                      ParagraphList const & paragraphs,
507                      odocstream & os,
508                      TexRow & texrow,
509                      OutputParams const & runparams,
510                      string const & everypar)
511 {
512         bool was_title = false;
513         bool already_title = false;
514         TextClass const & tclass = buf.params().getTextClass();
515         ParagraphList::const_iterator par = paragraphs.begin();
516         ParagraphList::const_iterator endpar = paragraphs.end();
517
518         BOOST_ASSERT(runparams.par_begin <= runparams.par_end);
519         // if only part of the paragraphs will be outputed
520         if (runparams.par_begin !=  runparams.par_end) {
521                 par = boost::next(paragraphs.begin(), runparams.par_begin);
522                 endpar = boost::next(paragraphs.begin(), runparams.par_end);
523                 // runparams will be passed to nested paragraphs, so
524                 // we have to reset the range parameters.
525                 const_cast<OutputParams&>(runparams).par_begin = 0;
526                 const_cast<OutputParams&>(runparams).par_end = 0;
527         }
528
529         // if only_body
530         while (par != endpar) {
531                 ParagraphList::const_iterator lastpar = par;
532                 // well we have to check if we are in an inset with unlimited
533                 // length (all in one row) if that is true then we don't allow
534                 // any special options in the paragraph and also we don't allow
535                 // any environment other than the default layout of the
536                 // text class to be valid!
537                 if (!par->forceDefaultParagraphs()) {
538                         Layout_ptr const & layout = par->layout();
539
540                         if (layout->intitle) {
541                                 if (already_title) {
542                                         lyxerr << "Error in latexParagraphs: You"
543                                                 " should not mix title layouts"
544                                                 " with normal ones." << endl;
545                                 } else if (!was_title) {
546                                         was_title = true;
547                                         if (tclass.titletype() == TITLE_ENVIRONMENT) {
548                                                 os << "\\begin{"
549                                                     << from_ascii(tclass.titlename())
550                                                     << "}\n";
551                                                 texrow.newline();
552                                         }
553                                 }
554                         } else if (was_title && !already_title) {
555                                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
556                                         os << "\\end{" << from_ascii(tclass.titlename())
557                                             << "}\n";
558                                 }
559                                 else {
560                                         os << "\\" << from_ascii(tclass.titlename())
561                                             << "\n";
562                                 }
563                                 texrow.newline();
564                                 already_title = true;
565                                 was_title = false;
566                         }
567
568                         if (layout->is_environment) {
569                                 par = TeXOnePar(buf, paragraphs, par, os, texrow,
570                                                 runparams, everypar);
571                         } else if (layout->isEnvironment() ||
572                                    !par->params().leftIndent().zero()) {
573                                 par = TeXEnvironment(buf, paragraphs, par, os,
574                                                      texrow, runparams);
575                         } else {
576                                 par = TeXOnePar(buf, paragraphs, par, os, texrow,
577                                                 runparams, everypar);
578                         }
579                 } else {
580                         par = TeXOnePar(buf, paragraphs, par, os, texrow,
581                                         runparams, everypar);
582                 }
583                 if (std::distance(lastpar, par) >= std::distance(lastpar, endpar))
584                         break;
585         }
586         // It might be that we only have a title in this document
587         if (was_title && !already_title) {
588                 if (tclass.titletype() == TITLE_ENVIRONMENT) {
589                         os << "\\end{" << from_ascii(tclass.titlename())
590                             << "}\n";
591                 }
592                 else {
593                         os << "\\" << from_ascii(tclass.titlename())
594                             << "\n";
595                                 }
596                 texrow.newline();
597         }
598 }
599
600
601 int switchEncoding(odocstream & os, BufferParams const & bparams,
602                    bool moving_arg, Encoding const & oldEnc,
603                    Encoding const & newEnc)
604 {
605         if ((bparams.inputenc != "auto" || moving_arg)
606                 && bparams.inputenc != "default")
607                 return 0;
608
609         // Do nothing if the encoding is unchanged.
610         if (oldEnc.name() == newEnc.name())
611                 return 0;
612
613         // FIXME We ignore encoding switches from/to encodings that do
614         // neither support the inputenc package nor the CJK package here.
615         // This does of course only work in special cases (e.g. switch from
616         // tis620-0 to latin1, but the text in latin1 contains ASCII only,
617         // but it is the best we can do
618         if (oldEnc.package() == Encoding::none
619                 || newEnc.package() == Encoding::none)
620                 return 0;
621
622         LYXERR(Debug::LATEX) << "Changing LaTeX encoding from "
623                 << oldEnc.name() << " to "
624                 << newEnc.name() << endl;
625         os << setEncoding(newEnc.iconvName());
626         if (bparams.inputenc == "default")
627                 return 0;
628
629         docstring const inputenc(from_ascii(newEnc.latexName()));
630         switch (newEnc.package()) {
631                 case Encoding::none:
632                         return 0;
633                 case Encoding::inputenc: {
634                         int count = inputenc.length();
635                         if (oldEnc.package() == Encoding::CJK) {
636                                 os << "\\end{CJK}";
637                                 count += 9;
638                         }
639                         os << "\\inputencoding{" << inputenc << '}';
640                         return count + 16;
641                  }
642                 case Encoding::CJK: {
643                         int count = inputenc.length();
644                         if (oldEnc.package() == Encoding::CJK) {
645                                 os << "\\end{CJK}";
646                                 count += 9;
647                         }
648                         os << "\\begin{CJK}{" << inputenc << "}{}";
649                         return count + 15;
650                 }
651         }
652         // Dead code to avoid a warning:
653         return 0;
654 }
655
656 } // namespace lyx