]> git.lyx.org Git - lyx.git/blob - src/LaTeXFeatures.cpp
5dc426eecc1c7faefc5b542a63bfa7f783878989
[lyx.git] / src / LaTeXFeatures.cpp
1 /**
2  * \file LaTeXFeatures.cpp
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 "Color.h"
26 #include "Language.h"
27 #include "Lexer.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/frontend_helpers.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         Lexer 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 Lexer::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::mustProvide(string const & name) const
157 {
158         return isRequired(name) && !params_.getLyXTextClass().provides(name);
159 }
160
161
162 bool LaTeXFeatures::isAvailable(string const & name)
163 {
164         if (packages_.empty())
165                 getAvailable();
166         return find(packages_.begin(), packages_.end(), name) != packages_.end();
167 }
168
169
170 void LaTeXFeatures::addPreambleSnippet(string const & preamble)
171 {
172         FeaturesList::const_iterator begin = preamble_snippets_.begin();
173         FeaturesList::const_iterator end   = preamble_snippets_.end();
174         if (find(begin, end, preamble) == end)
175                 preamble_snippets_.push_back(preamble);
176 }
177
178
179 void LaTeXFeatures::useFloat(string const & name)
180 {
181         usedFloats_.insert(name);
182         // We only need float.sty if we use non builtin floats, or if we
183         // use the "H" modifier. This includes modified table and
184         // figure floats. (Lgb)
185         Floating const & fl = params_.getLyXTextClass().floats().getType(name);
186         if (!fl.type().empty() && !fl.builtin()) {
187                 require("float");
188         }
189 }
190
191
192 void LaTeXFeatures::useLanguage(Language const * lang)
193 {
194         UsedLanguages_.insert(lang);
195 }
196
197
198 void LaTeXFeatures::includeFile(docstring const & key, string const & name)
199 {
200         IncludedFiles_[key] = name;
201 }
202
203
204 bool LaTeXFeatures::hasLanguages() const
205 {
206         return !UsedLanguages_.empty();
207 }
208
209
210 string LaTeXFeatures::getLanguages() const
211 {
212         ostringstream languages;
213
214         for (LanguageList::const_iterator cit =
215                     UsedLanguages_.begin();
216              cit != UsedLanguages_.end();
217              ++cit)
218                 languages << (*cit)->babel() << ',';
219         return languages.str();
220 }
221
222
223 set<string> LaTeXFeatures::getEncodingSet(string const & doc_encoding) const
224 {
225         set<string> encodings;
226         LanguageList::const_iterator it  = UsedLanguages_.begin();
227         LanguageList::const_iterator end = UsedLanguages_.end();
228         for (; it != end; ++it)
229                 // thailatex does not use the inputenc package, but sets up
230                 // babel directly for tis620-0 encoding, therefore we must
231                 // not add tis620-0 to the encoding set.
232                 if ((*it)->encoding()->latexName() != doc_encoding &&
233                     (*it)->encoding()->name() != "tis620-0")
234                         encodings.insert((*it)->encoding()->latexName());
235         return encodings;
236 }
237
238 namespace {
239
240 char const * simplefeatures[] = {
241         "array",
242         "verbatim",
243         "longtable",
244         "rotating",
245         "latexsym",
246         "pifont",
247         "subfigure",
248         "floatflt",
249         "varioref",
250         "prettyref",
251         "float",
252         "booktabs",
253         "dvipost",
254         "fancybox",
255         "calc",
256         "nicefrac",
257         "tipa",
258         "framed",
259         "textcomp",
260 };
261
262 int const nb_simplefeatures = sizeof(simplefeatures) / sizeof(char const *);
263
264 }
265
266
267 string const LaTeXFeatures::getPackages() const
268 {
269         ostringstream packages;
270         LyXTextClass const & tclass = params_.getLyXTextClass();
271
272         //
273         //  These are all the 'simple' includes.  i.e
274         //  packages which we just \usepackage{package}
275         //
276         for (int i = 0; i < nb_simplefeatures; ++i) {
277                 if (mustProvide(simplefeatures[i]))
278                         packages << "\\usepackage{"
279                                  << simplefeatures[i] << "}\n";
280         }
281
282         //
283         // The rest of these packages are somewhat more complicated
284         // than those above.
285         //
286
287         if (mustProvide("amsmath")
288             && params_.use_amsmath != BufferParams::package_off) {
289                 packages << "\\usepackage{amsmath}\n";
290         }
291
292         // wasysym is a simple feature, but it must be after amsmath if both
293         // are used
294         // wasysym redefines some integrals (e.g. iint) from amsmath. That
295         // leads to inconsistent integrals. We only load this package if
296         // esint is used, since esint redefines all relevant integral
297         // symbols from wasysym and amsmath.
298         // See http://bugzilla.lyx.org/show_bug.cgi?id=1942
299         if (mustProvide("wasysym") && isRequired("esint") &&
300             params_.use_esint != BufferParams::package_off)
301                 packages << "\\usepackage{wasysym}\n";
302
303         // color.sty
304         if (mustProvide("color")) {
305                 if (params_.graphicsDriver == "default")
306                         packages << "\\usepackage{color}\n";
307                 else
308                         packages << "\\usepackage["
309                                  << params_.graphicsDriver
310                                  << "]{color}\n";
311         }
312
313         // makeidx.sty
314         if (isRequired("makeidx")) {
315                 if (!tclass.provides("makeidx"))
316                         packages << "\\usepackage{makeidx}\n";
317                 packages << "\\makeindex\n";
318         }
319
320         // graphicx.sty
321         if (mustProvide("graphicx") && params_.graphicsDriver != "none") {
322                 if (params_.graphicsDriver == "default")
323                         packages << "\\usepackage{graphicx}\n";
324                 else
325                         packages << "\\usepackage["
326                                  << params_.graphicsDriver
327                                  << "]{graphicx}\n";
328         }
329         // shadecolor for shaded
330         if (mustProvide("framed")) {
331                 RGBColor c = RGBColor(lcolor.getX11Name(Color::shadedbg));
332                 packages << "\\definecolor{shadecolor}{rgb}{" 
333                         << c.r/255 << ',' << c.g/255 << ',' << c.b/255 << "}\n";
334         }
335
336         // lyxskak.sty --- newer chess support based on skak.sty
337         if (mustProvide("chess")) {
338                 packages << "\\usepackage[ps,mover]{lyxskak}\n";
339         }
340
341         // setspace.sty
342         if ((params_.spacing().getSpace() != Spacing::Single
343              && !params_.spacing().isDefault())
344             || isRequired("setspace")) {
345                 packages << "\\usepackage{setspace}\n";
346         }
347         switch (params_.spacing().getSpace()) {
348         case Spacing::Default:
349         case Spacing::Single:
350                 // we dont use setspace.sty so dont print anything
351                 //packages += "\\singlespacing\n";
352                 break;
353         case Spacing::Onehalf:
354                 packages << "\\onehalfspacing\n";
355                 break;
356         case Spacing::Double:
357                 packages << "\\doublespacing\n";
358                 break;
359         case Spacing::Other:
360                 packages << "\\setstretch{"
361                          << params_.spacing().getValue() << "}\n";
362                 break;
363         }
364
365         // amssymb.sty
366         if (mustProvide("amssymb") 
367             || params_.use_amsmath == BufferParams::package_on)
368                 packages << "\\usepackage{amssymb}\n";
369
370         // esint must be after amsmath and wasysym, since it will redeclare
371         // inconsistent integral symbols
372         if (mustProvide("esint") 
373             && params_.use_esint != BufferParams::package_off)
374                 packages << "\\usepackage{esint}\n";
375
376         // url.sty
377         if (mustProvide("url"))
378                 packages << "\\IfFileExists{url.sty}{\\usepackage{url}}\n"
379                             "                      {\\newcommand{\\url}{\\texttt}}\n";
380
381         // natbib.sty
382         if (mustProvide("natbib")) {
383                 packages << "\\usepackage[";
384                 if (params_.getEngine() == biblio::ENGINE_NATBIB_NUMERICAL) {
385                         packages << "numbers";
386                 } else {
387                         packages << "authoryear";
388                 }
389                 packages << "]{natbib}\n";
390         }
391
392         // jurabib -- we need version 0.6 at least.
393         if (mustProvide("jurabib")) {
394                 packages << "\\usepackage{jurabib}[2004/01/25]\n";
395         }
396
397         // bibtopic -- the dot provides the aux file naming which
398         // LyX can detect.
399         if (mustProvide("bibtopic")) {
400                 packages << "\\usepackage[dot]{bibtopic}\n";
401         }
402
403         if (mustProvide("xy"))
404                 packages << "\\usepackage[all]{xy}\n";
405
406         if (mustProvide("nomencl")) {
407                 // Make it work with the new and old version of the package,
408                 // but don't use the compatibility option since it is
409                 // incompatible to other packages.
410                 packages << "\\usepackage{nomencl}\n"
411                             "% the following is useful when we have the old nomencl.sty package\n"
412                             "\\providecommand{\\printnomenclature}{\\printglossary}\n"
413                             "\\providecommand{\\makenomenclature}{\\makeglossary}\n"
414                             "\\makenomenclature\n";
415         }
416  
417         return packages.str();
418 }
419
420
421 string const LaTeXFeatures::getMacros() const
422 {
423         ostringstream macros;
424
425         if (!preamble_snippets_.empty())
426                 macros << '\n';
427         FeaturesList::const_iterator pit  = preamble_snippets_.begin();
428         FeaturesList::const_iterator pend = preamble_snippets_.end();
429         for (; pit != pend; ++pit) {
430                 macros << *pit << '\n';
431         }
432
433         if (mustProvide("LyX"))
434                 macros << lyx_def << '\n';
435
436         if (mustProvide("lyxline"))
437                 macros << lyxline_def << '\n';
438
439         if (mustProvide("noun"))
440                 macros << noun_def << '\n';
441
442         if (mustProvide("lyxarrow"))
443                 macros << lyxarrow_def << '\n';
444
445         // quotes.
446         if (mustProvide("quotesinglbase"))
447                 macros << quotesinglbase_def << '\n';
448         if (mustProvide("quotedblbase"))
449                 macros << quotedblbase_def << '\n';
450         if (mustProvide("guilsinglleft"))
451                 macros << guilsinglleft_def << '\n';
452         if (mustProvide("guilsinglright"))
453                 macros << guilsinglright_def << '\n';
454         if (mustProvide("guillemotleft"))
455                 macros << guillemotleft_def << '\n';
456         if (mustProvide("guillemotright"))
457                 macros << guillemotright_def << '\n';
458
459         // Math mode
460         if (mustProvide("boldsymbol") && !isRequired("amsmath"))
461                 macros << boldsymbol_def << '\n';
462         if (mustProvide("binom") && !isRequired("amsmath"))
463                 macros << binom_def << '\n';
464         if (mustProvide("mathcircumflex"))
465                 macros << mathcircumflex_def << '\n';
466
467         // other
468         if (mustProvide("ParagraphLeftIndent"))
469                 macros << paragraphleftindent_def;
470         if (mustProvide("NeedLyXFootnoteCode"))
471                 macros << floatingfootnote_def;
472
473         // some problems with tex->html converters
474         if (mustProvide("NeedTabularnewline"))
475                 macros << tabularnewline_def;
476
477         // greyedout environment (note inset)
478         if (mustProvide("lyxgreyedout"))
479                 macros << lyxgreyedout_def;
480
481         if (mustProvide("lyxdot"))
482                 macros << lyxdot_def << '\n';
483
484         // floats
485         getFloatDefinitions(macros);
486
487         return macros.str();
488 }
489
490
491 string const LaTeXFeatures::getBabelOptions() const
492 {
493         ostringstream tmp;
494
495         LanguageList::const_iterator it  = UsedLanguages_.begin();
496         LanguageList::const_iterator end =  UsedLanguages_.end();
497         for (; it != end; ++it)
498                 if (!(*it)->latex_options().empty())
499                         tmp << (*it)->latex_options() << '\n';
500         if (!params_.language->latex_options().empty())
501                 tmp << params_.language->latex_options() << '\n';
502
503         return tmp.str();
504 }
505
506
507 docstring const LaTeXFeatures::getTClassPreamble() const
508 {
509         // the text class specific preamble
510         LyXTextClass const & tclass = params_.getLyXTextClass();
511         odocstringstream tcpreamble;
512
513         tcpreamble << tclass.preamble();
514
515         list<string>::const_iterator cit = usedLayouts_.begin();
516         list<string>::const_iterator end = usedLayouts_.end();
517         for (; cit != end; ++cit) {
518                 tcpreamble << tclass[*cit]->preamble();
519         }
520
521         CharStyles::iterator cs = tclass.charstyles().begin();
522         CharStyles::iterator csend = tclass.charstyles().end();
523         for (; cs != csend; ++cs) {
524                 if (isRequired(cs->name))
525                         tcpreamble << cs->preamble;
526         }
527
528         return tcpreamble.str();
529 }
530
531
532 docstring const LaTeXFeatures::getLyXSGMLEntities() const
533 {
534         // Definition of entities used in the document that are LyX related.
535         odocstringstream entities;
536
537         if (mustProvide("lyxarrow")) {
538                 entities << "<!ENTITY lyxarrow \"-&gt;\">" << '\n';
539         }
540
541         return entities.str();
542 }
543
544
545 docstring const LaTeXFeatures::getIncludedFiles(string const & fname) const
546 {
547         odocstringstream sgmlpreamble;
548         // FIXME UNICODE
549         docstring const basename(from_utf8(onlyPath(fname)));
550
551         FileMap::const_iterator end = IncludedFiles_.end();
552         for (FileMap::const_iterator fi = IncludedFiles_.begin();
553              fi != end; ++fi)
554                 // FIXME UNICODE
555                 sgmlpreamble << "\n<!ENTITY " << fi->first
556                              << (isSGMLFilename(fi->second) ? " SYSTEM \"" : " \"")
557                              << makeRelPath(from_utf8(fi->second), basename) << "\">";
558
559         return sgmlpreamble.str();
560 }
561
562
563 void LaTeXFeatures::showStruct() const {
564         lyxerr << "LyX needs the following commands when LaTeXing:"
565                << "\n***** Packages:" << getPackages()
566                << "\n***** Macros:" << getMacros()
567                << "\n***** Textclass stuff:" << to_utf8(getTClassPreamble())
568                << "\n***** done." << endl;
569 }
570
571
572 Buffer const & LaTeXFeatures::buffer() const
573 {
574         return *buffer_;
575 }
576
577
578 void LaTeXFeatures::setBuffer(Buffer const & buffer)
579 {
580         buffer_ = &buffer;
581 }
582
583
584 BufferParams const & LaTeXFeatures::bufferParams() const
585 {
586         return params_;
587 }
588
589
590 void LaTeXFeatures::getFloatDefinitions(ostream & os) const
591 {
592         FloatList const & floats = params_.getLyXTextClass().floats();
593
594         // Here we will output the code to create the needed float styles.
595         // We will try to do this as minimal as possible.
596         // \floatstyle{ruled}
597         // \newfloat{algorithm}{htbp}{loa}
598         // \floatname{algorithm}{Algorithm}
599         UsedFloats::const_iterator cit = usedFloats_.begin();
600         UsedFloats::const_iterator end = usedFloats_.end();
601         // ostringstream floats;
602         for (; cit != end; ++cit) {
603                 Floating const & fl = floats.getType((*cit));
604
605                 // For builtin floats we do nothing.
606                 if (fl.builtin()) continue;
607
608                 // We have to special case "table" and "figure"
609                 if (fl.type() == "tabular" || fl.type() == "figure") {
610                         // Output code to modify "table" or "figure"
611                         // but only if builtin == false
612                         // and that have to be true at this point in the
613                         // function.
614                         string const type = fl.type();
615                         string const placement = fl.placement();
616                         string const style = fl.style();
617                         if (!style.empty()) {
618                                 os << "\\floatstyle{" << style << "}\n"
619                                    << "\\restylefloat{" << type << "}\n";
620                         }
621                         if (!placement.empty()) {
622                                 os << "\\floatplacement{" << type << "}{"
623                                    << placement << "}\n";
624                         }
625                 } else {
626                         // The other non builtin floats.
627
628                         string const type = fl.type();
629                         string const placement = fl.placement();
630                         string const ext = fl.ext();
631                         string const within = fl.within();
632                         string const style = fl.style();
633                         string const name = fl.name();
634                         os << "\\floatstyle{" << style << "}\n"
635                            << "\\newfloat{" << type << "}{" << placement
636                            << "}{" << ext << '}';
637                         if (!within.empty())
638                                 os << '[' << within << ']';
639                         os << '\n'
640                            << "\\floatname{" << type << "}{"
641                            << name << "}\n";
642
643                         // What missing here is to code to minimalize the code
644                         // output so that the same floatstyle will not be
645                         // used several times, when the same style is still in
646                         // effect. (Lgb)
647                 }
648         }
649 }
650
651
652 } // namespace lyx