]> git.lyx.org Git - lyx.git/blob - src/LaTeXFeatures.C
remove redundant lyxerr.debugging checks; macro LYXERR already checks whether the...
[lyx.git] / src / LaTeXFeatures.C
1 /**
2  * \file LaTeXFeatures.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author José Matos
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author Jürgen Vigna
10  * \author André Pönitz
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include <config.h>
16
17 #include "LaTeXFeatures.h"
18
19 #include "bufferparams.h"
20 #include "Color.h"
21 #include "debug.h"
22 #include "encoding.h"
23 #include "Floating.h"
24 #include "FloatList.h"
25 #include "LColor.h"
26 #include "language.h"
27 #include "lyxlex.h"
28 #include "lyx_sty.h"
29 #include "lyxrc.h"
30
31 #include "support/docstream.h"
32 #include "support/filetools.h"
33
34 #include "frontends/controllers/biblio.h"
35
36 namespace lyx {
37
38 using support::isSGMLFilename;
39 using support::libFileSearch;
40 using support::makeRelPath;
41 using support::onlyPath;
42
43 using std::endl;
44 using std::find;
45 using std::string;
46 using std::list;
47 using std::ostream;
48 using std::ostringstream;
49 using std::set;
50
51 LaTeXFeatures::PackagesList LaTeXFeatures::packages_;
52
53
54 LaTeXFeatures::LaTeXFeatures(Buffer const & b, BufferParams const & p,
55                              OutputParams const & r)
56         : buffer_(&b), params_(p), runparams_(r)
57 {}
58
59
60 bool LaTeXFeatures::useBabel() const
61 {
62         return lyxrc.language_use_babel ||
63                 bufferParams().language->lang() != lyxrc.default_language ||
64                 this->hasLanguages();
65 }
66
67
68 void LaTeXFeatures::require(string const & name)
69 {
70         if (isRequired(name))
71                 return;
72
73         features_.push_back(name);
74 }
75
76
77 void LaTeXFeatures::getAvailable()
78 {
79         LyXLex lex(0, 0);
80         support::FileName const real_file = libFileSearch("", "packages.lst");
81
82         if (real_file.empty())
83                 return;
84
85         lex.setFile(real_file);
86
87         if (!lex.isOK())
88                 return;
89
90         // Make sure that we are clean
91         packages_.clear();
92
93         bool finished = false;
94         // Parse config-file
95         while (lex.isOK() && !finished) {
96                 switch (lex.lex()) {
97                 case LyXLex::LEX_FEOF:
98                         finished = true;
99                         break;
100                 default:
101                         string const name = lex.getString();
102                         PackagesList::const_iterator begin = packages_.begin();
103                         PackagesList::const_iterator end   = packages_.end();
104                         if (find(begin, end, name) == end)
105                                 packages_.push_back(name);
106                 }
107         }
108 }
109
110
111 void LaTeXFeatures::useLayout(string const & layoutname)
112 {
113         // Some code to avoid loops in dependency definition
114         static int level = 0;
115         const int maxlevel = 30;
116         if (level > maxlevel) {
117                 lyxerr << "LaTeXFeatures::useLayout: maximum level of "
118                        << "recursion attained by layout "
119                        << layoutname << endl;
120                 return;
121         }
122
123         LyXTextClass const & tclass = params_.getLyXTextClass();
124         if (tclass.hasLayout(layoutname)) {
125                 // Is this layout already in usedLayouts?
126                 list<string>::const_iterator cit = usedLayouts_.begin();
127                 list<string>::const_iterator end = usedLayouts_.end();
128                 for (; cit != end; ++cit) {
129                         if (layoutname == *cit)
130                                 return;
131                 }
132
133                 LyXLayout_ptr const & lyt = tclass[layoutname];
134                 if (!lyt->depends_on().empty()) {
135                         ++level;
136                         useLayout(lyt->depends_on());
137                         --level;
138                 }
139                 usedLayouts_.push_back(layoutname);
140         } else {
141                 lyxerr << "LaTeXFeatures::useLayout: layout `"
142                        << layoutname << "' does not exist in this class"
143                        << endl;
144         }
145
146         --level;
147 }
148
149
150 bool LaTeXFeatures::isRequired(string const & name) const
151 {
152         return find(features_.begin(), features_.end(), name) != features_.end();
153 }
154
155
156 bool LaTeXFeatures::isAvailable(string const & name)
157 {
158         if (packages_.empty())
159                 getAvailable();
160         return find(packages_.begin(), packages_.end(), name) != packages_.end();
161 }
162
163
164 void LaTeXFeatures::addPreambleSnippet(string const & preamble)
165 {
166         FeaturesList::const_iterator begin = preamble_snippets_.begin();
167         FeaturesList::const_iterator end   = preamble_snippets_.end();
168         if (find(begin, end, preamble) == end)
169                 preamble_snippets_.push_back(preamble);
170 }
171
172
173 void LaTeXFeatures::useFloat(string const & name)
174 {
175         usedFloats_.insert(name);
176         // We only need float.sty if we use non builtin floats, or if we
177         // use the "H" modifier. This includes modified table and
178         // figure floats. (Lgb)
179         Floating const & fl = params_.getLyXTextClass().floats().getType(name);
180         if (!fl.type().empty() && !fl.builtin()) {
181                 require("float");
182         }
183 }
184
185
186 void LaTeXFeatures::useLanguage(Language const * lang)
187 {
188         UsedLanguages_.insert(lang);
189 }
190
191
192 void LaTeXFeatures::includeFile(docstring const & key, string const & name)
193 {
194         IncludedFiles_[key] = name;
195 }
196
197
198 bool LaTeXFeatures::hasLanguages() const
199 {
200         return !UsedLanguages_.empty();
201 }
202
203
204 string LaTeXFeatures::getLanguages() const
205 {
206         ostringstream languages;
207
208         for (LanguageList::const_iterator cit =
209                     UsedLanguages_.begin();
210              cit != UsedLanguages_.end();
211              ++cit)
212                 languages << (*cit)->babel() << ',';
213         return languages.str();
214 }
215
216
217 set<string> LaTeXFeatures::getEncodingSet(string const & doc_encoding) const
218 {
219         set<string> encodings;
220         LanguageList::const_iterator it  = UsedLanguages_.begin();
221         LanguageList::const_iterator end = UsedLanguages_.end();
222         for (; it != end; ++it)
223                 // thailatex does not use the inputenc package, but sets up
224                 // babel directly for tis620-0 encoding, therefore we must
225                 // not add tis620-0 to the encoding set.
226                 if ((*it)->encoding()->latexName() != doc_encoding &&
227                     (*it)->encoding()->name() != "tis620-0")
228                         encodings.insert((*it)->encoding()->latexName());
229         return encodings;
230 }
231
232 namespace {
233
234 char const * simplefeatures[] = {
235         "array",
236         "verbatim",
237         "longtable",
238         "rotating",
239         "latexsym",
240         "pifont",
241         "subfigure",
242         "floatflt",
243         "varioref",
244         "prettyref",
245         "float",
246         "booktabs",
247         "dvipost",
248         "fancybox",
249         "calc",
250         "nicefrac",
251         "tipa",
252         "framed",
253         "textcomp",
254 };
255
256 int const nb_simplefeatures = sizeof(simplefeatures) / sizeof(char const *);
257
258 }
259
260
261 string const LaTeXFeatures::getPackages() const
262 {
263         ostringstream packages;
264         LyXTextClass const & tclass = params_.getLyXTextClass();
265
266         //
267         //  These are all the 'simple' includes.  i.e
268         //  packages which we just \usepackage{package}
269         //
270         for (int i = 0; i < nb_simplefeatures; ++i) {
271                 if (isRequired(simplefeatures[i]))
272                         packages << "\\usepackage{"
273                                  << simplefeatures[i] << "}\n";
274         }
275
276         //
277         // The rest of these packages are somewhat more complicated
278         // than those above.
279         //
280
281         if (isRequired("amsmath")
282             && !tclass.provides(LyXTextClass::amsmath)
283             && params_.use_amsmath != BufferParams::package_off) {
284                 packages << "\\usepackage{amsmath}\n";
285         }
286
287         // wasysym is a simple feature, but it must be after amsmath if both
288         // are used
289         // wasysym redefines some integrals (e.g. iint) from amsmath. That
290         // leads to inconsistent integrals. We only load this package if
291         // esint is used, since esint redefines all relevant integral
292         // symbols from wasysym and amsmath.
293         // See http://bugzilla.lyx.org/show_bug.cgi?id=1942
294         if (isRequired("wasysym") && isRequired("esint") &&
295             params_.use_esint != BufferParams::package_off)
296                 packages << "\\usepackage{wasysym}\n";
297
298         // color.sty
299         if (isRequired("color")) {
300                 if (params_.graphicsDriver == "default")
301                         packages << "\\usepackage{color}\n";
302                 else
303                         packages << "\\usepackage["
304                                  << params_.graphicsDriver
305                                  << "]{color}\n";
306         }
307
308         // makeidx.sty
309         if (isRequired("makeidx")) {
310                 if (!tclass.provides(LyXTextClass::makeidx))
311                         packages << "\\usepackage{makeidx}\n";
312                 packages << "\\makeindex\n";
313         }
314
315         // graphicx.sty
316         if (isRequired("graphicx") && params_.graphicsDriver != "none") {
317                 if (params_.graphicsDriver == "default")
318                         packages << "\\usepackage{graphicx}\n";
319                 else
320                         packages << "\\usepackage["
321                                  << params_.graphicsDriver
322                                  << "]{graphicx}\n";
323         }
324         // shadecolor for shaded
325         if (isRequired("framed")) {
326                 RGBColor c = RGBColor(lcolor.getX11Name(LColor::shadedbg));
327                 packages << "\\definecolor{shadecolor}{rgb}{" 
328                         << c.r/255 << ',' << c.g/255 << ',' << c.b/255 << "}\n";
329         }
330
331         //if (algorithm) {
332         //      packages << "\\usepackage{algorithm}\n";
333         //}
334
335         // lyxskak.sty --- newer chess support based on skak.sty
336         if (isRequired("chess")) {
337                 packages << "\\usepackage[ps,mover]{lyxskak}\n";
338         }
339
340         // setspace.sty
341         if ((params_.spacing().getSpace() != Spacing::Single
342              && !params_.spacing().isDefault())
343             || isRequired("setspace")) {
344                 packages << "\\usepackage{setspace}\n";
345         }
346         switch (params_.spacing().getSpace()) {
347         case Spacing::Default:
348         case Spacing::Single:
349                 // we dont use setspace.sty so dont print anything
350                 //packages += "\\singlespacing\n";
351                 break;
352         case Spacing::Onehalf:
353                 packages << "\\onehalfspacing\n";
354                 break;
355         case Spacing::Double:
356                 packages << "\\doublespacing\n";
357                 break;
358         case Spacing::Other:
359                 packages << "\\setstretch{"
360                          << params_.spacing().getValue() << "}\n";
361                 break;
362         }
363
364         // amssymb.sty
365         if (isRequired("amssymb") || params_.use_amsmath == BufferParams::package_on)
366                 packages << "\\usepackage{amssymb}\n";
367
368         // esint must be after amsmath and wasysym, since it will redeclare
369         // inconsistent integral symbols
370         if (isRequired("esint") && params_.use_esint != BufferParams::package_off)
371                 packages << "\\usepackage{esint}\n";
372
373         // url.sty
374         if (isRequired("url") && ! tclass.provides(LyXTextClass::url))
375                 packages << "\\IfFileExists{url.sty}{\\usepackage{url}}\n"
376                             "                      {\\newcommand{\\url}{\\texttt}}\n";
377
378         // float.sty
379         // natbib.sty
380         if (isRequired("natbib") && ! tclass.provides(LyXTextClass::natbib)) {
381                 packages << "\\usepackage[";
382                 if (params_.getEngine() == biblio::ENGINE_NATBIB_NUMERICAL) {
383                         packages << "numbers";
384                 } else {
385                         packages << "authoryear";
386                 }
387                 packages << "]{natbib}\n";
388         }
389
390         // jurabib -- we need version 0.6 at least.
391         if (isRequired("jurabib")) {
392                 packages << "\\usepackage{jurabib}[2004/01/25]\n";
393         }
394
395         // bibtopic -- the dot provides the aux file naming which
396         // LyX can detect.
397         if (isRequired("bibtopic")) {
398                 packages << "\\usepackage[dot]{bibtopic}\n";
399         }
400
401         if (isRequired("xy"))
402                 packages << "\\usepackage[all]{xy}\n";
403
404         if (isRequired("nomencl")) {
405                 packages << "\\usepackage{nomencl}[2005/09/22]\n"
406                          << "\\makenomenclature\n";
407         }
408  
409         return packages.str();
410 }
411
412
413 string const LaTeXFeatures::getMacros() const
414 {
415         ostringstream macros;
416
417         if (!preamble_snippets_.empty())
418                 macros << '\n';
419         FeaturesList::const_iterator pit  = preamble_snippets_.begin();
420         FeaturesList::const_iterator pend = preamble_snippets_.end();
421         for (; pit != pend; ++pit) {
422                 macros << *pit << '\n';
423         }
424
425         if (isRequired("LyX"))
426                 macros << lyx_def << '\n';
427
428         if (isRequired("lyxline"))
429                 macros << lyxline_def << '\n';
430
431         if (isRequired("noun"))
432                 macros << noun_def << '\n';
433
434         if (isRequired("lyxarrow"))
435                 macros << lyxarrow_def << '\n';
436
437         // quotes.
438         if (isRequired("quotesinglbase"))
439                 macros << quotesinglbase_def << '\n';
440         if (isRequired("quotedblbase"))
441                 macros << quotedblbase_def << '\n';
442         if (isRequired("guilsinglleft"))
443                 macros << guilsinglleft_def << '\n';
444         if (isRequired("guilsinglright"))
445                 macros << guilsinglright_def << '\n';
446         if (isRequired("guillemotleft"))
447                 macros << guillemotleft_def << '\n';
448         if (isRequired("guillemotright"))
449                 macros << guillemotright_def << '\n';
450
451         // Math mode
452         if (isRequired("boldsymbol") && !isRequired("amsmath"))
453                 macros << boldsymbol_def << '\n';
454         if (isRequired("binom") && !isRequired("amsmath"))
455                 macros << binom_def << '\n';
456         if (isRequired("mathcircumflex"))
457                 macros << mathcircumflex_def << '\n';
458
459         // other
460         if (isRequired("ParagraphLeftIndent"))
461                 macros << paragraphleftindent_def;
462         if (isRequired("NeedLyXFootnoteCode"))
463                 macros << floatingfootnote_def;
464
465         // some problems with tex->html converters
466         if (isRequired("NeedTabularnewline"))
467                 macros << tabularnewline_def;
468
469         // greyedout environment (note inset)
470         if (isRequired("lyxgreyedout"))
471                 macros << lyxgreyedout_def;
472
473         if (isRequired("lyxdot"))
474                 macros << lyxdot_def << '\n';
475
476         // floats
477         getFloatDefinitions(macros);
478
479         return macros.str();
480 }
481
482
483 string const LaTeXFeatures::getBabelOptions() const
484 {
485         ostringstream tmp;
486
487         LanguageList::const_iterator it  = UsedLanguages_.begin();
488         LanguageList::const_iterator end =  UsedLanguages_.end();
489         for (; it != end; ++it)
490                 if (!(*it)->latex_options().empty())
491                         tmp << (*it)->latex_options() << '\n';
492         if (!params_.language->latex_options().empty())
493                 tmp << params_.language->latex_options() << '\n';
494
495         return tmp.str();
496 }
497
498
499 docstring const LaTeXFeatures::getTClassPreamble() const
500 {
501         // the text class specific preamble
502         LyXTextClass const & tclass = params_.getLyXTextClass();
503         odocstringstream tcpreamble;
504
505         tcpreamble << tclass.preamble();
506
507         list<string>::const_iterator cit = usedLayouts_.begin();
508         list<string>::const_iterator end = usedLayouts_.end();
509         for (; cit != end; ++cit) {
510                 tcpreamble << tclass[*cit]->preamble();
511         }
512
513         CharStyles::iterator cs = tclass.charstyles().begin();
514         CharStyles::iterator csend = tclass.charstyles().end();
515         for (; cs != csend; ++cs) {
516                 if (isRequired(cs->name))
517                         tcpreamble << cs->preamble;
518         }
519
520         return tcpreamble.str();
521 }
522
523
524 docstring const LaTeXFeatures::getLyXSGMLEntities() const
525 {
526         // Definition of entities used in the document that are LyX related.
527         odocstringstream entities;
528
529         if (isRequired("lyxarrow")) {
530                 entities << "<!ENTITY lyxarrow \"-&gt;\">" << '\n';
531         }
532
533         return entities.str();
534 }
535
536
537 docstring const LaTeXFeatures::getIncludedFiles(string const & fname) const
538 {
539         odocstringstream sgmlpreamble;
540         // FIXME UNICODE
541         docstring const basename(from_utf8(onlyPath(fname)));
542
543         FileMap::const_iterator end = IncludedFiles_.end();
544         for (FileMap::const_iterator fi = IncludedFiles_.begin();
545              fi != end; ++fi)
546                 // FIXME UNICODE
547                 sgmlpreamble << "\n<!ENTITY " << fi->first
548                              << (isSGMLFilename(fi->second) ? " SYSTEM \"" : " \"")
549                              << makeRelPath(from_utf8(fi->second), basename) << "\">";
550
551         return sgmlpreamble.str();
552 }
553
554
555 void LaTeXFeatures::showStruct() const {
556         lyxerr << "LyX needs the following commands when LaTeXing:"
557                << "\n***** Packages:" << getPackages()
558                << "\n***** Macros:" << getMacros()
559                << "\n***** Textclass stuff:" << to_utf8(getTClassPreamble())
560                << "\n***** done." << endl;
561 }
562
563
564 Buffer const & LaTeXFeatures::buffer() const
565 {
566         return *buffer_;
567 }
568
569
570 void LaTeXFeatures::setBuffer(Buffer const & buffer)
571 {
572         buffer_ = &buffer;
573 }
574
575
576 BufferParams const & LaTeXFeatures::bufferParams() const
577 {
578         return params_;
579 }
580
581
582 void LaTeXFeatures::getFloatDefinitions(ostream & os) const
583 {
584         FloatList const & floats = params_.getLyXTextClass().floats();
585
586         // Here we will output the code to create the needed float styles.
587         // We will try to do this as minimal as possible.
588         // \floatstyle{ruled}
589         // \newfloat{algorithm}{htbp}{loa}
590         // \floatname{algorithm}{Algorithm}
591         UsedFloats::const_iterator cit = usedFloats_.begin();
592         UsedFloats::const_iterator end = usedFloats_.end();
593         // ostringstream floats;
594         for (; cit != end; ++cit) {
595                 Floating const & fl = floats.getType((*cit));
596
597                 // For builtin floats we do nothing.
598                 if (fl.builtin()) continue;
599
600                 // We have to special case "table" and "figure"
601                 if (fl.type() == "tabular" || fl.type() == "figure") {
602                         // Output code to modify "table" or "figure"
603                         // but only if builtin == false
604                         // and that have to be true at this point in the
605                         // function.
606                         string const type = fl.type();
607                         string const placement = fl.placement();
608                         string const style = fl.style();
609                         if (!style.empty()) {
610                                 os << "\\floatstyle{" << style << "}\n"
611                                    << "\\restylefloat{" << type << "}\n";
612                         }
613                         if (!placement.empty()) {
614                                 os << "\\floatplacement{" << type << "}{"
615                                    << placement << "}\n";
616                         }
617                 } else {
618                         // The other non builtin floats.
619
620                         string const type = fl.type();
621                         string const placement = fl.placement();
622                         string const ext = fl.ext();
623                         string const within = fl.within();
624                         string const style = fl.style();
625                         string const name = fl.name();
626                         os << "\\floatstyle{" << style << "}\n"
627                            << "\\newfloat{" << type << "}{" << placement
628                            << "}{" << ext << '}';
629                         if (!within.empty())
630                                 os << '[' << within << ']';
631                         os << '\n'
632                            << "\\floatname{" << type << "}{"
633                            << name << "}\n";
634
635                         // What missing here is to code to minimalize the code
636                         // output so that the same floatstyle will not be
637                         // used several times, when the same style is still in
638                         // effect. (Lgb)
639                 }
640         }
641 }
642
643
644 } // namespace lyx