]> git.lyx.org Git - lyx.git/blob - src/BufferParams.cpp
816c0c0112964c1b00359c5cb2e95f788c1f49ac
[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         int const nlines =
1423                 int(count(lyxpreamble.begin(), lyxpreamble.end(), '\n'));
1424         for (int j = 0; j != nlines; ++j) {
1425                 texrow.newline();
1426         }
1427
1428         os << lyxpreamble;
1429         return use_babel;
1430 }
1431
1432
1433 void BufferParams::useClassDefaults()
1434 {
1435         DocumentClass const & tclass = documentClass();
1436
1437         sides = tclass.sides();
1438         columns = tclass.columns();
1439         pagestyle = tclass.pagestyle();
1440         use_default_options = true;
1441         // Only if class has a ToC hierarchy
1442         if (tclass.hasTocLevels()) {
1443                 secnumdepth = tclass.secnumdepth();
1444                 tocdepth = tclass.tocdepth();
1445         }
1446 }
1447
1448
1449 bool BufferParams::hasClassDefaults() const
1450 {
1451         DocumentClass const & tclass = documentClass();
1452
1453         return sides == tclass.sides()
1454                 && columns == tclass.columns()
1455                 && pagestyle == tclass.pagestyle()
1456                 && use_default_options
1457                 && secnumdepth == tclass.secnumdepth()
1458                 && tocdepth == tclass.tocdepth();
1459 }
1460
1461
1462 DocumentClass const & BufferParams::documentClass() const
1463 {
1464         return *doc_class_;
1465 }
1466
1467
1468 DocumentClass const * BufferParams::documentClassPtr() const {
1469         return doc_class_;
1470 }
1471
1472
1473 void BufferParams::setDocumentClass(DocumentClass const * const tc) {
1474         // evil, but this function is evil
1475         doc_class_ = const_cast<DocumentClass *>(tc);
1476 }
1477
1478
1479 bool BufferParams::removeBadModules()
1480 {
1481         // we'll write a new list of modules, since we can't just remove them,
1482         // as that would invalidate our iterators
1483         list<string> oldModules = getModules();
1484         clearLayoutModules();
1485
1486         list<string> const & provmods = baseClass()->providedModules();
1487         list<string> const & exclmods = baseClass()->excludedModules();
1488         bool consistent = true; // set to false if we have to do anything
1489
1490         list<string>::const_iterator oit = oldModules.begin();
1491         list<string>::const_iterator const oen = oldModules.end();
1492         for (; oit != oen; ++oit) {
1493                 string const & modname = *oit;
1494                 // skip modules that the class provides
1495                 if (find(provmods.begin(), provmods.end(), modname) != provmods.end()) {
1496                         LYXERR0("Module `" << modname << "' dropped because provided by document class.");
1497                         consistent = false;
1498                         continue;
1499                 }
1500                 // are we excluded by the document class?
1501                 if (find(exclmods.begin(), exclmods.end(), modname) != exclmods.end()) {
1502                         LYXERR0("Module `" << modname << "' dropped because excluded by document class.");
1503                         consistent = false;
1504                         continue;
1505                 }
1506                 // determine whether some provided module excludes us or we exclude it
1507                 list<string>::const_iterator pit = provmods.begin();
1508                 list<string>::const_iterator const pen = provmods.end();
1509                 bool excluded = false;
1510                 for (; !excluded && pit != pen; ++pit) {
1511                         if (!LyXModule::areCompatible(modname, *pit)) {
1512                                 LYXERR0("Module " << modname << 
1513                                                 " dropped becuase it conflicts with provided module `" << *pit << "'.");
1514                                 consistent = false;
1515                                 excluded = true;
1516                         }
1517                 }
1518                 if (excluded)
1519                         continue;
1520                 layoutModules_.push_back(modname);
1521         }
1522         return consistent;
1523 }
1524
1525
1526 void BufferParams::addDefaultModules()
1527 {
1528         // add any default modules not already in use
1529         list<string> const & mods = baseClass()->defaultModules();
1530         list<string>::const_iterator mit = mods.begin();
1531         list<string>::const_iterator men = mods.end();
1532
1533         // We want to insert the default modules at the beginning of
1534         // the list, but also to insert them in the correct order.
1535         // The obvious thing to do would be to collect them and then
1536         // insert them, but that doesn't work because a later default
1537         // module may require an earlier one, and then the test below
1538         //     moduleCanBeAdded(modname)
1539         // will fail. So we have to do it a more complicated way.
1540         list<string>::iterator insertpos = layoutModules_.begin();
1541         int numinserts = 0;
1542
1543         for (; mit != men; mit++) {
1544                 string const & modName = *mit;
1545                 // make sure the user hasn't removed it
1546                 if (find(removedModules_.begin(), removedModules_.end(), modName) !=
1547                     removedModules_.end()) {
1548                         LYXERR(Debug::TCLASS, "Default module `" << modName << 
1549                                         "' not added because removed by user.");
1550                         continue;
1551                 }
1552
1553                 if (!moduleCanBeAdded(modName)) {
1554                         // FIXME This could be because it's already present, so we should
1555                         // probably return something indicating that.
1556                         LYXERR(Debug::TCLASS, "Default module `" << modName << 
1557                                         "' could not be added.");
1558                         continue;
1559                 }
1560                 LYXERR(Debug::TCLASS, "Default module `" << modName << "' added.");
1561                 layoutModules_.insert(insertpos, modName);
1562                 // now we reset insertpos
1563                 ++numinserts;
1564                 insertpos = layoutModules_.begin();
1565                 advance(insertpos, numinserts);
1566         }
1567 }
1568
1569
1570 bool BufferParams::checkModuleConsistency() {
1571         bool consistent = true;
1572         // Perform a consistency check on the set of modules. We need to make
1573         // sure that none of the modules exclude each other and that requires
1574         // are satisfied.
1575         list<string> oldModules = getModules();
1576         clearLayoutModules();
1577         list<string>::const_iterator oit = oldModules.begin();
1578         list<string>::const_iterator oen = oldModules.end();
1579         list<string> const & provmods = baseClass()->providedModules();
1580         for (; oit != oen; ++oit) {
1581                 string const & modname = *oit;
1582                 bool excluded = false;
1583                 // Determine whether some prior module excludes us, or we exclude it
1584                 list<string>::const_iterator lit = layoutModules_.begin();
1585                 list<string>::const_iterator len = layoutModules_.end();
1586                 for (; !excluded && lit != len; ++lit) {
1587                         if (!LyXModule::areCompatible(modname, *lit)) {
1588                                 consistent = false;
1589                                 LYXERR0("Module " << modname << 
1590                                                 " dropped because it is excluded by prior module " << *lit);
1591                                 excluded = true;
1592                         }
1593                 }
1594
1595                 if (excluded)
1596                         continue;
1597
1598                 // determine whether some provided module or some prior module
1599                 // satisfies our requirements
1600                 LyXModule const * const oldmod = moduleList[modname];
1601                 if (!oldmod) {
1602                         LYXERR0("Default module " << modname << 
1603                                         " added although it is unavailable and can't check requirements.");
1604                         continue;
1605                 }
1606                         
1607                 vector<string> const & reqs = oldmod->getRequiredModules();
1608                 if (!reqs.empty()) {
1609                         // we now set excluded to true, meaning that we haven't
1610                         // yet found a required module.
1611                         excluded = true;
1612                         vector<string>::const_iterator rit  = reqs.begin();
1613                         vector<string>::const_iterator ren = reqs.end();
1614                         for (; rit != ren; ++rit) {
1615                                 string const reqmod = *rit;
1616                                 if (find(provmods.begin(), provmods.end(), reqmod) != 
1617                                                 provmods.end()) {
1618                                         excluded = false;
1619                                         break;
1620                                 }
1621                                 if (find(layoutModules_.begin(), layoutModules_.end(), reqmod) != 
1622                                                 layoutModules_.end()) {
1623                                         excluded = false;
1624                                         break;
1625                                 }
1626                         }
1627                 }
1628                 if (excluded) {
1629                         consistent = false;
1630                         LYXERR0("Module " << modname << " dropped because requirements not met.");
1631                 } else {
1632                         LYXERR(Debug::TCLASS, "Module " << modname << " passed consistency check.");
1633                         layoutModules_.push_back(modname);
1634                 }
1635         }
1636         return consistent;
1637 }
1638
1639
1640 bool BufferParams::setBaseClass(string const & classname)
1641 {
1642         LYXERR(Debug::TCLASS, "setBaseClass: " << classname);
1643         LayoutFileList & bcl = LayoutFileList::get();
1644         if (!bcl.haveClass(classname)) {
1645                 docstring s = 
1646                         bformat(_("The document class %1$s could not be found. "
1647                                 "A default textclass with default layouts will be used. "
1648                                 "LyX might not be able to produce output unless a correct "
1649                                 "textclass is selected from the document settings dialog."),
1650                         from_utf8(classname));
1651                 frontend::Alert::error(_("Document class not found"), s);
1652                 bcl.addEmptyClass(classname);
1653         }
1654
1655         bool const success = bcl[classname].load();
1656         if (!success) {
1657                 docstring s = 
1658                         bformat(_("The document class %1$s could not be loaded."),
1659                         from_utf8(classname));
1660                 frontend::Alert::error(_("Could not load class"), s);
1661                 return false;
1662         }
1663
1664         pimpl_->baseClass_ = classname;
1665         // the previous document class may have loaded some modules that the
1666         // new one excludes, and the new class may provide, etc, some that
1667         // conflict with ones that were already loaded. So we need to go 
1668         // through the list and fix everything. I suppose there are various
1669         // ways this could be done, but the following seems to work at the 
1670         // moment. (Thanks to Philippe Charpentier for helping work out all 
1671         // the bugs---rgh.)
1672         // 
1673         // first, we remove any modules the new document class itself provides,
1674         // those it excludes, and those that conflict with ones it excludes.
1675         // this has to be done first because, otherwise, a module we're about
1676         // to remove could prevent a default module from being added.
1677         removeBadModules();
1678         // next, we add any default modules the new class provides.
1679         addDefaultModules();
1680         // finally, we perform a general consistency check on the set of
1681         // loaded modules.
1682         checkModuleConsistency();
1683         // FIXME removeBadModules() and checkModuleConsistency() both return
1684         // a boolean indicating whether something had to be changed. It might
1685         // be worth popping a message to the user if so.
1686
1687         return true;
1688 }
1689
1690
1691 LayoutFile const * BufferParams::baseClass() const
1692 {
1693         if (LayoutFileList::get().haveClass(pimpl_->baseClass_))
1694                 return &(LayoutFileList::get()[pimpl_->baseClass_]);
1695         else 
1696                 return 0;
1697 }
1698
1699
1700 LayoutFileIndex const & BufferParams::baseClassID() const
1701 {
1702         return pimpl_->baseClass_;
1703 }
1704
1705
1706 void BufferParams::makeDocumentClass()
1707 {
1708         if (!baseClass())
1709                 return;
1710
1711         doc_class_ = &(DocumentClassBundle::get().newClass(*baseClass()));
1712
1713         // FIXME It might be worth loading the children's modules here,
1714         // just as we load their bibliographies and such, instead of just 
1715         // doing a check in InsetInclude.
1716         LayoutModuleList::const_iterator it = layoutModules_.begin();
1717         for (; it != layoutModules_.end(); it++) {
1718                 string const modName = *it;
1719                 LyXModule * lm = moduleList[modName];
1720                 if (!lm) {
1721                         docstring const msg =
1722                                 bformat(_("The module %1$s has been requested by\n"
1723                                         "this document but has not been found in the list of\n"
1724                                         "available modules. If you recently installed it, you\n"
1725                                         "probably need to reconfigure LyX.\n"), from_utf8(modName));
1726                         frontend::Alert::warning(_("Module not available"),
1727                                         msg + _("Some layouts may not be available."));
1728                         LYXERR0("BufferParams::makeDocumentClass(): Module " <<
1729                                         modName << " requested but not found in module list.");
1730                         continue;
1731                 }
1732                 if (!lm->isAvailable()) {
1733                         docstring const msg =
1734                                                 bformat(_("The module %1$s requires a package that is\n"
1735                                                 "not available in your LaTeX installation. LaTeX output\n"
1736                                                 "may not be possible.\n"), from_utf8(modName));
1737                         frontend::Alert::warning(_("Package not available"), msg);
1738                 }
1739                 FileName layout_file = libFileSearch("layouts", lm->getFilename());
1740                 if (!doc_class_->read(layout_file, TextClass::MODULE)) {
1741                         docstring const msg =
1742                                 bformat(_("Error reading module %1$s\n"), from_utf8(modName));
1743                         frontend::Alert::warning(_("Read Error"), msg);
1744                 }
1745         }
1746         if (!local_layout.empty()) {
1747                 if (!doc_class_->read(local_layout, TextClass::MODULE)) {
1748                         docstring const msg = _("Error reading internal layout information");
1749                         frontend::Alert::warning(_("Read Error"), msg);
1750                 }
1751         }
1752 }
1753
1754
1755 bool BufferParams::moduleCanBeAdded(string const & modName) const
1756 {
1757         // Is the module already present?
1758         LayoutModuleList::const_iterator it = layoutModules_.begin();
1759         LayoutModuleList::const_iterator end = layoutModules_.end();
1760         for (; it != end; it++)
1761                 if (*it == modName) 
1762                         return false;
1763
1764         LyXModule const * const lm = moduleList[modName];
1765         if (!lm)
1766                 return true;
1767
1768         // Is this module explicitly excluded by the document class?
1769         list<string>::const_iterator const exclmodstart = 
1770                         baseClass()->excludedModules().begin();
1771         list<string>::const_iterator const exclmodend = 
1772                         baseClass()->excludedModules().end();
1773         if (find(exclmodstart, exclmodend, modName) != exclmodend)
1774                 return false;
1775
1776         // Is this module already provided by the document class?
1777         list<string>::const_iterator const provmodstart = 
1778                         baseClass()->providedModules().begin();
1779         list<string>::const_iterator const provmodend = 
1780                         baseClass()->providedModules().end();
1781         if (find(provmodstart, provmodend, modName) != provmodend)
1782                 return false;
1783
1784         // Check for conflicts with used modules
1785         // first the provided modules...
1786         list<string>::const_iterator provmodit = provmodstart;
1787         for (; provmodit != provmodend; ++provmodit) {
1788                 if (!LyXModule::areCompatible(modName, *provmodit))
1789                         return false;
1790         }
1791         // and then the selected modules
1792         LayoutModuleList::const_iterator mit = getModules().begin();
1793         LayoutModuleList::const_iterator const men = getModules().end();
1794         for (; mit != men; ++mit)
1795                 if (!LyXModule::areCompatible(modName, *mit))
1796                         return false;
1797
1798         // Check whether some required module is available
1799         vector<string> const reqs = lm->getRequiredModules();
1800         if (reqs.empty())
1801                 return true;
1802
1803         mit = getModules().begin(); // reset
1804         vector<string>::const_iterator rit = reqs.begin();
1805         vector<string>::const_iterator ren = reqs.end();
1806         bool foundone = false;
1807         for (; rit != ren; ++rit) {
1808                 if (find(mit, men, *rit) != men || 
1809                     find(provmodstart, provmodend, *rit) != provmodend) {
1810                         foundone = true;
1811                         break;
1812                 }
1813         }
1814
1815         return foundone;
1816 }
1817
1818
1819 bool BufferParams::addLayoutModule(string const & modName)
1820 {
1821         LayoutModuleList::const_iterator it = layoutModules_.begin();
1822         LayoutModuleList::const_iterator end = layoutModules_.end();
1823         for (; it != end; it++)
1824                 if (*it == modName) 
1825                         return false;
1826         layoutModules_.push_back(modName);
1827         return true;
1828 }
1829
1830
1831 Font const BufferParams::getFont() const
1832 {
1833         FontInfo f = documentClass().defaultfont();
1834         if (fontsDefaultFamily == "rmdefault")
1835                 f.setFamily(ROMAN_FAMILY);
1836         else if (fontsDefaultFamily == "sfdefault")
1837                 f.setFamily(SANS_FAMILY);
1838         else if (fontsDefaultFamily == "ttdefault")
1839                 f.setFamily(TYPEWRITER_FAMILY);
1840         return Font(f, language);
1841 }
1842
1843
1844 void BufferParams::readPreamble(Lexer & lex)
1845 {
1846         if (lex.getString() != "\\begin_preamble")
1847                 lyxerr << "Error (BufferParams::readPreamble):"
1848                         "consistency check failed." << endl;
1849
1850         preamble = lex.getLongString("\\end_preamble");
1851 }
1852
1853
1854 void BufferParams::readLocalLayout(Lexer & lex)
1855 {
1856         if (lex.getString() != "\\begin_local_layout")
1857                 lyxerr << "Error (BufferParams::readLocalLayout):"
1858                         "consistency check failed." << endl;
1859
1860         local_layout = lex.getLongString("\\end_local_layout");
1861 }
1862
1863
1864 void BufferParams::readLanguage(Lexer & lex)
1865 {
1866         if (!lex.next()) return;
1867
1868         string const tmptok = lex.getString();
1869
1870         // check if tmptok is part of tex_babel in tex-defs.h
1871         language = languages.getLanguage(tmptok);
1872         if (!language) {
1873                 // Language tmptok was not found
1874                 language = default_language;
1875                 lyxerr << "Warning: Setting language `"
1876                        << tmptok << "' to `" << language->lang()
1877                        << "'." << endl;
1878         }
1879 }
1880
1881
1882 void BufferParams::readGraphicsDriver(Lexer & lex)
1883 {
1884         if (!lex.next()) 
1885                 return;
1886
1887         string const tmptok = lex.getString();
1888         // check if tmptok is part of tex_graphics in tex_defs.h
1889         int n = 0;
1890         while (true) {
1891                 string const test = tex_graphics[n++];
1892
1893                 if (test == tmptok) {
1894                         graphicsDriver = tmptok;
1895                         break;
1896                 }
1897                 if (test.empty()) {
1898                         lex.printError(
1899                                 "Warning: graphics driver `$$Token' not recognized!\n"
1900                                 "         Setting graphics driver to `default'.\n");
1901                         graphicsDriver = "default";
1902                         break;
1903                 }
1904         }
1905 }
1906
1907
1908 void BufferParams::readBullets(Lexer & lex)
1909 {
1910         if (!lex.next()) 
1911                 return;
1912
1913         int const index = lex.getInteger();
1914         lex.next();
1915         int temp_int = lex.getInteger();
1916         user_defined_bullet(index).setFont(temp_int);
1917         temp_bullet(index).setFont(temp_int);
1918         lex >> temp_int;
1919         user_defined_bullet(index).setCharacter(temp_int);
1920         temp_bullet(index).setCharacter(temp_int);
1921         lex >> temp_int;
1922         user_defined_bullet(index).setSize(temp_int);
1923         temp_bullet(index).setSize(temp_int);
1924 }
1925
1926
1927 void BufferParams::readBulletsLaTeX(Lexer & lex)
1928 {
1929         // The bullet class should be able to read this.
1930         if (!lex.next()) 
1931                 return;
1932         int const index = lex.getInteger();
1933         lex.next(true);
1934         docstring const temp_str = lex.getDocString();
1935
1936         user_defined_bullet(index).setText(temp_str);
1937         temp_bullet(index).setText(temp_str);
1938 }
1939
1940
1941 void BufferParams::readModules(Lexer & lex)
1942 {
1943         if (!lex.eatLine()) {
1944                 lyxerr << "Error (BufferParams::readModules):"
1945                                 "Unexpected end of input." << endl;
1946                 return;
1947         }
1948         while (true) {
1949                 string mod = lex.getString();
1950                 if (mod == "\\end_modules")
1951                         break;
1952                 addLayoutModule(mod);
1953                 lex.eatLine();
1954         }
1955 }
1956
1957
1958 void BufferParams::readRemovedModules(Lexer & lex)
1959 {
1960         if (!lex.eatLine()) {
1961                 lyxerr << "Error (BufferParams::readRemovedModules):"
1962                                 "Unexpected end of input." << endl;
1963                 return;
1964         }
1965         while (true) {
1966                 string mod = lex.getString();
1967                 if (mod == "\\end_removed_modules")
1968                         break;
1969                 removedModules_.insert(mod);
1970                 lex.eatLine();
1971         }
1972         // now we want to remove any removed modules that were previously 
1973         // added. normally, that will be because default modules were added in 
1974         // setBaseClass(), which gets called when \textclass is read at the 
1975         // start of the read.
1976         set<string>::const_iterator rit = removedModules_.begin();
1977         set<string>::const_iterator const ren = removedModules_.end();
1978         for (; rit != ren; rit++) {
1979                 LayoutModuleList::iterator const mit = layoutModules_.begin();
1980                 LayoutModuleList::iterator const men = layoutModules_.end();
1981                 LayoutModuleList::iterator found = find(mit, men, *rit);
1982                 if (found == men)
1983                         continue;
1984                 layoutModules_.erase(found);
1985         }
1986 }
1987
1988
1989 string BufferParams::paperSizeName(PapersizePurpose purpose) const
1990 {
1991         char real_papersize = papersize;
1992         if (real_papersize == PAPER_DEFAULT)
1993                 real_papersize = lyxrc.default_papersize;
1994
1995         switch (real_papersize) {
1996         case PAPER_DEFAULT:
1997                 // could be anything, so don't guess
1998                 return string();
1999         case PAPER_CUSTOM: {
2000                 if (purpose == XDVI && !paperwidth.empty() &&
2001                     !paperheight.empty()) {
2002                         // heightxwidth<unit>
2003                         string first = paperwidth;
2004                         string second = paperheight;
2005                         if (orientation == ORIENTATION_LANDSCAPE)
2006                                 first.swap(second);
2007                         // cut off unit.
2008                         return first.erase(first.length() - 2)
2009                                 + "x" + second;
2010                 }
2011                 return string();
2012         }
2013         case PAPER_A3:
2014                 return "a3";
2015         case PAPER_A4:
2016                 return "a4";
2017         case PAPER_A5:
2018                 return "a5";
2019         case PAPER_B3:
2020                 // dvips and dvipdfm do not know this
2021                 if (purpose == DVIPS || purpose == DVIPDFM)
2022                         return string();
2023                 return "b3";
2024         case PAPER_B4:
2025                 // dvipdfm does not know this
2026                 if (purpose == DVIPDFM)
2027                         return string();
2028                 return "b4";
2029         case PAPER_B5:
2030                 // dvipdfm does not know this
2031                 if (purpose == DVIPDFM)
2032                         return string();
2033                 return "b5";
2034         case PAPER_USEXECUTIVE:
2035                 // dvipdfm does not know this
2036                 if (purpose == DVIPDFM)
2037                         return string();
2038                 return "foolscap";
2039         case PAPER_USLEGAL:
2040                 return "legal";
2041         case PAPER_USLETTER:
2042         default:
2043                 if (purpose == XDVI)
2044                         return "us";
2045                 return "letter";
2046         }
2047 }
2048
2049
2050 string const BufferParams::dvips_options() const
2051 {
2052         string result;
2053
2054         if (use_geometry
2055             && papersize == PAPER_CUSTOM
2056             && !lyxrc.print_paper_dimension_flag.empty()
2057             && !paperwidth.empty()
2058             && !paperheight.empty()) {
2059                 // using a custom papersize
2060                 result = lyxrc.print_paper_dimension_flag;
2061                 result += ' ' + paperwidth;
2062                 result += ',' + paperheight;
2063         } else {
2064                 string const paper_option = paperSizeName(DVIPS);
2065                 if (!paper_option.empty() && (paper_option != "letter" ||
2066                     orientation != ORIENTATION_LANDSCAPE)) {
2067                         // dvips won't accept -t letter -t landscape.
2068                         // In all other cases, include the paper size
2069                         // explicitly.
2070                         result = lyxrc.print_paper_flag;
2071                         result += ' ' + paper_option;
2072                 }
2073         }
2074         if (orientation == ORIENTATION_LANDSCAPE &&
2075             papersize != PAPER_CUSTOM)
2076                 result += ' ' + lyxrc.print_landscape_flag;
2077         return result;
2078 }
2079
2080
2081 string BufferParams::babelCall(string const & lang_opts) const
2082 {
2083         string lang_pack = lyxrc.language_package;
2084         if (lang_pack != "\\usepackage{babel}")
2085                 return lang_pack;
2086         // suppress the babel call when there is no babel language defined
2087         // for the document language in the lib/languages file and if no
2088         // other languages are used (lang_opts is then empty)
2089         if (lang_opts.empty())
2090                 return string();
2091         // If Vietnamese is used, babel must directly be loaded with the
2092         // language options, see
2093         // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129417.html
2094         size_t viet = lang_opts.find("vietnam");
2095         // viet = string::npos when not found
2096         // the same is for all other languages that are not directly supported by
2097         // babel, but where LaTeX-packages add babel support.
2098         // this is currently the case for Latvian, Lithuanian, and Mongolian
2099         size_t latvian = lang_opts.find("latvian");
2100         size_t lithu = lang_opts.find("lithuanian");
2101         size_t mongo = lang_opts.find("mongolian");
2102         // If Japanese is used, babel must directly be loaded with the
2103         // language options, see
2104         // http://bugzilla.lyx.org/show_bug.cgi?id=4597#c4
2105         size_t japan = lang_opts.find("japanese");
2106         if (!lyxrc.language_global_options || viet != string::npos
2107                 || japan != string::npos || latvian != string::npos
2108                 || lithu != string::npos || mongo != string::npos)
2109                 return "\\usepackage[" + lang_opts + "]{babel}";
2110         return lang_pack;
2111 }
2112
2113
2114 docstring BufferParams::getGraphicsDriver(string const & package) const
2115 {
2116         docstring result;
2117
2118         if (package == "geometry") {
2119                 if (graphicsDriver == "dvips"
2120                     || graphicsDriver == "dvipdfm"
2121                     || graphicsDriver == "pdftex"
2122                     || graphicsDriver == "vtex")
2123                         result = from_ascii(graphicsDriver);
2124                 else if (graphicsDriver == "dvipdfmx")
2125                         result = from_ascii("dvipdfm");
2126         }
2127
2128         return result;
2129 }
2130
2131
2132 void BufferParams::writeEncodingPreamble(odocstream & os,
2133                 LaTeXFeatures & features, TexRow & texrow) const
2134 {
2135         if (inputenc == "auto") {
2136                 string const doc_encoding =
2137                         language->encoding()->latexName();
2138                 Encoding::Package const package =
2139                         language->encoding()->package();
2140
2141                 // Create a list with all the input encodings used
2142                 // in the document
2143                 set<string> encodings =
2144                         features.getEncodingSet(doc_encoding);
2145
2146                 // If the "japanese" package (i.e. pLaTeX) is used,
2147                 // inputenc must be omitted.
2148                 // see http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129680.html
2149                 if (package == Encoding::japanese)
2150                      features.require("japanese");
2151
2152                 if ((!encodings.empty() || package == Encoding::inputenc)
2153                     && !features.isRequired("japanese")) {
2154                         os << "\\usepackage[";
2155                         set<string>::const_iterator it = encodings.begin();
2156                         set<string>::const_iterator const end = encodings.end();
2157                         if (it != end) {
2158                                 os << from_ascii(*it);
2159                                 ++it;
2160                         }
2161                         for (; it != end; ++it)
2162                                 os << ',' << from_ascii(*it);
2163                         if (package == Encoding::inputenc) {
2164                                 if (!encodings.empty())
2165                                         os << ',';
2166                                 os << from_ascii(doc_encoding);
2167                         }
2168                         os << "]{inputenc}\n";
2169                         texrow.newline();
2170                 }
2171                 if (package == Encoding::CJK || features.mustProvide("CJK")) {
2172                         if (language->encoding()->name() == "utf8-cjk"
2173                             && features.isAvailable("CJKutf8"))
2174                                 os << "\\usepackage{CJKutf8}\n";
2175                         else
2176                                 os << "\\usepackage{CJK}\n";
2177                         texrow.newline();
2178                 }
2179         } else if (inputenc != "default") {
2180                 switch (encoding().package()) {
2181                 case Encoding::none:
2182                 case Encoding::japanese:
2183                         break;
2184                 case Encoding::inputenc:
2185                         // do not load inputenc if japanese is used
2186                         if (features.isRequired("japanese"))
2187                                 break;
2188                         os << "\\usepackage[" << from_ascii(inputenc)
2189                            << "]{inputenc}\n";
2190                         texrow.newline();
2191                         break;
2192                 case Encoding::CJK:
2193                         if (encoding().name() == "utf8-cjk"
2194                             && features.isAvailable("CJKutf8"))
2195                                 os << "\\usepackage{CJKutf8}\n";
2196                         else
2197                                 os << "\\usepackage{CJK}\n";
2198                         texrow.newline();
2199                         break;
2200                 }
2201         }
2202
2203         // The encoding "armscii8" (for Armenian) is only available when
2204         // the package "armtex" is loaded.
2205         if (language->encoding()->latexName() == "armscii8"
2206             || inputenc == "armscii8") {
2207                 os << "\\usepackage{armtex}\n";
2208                 texrow.newline();
2209         }
2210 }
2211
2212
2213 string const BufferParams::loadFonts(string const & rm,
2214                                      string const & sf, string const & tt,
2215                                      bool const & sc, bool const & osf,
2216                                      int const & sfscale, int const & ttscale) const
2217 {
2218         /* The LaTeX font world is in a flux. In the PSNFSS font interface,
2219            several packages have been replaced by others, that might not
2220            be installed on every system. We have to take care for that
2221            (see psnfss.pdf). We try to support all psnfss fonts as well
2222            as the fonts that have become de facto standard in the LaTeX
2223            world (e.g. Latin Modern). We do not support obsolete fonts
2224            (like PSLatex). In general, it should be possible to mix any
2225            rm font with any sf or tt font, respectively. (JSpitzm)
2226            TODO:
2227                 -- separate math fonts.
2228         */
2229
2230         if (rm == "default" && sf == "default" && tt == "default")
2231                 //nothing to do
2232                 return string();
2233
2234         ostringstream os;
2235
2236         // ROMAN FONTS
2237         // Computer Modern (must be explicitely selectable -- there might be classes
2238         // that define a different default font!
2239         if (rm == "cmr") {
2240                 os << "\\renewcommand{\\rmdefault}{cmr}\n";
2241                 // osf for Computer Modern needs eco.sty
2242                 if (osf)
2243                         os << "\\usepackage{eco}\n";
2244         }
2245         // Latin Modern Roman
2246         else if (rm == "lmodern")
2247                 os << "\\usepackage{lmodern}\n";
2248         // AE
2249         else if (rm == "ae") {
2250                 // not needed when using OT1 font encoding.
2251                 if (lyxrc.fontenc != "default")
2252                         os << "\\usepackage{ae,aecompl}\n";
2253         }
2254         // Times
2255         else if (rm == "times") {
2256                 // try to load the best available package
2257                 if (LaTeXFeatures::isAvailable("mathptmx"))
2258                         os << "\\usepackage{mathptmx}\n";
2259                 else if (LaTeXFeatures::isAvailable("mathptm"))
2260                         os << "\\usepackage{mathptm}\n";
2261                 else
2262                         os << "\\usepackage{times}\n";
2263         }
2264         // Palatino
2265         else if (rm == "palatino") {
2266                 // try to load the best available package
2267                 if (LaTeXFeatures::isAvailable("mathpazo")) {
2268                         os << "\\usepackage";
2269                         if (osf || sc) {
2270                                 os << '[';
2271                                 if (!osf)
2272                                         os << "sc";
2273                                 else
2274                                         // "osf" includes "sc"!
2275                                         os << "osf";
2276                                 os << ']';
2277                         }
2278                         os << "{mathpazo}\n";
2279                 }
2280                 else if (LaTeXFeatures::isAvailable("mathpple"))
2281                         os << "\\usepackage{mathpple}\n";
2282                 else
2283                         os << "\\usepackage{palatino}\n";
2284         }
2285         // Utopia
2286         else if (rm == "utopia") {
2287                 // fourier supersedes utopia.sty, but does
2288                 // not work with OT1 encoding.
2289                 if (LaTeXFeatures::isAvailable("fourier")
2290                     && lyxrc.fontenc != "default") {
2291                         os << "\\usepackage";
2292                         if (osf || sc) {
2293                                 os << '[';
2294                                 if (sc)
2295                                         os << "expert";
2296                                 if (osf && sc)
2297                                         os << ',';
2298                                 if (osf)
2299                                         os << "oldstyle";
2300                                 os << ']';
2301                         }
2302                         os << "{fourier}\n";
2303                 }
2304                 else
2305                         os << "\\usepackage{utopia}\n";
2306         }
2307         // Bera (complete fontset)
2308         else if (rm == "bera" && sf == "default" && tt == "default")
2309                 os << "\\usepackage{bera}\n";
2310         // everything else
2311         else if (rm != "default")
2312                 os << "\\usepackage" << "{" << rm << "}\n";
2313
2314         // SANS SERIF
2315         // Helvetica, Bera Sans
2316         if (sf == "helvet" || sf == "berasans") {
2317                 if (sfscale != 100)
2318                         os << "\\usepackage[scaled=" << float(sfscale) / 100
2319                            << "]{" << sf << "}\n";
2320                 else
2321                         os << "\\usepackage{" << sf << "}\n";
2322         }
2323         // Avant Garde
2324         else if (sf == "avant")
2325                 os << "\\usepackage{" << sf << "}\n";
2326         // Computer Modern, Latin Modern, CM Bright
2327         else if (sf != "default")
2328                 os << "\\renewcommand{\\sfdefault}{" << sf << "}\n";
2329
2330         // monospaced/typewriter
2331         // Courier, LuxiMono
2332         if (tt == "luximono" || tt == "beramono") {
2333                 if (ttscale != 100)
2334                         os << "\\usepackage[scaled=" << float(ttscale) / 100
2335                            << "]{" << tt << "}\n";
2336                 else
2337                         os << "\\usepackage{" << tt << "}\n";
2338         }
2339         // Courier
2340         else if (tt == "courier" )
2341                 os << "\\usepackage{" << tt << "}\n";
2342         // Computer Modern, Latin Modern, CM Bright
2343         else if (tt != "default")
2344                 os << "\\renewcommand{\\ttdefault}{" << tt << "}\n";
2345
2346         return os.str();
2347 }
2348
2349
2350 Encoding const & BufferParams::encoding() const
2351 {
2352         if (inputenc == "auto" || inputenc == "default")
2353                 return *language->encoding();
2354         Encoding const * const enc = encodings.fromLaTeXName(inputenc);
2355         if (enc)
2356                 return *enc;
2357         LYXERR0("Unknown inputenc value `" << inputenc
2358                << "'. Using `auto' instead.");
2359         return *language->encoding();
2360 }
2361
2362
2363 CiteEngine BufferParams::citeEngine() const
2364 {
2365         // FIXME the class should provide the numerical/
2366         // authoryear choice
2367         if (documentClass().provides("natbib")
2368             && cite_engine_ != ENGINE_NATBIB_NUMERICAL)
2369                 return ENGINE_NATBIB_AUTHORYEAR;
2370         return cite_engine_;
2371 }
2372
2373
2374 void BufferParams::setCiteEngine(CiteEngine cite_engine)
2375 {
2376         cite_engine_ = cite_engine;
2377 }
2378
2379 } // namespace lyx