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