]> git.lyx.org Git - lyx.git/blob - src/bufferparams.C
Two fixes involving RtL text drawing
[lyx.git] / src / bufferparams.C
1 /**
2  * \file bufferparams.C
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 "BranchList.h"
22 #include "Bullet.h"
23 #include "debug.h"
24 #include "encoding.h"
25 #include "gettext.h"
26 #include "language.h"
27 #include "LaTeXFeatures.h"
28 #include "LColor.h"
29 #include "lyxlex.h"
30 #include "lyxrc.h"
31 #include "lyxtextclasslist.h"
32 #include "outputparams.h"
33 #include "tex-strings.h"
34 #include "Spacing.h"
35 #include "texrow.h"
36 #include "vspace.h"
37
38 #include "frontends/Alert.h"
39
40 #include "support/lyxalgo.h" // for lyx::count
41 #include "support/convert.h"
42 #include "support/translator.h"
43
44 #include <boost/array.hpp>
45
46 #include <sstream>
47
48 namespace support = lyx::support;
49 using lyx::support::bformat;
50 using lyx::support::rtrim;
51 using lyx::support::tokenPos;
52
53 using std::endl;
54 using std::string;
55 using std::istringstream;
56 using std::ostream;
57 using std::ostringstream;
58 using std::pair;
59
60 namespace biblio = lyx::biblio;
61
62
63 // Local translators
64 namespace {
65
66 // Paragraph separation
67 typedef Translator<string, BufferParams::PARSEP> ParSepTranslator;
68
69
70 ParSepTranslator const init_parseptranslator()
71 {
72         ParSepTranslator translator(string_paragraph_separation[0], BufferParams::PARSEP_INDENT);
73         translator.addPair(string_paragraph_separation[1], BufferParams::PARSEP_SKIP);
74         return translator;
75 }
76
77
78 ParSepTranslator const & parseptranslator()
79 {
80         static ParSepTranslator translator = init_parseptranslator();
81         return translator;
82 }
83
84
85 // Quotes language
86 typedef Translator<string, InsetQuotes::quote_language> QuotesLangTranslator;
87
88
89 QuotesLangTranslator const init_quoteslangtranslator()
90 {
91         QuotesLangTranslator translator(string_quotes_language[0], InsetQuotes::EnglishQ);
92         translator.addPair(string_quotes_language[1], InsetQuotes::SwedishQ);
93         translator.addPair(string_quotes_language[2], InsetQuotes::GermanQ);
94         translator.addPair(string_quotes_language[3], InsetQuotes::PolishQ);
95         translator.addPair(string_quotes_language[4], InsetQuotes::FrenchQ);
96         translator.addPair(string_quotes_language[5], InsetQuotes::DanishQ);
97         return translator;
98 }
99
100
101 QuotesLangTranslator const & quoteslangtranslator()
102 {
103         static QuotesLangTranslator translator = init_quoteslangtranslator();
104         return translator;
105 }
106
107
108 // Quote times
109 typedef Translator<int, InsetQuotes::quote_times> QuotesTimesTranslator;
110
111
112 QuotesTimesTranslator const init_quotestimestranslator()
113 {
114         QuotesTimesTranslator translator(1, InsetQuotes::SingleQ);
115         translator.addPair(2, InsetQuotes::DoubleQ);
116         return translator;
117 }
118
119
120 QuotesTimesTranslator const & quotestimestranslator()
121 {
122         static QuotesTimesTranslator translator = init_quotestimestranslator();
123         return translator;
124 }
125
126
127 // Paper size
128 typedef Translator<std::string, PAPER_SIZE> PaperSizeTranslator;
129
130
131 PaperSizeTranslator const init_papersizetranslator()
132 {
133         PaperSizeTranslator translator(string_papersize[0], PAPER_DEFAULT);
134         translator.addPair(string_papersize[1], PAPER_CUSTOM);
135         translator.addPair(string_papersize[2], PAPER_USLETTER);
136         translator.addPair(string_papersize[3], PAPER_USLEGAL);
137         translator.addPair(string_papersize[4], PAPER_USEXECUTIVE);
138         translator.addPair(string_papersize[5], PAPER_A3);
139         translator.addPair(string_papersize[6], PAPER_A4);
140         translator.addPair(string_papersize[7], PAPER_A5);
141         translator.addPair(string_papersize[8], PAPER_B3);
142         translator.addPair(string_papersize[9], PAPER_B4);
143         translator.addPair(string_papersize[10], PAPER_B5);
144         return translator;
145 }
146
147
148 PaperSizeTranslator const & papersizetranslator()
149 {
150         static PaperSizeTranslator translator = init_papersizetranslator();
151         return translator;
152 }
153
154
155 // Paper orientation
156 typedef Translator<string, PAPER_ORIENTATION> PaperOrientationTranslator;
157
158
159 PaperOrientationTranslator const init_paperorientationtranslator()
160 {
161         PaperOrientationTranslator translator(string_orientation[0], ORIENTATION_PORTRAIT);
162         translator.addPair(string_orientation[1], ORIENTATION_LANDSCAPE);
163         return translator;
164 }
165
166
167 PaperOrientationTranslator const & paperorientationtranslator()
168 {
169         static PaperOrientationTranslator translator = init_paperorientationtranslator();
170         return translator;
171 }
172
173
174 // Page sides
175 typedef Translator<int, LyXTextClass::PageSides> SidesTranslator;
176
177
178 SidesTranslator const init_sidestranslator()
179 {
180         SidesTranslator translator(1, LyXTextClass::OneSide);
181         translator.addPair(2, LyXTextClass::TwoSides);
182         return translator;
183 }
184
185
186 SidesTranslator const & sidestranslator()
187 {
188         static SidesTranslator translator = init_sidestranslator();
189         return translator;
190 }
191
192
193
194 // AMS
195 typedef Translator<int, BufferParams::AMS> AMSTranslator;
196
197
198 AMSTranslator const init_amstranslator()
199 {
200         AMSTranslator translator(0, BufferParams::AMS_OFF);
201         translator.addPair(1, BufferParams::AMS_AUTO);
202         translator.addPair(2, BufferParams::AMS_ON);
203         return translator;
204 }
205
206
207 AMSTranslator const & amstranslator()
208 {
209         static AMSTranslator translator = init_amstranslator();
210         return translator;
211 }
212
213
214 // Cite engine
215 typedef Translator<string, biblio::CiteEngine> CiteEngineTranslator;
216
217
218 CiteEngineTranslator const init_citeenginetranslator()
219 {
220         CiteEngineTranslator translator("basic", biblio::ENGINE_BASIC);
221         translator.addPair("natbib_numerical", biblio::ENGINE_NATBIB_NUMERICAL);
222         translator.addPair("natbib_authoryear", biblio::ENGINE_NATBIB_AUTHORYEAR);
223         translator.addPair("jurabib", biblio::ENGINE_JURABIB);
224         return translator;
225 }
226
227
228 CiteEngineTranslator const & citeenginetranslator()
229 {
230         static CiteEngineTranslator translator = init_citeenginetranslator();
231         return translator;
232 }
233
234
235 // Spacing
236 typedef Translator<string, Spacing::Space> SpaceTranslator;
237
238
239 SpaceTranslator const init_spacetranslator()
240 {
241         SpaceTranslator translator("default", Spacing::Default);
242         translator.addPair("single", Spacing::Single);
243         translator.addPair("onehalf", Spacing::Onehalf);
244         translator.addPair("double", Spacing::Double);
245         translator.addPair("other", Spacing::Other);
246         return translator;
247 }
248
249
250 SpaceTranslator const & spacetranslator()
251 {
252         static SpaceTranslator translator = init_spacetranslator();
253         return translator;
254 }
255
256 // ends annonym namespace
257 }
258
259
260 class BufferParams::Impl
261 {
262 public:
263         Impl();
264
265         AuthorList authorlist;
266         BranchList branchlist;
267         boost::array<Bullet, 4> temp_bullets;
268         boost::array<Bullet, 4> user_defined_bullets;
269         Spacing spacing;
270         /** This is the amount of space used for paragraph_separation "skip",
271          * and for detached paragraphs in "indented" documents.
272          */
273         VSpace defskip;
274 };
275
276
277 BufferParams::Impl::Impl()
278         : defskip(VSpace::MEDSKIP)
279 {
280         // set initial author
281         authorlist.record(Author(lyxrc.user_name, lyxrc.user_email));
282 }
283
284
285 BufferParams::Impl *
286 BufferParams::MemoryTraits::clone(BufferParams::Impl const * ptr)
287 {
288         BOOST_ASSERT(ptr);
289
290         return new BufferParams::Impl(*ptr);
291 }
292
293
294 void BufferParams::MemoryTraits::destroy(BufferParams::Impl * ptr)
295 {
296         delete ptr;
297 }
298
299
300 BufferParams::BufferParams()
301         : // Initialize textclass to point to article. if `first' is
302           // true in the returned pair, then `second' is the textclass
303           // number; if it is false, second is 0. In both cases, second
304           // is what we want.
305         textclass(textclasslist.NumberOfClass("article").second),
306         pimpl_(new Impl)
307 {
308         paragraph_separation = PARSEP_INDENT;
309         quotes_language = InsetQuotes::EnglishQ;
310         quotes_times = InsetQuotes::DoubleQ;
311         fontsize = "default";
312
313         /*  PaperLayout */
314         papersize = PAPER_DEFAULT;
315         orientation = ORIENTATION_PORTRAIT;
316         use_geometry = false;
317         use_amsmath = AMS_AUTO;
318         cite_engine = biblio::ENGINE_BASIC;
319         use_bibtopic = false;
320         tracking_changes = false;
321         output_changes = false;
322         secnumdepth = 3;
323         tocdepth = 3;
324         language = default_language;
325         fonts = "default";
326         inputenc = "auto";
327         graphicsDriver = "default";
328         sides = LyXTextClass::OneSide;
329         columns = 1;
330         pagestyle = "default";
331         compressed = false;
332         for (int iter = 0; iter < 4; ++iter) {
333                 user_defined_bullet(iter) = ITEMIZE_DEFAULTS[iter];
334                 temp_bullet(iter) = ITEMIZE_DEFAULTS[iter];
335         }
336 }
337
338
339 BufferParams::~BufferParams()
340 {}
341
342
343 AuthorList & BufferParams::authors()
344 {
345         return pimpl_->authorlist;
346 }
347
348
349 AuthorList const & BufferParams::authors() const
350 {
351         return pimpl_->authorlist;
352 }
353
354
355 BranchList & BufferParams::branchlist()
356 {
357         return pimpl_->branchlist;
358 }
359
360
361 BranchList const & BufferParams::branchlist() const
362 {
363         return pimpl_->branchlist;
364 }
365
366
367 Bullet & BufferParams::temp_bullet(lyx::size_type const index)
368 {
369         BOOST_ASSERT(index < 4);
370         return pimpl_->temp_bullets[index];
371 }
372
373
374 Bullet const & BufferParams::temp_bullet(lyx::size_type const index) const
375 {
376         BOOST_ASSERT(index < 4);
377         return pimpl_->temp_bullets[index];
378 }
379
380
381 Bullet & BufferParams::user_defined_bullet(lyx::size_type const index)
382 {
383         BOOST_ASSERT(index < 4);
384         return pimpl_->user_defined_bullets[index];
385 }
386
387
388 Bullet const & BufferParams::user_defined_bullet(lyx::size_type const index) const
389 {
390         BOOST_ASSERT(index < 4);
391         return pimpl_->user_defined_bullets[index];
392 }
393
394
395 Spacing & BufferParams::spacing()
396 {
397         return pimpl_->spacing;
398 }
399
400
401 Spacing const & BufferParams::spacing() const
402 {
403         return pimpl_->spacing;
404 }
405
406
407 VSpace const & BufferParams::getDefSkip() const
408 {
409         return pimpl_->defskip;
410 }
411
412
413 void BufferParams::setDefSkip(VSpace const & vs)
414 {
415         pimpl_->defskip = vs;
416 }
417
418
419 string const BufferParams::readToken(LyXLex & lex, string const & token)
420 {
421         if (token == "\\textclass") {
422                 lex.next();
423                 string const classname = lex.getString();
424                 pair<bool, lyx::textclass_type> pp =
425                         textclasslist.NumberOfClass(classname);
426                 if (pp.first) {
427                         textclass = pp.second;
428                 } else {
429                         textclass = 0;
430                         return classname;
431                 }
432                 if (!getLyXTextClass().isTeXClassAvailable()) {
433                         string const msg =
434                                 bformat(_("The document uses a missing "
435                                 "TeX class \"%1$s\".\n"), classname);
436                         Alert::warning(_("Document class not available"),
437                                        msg + _("LyX will not be able to produce output."));
438                 }
439         } else if (token == "\\begin_preamble") {
440                 readPreamble(lex);
441         } else if (token == "\\options") {
442                 lex.eatLine();
443                 options = lex.getString();
444         } else if (token == "\\language") {
445                 readLanguage(lex);
446         } else if (token == "\\inputencoding") {
447                 lex >> inputenc;
448         } else if (token == "\\graphics") {
449                 readGraphicsDriver(lex);
450         } else if (token == "\\fontscheme") {
451                 lex >> fonts;
452         } else if (token == "\\paragraph_separation") {
453                 string parsep;
454                 lex >> parsep;
455                 paragraph_separation = parseptranslator().find(parsep);
456         } else if (token == "\\defskip") {
457                 lex.next();
458                 pimpl_->defskip = VSpace(lex.getString());
459         } else if (token == "\\quotes_language") {
460                 string quotes_lang;
461                 lex >> quotes_lang;
462                 quotes_language = quoteslangtranslator().find(quotes_lang);
463         } else if (token == "\\quotes_times") {
464                 int qtimes;
465                 lex >> qtimes;
466                 quotes_times = quotestimestranslator().find(qtimes);
467         } else if (token == "\\papersize") {
468                 string ppsize;
469                 lex >> ppsize;
470                 papersize = papersizetranslator().find(ppsize);
471         } else if (token == "\\use_geometry") {
472                 lex >> use_geometry;
473         } else if (token == "\\use_amsmath") {
474                 int use_ams;
475                 lex >> use_ams;
476                 use_amsmath = amstranslator().find(use_ams);
477         } else if (token == "\\cite_engine") {
478                 string engine;
479                 lex >> engine;
480                 cite_engine = citeenginetranslator().find(engine);
481         } else if (token == "\\use_bibtopic") {
482                 lex >> use_bibtopic;
483         } else if (token == "\\tracking_changes") {
484                 lex >> tracking_changes;
485         } else if (token == "\\output_changes") {
486                 lex >> output_changes;
487         } else if (token == "\\branch") {
488                 lex.next();
489                 string branch = lex.getString();
490                 branchlist().add(branch);
491                 while (true) {
492                         lex.next();
493                         string const tok = lex.getString();
494                         if (tok == "\\end_branch")
495                                 break;
496                         Branch * branch_ptr = branchlist().find(branch);
497                         if (tok == "\\selected") {
498                                 lex.next();
499                                 if (branch_ptr)
500                                         branch_ptr->setSelected(lex.getInteger());
501                         }
502                         // not yet operational
503                         if (tok == "\\color") {
504                                 lex.eatLine();
505                                 string color = lex.getString();
506                                 if (branch_ptr)
507                                         branch_ptr->setColor(color);
508                                 // Update also the LColor table:
509                                 if (color == "none")
510                                         color = lcolor.getX11Name(LColor::background);
511                                 lcolor.setColor(branch, color);
512
513                         }
514                 }
515         } else if (token == "\\author") {
516                 lex.eatLine();
517                 istringstream ss(lex.getString());
518                 Author a;
519                 ss >> a;
520                 author_map.push_back(pimpl_->authorlist.record(a));
521         } else if (token == "\\paperorientation") {
522                 string orient;
523                 lex >> orient;
524                 orientation = paperorientationtranslator().find(orient);
525         } else if (token == "\\paperwidth") {
526                 lex >> paperwidth;
527         } else if (token == "\\paperheight") {
528                 lex >> paperheight;
529         } else if (token == "\\leftmargin") {
530                 lex >> leftmargin;
531         } else if (token == "\\topmargin") {
532                 lex >> topmargin;
533         } else if (token == "\\rightmargin") {
534                 lex >> rightmargin;
535         } else if (token == "\\bottommargin") {
536                 lex >> bottommargin;
537         } else if (token == "\\headheight") {
538                 lex >> headheight;
539         } else if (token == "\\headsep") {
540                 lex >> headsep;
541         } else if (token == "\\footskip") {
542                 lex >> footskip;
543         } else if (token == "\\paperfontsize") {
544                 lex >> fontsize;
545         } else if (token == "\\papercolumns") {
546                 lex >> columns;
547         } else if (token == "\\papersides") {
548                 int psides;
549                 lex >> psides;
550                 sides = sidestranslator().find(psides);
551         } else if (token == "\\paperpagestyle") {
552                 lex >> pagestyle;
553         } else if (token == "\\bullet") {
554                 readBullets(lex);
555         } else if (token == "\\bulletLaTeX") {
556                 readBulletsLaTeX(lex);
557         } else if (token == "\\secnumdepth") {
558                 lex >> secnumdepth;
559         } else if (token == "\\tocdepth") {
560                 lex >> tocdepth;
561         } else if (token == "\\spacing") {
562                 string nspacing;
563                 lex >> nspacing;
564                 string tmp_val;
565                 if (nspacing == "other") {
566                         lex >> tmp_val;
567                 }
568                 spacing().set(spacetranslator().find(nspacing), tmp_val);
569         } else if (token == "\\float_placement") {
570                 lex >> float_placement;
571         } else {
572                 return token;
573         }
574
575         return string();
576 }
577
578
579 void BufferParams::writeFile(ostream & os) const
580 {
581         // The top of the file is written by the buffer.
582         // Prints out the buffer info into the .lyx file given by file
583
584         // the textclass
585         os << "\\textclass " << textclasslist[textclass].name() << '\n';
586
587         // then the the preamble
588         if (!preamble.empty()) {
589                 // remove '\n' from the end of preamble
590                 string const tmppreamble = rtrim(preamble, "\n");
591                 os << "\\begin_preamble\n"
592                    << tmppreamble
593                    << "\n\\end_preamble\n";
594         }
595
596         // the options
597         if (!options.empty()) {
598                 os << "\\options " << options << '\n';
599         }
600
601         // then the text parameters
602         if (language != ignore_language)
603                 os << "\\language " << language->lang() << '\n';
604         os << "\\inputencoding " << inputenc
605            << "\n\\fontscheme " << fonts
606            << "\n\\graphics " << graphicsDriver << '\n';
607
608         if (!float_placement.empty()) {
609                 os << "\\float_placement " << float_placement << '\n';
610         }
611         os << "\\paperfontsize " << fontsize << '\n';
612
613         spacing().writeFile(os);
614
615         os << "\\papersize " << string_papersize[papersize]
616            << "\n\\use_geometry " << convert<string>(use_geometry)
617            << "\n\\use_amsmath " << use_amsmath
618            << "\n\\cite_engine " << citeenginetranslator().find(cite_engine)
619            << "\n\\use_bibtopic " << convert<string>(use_bibtopic)
620            << "\n\\paperorientation " << string_orientation[orientation]
621            << '\n';
622
623         std::list<Branch>::const_iterator it = branchlist().begin();
624         std::list<Branch>::const_iterator end = branchlist().end();
625         for (; it != end; ++it) {
626                 os << "\\branch " << it->getBranch()
627                    << "\n\\selected " << it->getSelected()
628                    << "\n\\color " << it->getColor()
629                    << "\n\\end_branch"
630                    << "\n";
631         }
632
633         if (!paperwidth.empty())
634                 os << "\\paperwidth "
635                    << VSpace(paperwidth).asLyXCommand() << '\n';
636         if (!paperheight.empty())
637                 os << "\\paperheight "
638                    << VSpace(paperheight).asLyXCommand() << '\n';
639         if (!leftmargin.empty())
640                 os << "\\leftmargin "
641                    << VSpace(leftmargin).asLyXCommand() << '\n';
642         if (!topmargin.empty())
643                 os << "\\topmargin "
644                    << VSpace(topmargin).asLyXCommand() << '\n';
645         if (!rightmargin.empty())
646                 os << "\\rightmargin "
647                    << VSpace(rightmargin).asLyXCommand() << '\n';
648         if (!bottommargin.empty())
649                 os << "\\bottommargin "
650                    << VSpace(bottommargin).asLyXCommand() << '\n';
651         if (!headheight.empty())
652                 os << "\\headheight "
653                    << VSpace(headheight).asLyXCommand() << '\n';
654         if (!headsep.empty())
655                 os << "\\headsep "
656                    << VSpace(headsep).asLyXCommand() << '\n';
657         if (!footskip.empty())
658                 os << "\\footskip "
659                    << VSpace(footskip).asLyXCommand() << '\n';
660         os << "\\secnumdepth " << secnumdepth
661            << "\n\\tocdepth " << tocdepth
662            << "\n\\paragraph_separation "
663            << string_paragraph_separation[paragraph_separation]
664            << "\n\\defskip " << getDefSkip().asLyXCommand()
665            << "\n\\quotes_language "
666            << string_quotes_language[quotes_language] << '\n'
667            << "\\quotes_times "
668            << quotestimestranslator().find(quotes_times)
669            << "\n\\papercolumns " << columns
670            << "\n\\papersides " << sides
671            << "\n\\paperpagestyle " << pagestyle << '\n';
672         for (int i = 0; i < 4; ++i) {
673                 if (user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
674                         if (user_defined_bullet(i).getFont() != -1) {
675                                 os << "\\bullet " << i << " "
676                                    << user_defined_bullet(i).getFont() << " "
677                                    << user_defined_bullet(i).getCharacter() << " "
678                                    << user_defined_bullet(i).getSize() << "\n";
679                         }
680                         else {
681                                 os << "\\bulletLaTeX " << i << " \""
682                                    << user_defined_bullet(i).getText()
683                                    << "\"\n";
684                         }
685                 }
686         }
687
688         os << "\\tracking_changes " << convert<string>(tracking_changes) << "\n";
689         os << "\\output_changes " << convert<string>(output_changes) << "\n";
690
691         if (tracking_changes) {
692                 AuthorList::Authors::const_iterator it = pimpl_->authorlist.begin();
693                 AuthorList::Authors::const_iterator end = pimpl_->authorlist.end();
694                 for (; it != end; ++it) {
695                         os << "\\author " << it->second << "\n";
696                 }
697         }
698 }
699
700
701 bool BufferParams::writeLaTeX(ostream & os, LaTeXFeatures & features,
702                               TexRow & texrow) const
703 {
704         os << "\\documentclass";
705
706         LyXTextClass const & tclass = getLyXTextClass();
707
708         ostringstream clsoptions; // the document class options.
709
710         if (tokenPos(tclass.opt_fontsize(),
711                      '|', fontsize) >= 0) {
712                 // only write if existing in list (and not default)
713                 clsoptions << fontsize << "pt,";
714         }
715
716         // custom, A3, B3 and B4 paper sizes need geometry
717         bool nonstandard_papersize = (papersize == PAPER_B3) ||
718                                      (papersize == PAPER_B4) ||
719                                      (papersize == PAPER_A3) ||
720                                      (papersize == PAPER_CUSTOM);
721
722         if (!use_geometry) {
723                 switch (papersize) {
724                 case PAPER_A4:
725                         clsoptions << "a4paper,";
726                         break;
727                 case PAPER_USLETTER:
728                         clsoptions << "letterpaper,";
729                         break;
730                 case PAPER_A5:
731                         clsoptions << "a5paper,";
732                         break;
733                 case PAPER_B5:
734                         clsoptions << "b5paper,";
735                         break;
736                 case PAPER_USEXECUTIVE:
737                         clsoptions << "executivepaper,";
738                         break;
739                 case PAPER_USLEGAL:
740                         clsoptions << "legalpaper,";
741                         break;
742                 case PAPER_DEFAULT:
743                 case PAPER_A3:
744                 case PAPER_B3:
745                 case PAPER_B4:
746                 case PAPER_CUSTOM:
747                         break;
748                 }
749         }
750
751         // if needed
752         if (sides != tclass.sides()) {
753                 switch (sides) {
754                 case LyXTextClass::OneSide:
755                         clsoptions << "oneside,";
756                         break;
757                 case LyXTextClass::TwoSides:
758                         clsoptions << "twoside,";
759                         break;
760                 }
761         }
762
763         // if needed
764         if (columns != tclass.columns()) {
765                 if (columns == 2)
766                         clsoptions << "twocolumn,";
767                 else
768                         clsoptions << "onecolumn,";
769         }
770
771         if (!use_geometry
772             && orientation == ORIENTATION_LANDSCAPE)
773                 clsoptions << "landscape,";
774
775         // language should be a parameter to \documentclass
776         if (language->babel() == "hebrew"
777             && default_language->babel() != "hebrew")
778                 // This seems necessary
779                 features.useLanguage(default_language);
780
781         ostringstream language_options;
782         bool const use_babel = features.useBabel();
783         if (use_babel) {
784                 language_options << features.getLanguages();
785                 language_options << language->babel();
786                 if (lyxrc.language_global_options)
787                         clsoptions << language_options.str() << ',';
788         }
789
790         // the user-defined options
791         if (!options.empty()) {
792                 clsoptions << options << ',';
793         }
794
795         string strOptions(clsoptions.str());
796         if (!strOptions.empty()) {
797                 strOptions = rtrim(strOptions, ",");
798                 os << '[' << strOptions << ']';
799         }
800
801         os << '{' << tclass.latexname() << "}\n";
802         texrow.newline();
803         // end of \documentclass defs
804
805         // font selection must be done before loading fontenc.sty
806         // The ae package is not needed when using OT1 font encoding.
807         if (fonts != "default" &&
808             (fonts != "ae" || lyxrc.fontenc != "default")) {
809                 os << "\\usepackage{" << fonts << "}\n";
810                 texrow.newline();
811                 if (fonts == "ae") {
812                         os << "\\usepackage{aecompl}\n";
813                         texrow.newline();
814                 }
815         }
816         // this one is not per buffer
817         if (lyxrc.fontenc != "default") {
818                 os << "\\usepackage[" << lyxrc.fontenc
819                    << "]{fontenc}\n";
820                 texrow.newline();
821         }
822
823         if (inputenc == "auto") {
824                 string const doc_encoding =
825                         language->encoding()->LatexName();
826
827                 // Create a list with all the input encodings used
828                 // in the document
829                 std::set<string> encodings =
830                         features.getEncodingSet(doc_encoding);
831
832                 os << "\\usepackage[";
833                 std::copy(encodings.begin(), encodings.end(),
834                           std::ostream_iterator<string>(os, ","));
835                 os << doc_encoding << "]{inputenc}\n";
836                 texrow.newline();
837         } else if (inputenc != "default") {
838                 os << "\\usepackage[" << inputenc
839                    << "]{inputenc}\n";
840                 texrow.newline();
841         }
842
843         if (use_geometry || nonstandard_papersize) {
844                 os << "\\usepackage{geometry}\n";
845                 texrow.newline();
846                 os << "\\geometry{verbose";
847                 if (orientation == ORIENTATION_LANDSCAPE)
848                         os << ",landscape";
849                 switch (papersize) {
850                 case PAPER_CUSTOM:
851                         if (!paperwidth.empty())
852                                 os << ",paperwidth="
853                                    << paperwidth;
854                         if (!paperheight.empty())
855                                 os << ",paperheight="
856                                    << paperheight;
857                         break;
858                 case PAPER_USLETTER:
859                         os << ",letterpaper";
860                         break;
861                 case PAPER_USLEGAL:
862                         os << ",legalpaper";
863                         break;
864                 case PAPER_USEXECUTIVE:
865                         os << ",executivepaper";
866                         break;
867                 case PAPER_A3:
868                         os << ",a3paper";
869                         break;
870                 case PAPER_A4:
871                         os << ",a4paper";
872                         break;
873                 case PAPER_A5:
874                         os << ",a5paper";
875                         break;
876                 case PAPER_B3:
877                         os << ",b3paper";
878                         break;
879                 case PAPER_B4:
880                         os << ",b4paper";
881                         break;
882                 case PAPER_B5:
883                         os << ",b5paper";
884                         break;
885                 default:
886                         // default papersize ie PAPER_DEFAULT
887                         switch (lyxrc.default_papersize) {
888                         case PAPER_DEFAULT: // keep compiler happy
889                         case PAPER_USLETTER:
890                                 os << ",letterpaper";
891                                 break;
892                         case PAPER_USLEGAL:
893                                 os << ",legalpaper";
894                                 break;
895                         case PAPER_USEXECUTIVE:
896                                 os << ",executivepaper";
897                                 break;
898                         case PAPER_A3:
899                                 os << ",a3paper";
900                                 break;
901                         case PAPER_A4:
902                                 os << ",a4paper";
903                                 break;
904                         case PAPER_A5:
905                                 os << ",a5paper";
906                                 break;
907                         case PAPER_B5:
908                                 os << ",b5paper";
909                                 break;
910                         case PAPER_B3:
911                         case PAPER_B4:
912                         case PAPER_CUSTOM:
913                                 break;
914                         }
915                 }
916                 if (!topmargin.empty())
917                         os << ",tmargin=" << topmargin;
918                 if (!bottommargin.empty())
919                         os << ",bmargin=" << bottommargin;
920                 if (!leftmargin.empty())
921                         os << ",lmargin=" << leftmargin;
922                 if (!rightmargin.empty())
923                         os << ",rmargin=" << rightmargin;
924                 if (!headheight.empty())
925                         os << ",headheight=" << headheight;
926                 if (!headsep.empty())
927                         os << ",headsep=" << headsep;
928                 if (!footskip.empty())
929                         os << ",footskip=" << footskip;
930                 os << "}\n";
931                 texrow.newline();
932         }
933
934         if (tokenPos(tclass.opt_pagestyle(),
935                      '|', pagestyle) >= 0) {
936                 if (pagestyle == "fancy") {
937                         os << "\\usepackage{fancyhdr}\n";
938                         texrow.newline();
939                 }
940                 os << "\\pagestyle{" << pagestyle << "}\n";
941                 texrow.newline();
942         }
943
944         if (secnumdepth != tclass.secnumdepth()) {
945                 os << "\\setcounter{secnumdepth}{"
946                    << secnumdepth
947                    << "}\n";
948                 texrow.newline();
949         }
950         if (tocdepth != tclass.tocdepth()) {
951                 os << "\\setcounter{tocdepth}{"
952                    << tocdepth
953                    << "}\n";
954                 texrow.newline();
955         }
956
957         if (paragraph_separation) {
958                 switch (getDefSkip().kind()) {
959                 case VSpace::SMALLSKIP:
960                         os << "\\setlength\\parskip{\\smallskipamount}\n";
961                         break;
962                 case VSpace::MEDSKIP:
963                         os << "\\setlength\\parskip{\\medskipamount}\n";
964                         break;
965                 case VSpace::BIGSKIP:
966                         os << "\\setlength\\parskip{\\bigskipamount}\n";
967                         break;
968                 case VSpace::LENGTH:
969                         os << "\\setlength\\parskip{"
970                            << getDefSkip().length().asLatexString()
971                            << "}\n";
972                         break;
973                 default: // should never happen // Then delete it.
974                         os << "\\setlength\\parskip{\\medskipamount}\n";
975                         break;
976                 }
977                 texrow.newline();
978
979                 os << "\\setlength\\parindent{0pt}\n";
980                 texrow.newline();
981         }
982
983         // If we use jurabib, we have to call babel here.
984         if (use_babel && features.isRequired("jurabib")) {
985                 os << babelCall(language_options.str())
986                    << '\n'
987                    << features.getBabelOptions();
988                 texrow.newline();
989         }
990
991         // Now insert the LyX specific LaTeX commands...
992
993         // The optional packages;
994         string lyxpreamble(features.getPackages());
995
996         // this might be useful...
997         lyxpreamble += "\n\\makeatletter\n";
998
999         // Some macros LyX will need
1000         string tmppreamble(features.getMacros());
1001
1002         if (!tmppreamble.empty()) {
1003                 lyxpreamble += "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
1004                         "LyX specific LaTeX commands.\n"
1005                         + tmppreamble + '\n';
1006         }
1007
1008         // the text class specific preamble
1009         tmppreamble = features.getTClassPreamble();
1010         if (!tmppreamble.empty()) {
1011                 lyxpreamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
1012                         "Textclass specific LaTeX commands.\n"
1013                         + tmppreamble + '\n';
1014         }
1015
1016         /* the user-defined preamble */
1017         if (!preamble.empty()) {
1018                 lyxpreamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
1019                         "User specified LaTeX commands.\n"
1020                         + preamble + '\n';
1021         }
1022
1023         // Itemize bullet settings need to be last in case the user
1024         // defines their own bullets that use a package included
1025         // in the user-defined preamble -- ARRae
1026         // Actually it has to be done much later than that
1027         // since some packages like frenchb make modifications
1028         // at \begin{document} time -- JMarc
1029         string bullets_def;
1030         for (int i = 0; i < 4; ++i) {
1031                 if (user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1032                         if (bullets_def.empty())
1033                                 bullets_def="\\AtBeginDocument{\n";
1034                         bullets_def += "  \\renewcommand{\\labelitemi";
1035                         switch (i) {
1036                                 // `i' is one less than the item to modify
1037                         case 0:
1038                                 break;
1039                         case 1:
1040                                 bullets_def += 'i';
1041                                 break;
1042                         case 2:
1043                                 bullets_def += "ii";
1044                                 break;
1045                         case 3:
1046                                 bullets_def += 'v';
1047                                 break;
1048                         }
1049                         bullets_def += "}{" +
1050                                 user_defined_bullet(i).getText()
1051                                 + "}\n";
1052                 }
1053         }
1054
1055         if (!bullets_def.empty())
1056                 lyxpreamble += bullets_def + "}\n\n";
1057
1058         // We try to load babel late, in case it interferes
1059         // with other packages.
1060         // Jurabib has to be called after babel, though.
1061         if (use_babel && !features.isRequired("jurabib")) {
1062                 lyxpreamble += babelCall(language_options.str()) + '\n';
1063                 lyxpreamble += features.getBabelOptions();
1064         }
1065
1066         lyxpreamble += "\\makeatother\n";
1067
1068         // dvipost settings come after everything else
1069         if (features.isAvailable("dvipost") && tracking_changes && output_changes) {
1070                 lyxpreamble +=
1071                         "\\dvipostlayout\n"
1072                         "\\dvipost{osstart color push Red}\n"
1073                         "\\dvipost{osend color pop}\n"
1074                         "\\dvipost{cbstart color push Blue}\n"
1075                         "\\dvipost{cbend color pop}\n";
1076         }
1077
1078         int const nlines =
1079                 int(lyx::count(lyxpreamble.begin(), lyxpreamble.end(), '\n'));
1080         for (int j = 0; j != nlines; ++j) {
1081                 texrow.newline();
1082         }
1083
1084         os << lyxpreamble;
1085         return use_babel;
1086 }
1087
1088
1089 void BufferParams::useClassDefaults()
1090 {
1091         LyXTextClass const & tclass = textclasslist[textclass];
1092
1093         sides = tclass.sides();
1094         columns = tclass.columns();
1095         pagestyle = tclass.pagestyle();
1096         options = tclass.options();
1097         secnumdepth = tclass.secnumdepth();
1098         tocdepth = tclass.tocdepth();
1099 }
1100
1101
1102 bool BufferParams::hasClassDefaults() const
1103 {
1104         LyXTextClass const & tclass = textclasslist[textclass];
1105
1106         return (sides == tclass.sides()
1107                 && columns == tclass.columns()
1108                 && pagestyle == tclass.pagestyle()
1109                 && options == tclass.options()
1110                 && secnumdepth == tclass.secnumdepth()
1111                 && tocdepth == tclass.tocdepth());
1112 }
1113
1114
1115 LyXTextClass const & BufferParams::getLyXTextClass() const
1116 {
1117         return textclasslist[textclass];
1118 }
1119
1120
1121 void BufferParams::readPreamble(LyXLex & lex)
1122 {
1123         if (lex.getString() != "\\begin_preamble")
1124                 lyxerr << "Error (BufferParams::readPreamble):"
1125                         "consistency check failed." << endl;
1126
1127         preamble = lex.getLongString("\\end_preamble");
1128 }
1129
1130
1131 void BufferParams::readLanguage(LyXLex & lex)
1132 {
1133         if (!lex.next()) return;
1134
1135         string const tmptok = lex.getString();
1136
1137         // check if tmptok is part of tex_babel in tex-defs.h
1138         language = languages.getLanguage(tmptok);
1139         if (!language) {
1140                 // Language tmptok was not found
1141                 language = default_language;
1142                 lyxerr << "Warning: Setting language `"
1143                        << tmptok << "' to `" << language->lang()
1144                        << "'." << endl;
1145         }
1146 }
1147
1148
1149 void BufferParams::readGraphicsDriver(LyXLex & lex)
1150 {
1151         if (!lex.next()) return;
1152
1153         string const tmptok = lex.getString();
1154         // check if tmptok is part of tex_graphics in tex_defs.h
1155         int n = 0;
1156         while (true) {
1157                 string const test = tex_graphics[n++];
1158
1159                 if (test == tmptok) {
1160                         graphicsDriver = tmptok;
1161                         break;
1162                 } else if (test == "") {
1163                         lex.printError(
1164                                 "Warning: graphics driver `$$Token' not recognized!\n"
1165                                 "         Setting graphics driver to `default'.\n");
1166                         graphicsDriver = "default";
1167                         break;
1168                 }
1169         }
1170 }
1171
1172
1173 void BufferParams::readBullets(LyXLex & lex)
1174 {
1175         if (!lex.next()) return;
1176
1177         int const index = lex.getInteger();
1178         lex.next();
1179         int temp_int = lex.getInteger();
1180         user_defined_bullet(index).setFont(temp_int);
1181         temp_bullet(index).setFont(temp_int);
1182         lex >> temp_int;
1183         user_defined_bullet(index).setCharacter(temp_int);
1184         temp_bullet(index).setCharacter(temp_int);
1185         lex >> temp_int;
1186         user_defined_bullet(index).setSize(temp_int);
1187         temp_bullet(index).setSize(temp_int);
1188 }
1189
1190
1191 void BufferParams::readBulletsLaTeX(LyXLex & lex)
1192 {
1193         // The bullet class should be able to read this.
1194         if (!lex.next()) return;
1195         int const index = lex.getInteger();
1196         lex.next(true);
1197         string const temp_str = lex.getString();
1198
1199         user_defined_bullet(index).setText(temp_str);
1200         temp_bullet(index).setText(temp_str);
1201 }
1202
1203
1204 string const BufferParams::paperSizeName() const
1205 {
1206         char real_papersize = papersize;
1207         if (real_papersize == PAPER_DEFAULT)
1208                 real_papersize = lyxrc.default_papersize;
1209
1210         switch (real_papersize) {
1211         case PAPER_A3:
1212                 return "a3";
1213         case PAPER_A4:
1214                 return "a4";
1215         case PAPER_A5:
1216                 return "a5";
1217         case PAPER_B5:
1218                 return "b5";
1219         case PAPER_USEXECUTIVE:
1220                 return "foolscap";
1221         case PAPER_USLEGAL:
1222                 return "legal";
1223         case PAPER_USLETTER:
1224         default:
1225                 return "letter";
1226         }
1227 }
1228
1229
1230 string const BufferParams::dvips_options() const
1231 {
1232         string result;
1233
1234         if (use_geometry
1235             && papersize == PAPER_CUSTOM
1236             && !lyxrc.print_paper_dimension_flag.empty()
1237             && !paperwidth.empty()
1238             && !paperheight.empty()) {
1239                 // using a custom papersize
1240                 result = lyxrc.print_paper_dimension_flag;
1241                 result += ' ' + paperwidth;
1242                 result += ',' + paperheight;
1243         } else {
1244                 string const paper_option = paperSizeName();
1245                 if (paper_option != "letter" ||
1246                     orientation != ORIENTATION_LANDSCAPE) {
1247                         // dvips won't accept -t letter -t landscape.
1248                         // In all other cases, include the paper size
1249                         // explicitly.
1250                         result = lyxrc.print_paper_flag;
1251                         result += ' ' + paper_option;
1252                 }
1253         }
1254         if (orientation == ORIENTATION_LANDSCAPE &&
1255             papersize != PAPER_CUSTOM)
1256                 result += ' ' + lyxrc.print_landscape_flag;
1257         return result;
1258 }
1259
1260
1261 string const BufferParams::babelCall(string const & lang_opts) const
1262 {
1263         string tmp = lyxrc.language_package;
1264         if (!lyxrc.language_global_options && tmp == "\\usepackage{babel}")
1265                 tmp = string("\\usepackage[") + lang_opts + "]{babel}";
1266         return tmp;
1267 }