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