]> git.lyx.org Git - lyx.git/blob - src/LaTeXFeatures.C
bcda1dc5be0ff49cbabb31c0440bc4b8710ec905
[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 "debug.h"
21 #include "encoding.h"
22 #include "Floating.h"
23 #include "FloatList.h"
24 #include "language.h"
25 #include "lyxlex.h"
26 #include "lyx_sty.h"
27 #include "lyxrc.h"
28
29 #include "support/filetools.h"
30
31 #include <sstream>
32
33 using lyx::support::isSGMLFilename;
34 using lyx::support::LibFileSearch;
35 using lyx::support::MakeRelPath;
36 using lyx::support::OnlyPath;
37
38 using std::endl;
39 using std::find;
40 using std::string;
41 using std::list;
42 using std::ostream;
43 using std::ostringstream;
44 using std::set;
45
46 namespace biblio = lyx::biblio;
47
48
49 LaTeXFeatures::PackagesList LaTeXFeatures::packages_;
50
51
52 LaTeXFeatures::LaTeXFeatures(Buffer const & b, BufferParams const & p,
53                              OutputParams const & r)
54         : buffer_(&b), params_(p), runparams_(r)
55 {}
56
57
58 bool LaTeXFeatures::useBabel() const
59 {
60         return lyxrc.language_use_babel ||
61                 bufferParams().language->lang() != lyxrc.default_language ||
62                 this->hasLanguages();
63 }
64
65
66 void LaTeXFeatures::require(string const & name)
67 {
68         if (isRequired(name))
69                 return;
70
71         features_.push_back(name);
72 }
73
74
75 void LaTeXFeatures::getAvailable()
76 {
77         LyXLex lex(0, 0);
78         string real_file = LibFileSearch("", "packages.lst");
79
80         if (real_file.empty())
81                 return;
82
83         lex.setFile(real_file);
84
85         if (!lex.isOK())
86                 return;
87
88         // Make sure that we are clean
89         packages_.clear();
90
91         bool finished = false;
92         // Parse config-file
93         while (lex.isOK() && !finished) {
94                 switch (lex.lex()) {
95                 case LyXLex::LEX_FEOF:
96                         finished = true;
97                         break;
98                 default:
99                         string const name = lex.getString();
100                         PackagesList::const_iterator begin = packages_.begin();
101                         PackagesList::const_iterator end   = packages_.end();
102                         if (find(begin, end, name) == end)
103                                 packages_.push_back(name);
104                 }
105         }
106
107         return;
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) const
157 {
158         if (packages_.empty())
159                 getAvailable();
160         return find(packages_.begin(), packages_.end(), name) != packages_.end();
161 }
162
163
164 void LaTeXFeatures::addExternalPreamble(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(string 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                 if ((*it)->encoding()->LatexName() != doc_encoding)
224                         encodings.insert((*it)->encoding()->LatexName());
225         return encodings;
226 }
227
228 namespace {
229
230 char const * simplefeatures[] = {
231         "array",
232         "verbatim",
233         "longtable",
234         "rotating",
235         "latexsym",
236         "pifont",
237         "subfigure",
238         "floatflt",
239         "varioref",
240         "prettyref",
241         "float",
242         "dvipost",
243         "fancybox",
244         "calc",
245         "nicefrac",
246 };
247
248 int const nb_simplefeatures = sizeof(simplefeatures) / sizeof(char const *);
249
250 }
251
252
253 string const LaTeXFeatures::getPackages() const
254 {
255         ostringstream packages;
256         LyXTextClass const & tclass = params_.getLyXTextClass();
257
258         //
259         //  These are all the 'simple' includes.  i.e
260         //  packages which we just \usepackage{package}
261         //
262         for (int i = 0; i < nb_simplefeatures; ++i) {
263                 if (isRequired(simplefeatures[i]))
264                         packages << "\\usepackage{"
265                                  << simplefeatures[i] << "}\n";
266         }
267
268         //
269         // The rest of these packages are somewhat more complicated
270         // than those above.
271         //
272
273         if (isRequired("amsmath")
274             && !tclass.provides(LyXTextClass::amsmath)
275             && params_.use_amsmath != BufferParams::AMS_OFF) {
276                 packages << "\\usepackage{amsmath}\n";
277         }
278
279         // wasysym is a simple feature, but it must be after amsmath if both
280         // are used
281         if (isRequired("wasysym"))
282                 packages << "\\usepackage{wasysym}\n";
283
284         // color.sty
285         if (isRequired("color")) {
286                 if (params_.graphicsDriver == "default")
287                         packages << "\\usepackage{color}\n";
288                 else
289                         packages << "\\usepackage["
290                                  << params_.graphicsDriver
291                                  << "]{color}\n";
292         }
293
294         // makeidx.sty
295         if (isRequired("makeidx")) {
296                 if (! tclass.provides(LyXTextClass::makeidx))
297                         packages << "\\usepackage{makeidx}\n";
298                 packages << "\\makeindex\n";
299         }
300
301         // graphicx.sty
302         if (isRequired("graphicx") && params_.graphicsDriver != "none") {
303                 if (params_.graphicsDriver == "default")
304                         packages << "\\usepackage{graphicx}\n";
305                 else
306                         packages << "\\usepackage["
307                                  << params_.graphicsDriver
308                                  << "]{graphicx}\n";
309         }
310
311         //if (algorithm) {
312         //      packages << "\\usepackage{algorithm}\n";
313         //}
314
315         // lyxskak.sty --- newer chess support based on skak.sty
316         if (isRequired("chess")) {
317                 packages << "\\usepackage[ps,mover]{lyxskak}\n";
318         }
319
320         // setspace.sty
321         if ((params_.spacing().getSpace() != Spacing::Single
322              && !params_.spacing().isDefault())
323             || isRequired("setspace")) {
324                 packages << "\\usepackage{setspace}\n";
325         }
326         switch (params_.spacing().getSpace()) {
327         case Spacing::Default:
328         case Spacing::Single:
329                 // we dont use setspace.sty so dont print anything
330                 //packages += "\\singlespacing\n";
331                 break;
332         case Spacing::Onehalf:
333                 packages << "\\onehalfspacing\n";
334                 break;
335         case Spacing::Double:
336                 packages << "\\doublespacing\n";
337                 break;
338         case Spacing::Other:
339                 packages << "\\setstretch{"
340                          << params_.spacing().getValue() << "}\n";
341                 break;
342         }
343
344         // amssymb.sty
345         if (isRequired("amssymb") || params_.use_amsmath == BufferParams::AMS_ON)
346                 packages << "\\usepackage{amssymb}\n";
347         // url.sty
348         if (isRequired("url") && ! tclass.provides(LyXTextClass::url))
349                 packages << "\\IfFileExists{url.sty}{\\usepackage{url}}\n"
350                             "                      {\\newcommand{\\url}{\\texttt}}\n";
351
352         // float.sty
353         // natbib.sty
354         if (isRequired("natbib") && ! tclass.provides(LyXTextClass::natbib)) {
355                 packages << "\\usepackage[";
356                 if (params_.cite_engine == biblio::ENGINE_NATBIB_NUMERICAL) {
357                         packages << "numbers";
358                 } else {
359                         packages << "authoryear";
360                 }
361                 packages << "]{natbib}\n";
362         }
363
364         // jurabib -- we need version 0.6 at least.
365         if (isRequired("jurabib")) {
366                 packages << "\\usepackage{jurabib}[2004/01/25]\n";
367         }
368
369         // bibtopic -- the dot provides the aux file naming which
370         // LyX can detect.
371         if (isRequired("bibtopic")) {
372                 packages << "\\usepackage[dot]{bibtopic}\n";
373         }
374
375         return packages.str();
376 }
377
378
379 string const LaTeXFeatures::getMacros() const
380 {
381         ostringstream macros;
382
383         if (!preamble_snippets_.empty())
384                 macros << '\n';
385         FeaturesList::const_iterator pit  = preamble_snippets_.begin();
386         FeaturesList::const_iterator pend = preamble_snippets_.end();
387         for (; pit != pend; ++pit) {
388                 macros << *pit << '\n';
389         }
390
391         if (isRequired("LyX"))
392                 macros << lyx_def << '\n';
393
394         if (isRequired("lyxline"))
395                 macros << lyxline_def << '\n';
396
397         if (isRequired("noun"))
398                 macros << noun_def << '\n';
399
400         if (isRequired("lyxarrow"))
401                 macros << lyxarrow_def << '\n';
402
403         // quotes.
404         if (isRequired("quotesinglbase"))
405                 macros << quotesinglbase_def << '\n';
406         if (isRequired("quotedblbase"))
407                 macros << quotedblbase_def << '\n';
408         if (isRequired("guilsinglleft"))
409                 macros << guilsinglleft_def << '\n';
410         if (isRequired("guilsinglright"))
411                 macros << guilsinglright_def << '\n';
412         if (isRequired("guillemotleft"))
413                 macros << guillemotleft_def << '\n';
414         if (isRequired("guillemotright"))
415                 macros << guillemotright_def << '\n';
416
417         // Math mode
418         if (isRequired("boldsymbol") && !isRequired("amsmath"))
419                 macros << boldsymbol_def << '\n';
420         if (isRequired("binom") && !isRequired("amsmath"))
421                 macros << binom_def << '\n';
422         if (isRequired("mathcircumflex"))
423                 macros << mathcircumflex_def << '\n';
424
425         // other
426         if (isRequired("ParagraphLeftIndent"))
427                 macros << paragraphleftindent_def;
428         if (isRequired("NeedLyXFootnoteCode"))
429                 macros << floatingfootnote_def;
430
431         // some problems with tex->html converters
432         if (isRequired("NeedTabularnewline"))
433                 macros << tabularnewline_def;
434
435         // greyedout environment (note inset)
436         if (isRequired("lyxgreyedout"))
437                 macros << lyxgreyedout_def;
438
439         if (isRequired("lyxdot"))
440                 macros << lyxdot_def << '\n';
441
442         // floats
443         getFloatDefinitions(macros);
444
445         return macros.str();
446 }
447
448
449 string const LaTeXFeatures::getBabelOptions() const
450 {
451         ostringstream tmp;
452
453         LanguageList::const_iterator it  = UsedLanguages_.begin();
454         LanguageList::const_iterator end =  UsedLanguages_.end();
455         for (; it != end; ++it)
456                 if (!(*it)->latex_options().empty())
457                         tmp << (*it)->latex_options() << '\n';
458         if (!params_.language->latex_options().empty())
459                 tmp << params_.language->latex_options() << '\n';
460
461         return tmp.str();
462 }
463
464
465 string const LaTeXFeatures::getTClassPreamble() const
466 {
467         // the text class specific preamble
468         LyXTextClass const & tclass = params_.getLyXTextClass();
469         ostringstream tcpreamble;
470
471         tcpreamble << tclass.preamble();
472
473         list<string>::const_iterator cit = usedLayouts_.begin();
474         list<string>::const_iterator end = usedLayouts_.end();
475         for (; cit != end; ++cit) {
476                 tcpreamble << tclass[*cit]->preamble();
477         }
478
479         CharStyles::iterator cs = tclass.charstyles().begin();
480         CharStyles::iterator csend = tclass.charstyles().end();
481         for (; cs != csend; ++cs) {
482                 if (isRequired(cs->name))
483                         tcpreamble << cs->preamble;
484         }
485
486         return tcpreamble.str();
487 }
488
489
490 string const LaTeXFeatures::getLyXSGMLEntities() const
491 {
492         // Definition of entities used in the document that are LyX related.
493         ostringstream entities;
494
495         if (isRequired("lyxarrow")) {
496                 entities << "<!ENTITY lyxarrow \"-&gt;\">" << '\n';
497         }
498
499         return entities.str();
500 }
501
502
503 string const LaTeXFeatures::getIncludedFiles(string const & fname) const
504 {
505         ostringstream sgmlpreamble;
506         string const basename = OnlyPath(fname);
507
508         FileMap::const_iterator end = IncludedFiles_.end();
509         for (FileMap::const_iterator fi = IncludedFiles_.begin();
510              fi != end; ++fi)
511                 sgmlpreamble << "\n<!ENTITY " << fi->first
512                              << (isSGMLFilename(fi->second) ? " SYSTEM \"" : " \"")
513                              << MakeRelPath(fi->second, basename) << "\">";
514
515         return sgmlpreamble.str();
516 }
517
518
519 void LaTeXFeatures::showStruct() const {
520         lyxerr << "LyX needs the following commands when LaTeXing:"
521                << "\n***** Packages:" << getPackages()
522                << "\n***** Macros:" << getMacros()
523                << "\n***** Textclass stuff:" << getTClassPreamble()
524                << "\n***** done." << endl;
525 }
526
527
528 Buffer const & LaTeXFeatures::buffer() const
529 {
530         return *buffer_;
531 }
532
533
534 void LaTeXFeatures::setBuffer(Buffer const & buffer)
535 {
536         buffer_ = &buffer;
537 }
538
539
540 BufferParams const & LaTeXFeatures::bufferParams() const
541 {
542         return params_;
543 }
544
545
546 void LaTeXFeatures::getFloatDefinitions(ostream & os) const
547 {
548         FloatList const & floats = params_.getLyXTextClass().floats();
549
550         // Here we will output the code to create the needed float styles.
551         // We will try to do this as minimal as possible.
552         // \floatstyle{ruled}
553         // \newfloat{algorithm}{htbp}{loa}
554         // \floatname{algorithm}{Algorithm}
555         UsedFloats::const_iterator cit = usedFloats_.begin();
556         UsedFloats::const_iterator end = usedFloats_.end();
557         // ostringstream floats;
558         for (; cit != end; ++cit) {
559                 Floating const & fl = floats.getType((*cit));
560
561                 // For builtin floats we do nothing.
562                 if (fl.builtin()) continue;
563
564                 // We have to special case "table" and "figure"
565                 if (fl.type() == "tabular" || fl.type() == "figure") {
566                         // Output code to modify "table" or "figure"
567                         // but only if builtin == false
568                         // and that have to be true at this point in the
569                         // function.
570                         string const type = fl.type();
571                         string const placement = fl.placement();
572                         string const style = fl.style();
573                         if (!style.empty()) {
574                                 os << "\\floatstyle{" << style << "}\n"
575                                    << "\\restylefloat{" << type << "}\n";
576                         }
577                         if (!placement.empty()) {
578                                 os << "\\floatplacement{" << type << "}{"
579                                    << placement << "}\n";
580                         }
581                 } else {
582                         // The other non builtin floats.
583
584                         string const type = fl.type();
585                         string const placement = fl.placement();
586                         string const ext = fl.ext();
587                         string const within = fl.within();
588                         string const style = fl.style();
589                         string const name = fl.name();
590                         os << "\\floatstyle{" << style << "}\n"
591                            << "\\newfloat{" << type << "}{" << placement
592                            << "}{" << ext << '}';
593                         if (!within.empty())
594                                 os << '[' << within << ']';
595                         os << '\n'
596                            << "\\floatname{" << type << "}{"
597                            << name << "}\n";
598
599                         // What missing here is to code to minimalize the code
600                         // output so that the same floatstyle will not be
601                         // used several times, when the same style is still in
602                         // effect. (Lgb)
603                 }
604         }
605 }