]> git.lyx.org Git - lyx.git/blob - src/BufferParams.cpp
e3131659130f3212271b45f2168a2e714305c92d
[lyx.git] / src / BufferParams.cpp
1 /**
2  * \file BufferParams.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alfredo Braunstein
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author John Levon
10  * \author André Pönitz
11  * \author Martin Vermeer
12  *
13  * Full author contact details are available in file CREDITS.
14  */
15
16 #include <config.h>
17
18 #include "BufferParams.h"
19
20 #include "Author.h"
21 #include "LayoutFile.h"
22 #include "BranchList.h"
23 #include "buffer_funcs.h"
24 #include "Bullet.h"
25 #include "Color.h"
26 #include "ColorSet.h"
27 #include "Encoding.h"
28 #include "Language.h"
29 #include "LaTeXFeatures.h"
30 #include "ModuleList.h"
31 #include "Font.h"
32 #include "Lexer.h"
33 #include "LyXRC.h"
34 #include "OutputParams.h"
35 #include "Spacing.h"
36 #include "TexRow.h"
37 #include "VSpace.h"
38 #include "PDFOptions.h"
39
40 #include "frontends/alert.h"
41
42 #include "insets/InsetListingsParams.h"
43
44 #include "support/convert.h"
45 #include "support/debug.h"
46 #include "support/docstream.h"
47 #include "support/FileName.h"
48 #include "support/filetools.h"
49 #include "support/gettext.h"
50 #include "support/Messages.h"
51 #include "support/Translator.h"
52 #include "support/lstrings.h"
53
54 #include <algorithm>
55 #include <sstream>
56
57 using namespace std;
58 using namespace lyx::support;
59
60
61 static char const * const string_paragraph_separation[] = {
62         "indent", "skip", ""
63 };
64
65
66 static char const * const string_quotes_language[] = {
67         "english", "swedish", "german", "polish", "french", "danish", ""
68 };
69
70
71 static char const * const string_papersize[] = {
72         "default", "custom", "letterpaper", "legalpaper", "executivepaper",
73         "a3paper", "a4paper", "a5paper", "b3paper", "b4paper", "b5paper", ""
74 };
75
76
77 static char const * const string_orientation[] = {
78         "portrait", "landscape", ""
79 };
80
81
82 static char const * const string_footnotekinds[] = {
83         "footnote", "margin", "fig", "tab", "alg", "wide-fig", "wide-tab", ""
84 };
85
86
87 static char const * const tex_graphics[] = {
88         "default", "dvialw", "dvilaser", "dvipdf", "dvipdfm", "dvipdfmx",
89         "dvips", "dvipsone", "dvitops", "dviwin", "dviwindo", "dvi2ps", "emtex",
90         "ln", "oztex", "pctexhp", "pctexps", "pctexwin", "pctex32", "pdftex",
91         "psprint", "pubps", "tcidvi", "textures", "truetex", "vtex", "xdvi",
92         "xetex", "none", ""
93 };
94
95
96 namespace lyx {
97
98 // Local translators
99 namespace {
100
101 // Paragraph separation
102 typedef Translator<string, BufferParams::ParagraphSeparation> ParSepTranslator;
103
104
105 ParSepTranslator const init_parseptranslator()
106 {
107         ParSepTranslator translator
108                 (string_paragraph_separation[0], BufferParams::ParagraphIndentSeparation);
109         translator.addPair(string_paragraph_separation[1], BufferParams::ParagraphSkipSeparation);
110         return translator;
111 }
112
113
114 ParSepTranslator const & parseptranslator()
115 {
116         static ParSepTranslator translator = init_parseptranslator();
117         return translator;
118 }
119
120
121 // Quotes language
122 typedef Translator<string, InsetQuotes::QuoteLanguage> QuotesLangTranslator;
123
124
125 QuotesLangTranslator const init_quoteslangtranslator()
126 {
127         QuotesLangTranslator translator
128                 (string_quotes_language[0], InsetQuotes::EnglishQuotes);
129         translator.addPair(string_quotes_language[1], InsetQuotes::SwedishQuotes);
130         translator.addPair(string_quotes_language[2], InsetQuotes::GermanQuotes);
131         translator.addPair(string_quotes_language[3], InsetQuotes::PolishQuotes);
132         translator.addPair(string_quotes_language[4], InsetQuotes::FrenchQuotes);
133         translator.addPair(string_quotes_language[5], InsetQuotes::DanishQuotes);
134         return translator;
135 }
136
137
138 QuotesLangTranslator const & quoteslangtranslator()
139 {
140         static QuotesLangTranslator translator = init_quoteslangtranslator();
141         return translator;
142 }
143
144
145 // Paper size
146 typedef Translator<string, PAPER_SIZE> PaperSizeTranslator;
147
148
149 static PaperSizeTranslator initPaperSizeTranslator()
150 {
151         PaperSizeTranslator translator(string_papersize[0], PAPER_DEFAULT);
152         translator.addPair(string_papersize[1], PAPER_CUSTOM);
153         translator.addPair(string_papersize[2], PAPER_USLETTER);
154         translator.addPair(string_papersize[3], PAPER_USLEGAL);
155         translator.addPair(string_papersize[4], PAPER_USEXECUTIVE);
156         translator.addPair(string_papersize[5], PAPER_A3);
157         translator.addPair(string_papersize[6], PAPER_A4);
158         translator.addPair(string_papersize[7], PAPER_A5);
159         translator.addPair(string_papersize[8], PAPER_B3);
160         translator.addPair(string_papersize[9], PAPER_B4);
161         translator.addPair(string_papersize[10], PAPER_B5);
162         return translator;
163 }
164
165
166 PaperSizeTranslator const & papersizetranslator()
167 {
168         static PaperSizeTranslator translator = initPaperSizeTranslator();
169         return translator;
170 }
171
172
173 // Paper orientation
174 typedef Translator<string, PAPER_ORIENTATION> PaperOrientationTranslator;
175
176
177 PaperOrientationTranslator const init_paperorientationtranslator()
178 {
179         PaperOrientationTranslator translator(string_orientation[0], ORIENTATION_PORTRAIT);
180         translator.addPair(string_orientation[1], ORIENTATION_LANDSCAPE);
181         return translator;
182 }
183
184
185 PaperOrientationTranslator const & paperorientationtranslator()
186 {
187         static PaperOrientationTranslator translator = init_paperorientationtranslator();
188         return translator;
189 }
190
191
192 // Page sides
193 typedef Translator<int, PageSides> SidesTranslator;
194
195
196 SidesTranslator const init_sidestranslator()
197 {
198         SidesTranslator translator(1, OneSide);
199         translator.addPair(2, TwoSides);
200         return translator;
201 }
202
203
204 SidesTranslator const & sidestranslator()
205 {
206         static SidesTranslator translator = init_sidestranslator();
207         return translator;
208 }
209
210
211 // LaTeX packages
212 typedef Translator<int, BufferParams::Package> PackageTranslator;
213
214
215 PackageTranslator const init_packagetranslator()
216 {
217         PackageTranslator translator(0, BufferParams::package_off);
218         translator.addPair(1, BufferParams::package_auto);
219         translator.addPair(2, BufferParams::package_on);
220         return translator;
221 }
222
223
224 PackageTranslator const & packagetranslator()
225 {
226         static PackageTranslator translator = init_packagetranslator();
227         return translator;
228 }
229
230
231 // Cite engine
232 typedef Translator<string, CiteEngine> CiteEngineTranslator;
233
234
235 CiteEngineTranslator const init_citeenginetranslator()
236 {
237         CiteEngineTranslator translator("basic", ENGINE_BASIC);
238         translator.addPair("natbib_numerical", ENGINE_NATBIB_NUMERICAL);
239         translator.addPair("natbib_authoryear", ENGINE_NATBIB_AUTHORYEAR);
240         translator.addPair("jurabib", ENGINE_JURABIB);
241         return translator;
242 }
243
244
245 CiteEngineTranslator const & citeenginetranslator()
246 {
247         static CiteEngineTranslator translator = init_citeenginetranslator();
248         return translator;
249 }
250
251
252 // Spacing
253 typedef Translator<string, Spacing::Space> SpaceTranslator;
254
255
256 SpaceTranslator const init_spacetranslator()
257 {
258         SpaceTranslator translator("default", Spacing::Default);
259         translator.addPair("single", Spacing::Single);
260         translator.addPair("onehalf", Spacing::Onehalf);
261         translator.addPair("double", Spacing::Double);
262         translator.addPair("other", Spacing::Other);
263         return translator;
264 }
265
266
267 SpaceTranslator const & spacetranslator()
268 {
269         static SpaceTranslator translator = init_spacetranslator();
270         return translator;
271 }
272
273
274 } // anon namespace
275
276
277 class BufferParams::Impl
278 {
279 public:
280         Impl();
281
282         AuthorList authorlist;
283         BranchList branchlist;
284         Bullet temp_bullets[4];
285         Bullet user_defined_bullets[4];
286         Spacing spacing;
287         /** This is the amount of space used for paragraph_separation "skip",
288          * and for detached paragraphs in "indented" documents.
289          */
290         VSpace defskip;
291         PDFOptions pdfoptions;
292         LayoutFileIndex baseClass_;
293 };
294
295
296 BufferParams::Impl::Impl()
297         : defskip(VSpace::MEDSKIP), baseClass_(string(""))
298 {
299         // set initial author
300         // FIXME UNICODE
301         authorlist.record(Author(from_utf8(lyxrc.user_name), from_utf8(lyxrc.user_email)));
302 }
303
304
305 BufferParams::Impl *
306 BufferParams::MemoryTraits::clone(BufferParams::Impl const * ptr)
307 {
308         LASSERT(ptr, /**/);
309
310         return new BufferParams::Impl(*ptr);
311 }
312
313
314 void BufferParams::MemoryTraits::destroy(BufferParams::Impl * ptr)
315 {
316         delete ptr;
317 }
318
319
320 BufferParams::BufferParams()
321         : pimpl_(new Impl)
322 {
323         setBaseClass(defaultBaseclass());
324         makeDocumentClass();
325         paragraph_separation = ParagraphIndentSeparation;
326         quotes_language = InsetQuotes::EnglishQuotes;
327         fontsize = "default";
328
329         /*  PaperLayout */
330         papersize = PAPER_DEFAULT;
331         orientation = ORIENTATION_PORTRAIT;
332         use_geometry = false;
333         use_amsmath = package_auto;
334         use_esint = package_auto;
335         cite_engine_ = ENGINE_BASIC;
336         use_bibtopic = false;
337         trackChanges = false;
338         outputChanges = false;
339         use_default_options = true;
340         secnumdepth = 3;
341         tocdepth = 3;
342         language = default_language;
343         fontsRoman = "default";
344         fontsSans = "default";
345         fontsTypewriter = "default";
346         fontsDefaultFamily = "default";
347         fontsSC = false;
348         fontsOSF = false;
349         fontsSansScale = 100;
350         fontsTypewriterScale = 100;
351         inputenc = "auto";
352         graphicsDriver = "default";
353         sides = OneSide;
354         columns = 1;
355         listings_params = string();
356         pagestyle = "default";
357         compressed = false;
358         for (int iter = 0; iter < 4; ++iter) {
359                 user_defined_bullet(iter) = ITEMIZE_DEFAULTS[iter];
360                 temp_bullet(iter) = ITEMIZE_DEFAULTS[iter];
361         }
362 }
363
364
365 docstring BufferParams::B_(string const & l10n) const
366 {
367         LASSERT(language, /**/);
368         return getMessages(language->code()).get(l10n);
369 }
370
371
372 AuthorList & BufferParams::authors()
373 {
374         return pimpl_->authorlist;
375 }
376
377
378 AuthorList const & BufferParams::authors() const
379 {
380         return pimpl_->authorlist;
381 }
382
383
384 BranchList & BufferParams::branchlist()
385 {
386         return pimpl_->branchlist;
387 }
388
389
390 BranchList const & BufferParams::branchlist() const
391 {
392         return pimpl_->branchlist;
393 }
394
395
396 Bullet & BufferParams::temp_bullet(lyx::size_type const index)
397 {
398         LASSERT(index < 4, /**/);
399         return pimpl_->temp_bullets[index];
400 }
401
402
403 Bullet const & BufferParams::temp_bullet(lyx::size_type const index) const
404 {
405         LASSERT(index < 4, /**/);
406         return pimpl_->temp_bullets[index];
407 }
408
409
410 Bullet & BufferParams::user_defined_bullet(lyx::size_type const index)
411 {
412         LASSERT(index < 4, /**/);
413         return pimpl_->user_defined_bullets[index];
414 }
415
416
417 Bullet const & BufferParams::user_defined_bullet(lyx::size_type const index) const
418 {
419         LASSERT(index < 4, /**/);
420         return pimpl_->user_defined_bullets[index];
421 }
422
423
424 Spacing & BufferParams::spacing()
425 {
426         return pimpl_->spacing;
427 }
428
429
430 Spacing const & BufferParams::spacing() const
431 {
432         return pimpl_->spacing;
433 }
434
435
436 PDFOptions & BufferParams::pdfoptions()
437 {
438         return pimpl_->pdfoptions;
439 }
440
441
442 PDFOptions const & BufferParams::pdfoptions() const
443 {
444         return pimpl_->pdfoptions;
445 }
446
447
448 VSpace const & BufferParams::getDefSkip() const
449 {
450         return pimpl_->defskip;
451 }
452
453
454 void BufferParams::setDefSkip(VSpace const & vs)
455 {
456         pimpl_->defskip = vs;
457 }
458
459
460 string BufferParams::readToken(Lexer & lex, string const & token,
461         FileName const & filepath)
462 {
463         if (token == "\\textclass") {
464                 lex.next();
465                 string const classname = lex.getString();
466                 // if there exists a local layout file, ignore the system one
467                 // NOTE: in this case, the textclass (.cls file) is assumed to be available.
468                 string tcp;
469                 LayoutFileList & bcl = LayoutFileList::get();
470                 if (tcp.empty() && !filepath.empty())
471                         tcp = bcl.addLocalLayout(classname, filepath.absFilename());
472                 if (!tcp.empty())
473                         setBaseClass(tcp);
474                 else
475                         setBaseClass(classname);
476                 // We assume that a tex class exists for local or unknown layouts so this warning
477                 // will only be given for system layouts.
478                 if (!baseClass()->isTeXClassAvailable()) {
479                         docstring const msg =
480                                 bformat(_("The layout file requested by this document,\n"
481                                                  "%1$s.layout,\n"
482                                                  "is not usable. This is probably because a LaTeX\n"
483                                                  "class or style file required by it is not\n"
484                                                  "available. See the Customization documentation\n"
485                                                  "for more information.\n"), from_utf8(classname));
486                         frontend::Alert::warning(_("Document class not available"),
487                                        msg + _("LyX will not be able to produce output."));
488                 } 
489         } else if (token == "\\begin_preamble") {
490                 readPreamble(lex);
491         } else if (token == "\\begin_local_layout") {
492                 readLocalLayout(lex);
493         } else if (token == "\\begin_modules") {
494                 readModules(lex);
495         } else if (token == "\\begin_removed_modules") {
496                 readRemovedModules(lex);
497         } else if (token == "\\options") {
498                 lex.eatLine();
499                 options = lex.getString();
500         } else if (token == "\\use_default_options") {
501                 lex >> use_default_options;
502         } else if (token == "\\master") {
503                 lex.eatLine();
504                 master = lex.getString();
505         } else if (token == "\\language") {
506                 readLanguage(lex);
507         } else if (token == "\\inputencoding") {
508                 lex >> inputenc;
509         } else if (token == "\\graphics") {
510                 readGraphicsDriver(lex);
511         } else if (token == "\\font_roman") {
512                 lex >> fontsRoman;
513         } else if (token == "\\font_sans") {
514                 lex >> fontsSans;
515         } else if (token == "\\font_typewriter") {
516                 lex >> fontsTypewriter;
517         } else if (token == "\\font_default_family") {
518                 lex >> fontsDefaultFamily;
519         } else if (token == "\\font_sc") {
520                 lex >> fontsSC;
521         } else if (token == "\\font_osf") {
522                 lex >> fontsOSF;
523         } else if (token == "\\font_sf_scale") {
524                 lex >> fontsSansScale;
525         } else if (token == "\\font_tt_scale") {
526                 lex >> fontsTypewriterScale;
527         } else if (token == "\\font_cjk") {
528                 lex >> fontsCJK;
529         } else if (token == "\\paragraph_separation") {
530                 string parsep;
531                 lex >> parsep;
532                 paragraph_separation = parseptranslator().find(parsep);
533         } else if (token == "\\defskip") {
534                 lex.next();
535                 string defskip = lex.getString();
536                 if (defskip == "defskip")
537                         // this is invalid
538                         defskip = "medskip";
539                 pimpl_->defskip = VSpace(defskip);
540         } else if (token == "\\quotes_language") {
541                 string quotes_lang;
542                 lex >> quotes_lang;
543                 quotes_language = quoteslangtranslator().find(quotes_lang);
544         } else if (token == "\\papersize") {
545                 string ppsize;
546                 lex >> ppsize;
547                 papersize = papersizetranslator().find(ppsize);
548         } else if (token == "\\use_geometry") {
549                 lex >> use_geometry;
550         } else if (token == "\\use_amsmath") {
551                 int use_ams;
552                 lex >> use_ams;
553                 use_amsmath = packagetranslator().find(use_ams);
554         } else if (token == "\\use_esint") {
555                 int useesint;
556                 lex >> useesint;
557                 use_esint = packagetranslator().find(useesint);
558         } else if (token == "\\cite_engine") {
559                 string engine;
560                 lex >> engine;
561                 cite_engine_ = citeenginetranslator().find(engine);
562         } else if (token == "\\use_bibtopic") {
563                 lex >> use_bibtopic;
564         } else if (token == "\\tracking_changes") {
565                 lex >> trackChanges;
566         } else if (token == "\\output_changes") {
567                 lex >> outputChanges;
568         } else if (token == "\\branch") {
569                 lex.next();
570                 docstring branch = lex.getDocString();
571                 branchlist().add(branch);
572                 while (true) {
573                         lex.next();
574                         string const tok = lex.getString();
575                         if (tok == "\\end_branch")
576                                 break;
577                         Branch * branch_ptr = branchlist().find(branch);
578                         if (tok == "\\selected") {
579                                 lex.next();
580                                 if (branch_ptr)
581                                         branch_ptr->setSelected(lex.getInteger());
582                         }
583                         // not yet operational
584                         if (tok == "\\color") {
585                                 lex.eatLine();
586                                 string color = lex.getString();
587                                 if (branch_ptr)
588                                         branch_ptr->setColor(color);
589                                 // Update also the Color table:
590                                 if (color == "none")
591                                         color = lcolor.getX11Name(Color_background);
592                                 // FIXME UNICODE
593                                 lcolor.setColor(to_utf8(branch), color);
594
595                         }
596                 }
597         } else if (token == "\\author") {
598                 lex.eatLine();
599                 istringstream ss(lex.getString());
600                 Author a;
601                 ss >> a;
602                 author_map.push_back(pimpl_->authorlist.record(a));
603         } else if (token == "\\paperorientation") {
604                 string orient;
605                 lex >> orient;
606                 orientation = paperorientationtranslator().find(orient);
607         } else if (token == "\\paperwidth") {
608                 lex >> paperwidth;
609         } else if (token == "\\paperheight") {
610                 lex >> paperheight;
611         } else if (token == "\\leftmargin") {
612                 lex >> leftmargin;
613         } else if (token == "\\topmargin") {
614                 lex >> topmargin;
615         } else if (token == "\\rightmargin") {
616                 lex >> rightmargin;
617         } else if (token == "\\bottommargin") {
618                 lex >> bottommargin;
619         } else if (token == "\\headheight") {
620                 lex >> headheight;
621         } else if (token == "\\headsep") {
622                 lex >> headsep;
623         } else if (token == "\\footskip") {
624                 lex >> footskip;
625         } else if (token == "\\columnsep") {
626                 lex >> columnsep;
627         } else if (token == "\\paperfontsize") {
628                 lex >> fontsize;
629         } else if (token == "\\papercolumns") {
630                 lex >> columns;
631         } else if (token == "\\listings_params") {
632                 string par;
633                 lex >> par;
634                 listings_params = InsetListingsParams(par).params();
635         } else if (token == "\\papersides") {
636                 int psides;
637                 lex >> psides;
638                 sides = sidestranslator().find(psides);
639         } else if (token == "\\paperpagestyle") {
640                 lex >> pagestyle;
641         } else if (token == "\\bullet") {
642                 readBullets(lex);
643         } else if (token == "\\bulletLaTeX") {
644                 readBulletsLaTeX(lex);
645         } else if (token == "\\secnumdepth") {
646                 lex >> secnumdepth;
647         } else if (token == "\\tocdepth") {
648                 lex >> tocdepth;
649         } else if (token == "\\spacing") {
650                 string nspacing;
651                 lex >> nspacing;
652                 string tmp_val;
653                 if (nspacing == "other") {
654                         lex >> tmp_val;
655                 }
656                 spacing().set(spacetranslator().find(nspacing), tmp_val);
657         } else if (token == "\\float_placement") {
658                 lex >> float_placement;
659
660         } else if (prefixIs(token, "\\pdf_") || token == "\\use_hyperref") {
661                 string toktmp = pdfoptions().readToken(lex, token);
662                 if (!toktmp.empty()) {
663                         lyxerr << "PDFOptions::readToken(): Unknown token: " <<
664                                 toktmp << endl;
665                         return toktmp;
666                 }
667         } else {
668                 lyxerr << "BufferParams::readToken(): Unknown token: " << 
669                         token << endl;
670                 return token;
671         }
672
673         return string();
674 }
675
676
677 void BufferParams::writeFile(ostream & os) const
678 {
679         // The top of the file is written by the buffer.
680         // Prints out the buffer info into the .lyx file given by file
681
682         // the textclass
683         os << "\\textclass " << baseClass()->name() << '\n';
684
685         // then the preamble
686         if (!preamble.empty()) {
687                 // remove '\n' from the end of preamble
688                 string const tmppreamble = rtrim(preamble, "\n");
689                 os << "\\begin_preamble\n"
690                    << tmppreamble
691                    << "\n\\end_preamble\n";
692         }
693
694         // the options
695         if (!options.empty()) {
696                 os << "\\options " << options << '\n';
697         }
698
699         // use the class options defined in the layout?
700         os << "\\use_default_options " 
701            << convert<string>(use_default_options) << "\n";
702
703         // the master document
704         if (!master.empty()) {
705                 os << "\\master " << master << '\n';
706         }
707         
708         // removed modules
709         if (!removedModules_.empty()) {
710                 os << "\\begin_removed_modules" << '\n';
711                 set<string>::const_iterator it = removedModules_.begin();
712                 set<string>::const_iterator en = removedModules_.end();
713                 for (; it != en; it++)
714                         os << *it << '\n';
715                 os << "\\end_removed_modules" << '\n';
716         }
717
718         // the modules
719         if (!layoutModules_.empty()) {
720                 os << "\\begin_modules" << '\n';
721                 LayoutModuleList::const_iterator it = layoutModules_.begin();
722                 LayoutModuleList::const_iterator en = layoutModules_.end();
723                 for (; it != en; it++)
724                         os << *it << '\n';
725                 os << "\\end_modules" << '\n';
726         }
727         
728         // local layout information
729         if (!local_layout.empty()) {
730                 // remove '\n' from the end 
731                 string const tmplocal = rtrim(local_layout, "\n");
732                 os << "\\begin_local_layout\n"
733                    << tmplocal
734                    << "\n\\end_local_layout\n";
735         }
736
737         // then the text parameters
738         if (language != ignore_language)
739                 os << "\\language " << language->lang() << '\n';
740         os << "\\inputencoding " << inputenc
741            << "\n\\font_roman " << fontsRoman
742            << "\n\\font_sans " << fontsSans
743            << "\n\\font_typewriter " << fontsTypewriter
744            << "\n\\font_default_family " << fontsDefaultFamily
745            << "\n\\font_sc " << convert<string>(fontsSC)
746            << "\n\\font_osf " << convert<string>(fontsOSF)
747            << "\n\\font_sf_scale " << fontsSansScale
748            << "\n\\font_tt_scale " << fontsTypewriterScale
749            << '\n';
750         if (!fontsCJK.empty()) {
751                 os << "\\font_cjk " << fontsCJK << '\n';
752         }
753         os << "\n\\graphics " << graphicsDriver << '\n';
754
755         if (!float_placement.empty()) {
756                 os << "\\float_placement " << float_placement << '\n';
757         }
758         os << "\\paperfontsize " << fontsize << '\n';
759
760         spacing().writeFile(os);
761         pdfoptions().writeFile(os);
762
763         os << "\\papersize " << string_papersize[papersize]
764            << "\n\\use_geometry " << convert<string>(use_geometry)
765            << "\n\\use_amsmath " << use_amsmath
766            << "\n\\use_esint " << use_esint
767            << "\n\\cite_engine " << citeenginetranslator().find(cite_engine_)
768            << "\n\\use_bibtopic " << convert<string>(use_bibtopic)
769            << "\n\\paperorientation " << string_orientation[orientation]
770            << '\n';
771
772         BranchList::const_iterator it = branchlist().begin();
773         BranchList::const_iterator end = branchlist().end();
774         for (; it != end; ++it) {
775                 os << "\\branch " << to_utf8(it->branch())
776                    << "\n\\selected " << it->isSelected()
777                    << "\n\\color " << lyx::X11hexname(it->color())
778                    << "\n\\end_branch"
779                    << "\n";
780         }
781
782         if (!paperwidth.empty())
783                 os << "\\paperwidth "
784                    << VSpace(paperwidth).asLyXCommand() << '\n';
785         if (!paperheight.empty())
786                 os << "\\paperheight "
787                    << VSpace(paperheight).asLyXCommand() << '\n';
788         if (!leftmargin.empty())
789                 os << "\\leftmargin "
790                    << VSpace(leftmargin).asLyXCommand() << '\n';
791         if (!topmargin.empty())
792                 os << "\\topmargin "
793                    << VSpace(topmargin).asLyXCommand() << '\n';
794         if (!rightmargin.empty())
795                 os << "\\rightmargin "
796                    << VSpace(rightmargin).asLyXCommand() << '\n';
797         if (!bottommargin.empty())
798                 os << "\\bottommargin "
799                    << VSpace(bottommargin).asLyXCommand() << '\n';
800         if (!headheight.empty())
801                 os << "\\headheight "
802                    << VSpace(headheight).asLyXCommand() << '\n';
803         if (!headsep.empty())
804                 os << "\\headsep "
805                    << VSpace(headsep).asLyXCommand() << '\n';
806         if (!footskip.empty())
807                 os << "\\footskip "
808                    << VSpace(footskip).asLyXCommand() << '\n';
809         if (!columnsep.empty())
810                 os << "\\columnsep " 
811                          << VSpace(columnsep).asLyXCommand() << '\n';
812         os << "\\secnumdepth " << secnumdepth
813            << "\n\\tocdepth " << tocdepth
814            << "\n\\paragraph_separation "
815            << string_paragraph_separation[paragraph_separation]
816            << "\n\\defskip " << getDefSkip().asLyXCommand()
817            << "\n\\quotes_language "
818            << string_quotes_language[quotes_language]
819            << "\n\\papercolumns " << columns
820            << "\n\\papersides " << sides
821            << "\n\\paperpagestyle " << pagestyle << '\n';
822         if (!listings_params.empty())
823                 os << "\\listings_params \"" <<
824                         InsetListingsParams(listings_params).encodedString() << "\"\n";
825         for (int i = 0; i < 4; ++i) {
826                 if (user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
827                         if (user_defined_bullet(i).getFont() != -1) {
828                                 os << "\\bullet " << i << " "
829                                    << user_defined_bullet(i).getFont() << " "
830                                    << user_defined_bullet(i).getCharacter() << " "
831                                    << user_defined_bullet(i).getSize() << "\n";
832                         }
833                         else {
834                                 // FIXME UNICODE
835                                 os << "\\bulletLaTeX " << i << " \""
836                                    << lyx::to_ascii(user_defined_bullet(i).getText())
837                                    << "\"\n";
838                         }
839                 }
840         }
841
842         os << "\\tracking_changes " << convert<string>(trackChanges) << "\n";
843         os << "\\output_changes " << convert<string>(outputChanges) << "\n";
844
845         AuthorList::Authors::const_iterator a_it = pimpl_->authorlist.begin();
846         AuthorList::Authors::const_iterator a_end = pimpl_->authorlist.end();
847         for (; a_it != a_end; ++a_it) {
848                 if (a_it->second.used())
849                         os << "\\author " << a_it->second << "\n";
850                 else
851                         os << "\\author " << Author() << "\n";
852         }
853 }
854
855
856 void BufferParams::validate(LaTeXFeatures & features) const
857 {
858         features.require(documentClass().requires());
859
860         if (outputChanges) {
861                 bool dvipost    = LaTeXFeatures::isAvailable("dvipost");
862                 bool xcolorulem = LaTeXFeatures::isAvailable("ulem") &&
863                                   LaTeXFeatures::isAvailable("xcolor");
864
865                 switch (features.runparams().flavor) {
866                 case OutputParams::LATEX:
867                         if (dvipost) {
868                                 features.require("ct-dvipost");
869                                 features.require("dvipost");
870                         } else if (xcolorulem) {
871                                 features.require("ct-xcolor-ulem");
872                                 features.require("ulem");
873                                 features.require("xcolor");
874                         } else {
875                                 features.require("ct-none");
876                         }
877                         break;
878                 case OutputParams::PDFLATEX:
879                         if (xcolorulem) {
880                                 features.require("ct-xcolor-ulem");
881                                 features.require("ulem");
882                                 features.require("xcolor");
883                                 // improves color handling in PDF output
884                                 features.require("pdfcolmk"); 
885                         } else {
886                                 features.require("ct-none");
887                         }
888                         break;
889                 default:
890                         break;
891                 }
892         }
893
894         // Floats with 'Here definitely' as default setting.
895         if (float_placement.find('H') != string::npos)
896                 features.require("float");
897
898         // AMS Style is at document level
899         if (use_amsmath == package_on
900             || documentClass().provides("amsmath"))
901                 features.require("amsmath");
902         if (use_esint == package_on)
903                 features.require("esint");
904
905         // Document-level line spacing
906         if (spacing().getSpace() != Spacing::Single && !spacing().isDefault())
907                 features.require("setspace");
908
909         // the bullet shapes are buffer level not paragraph level
910         // so they are tested here
911         for (int i = 0; i < 4; ++i) {
912                 if (user_defined_bullet(i) == ITEMIZE_DEFAULTS[i]) 
913                         continue;
914                 int const font = user_defined_bullet(i).getFont();
915                 if (font == 0) {
916                         int const c = user_defined_bullet(i).getCharacter();
917                         if (c == 16
918                             || c == 17
919                             || c == 25
920                             || c == 26
921                             || c == 31) {
922                                 features.require("latexsym");
923                         }
924                 } else if (font == 1) {
925                         features.require("amssymb");
926                 } else if (font >= 2 && font <= 5) {
927                         features.require("pifont");
928                 }
929         }
930
931         if (pdfoptions().use_hyperref) {
932                 features.require("hyperref");
933                 // due to interferences with babel and hyperref, the color package has to
934                 // be loaded after hyperref when hyperref is used with the colorlinks
935                 // option, see http://bugzilla.lyx.org/show_bug.cgi?id=5291
936                 if (pdfoptions().colorlinks)
937                         features.require("color");
938         }
939
940         if (language->lang() == "vietnamese")
941                 features.require("vietnamese");
942         else if (language->lang() == "japanese")
943                 features.require("japanese");
944 }
945
946
947 bool BufferParams::writeLaTeX(odocstream & os, LaTeXFeatures & features,
948                               TexRow & texrow) const
949 {
950         os << "\\documentclass";
951
952         DocumentClass const & tclass = documentClass();
953
954         ostringstream clsoptions; // the document class options.
955
956         if (tokenPos(tclass.opt_fontsize(),
957                      '|', fontsize) >= 0) {
958                 // only write if existing in list (and not default)
959                 clsoptions << fontsize << "pt,";
960         }
961
962         // custom, A3, B3 and B4 paper sizes need geometry
963         bool nonstandard_papersize = papersize == PAPER_B3
964                 || papersize == PAPER_B4
965                 || papersize == PAPER_A3
966                 || papersize == PAPER_CUSTOM;
967
968         if (!use_geometry) {
969                 switch (papersize) {
970                 case PAPER_A4:
971                         clsoptions << "a4paper,";
972                         break;
973                 case PAPER_USLETTER:
974                         clsoptions << "letterpaper,";
975                         break;
976                 case PAPER_A5:
977                         clsoptions << "a5paper,";
978                         break;
979                 case PAPER_B5:
980                         clsoptions << "b5paper,";
981                         break;
982                 case PAPER_USEXECUTIVE:
983                         clsoptions << "executivepaper,";
984                         break;
985                 case PAPER_USLEGAL:
986                         clsoptions << "legalpaper,";
987                         break;
988                 case PAPER_DEFAULT:
989                 case PAPER_A3:
990                 case PAPER_B3:
991                 case PAPER_B4:
992                 case PAPER_CUSTOM:
993                         break;
994                 }
995         }
996
997         // if needed
998         if (sides != tclass.sides()) {
999                 switch (sides) {
1000                 case OneSide:
1001                         clsoptions << "oneside,";
1002                         break;
1003                 case TwoSides:
1004                         clsoptions << "twoside,";
1005                         break;
1006                 }
1007         }
1008
1009         // if needed
1010         if (columns != tclass.columns()) {
1011                 if (columns == 2)
1012                         clsoptions << "twocolumn,";
1013                 else
1014                         clsoptions << "onecolumn,";
1015         }
1016
1017         if (!use_geometry
1018             && orientation == ORIENTATION_LANDSCAPE)
1019                 clsoptions << "landscape,";
1020
1021         // language should be a parameter to \documentclass
1022         if (language->babel() == "hebrew"
1023             && default_language->babel() != "hebrew")
1024                 // This seems necessary
1025                 features.useLanguage(default_language);
1026
1027         ostringstream language_options;
1028         bool const use_babel = features.useBabel();
1029         if (use_babel) {
1030                 language_options << features.getLanguages();
1031                 if (!language->babel().empty()) {
1032                         if (!language_options.str().empty())
1033                                 language_options << ',';
1034                         language_options << language->babel();
1035                 }
1036                 // if Vietnamese is used, babel must directly be loaded
1037                 // with language options, not in the class options, see
1038                 // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129417.html
1039                 size_t viet = language_options.str().find("vietnam");
1040                 // viet = string::npos when not found
1041                 // the same is for all other languages that are not directly supported by
1042                 // babel, but where LaTeX-packages add babel support.
1043                 // this is currently the case for Latvian, Lithuanian, and Mongolian
1044                 size_t latvian = language_options.str().find("latvian");
1045                 size_t lithu = language_options.str().find("lithuanian");
1046                 size_t mongo = language_options.str().find("mongolian");
1047                 // if Japanese is used, babel must directly be loaded
1048                 // with language options, not in the class options, see
1049                 // http://bugzilla.lyx.org/show_bug.cgi?id=4597#c4
1050                 size_t japan = language_options.str().find("japanese");
1051                 if (lyxrc.language_global_options && !language_options.str().empty()
1052                         && viet == string::npos && japan == string::npos
1053                         && latvian == string::npos && lithu == string::npos
1054                         && mongo == string::npos)
1055                         clsoptions << language_options.str() << ',';
1056         }
1057
1058         // the predefined options from the layout
1059         if (use_default_options && !tclass.options().empty())
1060                 clsoptions << tclass.options() << ',';
1061
1062         // the user-defined options
1063         if (!options.empty()) {
1064                 clsoptions << options << ',';
1065         }
1066
1067         string strOptions(clsoptions.str());
1068         if (!strOptions.empty()) {
1069                 strOptions = rtrim(strOptions, ",");
1070                 // FIXME UNICODE
1071                 os << '[' << from_utf8(strOptions) << ']';
1072         }
1073
1074         os << '{' << from_ascii(tclass.latexname()) << "}\n";
1075         texrow.newline();
1076         // end of \documentclass defs
1077
1078         // font selection must be done before loading fontenc.sty
1079         string const fonts =
1080                 loadFonts(fontsRoman, fontsSans,
1081                           fontsTypewriter, fontsSC, fontsOSF,
1082                           fontsSansScale, fontsTypewriterScale);
1083         if (!fonts.empty()) {
1084                 os << from_ascii(fonts);
1085                 texrow.newline();
1086         }
1087         if (fontsDefaultFamily != "default")
1088                 os << "\\renewcommand{\\familydefault}{\\"
1089                    << from_ascii(fontsDefaultFamily) << "}\n";
1090
1091         // set font encoding
1092         // this one is not per buffer
1093         // for arabic_arabi and farsi we also need to load the LAE and
1094         // LFE encoding
1095         if (lyxrc.fontenc != "default" && language->lang() != "japanese") {
1096                 if (language->lang() == "arabic_arabi"
1097                     || language->lang() == "farsi") {
1098                         os << "\\usepackage[" << from_ascii(lyxrc.fontenc)
1099                            << ",LFE,LAE]{fontenc}\n";
1100                         texrow.newline();
1101                 } else {
1102                         os << "\\usepackage[" << from_ascii(lyxrc.fontenc)
1103                            << "]{fontenc}\n";
1104                         texrow.newline();
1105                 }
1106         }
1107
1108         // handle inputenc etc.
1109         writeEncodingPreamble(os, features, texrow);
1110
1111         if (!listings_params.empty() || features.isRequired("listings")) {
1112                 os << "\\usepackage{listings}\n";
1113                 texrow.newline();
1114         }
1115         if (!listings_params.empty()) {
1116                 os << "\\lstset{";
1117                 // do not test validity because listings_params is 
1118                 // supposed to be valid
1119                 string par =
1120                         InsetListingsParams(listings_params).separatedParams(true);
1121                 // we can't support all packages, but we should load the color package
1122                 if (par.find("\\color", 0) != string::npos)
1123                         features.require("color");
1124                 os << from_utf8(par);
1125                 // count the number of newlines
1126                 for (size_t i = 0; i < par.size(); ++i)
1127                         if (par[i] == '\n')
1128                                 texrow.newline();
1129                 os << "}\n";
1130                 texrow.newline();
1131         }
1132         if (use_geometry || nonstandard_papersize) {
1133                 odocstringstream ods;
1134                 if (!getGraphicsDriver("geometry").empty())
1135                         ods << getGraphicsDriver("geometry");
1136                 if (orientation == ORIENTATION_LANDSCAPE)
1137                         ods << ",landscape";
1138                 switch (papersize) {
1139                 case PAPER_CUSTOM:
1140                         if (!paperwidth.empty())
1141                                 ods << ",paperwidth="
1142                                    << from_ascii(paperwidth);
1143                         if (!paperheight.empty())
1144                                 ods << ",paperheight="
1145                                    << from_ascii(paperheight);
1146                         break;
1147                 case PAPER_USLETTER:
1148                         ods << ",letterpaper";
1149                         break;
1150                 case PAPER_USLEGAL:
1151                         ods << ",legalpaper";
1152                         break;
1153                 case PAPER_USEXECUTIVE:
1154                         ods << ",executivepaper";
1155                         break;
1156                 case PAPER_A3:
1157                         ods << ",a3paper";
1158                         break;
1159                 case PAPER_A4:
1160                         ods << ",a4paper";
1161                         break;
1162                 case PAPER_A5:
1163                         ods << ",a5paper";
1164                         break;
1165                 case PAPER_B3:
1166                         ods << ",b3paper";
1167                         break;
1168                 case PAPER_B4:
1169                         ods << ",b4paper";
1170                         break;
1171                 case PAPER_B5:
1172                         ods << ",b5paper";
1173                         break;
1174                 default:
1175                         // default papersize ie PAPER_DEFAULT
1176                         switch (lyxrc.default_papersize) {
1177                         case PAPER_DEFAULT: // keep compiler happy
1178                         case PAPER_USLETTER:
1179                                 ods << ",letterpaper";
1180                                 break;
1181                         case PAPER_USLEGAL:
1182                                 ods << ",legalpaper";
1183                                 break;
1184                         case PAPER_USEXECUTIVE:
1185                                 ods << ",executivepaper";
1186                                 break;
1187                         case PAPER_A3:
1188                                 ods << ",a3paper";
1189                                 break;
1190                         case PAPER_A4:
1191                                 ods << ",a4paper";
1192                                 break;
1193                         case PAPER_A5:
1194                                 ods << ",a5paper";
1195                                 break;
1196                         case PAPER_B5:
1197                                 ods << ",b5paper";
1198                                 break;
1199                         case PAPER_B3:
1200                         case PAPER_B4:
1201                         case PAPER_CUSTOM:
1202                                 break;
1203                         }
1204                 }
1205                 docstring const g_options = trim(ods.str(), ",");
1206                 os << "\\usepackage";
1207                 if (!g_options.empty())
1208                         os << '[' << g_options << ']';
1209                 os << "{geometry}\n";
1210                 texrow.newline();
1211                 os << "\\geometry{verbose";
1212                 if (!topmargin.empty())
1213                         os << ",tmargin=" << from_ascii(Length(topmargin).asLatexString());
1214                 if (!bottommargin.empty())
1215                         os << ",bmargin=" << from_ascii(Length(bottommargin).asLatexString());
1216                 if (!leftmargin.empty())
1217                         os << ",lmargin=" << from_ascii(Length(leftmargin).asLatexString());
1218                 if (!rightmargin.empty())
1219                         os << ",rmargin=" << from_ascii(Length(rightmargin).asLatexString());
1220                 if (!headheight.empty())
1221                         os << ",headheight=" << from_ascii(Length(headheight).asLatexString());
1222                 if (!headsep.empty())
1223                         os << ",headsep=" << from_ascii(Length(headsep).asLatexString());
1224                 if (!footskip.empty())
1225                         os << ",footskip=" << from_ascii(Length(footskip).asLatexString());
1226                 if (!columnsep.empty())
1227                         os << ",columnsep=" << from_ascii(Length(columnsep).asLatexString());
1228                 os << "}\n";
1229                 texrow.newline();
1230         } else if (orientation == ORIENTATION_LANDSCAPE) {
1231                 features.require("papersize");
1232         }
1233
1234         if (tokenPos(tclass.opt_pagestyle(),
1235                      '|', pagestyle) >= 0) {
1236                 if (pagestyle == "fancy") {
1237                         os << "\\usepackage{fancyhdr}\n";
1238                         texrow.newline();
1239                 }
1240                 os << "\\pagestyle{" << from_ascii(pagestyle) << "}\n";
1241                 texrow.newline();
1242         }
1243
1244         // Only if class has a ToC hierarchy
1245         if (tclass.hasTocLevels()) {
1246                 if (secnumdepth != tclass.secnumdepth()) {
1247                         os << "\\setcounter{secnumdepth}{"
1248                            << secnumdepth
1249                            << "}\n";
1250                         texrow.newline();
1251                 }
1252                 if (tocdepth != tclass.tocdepth()) {
1253                         os << "\\setcounter{tocdepth}{"
1254                            << tocdepth
1255                            << "}\n";
1256                         texrow.newline();
1257                 }
1258         }
1259
1260         if (paragraph_separation) {
1261                 switch (getDefSkip().kind()) {
1262                 case VSpace::SMALLSKIP:
1263                         os << "\\setlength{\\parskip}{\\smallskipamount}\n";
1264                         break;
1265                 case VSpace::MEDSKIP:
1266                         os << "\\setlength{\\parskip}{\\medskipamount}\n";
1267                         break;
1268                 case VSpace::BIGSKIP:
1269                         os << "\\setlength{\\parskip}{\\bigskipamount}\n";
1270                         break;
1271                 case VSpace::LENGTH:
1272                         os << "\\setlength{\\parskip}{"
1273                            << from_utf8(getDefSkip().length().asLatexString())
1274                            << "}\n";
1275                         break;
1276                 default: // should never happen // Then delete it.
1277                         os << "\\setlength{\\parskip}{\\medskipamount}\n";
1278                         break;
1279                 }
1280                 texrow.newline();
1281
1282                 os << "\\setlength{\\parindent}{0pt}\n";
1283                 texrow.newline();
1284         }
1285
1286         // Now insert the LyX specific LaTeX commands...
1287         docstring lyxpreamble;
1288
1289         // due to interferences with babel and hyperref, the color package has to
1290         // be loaded (when it is not already loaded) before babel when hyperref
1291         // is used with the colorlinks option, see
1292         // http://bugzilla.lyx.org/show_bug.cgi?id=5291
1293         // we decided therefore to load color always before babel, see
1294         // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg144349.html
1295         lyxpreamble += from_ascii(features.getColorOptions());
1296         
1297         // If we use hyperref, jurabib, japanese, or vietnamese, we have to call babel before them.
1298         if (use_babel
1299                 && (features.isRequired("jurabib")
1300                         || features.isRequired("hyperref")
1301                         || features.isRequired("vietnamese")
1302                         || features.isRequired("japanese") ) ) {
1303                                 // FIXME UNICODE
1304                                 lyxpreamble += from_utf8(babelCall(language_options.str())) + '\n';
1305                                 lyxpreamble += from_utf8(features.getBabelOptions()) + '\n';
1306         }
1307
1308         // The optional packages;
1309         lyxpreamble += from_ascii(features.getPackages());
1310
1311         // Line spacing
1312         lyxpreamble += from_utf8(spacing().writePreamble(tclass.provides("SetSpace")));
1313
1314         // PDF support.
1315         // * Hyperref manual: "Make sure it comes last of your loaded
1316         //   packages, to give it a fighting chance of not being over-written,
1317         //   since its job is to redefine many LaTeX commands."
1318         // * Email from Heiko Oberdiek: "It is usually better to load babel
1319         //   before hyperref. Then hyperref has a chance to detect babel.
1320         // * Has to be loaded before the "LyX specific LaTeX commands" to
1321         //   avoid errors with algorithm floats.
1322         // use hyperref explicitely when it is required
1323         if (features.isRequired("hyperref")) {
1324                 odocstringstream oss;
1325                 pdfoptions().writeLaTeX(oss, documentClass().provides("hyperref"));
1326                 lyxpreamble += oss.str();
1327         }
1328         
1329         // Will be surrounded by \makeatletter and \makeatother when needed
1330         docstring atlyxpreamble;
1331
1332         // Some macros LyX will need
1333         docstring tmppreamble(from_ascii(features.getMacros()));
1334
1335         if (!tmppreamble.empty())
1336                 atlyxpreamble += "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
1337                         "LyX specific LaTeX commands.\n"
1338                         + tmppreamble + '\n';
1339
1340         // the text class specific preamble
1341         tmppreamble = features.getTClassPreamble();
1342         if (!tmppreamble.empty())
1343                 atlyxpreamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
1344                         "Textclass specific LaTeX commands.\n"
1345                         + tmppreamble + '\n';
1346
1347         /* the user-defined preamble */
1348         if (!preamble.empty())
1349                 // FIXME UNICODE
1350                 atlyxpreamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
1351                         "User specified LaTeX commands.\n"
1352                         + from_utf8(preamble) + '\n';
1353
1354         // subfig loads internally the LaTeX package "caption". As
1355         // caption is a very popular package, users will load it in
1356         // the preamble. Therefore we must load subfig behind the
1357         // user-defined preamble and check if the caption package was
1358         // loaded or not. For the case that caption is loaded before
1359         // subfig, there is the subfig option "caption=false". This
1360         // option also works when a koma-script class is used and
1361         // koma's own caption commands are used instead of caption. We
1362         // use \PassOptionsToPackage here because the user could have
1363         // already loaded subfig in the preamble.
1364         if (features.isRequired("subfig")) {
1365                 atlyxpreamble += "\\@ifundefined{showcaptionsetup}{}{%\n"
1366                         " \\PassOptionsToPackage{caption=false}{subfig}}\n"
1367                         "\\usepackage{subfig}\n";
1368         }
1369
1370         // Itemize bullet settings need to be last in case the user
1371         // defines their own bullets that use a package included
1372         // in the user-defined preamble -- ARRae
1373         // Actually it has to be done much later than that
1374         // since some packages like frenchb make modifications
1375         // at \begin{document} time -- JMarc
1376         docstring bullets_def;
1377         for (int i = 0; i < 4; ++i) {
1378                 if (user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1379                         if (bullets_def.empty())
1380                                 bullets_def += "\\AtBeginDocument{\n";
1381                         bullets_def += "  \\def\\labelitemi";
1382                         switch (i) {
1383                                 // `i' is one less than the item to modify
1384                         case 0:
1385                                 break;
1386                         case 1:
1387                                 bullets_def += 'i';
1388                                 break;
1389                         case 2:
1390                                 bullets_def += "ii";
1391                                 break;
1392                         case 3:
1393                                 bullets_def += 'v';
1394                                 break;
1395                         }
1396                         bullets_def += '{' +
1397                                 user_defined_bullet(i).getText()
1398                                 + "}\n";
1399                 }
1400         }
1401
1402         if (!bullets_def.empty())
1403                 atlyxpreamble += bullets_def + "}\n\n";
1404
1405         if (atlyxpreamble.find(from_ascii("@")) != docstring::npos)
1406                 lyxpreamble += "\n\\makeatletter\n"
1407                         + atlyxpreamble + "\\makeatother\n\n";
1408         else
1409                 lyxpreamble += '\n' + atlyxpreamble;
1410
1411         // We try to load babel late, in case it interferes with other packages.
1412         // Jurabib and Hyperref have to be called after babel, though.
1413         if (use_babel && !features.isRequired("jurabib")
1414             && !features.isRequired("hyperref")
1415             && !features.isRequired("vietnamese")
1416             && !features.isRequired("japanese")) {
1417                 // FIXME UNICODE
1418                 lyxpreamble += from_utf8(babelCall(language_options.str())) + '\n';
1419                 lyxpreamble += from_utf8(features.getBabelOptions()) + '\n';
1420         }
1421
1422         docstring const i18npreamble = features.getTClassI18nPreamble(use_babel);
1423         if (!i18npreamble.empty())
1424                 lyxpreamble += i18npreamble + '\n';
1425
1426         int const nlines =
1427                 int(count(lyxpreamble.begin(), lyxpreamble.end(), '\n'));
1428         for (int j = 0; j != nlines; ++j) {
1429                 texrow.newline();
1430         }
1431
1432         os << lyxpreamble;
1433         return use_babel;
1434 }
1435
1436
1437 void BufferParams::useClassDefaults()
1438 {
1439         DocumentClass const & tclass = documentClass();
1440
1441         sides = tclass.sides();
1442         columns = tclass.columns();
1443         pagestyle = tclass.pagestyle();
1444         use_default_options = true;
1445         // Only if class has a ToC hierarchy
1446         if (tclass.hasTocLevels()) {
1447                 secnumdepth = tclass.secnumdepth();
1448                 tocdepth = tclass.tocdepth();
1449         }
1450 }
1451
1452
1453 bool BufferParams::hasClassDefaults() const
1454 {
1455         DocumentClass const & tclass = documentClass();
1456
1457         return sides == tclass.sides()
1458                 && columns == tclass.columns()
1459                 && pagestyle == tclass.pagestyle()
1460                 && use_default_options
1461                 && secnumdepth == tclass.secnumdepth()
1462                 && tocdepth == tclass.tocdepth();
1463 }
1464
1465
1466 DocumentClass const & BufferParams::documentClass() const
1467 {
1468         return *doc_class_;
1469 }
1470
1471
1472 DocumentClass const * BufferParams::documentClassPtr() const {
1473         return doc_class_;
1474 }
1475
1476
1477 void BufferParams::setDocumentClass(DocumentClass const * const tc) {
1478         // evil, but this function is evil
1479         doc_class_ = const_cast<DocumentClass *>(tc);
1480 }
1481
1482
1483 bool BufferParams::removeBadModules()
1484 {
1485         // we'll write a new list of modules, since we can't just remove them,
1486         // as that would invalidate our iterators
1487         LayoutModuleList oldModules = layoutModules_;
1488         clearLayoutModules();
1489
1490         LayoutModuleList const & provmods = baseClass()->providedModules();
1491         LayoutModuleList const & exclmods = baseClass()->excludedModules();
1492         bool consistent = true; // set to false if we have to do anything
1493
1494         LayoutModuleList::const_iterator oit = oldModules.begin();
1495         LayoutModuleList::const_iterator const oen = oldModules.end();
1496         for (; oit != oen; ++oit) {
1497                 string const & modname = *oit;
1498                 // skip modules that the class provides
1499                 if (find(provmods.begin(), provmods.end(), modname) != provmods.end()) {
1500                         LYXERR0("Module `" << modname << "' dropped because provided by document class.");
1501                         consistent = false;
1502                         continue;
1503                 }
1504                 // are we excluded by the document class?
1505                 if (find(exclmods.begin(), exclmods.end(), modname) != exclmods.end()) {
1506                         LYXERR0("Module `" << modname << "' dropped because excluded by document class.");
1507                         consistent = false;
1508                         continue;
1509                 }
1510                 // determine whether some provided module excludes us or we exclude it
1511                 LayoutModuleList::const_iterator pit = provmods.begin();
1512                 LayoutModuleList::const_iterator const pen = provmods.end();
1513                 bool excluded = false;
1514                 for (; !excluded && pit != pen; ++pit) {
1515                         if (!LyXModule::areCompatible(modname, *pit)) {
1516                                 LYXERR0("Module " << modname << 
1517                                                 " dropped becuase it conflicts with provided module `" << *pit << "'.");
1518                                 consistent = false;
1519                                 excluded = true;
1520                         }
1521                 }
1522                 if (excluded)
1523                         continue;
1524                 layoutModules_.push_back(modname);
1525         }
1526         return consistent;
1527 }
1528
1529
1530 void BufferParams::addDefaultModules()
1531 {
1532         // add any default modules not already in use
1533         LayoutModuleList const & mods = baseClass()->defaultModules();
1534         LayoutModuleList::const_iterator mit = mods.begin();
1535         LayoutModuleList::const_iterator men = mods.end();
1536
1537         // We want to insert the default modules at the beginning of
1538         // the list, but also to insert them in the correct order.
1539         // The obvious thing to do would be to collect them and then
1540         // insert them, but that doesn't work because a later default
1541         // module may require an earlier one, and then the test below
1542         //     moduleCanBeAdded(modname)
1543         // will fail. So we have to do it a more complicated way.
1544         LayoutModuleList::iterator insertpos = layoutModules_.begin();
1545         int numinserts = 0;
1546
1547         for (; mit != men; mit++) {
1548                 string const & modName = *mit;
1549                 // make sure the user hasn't removed it
1550                 if (find(removedModules_.begin(), removedModules_.end(), modName) !=
1551                     removedModules_.end()) {
1552                         LYXERR(Debug::TCLASS, "Default module `" << modName << 
1553                                         "' not added because removed by user.");
1554                         continue;
1555                 }
1556
1557                 if (!moduleCanBeAdded(modName)) {
1558                         // FIXME This could be because it's already present, so we should
1559                         // probably return something indicating that.
1560                         LYXERR(Debug::TCLASS, "Default module `" << modName << 
1561                                         "' could not be added.");
1562                         continue;
1563                 }
1564                 LYXERR(Debug::TCLASS, "Default module `" << modName << "' added.");
1565                 layoutModules_.insert(insertpos, modName);
1566                 // now we reset insertpos
1567                 ++numinserts;
1568                 insertpos = layoutModules_.begin();
1569                 advance(insertpos, numinserts);
1570         }
1571 }
1572
1573
1574 // Perform a consistency check on the set of modules. We need to make
1575 // sure that none of the modules exclude each other and that requires
1576 // are satisfied.
1577 bool BufferParams::checkModuleConsistency() {
1578         bool consistent = true;
1579         LayoutModuleList oldModules = layoutModules_;
1580         clearLayoutModules();
1581         LayoutModuleList::const_iterator oit = oldModules.begin();
1582         LayoutModuleList::const_iterator oen = oldModules.end();
1583         LayoutModuleList const & provmods = baseClass()->providedModules();
1584         for (; oit != oen; ++oit) {
1585                 string const & modname = *oit;
1586                 bool excluded = false;
1587                 // Determine whether some prior module excludes us, or we exclude it
1588                 LayoutModuleList::const_iterator lit = layoutModules_.begin();
1589                 LayoutModuleList::const_iterator len = layoutModules_.end();
1590                 for (; !excluded && lit != len; ++lit) {
1591                         if (!LyXModule::areCompatible(modname, *lit)) {
1592                                 consistent = false;
1593                                 LYXERR0("Module " << modname << 
1594                                                 " dropped because it is excluded by prior module " << *lit);
1595                                 excluded = true;
1596                         }
1597                 }
1598
1599                 if (excluded)
1600                         continue;
1601
1602                 // determine whether some provided module or some prior module
1603                 // satisfies our requirements
1604                 LyXModule const * const oldmod = moduleList[modname];
1605                 if (!oldmod) {
1606                         LYXERR0("Default module " << modname << 
1607                                         " added although it is unavailable and can't check requirements.");
1608                         continue;
1609                 }
1610                         
1611                 vector<string> const & reqs = oldmod->getRequiredModules();
1612                 if (!reqs.empty()) {
1613                         // we now set excluded to true, meaning that we haven't
1614                         // yet found a required module.
1615                         excluded = true;
1616                         vector<string>::const_iterator rit  = reqs.begin();
1617                         vector<string>::const_iterator ren = reqs.end();
1618                         for (; rit != ren; ++rit) {
1619                                 string const reqmod = *rit;
1620                                 if (find(provmods.begin(), provmods.end(), reqmod) != 
1621                                                 provmods.end()) {
1622                                         excluded = false;
1623                                         break;
1624                                 }
1625                                 if (find(layoutModules_.begin(), layoutModules_.end(), reqmod) != 
1626                                                 layoutModules_.end()) {
1627                                         excluded = false;
1628                                         break;
1629                                 }
1630                         }
1631                 }
1632                 if (excluded) {
1633                         consistent = false;
1634                         LYXERR0("Module " << modname << " dropped because requirements not met.");
1635                 } else {
1636                         LYXERR(Debug::TCLASS, "Module " << modname << " passed consistency check.");
1637                         layoutModules_.push_back(modname);
1638                 }
1639         }
1640         return consistent;
1641 }
1642
1643
1644 bool BufferParams::setBaseClass(string const & classname)
1645 {
1646         LYXERR(Debug::TCLASS, "setBaseClass: " << classname);
1647         LayoutFileList & bcl = LayoutFileList::get();
1648         if (!bcl.haveClass(classname)) {
1649                 docstring s = 
1650                         bformat(_("The document class %1$s could not be found. "
1651                                 "A default textclass with default layouts will be used. "
1652                                 "LyX might not be able to produce output unless a correct "
1653                                 "textclass is selected from the document settings dialog."),
1654                         from_utf8(classname));
1655                 frontend::Alert::error(_("Document class not found"), s);
1656                 bcl.addEmptyClass(classname);
1657         }
1658
1659         bool const success = bcl[classname].load();
1660         if (!success) {
1661                 docstring s = 
1662                         bformat(_("The document class %1$s could not be loaded."),
1663                         from_utf8(classname));
1664                 frontend::Alert::error(_("Could not load class"), s);
1665                 return false;
1666         }
1667
1668         pimpl_->baseClass_ = classname;
1669         // the previous document class may have loaded some modules that the
1670         // new one excludes, and the new class may provide, etc, some that
1671         // conflict with ones that were already loaded. So we need to go 
1672         // through the list and fix everything. I suppose there are various
1673         // ways this could be done, but the following seems to work at the 
1674         // moment. (Thanks to Philippe Charpentier for helping work out all 
1675         // the bugs---rgh.)
1676         // 
1677         // first, we remove any modules the new document class itself provides,
1678         // those it excludes, and those that conflict with ones it excludes.
1679         // this has to be done first because, otherwise, a module we're about
1680         // to remove could prevent a default module from being added.
1681         removeBadModules();
1682         // next, we add any default modules the new class provides.
1683         addDefaultModules();
1684         // finally, we perform a general consistency check on the set of
1685         // loaded modules.
1686         checkModuleConsistency();
1687         // FIXME removeBadModules() and checkModuleConsistency() both return
1688         // a boolean indicating whether something had to be changed. It might
1689         // be worth popping a message to the user if so.
1690
1691         return true;
1692 }
1693
1694
1695 LayoutFile const * BufferParams::baseClass() const
1696 {
1697         if (LayoutFileList::get().haveClass(pimpl_->baseClass_))
1698                 return &(LayoutFileList::get()[pimpl_->baseClass_]);
1699         else 
1700                 return 0;
1701 }
1702
1703
1704 LayoutFileIndex const & BufferParams::baseClassID() const
1705 {
1706         return pimpl_->baseClass_;
1707 }
1708
1709
1710 void BufferParams::makeDocumentClass()
1711 {
1712         if (!baseClass())
1713                 return;
1714
1715         doc_class_ = &(DocumentClassBundle::get().makeDocumentClass(*baseClass(), layoutModules_));
1716
1717         if (!local_layout.empty()) {
1718                 if (!doc_class_->read(local_layout, TextClass::MODULE)) {
1719                         docstring const msg = _("Error reading internal layout information");
1720                         frontend::Alert::warning(_("Read Error"), msg);
1721                 }
1722         }
1723 }
1724
1725
1726 bool BufferParams::moduleCanBeAdded(string const & modName) const
1727 {
1728         // Is the module already present?
1729         LayoutModuleList::const_iterator it = layoutModules_.begin();
1730         LayoutModuleList::const_iterator end = layoutModules_.end();
1731         for (; it != end; it++)
1732                 if (*it == modName) 
1733                         return false;
1734
1735         LyXModule const * const lm = moduleList[modName];
1736         if (!lm)
1737                 return true;
1738
1739         // Is this module explicitly excluded by the document class?
1740         LayoutModuleList::const_iterator const exclmodstart = 
1741                         baseClass()->excludedModules().begin();
1742         LayoutModuleList::const_iterator const exclmodend = 
1743                         baseClass()->excludedModules().end();
1744         if (find(exclmodstart, exclmodend, modName) != exclmodend)
1745                 return false;
1746
1747         // Is this module already provided by the document class?
1748         LayoutModuleList::const_iterator const provmodstart = 
1749                         baseClass()->providedModules().begin();
1750         LayoutModuleList::const_iterator const provmodend = 
1751                         baseClass()->providedModules().end();
1752         if (find(provmodstart, provmodend, modName) != provmodend)
1753                 return false;
1754
1755         // Check for conflicts with used modules
1756         // first the provided modules...
1757         LayoutModuleList::const_iterator provmodit = provmodstart;
1758         for (; provmodit != provmodend; ++provmodit) {
1759                 if (!LyXModule::areCompatible(modName, *provmodit))
1760                         return false;
1761         }
1762         // and then the selected modules
1763         LayoutModuleList::const_iterator mit = layoutModules_.begin();
1764         LayoutModuleList::const_iterator const men = layoutModules_.end();
1765         for (; mit != men; ++mit)
1766                 if (!LyXModule::areCompatible(modName, *mit))
1767                         return false;
1768
1769         // Check whether some required module is available
1770         vector<string> const reqs = lm->getRequiredModules();
1771         if (reqs.empty())
1772                 return true;
1773
1774         mit = layoutModules_.begin(); // reset
1775         vector<string>::const_iterator rit = reqs.begin();
1776         vector<string>::const_iterator ren = reqs.end();
1777         bool foundone = false;
1778         for (; rit != ren; ++rit) {
1779                 if (find(mit, men, *rit) != men || 
1780                     find(provmodstart, provmodend, *rit) != provmodend) {
1781                         foundone = true;
1782                         break;
1783                 }
1784         }
1785
1786         return foundone;
1787 }
1788
1789
1790 bool BufferParams::addLayoutModule(string const & modName)
1791 {
1792         LayoutModuleList::const_iterator it = layoutModules_.begin();
1793         LayoutModuleList::const_iterator end = layoutModules_.end();
1794         for (; it != end; it++)
1795                 if (*it == modName) 
1796                         return false;
1797         layoutModules_.push_back(modName);
1798         return true;
1799 }
1800
1801
1802 Font const BufferParams::getFont() const
1803 {
1804         FontInfo f = documentClass().defaultfont();
1805         if (fontsDefaultFamily == "rmdefault")
1806                 f.setFamily(ROMAN_FAMILY);
1807         else if (fontsDefaultFamily == "sfdefault")
1808                 f.setFamily(SANS_FAMILY);
1809         else if (fontsDefaultFamily == "ttdefault")
1810                 f.setFamily(TYPEWRITER_FAMILY);
1811         return Font(f, language);
1812 }
1813
1814
1815 void BufferParams::readPreamble(Lexer & lex)
1816 {
1817         if (lex.getString() != "\\begin_preamble")
1818                 lyxerr << "Error (BufferParams::readPreamble):"
1819                         "consistency check failed." << endl;
1820
1821         preamble = lex.getLongString("\\end_preamble");
1822 }
1823
1824
1825 void BufferParams::readLocalLayout(Lexer & lex)
1826 {
1827         if (lex.getString() != "\\begin_local_layout")
1828                 lyxerr << "Error (BufferParams::readLocalLayout):"
1829                         "consistency check failed." << endl;
1830
1831         local_layout = lex.getLongString("\\end_local_layout");
1832 }
1833
1834
1835 void BufferParams::readLanguage(Lexer & lex)
1836 {
1837         if (!lex.next()) return;
1838
1839         string const tmptok = lex.getString();
1840
1841         // check if tmptok is part of tex_babel in tex-defs.h
1842         language = languages.getLanguage(tmptok);
1843         if (!language) {
1844                 // Language tmptok was not found
1845                 language = default_language;
1846                 lyxerr << "Warning: Setting language `"
1847                        << tmptok << "' to `" << language->lang()
1848                        << "'." << endl;
1849         }
1850 }
1851
1852
1853 void BufferParams::readGraphicsDriver(Lexer & lex)
1854 {
1855         if (!lex.next()) 
1856                 return;
1857
1858         string const tmptok = lex.getString();
1859         // check if tmptok is part of tex_graphics in tex_defs.h
1860         int n = 0;
1861         while (true) {
1862                 string const test = tex_graphics[n++];
1863
1864                 if (test == tmptok) {
1865                         graphicsDriver = tmptok;
1866                         break;
1867                 }
1868                 if (test.empty()) {
1869                         lex.printError(
1870                                 "Warning: graphics driver `$$Token' not recognized!\n"
1871                                 "         Setting graphics driver to `default'.\n");
1872                         graphicsDriver = "default";
1873                         break;
1874                 }
1875         }
1876 }
1877
1878
1879 void BufferParams::readBullets(Lexer & lex)
1880 {
1881         if (!lex.next()) 
1882                 return;
1883
1884         int const index = lex.getInteger();
1885         lex.next();
1886         int temp_int = lex.getInteger();
1887         user_defined_bullet(index).setFont(temp_int);
1888         temp_bullet(index).setFont(temp_int);
1889         lex >> temp_int;
1890         user_defined_bullet(index).setCharacter(temp_int);
1891         temp_bullet(index).setCharacter(temp_int);
1892         lex >> temp_int;
1893         user_defined_bullet(index).setSize(temp_int);
1894         temp_bullet(index).setSize(temp_int);
1895 }
1896
1897
1898 void BufferParams::readBulletsLaTeX(Lexer & lex)
1899 {
1900         // The bullet class should be able to read this.
1901         if (!lex.next()) 
1902                 return;
1903         int const index = lex.getInteger();
1904         lex.next(true);
1905         docstring const temp_str = lex.getDocString();
1906
1907         user_defined_bullet(index).setText(temp_str);
1908         temp_bullet(index).setText(temp_str);
1909 }
1910
1911
1912 void BufferParams::readModules(Lexer & lex)
1913 {
1914         if (!lex.eatLine()) {
1915                 lyxerr << "Error (BufferParams::readModules):"
1916                                 "Unexpected end of input." << endl;
1917                 return;
1918         }
1919         while (true) {
1920                 string mod = lex.getString();
1921                 if (mod == "\\end_modules")
1922                         break;
1923                 addLayoutModule(mod);
1924                 lex.eatLine();
1925         }
1926 }
1927
1928
1929 void BufferParams::readRemovedModules(Lexer & lex)
1930 {
1931         if (!lex.eatLine()) {
1932                 lyxerr << "Error (BufferParams::readRemovedModules):"
1933                                 "Unexpected end of input." << endl;
1934                 return;
1935         }
1936         while (true) {
1937                 string mod = lex.getString();
1938                 if (mod == "\\end_removed_modules")
1939                         break;
1940                 removedModules_.insert(mod);
1941                 lex.eatLine();
1942         }
1943         // now we want to remove any removed modules that were previously 
1944         // added. normally, that will be because default modules were added in 
1945         // setBaseClass(), which gets called when \textclass is read at the 
1946         // start of the read.
1947         set<string>::const_iterator rit = removedModules_.begin();
1948         set<string>::const_iterator const ren = removedModules_.end();
1949         for (; rit != ren; rit++) {
1950                 LayoutModuleList::iterator const mit = layoutModules_.begin();
1951                 LayoutModuleList::iterator const men = layoutModules_.end();
1952                 LayoutModuleList::iterator found = find(mit, men, *rit);
1953                 if (found == men)
1954                         continue;
1955                 layoutModules_.erase(found);
1956         }
1957 }
1958
1959
1960 string BufferParams::paperSizeName(PapersizePurpose purpose) const
1961 {
1962         char real_papersize = papersize;
1963         if (real_papersize == PAPER_DEFAULT)
1964                 real_papersize = lyxrc.default_papersize;
1965
1966         switch (real_papersize) {
1967         case PAPER_DEFAULT:
1968                 // could be anything, so don't guess
1969                 return string();
1970         case PAPER_CUSTOM: {
1971                 if (purpose == XDVI && !paperwidth.empty() &&
1972                     !paperheight.empty()) {
1973                         // heightxwidth<unit>
1974                         string first = paperwidth;
1975                         string second = paperheight;
1976                         if (orientation == ORIENTATION_LANDSCAPE)
1977                                 first.swap(second);
1978                         // cut off unit.
1979                         return first.erase(first.length() - 2)
1980                                 + "x" + second;
1981                 }
1982                 return string();
1983         }
1984         case PAPER_A3:
1985                 return "a3";
1986         case PAPER_A4:
1987                 return "a4";
1988         case PAPER_A5:
1989                 return "a5";
1990         case PAPER_B3:
1991                 // dvips and dvipdfm do not know this
1992                 if (purpose == DVIPS || purpose == DVIPDFM)
1993                         return string();
1994                 return "b3";
1995         case PAPER_B4:
1996                 // dvipdfm does not know this
1997                 if (purpose == DVIPDFM)
1998                         return string();
1999                 return "b4";
2000         case PAPER_B5:
2001                 // dvipdfm does not know this
2002                 if (purpose == DVIPDFM)
2003                         return string();
2004                 return "b5";
2005         case PAPER_USEXECUTIVE:
2006                 // dvipdfm does not know this
2007                 if (purpose == DVIPDFM)
2008                         return string();
2009                 return "foolscap";
2010         case PAPER_USLEGAL:
2011                 return "legal";
2012         case PAPER_USLETTER:
2013         default:
2014                 if (purpose == XDVI)
2015                         return "us";
2016                 return "letter";
2017         }
2018 }
2019
2020
2021 string const BufferParams::dvips_options() const
2022 {
2023         string result;
2024
2025         if (use_geometry
2026             && papersize == PAPER_CUSTOM
2027             && !lyxrc.print_paper_dimension_flag.empty()
2028             && !paperwidth.empty()
2029             && !paperheight.empty()) {
2030                 // using a custom papersize
2031                 result = lyxrc.print_paper_dimension_flag;
2032                 result += ' ' + paperwidth;
2033                 result += ',' + paperheight;
2034         } else {
2035                 string const paper_option = paperSizeName(DVIPS);
2036                 if (!paper_option.empty() && (paper_option != "letter" ||
2037                     orientation != ORIENTATION_LANDSCAPE)) {
2038                         // dvips won't accept -t letter -t landscape.
2039                         // In all other cases, include the paper size
2040                         // explicitly.
2041                         result = lyxrc.print_paper_flag;
2042                         result += ' ' + paper_option;
2043                 }
2044         }
2045         if (orientation == ORIENTATION_LANDSCAPE &&
2046             papersize != PAPER_CUSTOM)
2047                 result += ' ' + lyxrc.print_landscape_flag;
2048         return result;
2049 }
2050
2051
2052 string BufferParams::babelCall(string const & lang_opts) const
2053 {
2054         string lang_pack = lyxrc.language_package;
2055         if (lang_pack != "\\usepackage{babel}")
2056                 return lang_pack;
2057         // suppress the babel call when there is no babel language defined
2058         // for the document language in the lib/languages file and if no
2059         // other languages are used (lang_opts is then empty)
2060         if (lang_opts.empty())
2061                 return string();
2062         // If Vietnamese is used, babel must directly be loaded with the
2063         // language options, see
2064         // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129417.html
2065         size_t viet = lang_opts.find("vietnam");
2066         // viet = string::npos when not found
2067         // the same is for all other languages that are not directly supported by
2068         // babel, but where LaTeX-packages add babel support.
2069         // this is currently the case for Latvian, Lithuanian, and Mongolian
2070         size_t latvian = lang_opts.find("latvian");
2071         size_t lithu = lang_opts.find("lithuanian");
2072         size_t mongo = lang_opts.find("mongolian");
2073         // If Japanese is used, babel must directly be loaded with the
2074         // language options, see
2075         // http://bugzilla.lyx.org/show_bug.cgi?id=4597#c4
2076         size_t japan = lang_opts.find("japanese");
2077         if (!lyxrc.language_global_options || viet != string::npos
2078                 || japan != string::npos || latvian != string::npos
2079                 || lithu != string::npos || mongo != string::npos)
2080                 return "\\usepackage[" + lang_opts + "]{babel}";
2081         return lang_pack;
2082 }
2083
2084
2085 docstring BufferParams::getGraphicsDriver(string const & package) const
2086 {
2087         docstring result;
2088
2089         if (package == "geometry") {
2090                 if (graphicsDriver == "dvips"
2091                     || graphicsDriver == "dvipdfm"
2092                     || graphicsDriver == "pdftex"
2093                     || graphicsDriver == "vtex")
2094                         result = from_ascii(graphicsDriver);
2095                 else if (graphicsDriver == "dvipdfmx")
2096                         result = from_ascii("dvipdfm");
2097         }
2098
2099         return result;
2100 }
2101
2102
2103 void BufferParams::writeEncodingPreamble(odocstream & os,
2104                 LaTeXFeatures & features, TexRow & texrow) const
2105 {
2106         if (inputenc == "auto") {
2107                 string const doc_encoding =
2108                         language->encoding()->latexName();
2109                 Encoding::Package const package =
2110                         language->encoding()->package();
2111
2112                 // Create a list with all the input encodings used
2113                 // in the document
2114                 set<string> encodings =
2115                         features.getEncodingSet(doc_encoding);
2116
2117                 // If the "japanese" package (i.e. pLaTeX) is used,
2118                 // inputenc must be omitted.
2119                 // see http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129680.html
2120                 if (package == Encoding::japanese)
2121                      features.require("japanese");
2122
2123                 if ((!encodings.empty() || package == Encoding::inputenc)
2124                     && !features.isRequired("japanese")) {
2125                         os << "\\usepackage[";
2126                         set<string>::const_iterator it = encodings.begin();
2127                         set<string>::const_iterator const end = encodings.end();
2128                         if (it != end) {
2129                                 os << from_ascii(*it);
2130                                 ++it;
2131                         }
2132                         for (; it != end; ++it)
2133                                 os << ',' << from_ascii(*it);
2134                         if (package == Encoding::inputenc) {
2135                                 if (!encodings.empty())
2136                                         os << ',';
2137                                 os << from_ascii(doc_encoding);
2138                         }
2139                         os << "]{inputenc}\n";
2140                         texrow.newline();
2141                 }
2142                 if (package == Encoding::CJK || features.mustProvide("CJK")) {
2143                         if (language->encoding()->name() == "utf8-cjk"
2144                             && features.isAvailable("CJKutf8"))
2145                                 os << "\\usepackage{CJKutf8}\n";
2146                         else
2147                                 os << "\\usepackage{CJK}\n";
2148                         texrow.newline();
2149                 }
2150         } else if (inputenc != "default") {
2151                 switch (encoding().package()) {
2152                 case Encoding::none:
2153                 case Encoding::japanese:
2154                         break;
2155                 case Encoding::inputenc:
2156                         // do not load inputenc if japanese is used
2157                         if (features.isRequired("japanese"))
2158                                 break;
2159                         os << "\\usepackage[" << from_ascii(inputenc)
2160                            << "]{inputenc}\n";
2161                         texrow.newline();
2162                         break;
2163                 case Encoding::CJK:
2164                         if (encoding().name() == "utf8-cjk"
2165                             && features.isAvailable("CJKutf8"))
2166                                 os << "\\usepackage{CJKutf8}\n";
2167                         else
2168                                 os << "\\usepackage{CJK}\n";
2169                         texrow.newline();
2170                         break;
2171                 }
2172         }
2173
2174         // The encoding "armscii8" (for Armenian) is only available when
2175         // the package "armtex" is loaded.
2176         if (language->encoding()->latexName() == "armscii8"
2177             || inputenc == "armscii8") {
2178                 os << "\\usepackage{armtex}\n";
2179                 texrow.newline();
2180         }
2181 }
2182
2183
2184 string const BufferParams::loadFonts(string const & rm,
2185                                      string const & sf, string const & tt,
2186                                      bool const & sc, bool const & osf,
2187                                      int const & sfscale, int const & ttscale) const
2188 {
2189         /* The LaTeX font world is in a flux. In the PSNFSS font interface,
2190            several packages have been replaced by others, that might not
2191            be installed on every system. We have to take care for that
2192            (see psnfss.pdf). We try to support all psnfss fonts as well
2193            as the fonts that have become de facto standard in the LaTeX
2194            world (e.g. Latin Modern). We do not support obsolete fonts
2195            (like PSLatex). In general, it should be possible to mix any
2196            rm font with any sf or tt font, respectively. (JSpitzm)
2197            TODO:
2198                 -- separate math fonts.
2199         */
2200
2201         if (rm == "default" && sf == "default" && tt == "default")
2202                 //nothing to do
2203                 return string();
2204
2205         ostringstream os;
2206
2207         // ROMAN FONTS
2208         // Computer Modern (must be explicitely selectable -- there might be classes
2209         // that define a different default font!
2210         if (rm == "cmr") {
2211                 os << "\\renewcommand{\\rmdefault}{cmr}\n";
2212                 // osf for Computer Modern needs eco.sty
2213                 if (osf)
2214                         os << "\\usepackage{eco}\n";
2215         }
2216         // Latin Modern Roman
2217         else if (rm == "lmodern")
2218                 os << "\\usepackage{lmodern}\n";
2219         // AE
2220         else if (rm == "ae") {
2221                 // not needed when using OT1 font encoding.
2222                 if (lyxrc.fontenc != "default")
2223                         os << "\\usepackage{ae,aecompl}\n";
2224         }
2225         // Times
2226         else if (rm == "times") {
2227                 // try to load the best available package
2228                 if (LaTeXFeatures::isAvailable("mathptmx"))
2229                         os << "\\usepackage{mathptmx}\n";
2230                 else if (LaTeXFeatures::isAvailable("mathptm"))
2231                         os << "\\usepackage{mathptm}\n";
2232                 else
2233                         os << "\\usepackage{times}\n";
2234         }
2235         // Palatino
2236         else if (rm == "palatino") {
2237                 // try to load the best available package
2238                 if (LaTeXFeatures::isAvailable("mathpazo")) {
2239                         os << "\\usepackage";
2240                         if (osf || sc) {
2241                                 os << '[';
2242                                 if (!osf)
2243                                         os << "sc";
2244                                 else
2245                                         // "osf" includes "sc"!
2246                                         os << "osf";
2247                                 os << ']';
2248                         }
2249                         os << "{mathpazo}\n";
2250                 }
2251                 else if (LaTeXFeatures::isAvailable("mathpple"))
2252                         os << "\\usepackage{mathpple}\n";
2253                 else
2254                         os << "\\usepackage{palatino}\n";
2255         }
2256         // Utopia
2257         else if (rm == "utopia") {
2258                 // fourier supersedes utopia.sty, but does
2259                 // not work with OT1 encoding.
2260                 if (LaTeXFeatures::isAvailable("fourier")
2261                     && lyxrc.fontenc != "default") {
2262                         os << "\\usepackage";
2263                         if (osf || sc) {
2264                                 os << '[';
2265                                 if (sc)
2266                                         os << "expert";
2267                                 if (osf && sc)
2268                                         os << ',';
2269                                 if (osf)
2270                                         os << "oldstyle";
2271                                 os << ']';
2272                         }
2273                         os << "{fourier}\n";
2274                 }
2275                 else
2276                         os << "\\usepackage{utopia}\n";
2277         }
2278         // Bera (complete fontset)
2279         else if (rm == "bera" && sf == "default" && tt == "default")
2280                 os << "\\usepackage{bera}\n";
2281         // everything else
2282         else if (rm != "default")
2283                 os << "\\usepackage" << "{" << rm << "}\n";
2284
2285         // SANS SERIF
2286         // Helvetica, Bera Sans
2287         if (sf == "helvet" || sf == "berasans") {
2288                 if (sfscale != 100)
2289                         os << "\\usepackage[scaled=" << float(sfscale) / 100
2290                            << "]{" << sf << "}\n";
2291                 else
2292                         os << "\\usepackage{" << sf << "}\n";
2293         }
2294         // Avant Garde
2295         else if (sf == "avant")
2296                 os << "\\usepackage{" << sf << "}\n";
2297         // Computer Modern, Latin Modern, CM Bright
2298         else if (sf != "default")
2299                 os << "\\renewcommand{\\sfdefault}{" << sf << "}\n";
2300
2301         // monospaced/typewriter
2302         // Courier, LuxiMono
2303         if (tt == "luximono" || tt == "beramono") {
2304                 if (ttscale != 100)
2305                         os << "\\usepackage[scaled=" << float(ttscale) / 100
2306                            << "]{" << tt << "}\n";
2307                 else
2308                         os << "\\usepackage{" << tt << "}\n";
2309         }
2310         // Courier
2311         else if (tt == "courier" )
2312                 os << "\\usepackage{" << tt << "}\n";
2313         // Computer Modern, Latin Modern, CM Bright
2314         else if (tt != "default")
2315                 os << "\\renewcommand{\\ttdefault}{" << tt << "}\n";
2316
2317         return os.str();
2318 }
2319
2320
2321 Encoding const & BufferParams::encoding() const
2322 {
2323         if (inputenc == "auto" || inputenc == "default")
2324                 return *language->encoding();
2325         Encoding const * const enc = encodings.fromLaTeXName(inputenc);
2326         if (enc)
2327                 return *enc;
2328         LYXERR0("Unknown inputenc value `" << inputenc
2329                << "'. Using `auto' instead.");
2330         return *language->encoding();
2331 }
2332
2333
2334 CiteEngine BufferParams::citeEngine() const
2335 {
2336         // FIXME the class should provide the numerical/
2337         // authoryear choice
2338         if (documentClass().provides("natbib")
2339             && cite_engine_ != ENGINE_NATBIB_NUMERICAL)
2340                 return ENGINE_NATBIB_AUTHORYEAR;
2341         return cite_engine_;
2342 }
2343
2344
2345 void BufferParams::setCiteEngine(CiteEngine cite_engine)
2346 {
2347         cite_engine_ = cite_engine;
2348 }
2349
2350 } // namespace lyx