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