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