]> git.lyx.org Git - lyx.git/blob - src/BufferParams.cpp
Cache icons of dynamic menu buttons
[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.h"
24 #include "Bullet.h"
25 #include "CiteEnginesList.h"
26 #include "Color.h"
27 #include "ColorSet.h"
28 #include "Converter.h"
29 #include "Encoding.h"
30 #include "Format.h"
31 #include "IndicesList.h"
32 #include "Language.h"
33 #include "LaTeXFeatures.h"
34 #include "LaTeXFonts.h"
35 #include "Font.h"
36 #include "Lexer.h"
37 #include "LyXRC.h"
38 #include "OutputParams.h"
39 #include "Spacing.h"
40 #include "texstream.h"
41 #include "TexRow.h"
42 #include "VSpace.h"
43 #include "PDFOptions.h"
44
45 #include "frontends/alert.h"
46
47 #include "insets/InsetListingsParams.h"
48 #include "insets/InsetQuotes.h"
49
50 #include "support/convert.h"
51 #include "support/debug.h"
52 #include "support/FileName.h"
53 #include "support/filetools.h"
54 #include "support/gettext.h"
55 #include "support/Length.h"
56 #include "support/Messages.h"
57 #include "support/mutex.h"
58 #include "support/Package.h"
59 #include "support/Translator.h"
60 #include "support/lstrings.h"
61
62 #include <algorithm>
63 #include <sstream>
64
65 using namespace std;
66 using namespace lyx::support;
67
68
69 static char const * const string_paragraph_separation[] = {
70         "indent", "skip", ""
71 };
72
73
74 static char const * const string_quotes_style[] = {
75         "english", "swedish", "german", "polish", "swiss", "danish", "plain",
76         "british", "swedishg", "french", "frenchin", "russian", "cjk", "cjkangle",
77         "hungarian", ""
78 };
79
80
81 static char const * const string_papersize[] = {
82         "default", "custom", "letter", "legal", "executive",
83         "a0", "a1", "a2", "a3", "a4", "a5", "a6",
84         "b0", "b1", "b2", "b3", "b4", "b5", "b6",
85         "c0", "c1", "c2", "c3", "c4", "c5", "c6",
86         "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", ""
87 };
88
89
90 static char const * const string_papersize_geometry[] = {
91         "default", "custom", "letterpaper", "legalpaper", "executivepaper",
92         "a0paper", "a1paper", "a2paper", "a3paper", "a4paper", "a5paper", "a6paper",
93         "b0paper", "b1paper", "b2paper", "b3paper", "b4paper", "b5paper", "b6paper",
94         "c0paper", "c1paper", "c2paper", "c3paper", "c4paper", "c5paper", "c6paper",
95         "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", ""
96 };
97
98
99 static char const * const string_orientation[] = {
100         "portrait", "landscape", ""
101 };
102
103
104 static char const * const tex_graphics[] = {
105         "default", "dvialw", "dvilaser", "dvipdf", "dvipdfm", "dvipdfmx",
106         "dvips", "dvipsone", "dvitops", "dviwin", "dviwindo", "dvi2ps", "emtex",
107         "ln", "oztex", "pctexhp", "pctexps", "pctexwin", "pctex32", "pdftex",
108         "psprint", "pubps", "tcidvi", "textures", "truetex", "vtex", "xdvi",
109         "xetex", "none", ""
110 };
111
112
113 namespace lyx {
114
115 // Local translators
116 namespace {
117
118 // Paragraph separation
119 typedef Translator<string, BufferParams::ParagraphSeparation> ParSepTranslator;
120
121
122 ParSepTranslator const init_parseptranslator()
123 {
124         ParSepTranslator translator
125                 (string_paragraph_separation[0], BufferParams::ParagraphIndentSeparation);
126         translator.addPair(string_paragraph_separation[1], BufferParams::ParagraphSkipSeparation);
127         return translator;
128 }
129
130
131 ParSepTranslator const & parseptranslator()
132 {
133         static ParSepTranslator const translator =
134                 init_parseptranslator();
135         return translator;
136 }
137
138
139 // Quotes style
140 typedef Translator<string, QuoteStyle> QuotesStyleTranslator;
141
142
143 QuotesStyleTranslator const init_quotesstyletranslator()
144 {
145         QuotesStyleTranslator translator
146                 (string_quotes_style[0], QuoteStyle::English);
147         translator.addPair(string_quotes_style[1], QuoteStyle::Swedish);
148         translator.addPair(string_quotes_style[2], QuoteStyle::German);
149         translator.addPair(string_quotes_style[3], QuoteStyle::Polish);
150         translator.addPair(string_quotes_style[4], QuoteStyle::Swiss);
151         translator.addPair(string_quotes_style[5], QuoteStyle::Danish);
152         translator.addPair(string_quotes_style[6], QuoteStyle::Plain);
153         translator.addPair(string_quotes_style[7], QuoteStyle::British);
154         translator.addPair(string_quotes_style[8], QuoteStyle::SwedishG);
155         translator.addPair(string_quotes_style[9], QuoteStyle::French);
156         translator.addPair(string_quotes_style[10], QuoteStyle::FrenchIN);
157         translator.addPair(string_quotes_style[11], QuoteStyle::Russian);
158         translator.addPair(string_quotes_style[12], QuoteStyle::CJK);
159         translator.addPair(string_quotes_style[13], QuoteStyle::CJKAngle);
160         translator.addPair(string_quotes_style[14], QuoteStyle::Hungarian);
161         return translator;
162 }
163
164
165 QuotesStyleTranslator const & quotesstyletranslator()
166 {
167         static QuotesStyleTranslator const translator =
168                 init_quotesstyletranslator();
169         return translator;
170 }
171
172
173 // Paper size
174 typedef Translator<string, PAPER_SIZE> PaperSizeTranslator;
175
176
177 static PaperSizeTranslator initPaperSizeTranslator()
178 {
179         PaperSizeTranslator translator(string_papersize[0], PAPER_DEFAULT);
180         translator.addPair(string_papersize[1], PAPER_CUSTOM);
181         translator.addPair(string_papersize[2], PAPER_USLETTER);
182         translator.addPair(string_papersize[3], PAPER_USLEGAL);
183         translator.addPair(string_papersize[4], PAPER_USEXECUTIVE);
184         translator.addPair(string_papersize[5], PAPER_A0);
185         translator.addPair(string_papersize[6], PAPER_A1);
186         translator.addPair(string_papersize[7], PAPER_A2);
187         translator.addPair(string_papersize[8], PAPER_A3);
188         translator.addPair(string_papersize[9], PAPER_A4);
189         translator.addPair(string_papersize[10], PAPER_A5);
190         translator.addPair(string_papersize[11], PAPER_A6);
191         translator.addPair(string_papersize[12], PAPER_B0);
192         translator.addPair(string_papersize[13], PAPER_B1);
193         translator.addPair(string_papersize[14], PAPER_B2);
194         translator.addPair(string_papersize[15], PAPER_B3);
195         translator.addPair(string_papersize[16], PAPER_B4);
196         translator.addPair(string_papersize[17], PAPER_B5);
197         translator.addPair(string_papersize[18], PAPER_B6);
198         translator.addPair(string_papersize[19], PAPER_C0);
199         translator.addPair(string_papersize[20], PAPER_C1);
200         translator.addPair(string_papersize[21], PAPER_C2);
201         translator.addPair(string_papersize[22], PAPER_C3);
202         translator.addPair(string_papersize[23], PAPER_C4);
203         translator.addPair(string_papersize[24], PAPER_C5);
204         translator.addPair(string_papersize[25], PAPER_C6);
205         translator.addPair(string_papersize[26], PAPER_JISB0);
206         translator.addPair(string_papersize[27], PAPER_JISB1);
207         translator.addPair(string_papersize[28], PAPER_JISB2);
208         translator.addPair(string_papersize[29], PAPER_JISB3);
209         translator.addPair(string_papersize[30], PAPER_JISB4);
210         translator.addPair(string_papersize[31], PAPER_JISB5);
211         translator.addPair(string_papersize[32], PAPER_JISB6);
212         return translator;
213 }
214
215
216 PaperSizeTranslator const & papersizetranslator()
217 {
218         static PaperSizeTranslator const translator =
219                 initPaperSizeTranslator();
220         return translator;
221 }
222
223
224 // Paper orientation
225 typedef Translator<string, PAPER_ORIENTATION> PaperOrientationTranslator;
226
227
228 PaperOrientationTranslator const init_paperorientationtranslator()
229 {
230         PaperOrientationTranslator translator(string_orientation[0], ORIENTATION_PORTRAIT);
231         translator.addPair(string_orientation[1], ORIENTATION_LANDSCAPE);
232         return translator;
233 }
234
235
236 PaperOrientationTranslator const & paperorientationtranslator()
237 {
238         static PaperOrientationTranslator const translator =
239             init_paperorientationtranslator();
240         return translator;
241 }
242
243
244 // Page sides
245 typedef Translator<int, PageSides> SidesTranslator;
246
247
248 SidesTranslator const init_sidestranslator()
249 {
250         SidesTranslator translator(1, OneSide);
251         translator.addPair(2, TwoSides);
252         return translator;
253 }
254
255
256 SidesTranslator const & sidestranslator()
257 {
258         static SidesTranslator const translator = init_sidestranslator();
259         return translator;
260 }
261
262
263 // LaTeX packages
264 typedef Translator<int, BufferParams::Package> PackageTranslator;
265
266
267 PackageTranslator const init_packagetranslator()
268 {
269         PackageTranslator translator(0, BufferParams::package_off);
270         translator.addPair(1, BufferParams::package_auto);
271         translator.addPair(2, BufferParams::package_on);
272         return translator;
273 }
274
275
276 PackageTranslator const & packagetranslator()
277 {
278         static PackageTranslator const translator =
279                 init_packagetranslator();
280         return translator;
281 }
282
283
284 // Spacing
285 typedef Translator<string, Spacing::Space> SpaceTranslator;
286
287
288 SpaceTranslator const init_spacetranslator()
289 {
290         SpaceTranslator translator("default", Spacing::Default);
291         translator.addPair("single", Spacing::Single);
292         translator.addPair("onehalf", Spacing::Onehalf);
293         translator.addPair("double", Spacing::Double);
294         translator.addPair("other", Spacing::Other);
295         return translator;
296 }
297
298
299 SpaceTranslator const & spacetranslator()
300 {
301         static SpaceTranslator const translator = init_spacetranslator();
302         return translator;
303 }
304
305
306 bool inSystemDir(FileName const & document_dir, string & system_dir)
307 {
308         // A document is assumed to be in a system LyX directory (not
309         // necessarily the system directory of the running instance)
310         // if both "configure.py" and "chkconfig.ltx" are found in
311         // either document_dir/../ or document_dir/../../.
312         // If true, the system directory path is returned in system_dir
313         // with a trailing path separator.
314
315         string const msg = "Checking whether document is in a system dir...";
316
317         string dir = document_dir.absFileName();
318
319         for (int i = 0; i < 3; ++i) {
320                 dir = addPath(dir, "..");
321                 if (!fileSearch(dir, "configure.py").empty() &&
322                     !fileSearch(dir, "chkconfig.ltx").empty()) {
323                         LYXERR(Debug::FILES, msg << " yes");
324                         system_dir = addPath(FileName(dir).realPath(), "");
325                         return true;
326                 }
327         }
328
329         LYXERR(Debug::FILES, msg << " no");
330         system_dir = string();
331         return false;
332 }
333
334 } // namespace
335
336
337 class BufferParams::Impl
338 {
339 public:
340         Impl();
341
342         AuthorList authorlist;
343         BranchList branchlist;
344         IgnoreList spellignore;
345         Bullet temp_bullets[4];
346         Bullet user_defined_bullets[4];
347         IndicesList indiceslist;
348         Spacing spacing;
349         Length parindent;
350         Length mathindent;
351         /** This is the amount of space used for paragraph_separation "skip",
352          * and for detached paragraphs in "indented" documents.
353          */
354         VSpace defskip;
355         PDFOptions pdfoptions;
356         LayoutFileIndex baseClass_;
357         FormatList exportableFormatList;
358         FormatList viewableFormatList;
359         bool isViewCacheValid;
360         bool isExportCacheValid;
361 };
362
363
364 BufferParams::Impl::Impl()
365         : defskip(VSpace::MEDSKIP), baseClass_(string("")),
366           isViewCacheValid(false), isExportCacheValid(false)
367 {
368         // set initial author
369         // FIXME UNICODE
370         authorlist.record(Author(from_utf8(lyxrc.user_name),
371                                  from_utf8(lyxrc.user_email),
372                                  from_utf8(lyxrc.user_initials)));
373 }
374
375
376 BufferParams::Impl *
377 BufferParams::MemoryTraits::clone(BufferParams::Impl const * ptr)
378 {
379         LBUFERR(ptr);
380         return new BufferParams::Impl(*ptr);
381 }
382
383
384 void BufferParams::MemoryTraits::destroy(BufferParams::Impl * ptr)
385 {
386         delete ptr;
387 }
388
389
390 BufferParams::BufferParams()
391         : pimpl_(new Impl)
392 {
393         setBaseClass(defaultBaseclass());
394         cite_engine_ = "basic";
395         cite_engine_type_ = ENGINE_TYPE_DEFAULT;
396         makeDocumentClass();
397         paragraph_separation = ParagraphIndentSeparation;
398         is_math_indent = false;
399         math_numbering_side = DEFAULT;
400         quotes_style = QuoteStyle::English;
401         dynamic_quotes = false;
402         fontsize = "default";
403
404         /*  PaperLayout */
405         papersize = PAPER_DEFAULT;
406         orientation = ORIENTATION_PORTRAIT;
407         use_geometry = false;
408         biblio_style = string();
409         use_bibtopic = false;
410         multibib = string();
411         use_indices = false;
412         save_transient_properties = true;
413         track_changes = false;
414         output_changes = false;
415         change_bars = false;
416         postpone_fragile_content = true;
417         use_default_options = true;
418         maintain_unincluded_children = CM_None;
419         secnumdepth = 3;
420         tocdepth = 3;
421         language = default_language;
422         fontenc = "auto";
423         fonts_roman[0] = "default";
424         fonts_roman[1] = "default";
425         fonts_sans[0] = "default";
426         fonts_sans[1] = "default";
427         fonts_typewriter[0] = "default";
428         fonts_typewriter[1] = "default";
429         fonts_math[0] = "auto";
430         fonts_math[1] = "auto";
431         fonts_default_family = "default";
432         useNonTeXFonts = false;
433         use_microtype = false;
434         use_dash_ligatures = true;
435         fonts_expert_sc = false;
436         fonts_roman_osf = false;
437         fonts_sans_osf = false;
438         fonts_typewriter_osf = false;
439         fonts_sans_scale[0] = 100;
440         fonts_sans_scale[1] = 100;
441         fonts_typewriter_scale[0] = 100;
442         fonts_typewriter_scale[1] = 100;
443         inputenc = "utf8";
444         lang_package = "default";
445         graphics_driver = "default";
446         default_output_format = "default";
447         bibtex_command = "default";
448         index_command = "default";
449         sides = OneSide;
450         columns = 1;
451         listings_params = string();
452         pagestyle = "default";
453         tablestyle = "default";
454         float_alignment = "class";
455         float_placement = "class";
456         suppress_date = false;
457         justification = true;
458         // no color is the default (white)
459         backgroundcolor = lyx::rgbFromHexName("#ffffff");
460         isbackgroundcolor = false;
461         // no color is the default (black)
462         fontcolor = lyx::rgbFromHexName("#000000");
463         isfontcolor = false;
464         // light gray is the default font color for greyed-out notes
465         notefontcolor = lyx::rgbFromHexName("#cccccc");
466         isnotefontcolor = false;
467         boxbgcolor = lyx::rgbFromHexName("#ff0000");
468         isboxbgcolor = false;
469         compressed = lyxrc.save_compressed;
470         for (int iter = 0; iter < 4; ++iter) {
471                 user_defined_bullet(iter) = ITEMIZE_DEFAULTS[iter];
472                 temp_bullet(iter) = ITEMIZE_DEFAULTS[iter];
473         }
474         // default index
475         indiceslist().addDefault(B_("Index"));
476         html_be_strict = false;
477         html_math_output = MathML;
478         html_math_img_scale = 1.0;
479         html_css_as_file = false;
480         docbook_table_output = HTMLTable;
481         docbook_mathml_prefix = MPrefix;
482         display_pixel_ratio = 1.0;
483
484         shell_escape = false;
485         output_sync = false;
486         use_refstyle = true;
487         use_minted = false;
488         use_lineno = false;
489
490         // map current author
491         author_map_[pimpl_->authorlist.get(0).bufferId()] = 0;
492 }
493
494
495 docstring BufferParams::B_(string const & l10n) const
496 {
497         LASSERT(language, return from_utf8(l10n));
498         return getMessages(language->code()).get(l10n);
499 }
500
501
502 BufferParams::Package BufferParams::use_package(std::string const & p) const
503 {
504         PackageMap::const_iterator it = use_packages.find(p);
505         if (it == use_packages.end())
506                 return package_auto;
507         return it->second;
508 }
509
510
511 void BufferParams::use_package(std::string const & p, BufferParams::Package u)
512 {
513         use_packages[p] = u;
514 }
515
516
517 map<string, string> const & BufferParams::auto_packages()
518 {
519         static map<string, string> packages;
520         if (packages.empty()) {
521                 // We could have a race condition here that two threads
522                 // discover an empty map at the same time and want to fill
523                 // it, but that is no problem, since the same contents is
524                 // filled in twice then. Having the locker inside the
525                 // packages.empty() condition has the advantage that we
526                 // don't need the mutex overhead for simple reading.
527                 static Mutex mutex;
528                 Mutex::Locker locker(&mutex);
529                 // adding a package here implies a file format change!
530                 packages["amsmath"] =
531                         N_("The LaTeX package amsmath is only used if AMS formula types or symbols from the AMS math toolbars are inserted into formulas");
532                 packages["amssymb"] =
533                         N_("The LaTeX package amssymb is only used if symbols from the AMS math toolbars are inserted into formulas");
534                 packages["cancel"] =
535                         N_("The LaTeX package cancel is only used if \\cancel commands are used in formulas");
536                 packages["esint"] =
537                         N_("The LaTeX package esint is only used if special integral symbols are inserted into formulas");
538                 packages["mathdots"] =
539                         N_("The LaTeX package mathdots is only used if the command \\iddots is inserted into formulas");
540                 packages["mathtools"] =
541                         N_("The LaTeX package mathtools is only used if some mathematical relations are inserted into formulas");
542                 packages["mhchem"] =
543                         N_("The LaTeX package mhchem is only used if either the command \\ce or \\cf is inserted into formulas");
544                 packages["stackrel"] =
545                         N_("The LaTeX package stackrel is only used if the command \\stackrel with subscript is inserted into formulas");
546                 packages["stmaryrd"] =
547                         N_("The LaTeX package stmaryrd is only used if symbols from the St Mary's Road symbol font for theoretical computer science are inserted into formulas");
548                 packages["undertilde"] =
549                         N_("The LaTeX package undertilde is only used if you use the math frame decoration 'utilde'");
550         }
551         return packages;
552 }
553
554
555 bool BufferParams::useBibtopic() const
556 {
557         if (useBiblatex())
558                 return false;
559         return (use_bibtopic || (!multibib.empty() && multibib != "child"));
560 }
561
562
563 AuthorList & BufferParams::authors()
564 {
565         return pimpl_->authorlist;
566 }
567
568
569 AuthorList const & BufferParams::authors() const
570 {
571         return pimpl_->authorlist;
572 }
573
574
575 void BufferParams::addAuthor(Author const & a)
576 {
577         author_map_[a.bufferId()] = pimpl_->authorlist.record(a);
578 }
579
580
581 BranchList & BufferParams::branchlist()
582 {
583         return pimpl_->branchlist;
584 }
585
586
587 BranchList const & BufferParams::branchlist() const
588 {
589         return pimpl_->branchlist;
590 }
591
592
593 IndicesList & BufferParams::indiceslist()
594 {
595         return pimpl_->indiceslist;
596 }
597
598
599 IndicesList const & BufferParams::indiceslist() const
600 {
601         return pimpl_->indiceslist;
602 }
603
604
605 typedef std::vector<WordLangTuple> IgnoreList;
606
607
608 IgnoreList & BufferParams::spellignore()
609 {
610         return pimpl_->spellignore;
611 }
612
613
614 IgnoreList const & BufferParams::spellignore() const
615 {
616         return pimpl_->spellignore;
617 }
618
619
620 bool BufferParams::spellignored(WordLangTuple const & wl) const
621 {
622         bool has_item = false;
623         vector<WordLangTuple> il = spellignore();
624         vector<WordLangTuple>::const_iterator it = il.begin();
625         for (; it != il.end(); ++it) {
626                 if (it->lang()->code() != wl.lang()->code())
627                         continue;
628                 if (it->word() == wl.word()) {
629                         has_item = true;
630                         break;
631                 }
632         }
633         return has_item;
634 }
635
636
637 Bullet & BufferParams::temp_bullet(lyx::size_type const index)
638 {
639         LASSERT(index < 4, return pimpl_->temp_bullets[0]);
640         return pimpl_->temp_bullets[index];
641 }
642
643
644 Bullet const & BufferParams::temp_bullet(lyx::size_type const index) const
645 {
646         LASSERT(index < 4, return pimpl_->temp_bullets[0]);
647         return pimpl_->temp_bullets[index];
648 }
649
650
651 Bullet & BufferParams::user_defined_bullet(lyx::size_type const index)
652 {
653         LASSERT(index < 4, return pimpl_->temp_bullets[0]);
654         return pimpl_->user_defined_bullets[index];
655 }
656
657
658 Bullet const & BufferParams::user_defined_bullet(lyx::size_type const index) const
659 {
660         LASSERT(index < 4, return pimpl_->temp_bullets[0]);
661         return pimpl_->user_defined_bullets[index];
662 }
663
664
665 Spacing & BufferParams::spacing()
666 {
667         return pimpl_->spacing;
668 }
669
670
671 Spacing const & BufferParams::spacing() const
672 {
673         return pimpl_->spacing;
674 }
675
676
677 PDFOptions & BufferParams::pdfoptions()
678 {
679         return pimpl_->pdfoptions;
680 }
681
682
683 PDFOptions const & BufferParams::pdfoptions() const
684 {
685         return pimpl_->pdfoptions;
686 }
687
688
689 Length const & BufferParams::getMathIndent() const
690 {
691         return pimpl_->mathindent;
692 }
693
694
695 void BufferParams::setMathIndent(Length const & indent)
696 {
697         pimpl_->mathindent = indent;
698 }
699
700
701 Length const & BufferParams::getParIndent() const
702 {
703         return pimpl_->parindent;
704 }
705
706
707 void BufferParams::setParIndent(Length const & indent)
708 {
709         pimpl_->parindent = indent;
710 }
711
712
713 VSpace const & BufferParams::getDefSkip() const
714 {
715         return pimpl_->defskip;
716 }
717
718
719 void BufferParams::setDefSkip(VSpace const & vs)
720 {
721         // DEFSKIP will cause an infinite loop
722         LASSERT(vs.kind() != VSpace::DEFSKIP, return);
723         pimpl_->defskip = vs;
724 }
725
726
727 BufferParams::MathNumber BufferParams::getMathNumber() const
728 {
729         if (math_numbering_side != DEFAULT)
730                 return math_numbering_side;
731         // FIXME: do not hardcode language here
732         else if (language->lang() == "arabic_arabi"
733                  || documentClass().provides("leqno"))
734                 return LEFT;
735         else
736                 return RIGHT;
737 }
738
739
740 string BufferParams::readToken(Lexer & lex, string const & token,
741         FileName const & filename)
742 {
743         string result;
744         FileName const & filepath = filename.onlyPath();
745
746         if (token == "\\textclass") {
747                 lex.next();
748                 string const classname = lex.getString();
749                 // if there exists a local layout file, ignore the system one
750                 // NOTE: in this case, the textclass (.cls file) is assumed to
751                 // be available.
752                 string tcp;
753                 LayoutFileList & bcl = LayoutFileList::get();
754                 if (!filepath.empty()) {
755                         // If classname is an absolute path, the document is
756                         // using a local layout file which could not be accessed
757                         // by a relative path. In this case the path is correct
758                         // even if the document was moved to a different
759                         // location. However, we will have a problem if the
760                         // document was generated on a different platform.
761                         bool isabsolute = FileName::isAbsolute(classname);
762                         string const classpath = onlyPath(classname);
763                         string const path = isabsolute ? classpath
764                                 : FileName(addPath(filepath.absFileName(),
765                                                 classpath)).realPath();
766                         string const oldpath = isabsolute ? string()
767                                 : FileName(addPath(origin, classpath)).realPath();
768                         tcp = bcl.addLocalLayout(onlyFileName(classname), path, oldpath);
769                 }
770                 // that returns non-empty if a "local" layout file is found.
771                 if (!tcp.empty()) {
772                         result = to_utf8(makeRelPath(from_utf8(onlyPath(tcp)),
773                                                 from_utf8(filepath.absFileName())));
774                         if (result.empty())
775                                 result = ".";
776                         setBaseClass(onlyFileName(tcp));
777                 } else
778                         setBaseClass(onlyFileName(classname));
779                 // We assume that a tex class exists for local or unknown
780                 // layouts so this warning, will only be given for system layouts.
781                 if (!baseClass()->isTeXClassAvailable()) {
782                         docstring const desc =
783                                 translateIfPossible(from_utf8(baseClass()->description()));
784                         docstring const prereqs =
785                                 from_utf8(baseClass()->prerequisites());
786                         docstring const msg =
787                                 bformat(_("The selected document class\n"
788                                                  "\t%1$s\n"
789                                                  "requires external files that are not available.\n"
790                                                  "The document class can still be used, but the\n"
791                                                  "document cannot be compiled until the following\n"
792                                                  "prerequisites are installed:\n"
793                                                  "\t%2$s\n"
794                                                  "See section 3.1.2.2 (Class Availability) of the\n"
795                                                  "User's Guide for more information."), desc, prereqs);
796                         frontend::Alert::warning(_("Document class not available"),
797                                        msg, true);
798                 }
799         } else if (token == "\\save_transient_properties") {
800                 lex >> save_transient_properties;
801         } else if (token == "\\origin") {
802                 lex.eatLine();
803                 origin = lex.getString();
804                 string const sysdirprefix = "/systemlyxdir/";
805                 if (prefixIs(origin, sysdirprefix)) {
806                         string docsys;
807                         if (inSystemDir(filepath, docsys))
808                                 origin.replace(0, sysdirprefix.length() - 1, docsys);
809                         else
810                                 origin.replace(0, sysdirprefix.length() - 1,
811                                         package().system_support().absFileName());
812                 }
813         } else if (token == "\\begin_preamble") {
814                 readPreamble(lex);
815         } else if (token == "\\begin_local_layout") {
816                 readLocalLayout(lex, false);
817         } else if (token == "\\begin_forced_local_layout") {
818                 readLocalLayout(lex, true);
819         } else if (token == "\\begin_modules") {
820                 readModules(lex);
821         } else if (token == "\\begin_removed_modules") {
822                 readRemovedModules(lex);
823         } else if (token == "\\begin_includeonly") {
824                 readIncludeonly(lex);
825         } else if (token == "\\maintain_unincluded_children") {
826                 string tmp;
827                 lex >> tmp;
828                 if (tmp == "no")
829                         maintain_unincluded_children = CM_None;
830                 else if (tmp == "mostly")
831                         maintain_unincluded_children = CM_Mostly;
832                 else if (tmp == "strict")
833                         maintain_unincluded_children = CM_Strict;
834         } else if (token == "\\options") {
835                 lex.eatLine();
836                 options = lex.getString();
837         } else if (token == "\\use_default_options") {
838                 lex >> use_default_options;
839         } else if (token == "\\master") {
840                 lex.eatLine();
841                 master = lex.getString();
842                 if (!filepath.empty() && FileName::isAbsolute(origin)) {
843                         bool const isabs = FileName::isAbsolute(master);
844                         FileName const abspath(isabs ? master : origin + master);
845                         bool const moved = filepath != FileName(origin);
846                         if (moved && abspath.exists()) {
847                                 docstring const path = isabs
848                                         ? from_utf8(master)
849                                         : from_utf8(abspath.realPath());
850                                 docstring const refpath =
851                                         from_utf8(filepath.absFileName());
852                                 master = to_utf8(makeRelPath(path, refpath));
853                         }
854                 }
855         } else if (token == "\\suppress_date") {
856                 lex >> suppress_date;
857         } else if (token == "\\justification") {
858                 lex >> justification;
859         } else if (token == "\\language") {
860                 readLanguage(lex);
861         } else if (token == "\\language_package") {
862                 lex.eatLine();
863                 lang_package = lex.getString();
864         } else if (token == "\\inputencoding") {
865                 lex >> inputenc;
866         } else if (token == "\\graphics") {
867                 readGraphicsDriver(lex);
868         } else if (token == "\\default_output_format") {
869                 lex >> default_output_format;
870         } else if (token == "\\bibtex_command") {
871                 lex.eatLine();
872                 bibtex_command = lex.getString();
873         } else if (token == "\\index_command") {
874                 lex.eatLine();
875                 index_command = lex.getString();
876         } else if (token == "\\fontencoding") {
877                 lex.eatLine();
878                 fontenc = lex.getString();
879         } else if (token == "\\font_roman") {
880                 lex >> fonts_roman[0];
881                 lex >> fonts_roman[1];
882         } else if (token == "\\font_sans") {
883                 lex >> fonts_sans[0];
884                 lex >> fonts_sans[1];
885         } else if (token == "\\font_typewriter") {
886                 lex >> fonts_typewriter[0];
887                 lex >> fonts_typewriter[1];
888         } else if (token == "\\font_math") {
889                 lex >> fonts_math[0];
890                 lex >> fonts_math[1];
891         } else if (token == "\\font_default_family") {
892                 lex >> fonts_default_family;
893         } else if (token == "\\use_non_tex_fonts") {
894                 lex >> useNonTeXFonts;
895         } else if (token == "\\font_sc") {
896                 lex >> fonts_expert_sc;
897         } else if (token == "\\font_roman_osf") {
898                 lex >> fonts_roman_osf;
899         } else if (token == "\\font_sans_osf") {
900                 lex >> fonts_sans_osf;
901         } else if (token == "\\font_typewriter_osf") {
902                 lex >> fonts_typewriter_osf;
903         } else if (token == "\\font_roman_opts") {
904                 lex >> font_roman_opts;
905         } else if (token == "\\font_sf_scale") {
906                 lex >> fonts_sans_scale[0];
907                 lex >> fonts_sans_scale[1];
908         } else if (token == "\\font_sans_opts") {
909                 lex >> font_sans_opts;
910         } else if (token == "\\font_tt_scale") {
911                 lex >> fonts_typewriter_scale[0];
912                 lex >> fonts_typewriter_scale[1];
913         } else if (token == "\\font_typewriter_opts") {
914                 lex >> font_typewriter_opts;
915         } else if (token == "\\font_cjk") {
916                 lex >> fonts_cjk;
917         } else if (token == "\\use_microtype") {
918                 lex >> use_microtype;
919         } else if (token == "\\use_dash_ligatures") {
920                 lex >> use_dash_ligatures;
921         } else if (token == "\\paragraph_separation") {
922                 string parsep;
923                 lex >> parsep;
924                 paragraph_separation = parseptranslator().find(parsep);
925         } else if (token == "\\paragraph_indentation") {
926                 lex.next();
927                 string parindent = lex.getString();
928                 if (parindent == "default")
929                         pimpl_->parindent = Length();
930                 else
931                         pimpl_->parindent = Length(parindent);
932         } else if (token == "\\defskip") {
933                 lex.next();
934                 string const defskip = lex.getString();
935                 pimpl_->defskip = VSpace(defskip);
936                 if (pimpl_->defskip.kind() == VSpace::DEFSKIP)
937                         // that is invalid
938                         pimpl_->defskip = VSpace(VSpace::MEDSKIP);
939         } else if (token == "\\is_math_indent") {
940                 lex >> is_math_indent;
941         } else if (token == "\\math_indentation") {
942                 lex.next();
943                 string mathindent = lex.getString();
944                 if (mathindent == "default")
945                         pimpl_->mathindent = Length();
946                 else
947                         pimpl_->mathindent = Length(mathindent);
948         } else if (token == "\\math_numbering_side") {
949                 string tmp;
950                 lex >> tmp;
951                 if (tmp == "left")
952                         math_numbering_side = LEFT;
953                 else if (tmp == "right")
954                         math_numbering_side = RIGHT;
955                 else
956                         math_numbering_side = DEFAULT;
957         } else if (token == "\\quotes_style") {
958                 string qstyle;
959                 lex >> qstyle;
960                 quotes_style = quotesstyletranslator().find(qstyle);
961         } else if (token == "\\dynamic_quotes") {
962                 lex >> dynamic_quotes;
963         } else if (token == "\\papersize") {
964                 string ppsize;
965                 lex >> ppsize;
966                 papersize = papersizetranslator().find(ppsize);
967         } else if (token == "\\use_geometry") {
968                 lex >> use_geometry;
969         } else if (token == "\\use_package") {
970                 string package;
971                 int use;
972                 lex >> package;
973                 lex >> use;
974                 use_package(package, packagetranslator().find(use));
975         } else if (token == "\\cite_engine") {
976                 lex.eatLine();
977                 cite_engine_ = lex.getString();
978         } else if (token == "\\cite_engine_type") {
979                 string engine_type;
980                 lex >> engine_type;
981                 cite_engine_type_ = theCiteEnginesList.getType(engine_type);
982         } else if (token == "\\biblio_style") {
983                 lex.eatLine();
984                 biblio_style = lex.getString();
985         } else if (token == "\\biblio_options") {
986                 lex.eatLine();
987                 biblio_opts = trim(lex.getString());
988         } else if (token == "\\biblatex_bibstyle") {
989                 lex.eatLine();
990                 biblatex_bibstyle = trim(lex.getString());
991         } else if (token == "\\biblatex_citestyle") {
992                 lex.eatLine();
993                 biblatex_citestyle = trim(lex.getString());
994         } else if (token == "\\use_bibtopic") {
995                 lex >> use_bibtopic;
996         } else if (token == "\\multibib") {
997                 lex >> multibib;
998         } else if (token == "\\use_indices") {
999                 lex >> use_indices;
1000         } else if (token == "\\tracking_changes") {
1001                 lex >> track_changes;
1002         } else if (token == "\\output_changes") {
1003                 lex >> output_changes;
1004         } else if (token == "\\change_bars") {
1005                 lex >> change_bars;
1006         } else if (token == "\\postpone_fragile_content") {
1007                 lex >> postpone_fragile_content;
1008         } else if (token == "\\branch") {
1009                 lex.eatLine();
1010                 docstring branch = lex.getDocString();
1011                 branchlist().add(branch);
1012                 while (true) {
1013                         lex.next();
1014                         string const tok = lex.getString();
1015                         if (tok == "\\end_branch")
1016                                 break;
1017                         Branch * branch_ptr = branchlist().find(branch);
1018                         if (tok == "\\selected") {
1019                                 lex.next();
1020                                 if (branch_ptr)
1021                                         branch_ptr->setSelected(lex.getInteger());
1022                         }
1023                         if (tok == "\\filename_suffix") {
1024                                 lex.next();
1025                                 if (branch_ptr)
1026                                         branch_ptr->setFileNameSuffix(lex.getInteger());
1027                         }
1028                         if (tok == "\\color") {
1029                                 lex.eatLine();
1030                                 vector<string> const colors = getVectorFromString(lex.getString(), " ");
1031                                 string const lmcolor = colors.front();
1032                                 string dmcolor;
1033                                 if (colors.size() > 1)
1034                                         dmcolor = colors.back();
1035                                 if (branch_ptr)
1036                                         branch_ptr->setColors(lmcolor, dmcolor);
1037                         }
1038                 }
1039         } else if (token == "\\index") {
1040                 lex.eatLine();
1041                 docstring index = lex.getDocString();
1042                 docstring shortcut;
1043                 indiceslist().add(index);
1044                 while (true) {
1045                         lex.next();
1046                         string const tok = lex.getString();
1047                         if (tok == "\\end_index")
1048                                 break;
1049                         Index * index_ptr = indiceslist().find(index);
1050                         if (tok == "\\shortcut") {
1051                                 lex.next();
1052                                 shortcut = lex.getDocString();
1053                                 if (index_ptr)
1054                                         index_ptr->setShortcut(shortcut);
1055                         }
1056                         if (tok == "\\color") {
1057                                 lex.eatLine();
1058                                 string color = lex.getString();
1059                                 if (index_ptr)
1060                                         index_ptr->setColor(color);
1061                                 // Update also the Color table:
1062                                 if (color == "none")
1063                                         color = lcolor.getX11HexName(Color_background);
1064                                 // FIXME UNICODE
1065                                 if (!shortcut.empty())
1066                                         lcolor.setColor(to_utf8(shortcut)+ "@" + filename.absFileName(), color);
1067                         }
1068                 }
1069         } else if (token == "\\spellchecker_ignore") {
1070                 lex.eatLine();
1071                 docstring wl = lex.getDocString();
1072                 docstring language;
1073                 docstring word = split(wl, language, ' ');
1074                 Language const * lang = languages.getLanguage(to_ascii(language));
1075                 if (lang)
1076                         spellignore().push_back(WordLangTuple(word, lang));
1077         } else if (token == "\\author") {
1078                 lex.eatLine();
1079                 istringstream ss(lex.getString());
1080                 Author a;
1081                 ss >> a;
1082                 addAuthor(a);
1083         } else if (token == "\\paperorientation") {
1084                 string orient;
1085                 lex >> orient;
1086                 orientation = paperorientationtranslator().find(orient);
1087         } else if (token == "\\backgroundcolor") {
1088                 lex.eatLine();
1089                 backgroundcolor = lyx::rgbFromHexName(lex.getString());
1090                 isbackgroundcolor = true;
1091         } else if (token == "\\fontcolor") {
1092                 lex.eatLine();
1093                 fontcolor = lyx::rgbFromHexName(lex.getString());
1094                 isfontcolor = true;
1095         } else if (token == "\\notefontcolor") {
1096                 lex.eatLine();
1097                 string color = lex.getString();
1098                 notefontcolor = lyx::rgbFromHexName(color);
1099                 lcolor.setColor("notefontcolor", color);
1100                 lcolor.setLaTeXName("notefontcolor", "note_fontcolor");
1101                 lcolor.setGUIName("notefontcolor", N_("greyedout inset text"));
1102                 // set a local name for the painter
1103                 lcolor.setColor("notefontcolor@" + filename.absFileName(), color);
1104                 isnotefontcolor = true;
1105         } else if (token == "\\boxbgcolor") {
1106                 lex.eatLine();
1107                 string color = lex.getString();
1108                 boxbgcolor = lyx::rgbFromHexName(color);
1109                 lcolor.setColor("boxbgcolor@" + filename.absFileName(), color);
1110                 isboxbgcolor = true;
1111         } else if (token == "\\paperwidth") {
1112                 lex >> paperwidth;
1113         } else if (token == "\\paperheight") {
1114                 lex >> paperheight;
1115         } else if (token == "\\leftmargin") {
1116                 lex >> leftmargin;
1117         } else if (token == "\\topmargin") {
1118                 lex >> topmargin;
1119         } else if (token == "\\rightmargin") {
1120                 lex >> rightmargin;
1121         } else if (token == "\\bottommargin") {
1122                 lex >> bottommargin;
1123         } else if (token == "\\headheight") {
1124                 lex >> headheight;
1125         } else if (token == "\\headsep") {
1126                 lex >> headsep;
1127         } else if (token == "\\footskip") {
1128                 lex >> footskip;
1129         } else if (token == "\\columnsep") {
1130                 lex >> columnsep;
1131         } else if (token == "\\paperfontsize") {
1132                 lex >> fontsize;
1133         } else if (token == "\\papercolumns") {
1134                 lex >> columns;
1135         } else if (token == "\\listings_params") {
1136                 string par;
1137                 lex >> par;
1138                 listings_params = InsetListingsParams(par).params();
1139         } else if (token == "\\papersides") {
1140                 int psides;
1141                 lex >> psides;
1142                 sides = sidestranslator().find(psides);
1143         } else if (token == "\\paperpagestyle") {
1144                 lex >> pagestyle;
1145         } else if (token == "\\tablestyle") {
1146                 lex >> tablestyle;
1147         } else if (token == "\\bullet") {
1148                 readBullets(lex);
1149         } else if (token == "\\bulletLaTeX") {
1150                 readBulletsLaTeX(lex);
1151         } else if (token == "\\secnumdepth") {
1152                 lex >> secnumdepth;
1153         } else if (token == "\\tocdepth") {
1154                 lex >> tocdepth;
1155         } else if (token == "\\spacing") {
1156                 string nspacing;
1157                 lex >> nspacing;
1158                 string tmp_val;
1159                 if (nspacing == "other") {
1160                         lex >> tmp_val;
1161                 }
1162                 spacing().set(spacetranslator().find(nspacing), tmp_val);
1163         } else if (token == "\\float_placement") {
1164                 lex >> float_placement;
1165         } else if (token == "\\float_alignment") {
1166                 lex >> float_alignment;
1167
1168         } else if (prefixIs(token, "\\pdf_") || token == "\\use_hyperref") {
1169                 string toktmp = pdfoptions().readToken(lex, token);
1170                 if (!toktmp.empty()) {
1171                         lyxerr << "PDFOptions::readToken(): Unknown token: " <<
1172                                 toktmp << endl;
1173                         return toktmp;
1174                 }
1175         } else if (token == "\\html_math_output") {
1176                 int temp;
1177                 lex >> temp;
1178                 html_math_output = static_cast<MathOutput>(temp);
1179         } else if (token == "\\html_be_strict") {
1180                 lex >> html_be_strict;
1181         } else if (token == "\\html_css_as_file") {
1182                 lex >> html_css_as_file;
1183         } else if (token == "\\html_math_img_scale") {
1184                 lex >> html_math_img_scale;
1185         } else if (token == "\\html_latex_start") {
1186                 lex.eatLine();
1187                 html_latex_start = lex.getString();
1188         } else if (token == "\\html_latex_end") {
1189                 lex.eatLine();
1190                 html_latex_end = lex.getString();
1191         } else if (token == "\\docbook_table_output") {
1192                 int temp;
1193                 lex >> temp;
1194                 docbook_table_output = static_cast<TableOutput>(temp);
1195         } else if (token == "\\docbook_mathml_prefix") {
1196                 int temp;
1197                 lex >> temp;
1198                 docbook_mathml_prefix = static_cast<MathMLNameSpacePrefix>(temp);
1199         } else if (token == "\\output_sync") {
1200                 lex >> output_sync;
1201         } else if (token == "\\output_sync_macro") {
1202                 lex >> output_sync_macro;
1203         } else if (token == "\\use_refstyle") {
1204                 lex >> use_refstyle;
1205         } else if (token == "\\use_minted") {
1206                 lex >> use_minted;
1207         } else if (token == "\\use_lineno") {
1208                 lex >> use_lineno;
1209         } else if (token == "\\lineno_options") {
1210                 lex.eatLine();
1211                 lineno_opts = trim(lex.getString());
1212         } else {
1213                 lyxerr << "BufferParams::readToken(): Unknown token: " <<
1214                         token << endl;
1215                 return token;
1216         }
1217
1218         return result;
1219 }
1220
1221
1222 namespace {
1223         // Quote argument if it contains spaces
1224         string quoteIfNeeded(string const & str) {
1225                 if (contains(str, ' '))
1226                         return "\"" + str + "\"";
1227                 return str;
1228         }
1229 } // namespace
1230
1231
1232 void BufferParams::writeFile(ostream & os, Buffer const * buf) const
1233 {
1234         // The top of the file is written by the buffer.
1235         // Prints out the buffer info into the .lyx file given by file
1236
1237         os << "\\save_transient_properties "
1238            << convert<string>(save_transient_properties) << '\n';
1239
1240         // the document directory (must end with a path separator)
1241         // realPath() is used to resolve symlinks, while addPath(..., "")
1242         // ensures a trailing path separator.
1243         string docsys;
1244         string filepath = addPath(buf->fileName().onlyPath().realPath(), "");
1245         string const sysdir = inSystemDir(FileName(filepath), docsys) ? docsys
1246                         : addPath(package().system_support().realPath(), "");
1247         string const relpath =
1248                 to_utf8(makeRelPath(from_utf8(filepath), from_utf8(sysdir)));
1249         if (!prefixIs(relpath, "../") && !FileName::isAbsolute(relpath))
1250                 filepath = addPath("/systemlyxdir", relpath);
1251         else if (!save_transient_properties || !lyxrc.save_origin)
1252                 filepath = "unavailable";
1253         os << "\\origin " << quoteIfNeeded(filepath) << '\n';
1254
1255         // the textclass
1256         os << "\\textclass "
1257            << quoteIfNeeded(buf->includedFilePath(addName(buf->layoutPos(),
1258                                                 baseClass()->name()), "layout"))
1259            << '\n';
1260
1261         // then the preamble
1262         if (!preamble.empty()) {
1263                 // remove '\n' from the end of preamble
1264                 docstring const tmppreamble = rtrim(preamble, "\n");
1265                 os << "\\begin_preamble\n"
1266                    << to_utf8(tmppreamble)
1267                    << "\n\\end_preamble\n";
1268         }
1269
1270         // the options
1271         if (!options.empty()) {
1272                 os << "\\options " << options << '\n';
1273         }
1274
1275         // use the class options defined in the layout?
1276         os << "\\use_default_options "
1277            << convert<string>(use_default_options) << "\n";
1278
1279         // the master document
1280         if (!master.empty()) {
1281                 os << "\\master " << master << '\n';
1282         }
1283
1284         // removed modules
1285         if (!removed_modules_.empty()) {
1286                 os << "\\begin_removed_modules" << '\n';
1287                 for (auto const & mod : removed_modules_)
1288                         os << mod << '\n';
1289                 os << "\\end_removed_modules" << '\n';
1290         }
1291
1292         // the modules
1293         if (!layout_modules_.empty()) {
1294                 os << "\\begin_modules" << '\n';
1295                 for (auto const & mod : layout_modules_)
1296                         os << mod << '\n';
1297                 os << "\\end_modules" << '\n';
1298         }
1299
1300         // includeonly
1301         if (!included_children_.empty()) {
1302                 os << "\\begin_includeonly" << '\n';
1303                 for (auto const & c : included_children_)
1304                         os << c << '\n';
1305                 os << "\\end_includeonly" << '\n';
1306         }
1307         string muc = "no";
1308         switch (maintain_unincluded_children) {
1309         case CM_Mostly:
1310                 muc = "mostly";
1311                 break;
1312         case CM_Strict:
1313                 muc = "strict";
1314                 break;
1315         case CM_None:
1316         default:
1317                 break;
1318         }
1319         os << "\\maintain_unincluded_children " << muc << '\n';
1320
1321         // local layout information
1322         docstring const local_layout = getLocalLayout(false);
1323         if (!local_layout.empty()) {
1324                 // remove '\n' from the end
1325                 docstring const tmplocal = rtrim(local_layout, "\n");
1326                 os << "\\begin_local_layout\n"
1327                    << to_utf8(tmplocal)
1328                    << "\n\\end_local_layout\n";
1329         }
1330         docstring const forced_local_layout = getLocalLayout(true);
1331         if (!forced_local_layout.empty()) {
1332                 // remove '\n' from the end
1333                 docstring const tmplocal = rtrim(forced_local_layout, "\n");
1334                 os << "\\begin_forced_local_layout\n"
1335                    << to_utf8(tmplocal)
1336                    << "\n\\end_forced_local_layout\n";
1337         }
1338
1339         // then the text parameters
1340         if (language != ignore_language)
1341                 os << "\\language " << language->lang() << '\n';
1342         os << "\\language_package " << lang_package
1343            << "\n\\inputencoding " << inputenc
1344            << "\n\\fontencoding " << fontenc
1345            << "\n\\font_roman \"" << fonts_roman[0]
1346            << "\" \"" << fonts_roman[1] << '"'
1347            << "\n\\font_sans \"" << fonts_sans[0]
1348            << "\" \"" << fonts_sans[1] << '"'
1349            << "\n\\font_typewriter \"" << fonts_typewriter[0]
1350            << "\" \"" << fonts_typewriter[1] << '"'
1351            << "\n\\font_math \"" << fonts_math[0]
1352            << "\" \"" << fonts_math[1] << '"'
1353            << "\n\\font_default_family " << fonts_default_family
1354            << "\n\\use_non_tex_fonts " << convert<string>(useNonTeXFonts)
1355            << "\n\\font_sc " << convert<string>(fonts_expert_sc)
1356            << "\n\\font_roman_osf " << convert<string>(fonts_roman_osf)
1357            << "\n\\font_sans_osf " << convert<string>(fonts_sans_osf)
1358            << "\n\\font_typewriter_osf " << convert<string>(fonts_typewriter_osf);
1359         if (!font_roman_opts.empty())
1360                 os << "\n\\font_roman_opts \"" << font_roman_opts << "\"";
1361         os << "\n\\font_sf_scale " << fonts_sans_scale[0]
1362            << ' ' << fonts_sans_scale[1];
1363         if (!font_sans_opts.empty())
1364                 os << "\n\\font_sans_opts \"" << font_sans_opts << "\"";
1365         os << "\n\\font_tt_scale " << fonts_typewriter_scale[0]
1366            << ' ' << fonts_typewriter_scale[1];
1367         if (!font_typewriter_opts.empty())
1368                 os << "\n\\font_typewriter_opts \"" << font_typewriter_opts << "\"";
1369         os << '\n';
1370         if (!fonts_cjk.empty())
1371                 os << "\\font_cjk " << fonts_cjk << '\n';
1372         os << "\\use_microtype " << convert<string>(use_microtype) << '\n';
1373         os << "\\use_dash_ligatures " << convert<string>(use_dash_ligatures) << '\n';
1374         os << "\\graphics " << graphics_driver << '\n';
1375         os << "\\default_output_format " << default_output_format << '\n';
1376         os << "\\output_sync " << output_sync << '\n';
1377         if (!output_sync_macro.empty())
1378                 os << "\\output_sync_macro \"" << output_sync_macro << "\"\n";
1379         os << "\\bibtex_command " << bibtex_command << '\n';
1380         os << "\\index_command " << index_command << '\n';
1381
1382         if (!float_placement.empty())
1383                 os << "\\float_placement " << float_placement << '\n';
1384         if (!float_alignment.empty())
1385                 os << "\\float_alignment " << float_alignment << '\n';
1386         os << "\\paperfontsize " << fontsize << '\n';
1387
1388         spacing().writeFile(os);
1389         pdfoptions().writeFile(os);
1390
1391         os << "\\papersize " << string_papersize[papersize]
1392            << "\n\\use_geometry " << convert<string>(use_geometry);
1393         map<string, string> const & packages = auto_packages();
1394         for (auto const & pack : packages)
1395                 os << "\n\\use_package " << pack.first << ' '
1396                    << use_package(pack.first);
1397
1398         os << "\n\\cite_engine ";
1399
1400         if (!cite_engine_.empty())
1401                 os << cite_engine_;
1402         else
1403                 os << "basic";
1404
1405         os << "\n\\cite_engine_type " << theCiteEnginesList.getTypeAsString(cite_engine_type_);
1406
1407         if (!biblio_style.empty())
1408                 os << "\n\\biblio_style " << biblio_style;
1409         if (!biblio_opts.empty())
1410                 os << "\n\\biblio_options " << biblio_opts;
1411         if (!biblatex_bibstyle.empty())
1412                 os << "\n\\biblatex_bibstyle " << biblatex_bibstyle;
1413         if (!biblatex_citestyle.empty())
1414                 os << "\n\\biblatex_citestyle " << biblatex_citestyle;
1415         if (!multibib.empty())
1416                 os << "\n\\multibib " << multibib;
1417
1418         os << "\n\\use_bibtopic " << convert<string>(use_bibtopic)
1419            << "\n\\use_indices " << convert<string>(use_indices)
1420            << "\n\\paperorientation " << string_orientation[orientation]
1421            << "\n\\suppress_date " << convert<string>(suppress_date)
1422            << "\n\\justification " << convert<string>(justification)
1423            << "\n\\use_refstyle " << use_refstyle
1424            << "\n\\use_minted " << use_minted
1425            << "\n\\use_lineno " << use_lineno
1426            << '\n';
1427
1428         if (!lineno_opts.empty())
1429                 os << "\\lineno_options " << lineno_opts << '\n';
1430
1431         if (isbackgroundcolor)
1432                 os << "\\backgroundcolor " << lyx::X11hexname(backgroundcolor) << '\n';
1433         if (isfontcolor)
1434                 os << "\\fontcolor " << lyx::X11hexname(fontcolor) << '\n';
1435         if (isnotefontcolor)
1436                 os << "\\notefontcolor " << lyx::X11hexname(notefontcolor) << '\n';
1437         if (isboxbgcolor)
1438                 os << "\\boxbgcolor " << lyx::X11hexname(boxbgcolor) << '\n';
1439
1440         for (auto const & br : branchlist()) {
1441                 os << "\\branch " << to_utf8(br.branch())
1442                    << "\n\\selected " << br.isSelected()
1443                    << "\n\\filename_suffix " << br.hasFileNameSuffix()
1444                    << "\n\\color " << br.lightModeColor() << " " << br.darkModeColor()
1445                    << "\n\\end_branch"
1446                    << "\n";
1447         }
1448
1449         for (auto const & id : indiceslist()) {
1450                 os << "\\index " << to_utf8(id.index())
1451                    << "\n\\shortcut " << to_utf8(id.shortcut())
1452                    << "\n\\color " << lyx::X11hexname(id.color())
1453                    << "\n\\end_index"
1454                    << "\n";
1455         }
1456
1457         for (auto const & si : spellignore()) {
1458                 os << "\\spellchecker_ignore " << si.lang()->lang()
1459                    << " " << to_utf8(si.word())
1460                    << "\n";
1461         }
1462
1463         if (!paperwidth.empty())
1464                 os << "\\paperwidth "
1465                    << VSpace(paperwidth).asLyXCommand() << '\n';
1466         if (!paperheight.empty())
1467                 os << "\\paperheight "
1468                    << VSpace(paperheight).asLyXCommand() << '\n';
1469         if (!leftmargin.empty())
1470                 os << "\\leftmargin "
1471                    << VSpace(leftmargin).asLyXCommand() << '\n';
1472         if (!topmargin.empty())
1473                 os << "\\topmargin "
1474                    << VSpace(topmargin).asLyXCommand() << '\n';
1475         if (!rightmargin.empty())
1476                 os << "\\rightmargin "
1477                    << VSpace(rightmargin).asLyXCommand() << '\n';
1478         if (!bottommargin.empty())
1479                 os << "\\bottommargin "
1480                    << VSpace(bottommargin).asLyXCommand() << '\n';
1481         if (!headheight.empty())
1482                 os << "\\headheight "
1483                    << VSpace(headheight).asLyXCommand() << '\n';
1484         if (!headsep.empty())
1485                 os << "\\headsep "
1486                    << VSpace(headsep).asLyXCommand() << '\n';
1487         if (!footskip.empty())
1488                 os << "\\footskip "
1489                    << VSpace(footskip).asLyXCommand() << '\n';
1490         if (!columnsep.empty())
1491                 os << "\\columnsep "
1492                          << VSpace(columnsep).asLyXCommand() << '\n';
1493         os << "\\secnumdepth " << secnumdepth
1494            << "\n\\tocdepth " << tocdepth
1495            << "\n\\paragraph_separation "
1496            << string_paragraph_separation[paragraph_separation];
1497         if (!paragraph_separation)
1498                 os << "\n\\paragraph_indentation "
1499                    << (getParIndent().empty() ? "default" : getParIndent().asString());
1500         else
1501                 os << "\n\\defskip " << getDefSkip().asLyXCommand();
1502         os << "\n\\is_math_indent " << is_math_indent;
1503         if (is_math_indent)
1504                 os << "\n\\math_indentation "
1505                    << (getMathIndent().empty() ? "default" : getMathIndent().asString());
1506         os << "\n\\math_numbering_side ";
1507         switch(math_numbering_side) {
1508         case LEFT:
1509                 os << "left";
1510                 break;
1511         case RIGHT:
1512                 os << "right";
1513                 break;
1514         case DEFAULT:
1515                 os << "default";
1516         }
1517         os << "\n\\quotes_style "
1518            << string_quotes_style[static_cast<int>(quotes_style)]
1519            << "\n\\dynamic_quotes " << dynamic_quotes
1520            << "\n\\papercolumns " << columns
1521            << "\n\\papersides " << sides
1522            << "\n\\paperpagestyle " << pagestyle
1523            << "\n\\tablestyle " << tablestyle << '\n';
1524         if (!listings_params.empty())
1525                 os << "\\listings_params \"" <<
1526                         InsetListingsParams(listings_params).encodedString() << "\"\n";
1527         for (int i = 0; i < 4; ++i) {
1528                 if (user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
1529                         if (user_defined_bullet(i).getFont() != -1) {
1530                                 os << "\\bullet " << i << " "
1531                                    << user_defined_bullet(i).getFont() << " "
1532                                    << user_defined_bullet(i).getCharacter() << " "
1533                                    << user_defined_bullet(i).getSize() << "\n";
1534                         }
1535                         else {
1536                                 // FIXME UNICODE
1537                                 os << "\\bulletLaTeX " << i << " \""
1538                                    << lyx::to_ascii(user_defined_bullet(i).getText())
1539                                    << "\"\n";
1540                         }
1541                 }
1542         }
1543
1544         os << "\\tracking_changes "
1545            << (save_transient_properties ? convert<string>(track_changes) : "false")
1546            << '\n';
1547
1548         os << "\\output_changes "
1549            << (save_transient_properties ? convert<string>(output_changes) : "false")
1550            << '\n';
1551
1552         os << "\\change_bars "
1553            << (save_transient_properties ? convert<string>(change_bars) : "false")
1554            << '\n';
1555
1556         os << "\\postpone_fragile_content " << convert<string>(postpone_fragile_content) << '\n';
1557
1558         os << "\\html_math_output " << html_math_output << '\n'
1559            << "\\html_css_as_file " << html_css_as_file << '\n'
1560            << "\\html_be_strict " << convert<string>(html_be_strict) << '\n';
1561
1562         os << "\\docbook_table_output " << docbook_table_output << '\n';
1563         os << "\\docbook_mathml_prefix " << docbook_mathml_prefix << '\n';
1564
1565         if (html_math_img_scale != 1.0)
1566                 os << "\\html_math_img_scale " << convert<string>(html_math_img_scale) << '\n';
1567         if (!html_latex_start.empty())
1568                 os << "\\html_latex_start " << html_latex_start << '\n';
1569         if (!html_latex_end.empty())
1570                  os << "\\html_latex_end " << html_latex_end << '\n';
1571
1572         os << pimpl_->authorlist;
1573 }
1574
1575
1576 void BufferParams::validate(LaTeXFeatures & features) const
1577 {
1578         features.require(documentClass().required());
1579
1580         if (columns > 1 && language->rightToLeft())
1581                 features.require("rtloutputdblcol");
1582
1583         if (output_changes) {
1584                 bool xcolorulem = LaTeXFeatures::isAvailable("ulem") &&
1585                                   LaTeXFeatures::isAvailable("xcolor");
1586
1587                 switch (features.runparams().flavor) {
1588                 case Flavor::LaTeX:
1589                 case Flavor::DviLuaTeX:
1590                         if (xcolorulem) {
1591                                 features.require("ct-xcolor-ulem");
1592                                 features.require("ulem");
1593                                 features.require("xcolor");
1594                         } else {
1595                                 features.require("ct-none");
1596                         }
1597                         break;
1598                 case Flavor::LuaTeX:
1599                 case Flavor::PdfLaTeX:
1600                 case Flavor::XeTeX:
1601                         if (xcolorulem) {
1602                                 features.require("ct-xcolor-ulem");
1603                                 features.require("ulem");
1604                                 features.require("xcolor");
1605                                 // improves color handling in PDF output
1606                                 features.require("pdfcolmk");
1607                         } else {
1608                                 features.require("ct-none");
1609                         }
1610                         break;
1611                 default:
1612                         break;
1613                 }
1614                 if (change_bars)
1615                         features.require("changebar");
1616         }
1617
1618         // Floats with 'Here definitely' as default setting.
1619         if (float_placement.find('H') != string::npos)
1620                 features.require("float");
1621
1622         for (auto const & pm : use_packages) {
1623                 if (pm.first == "amsmath") {
1624                         // AMS Style is at document level
1625                         if (pm.second == package_on ||
1626                             features.isProvided("amsmath"))
1627                                 features.require(pm.first);
1628                 } else if (pm.second == package_on)
1629                         features.require(pm.first);
1630         }
1631
1632         // Document-level line spacing
1633         if (spacing().getSpace() != Spacing::Single && !spacing().isDefault())
1634                 features.require("setspace");
1635
1636         // the bullet shapes are buffer level not paragraph level
1637         // so they are tested here
1638         for (int i = 0; i < 4; ++i) {
1639                 if (user_defined_bullet(i) == ITEMIZE_DEFAULTS[i])
1640                         continue;
1641                 int const font = user_defined_bullet(i).getFont();
1642                 if (font == 0) {
1643                         int const c = user_defined_bullet(i).getCharacter();
1644                         if (c == 16
1645                             || c == 17
1646                             || c == 25
1647                             || c == 26
1648                             || c == 31) {
1649                                 features.require("latexsym");
1650                         }
1651                 } else if (font == 1) {
1652                         features.require("amssymb");
1653                 } else if (font >= 2 && font <= 5) {
1654                         features.require("pifont");
1655                 }
1656         }
1657
1658         if (pdfoptions().use_hyperref) {
1659                 features.require("hyperref");
1660                 // due to interferences with babel and hyperref, the color package has to
1661                 // be loaded after hyperref when hyperref is used with the colorlinks
1662                 // option, see http://www.lyx.org/trac/ticket/5291
1663                 if (pdfoptions().colorlinks)
1664                         features.require("color");
1665         }
1666         if (!listings_params.empty()) {
1667                 // do not test validity because listings_params is
1668                 // supposed to be valid
1669                 string par =
1670                         InsetListingsParams(listings_params).separatedParams(true);
1671                 // we can't support all packages, but we should load the color package
1672                 if (par.find("\\color", 0) != string::npos)
1673                         features.require("color");
1674         }
1675
1676         // some languages are only available via polyglossia
1677         if (features.hasPolyglossiaExclusiveLanguages())
1678                 features.require("polyglossia");
1679
1680         if (useNonTeXFonts && fontsMath() != "auto")
1681                 features.require("unicode-math");
1682
1683         if (use_microtype)
1684                 features.require("microtype");
1685
1686         if (!language->required().empty())
1687                 features.require(language->required());
1688 }
1689
1690
1691 bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
1692                               FileName const & filepath) const
1693 {
1694         // http://www.tug.org/texmf-dist/doc/latex/base/fixltx2e.pdf
1695         // !! To use the Fix-cm package, load it before \documentclass, and use the command
1696         // \RequirePackage to do so, rather than the normal \usepackage
1697         // Do not try to load any other package before the document class, unless you
1698         // have a thorough understanding of the LATEX internals and know exactly what you
1699         // are doing!
1700         if (features.mustProvide("fix-cm"))
1701                 os << "\\RequirePackage{fix-cm}\n";
1702         // Likewise for fixltx2e. If other packages conflict with this policy,
1703         // treat it as a package bug (and report it!)
1704         // See http://www.latex-project.org/cgi-bin/ltxbugs2html?pr=latex/4407
1705         if (features.mustProvide("fixltx2e"))
1706                 os << "\\RequirePackage{fixltx2e}\n";
1707
1708         os << "\\documentclass";
1709
1710         DocumentClass const & tclass = documentClass();
1711
1712         ostringstream clsoptions; // the document class options.
1713
1714         if (tokenPos(tclass.opt_fontsize(),
1715                      '|', fontsize) >= 0) {
1716                 // only write if existing in list (and not default)
1717                 clsoptions << subst(tclass.fontsizeformat(), "$$s", fontsize) << ",";
1718         }
1719
1720         // paper sizes not supported by the class itself need the
1721         // geometry package
1722         vector<string> classpsizes = getVectorFromString(tclass.opt_pagesize(), "|");
1723         bool class_supported_papersize = papersize == PAPER_DEFAULT
1724                 || find(classpsizes.begin(), classpsizes.end(), string_papersize[papersize]) != classpsizes.end();
1725
1726         if ((!use_geometry || features.isProvided("geometry-light"))
1727             && class_supported_papersize && papersize != PAPER_DEFAULT)
1728                 clsoptions << subst(tclass.pagesizeformat(), "$$s", string_papersize[papersize]) << ",";
1729
1730         // if needed
1731         if (sides != tclass.sides()) {
1732                 switch (sides) {
1733                 case OneSide:
1734                         clsoptions << "oneside,";
1735                         break;
1736                 case TwoSides:
1737                         clsoptions << "twoside,";
1738                         break;
1739                 }
1740         }
1741
1742         // if needed
1743         if (columns != tclass.columns()) {
1744                 if (columns == 2)
1745                         clsoptions << "twocolumn,";
1746                 else
1747                         clsoptions << "onecolumn,";
1748         }
1749
1750         if (!use_geometry
1751             && orientation == ORIENTATION_LANDSCAPE)
1752                 clsoptions << "landscape,";
1753
1754         if (is_math_indent)
1755                 clsoptions << "fleqn,";
1756
1757         switch(math_numbering_side) {
1758         case LEFT:
1759                 clsoptions << "leqno,";
1760                 break;
1761         case RIGHT:
1762                 clsoptions << "reqno,";
1763                 features.require("amsmath");
1764                 break;
1765         case DEFAULT:
1766                 break;
1767         }
1768
1769         // language should be a parameter to \documentclass
1770         if (language->babel() == "hebrew"
1771             && default_language->babel() != "hebrew")
1772                 // This seems necessary
1773                 features.useLanguage(default_language);
1774
1775         ostringstream language_options;
1776         bool const use_babel = features.useBabel() && !features.isProvided("babel");
1777         bool const use_polyglossia = features.usePolyglossia();
1778         bool const global = lyxrc.language_global_options;
1779         if (features.useBabel() || (use_polyglossia && global)) {
1780                 language_options << features.getBabelLanguages();
1781                 if (!language->babel().empty()) {
1782                         if (!language_options.str().empty())
1783                                 language_options << ',';
1784                         language_options << language->babel();
1785                 }
1786                 if (global && !language_options.str().empty())
1787                         clsoptions << language_options.str() << ',';
1788         }
1789
1790         // the predefined options from the layout
1791         if (use_default_options && !tclass.options().empty())
1792                 clsoptions << tclass.options() << ',';
1793
1794         // the user-defined options
1795         if (!options.empty()) {
1796                 clsoptions << options << ',';
1797         }
1798         
1799         docstring const strOptions = from_utf8(clsoptions.str());
1800         if (!strOptions.empty()) {
1801                 // Check if class options contain uncodable glyphs
1802                 docstring uncodable_glyphs;
1803                 docstring options_encodable;
1804                 Encoding const * const enc = features.runparams().encoding;
1805                 if (enc) {
1806                         for (char_type c : strOptions) {
1807                                 if (!enc->encodable(c)) {
1808                                         docstring const glyph(1, c);
1809                                         LYXERR0("Uncodable character '"
1810                                                 << glyph
1811                                                 << "' in class options!");
1812                                         uncodable_glyphs += glyph;
1813                                         if (features.runparams().dryrun) {
1814                                                 options_encodable += "<" + _("LyX Warning: ")
1815                                                    + _("uncodable character") + " '";
1816                                                 options_encodable += c;
1817                                                 options_encodable += "'>";
1818                                         }
1819                                 } else
1820                                         options_encodable += c;
1821                         }
1822                 } else
1823                         options_encodable = strOptions;
1824         
1825                 // On BUFFER_VIEW|UPDATE, warn user if we found uncodable glyphs
1826                 if (!features.runparams().dryrun && !uncodable_glyphs.empty()) {
1827                         frontend::Alert::warning(
1828                                 _("Uncodable character in class options"),
1829                                 support::bformat(
1830                                   _("The class options of your document contain glyphs "
1831                                     "that are unknown in the current document encoding "
1832                                     "(namely %1$s).\nThese glyphs are omitted "
1833                                     " from the output, which may result in "
1834                                     "incomplete output."
1835                                     "\n\nPlease select an appropriate "
1836                                     "document encoding\n"
1837                                     "(such as utf8) or change the "
1838                                     "class options accordingly."),
1839                                   uncodable_glyphs));
1840                 }
1841                 options_encodable = rtrim(options_encodable, ",");
1842                 os << '[' << options_encodable << ']';
1843         }
1844
1845         os << '{' << from_ascii(tclass.latexname()) << "}\n";
1846         // end of \documentclass defs
1847
1848         // The package options (via \PassOptionsToPackage)
1849         os << from_ascii(features.getPackageOptions());
1850
1851         // if we use fontspec or newtxmath, we have to load the AMS packages here
1852         string const ams = features.loadAMSPackages();
1853         bool const ot1 = (main_font_encoding() == "default" || main_font_encoding() == "OT1");
1854         bool const use_newtxmath =
1855                 theLaTeXFonts().getLaTeXFont(from_ascii(fontsMath())).getUsedPackage(
1856                         ot1, false, false) == "newtxmath";
1857         if ((useNonTeXFonts || use_newtxmath) && !ams.empty())
1858                 os << from_ascii(ams);
1859
1860         if (useNonTeXFonts) {
1861                 // Babel (as of 2017/11/03) loads fontspec itself
1862                 if (!features.isProvided("fontspec")
1863                     && !(features.useBabel() && features.isAvailable("babel-2017/11/03")))
1864                         os << "\\usepackage{fontspec}\n";
1865                 if (features.mustProvide("unicode-math")
1866                     && features.isAvailable("unicode-math"))
1867                         os << "\\usepackage{unicode-math}\n";
1868         }
1869
1870         // load CJK support package before font selection
1871         // (see autotests/export/latex/CJK/micro-sign_utf8-cjk-libertine.lyx)
1872         if (!useNonTeXFonts && encoding().package() != Encoding::none && inputenc != "utf8x"
1873                 && (encoding().package() == Encoding::CJK || features.mustProvide("CJK"))) {
1874                 if (inputenc == "utf8-cjk" || inputenc == "utf8")
1875                         os << "\\usepackage{CJKutf8}\n";
1876                 else
1877                         os << "\\usepackage[encapsulated]{CJK}\n";
1878         }
1879
1880         // font selection must be done before loading fontenc.sty
1881         // but after babel with non-TeX fonts
1882         string const fonts = loadFonts(features);
1883         if (!fonts.empty() && (!features.useBabel() || !useNonTeXFonts))
1884                 os << from_utf8(fonts);
1885
1886         if (fonts_default_family != "default")
1887                 os << "\\renewcommand{\\familydefault}{\\"
1888                    << from_ascii(fonts_default_family) << "}\n";
1889
1890         // set font encoding
1891         // non-TeX fonts use font encoding TU (set by fontspec)
1892         if (!useNonTeXFonts && !features.isProvided("fontenc")
1893             && main_font_encoding() != "default") {
1894                 // get main font encodings
1895                 vector<string> fontencs = font_encodings();
1896                 // get font encodings of secondary languages
1897                 // FIXME: some languages (hebrew, ...) assume a standard font encoding as last
1898                 // option (for text in other languages).
1899                 features.getFontEncodings(fontencs);
1900                 if (!fontencs.empty()) {
1901                         os << "\\usepackage["
1902                            << from_ascii(getStringFromVector(fontencs))
1903                            << "]{fontenc}\n";
1904                 }
1905         }
1906
1907         // Load textcomp and pmboxdraw before (lua)inputenc (#11454)
1908         if (features.mustProvide("textcomp"))
1909                 os << "\\usepackage{textcomp}\n";
1910         if (features.mustProvide("pmboxdraw"))
1911                 os << "\\usepackage{pmboxdraw}\n";
1912         
1913         // handle inputenc etc.
1914         // (In documents containing text in Thai language, 
1915         // we must load inputenc after babel, see lib/languages).
1916         if (!contains(features.getBabelPostsettings(), from_ascii("thai.ldf")))
1917                 writeEncodingPreamble(os, features);
1918
1919         // includeonly
1920         if (!features.runparams().includeall && !included_children_.empty()) {
1921                 os << "\\includeonly{";
1922                 bool first = true;
1923                 for (auto incfile : included_children_) {
1924                         FileName inc = makeAbsPath(incfile, filepath.absFileName());
1925                         string mangled = DocFileName(changeExtension(inc.absFileName(), ".tex")).
1926                                         mangledFileName();
1927                         if (!features.runparams().nice)
1928                                 incfile = mangled;
1929                         // \includeonly doesn't want an extension
1930                         incfile = changeExtension(incfile, string());
1931                         incfile = support::latex_path(incfile);
1932                         if (!incfile.empty()) {
1933                                 if (!first)
1934                                         os << ",";
1935                                 os << from_utf8(incfile);
1936                         }
1937                         first = false;
1938                 }
1939                 os << "}\n";
1940         }
1941
1942         if (!features.isProvided("geometry")
1943             && (use_geometry || !class_supported_papersize)) {
1944                 odocstringstream ods;
1945                 if (!getGraphicsDriver("geometry").empty())
1946                         ods << getGraphicsDriver("geometry");
1947                 if (orientation == ORIENTATION_LANDSCAPE)
1948                         ods << ",landscape";
1949                 switch (papersize) {
1950                 case PAPER_CUSTOM:
1951                         if (!paperwidth.empty())
1952                                 ods << ",paperwidth="
1953                                    << from_ascii(paperwidth);
1954                         if (!paperheight.empty())
1955                                 ods << ",paperheight="
1956                                    << from_ascii(paperheight);
1957                         break;
1958                 case PAPER_USLETTER:
1959                 case PAPER_USLEGAL:
1960                 case PAPER_USEXECUTIVE:
1961                 case PAPER_A0:
1962                 case PAPER_A1:
1963                 case PAPER_A2:
1964                 case PAPER_A3:
1965                 case PAPER_A4:
1966                 case PAPER_A5:
1967                 case PAPER_A6:
1968                 case PAPER_B0:
1969                 case PAPER_B1:
1970                 case PAPER_B2:
1971                 case PAPER_B3:
1972                 case PAPER_B4:
1973                 case PAPER_B5:
1974                 case PAPER_B6:
1975                 case PAPER_C0:
1976                 case PAPER_C1:
1977                 case PAPER_C2:
1978                 case PAPER_C3:
1979                 case PAPER_C4:
1980                 case PAPER_C5:
1981                 case PAPER_C6:
1982                 case PAPER_JISB0:
1983                 case PAPER_JISB1:
1984                 case PAPER_JISB2:
1985                 case PAPER_JISB3:
1986                 case PAPER_JISB4:
1987                 case PAPER_JISB5:
1988                 case PAPER_JISB6:
1989                         ods << "," << from_ascii(string_papersize_geometry[papersize]);
1990                         break;
1991                 case PAPER_DEFAULT:
1992                         break;
1993                 }
1994                 docstring g_options = trim(ods.str(), ",");
1995                 os << "\\usepackage";
1996                 // geometry-light means that the class works with geometry, but overwrites
1997                 // the package options and paper sizes (memoir does this).
1998                 // In this case, all options need to go to \geometry
1999                 // and the standard paper sizes need to go to the class options.
2000                 if (!g_options.empty() && !features.isProvided("geometry-light")) {
2001                         os << '[' << g_options << ']';
2002                         g_options.clear();
2003                 }
2004                 os << "{geometry}\n";
2005                 if (use_geometry || features.isProvided("geometry-light")) {
2006                         os << "\\geometry{verbose";
2007                         if (!g_options.empty())
2008                                 // Output general options here with "geometry light".
2009                                 os << "," << g_options;
2010                         // output this only if use_geometry is true
2011                         if (use_geometry) {
2012                                 if (!topmargin.empty())
2013                                         os << ",tmargin=" << from_ascii(Length(topmargin).asLatexString());
2014                                 if (!bottommargin.empty())
2015                                         os << ",bmargin=" << from_ascii(Length(bottommargin).asLatexString());
2016                                 if (!leftmargin.empty())
2017                                         os << ",lmargin=" << from_ascii(Length(leftmargin).asLatexString());
2018                                 if (!rightmargin.empty())
2019                                         os << ",rmargin=" << from_ascii(Length(rightmargin).asLatexString());
2020                                 if (!headheight.empty())
2021                                         os << ",headheight=" << from_ascii(Length(headheight).asLatexString());
2022                                 if (!headsep.empty())
2023                                         os << ",headsep=" << from_ascii(Length(headsep).asLatexString());
2024                                 if (!footskip.empty())
2025                                         os << ",footskip=" << from_ascii(Length(footskip).asLatexString());
2026                                 if (!columnsep.empty())
2027                                         os << ",columnsep=" << from_ascii(Length(columnsep).asLatexString());
2028                         }
2029                 os << "}\n";
2030                 }
2031         } else if (orientation == ORIENTATION_LANDSCAPE
2032                    || papersize != PAPER_DEFAULT) {
2033                 features.require("papersize");
2034         }
2035
2036         if (tokenPos(tclass.opt_pagestyle(), '|', pagestyle) >= 0) {
2037                 if (pagestyle == "fancy")
2038                         os << "\\usepackage{fancyhdr}\n";
2039                 os << "\\pagestyle{" << from_ascii(pagestyle) << "}\n";
2040         }
2041
2042         // only output when the background color is not default
2043         if (isbackgroundcolor) {
2044                 // only require color here, the background color will be defined
2045                 // in LaTeXFeatures.cpp to avoid interferences with the LaTeX
2046                 // package pdfpages
2047                 features.require("color");
2048                 features.require("pagecolor");
2049         }
2050
2051         // only output when the font color is not default
2052         if (isfontcolor) {
2053                 // only require color here, the font color will be defined
2054                 // in LaTeXFeatures.cpp to avoid interferences with the LaTeX
2055                 // package pdfpages
2056                 features.require("color");
2057                 features.require("fontcolor");
2058         }
2059
2060         // Only if class has a ToC hierarchy
2061         if (tclass.hasTocLevels()) {
2062                 if (secnumdepth != tclass.secnumdepth()) {
2063                         os << "\\setcounter{secnumdepth}{"
2064                            << secnumdepth
2065                            << "}\n";
2066                 }
2067                 if (tocdepth != tclass.tocdepth()) {
2068                         os << "\\setcounter{tocdepth}{"
2069                            << tocdepth
2070                            << "}\n";
2071                 }
2072         }
2073
2074         if (paragraph_separation) {
2075                 // when skip separation
2076                 string psopt;
2077                 switch (getDefSkip().kind()) {
2078                 case VSpace::SMALLSKIP:
2079                         psopt = "\\smallskipamount";
2080                         break;
2081                 case VSpace::MEDSKIP:
2082                         psopt = "\\medskipamount";
2083                         break;
2084                 case VSpace::BIGSKIP:
2085                         psopt = "\\bigskipamount";
2086                         break;
2087                 case VSpace::HALFLINE:
2088                         // default (no option)
2089                         break;
2090                 case VSpace::FULLLINE:
2091                         psopt = "\\baselineskip";
2092                         break;
2093                 case VSpace::LENGTH:
2094                         psopt = getDefSkip().length().asLatexString();
2095                         break;
2096                 default:
2097                         break;
2098                 }
2099                 if (!features.isProvided("parskip")) {
2100                         if (!psopt.empty())
2101                                 psopt = "[skip=" + psopt + "]";
2102                         os << "\\usepackage" + psopt + "{parskip}\n";
2103                 } else {
2104                         os << "\\setlength{\\parskip}{" + psopt + "}\n";
2105                 }
2106         } else {
2107                 // when separation by indentation
2108                 // only output something when a width is given
2109                 if (!getParIndent().empty()) {
2110                         os << "\\setlength{\\parindent}{"
2111                            << from_utf8(getParIndent().asLatexString())
2112                            << "}\n";
2113                 }
2114         }
2115
2116         if (is_math_indent) {
2117                 // when formula indentation
2118                 // only output something when it is not the default
2119                 if (!getMathIndent().empty()) {
2120                         os << "\\setlength{\\mathindent}{"
2121                            << from_utf8(getMathIndent().asString())
2122                            << "}\n";
2123                 }
2124         }
2125
2126         // Now insert the LyX specific LaTeX commands...
2127         features.resolveAlternatives();
2128         features.expandMultiples();
2129
2130         if (output_sync) {
2131                 if (!output_sync_macro.empty())
2132                         os << from_utf8(output_sync_macro) +"\n";
2133                 else if (features.runparams().flavor == Flavor::LaTeX)
2134                         os << "\\usepackage[active]{srcltx}\n";
2135                 else if (features.runparams().flavor == Flavor::PdfLaTeX)
2136                         os << "\\synctex=-1\n";
2137         }
2138
2139         // due to interferences with babel and hyperref, the color package has to
2140         // be loaded (when it is not already loaded) before babel when hyperref
2141         // is used with the colorlinks option, see
2142         // http://www.lyx.org/trac/ticket/5291
2143         // we decided therefore to load color always before babel, see
2144         // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg144349.html
2145         os << from_ascii(features.getColorOptions());
2146
2147         // If we use hyperref, jurabib, japanese or varioref,
2148         // we have to call babel before
2149         if (use_babel
2150             && (features.isRequired("jurabib")
2151                 || features.isRequired("hyperref")
2152                 || features.isRequired("varioref")
2153                 || features.isRequired("japanese"))) {
2154                         os << features.getBabelPresettings();
2155                         // FIXME UNICODE
2156                         os << from_utf8(babelCall(language_options.str(),
2157                                                                           !lyxrc.language_global_options)) + '\n';
2158                         os << features.getBabelPostsettings();
2159         }
2160
2161         // The optional packages;
2162         os << from_ascii(features.getPackages());
2163
2164         // Additional Indices
2165         if (features.isRequired("splitidx")) {
2166                 for (auto const & idx : indiceslist()) {
2167                         os << "\\newindex{";
2168                         os << escape(idx.shortcut());
2169                         os << "}\n";
2170                 }
2171         }
2172
2173         // Line spacing
2174         os << from_utf8(spacing().writePreamble(features.isProvided("SetSpace")));
2175
2176         // PDF support.
2177         // * Hyperref manual: "Make sure it comes last of your loaded
2178         //   packages, to give it a fighting chance of not being over-written,
2179         //   since its job is to redefine many LaTeX commands."
2180         // * Email from Heiko Oberdiek: "It is usually better to load babel
2181         //   before hyperref. Then hyperref has a chance to detect babel.
2182         // * Has to be loaded before the "LyX specific LaTeX commands" to
2183         //   avoid errors with algorithm floats.
2184         // use hyperref explicitly if it is required
2185         if (features.isRequired("hyperref")) {
2186                 OutputParams tmp_params = features.runparams();
2187                 pdfoptions().writeLaTeX(tmp_params, os,
2188                                         features.isProvided("hyperref"));
2189                 // correctly break URLs with hyperref and dvi/ps output
2190                 if (features.runparams().hyperref_driver == "dvips"
2191                     && features.isAvailable("breakurl"))
2192                         os << "\\usepackage{breakurl}\n";
2193         } else if (features.isRequired("nameref"))
2194                 // hyperref loads this automatically
2195                 os << "\\usepackage{nameref}\n";
2196
2197         if (use_lineno){
2198                 os << "\\usepackage";
2199                 if (!lineno_opts.empty())
2200                         os << "[" << lineno_opts << "]";
2201                 os << "{lineno}\n";
2202                 os << "\\linenumbers\n";
2203         }
2204
2205         // bibtopic needs to be loaded after hyperref.
2206         // the dot provides the aux file naming which LyX can detect.
2207         if (features.mustProvide("bibtopic"))
2208                 os << "\\usepackage[dot]{bibtopic}\n";
2209
2210         // Will be surrounded by \makeatletter and \makeatother when not empty
2211         otexstringstream atlyxpreamble;
2212
2213         // Some macros LyX will need
2214         {
2215                 TexString tmppreamble = features.getMacros();
2216                 if (!tmppreamble.str.empty())
2217                         atlyxpreamble << "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2218                                          "LyX specific LaTeX commands.\n"
2219                                       << move(tmppreamble)
2220                                       << '\n';
2221         }
2222         // the text class specific preamble
2223         {
2224                 docstring tmppreamble = features.getTClassPreamble();
2225                 if (!tmppreamble.empty())
2226                         atlyxpreamble << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2227                                          "Textclass specific LaTeX commands.\n"
2228                                       << tmppreamble
2229                                       << '\n';
2230         }
2231         // suppress date if selected
2232         // use \@ifundefined because we cannot be sure that every document class
2233         // has a \date command
2234         if (suppress_date)
2235                 atlyxpreamble << "\\@ifundefined{date}{}{\\date{}}\n";
2236
2237         /* the user-defined preamble */
2238         if (!containsOnly(preamble, " \n\t")) {
2239                 // FIXME UNICODE
2240                 atlyxpreamble << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2241                                  "User specified LaTeX commands.\n";
2242
2243                 // Check if the user preamble contains uncodable glyphs
2244                 odocstringstream user_preamble;
2245                 docstring uncodable_glyphs;
2246                 Encoding const * const enc = features.runparams().encoding;
2247                 if (enc) {
2248                         for (char_type c : preamble) {
2249                                 if (!enc->encodable(c)) {
2250                                         docstring const glyph(1, c);
2251                                         LYXERR0("Uncodable character '"
2252                                                 << glyph
2253                                                 << "' in user preamble!");
2254                                         uncodable_glyphs += glyph;
2255                                         if (features.runparams().dryrun) {
2256                                                 user_preamble << "<" << _("LyX Warning: ")
2257                                                    << _("uncodable character") << " '";
2258                                                 user_preamble.put(c);
2259                                                 user_preamble << "'>";
2260                                         }
2261                                 } else
2262                                         user_preamble.put(c);
2263                         }
2264                 } else
2265                         user_preamble << preamble;
2266
2267                 // On BUFFER_VIEW|UPDATE, warn user if we found uncodable glyphs
2268                 if (!features.runparams().dryrun && !uncodable_glyphs.empty()) {
2269                         frontend::Alert::warning(
2270                                 _("Uncodable character in user preamble"),
2271                                 support::bformat(
2272                                   _("The user preamble of your document contains glyphs "
2273                                     "that are unknown in the current document encoding "
2274                                     "(namely %1$s).\nThese glyphs are omitted "
2275                                     " from the output, which may result in "
2276                                     "incomplete output."
2277                                     "\n\nPlease select an appropriate "
2278                                     "document encoding\n"
2279                                     "(such as utf8) or change the "
2280                                     "preamble code accordingly."),
2281                                   uncodable_glyphs));
2282                 }
2283                 atlyxpreamble << user_preamble.str() << '\n';
2284         }
2285
2286         // footmisc must be loaded after setspace
2287         // Load it here to avoid clashes with footmisc loaded in the user
2288         // preamble. For that reason we also pass the options via
2289         // \PassOptionsToPackage in getPreamble() and not here.
2290         if (features.mustProvide("footmisc"))
2291                 atlyxpreamble << "\\usepackage{footmisc}\n";
2292
2293         // subfig loads internally the LaTeX package "caption". As
2294         // caption is a very popular package, users will load it in
2295         // the preamble. Therefore we must load subfig behind the
2296         // user-defined preamble and check if the caption package was
2297         // loaded or not. For the case that caption is loaded before
2298         // subfig, there is the subfig option "caption=false". This
2299         // option also works when a koma-script class is used and
2300         // koma's own caption commands are used instead of caption. We
2301         // use \PassOptionsToPackage here because the user could have
2302         // already loaded subfig in the preamble.
2303         if (features.mustProvide("subfig"))
2304                 atlyxpreamble << "\\ifdefined\\showcaptionsetup\n"
2305                                  " % Caption package is used. Advise subfig not to load it again.\n"
2306                                  " \\PassOptionsToPackage{caption=false}{subfig}\n"
2307                                  "\\fi\n"
2308                                  "\\usepackage{subfig}\n";
2309
2310         // Itemize bullet settings need to be last in case the user
2311         // defines their own bullets that use a package included
2312         // in the user-defined preamble -- ARRae
2313         // Actually it has to be done much later than that
2314         // since some packages like frenchb make modifications
2315         // at \begin{document} time -- JMarc
2316         docstring bullets_def;
2317         for (int i = 0; i < 4; ++i) {
2318                 if (user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
2319                         if (bullets_def.empty())
2320                                 bullets_def += "\\AtBeginDocument{\n";
2321                         bullets_def += "  \\def\\labelitemi";
2322                         switch (i) {
2323                                 // `i' is one less than the item to modify
2324                         case 0:
2325                                 break;
2326                         case 1:
2327                                 bullets_def += 'i';
2328                                 break;
2329                         case 2:
2330                                 bullets_def += "ii";
2331                                 break;
2332                         case 3:
2333                                 bullets_def += 'v';
2334                                 break;
2335                         }
2336                         bullets_def += '{' +
2337                                 user_defined_bullet(i).getText()
2338                                 + "}\n";
2339                 }
2340         }
2341
2342         if (!bullets_def.empty())
2343                 atlyxpreamble << bullets_def << "}\n\n";
2344
2345         if (!atlyxpreamble.empty())
2346                 os << "\n\\makeatletter\n"
2347                    << atlyxpreamble.release()
2348                    << "\\makeatother\n\n";
2349
2350         // We try to load babel late, in case it interferes with other packages.
2351         // Jurabib, hyperref, varioref, bicaption, menukeys and listings (bug 8995)
2352         // have to be called after babel, though.
2353         if (use_babel && !features.isRequired("jurabib")
2354             && !features.isRequired("hyperref")
2355             && !features.isRequired("varioref")
2356             && !features.isRequired("japanese")) {
2357                 os << features.getBabelPresettings();
2358                 // FIXME UNICODE
2359                 os << from_utf8(babelCall(language_options.str(),
2360                                           !lyxrc.language_global_options)) + '\n';
2361                 os << features.getBabelPostsettings();
2362         }
2363         // In documents containing text in Thai language, 
2364         // we must load inputenc after babel (see lib/languages).
2365         if (contains(features.getBabelPostsettings(), from_ascii("thai.ldf")))
2366                 writeEncodingPreamble(os, features);
2367
2368         // font selection must be done after babel with non-TeX fonts
2369         if (!fonts.empty() && features.useBabel() && useNonTeXFonts)
2370                 os << from_utf8(fonts);
2371
2372         if (features.isRequired("bicaption"))
2373                 os << "\\usepackage{bicaption}\n";
2374         if (!listings_params.empty()
2375             || features.mustProvide("listings")
2376             || features.mustProvide("minted")) {
2377                 if (use_minted)
2378                         os << "\\usepackage{minted}\n";
2379                 else
2380                         os << "\\usepackage{listings}\n";
2381         }
2382         string lst_params = listings_params;
2383         // If minted, do not output the language option (bug 11203)
2384         if (use_minted && contains(lst_params, "language=")) {
2385                 vector<string> opts =
2386                         getVectorFromString(lst_params, ",", false);
2387                 for (size_t i = 0; i < opts.size(); ++i) {
2388                         if (prefixIs(opts[i], "language="))
2389                                 opts.erase(opts.begin() + i--);
2390                 }
2391                 lst_params = getStringFromVector(opts, ",");
2392         }
2393         if (!lst_params.empty()) {
2394                 if (use_minted)
2395                         os << "\\setminted{";
2396                 else
2397                         os << "\\lstset{";
2398                 // do not test validity because listings_params is
2399                 // supposed to be valid
2400                 string par =
2401                         InsetListingsParams(lst_params).separatedParams(true);
2402                 os << from_utf8(par);
2403                 os << "}\n";
2404         }
2405
2406         // xunicode only needs to be loaded if tipa is used
2407         // (the rest is obsoleted by the new TU encoding).
2408         // It needs to be loaded at least after amsmath, amssymb,
2409         // esint and the other packages that provide special glyphs
2410         if (features.mustProvide("tipa") && useNonTeXFonts
2411             && !features.isProvided("xunicode")) {
2412                 // The `xunicode` package officially only supports XeTeX,
2413                 //  but also works with LuaTeX. We work around its XeTeX test.
2414                 if (features.runparams().flavor != Flavor::XeTeX) {
2415                         os << "% Pretend to xunicode that we are XeTeX\n"
2416                            << "\\def\\XeTeXpicfile{}\n";
2417                 }
2418                 os << "\\usepackage{xunicode}\n";
2419         }
2420
2421         // covington must be loaded after beamerarticle
2422         if (features.isRequired("covington"))
2423             os << "\\usepackage{covington}\n";
2424
2425         // Polyglossia must be loaded last ...
2426         if (use_polyglossia) {
2427                 // call the package
2428                 os << "\\usepackage{polyglossia}\n";
2429                 // set the main language
2430                 os << "\\setdefaultlanguage";
2431                 if (!language->polyglossiaOpts().empty())
2432                         os << "[" << from_ascii(language->polyglossiaOpts()) << "]";
2433                 os << "{" << from_ascii(language->polyglossia()) << "}\n";
2434                 // now setup the other languages
2435                 set<string> const polylangs =
2436                         features.getPolyglossiaLanguages();
2437                 for (auto const & pl : polylangs) {
2438                         // We do not output the options here; they are output in
2439                         // the language switch commands. This is safer if multiple
2440                         // varieties are used.
2441                         if (pl == language->polyglossia())
2442                                 continue;
2443                         os << "\\setotherlanguage";
2444                         os << "{" << from_ascii(pl) << "}\n";
2445                 }
2446         }
2447
2448         // ... but before biblatex (see #7065)
2449         if ((features.mustProvide("biblatex")
2450              || features.isRequired("biblatex-chicago"))
2451             && !features.isProvided("biblatex-chicago")
2452             && !features.isProvided("biblatex-natbib")
2453             && !features.isProvided("natbib-internal")
2454             && !features.isProvided("natbib")
2455             && !features.isProvided("jurabib")) {
2456                 // The biblatex-chicago package has a differing interface
2457                 // it uses a wrapper package and loads styles via fixed options
2458                 bool const chicago = features.isRequired("biblatex-chicago");
2459                 string delim = "";
2460                 string opts;
2461                 os << "\\usepackage";
2462                 if (!biblatex_bibstyle.empty()
2463                     && (biblatex_bibstyle == biblatex_citestyle)
2464                     && !chicago) {
2465                         opts = "style=" + biblatex_bibstyle;
2466                         delim = ",";
2467                 } else if (!chicago) {
2468                         if (!biblatex_bibstyle.empty()) {
2469                                 opts = "bibstyle=" + biblatex_bibstyle;
2470                                 delim = ",";
2471                         }
2472                         if (!biblatex_citestyle.empty()) {
2473                                 opts += delim + "citestyle=" + biblatex_citestyle;
2474                                 delim = ",";
2475                         }
2476                 }
2477                 if (!multibib.empty() && multibib != "child") {
2478                         opts += delim + "refsection=" + multibib;
2479                         delim = ",";
2480                 }
2481                 if (bibtexCommand() == "bibtex8"
2482                     || prefixIs(bibtexCommand(), "bibtex8 ")) {
2483                         opts += delim + "backend=bibtex8";
2484                         delim = ",";
2485                 } else if (bibtexCommand() == "bibtex"
2486                            || prefixIs(bibtexCommand(), "bibtex ")) {
2487                         opts += delim + "backend=bibtex";
2488                         delim = ",";
2489                 }
2490                 if (!bib_encoding.empty() && encodings.fromLyXName(bib_encoding)) {
2491                         opts += delim + "bibencoding="
2492                                 + encodings.fromLyXName(bib_encoding)->latexName();
2493                         delim = ",";
2494                 }
2495                 if (!biblio_opts.empty())
2496                         opts += delim + biblio_opts;
2497                 if (!opts.empty())
2498                         os << "[" << opts << "]";
2499                 if (chicago)
2500                         os << "{biblatex-chicago}\n";
2501                 else
2502                         os << "{biblatex}\n";
2503         }
2504
2505
2506         // Load custom language package here
2507         if (features.langPackage() == LaTeXFeatures::LANG_PACK_CUSTOM) {
2508                 if (lang_package == "default")
2509                         os << from_utf8(lyxrc.language_custom_package);
2510                 else
2511                         os << from_utf8(lang_package);
2512                 os << '\n';
2513         }
2514
2515         // Since menukeys uses catoptions, which does some heavy changes on key-value options,
2516         // it is recommended to load menukeys as the last package (even after hyperref)
2517         if (features.isRequired("menukeys"))
2518                 os << "\\usepackage{menukeys}\n";
2519
2520         docstring const i18npreamble =
2521                 features.getTClassI18nPreamble(use_babel, use_polyglossia,
2522                                                use_minted);
2523         if (!i18npreamble.empty())
2524                 os << i18npreamble + '\n';
2525
2526         return use_babel;
2527 }
2528
2529
2530 void BufferParams::useClassDefaults()
2531 {
2532         DocumentClass const & tclass = documentClass();
2533
2534         sides = tclass.sides();
2535         columns = tclass.columns();
2536         pagestyle = tclass.pagestyle();
2537         tablestyle = tclass.tablestyle();
2538         use_default_options = true;
2539         // Only if class has a ToC hierarchy
2540         if (tclass.hasTocLevels()) {
2541                 secnumdepth = tclass.secnumdepth();
2542                 tocdepth = tclass.tocdepth();
2543         }
2544 }
2545
2546
2547 bool BufferParams::hasClassDefaults() const
2548 {
2549         DocumentClass const & tclass = documentClass();
2550
2551         return sides == tclass.sides()
2552                 && columns == tclass.columns()
2553                 && pagestyle == tclass.pagestyle()
2554                 && tablestyle == tclass.tablestyle()
2555                 && use_default_options
2556                 && secnumdepth == tclass.secnumdepth()
2557                 && tocdepth == tclass.tocdepth();
2558 }
2559
2560
2561 DocumentClass const & BufferParams::documentClass() const
2562 {
2563         return *doc_class_;
2564 }
2565
2566
2567 DocumentClassConstPtr BufferParams::documentClassPtr() const
2568 {
2569         return doc_class_;
2570 }
2571
2572
2573 void BufferParams::setDocumentClass(DocumentClassConstPtr tc)
2574 {
2575         // evil, but this function is evil
2576         doc_class_ = const_pointer_cast<DocumentClass>(tc);
2577         invalidateConverterCache();
2578 }
2579
2580
2581 bool BufferParams::setBaseClass(string const & classname, string const & path)
2582 {
2583         LYXERR(Debug::TCLASS, "setBaseClass: " << classname);
2584         LayoutFileList & bcl = LayoutFileList::get();
2585         if (!bcl.haveClass(classname)) {
2586                 docstring s =
2587                         bformat(_("The layout file:\n"
2588                                 "%1$s\n"
2589                                 "could not be found. A default textclass with default\n"
2590                                 "layouts will be used. LyX will not be able to produce\n"
2591                                 "correct output."),
2592                         from_utf8(classname));
2593                 frontend::Alert::error(_("Document class not found"), s);
2594                 bcl.addEmptyClass(classname);
2595         }
2596
2597         bool const success = bcl[classname].load(path);
2598         if (!success) {
2599                 docstring s =
2600                         bformat(_("Due to some error in it, the layout file:\n"
2601                                 "%1$s\n"
2602                                 "could not be loaded. A default textclass with default\n"
2603                                 "layouts will be used. LyX will not be able to produce\n"
2604                                 "correct output."),
2605                         from_utf8(classname));
2606                 frontend::Alert::error(_("Could not load class"), s);
2607                 bcl.addEmptyClass(classname);
2608         }
2609
2610         pimpl_->baseClass_ = classname;
2611         layout_modules_.adaptToBaseClass(baseClass(), removed_modules_);
2612         return true;
2613 }
2614
2615
2616 LayoutFile const * BufferParams::baseClass() const
2617 {
2618         if (LayoutFileList::get().haveClass(pimpl_->baseClass_))
2619                 return &(LayoutFileList::get()[pimpl_->baseClass_]);
2620
2621         return nullptr;
2622 }
2623
2624
2625 LayoutFileIndex const & BufferParams::baseClassID() const
2626 {
2627         return pimpl_->baseClass_;
2628 }
2629
2630
2631 void BufferParams::makeDocumentClass(bool clone, bool internal)
2632 {
2633         if (!baseClass())
2634                 return;
2635
2636         invalidateConverterCache();
2637         LayoutModuleList mods;
2638         for (auto const & mod : layout_modules_)
2639                 mods.push_back(mod);
2640
2641         doc_class_ = getDocumentClass(*baseClass(), mods, cite_engine_, clone, internal);
2642
2643         TextClass::ReturnValues success = TextClass::OK;
2644         if (!forced_local_layout_.empty())
2645                 success = doc_class_->read(to_utf8(forced_local_layout_),
2646                                            TextClass::MODULE);
2647         if (!local_layout_.empty() &&
2648             (success == TextClass::OK || success == TextClass::OK_OLDFORMAT))
2649                 success = doc_class_->read(to_utf8(local_layout_), TextClass::MODULE);
2650         if (success != TextClass::OK && success != TextClass::OK_OLDFORMAT) {
2651                 docstring const msg = _("Error reading internal layout information");
2652                 frontend::Alert::warning(_("Read Error"), msg);
2653         }
2654 }
2655
2656
2657 bool BufferParams::layoutModuleCanBeAdded(string const & modName) const
2658 {
2659         return layout_modules_.moduleCanBeAdded(modName, baseClass());
2660 }
2661
2662
2663 docstring BufferParams::getLocalLayout(bool forced) const
2664 {
2665         if (forced)
2666                 return from_utf8(doc_class_->forcedLayouts());
2667         else
2668                 return local_layout_;
2669 }
2670
2671
2672 void BufferParams::setLocalLayout(docstring const & layout, bool forced)
2673 {
2674         if (forced)
2675                 forced_local_layout_ = layout;
2676         else
2677                 local_layout_ = layout;
2678 }
2679
2680
2681 bool BufferParams::addLayoutModule(string const & modName)
2682 {
2683         for (auto const & mod : layout_modules_)
2684                 if (mod == modName)
2685                         return false;
2686         layout_modules_.push_back(modName);
2687         return true;
2688 }
2689
2690
2691 string BufferParams::bufferFormat() const
2692 {
2693         return documentClass().outputFormat();
2694 }
2695
2696
2697 bool BufferParams::isExportable(string const & format, bool need_viewable) const
2698 {
2699         FormatList const & formats = exportableFormats(need_viewable);
2700         for (auto const & fmt : formats) {
2701                 if (fmt->name() == format)
2702                         return true;
2703         }
2704         return false;
2705 }
2706
2707
2708 FormatList const & BufferParams::exportableFormats(bool only_viewable) const
2709 {
2710         FormatList & cached = only_viewable ?
2711                         pimpl_->viewableFormatList : pimpl_->exportableFormatList;
2712         bool & valid = only_viewable ?
2713                         pimpl_->isViewCacheValid : pimpl_->isExportCacheValid;
2714         if (valid)
2715                 return cached;
2716
2717         vector<string> const backs = backends();
2718         set<string> excludes;
2719         if (useNonTeXFonts) {
2720                 excludes.insert("latex");
2721                 excludes.insert("pdflatex");
2722         } else if (inputenc != "ascii" && inputenc != "utf8-plain") {
2723                   // XeTeX with TeX fonts requires input encoding ascii (#10600).
2724                   excludes.insert("xetex");
2725         }
2726
2727         FormatList result =
2728                 theConverters().getReachable(backs[0], only_viewable, true, excludes);
2729         vector<string>::const_iterator it = backs.begin() + 1;
2730         for (; it != backs.end(); ++it) {
2731                 FormatList r = theConverters().getReachable(*it, only_viewable,
2732                                                                                                         false, excludes);
2733                 result.insert(result.end(), r.begin(), r.end());
2734         }
2735         sort(result.begin(), result.end(), Format::formatSorter);
2736         cached = result;
2737         valid = true;
2738         return cached;
2739 }
2740
2741
2742 vector<string> BufferParams::backends() const
2743 {
2744         vector<string> v;
2745         string const buffmt = bufferFormat();
2746
2747         // FIXME: Don't hardcode format names here, but use a flag
2748         if (buffmt == "latex") {
2749                 if (encoding().package() == Encoding::japanese)
2750                         v.push_back("platex");
2751                 else {
2752                         if (!useNonTeXFonts) {
2753                                 v.push_back("pdflatex");
2754                                 v.push_back("latex");
2755                         }
2756                         if (useNonTeXFonts 
2757                                 || inputenc == "ascii" || inputenc == "utf8-plain")
2758                                 v.push_back("xetex");
2759                         v.push_back("luatex");
2760                         v.push_back("dviluatex");
2761                 }
2762         } else {
2763                 string rbuffmt = buffmt;
2764                 // If we use an OutputFormat in Japanese docs,
2765                 // we need special format in order to get the path
2766                 // via pLaTeX (#8823)
2767                 if (documentClass().hasOutputFormat()
2768                     && encoding().package() == Encoding::japanese)
2769                         rbuffmt += "-ja";
2770                 v.push_back(rbuffmt);
2771         }
2772
2773         v.push_back("xhtml");
2774         v.push_back("docbook5");
2775         v.push_back("text");
2776         v.push_back("lyx");
2777         return v;
2778 }
2779
2780
2781 Flavor BufferParams::getOutputFlavor(string const & format) const
2782 {
2783         string const dformat = (format.empty() || format == "default") ?
2784                 getDefaultOutputFormat() : format;
2785         DefaultFlavorCache::const_iterator it =
2786                 default_flavors_.find(dformat);
2787
2788         if (it != default_flavors_.end())
2789                 return it->second;
2790
2791         Flavor result = Flavor::LaTeX;
2792
2793         // FIXME It'd be better not to hardcode this, but to do
2794         //       something with formats.
2795         if (dformat == "xhtml")
2796                 result = Flavor::Html;
2797         else if (dformat == "docbook5")
2798                 result = Flavor::DocBook5;
2799         else if (dformat == "text")
2800                 result = Flavor::Text;
2801         else if (dformat == "lyx")
2802                 result = Flavor::LyX;
2803         else if (dformat == "pdflatex")
2804                 result = Flavor::PdfLaTeX;
2805         else if (dformat == "xetex")
2806                 result = Flavor::XeTeX;
2807         else if (dformat == "luatex")
2808                 result = Flavor::LuaTeX;
2809         else if (dformat == "dviluatex")
2810                 result = Flavor::DviLuaTeX;
2811         else {
2812                 // Try to determine flavor of default output format
2813                 vector<string> backs = backends();
2814                 if (find(backs.begin(), backs.end(), dformat) == backs.end()) {
2815                         // Get shortest path to format
2816                         Graph::EdgePath path;
2817                         for (auto const & bvar : backs) {
2818                                 Graph::EdgePath p = theConverters().getPath(bvar, dformat);
2819                                 if (!p.empty() && (path.empty() || p.size() < path.size())) {
2820                                         path = p;
2821                                 }
2822                         }
2823                         if (!path.empty())
2824                                 result = theConverters().getFlavor(path);
2825                 }
2826         }
2827         // cache this flavor
2828         default_flavors_[dformat] = result;
2829         return result;
2830 }
2831
2832
2833 string BufferParams::getDefaultOutputFormat() const
2834 {
2835         if (!default_output_format.empty()
2836             && default_output_format != "default")
2837                 return default_output_format;
2838         if (encoding().package() == Encoding::japanese)
2839                 return lyxrc.default_platex_view_format;
2840         if (useNonTeXFonts)
2841                 return lyxrc.default_otf_view_format;
2842         return lyxrc.default_view_format;
2843 }
2844
2845 Font const BufferParams::getFont() const
2846 {
2847         FontInfo f = documentClass().defaultfont();
2848         if (fonts_default_family == "rmdefault")
2849                 f.setFamily(ROMAN_FAMILY);
2850         else if (fonts_default_family == "sfdefault")
2851                 f.setFamily(SANS_FAMILY);
2852         else if (fonts_default_family == "ttdefault")
2853                 f.setFamily(TYPEWRITER_FAMILY);
2854         return Font(f, language);
2855 }
2856
2857
2858 QuoteStyle BufferParams::getQuoteStyle(string const & qs) const
2859 {
2860         return quotesstyletranslator().find(qs);
2861 }
2862
2863
2864 bool BufferParams::isLatex() const
2865 {
2866         return documentClass().outputType() == LATEX;
2867 }
2868
2869
2870 bool BufferParams::isLiterate() const
2871 {
2872         return documentClass().outputType() == LITERATE;
2873 }
2874
2875
2876 void BufferParams::readPreamble(Lexer & lex)
2877 {
2878         if (lex.getString() != "\\begin_preamble")
2879                 lyxerr << "Error (BufferParams::readPreamble):"
2880                         "consistency check failed." << endl;
2881
2882         preamble = lex.getLongString(from_ascii("\\end_preamble"));
2883 }
2884
2885
2886 void BufferParams::readLocalLayout(Lexer & lex, bool forced)
2887 {
2888         string const expected = forced ? "\\begin_forced_local_layout" :
2889                                          "\\begin_local_layout";
2890         if (lex.getString() != expected)
2891                 lyxerr << "Error (BufferParams::readLocalLayout):"
2892                         "consistency check failed." << endl;
2893
2894         if (forced)
2895                 forced_local_layout_ =
2896                         lex.getLongString(from_ascii("\\end_forced_local_layout"));
2897         else
2898                 local_layout_ = lex.getLongString(from_ascii("\\end_local_layout"));
2899 }
2900
2901
2902 bool BufferParams::setLanguage(string const & lang)
2903 {
2904         Language const *new_language = languages.getLanguage(lang);
2905         if (!new_language) {
2906                 // Language lang was not found
2907                 return false;
2908         }
2909         language = new_language;
2910         return true;
2911 }
2912
2913
2914 void BufferParams::readLanguage(Lexer & lex)
2915 {
2916         if (!lex.next()) return;
2917
2918         string const tmptok = lex.getString();
2919
2920         // check if tmptok is part of tex_babel in tex-defs.h
2921         if (!setLanguage(tmptok)) {
2922                 // Language tmptok was not found
2923                 language = default_language;
2924                 lyxerr << "Warning: Setting language `"
2925                        << tmptok << "' to `" << language->lang()
2926                        << "'." << endl;
2927         }
2928 }
2929
2930
2931 void BufferParams::readGraphicsDriver(Lexer & lex)
2932 {
2933         if (!lex.next())
2934                 return;
2935
2936         string const tmptok = lex.getString();
2937         // check if tmptok is part of tex_graphics in tex_defs.h
2938         int n = 0;
2939         while (true) {
2940                 string const test = tex_graphics[n++];
2941
2942                 if (test == tmptok) {
2943                         graphics_driver = tmptok;
2944                         break;
2945                 }
2946                 if (test.empty()) {
2947                         lex.printError(
2948                                 "Warning: graphics driver `$$Token' not recognized!\n"
2949                                 "         Setting graphics driver to `default'.\n");
2950                         graphics_driver = "default";
2951                         break;
2952                 }
2953         }
2954 }
2955
2956
2957 void BufferParams::readBullets(Lexer & lex)
2958 {
2959         if (!lex.next())
2960                 return;
2961
2962         int const index = lex.getInteger();
2963         lex.next();
2964         int temp_int = lex.getInteger();
2965         user_defined_bullet(index).setFont(temp_int);
2966         temp_bullet(index).setFont(temp_int);
2967         lex >> temp_int;
2968         user_defined_bullet(index).setCharacter(temp_int);
2969         temp_bullet(index).setCharacter(temp_int);
2970         lex >> temp_int;
2971         user_defined_bullet(index).setSize(temp_int);
2972         temp_bullet(index).setSize(temp_int);
2973 }
2974
2975
2976 void BufferParams::readBulletsLaTeX(Lexer & lex)
2977 {
2978         // The bullet class should be able to read this.
2979         if (!lex.next())
2980                 return;
2981         int const index = lex.getInteger();
2982         lex.next(true);
2983         docstring const temp_str = lex.getDocString();
2984
2985         user_defined_bullet(index).setText(temp_str);
2986         temp_bullet(index).setText(temp_str);
2987 }
2988
2989
2990 void BufferParams::readModules(Lexer & lex)
2991 {
2992         if (!lex.eatLine()) {
2993                 lyxerr << "Error (BufferParams::readModules):"
2994                                 "Unexpected end of input." << endl;
2995                 return;
2996         }
2997         while (true) {
2998                 string mod = lex.getString();
2999                 if (mod == "\\end_modules")
3000                         break;
3001                 addLayoutModule(mod);
3002                 lex.eatLine();
3003         }
3004 }
3005
3006
3007 void BufferParams::readRemovedModules(Lexer & lex)
3008 {
3009         if (!lex.eatLine()) {
3010                 lyxerr << "Error (BufferParams::readRemovedModules):"
3011                                 "Unexpected end of input." << endl;
3012                 return;
3013         }
3014         while (true) {
3015                 string mod = lex.getString();
3016                 if (mod == "\\end_removed_modules")
3017                         break;
3018                 removed_modules_.push_back(mod);
3019                 lex.eatLine();
3020         }
3021         // now we want to remove any removed modules that were previously
3022         // added. normally, that will be because default modules were added in
3023         // setBaseClass(), which gets called when \textclass is read at the
3024         // start of the read.
3025         for (auto const & rm : removed_modules_) {
3026                 LayoutModuleList::iterator const mit = layout_modules_.begin();
3027                 LayoutModuleList::iterator const men = layout_modules_.end();
3028                 LayoutModuleList::iterator found = find(mit, men, rm);
3029                 if (found == men)
3030                         continue;
3031                 layout_modules_.erase(found);
3032         }
3033 }
3034
3035
3036 void BufferParams::readIncludeonly(Lexer & lex)
3037 {
3038         if (!lex.eatLine()) {
3039                 lyxerr << "Error (BufferParams::readIncludeonly):"
3040                                 "Unexpected end of input." << endl;
3041                 return;
3042         }
3043         while (true) {
3044                 string child = lex.getString();
3045                 if (child == "\\end_includeonly")
3046                         break;
3047                 included_children_.push_back(child);
3048                 lex.eatLine();
3049         }
3050 }
3051
3052
3053 string BufferParams::paperSizeName(PapersizePurpose purpose, string const & psize) const
3054 {
3055         PAPER_SIZE ppsize = psize.empty() ? papersize : papersizetranslator().find(psize);
3056         switch (ppsize) {
3057         case PAPER_DEFAULT:
3058                 if (documentClass().pagesize() == "default")
3059                         // could be anything, so don't guess
3060                         return string();
3061                 return paperSizeName(purpose, documentClass().pagesize());
3062         case PAPER_CUSTOM: {
3063                 if (purpose == XDVI && !paperwidth.empty() &&
3064                     !paperheight.empty()) {
3065                         // heightxwidth<unit>
3066                         string first = paperwidth;
3067                         string second = paperheight;
3068                         if (orientation == ORIENTATION_LANDSCAPE)
3069                                 first.swap(second);
3070                         // cut off unit.
3071                         return first.erase(first.length() - 2)
3072                                 + "x" + second;
3073                 }
3074                 return string();
3075         }
3076         case PAPER_A0:
3077                 // dvips and dvipdfm do not know this
3078                 if (purpose == DVIPS || purpose == DVIPDFM)
3079                         return string();
3080                 return "a0";
3081         case PAPER_A1:
3082                 if (purpose == DVIPS || purpose == DVIPDFM)
3083                         return string();
3084                 return "a1";
3085         case PAPER_A2:
3086                 if (purpose == DVIPS || purpose == DVIPDFM)
3087                         return string();
3088                 return "a2";
3089         case PAPER_A3:
3090                 return "a3";
3091         case PAPER_A4:
3092                 return "a4";
3093         case PAPER_A5:
3094                 return "a5";
3095         case PAPER_A6:
3096                 if (purpose == DVIPS || purpose == DVIPDFM)
3097                         return string();
3098                 return "a6";
3099         case PAPER_B0:
3100                 if (purpose == DVIPS || purpose == DVIPDFM)
3101                         return string();
3102                 return "b0";
3103         case PAPER_B1:
3104                 if (purpose == DVIPS || purpose == DVIPDFM)
3105                         return string();
3106                 return "b1";
3107         case PAPER_B2:
3108                 if (purpose == DVIPS || purpose == DVIPDFM)
3109                         return string();
3110                 return "b2";
3111         case PAPER_B3:
3112                 if (purpose == DVIPS || purpose == DVIPDFM)
3113                         return string();
3114                 return "b3";
3115         case PAPER_B4:
3116                 // dvipdfm does not know this
3117                 if (purpose == DVIPDFM)
3118                         return string();
3119                 return "b4";
3120         case PAPER_B5:
3121                 if (purpose == DVIPDFM)
3122                         return string();
3123                 return "b5";
3124         case PAPER_B6:
3125                 if (purpose == DVIPS || purpose == DVIPDFM)
3126                         return string();
3127                 return "b6";
3128         case PAPER_C0:
3129                 if (purpose == DVIPS || purpose == DVIPDFM)
3130                         return string();
3131                 return "c0";
3132         case PAPER_C1:
3133                 if (purpose == DVIPS || purpose == DVIPDFM)
3134                         return string();
3135                 return "c1";
3136         case PAPER_C2:
3137                 if (purpose == DVIPS || purpose == DVIPDFM)
3138                         return string();
3139                 return "c2";
3140         case PAPER_C3:
3141                 if (purpose == DVIPS || purpose == DVIPDFM)
3142                         return string();
3143                 return "c3";
3144         case PAPER_C4:
3145                 if (purpose == DVIPS || purpose == DVIPDFM)
3146                         return string();
3147                 return "c4";
3148         case PAPER_C5:
3149                 if (purpose == DVIPS || purpose == DVIPDFM)
3150                         return string();
3151                 return "c5";
3152         case PAPER_C6:
3153                 if (purpose == DVIPS || purpose == DVIPDFM)
3154                         return string();
3155                 return "c6";
3156         case PAPER_JISB0:
3157                 if (purpose == DVIPS || purpose == DVIPDFM)
3158                         return string();
3159                 return "jisb0";
3160         case PAPER_JISB1:
3161                 if (purpose == DVIPS || purpose == DVIPDFM)
3162                         return string();
3163                 return "jisb1";
3164         case PAPER_JISB2:
3165                 if (purpose == DVIPS || purpose == DVIPDFM)
3166                         return string();
3167                 return "jisb2";
3168         case PAPER_JISB3:
3169                 if (purpose == DVIPS || purpose == DVIPDFM)
3170                         return string();
3171                 return "jisb3";
3172         case PAPER_JISB4:
3173                 if (purpose == DVIPS || purpose == DVIPDFM)
3174                         return string();
3175                 return "jisb4";
3176         case PAPER_JISB5:
3177                 if (purpose == DVIPS || purpose == DVIPDFM)
3178                         return string();
3179                 return "jisb5";
3180         case PAPER_JISB6:
3181                 if (purpose == DVIPS || purpose == DVIPDFM)
3182                         return string();
3183                 return "jisb6";
3184         case PAPER_USEXECUTIVE:
3185                 // dvipdfm does not know this
3186                 if (purpose == DVIPDFM)
3187                         return string();
3188                 return "foolscap";
3189         case PAPER_USLEGAL:
3190                 return "legal";
3191         case PAPER_USLETTER:
3192         default:
3193                 if (purpose == XDVI)
3194                         return "us";
3195                 return "letter";
3196         }
3197 }
3198
3199
3200 string const BufferParams::dvips_options() const
3201 {
3202         string result;
3203
3204         // If the class loads the geometry package, we do not know which
3205         // paper size is used, since we do not set it (bug 7013).
3206         // Therefore we must not specify any argument here.
3207         // dvips gets the correct paper size via DVI specials in this case
3208         // (if the class uses the geometry package correctly).
3209         if (documentClass().provides("geometry"))
3210                 return result;
3211
3212         if (use_geometry
3213             && papersize == PAPER_CUSTOM
3214             && !lyxrc.print_paper_dimension_flag.empty()
3215             && !paperwidth.empty()
3216             && !paperheight.empty()) {
3217                 // using a custom papersize
3218                 result = lyxrc.print_paper_dimension_flag;
3219                 result += ' ' + paperwidth;
3220                 result += ',' + paperheight;
3221         } else {
3222                 string const paper_option = paperSizeName(DVIPS);
3223                 if (!paper_option.empty() && (paper_option != "letter" ||
3224                     orientation != ORIENTATION_LANDSCAPE)) {
3225                         // dvips won't accept -t letter -t landscape.
3226                         // In all other cases, include the paper size
3227                         // explicitly.
3228                         result = lyxrc.print_paper_flag;
3229                         result += ' ' + paper_option;
3230                 }
3231         }
3232         if (orientation == ORIENTATION_LANDSCAPE &&
3233             papersize != PAPER_CUSTOM)
3234                 result += ' ' + lyxrc.print_landscape_flag;
3235         return result;
3236 }
3237
3238
3239 string const BufferParams::main_font_encoding() const
3240 {
3241         if (font_encodings().empty()) {
3242                 if (ascii_lowercase(language->fontenc(*this)) == "none")
3243                         return "none";
3244                 return "default";
3245         }
3246         return font_encodings().back();
3247 }
3248
3249
3250 vector<string> const BufferParams::font_encodings() const
3251 {
3252         string doc_fontenc = (fontenc == "auto") ? string() : fontenc;
3253
3254         vector<string> fontencs;
3255
3256         // "default" means "no explicit font encoding"
3257         if (doc_fontenc != "default") {
3258                 if (!doc_fontenc.empty())
3259                         // If we have a custom setting, we use only that!
3260                         return getVectorFromString(doc_fontenc);
3261                 if (!language->fontenc(*this).empty()
3262                     && ascii_lowercase(language->fontenc(*this)) != "none") {
3263                         vector<string> fencs = getVectorFromString(language->fontenc(*this));
3264                         for (auto & fe : fencs) {
3265                                 if (find(fontencs.begin(), fontencs.end(), fe) == fontencs.end())
3266                                         fontencs.push_back(fe);
3267                         }
3268                 }
3269         }
3270
3271         return fontencs;
3272 }
3273
3274
3275 string BufferParams::babelCall(string const & lang_opts, bool const langoptions) const
3276 {
3277         // suppress the babel call if there is no BabelName defined
3278         // for the document language in the lib/languages file and if no
3279         // other languages are used (lang_opts is then empty)
3280         if (lang_opts.empty())
3281                 return string();
3282         // The prefs may require the languages to
3283         // be submitted to babel itself (not the class).
3284         if (langoptions)
3285                 return "\\usepackage[" + lang_opts + "]{babel}";
3286         return "\\usepackage{babel}";
3287 }
3288
3289
3290 docstring BufferParams::getGraphicsDriver(string const & package) const
3291 {
3292         docstring result;
3293
3294         if (package == "geometry") {
3295                 if (graphics_driver == "dvips"
3296                     || graphics_driver == "dvipdfm"
3297                     || graphics_driver == "pdftex"
3298                     || graphics_driver == "vtex")
3299                         result = from_ascii(graphics_driver);
3300                 else if (graphics_driver == "dvipdfmx")
3301                         result = from_ascii("dvipdfm");
3302         }
3303
3304         return result;
3305 }
3306
3307
3308 void BufferParams::writeEncodingPreamble(otexstream & os,
3309                                          LaTeXFeatures & features) const
3310 {
3311         // With no-TeX fonts we use utf8-plain without encoding package.
3312         if (useNonTeXFonts)
3313                 return;
3314
3315         if (inputenc == "auto-legacy") {
3316                 string const doc_encoding =
3317                         language->encoding()->latexName();
3318                 Encoding::Package const package =
3319                         language->encoding()->package();
3320
3321                 // Create list of inputenc options:
3322                 set<string> encoding_set;
3323                 // luainputenc fails with more than one encoding
3324                 if (features.runparams().flavor != Flavor::LuaTeX
3325                         && features.runparams().flavor != Flavor::DviLuaTeX)
3326                         // list all input encodings used in the document
3327                         encoding_set = features.getEncodingSet(doc_encoding);
3328
3329                 // The "japanese" babel-language requires  the pLaTeX engine
3330                 // which conflicts with "inputenc".
3331                 // See http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129680.html
3332                 if ((!encoding_set.empty() || package == Encoding::inputenc)
3333                     && !features.isRequired("japanese")
3334                     && !features.isProvided("inputenc")) {
3335                         os << "\\usepackage[";
3336                         set<string>::const_iterator it = encoding_set.begin();
3337                         set<string>::const_iterator const end = encoding_set.end();
3338                         if (it != end) {
3339                                 os << from_ascii(*it);
3340                                 ++it;
3341                         }
3342                         for (; it != end; ++it)
3343                                 os << ',' << from_ascii(*it);
3344                         if (package == Encoding::inputenc) {
3345                                 if (!encoding_set.empty())
3346                                         os << ',';
3347                                 os << from_ascii(doc_encoding);
3348                         }
3349                         if (features.runparams().flavor == Flavor::LuaTeX
3350                             || features.runparams().flavor == Flavor::DviLuaTeX)
3351                                 os << "]{luainputenc}\n";
3352                         else
3353                                 os << "]{inputenc}\n";
3354                 }
3355         } else if (inputenc != "auto-legacy-plain") {
3356                 switch (encoding().package()) {
3357                 case Encoding::none:
3358                 case Encoding::CJK:
3359                 case Encoding::japanese:
3360                         if (encoding().iconvName() != "UTF-8"
3361                                 && !features.runparams().isFullUnicode())
3362                           // don't default to [utf8]{inputenc} with TeXLive >= 18
3363                           os << "\\ifdefined\\UseRawInputEncoding\n"
3364                                  << "  \\UseRawInputEncoding\\fi\n";
3365                         break;
3366                 case Encoding::inputenc:
3367                         // do not load inputenc if japanese is used
3368                         // or if the class provides inputenc
3369                         if (features.isRequired("japanese")
3370                             || features.isProvided("inputenc"))
3371                                 break;
3372                         os << "\\usepackage[" << from_ascii(encoding().latexName());
3373                         if (features.runparams().flavor == Flavor::LuaTeX
3374                             || features.runparams().flavor == Flavor::DviLuaTeX)
3375                                 os << "]{luainputenc}\n";
3376                         else
3377                                 os << "]{inputenc}\n";
3378                         break;
3379                 }
3380         }
3381         if (inputenc == "auto-legacy-plain" || features.isRequired("japanese")) {
3382                 // don't default to [utf8]{inputenc} with TeXLive >= 18
3383                 os << "\\ifdefined\\UseRawInputEncoding\n";
3384                 os << "  \\UseRawInputEncoding\\fi\n";
3385         }
3386 }
3387
3388
3389 string const BufferParams::parseFontName(string const & name) const
3390 {
3391         string mangled = name;
3392         size_t const idx = mangled.find('[');
3393         if (idx == string::npos || idx == 0)
3394                 return mangled;
3395         else
3396                 return mangled.substr(0, idx - 1);
3397 }
3398
3399
3400 string const BufferParams::loadFonts(LaTeXFeatures & features) const
3401 {
3402         if (fontsRoman() == "default" && fontsSans() == "default"
3403             && fontsTypewriter() == "default"
3404             && (fontsMath() == "default" || fontsMath() == "auto"))
3405                 //nothing to do
3406                 return string();
3407
3408         ostringstream os;
3409
3410         /* Fontspec (XeTeX, LuaTeX): we provide GUI support for oldstyle
3411          * numbers (Numbers=OldStyle) and sf/tt scaling. The Ligatures=TeX/
3412          * Mapping=tex-text option assures TeX ligatures (such as "--")
3413          * are resolved. Note that tt does not use these ligatures.
3414          * TODO:
3415          *    -- add more GUI options?
3416          *    -- add more fonts (fonts for other scripts)
3417          *    -- if there's a way to find out if a font really supports
3418          *       OldStyle, enable/disable the widget accordingly.
3419         */
3420         if (useNonTeXFonts && features.isAvailable("fontspec")) {
3421                 // "Mapping=tex-text" and "Ligatures=TeX" are equivalent.
3422                 // However, until v.2 (2010/07/11) fontspec only knew
3423                 // Mapping=tex-text (for XeTeX only); then "Ligatures=TeX"
3424                 // was introduced for both XeTeX and LuaTeX (LuaTeX
3425                 // didn't understand "Mapping=tex-text", while XeTeX
3426                 // understood both. With most recent versions, both
3427                 // variants are understood by both engines. However,
3428                 // we want to provide support for at least TeXLive 2009
3429                 // (for XeTeX; LuaTeX is only supported as of v.2)
3430                 // As of 2017/11/03, Babel has its own higher-level
3431                 // interface on top of fontspec that is to be used.
3432                 bool const babelfonts = features.useBabel()
3433                                 && features.isAvailable("babel-2017/11/03");
3434                 string const texmapping =
3435                         (features.runparams().flavor == Flavor::XeTeX) ?
3436                         "Mapping=tex-text" : "Ligatures=TeX";
3437                 if (fontsRoman() != "default") {
3438                         if (babelfonts)
3439                                 os << "\\babelfont{rm}[";
3440                         else
3441                                 os << "\\setmainfont[";
3442                         if (!font_roman_opts.empty())
3443                                 os << font_roman_opts << ',';
3444                         os << texmapping;
3445                         if (fonts_roman_osf)
3446                                 os << ",Numbers=OldStyle";
3447                         os << "]{" << parseFontName(fontsRoman()) << "}\n";
3448                 }
3449                 if (fontsSans() != "default") {
3450                         string const sans = parseFontName(fontsSans());
3451                         if (fontsSansScale() != 100) {
3452                                 if (babelfonts)
3453                                         os << "\\babelfont{sf}";
3454                                 else
3455                                         os << "\\setsansfont";
3456                                 os << "[Scale="
3457                                    << float(fontsSansScale()) / 100 << ',';
3458                                 if (fonts_sans_osf)
3459                                         os << "Numbers=OldStyle,";
3460                                 if (!font_sans_opts.empty())
3461                                         os << font_sans_opts << ',';
3462                                 os << texmapping << "]{"
3463                                    << sans << "}\n";
3464                         } else {
3465                                 if (babelfonts)
3466                                         os << "\\babelfont{sf}[";
3467                                 else
3468                                         os << "\\setsansfont[";
3469                                 if (fonts_sans_osf)
3470                                         os << "Numbers=OldStyle,";
3471                                 if (!font_sans_opts.empty())
3472                                         os << font_sans_opts << ',';
3473                                 os << texmapping << "]{"
3474                                    << sans << "}\n";
3475                         }
3476                 }
3477                 if (fontsTypewriter() != "default") {
3478                         string const mono = parseFontName(fontsTypewriter());
3479                         if (fontsTypewriterScale() != 100) {
3480                                 if (babelfonts)
3481                                         os << "\\babelfont{tt}";
3482                                 else
3483                                         os << "\\setmonofont";
3484                                 os << "[Scale="
3485                                    << float(fontsTypewriterScale()) / 100;
3486                                 if (fonts_typewriter_osf)
3487                                         os << ",Numbers=OldStyle";
3488                                 if (!font_typewriter_opts.empty())
3489                                         os << ',' << font_typewriter_opts;
3490                                 os << "]{"
3491                                    << mono << "}\n";
3492                         } else {
3493                                 if (babelfonts)
3494                                         os << "\\babelfont{tt}";
3495                                 else
3496                                         os << "\\setmonofont";
3497                                 if (!font_typewriter_opts.empty() || fonts_typewriter_osf) {
3498                                         os << '[';
3499                                         if (fonts_typewriter_osf)
3500                                                 os << "Numbers=OldStyle";
3501                                         if (!font_typewriter_opts.empty()) {
3502                                                 if (fonts_typewriter_osf)
3503                                                         os << ',';
3504                                                 os << font_typewriter_opts;
3505                                         }
3506                                         os << ']';
3507                                 }
3508                                 os << '{' << mono << "}\n";
3509                         }
3510                 }
3511                 return os.str();
3512         }
3513
3514         // Tex Fonts
3515         bool const ot1 = (main_font_encoding() == "default" || main_font_encoding() == "OT1");
3516         bool const dryrun = features.runparams().dryrun;
3517         bool const complete = (fontsSans() == "default" && fontsTypewriter() == "default");
3518         bool const nomath = (fontsMath() != "auto");
3519
3520         // ROMAN FONTS
3521         os << theLaTeXFonts().getLaTeXFont(from_ascii(fontsRoman())).getLaTeXCode(
3522                 dryrun, ot1, complete, fonts_expert_sc, fonts_roman_osf,
3523                 nomath, font_roman_opts);
3524
3525         // SANS SERIF
3526         os << theLaTeXFonts().getLaTeXFont(from_ascii(fontsSans())).getLaTeXCode(
3527                 dryrun, ot1, complete, fonts_expert_sc, fonts_sans_osf,
3528                 nomath, font_sans_opts, fontsSansScale());
3529
3530         // MONOSPACED/TYPEWRITER
3531         os << theLaTeXFonts().getLaTeXFont(from_ascii(fontsTypewriter())).getLaTeXCode(
3532                 dryrun, ot1, complete, fonts_expert_sc, fonts_typewriter_osf,
3533                 nomath, font_typewriter_opts, fontsTypewriterScale());
3534
3535         // MATH
3536         os << theLaTeXFonts().getLaTeXFont(from_ascii(fontsMath())).getLaTeXCode(
3537                 dryrun, ot1, complete, fonts_expert_sc, fonts_roman_osf,
3538                 nomath);
3539
3540         return os.str();
3541 }
3542
3543
3544 Encoding const & BufferParams::encoding() const
3545 {
3546         // Main encoding for LaTeX output.
3547         if (useNonTeXFonts)
3548                 return *(encodings.fromLyXName("utf8-plain"));
3549         if (inputenc == "auto-legacy" || inputenc == "auto-legacy-plain")
3550                 return *language->encoding();
3551         if (inputenc == "utf8" && language->lang() == "japanese")
3552                 return *(encodings.fromLyXName("utf8-platex"));
3553         Encoding const * const enc = encodings.fromLyXName(inputenc);
3554         if (enc)
3555                 return *enc;
3556         LYXERR0("Unknown inputenc value `" << inputenc
3557                << "'. Using `auto' instead.");
3558         return *language->encoding();
3559 }
3560
3561
3562 string const & BufferParams::defaultBiblioStyle() const
3563 {
3564         if (!biblio_style.empty())
3565                 return biblio_style;
3566
3567         map<string, string> const & bs = documentClass().defaultBiblioStyle();
3568         auto cit = bs.find(theCiteEnginesList.getTypeAsString(citeEngineType()));
3569         if (cit != bs.end())
3570                 return cit->second;
3571         else
3572                 return empty_string();
3573 }
3574
3575
3576 bool BufferParams::fullAuthorList() const
3577 {
3578         return documentClass().fullAuthorList();
3579 }
3580
3581
3582 string BufferParams::getCiteAlias(string const & s) const
3583 {
3584         vector<string> commands =
3585                 documentClass().citeCommands(citeEngineType());
3586         // If it is a real command, don't treat it as an alias
3587         if (find(commands.begin(), commands.end(), s) != commands.end())
3588                 return string();
3589         map<string,string> aliases = documentClass().citeCommandAliases();
3590         if (aliases.find(s) != aliases.end())
3591                 return aliases[s];
3592         return string();
3593 }
3594
3595
3596 vector<string> BufferParams::citeCommands() const
3597 {
3598         static CitationStyle const default_style;
3599         vector<string> commands =
3600                 documentClass().citeCommands(citeEngineType());
3601         if (commands.empty())
3602                 commands.push_back(default_style.name);
3603         return commands;
3604 }
3605
3606
3607 vector<CitationStyle> BufferParams::citeStyles() const
3608 {
3609         static CitationStyle const default_style;
3610         vector<CitationStyle> styles =
3611                 documentClass().citeStyles(citeEngineType());
3612         if (styles.empty())
3613                 styles.push_back(default_style);
3614         return styles;
3615 }
3616
3617
3618 string const BufferParams::bibtexCommand() const
3619 {
3620         // Return document-specific setting if available
3621         if (bibtex_command != "default")
3622                 return bibtex_command;
3623
3624         // If we have "default" in document settings, consult the prefs
3625         // 1. Japanese (uses a specific processor)
3626         if (encoding().package() == Encoding::japanese) {
3627                 if (lyxrc.jbibtex_command != "automatic")
3628                         // Return the specified program, if "automatic" is not set
3629                         return lyxrc.jbibtex_command;
3630                 else if (!useBiblatex()) {
3631                         // With classic BibTeX, return pbibtex, jbibtex, bibtex
3632                         if (lyxrc.jbibtex_alternatives.find("pbibtex") != lyxrc.jbibtex_alternatives.end())
3633                                 return "pbibtex";
3634                         if (lyxrc.jbibtex_alternatives.find("jbibtex") != lyxrc.jbibtex_alternatives.end())
3635                                 return "jbibtex";
3636                         return "bibtex";
3637                 }
3638         }
3639         // 2. All other languages
3640         else if (lyxrc.bibtex_command != "automatic")
3641                 // Return the specified program, if "automatic" is not set
3642                 return lyxrc.bibtex_command;
3643
3644         // 3. Automatic: find the most suitable for the current cite framework
3645         if (useBiblatex()) {
3646                 // For Biblatex, we prefer biber (also for Japanese)
3647                 // and fall back to bibtex8 and, as last resort, bibtex
3648                 if (lyxrc.bibtex_alternatives.find("biber") != lyxrc.bibtex_alternatives.end())
3649                         return "biber";
3650                 else if (lyxrc.bibtex_alternatives.find("bibtex8") != lyxrc.bibtex_alternatives.end())
3651                         return "bibtex8";
3652         }
3653         return "bibtex";
3654 }
3655
3656
3657 bool BufferParams::useBiblatex() const
3658 {
3659         return theCiteEnginesList[citeEngine()]->getCiteFramework() == "biblatex";
3660 }
3661
3662
3663 void BufferParams::invalidateConverterCache() const
3664 {
3665         pimpl_->isExportCacheValid = false;
3666         pimpl_->isViewCacheValid = false;
3667 }
3668
3669
3670 // We shouldn't need to reset the params here, since anything
3671 // we need will be recopied.
3672 void BufferParams::copyForAdvFR(const BufferParams & bp)
3673 {
3674         string const & lang = bp.language->lang();
3675         setLanguage(lang);
3676         layout_modules_ = bp.layout_modules_;
3677         string const & doc_class = bp.documentClass().name();
3678         setBaseClass(doc_class);
3679 }
3680
3681
3682 void BufferParams::setBibFileEncoding(string const & file, string const & enc)
3683 {
3684         bib_encodings[file] = enc;
3685 }
3686
3687
3688 string const BufferParams::bibFileEncoding(string const & file) const
3689 {
3690         if (bib_encodings.find(file) == bib_encodings.end())
3691                 return string();
3692         return bib_encodings.find(file)->second;
3693 }
3694
3695
3696
3697 } // namespace lyx