]> git.lyx.org Git - lyx.git/blob - src/LaTeXFeatures.C
* BufferParams:
[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         string const basename = onlyPath(fname);
541
542         FileMap::const_iterator end = IncludedFiles_.end();
543         for (FileMap::const_iterator fi = IncludedFiles_.begin();
544              fi != end; ++fi)
545                 sgmlpreamble << "\n<!ENTITY " << fi->first
546                              << (isSGMLFilename(fi->second) ? " SYSTEM \"" : " \"")
547                              << from_ascii(makeRelPath(fi->second, basename)) << "\">";
548
549         return sgmlpreamble.str();
550 }
551
552
553 void LaTeXFeatures::showStruct() const {
554         lyxerr << "LyX needs the following commands when LaTeXing:"
555                << "\n***** Packages:" << getPackages()
556                << "\n***** Macros:" << getMacros()
557                << "\n***** Textclass stuff:" << to_utf8(getTClassPreamble())
558                << "\n***** done." << endl;
559 }
560
561
562 Buffer const & LaTeXFeatures::buffer() const
563 {
564         return *buffer_;
565 }
566
567
568 void LaTeXFeatures::setBuffer(Buffer const & buffer)
569 {
570         buffer_ = &buffer;
571 }
572
573
574 BufferParams const & LaTeXFeatures::bufferParams() const
575 {
576         return params_;
577 }
578
579
580 void LaTeXFeatures::getFloatDefinitions(ostream & os) const
581 {
582         FloatList const & floats = params_.getLyXTextClass().floats();
583
584         // Here we will output the code to create the needed float styles.
585         // We will try to do this as minimal as possible.
586         // \floatstyle{ruled}
587         // \newfloat{algorithm}{htbp}{loa}
588         // \floatname{algorithm}{Algorithm}
589         UsedFloats::const_iterator cit = usedFloats_.begin();
590         UsedFloats::const_iterator end = usedFloats_.end();
591         // ostringstream floats;
592         for (; cit != end; ++cit) {
593                 Floating const & fl = floats.getType((*cit));
594
595                 // For builtin floats we do nothing.
596                 if (fl.builtin()) continue;
597
598                 // We have to special case "table" and "figure"
599                 if (fl.type() == "tabular" || fl.type() == "figure") {
600                         // Output code to modify "table" or "figure"
601                         // but only if builtin == false
602                         // and that have to be true at this point in the
603                         // function.
604                         string const type = fl.type();
605                         string const placement = fl.placement();
606                         string const style = fl.style();
607                         if (!style.empty()) {
608                                 os << "\\floatstyle{" << style << "}\n"
609                                    << "\\restylefloat{" << type << "}\n";
610                         }
611                         if (!placement.empty()) {
612                                 os << "\\floatplacement{" << type << "}{"
613                                    << placement << "}\n";
614                         }
615                 } else {
616                         // The other non builtin floats.
617
618                         string const type = fl.type();
619                         string const placement = fl.placement();
620                         string const ext = fl.ext();
621                         string const within = fl.within();
622                         string const style = fl.style();
623                         string const name = fl.name();
624                         os << "\\floatstyle{" << style << "}\n"
625                            << "\\newfloat{" << type << "}{" << placement
626                            << "}{" << ext << '}';
627                         if (!within.empty())
628                                 os << '[' << within << ']';
629                         os << '\n'
630                            << "\\floatname{" << type << "}{"
631                            << name << "}\n";
632
633                         // What missing here is to code to minimalize the code
634                         // output so that the same floatstyle will not be
635                         // used several times, when the same style is still in
636                         // effect. (Lgb)
637                 }
638         }
639 }
640
641
642 } // namespace lyx