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