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