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