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