]> git.lyx.org Git - lyx.git/blob - src/BufferParams.cpp
Fix functions that used functions but did not defined it
[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             && language->babel() != "hebrew")
1599                 features.require("rtloutputdblcol");
1600
1601         if (output_changes) {
1602                 bool xcolorulem = LaTeXFeatures::isAvailable("ulem") &&
1603                                   LaTeXFeatures::isAvailable("xcolor");
1604
1605                 switch (features.runparams().flavor) {
1606                 case Flavor::LaTeX:
1607                 case Flavor::DviLuaTeX:
1608                         if (xcolorulem) {
1609                                 features.require("ct-xcolor-ulem");
1610                                 features.require("ulem");
1611                                 features.require("xcolor");
1612                         } else {
1613                                 features.require("ct-none");
1614                         }
1615                         break;
1616                 case Flavor::LuaTeX:
1617                 case Flavor::PdfLaTeX:
1618                 case Flavor::XeTeX:
1619                         if (xcolorulem) {
1620                                 features.require("ct-xcolor-ulem");
1621                                 features.require("ulem");
1622                                 features.require("xcolor");
1623                                 // improves color handling in PDF output
1624                         } else {
1625                                 features.require("ct-none");
1626                         }
1627                         break;
1628                 default:
1629                         break;
1630                 }
1631                 if (change_bars)
1632                         features.require("changebar");
1633         }
1634
1635         // Floats with 'Here definitely' as default setting.
1636         if (float_placement.find('H') != string::npos)
1637                 features.require("float");
1638
1639         for (auto const & pm : use_packages) {
1640                 if (pm.first == "amsmath") {
1641                         // AMS Style is at document level
1642                         if (pm.second == package_on ||
1643                             features.isProvided("amsmath"))
1644                                 features.require(pm.first);
1645                 } else if (pm.second == package_on)
1646                         features.require(pm.first);
1647         }
1648
1649         // Document-level line spacing
1650         if (spacing().getSpace() != Spacing::Single && !spacing().isDefault())
1651                 features.require("setspace");
1652
1653         // the bullet shapes are buffer level not paragraph level
1654         // so they are tested here
1655         for (int i = 0; i < 4; ++i) {
1656                 if (user_defined_bullet(i) == ITEMIZE_DEFAULTS[i])
1657                         continue;
1658                 int const font = user_defined_bullet(i).getFont();
1659                 if (font == 0) {
1660                         int const c = user_defined_bullet(i).getCharacter();
1661                         if (c == 16
1662                             || c == 17
1663                             || c == 25
1664                             || c == 26
1665                             || c == 31) {
1666                                 features.require("latexsym");
1667                         }
1668                 } else if (font == 1) {
1669                         features.require("amssymb");
1670                 } else if (font >= 2 && font <= 5) {
1671                         features.require("pifont");
1672                 }
1673         }
1674
1675         if (pdfoptions().use_hyperref) {
1676                 features.require("hyperref");
1677                 // due to interferences with babel and hyperref, the color package has to
1678                 // be loaded after hyperref when hyperref is used with the colorlinks
1679                 // option, see http://www.lyx.org/trac/ticket/5291
1680                 if (pdfoptions().colorlinks)
1681                         features.require("color");
1682         }
1683         if (!listings_params.empty()) {
1684                 // do not test validity because listings_params is
1685                 // supposed to be valid
1686                 string par =
1687                         InsetListingsParams(listings_params).separatedParams(true);
1688                 // we can't support all packages, but we should load the color package
1689                 if (par.find("\\color", 0) != string::npos)
1690                         features.require("color");
1691         }
1692
1693         // some languages are only available via polyglossia
1694         if (features.hasPolyglossiaExclusiveLanguages())
1695                 features.require("polyglossia");
1696
1697         if (useNonTeXFonts && fontsMath() != "auto")
1698                 features.require("unicode-math");
1699
1700         if (use_microtype)
1701                 features.require("microtype");
1702
1703         if (!language->required().empty())
1704                 features.require(language->required());
1705 }
1706
1707
1708 bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
1709                               FileName const & filepath) const
1710 {
1711         // DocumentMetadata must come before anything else
1712         if (features.isAvailableAtLeastFrom("LaTeX", 2022, 6)
1713             && !containsOnly(document_metadata, " \n\t")) {
1714                 // Check if the user preamble contains uncodable glyphs
1715                 odocstringstream doc_metadata;
1716                 docstring uncodable_glyphs;
1717                 Encoding const * const enc = features.runparams().encoding;
1718                 if (enc) {
1719                         for (char_type c : document_metadata) {
1720                                 if (!enc->encodable(c)) {
1721                                         docstring const glyph(1, c);
1722                                         LYXERR0("Uncodable character '"
1723                                                 << glyph
1724                                                 << "' in document metadata!");
1725                                         uncodable_glyphs += glyph;
1726                                         if (features.runparams().dryrun) {
1727                                                 doc_metadata << "<" << _("LyX Warning: ")
1728                                                    << _("uncodable character") << " '";
1729                                                 doc_metadata.put(c);
1730                                                 doc_metadata << "'>";
1731                                         }
1732                                 } else
1733                                         doc_metadata.put(c);
1734                         }
1735                 } else
1736                         doc_metadata << document_metadata;
1737
1738                 // On BUFFER_VIEW|UPDATE, warn user if we found uncodable glyphs
1739                 if (!features.runparams().dryrun && !uncodable_glyphs.empty()) {
1740                         frontend::Alert::warning(
1741                                 _("Uncodable character in document metadata"),
1742                                 support::bformat(
1743                                   _("The metadata of your document contains glyphs "
1744                                     "that are unknown in the current document encoding "
1745                                     "(namely %1$s).\nThese glyphs are omitted "
1746                                     " from the output, which may result in "
1747                                     "incomplete output."
1748                                     "\n\nPlease select an appropriate "
1749                                     "document encoding\n"
1750                                     "(such as utf8) or change the "
1751                                     "metadata accordingly."),
1752                                   uncodable_glyphs));
1753                 }
1754                 if (!doc_metadata.str().empty()) {
1755                         os << "\\DocumentMetadata{\n"
1756                            << doc_metadata.str()
1757                            << "}\n";
1758                 }
1759         }
1760
1761         // http://www.tug.org/texmf-dist/doc/latex/base/fixltx2e.pdf
1762         // !! To use the Fix-cm package, load it before \documentclass, and use the command
1763         // \RequirePackage to do so, rather than the normal \usepackage
1764         // Do not try to load any other package before the document class, unless you
1765         // have a thorough understanding of the LATEX internals and know exactly what you
1766         // are doing!
1767         if (features.mustProvide("fix-cm"))
1768                 os << "\\RequirePackage{fix-cm}\n";
1769         // Likewise for fixltx2e. If other packages conflict with this policy,
1770         // treat it as a package bug (and report it!)
1771         // See http://www.latex-project.org/cgi-bin/ltxbugs2html?pr=latex/4407
1772         if (features.mustProvide("fixltx2e"))
1773                 os << "\\RequirePackage{fixltx2e}\n";
1774
1775         os << "\\documentclass";
1776
1777         DocumentClass const & tclass = documentClass();
1778
1779         ostringstream clsoptions; // the document class options.
1780
1781         if (tokenPos(tclass.opt_fontsize(),
1782                      '|', fontsize) >= 0) {
1783                 // only write if existing in list (and not default)
1784                 clsoptions << subst(tclass.fontsizeformat(), "$$s", fontsize) << ",";
1785         }
1786
1787         // paper sizes not supported by the class itself need the
1788         // geometry package
1789         vector<string> classpsizes = getVectorFromString(tclass.opt_pagesize(), "|");
1790         bool class_supported_papersize = papersize == PAPER_DEFAULT
1791                 || find(classpsizes.begin(), classpsizes.end(), string_papersize[papersize]) != classpsizes.end();
1792
1793         if ((!use_geometry || features.isProvided("geometry-light"))
1794             && class_supported_papersize && papersize != PAPER_DEFAULT)
1795                 clsoptions << subst(tclass.pagesizeformat(), "$$s", string_papersize[papersize]) << ",";
1796
1797         // if needed
1798         if (sides != tclass.sides()) {
1799                 switch (sides) {
1800                 case OneSide:
1801                         clsoptions << "oneside,";
1802                         break;
1803                 case TwoSides:
1804                         clsoptions << "twoside,";
1805                         break;
1806                 }
1807         }
1808
1809         // if needed
1810         if (columns != tclass.columns()) {
1811                 if (columns == 2)
1812                         clsoptions << "twocolumn,";
1813                 else
1814                         clsoptions << "onecolumn,";
1815         }
1816
1817         if (!use_geometry
1818             && orientation == ORIENTATION_LANDSCAPE)
1819                 clsoptions << "landscape,";
1820
1821         if (is_math_indent)
1822                 clsoptions << "fleqn,";
1823
1824         switch(math_numbering_side) {
1825         case LEFT:
1826                 clsoptions << "leqno,";
1827                 break;
1828         case RIGHT:
1829                 clsoptions << "reqno,";
1830                 features.require("amsmath");
1831                 break;
1832         case DEFAULT:
1833                 break;
1834         }
1835
1836         if (paragraph_separation) {
1837                 if (!tclass.halfparskip().empty() && getDefSkip().kind() == VSpace::HALFLINE)
1838                         clsoptions << tclass.halfparskip() << ",";
1839                 if (!tclass.fullparskip().empty() && getDefSkip().kind() == VSpace::FULLLINE)
1840                         clsoptions << tclass.fullparskip() << ",";
1841         }
1842
1843         // language should be a parameter to \documentclass
1844         if (language->babel() == "hebrew"
1845             && default_language->babel() != "hebrew")
1846                 // This seems necessary
1847                 features.useLanguage(default_language);
1848
1849         ostringstream language_options;
1850         bool const use_babel = features.useBabel() && !features.isProvided("babel");
1851         bool const use_polyglossia = features.usePolyglossia();
1852         bool const global = lyxrc.language_global_options;
1853         if (features.useBabel() || (use_polyglossia && global)) {
1854                 language_options << features.getBabelLanguages();
1855                 if (!language->babel().empty()) {
1856                         if (!language_options.str().empty())
1857                                 language_options << ',';
1858                         language_options << language->babel();
1859                 }
1860                 if (global && !language_options.str().empty())
1861                         clsoptions << language_options.str() << ',';
1862         }
1863
1864         // the predefined options from the layout
1865         if (use_default_options && !tclass.options().empty())
1866                 clsoptions << tclass.options() << ',';
1867
1868         // the user-defined options
1869         if (!options.empty()) {
1870                 clsoptions << options << ',';
1871         }
1872         
1873         docstring const strOptions = from_utf8(clsoptions.str());
1874         if (!strOptions.empty()) {
1875                 // Check if class options contain uncodable glyphs
1876                 docstring uncodable_glyphs;
1877                 docstring options_encodable;
1878                 Encoding const * const enc = features.runparams().encoding;
1879                 if (enc) {
1880                         for (char_type c : strOptions) {
1881                                 if (!enc->encodable(c)) {
1882                                         docstring const glyph(1, c);
1883                                         LYXERR0("Uncodable character '"
1884                                                 << glyph
1885                                                 << "' in class options!");
1886                                         uncodable_glyphs += glyph;
1887                                         if (features.runparams().dryrun) {
1888                                                 options_encodable += "<" + _("LyX Warning: ")
1889                                                    + _("uncodable character") + " '";
1890                                                 options_encodable += c;
1891                                                 options_encodable += "'>";
1892                                         }
1893                                 } else
1894                                         options_encodable += c;
1895                         }
1896                 } else
1897                         options_encodable = strOptions;
1898         
1899                 // On BUFFER_VIEW|UPDATE, warn user if we found uncodable glyphs
1900                 if (!features.runparams().dryrun && !uncodable_glyphs.empty()) {
1901                         frontend::Alert::warning(
1902                                 _("Uncodable character in class options"),
1903                                 support::bformat(
1904                                   _("The class options of your document contain glyphs "
1905                                     "that are unknown in the current document encoding "
1906                                     "(namely %1$s).\nThese glyphs are omitted "
1907                                     " from the output, which may result in "
1908                                     "incomplete output."
1909                                     "\n\nPlease select an appropriate "
1910                                     "document encoding\n"
1911                                     "(such as utf8) or change the "
1912                                     "class options accordingly."),
1913                                   uncodable_glyphs));
1914                 }
1915                 options_encodable = rtrim(options_encodable, ",");
1916                 os << '[' << options_encodable << ']';
1917         }
1918
1919         os << '{' << from_ascii(tclass.latexname()) << "}\n";
1920         // end of \documentclass defs
1921
1922         // The package options (via \PassOptionsToPackage)
1923         os << from_ascii(features.getPackageOptions());
1924
1925         // if we use fontspec or newtxmath, we have to load the AMS packages here
1926         string const ams = features.loadAMSPackages();
1927         string const main_font_enc = features.runparams().main_fontenc;
1928         bool const ot1 = (main_font_enc == "default" || main_font_enc == "OT1");
1929         bool const use_newtxmath =
1930                 theLaTeXFonts().getLaTeXFont(from_ascii(fontsMath())).getUsedPackage(
1931                         ot1, false, false) == "newtxmath";
1932         if ((useNonTeXFonts || use_newtxmath) && !ams.empty())
1933                 os << from_ascii(ams);
1934
1935         if (useNonTeXFonts) {
1936                 // Babel (as of 2017/11/03) loads fontspec itself
1937                 // However, it does so only if a non-default font is requested via \babelfont
1938                 // Thus load fontspec if this is not the case and we need fontspec features
1939                 bool const babel_needfontspec =
1940                                 !features.isAvailableAtLeastFrom("babel", 2017, 11, 3)
1941                                 || (fontsRoman() == "default"
1942                                     && fontsSans() == "default"
1943                                     && fontsTypewriter() == "default"
1944                                     // these need fontspec features
1945                                     && (features.isRequired("textquotesinglep")
1946                                         || features.isRequired("textquotedblp")));
1947                 if (!features.isProvided("fontspec")
1948                     && (!features.useBabel() || babel_needfontspec))
1949                         os << "\\usepackage{fontspec}\n";
1950                 if (features.mustProvide("unicode-math")
1951                     && features.isAvailable("unicode-math"))
1952                         os << "\\usepackage{unicode-math}\n";
1953         }
1954
1955         // load CJK support package before font selection
1956         // (see autotests/export/latex/CJK/micro-sign_utf8-cjk-libertine.lyx)
1957         if (!useNonTeXFonts && encoding().package() != Encoding::none && inputenc != "utf8x"
1958                 && (encoding().package() == Encoding::CJK || features.mustProvide("CJK"))) {
1959                 if (inputenc == "utf8-cjk" || inputenc == "utf8")
1960                         os << "\\usepackage{CJKutf8}\n";
1961                 else
1962                         os << "\\usepackage[encapsulated]{CJK}\n";
1963         }
1964
1965         // font selection must be done before loading fontenc.sty
1966         // but after babel with non-TeX fonts
1967         string const fonts = loadFonts(features);
1968         if (!fonts.empty() && (!features.useBabel() || !useNonTeXFonts))
1969                 os << from_utf8(fonts);
1970
1971         if (fonts_default_family != "default")
1972                 os << "\\renewcommand{\\familydefault}{\\"
1973                    << from_ascii(fonts_default_family) << "}\n";
1974
1975         // set font encoding
1976         // non-TeX fonts use font encoding TU (set by fontspec)
1977         if (!useNonTeXFonts && !features.isProvided("fontenc")
1978             && main_font_enc != "default") {
1979                 // get main font encodings
1980                 vector<string> fontencs = font_encodings();
1981                 // get font encodings of secondary languages
1982                 // FIXME: some languages (hebrew, ...) assume a standard font encoding as last
1983                 // option (for text in other languages).
1984                 features.getFontEncodings(fontencs);
1985                 if (!fontencs.empty()) {
1986                         os << "\\usepackage["
1987                            << from_ascii(getStringFromVector(fontencs))
1988                            << "]{fontenc}\n";
1989                 }
1990         }
1991
1992         // Load textcomp and pmboxdraw before (lua)inputenc (#11454)
1993         if (features.mustProvide("textcomp"))
1994                 os << "\\usepackage{textcomp}\n";
1995         if (features.mustProvide("pmboxdraw"))
1996                 os << "\\usepackage{pmboxdraw}\n";
1997         
1998         // handle inputenc etc.
1999         // (In documents containing text in Thai language, 
2000         // we must load inputenc after babel, see lib/languages).
2001         if (!contains(features.getBabelPostsettings(), from_ascii("thai.ldf")))
2002                 writeEncodingPreamble(os, features);
2003
2004         // includeonly
2005         if (!features.runparams().includeall && !included_children_.empty()) {
2006                 os << "\\includeonly{";
2007                 bool first = true;
2008                 // we do not use "auto const &" here, because incfile is modified later
2009                 // coverity[auto_causes_copy]
2010                 for (auto incfile : included_children_) {
2011                         FileName inc = makeAbsPath(incfile, filepath.absFileName());
2012                         string mangled = DocFileName(changeExtension(inc.absFileName(), ".tex")).
2013                                         mangledFileName();
2014                         if (!features.runparams().nice)
2015                                 incfile = mangled;
2016                         // \includeonly doesn't want an extension
2017                         incfile = changeExtension(incfile, string());
2018                         incfile = support::latex_path(incfile);
2019                         if (!incfile.empty()) {
2020                                 if (!first)
2021                                         os << ",";
2022                                 os << from_utf8(incfile);
2023                         }
2024                         first = false;
2025                 }
2026                 os << "}\n";
2027         }
2028
2029         if (!features.isProvided("geometry")
2030             && (use_geometry || !class_supported_papersize)) {
2031                 odocstringstream ods;
2032                 if (!getGraphicsDriver("geometry").empty())
2033                         ods << getGraphicsDriver("geometry");
2034                 if (orientation == ORIENTATION_LANDSCAPE)
2035                         ods << ",landscape";
2036                 switch (papersize) {
2037                 case PAPER_CUSTOM:
2038                         if (!paperwidth.empty())
2039                                 ods << ",paperwidth="
2040                                    << from_ascii(paperwidth);
2041                         if (!paperheight.empty())
2042                                 ods << ",paperheight="
2043                                    << from_ascii(paperheight);
2044                         break;
2045                 case PAPER_USLETTER:
2046                 case PAPER_USLEGAL:
2047                 case PAPER_USEXECUTIVE:
2048                 case PAPER_A0:
2049                 case PAPER_A1:
2050                 case PAPER_A2:
2051                 case PAPER_A3:
2052                 case PAPER_A4:
2053                 case PAPER_A5:
2054                 case PAPER_A6:
2055                 case PAPER_B0:
2056                 case PAPER_B1:
2057                 case PAPER_B2:
2058                 case PAPER_B3:
2059                 case PAPER_B4:
2060                 case PAPER_B5:
2061                 case PAPER_B6:
2062                 case PAPER_C0:
2063                 case PAPER_C1:
2064                 case PAPER_C2:
2065                 case PAPER_C3:
2066                 case PAPER_C4:
2067                 case PAPER_C5:
2068                 case PAPER_C6:
2069                 case PAPER_JISB0:
2070                 case PAPER_JISB1:
2071                 case PAPER_JISB2:
2072                 case PAPER_JISB3:
2073                 case PAPER_JISB4:
2074                 case PAPER_JISB5:
2075                 case PAPER_JISB6:
2076                         ods << "," << from_ascii(string_papersize_geometry[papersize]);
2077                         break;
2078                 case PAPER_DEFAULT:
2079                         break;
2080                 }
2081                 docstring g_options = trim(ods.str(), ",");
2082                 os << "\\usepackage";
2083                 // geometry-light means that the class works with geometry, but overwrites
2084                 // the package options and paper sizes (memoir does this).
2085                 // In this case, all options need to go to \geometry
2086                 // and the standard paper sizes need to go to the class options.
2087                 if (!g_options.empty() && !features.isProvided("geometry-light")) {
2088                         os << '[' << g_options << ']';
2089                         g_options.clear();
2090                 }
2091                 os << "{geometry}\n";
2092                 if (use_geometry || features.isProvided("geometry-light")) {
2093                         os << "\\geometry{verbose";
2094                         if (!g_options.empty())
2095                                 // Output general options here with "geometry light".
2096                                 os << "," << g_options;
2097                         // output this only if use_geometry is true
2098                         if (use_geometry) {
2099                                 if (!topmargin.empty())
2100                                         os << ",tmargin=" << from_ascii(Length(topmargin).asLatexString());
2101                                 if (!bottommargin.empty())
2102                                         os << ",bmargin=" << from_ascii(Length(bottommargin).asLatexString());
2103                                 if (!leftmargin.empty())
2104                                         os << ",lmargin=" << from_ascii(Length(leftmargin).asLatexString());
2105                                 if (!rightmargin.empty())
2106                                         os << ",rmargin=" << from_ascii(Length(rightmargin).asLatexString());
2107                                 if (!headheight.empty())
2108                                         os << ",headheight=" << from_ascii(Length(headheight).asLatexString());
2109                                 if (!headsep.empty())
2110                                         os << ",headsep=" << from_ascii(Length(headsep).asLatexString());
2111                                 if (!footskip.empty())
2112                                         os << ",footskip=" << from_ascii(Length(footskip).asLatexString());
2113                                 if (!columnsep.empty())
2114                                         os << ",columnsep=" << from_ascii(Length(columnsep).asLatexString());
2115                         }
2116                 os << "}\n";
2117                 }
2118         } else if (orientation == ORIENTATION_LANDSCAPE
2119                    || papersize != PAPER_DEFAULT) {
2120                 features.require("papersize");
2121         }
2122
2123         if (tokenPos(tclass.opt_pagestyle(), '|', pagestyle) >= 0) {
2124                 if (pagestyle == "fancy")
2125                         os << "\\usepackage{fancyhdr}\n";
2126                 os << "\\pagestyle{" << from_ascii(pagestyle) << "}\n";
2127         }
2128
2129         // only output when the background color is not default
2130         if (isbackgroundcolor) {
2131                 // only require color here, the background color will be defined
2132                 // in LaTeXFeatures.cpp to avoid interferences with the LaTeX
2133                 // package pdfpages
2134                 features.require("color");
2135                 features.require("pagecolor");
2136         }
2137
2138         // only output when the font color is not default
2139         if (isfontcolor) {
2140                 // only require color here, the font color will be defined
2141                 // in LaTeXFeatures.cpp to avoid interferences with the LaTeX
2142                 // package pdfpages
2143                 features.require("color");
2144                 features.require("fontcolor");
2145         }
2146
2147         // Only if class has a ToC hierarchy
2148         if (tclass.hasTocLevels()) {
2149                 if (secnumdepth != tclass.secnumdepth()) {
2150                         os << "\\setcounter{secnumdepth}{"
2151                            << secnumdepth
2152                            << "}\n";
2153                 }
2154                 if (tocdepth != tclass.tocdepth()) {
2155                         os << "\\setcounter{tocdepth}{"
2156                            << tocdepth
2157                            << "}\n";
2158                 }
2159         }
2160
2161         if (paragraph_separation) {
2162                 // when skip separation
2163                 string psopt;
2164                 bool default_skip = false;
2165                 bool by_class_option = false;
2166                 switch (getDefSkip().kind()) {
2167                 case VSpace::SMALLSKIP:
2168                         psopt = "\\smallskipamount";
2169                         break;
2170                 case VSpace::MEDSKIP:
2171                         psopt = "\\medskipamount";
2172                         break;
2173                 case VSpace::BIGSKIP:
2174                         psopt = "\\bigskipamount";
2175                         break;
2176                 case VSpace::HALFLINE:
2177                         // default (no option)
2178                         default_skip = true;
2179                         by_class_option = !tclass.halfparskip().empty();
2180                         break;
2181                 case VSpace::FULLLINE:
2182                         psopt = "\\baselineskip";
2183                         by_class_option = !tclass.fullparskip().empty();
2184                         break;
2185                 case VSpace::LENGTH:
2186                         psopt = getDefSkip().length().asLatexString();
2187                         break;
2188                 default:
2189                         break;
2190                 }
2191                 if (features.isProvided("parskip")) {
2192                         // package already loaded (with arbitrary options)
2193                         // change parskip value only
2194                         if (!psopt.empty())
2195                                 os << "\\setlength{\\parskip}{" + psopt + "}\n";
2196                         else if (default_skip)
2197                                 // explicitly reset default (might have been changed
2198                                 // in a class or package)
2199                                 os << "\\parskip=.5\\baselineskip plus 2pt\\relax\n";
2200                 } else if (!by_class_option) {
2201                         // load parskip package with required options
2202                         string psopts;
2203                         if (!psopt.empty()) {
2204                                 if (contains(psopt, ' '))
2205                                         // glue length has spaces: embrace
2206                                         psopts = "skip={" + psopt + "}";
2207                                 else
2208                                         psopts = "skip=" + psopt;
2209                         }
2210                         string const xpsopts = getPackageOptions("parskip");
2211                         if (!xpsopts.empty()) {
2212                                 if (!psopts.empty())
2213                                         psopts += ",";
2214                                 psopts += xpsopts;
2215                         }
2216                         os << "\\usepackage";
2217                         if (!psopts.empty())
2218                                 os << "[" << psopts << "]";
2219                         os << "{parskip}\n";
2220                 }
2221         } else {
2222                 // when separation by indentation
2223                 // only output something when a width is given
2224                 if (!getParIndent().empty()) {
2225                         os << "\\setlength{\\parindent}{"
2226                            << from_utf8(getParIndent().asLatexString())
2227                            << "}\n";
2228                 }
2229         }
2230
2231         if (is_math_indent) {
2232                 // when formula indentation
2233                 // only output something when it is not the default
2234                 if (!getMathIndent().empty()) {
2235                         os << "\\setlength{\\mathindent}{"
2236                            << from_utf8(getMathIndent().asString())
2237                            << "}\n";
2238                 }
2239         }
2240
2241         // Now insert the LyX specific LaTeX commands...
2242         features.resolveAlternatives();
2243         features.expandMultiples();
2244
2245         if (output_sync) {
2246                 if (!output_sync_macro.empty())
2247                         os << from_utf8(output_sync_macro) +"\n";
2248                 else if (features.runparams().flavor == Flavor::LaTeX)
2249                         os << "\\usepackage[active]{srcltx}\n";
2250                 else
2251                         os << "\\synctex=-1\n";
2252         }
2253
2254         // due to interferences with babel and hyperref, the color package has to
2255         // be loaded (when it is not already loaded) before babel when hyperref
2256         // is used with the colorlinks option, see
2257         // http://www.lyx.org/trac/ticket/5291
2258         // we decided therefore to load color always before babel, see
2259         // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg144349.html
2260         os << from_ascii(features.getColorOptions());
2261
2262         // If we use hyperref, jurabib, japanese or varioref,
2263         // we have to call babel before
2264         if (use_babel
2265             && (features.isRequired("jurabib")
2266                 || features.isRequired("hyperref")
2267                 || features.isRequired("varioref")
2268                 || features.isRequired("japanese"))) {
2269                         os << features.getBabelPresettings();
2270                         // FIXME UNICODE
2271                         os << from_utf8(babelCall(language_options.str(),
2272                                                                           !lyxrc.language_global_options)) + '\n';
2273                         os << features.getBabelPostsettings();
2274         }
2275
2276         // The optional packages;
2277         os << from_ascii(features.getPackages());
2278
2279         // Additional Indices
2280         if (features.isRequired("splitidx")) {
2281                 for (auto const & idx : indiceslist()) {
2282                         os << "\\newindex{";
2283                         os << escape(idx.shortcut());
2284                         os << "}\n";
2285                 }
2286         }
2287
2288         // Line spacing
2289         os << from_utf8(spacing().writePreamble(features.isProvided("SetSpace")));
2290
2291         // PDF support.
2292         // * Hyperref manual: "Make sure it comes last of your loaded
2293         //   packages, to give it a fighting chance of not being over-written,
2294         //   since its job is to redefine many LaTeX commands."
2295         // * Email from Heiko Oberdiek: "It is usually better to load babel
2296         //   before hyperref. Then hyperref has a chance to detect babel.
2297         // * Has to be loaded before the "LyX specific LaTeX commands" to
2298         //   avoid errors with algorithm floats.
2299         // use hyperref explicitly if it is required
2300         if (features.isRequired("hyperref")) {
2301                 OutputParams tmp_params = features.runparams();
2302                 pdfoptions().writeLaTeX(tmp_params, os,
2303                                         features.isProvided("hyperref"));
2304                 // correctly break URLs with hyperref and dvi/ps output
2305                 if (features.runparams().hyperref_driver == "dvips"
2306                     && features.isAvailable("breakurl"))
2307                         os << "\\usepackage{breakurl}\n";
2308         } else if (features.isRequired("nameref"))
2309                 // hyperref loads this automatically
2310                 os << "\\usepackage{nameref}\n";
2311
2312         if (use_lineno){
2313                 os << "\\usepackage";
2314                 if (!lineno_opts.empty())
2315                         os << "[" << lineno_opts << "]";
2316                 os << "{lineno}\n";
2317                 os << "\\linenumbers\n";
2318         }
2319
2320         // bibtopic needs to be loaded after hyperref.
2321         // the dot provides the aux file naming which LyX can detect.
2322         if (features.mustProvide("bibtopic"))
2323                 os << "\\usepackage[dot]{bibtopic}\n";
2324
2325         // Will be surrounded by \makeatletter and \makeatother when not empty
2326         otexstringstream atlyxpreamble;
2327
2328         // Some macros LyX will need
2329         {
2330                 TexString tmppreamble = features.getMacros();
2331                 if (!tmppreamble.str.empty())
2332                         atlyxpreamble << "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2333                                          "LyX specific LaTeX commands.\n"
2334                                       << std::move(tmppreamble)
2335                                       << '\n';
2336         }
2337         // the text class specific preamble
2338         {
2339                 docstring tmppreamble = features.getTClassPreamble();
2340                 if (!tmppreamble.empty())
2341                         atlyxpreamble << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2342                                          "Textclass specific LaTeX commands.\n"
2343                                       << tmppreamble
2344                                       << '\n';
2345         }
2346         // suppress date if selected
2347         // use \@ifundefined because we cannot be sure that every document class
2348         // has a \date command
2349         if (suppress_date)
2350                 atlyxpreamble << "\\@ifundefined{date}{}{\\date{}}\n";
2351
2352         /* the user-defined preamble */
2353         if (!containsOnly(preamble, " \n\t")) {
2354                 // FIXME UNICODE
2355                 atlyxpreamble << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2356                                  "User specified LaTeX commands.\n";
2357
2358                 // Check if the user preamble contains uncodable glyphs
2359                 odocstringstream user_preamble;
2360                 docstring uncodable_glyphs;
2361                 Encoding const * const enc = features.runparams().encoding;
2362                 if (enc) {
2363                         for (char_type c : preamble) {
2364                                 if (!enc->encodable(c)) {
2365                                         docstring const glyph(1, c);
2366                                         LYXERR0("Uncodable character '"
2367                                                 << glyph
2368                                                 << "' in user preamble!");
2369                                         uncodable_glyphs += glyph;
2370                                         if (features.runparams().dryrun) {
2371                                                 user_preamble << "<" << _("LyX Warning: ")
2372                                                    << _("uncodable character") << " '";
2373                                                 user_preamble.put(c);
2374                                                 user_preamble << "'>";
2375                                         }
2376                                 } else
2377                                         user_preamble.put(c);
2378                         }
2379                 } else
2380                         user_preamble << preamble;
2381
2382                 // On BUFFER_VIEW|UPDATE, warn user if we found uncodable glyphs
2383                 if (!features.runparams().dryrun && !uncodable_glyphs.empty()) {
2384                         frontend::Alert::warning(
2385                                 _("Uncodable character in user preamble"),
2386                                 support::bformat(
2387                                   _("The user preamble of your document contains glyphs "
2388                                     "that are unknown in the current document encoding "
2389                                     "(namely %1$s).\nThese glyphs are omitted "
2390                                     " from the output, which may result in "
2391                                     "incomplete output."
2392                                     "\n\nPlease select an appropriate "
2393                                     "document encoding\n"
2394                                     "(such as utf8) or change the "
2395                                     "preamble code accordingly."),
2396                                   uncodable_glyphs));
2397                 }
2398                 atlyxpreamble << user_preamble.str() << '\n';
2399         }
2400
2401         // footmisc must be loaded after setspace
2402         // Load it here to avoid clashes with footmisc loaded in the user
2403         // preamble. For that reason we also pass the options via
2404         // \PassOptionsToPackage in getPreamble() and not here.
2405         if (features.mustProvide("footmisc"))
2406                 atlyxpreamble << "\\usepackage{footmisc}\n";
2407
2408         // subfig loads internally the LaTeX package "caption". As
2409         // caption is a very popular package, users will load it in
2410         // the preamble. Therefore we must load subfig behind the
2411         // user-defined preamble and check if the caption package was
2412         // loaded or not. For the case that caption is loaded before
2413         // subfig, there is the subfig option "caption=false". This
2414         // option also works when a koma-script class is used and
2415         // koma's own caption commands are used instead of caption. We
2416         // use \PassOptionsToPackage here because the user could have
2417         // already loaded subfig in the preamble.
2418         if (features.mustProvide("subfig"))
2419                 atlyxpreamble << "\\ifdefined\\showcaptionsetup\n"
2420                                  " % Caption package is used. Advise subfig not to load it again.\n"
2421                                  " \\PassOptionsToPackage{caption=false}{subfig}\n"
2422                                  "\\fi\n"
2423                                  "\\usepackage{subfig}\n";
2424
2425         // Itemize bullet settings need to be last in case the user
2426         // defines their own bullets that use a package included
2427         // in the user-defined preamble -- ARRae
2428         // Actually it has to be done much later than that
2429         // since some packages like frenchb make modifications
2430         // at \begin{document} time -- JMarc
2431         docstring bullets_def;
2432         for (int i = 0; i < 4; ++i) {
2433                 if (user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
2434                         if (bullets_def.empty())
2435                                 bullets_def += "\\AtBeginDocument{\n";
2436                         bullets_def += "  \\def\\labelitemi";
2437                         switch (i) {
2438                                 // `i' is one less than the item to modify
2439                         case 0:
2440                                 break;
2441                         case 1:
2442                                 bullets_def += 'i';
2443                                 break;
2444                         case 2:
2445                                 bullets_def += "ii";
2446                                 break;
2447                         case 3:
2448                                 bullets_def += 'v';
2449                                 break;
2450                         }
2451                         bullets_def += '{' +
2452                                 user_defined_bullet(i).getText()
2453                                 + "}\n";
2454                 }
2455         }
2456
2457         if (!bullets_def.empty())
2458                 atlyxpreamble << bullets_def << "}\n\n";
2459
2460         if (!atlyxpreamble.empty())
2461                 os << "\n\\makeatletter\n"
2462                    << atlyxpreamble.release()
2463                    << "\\makeatother\n\n";
2464
2465         // We try to load babel late, in case it interferes with other packages.
2466         // Jurabib, hyperref, varioref, bicaption, menukeys and listings (bug 8995)
2467         // have to be called after babel, though.
2468         if (use_babel && !features.isRequired("jurabib")
2469             && !features.isRequired("hyperref")
2470             && !features.isRequired("varioref")
2471             && !features.isRequired("japanese")) {
2472                 os << features.getBabelPresettings();
2473                 // FIXME UNICODE
2474                 os << from_utf8(babelCall(language_options.str(),
2475                                           !lyxrc.language_global_options)) + '\n';
2476                 os << features.getBabelPostsettings();
2477         }
2478         // In documents containing text in Thai language, 
2479         // we must load inputenc after babel (see lib/languages).
2480         if (contains(features.getBabelPostsettings(), from_ascii("thai.ldf")))
2481                 writeEncodingPreamble(os, features);
2482
2483         // font selection must be done after babel with non-TeX fonts
2484         if (!fonts.empty() && features.useBabel() && useNonTeXFonts)
2485                 os << from_utf8(fonts);
2486
2487         if (features.isRequired("bicaption"))
2488                 os << "\\usepackage{bicaption}\n";
2489         if (!listings_params.empty()
2490             || features.mustProvide("listings")
2491             || features.mustProvide("minted")) {
2492                 if (use_minted)
2493                         os << "\\usepackage{minted}\n";
2494                 else
2495                         os << "\\usepackage{listings}\n";
2496         }
2497         string lst_params = listings_params;
2498         // If minted, do not output the language option (bug 11203)
2499         if (use_minted && contains(lst_params, "language=")) {
2500                 vector<string> opts =
2501                         getVectorFromString(lst_params, ",", false);
2502                 for (size_t i = 0; i < opts.size(); ++i) {
2503                         if (prefixIs(opts[i], "language="))
2504                                 opts.erase(opts.begin() + i--);
2505                 }
2506                 lst_params = getStringFromVector(opts, ",");
2507         }
2508         if (!lst_params.empty()) {
2509                 if (use_minted)
2510                         os << "\\setminted{";
2511                 else
2512                         os << "\\lstset{";
2513                 // do not test validity because listings_params is
2514                 // supposed to be valid
2515                 string par =
2516                         InsetListingsParams(lst_params).separatedParams(true);
2517                 os << from_utf8(par);
2518                 os << "}\n";
2519         }
2520
2521         // xunicode only needs to be loaded if tipa is used
2522         // (the rest is obsoleted by the new TU encoding).
2523         // It needs to be loaded at least after amsmath, amssymb,
2524         // esint and the other packages that provide special glyphs
2525         if (features.mustProvide("tipa") && useNonTeXFonts
2526             && !features.isProvided("xunicode")) {
2527                 // The `xunicode` package officially only supports XeTeX,
2528                 //  but also works with LuaTeX. We work around its XeTeX test.
2529                 if (features.runparams().flavor != Flavor::XeTeX) {
2530                         os << "% Pretend to xunicode that we are XeTeX\n"
2531                            << "\\def\\XeTeXpicfile{}\n";
2532                 }
2533                 os << "\\usepackage{xunicode}\n";
2534         }
2535
2536         // covington must be loaded after beamerarticle
2537         if (features.isRequired("covington"))
2538             os << "\\usepackage{covington}\n";
2539
2540         // Polyglossia must be loaded last ...
2541         if (use_polyglossia) {
2542                 // call the package
2543                 os << "\\usepackage{polyglossia}\n";
2544                 // set the main language
2545                 os << "\\setdefaultlanguage";
2546                 if (!language->polyglossiaOpts().empty())
2547                         os << "[" << from_ascii(language->polyglossiaOpts()) << "]";
2548                 os << "{" << from_ascii(language->polyglossia()) << "}\n";
2549                 // now setup the other languages
2550                 set<string> const polylangs =
2551                         features.getPolyglossiaLanguages();
2552                 for (auto const & pl : polylangs) {
2553                         // We do not output the options here; they are output in
2554                         // the language switch commands. This is safer if multiple
2555                         // varieties are used.
2556                         if (pl == language->polyglossia())
2557                                 continue;
2558                         os << "\\setotherlanguage";
2559                         os << "{" << from_ascii(pl) << "}\n";
2560                 }
2561         }
2562
2563         // ... but before biblatex (see #7065)
2564         if ((features.mustProvide("biblatex")
2565              || features.isRequired("biblatex-chicago"))
2566             && !features.isProvided("biblatex-chicago")
2567             && !features.isProvided("biblatex-natbib")
2568             && !features.isProvided("natbib-internal")
2569             && !features.isProvided("natbib")
2570             && !features.isProvided("jurabib")) {
2571                 // The biblatex-chicago package has a differing interface
2572                 // it uses a wrapper package and loads styles via fixed options
2573                 bool const chicago = features.isRequired("biblatex-chicago");
2574                 string delim = "";
2575                 string opts;
2576                 os << "\\usepackage";
2577                 if (!biblatex_bibstyle.empty()
2578                     && (biblatex_bibstyle == biblatex_citestyle)
2579                     && !chicago) {
2580                         opts = "style=" + biblatex_bibstyle;
2581                         delim = ",";
2582                 } else if (!chicago) {
2583                         if (!biblatex_bibstyle.empty()) {
2584                                 opts = "bibstyle=" + biblatex_bibstyle;
2585                                 delim = ",";
2586                         }
2587                         if (!biblatex_citestyle.empty()) {
2588                                 opts += delim + "citestyle=" + biblatex_citestyle;
2589                                 delim = ",";
2590                         }
2591                 }
2592                 if (!multibib.empty() && multibib != "child") {
2593                         opts += delim + "refsection=" + multibib;
2594                         delim = ",";
2595                 }
2596                 if (bibtexCommand() == "bibtex8"
2597                     || prefixIs(bibtexCommand(), "bibtex8 ")) {
2598                         opts += delim + "backend=bibtex8";
2599                         delim = ",";
2600                 } else if (bibtexCommand() == "bibtex"
2601                            || prefixIs(bibtexCommand(), "bibtex ")) {
2602                         opts += delim + "backend=bibtex";
2603                         delim = ",";
2604                 }
2605                 if (!bib_encoding.empty() && encodings.fromLyXName(bib_encoding)) {
2606                         opts += delim + "bibencoding="
2607                                 + encodings.fromLyXName(bib_encoding)->latexName();
2608                         delim = ",";
2609                 }
2610                 if (!biblio_opts.empty())
2611                         opts += delim + biblio_opts;
2612                 if (!opts.empty())
2613                         os << "[" << opts << "]";
2614                 if (chicago)
2615                         os << "{biblatex-chicago}\n";
2616                 else
2617                         os << "{biblatex}\n";
2618         }
2619
2620
2621         // Load custom language package here
2622         if (features.langPackage() == LaTeXFeatures::LANG_PACK_CUSTOM) {
2623                 if (lang_package == "default")
2624                         os << from_utf8(lyxrc.language_custom_package);
2625                 else
2626                         os << from_utf8(lang_package);
2627                 os << '\n';
2628         }
2629
2630         // Since menukeys uses catoptions, which does some heavy changes on key-value options,
2631         // it is recommended to load menukeys as the last package (even after hyperref)
2632         if (features.isRequired("menukeys"))
2633                 os << "\\usepackage{menukeys}\n";
2634
2635         docstring const i18npreamble =
2636                 features.getTClassI18nPreamble(use_babel, use_polyglossia,
2637                                                use_minted);
2638         if (!i18npreamble.empty())
2639                 os << i18npreamble + '\n';
2640
2641         return use_babel;
2642 }
2643
2644
2645 void BufferParams::useClassDefaults()
2646 {
2647         DocumentClass const & tclass = documentClass();
2648
2649         sides = tclass.sides();
2650         columns = tclass.columns();
2651         pagestyle = tclass.pagestyle();
2652         tablestyle = tclass.tablestyle();
2653         use_default_options = true;
2654         // Only if class has a ToC hierarchy
2655         if (tclass.hasTocLevels()) {
2656                 secnumdepth = tclass.secnumdepth();
2657                 tocdepth = tclass.tocdepth();
2658         }
2659 }
2660
2661
2662 bool BufferParams::hasClassDefaults() const
2663 {
2664         DocumentClass const & tclass = documentClass();
2665
2666         return sides == tclass.sides()
2667                 && columns == tclass.columns()
2668                 && pagestyle == tclass.pagestyle()
2669                 && tablestyle == tclass.tablestyle()
2670                 && use_default_options
2671                 && secnumdepth == tclass.secnumdepth()
2672                 && tocdepth == tclass.tocdepth();
2673 }
2674
2675
2676 DocumentClass const & BufferParams::documentClass() const
2677 {
2678         return *doc_class_;
2679 }
2680
2681
2682 DocumentClassConstPtr BufferParams::documentClassPtr() const
2683 {
2684         return doc_class_;
2685 }
2686
2687
2688 void BufferParams::setDocumentClass(DocumentClassConstPtr tc)
2689 {
2690         // evil, but this function is evil
2691         doc_class_ = const_pointer_cast<DocumentClass>(tc);
2692         invalidateConverterCache();
2693 }
2694
2695
2696 bool BufferParams::setBaseClass(string const & classname, string const & path)
2697 {
2698         LYXERR(Debug::TCLASS, "setBaseClass: " << classname);
2699         LayoutFileList & bcl = LayoutFileList::get();
2700         if (!bcl.haveClass(classname)) {
2701                 docstring s =
2702                         bformat(_("The layout file:\n"
2703                                 "%1$s\n"
2704                                 "could not be found. A default textclass with default\n"
2705                                 "layouts will be used. LyX will not be able to produce\n"
2706                                 "correct output."),
2707                         from_utf8(classname));
2708                 frontend::Alert::error(_("Document class not found"), s);
2709                 bcl.addEmptyClass(classname);
2710         }
2711
2712         bool const success = bcl[classname].load(path);
2713         if (!success) {
2714                 docstring s =
2715                         bformat(_("Due to some error in it, the layout file:\n"
2716                                 "%1$s\n"
2717                                 "could not be loaded. A default textclass with default\n"
2718                                 "layouts will be used. LyX will not be able to produce\n"
2719                                 "correct output."),
2720                         from_utf8(classname));
2721                 frontend::Alert::error(_("Could not load class"), s);
2722                 bcl.addEmptyClass(classname);
2723         }
2724
2725         pimpl_->baseClass_ = classname;
2726         layout_modules_.adaptToBaseClass(baseClass(), removed_modules_);
2727         return true;
2728 }
2729
2730
2731 LayoutFile const * BufferParams::baseClass() const
2732 {
2733         if (LayoutFileList::get().haveClass(pimpl_->baseClass_))
2734                 return &(LayoutFileList::get()[pimpl_->baseClass_]);
2735
2736         return nullptr;
2737 }
2738
2739
2740 LayoutFileIndex const & BufferParams::baseClassID() const
2741 {
2742         return pimpl_->baseClass_;
2743 }
2744
2745
2746 void BufferParams::makeDocumentClass(bool clone, bool internal)
2747 {
2748         if (!baseClass())
2749                 return;
2750
2751         invalidateConverterCache();
2752         LayoutModuleList mods;
2753         for (auto const & mod : layout_modules_)
2754                 mods.push_back(mod);
2755
2756         doc_class_ = getDocumentClass(*baseClass(), mods, cite_engine_, clone, internal);
2757
2758         TextClass::ReturnValues success = TextClass::OK;
2759         if (!forced_local_layout_.empty())
2760                 success = doc_class_->read(to_utf8(forced_local_layout_),
2761                                            TextClass::MODULE);
2762         if (!local_layout_.empty() &&
2763             (success == TextClass::OK || success == TextClass::OK_OLDFORMAT))
2764                 success = doc_class_->read(to_utf8(local_layout_), TextClass::MODULE);
2765         if (success != TextClass::OK && success != TextClass::OK_OLDFORMAT) {
2766                 docstring const msg = _("Error reading internal layout information");
2767                 frontend::Alert::warning(_("Read Error"), msg);
2768         }
2769 }
2770
2771
2772 bool BufferParams::layoutModuleCanBeAdded(string const & modName) const
2773 {
2774         return layout_modules_.moduleCanBeAdded(modName, baseClass());
2775 }
2776
2777
2778 docstring BufferParams::getLocalLayout(bool forced) const
2779 {
2780         if (forced)
2781                 return from_utf8(doc_class_->forcedLayouts());
2782         else
2783                 return local_layout_;
2784 }
2785
2786
2787 void BufferParams::setLocalLayout(docstring const & layout, bool forced)
2788 {
2789         if (forced)
2790                 forced_local_layout_ = layout;
2791         else
2792                 local_layout_ = layout;
2793 }
2794
2795
2796 bool BufferParams::addLayoutModule(string const & modName)
2797 {
2798         for (auto const & mod : layout_modules_)
2799                 if (mod == modName)
2800                         return false;
2801         layout_modules_.push_back(modName);
2802         return true;
2803 }
2804
2805
2806 string BufferParams::bufferFormat() const
2807 {
2808         return documentClass().outputFormat();
2809 }
2810
2811
2812 bool BufferParams::isExportable(string const & format, bool need_viewable) const
2813 {
2814         FormatList const & formats = exportableFormats(need_viewable);
2815         for (auto const & fmt : formats) {
2816                 if (fmt->name() == format)
2817                         return true;
2818         }
2819         return false;
2820 }
2821
2822
2823 FormatList const & BufferParams::exportableFormats(bool only_viewable) const
2824 {
2825         FormatList & cached = only_viewable ?
2826                         pimpl_->viewableFormatList : pimpl_->exportableFormatList;
2827         bool & valid = only_viewable ?
2828                         pimpl_->isViewCacheValid : pimpl_->isExportCacheValid;
2829         if (valid)
2830                 return cached;
2831
2832         vector<string> const backs = backends();
2833         set<string> excludes;
2834         if (useNonTeXFonts) {
2835                 excludes.insert("latex");
2836                 excludes.insert("pdflatex");
2837         } else if (inputenc != "ascii" && inputenc != "utf8-plain") {
2838                   // XeTeX with TeX fonts requires input encoding ascii (#10600).
2839                   excludes.insert("xetex");
2840         }
2841
2842         FormatList result =
2843                 theConverters().getReachable(backs[0], only_viewable, true, excludes);
2844         vector<string>::const_iterator it = backs.begin() + 1;
2845         for (; it != backs.end(); ++it) {
2846                 FormatList r = theConverters().getReachable(*it, only_viewable,
2847                                                                                                         false, excludes);
2848                 result.insert(result.end(), r.begin(), r.end());
2849         }
2850         sort(result.begin(), result.end(), Format::formatSorter);
2851         cached = result;
2852         valid = true;
2853         return cached;
2854 }
2855
2856
2857 vector<string> BufferParams::backends() const
2858 {
2859         vector<string> v;
2860         string const buffmt = bufferFormat();
2861
2862         // FIXME: Don't hardcode format names here, but use a flag
2863         if (buffmt == "latex") {
2864                 if (encoding().package() == Encoding::japanese)
2865                         v.push_back("platex");
2866                 else {
2867                         if (!useNonTeXFonts) {
2868                                 v.push_back("pdflatex");
2869                                 v.push_back("latex");
2870                         }
2871                         if (useNonTeXFonts 
2872                                 || inputenc == "ascii" || inputenc == "utf8-plain")
2873                                 v.push_back("xetex");
2874                         v.push_back("luatex");
2875                         v.push_back("dviluatex");
2876                 }
2877         } else {
2878                 string rbuffmt = buffmt;
2879                 // If we use an OutputFormat in Japanese docs,
2880                 // we need special format in order to get the path
2881                 // via pLaTeX (#8823)
2882                 if (documentClass().hasOutputFormat()
2883                     && encoding().package() == Encoding::japanese)
2884                         rbuffmt += "-ja";
2885                 v.push_back(rbuffmt);
2886         }
2887
2888         v.push_back("xhtml");
2889         v.push_back("docbook5");
2890         v.push_back("text");
2891         v.push_back("lyx");
2892         return v;
2893 }
2894
2895
2896 Flavor BufferParams::getOutputFlavor(string const & format) const
2897 {
2898         string const dformat = (format.empty() || format == "default") ?
2899                 getDefaultOutputFormat() : format;
2900         DefaultFlavorCache::const_iterator it =
2901                 default_flavors_.find(dformat);
2902
2903         if (it != default_flavors_.end())
2904                 return it->second;
2905
2906         Flavor result = Flavor::LaTeX;
2907
2908         // FIXME It'd be better not to hardcode this, but to do
2909         //       something with formats.
2910         if (dformat == "xhtml")
2911                 result = Flavor::Html;
2912         else if (dformat == "docbook5")
2913                 result = Flavor::DocBook5;
2914         else if (dformat == "text")
2915                 result = Flavor::Text;
2916         else if (dformat == "lyx")
2917                 result = Flavor::LyX;
2918         else if (dformat == "pdflatex")
2919                 result = Flavor::PdfLaTeX;
2920         else if (dformat == "xetex")
2921                 result = Flavor::XeTeX;
2922         else if (dformat == "luatex")
2923                 result = Flavor::LuaTeX;
2924         else if (dformat == "dviluatex")
2925                 result = Flavor::DviLuaTeX;
2926         else {
2927                 // Try to determine flavor of default output format
2928                 vector<string> backs = backends();
2929                 if (find(backs.begin(), backs.end(), dformat) == backs.end()) {
2930                         // Get shortest path to format
2931                         Graph::EdgePath path;
2932                         for (auto const & bvar : backs) {
2933                                 Graph::EdgePath p = theConverters().getPath(bvar, dformat);
2934                                 if (!p.empty() && (path.empty() || p.size() < path.size())) {
2935                                         path = p;
2936                                 }
2937                         }
2938                         if (!path.empty())
2939                                 result = theConverters().getFlavor(path);
2940                 }
2941         }
2942         // cache this flavor
2943         default_flavors_[dformat] = result;
2944         return result;
2945 }
2946
2947
2948 string BufferParams::getDefaultOutputFormat() const
2949 {
2950         if (!default_output_format.empty()
2951             && default_output_format != "default")
2952                 return default_output_format;
2953         if (encoding().package() == Encoding::japanese)
2954                 return lyxrc.default_platex_view_format;
2955         if (useNonTeXFonts)
2956                 return lyxrc.default_otf_view_format;
2957         return lyxrc.default_view_format;
2958 }
2959
2960 Font const BufferParams::getFont() const
2961 {
2962         FontInfo f = documentClass().defaultfont();
2963         if (fonts_default_family == "rmdefault")
2964                 f.setFamily(ROMAN_FAMILY);
2965         else if (fonts_default_family == "sfdefault")
2966                 f.setFamily(SANS_FAMILY);
2967         else if (fonts_default_family == "ttdefault")
2968                 f.setFamily(TYPEWRITER_FAMILY);
2969         return Font(f, language);
2970 }
2971
2972
2973 QuoteStyle BufferParams::getQuoteStyle(string const & qs) const
2974 {
2975         return quotesstyletranslator().find(qs);
2976 }
2977
2978
2979 bool BufferParams::isLatex() const
2980 {
2981         return documentClass().outputType() == LATEX;
2982 }
2983
2984
2985 bool BufferParams::isLiterate() const
2986 {
2987         return documentClass().outputType() == LITERATE;
2988 }
2989
2990
2991 bool BufferParams::hasPackageOption(string const package, string const opt) const
2992 {
2993         for (auto const & p : documentClass().packageOptions())
2994                 if (package == p.first && opt == p.second)
2995                         return true;
2996         return false;
2997 }
2998
2999
3000 string BufferParams::getPackageOptions(string const package) const
3001 {
3002         for (auto const & p : documentClass().packageOptions())
3003                 if (package == p.first)
3004                         return p.second;
3005         return string();
3006 }
3007
3008
3009 bool BufferParams::useBidiPackage(OutputParams const & rp) const
3010 {
3011         return (rp.use_polyglossia
3012                 // as of babel 3.29, bidi=bidi-* is supported by babel
3013                 // So we check whether we use a respective version and
3014                 // whethert bidi-r or bidi-l have been requested either via class
3015                 // or package options
3016                 || (rp.use_babel
3017                     && LaTeXFeatures::isAvailableAtLeastFrom("babel", 2019, 4, 3)
3018                     && (hasPackageOption("babel", "bidi-r")
3019                         || hasPackageOption("babel", "bidi-l")
3020                         || contains(options, "bidi-r")
3021                         || contains(options, "bidi-l")))
3022                 )
3023                 && rp.flavor == Flavor::XeTeX;
3024 }
3025
3026
3027 void BufferParams::readPreamble(Lexer & lex)
3028 {
3029         if (lex.getString() != "\\begin_preamble")
3030                 lyxerr << "Error (BufferParams::readPreamble):"
3031                         "consistency check failed." << endl;
3032
3033         preamble = lex.getLongString(from_ascii("\\end_preamble"));
3034 }
3035
3036
3037 void BufferParams::readDocumentMetadata(Lexer & lex)
3038 {
3039         if (lex.getString() != "\\begin_metadata")
3040                 lyxerr << "Error (BufferParams::readDocumentMetadata):"
3041                         "consistency check failed." << endl;
3042
3043         document_metadata = lex.getLongString(from_ascii("\\end_metadata"));
3044 }
3045
3046
3047 void BufferParams::readLocalLayout(Lexer & lex, bool forced)
3048 {
3049         string const expected = forced ? "\\begin_forced_local_layout" :
3050                                          "\\begin_local_layout";
3051         if (lex.getString() != expected)
3052                 lyxerr << "Error (BufferParams::readLocalLayout):"
3053                         "consistency check failed." << endl;
3054
3055         if (forced)
3056                 forced_local_layout_ =
3057                         lex.getLongString(from_ascii("\\end_forced_local_layout"));
3058         else
3059                 local_layout_ = lex.getLongString(from_ascii("\\end_local_layout"));
3060 }
3061
3062
3063 bool BufferParams::setLanguage(string const & lang)
3064 {
3065         Language const *new_language = languages.getLanguage(lang);
3066         if (!new_language) {
3067                 // Language lang was not found
3068                 return false;
3069         }
3070         language = new_language;
3071         return true;
3072 }
3073
3074
3075 void BufferParams::readLanguage(Lexer & lex)
3076 {
3077         if (!lex.next()) return;
3078
3079         string const tmptok = lex.getString();
3080
3081         // check if tmptok is part of tex_babel in tex-defs.h
3082         if (!setLanguage(tmptok)) {
3083                 // Language tmptok was not found
3084                 language = default_language;
3085                 lyxerr << "Warning: Setting language `"
3086                        << tmptok << "' to `" << language->lang()
3087                        << "'." << endl;
3088         }
3089 }
3090
3091
3092 void BufferParams::readGraphicsDriver(Lexer & lex)
3093 {
3094         if (!lex.next())
3095                 return;
3096
3097         string const tmptok = lex.getString();
3098         // check if tmptok is part of tex_graphics in tex_defs.h
3099         int n = 0;
3100         while (true) {
3101                 string const test = tex_graphics[n++];
3102
3103                 if (test == tmptok) {
3104                         graphics_driver = tmptok;
3105                         break;
3106                 }
3107                 if (test.empty()) {
3108                         lex.printError(
3109                                 "Warning: graphics driver `$$Token' not recognized!\n"
3110                                 "         Setting graphics driver to `default'.\n");
3111                         graphics_driver = "default";
3112                         break;
3113                 }
3114         }
3115 }
3116
3117
3118 void BufferParams::readBullets(Lexer & lex)
3119 {
3120         if (!lex.next())
3121                 return;
3122
3123         int const index = lex.getInteger();
3124         lex.next();
3125         int temp_int = lex.getInteger();
3126         user_defined_bullet(index).setFont(temp_int);
3127         temp_bullet(index).setFont(temp_int);
3128         lex >> temp_int;
3129         user_defined_bullet(index).setCharacter(temp_int);
3130         temp_bullet(index).setCharacter(temp_int);
3131         lex >> temp_int;
3132         user_defined_bullet(index).setSize(temp_int);
3133         temp_bullet(index).setSize(temp_int);
3134 }
3135
3136
3137 void BufferParams::readBulletsLaTeX(Lexer & lex)
3138 {
3139         // The bullet class should be able to read this.
3140         if (!lex.next())
3141                 return;
3142         int const index = lex.getInteger();
3143         lex.next(true);
3144         docstring const temp_str = lex.getDocString();
3145
3146         user_defined_bullet(index).setText(temp_str);
3147         temp_bullet(index).setText(temp_str);
3148 }
3149
3150
3151 void BufferParams::readModules(Lexer & lex)
3152 {
3153         if (!lex.eatLine()) {
3154                 lyxerr << "Error (BufferParams::readModules):"
3155                                 "Unexpected end of input." << endl;
3156                 return;
3157         }
3158         while (true) {
3159                 string mod = lex.getString();
3160                 if (mod == "\\end_modules")
3161                         break;
3162                 addLayoutModule(mod);
3163                 lex.eatLine();
3164         }
3165 }
3166
3167
3168 void BufferParams::readRemovedModules(Lexer & lex)
3169 {
3170         if (!lex.eatLine()) {
3171                 lyxerr << "Error (BufferParams::readRemovedModules):"
3172                                 "Unexpected end of input." << endl;
3173                 return;
3174         }
3175         while (true) {
3176                 string mod = lex.getString();
3177                 if (mod == "\\end_removed_modules")
3178                         break;
3179                 removed_modules_.push_back(mod);
3180                 lex.eatLine();
3181         }
3182         // now we want to remove any removed modules that were previously
3183         // added. normally, that will be because default modules were added in
3184         // setBaseClass(), which gets called when \textclass is read at the
3185         // start of the read.
3186         for (auto const & rm : removed_modules_) {
3187                 LayoutModuleList::iterator const mit = layout_modules_.begin();
3188                 LayoutModuleList::iterator const men = layout_modules_.end();
3189                 LayoutModuleList::iterator found = find(mit, men, rm);
3190                 if (found == men)
3191                         continue;
3192                 layout_modules_.erase(found);
3193         }
3194 }
3195
3196
3197 void BufferParams::readIncludeonly(Lexer & lex)
3198 {
3199         if (!lex.eatLine()) {
3200                 lyxerr << "Error (BufferParams::readIncludeonly):"
3201                                 "Unexpected end of input." << endl;
3202                 return;
3203         }
3204         while (true) {
3205                 string child = lex.getString();
3206                 if (child == "\\end_includeonly")
3207                         break;
3208                 included_children_.push_back(child);
3209                 lex.eatLine();
3210         }
3211 }
3212
3213
3214 string BufferParams::paperSizeName(PapersizePurpose purpose, string const & psize) const
3215 {
3216         PAPER_SIZE ppsize = psize.empty() ? papersize : papersizetranslator().find(psize);
3217         switch (ppsize) {
3218         case PAPER_DEFAULT:
3219                 if (documentClass().pagesize() == "default")
3220                         // could be anything, so don't guess
3221                         return string();
3222                 return paperSizeName(purpose, documentClass().pagesize());
3223         case PAPER_CUSTOM: {
3224                 if (purpose == XDVI && !paperwidth.empty() &&
3225                     !paperheight.empty()) {
3226                         // heightxwidth<unit>
3227                         string first = paperwidth;
3228                         string second = paperheight;
3229                         if (orientation == ORIENTATION_LANDSCAPE)
3230                                 first.swap(second);
3231                         // cut off unit.
3232                         return first.erase(first.length() - 2)
3233                                 + "x" + second;
3234                 }
3235                 return string();
3236         }
3237         case PAPER_A0:
3238                 // dvips and dvipdfm do not know this
3239                 if (purpose == DVIPS || purpose == DVIPDFM)
3240                         return string();
3241                 return "a0";
3242         case PAPER_A1:
3243                 if (purpose == DVIPS || purpose == DVIPDFM)
3244                         return string();
3245                 return "a1";
3246         case PAPER_A2:
3247                 if (purpose == DVIPS || purpose == DVIPDFM)
3248                         return string();
3249                 return "a2";
3250         case PAPER_A3:
3251                 return "a3";
3252         case PAPER_A4:
3253                 return "a4";
3254         case PAPER_A5:
3255                 return "a5";
3256         case PAPER_A6:
3257                 if (purpose == DVIPS || purpose == DVIPDFM)
3258                         return string();
3259                 return "a6";
3260         case PAPER_B0:
3261                 if (purpose == DVIPS || purpose == DVIPDFM)
3262                         return string();
3263                 return "b0";
3264         case PAPER_B1:
3265                 if (purpose == DVIPS || purpose == DVIPDFM)
3266                         return string();
3267                 return "b1";
3268         case PAPER_B2:
3269                 if (purpose == DVIPS || purpose == DVIPDFM)
3270                         return string();
3271                 return "b2";
3272         case PAPER_B3:
3273                 if (purpose == DVIPS || purpose == DVIPDFM)
3274                         return string();
3275                 return "b3";
3276         case PAPER_B4:
3277                 // dvipdfm does not know this
3278                 if (purpose == DVIPDFM)
3279                         return string();
3280                 return "b4";
3281         case PAPER_B5:
3282                 if (purpose == DVIPDFM)
3283                         return string();
3284                 return "b5";
3285         case PAPER_B6:
3286                 if (purpose == DVIPS || purpose == DVIPDFM)
3287                         return string();
3288                 return "b6";
3289         case PAPER_C0:
3290                 if (purpose == DVIPS || purpose == DVIPDFM)
3291                         return string();
3292                 return "c0";
3293         case PAPER_C1:
3294                 if (purpose == DVIPS || purpose == DVIPDFM)
3295                         return string();
3296                 return "c1";
3297         case PAPER_C2:
3298                 if (purpose == DVIPS || purpose == DVIPDFM)
3299                         return string();
3300                 return "c2";
3301         case PAPER_C3:
3302                 if (purpose == DVIPS || purpose == DVIPDFM)
3303                         return string();
3304                 return "c3";
3305         case PAPER_C4:
3306                 if (purpose == DVIPS || purpose == DVIPDFM)
3307                         return string();
3308                 return "c4";
3309         case PAPER_C5:
3310                 if (purpose == DVIPS || purpose == DVIPDFM)
3311                         return string();
3312                 return "c5";
3313         case PAPER_C6:
3314                 if (purpose == DVIPS || purpose == DVIPDFM)
3315                         return string();
3316                 return "c6";
3317         case PAPER_JISB0:
3318                 if (purpose == DVIPS || purpose == DVIPDFM)
3319                         return string();
3320                 return "jisb0";
3321         case PAPER_JISB1:
3322                 if (purpose == DVIPS || purpose == DVIPDFM)
3323                         return string();
3324                 return "jisb1";
3325         case PAPER_JISB2:
3326                 if (purpose == DVIPS || purpose == DVIPDFM)
3327                         return string();
3328                 return "jisb2";
3329         case PAPER_JISB3:
3330                 if (purpose == DVIPS || purpose == DVIPDFM)
3331                         return string();
3332                 return "jisb3";
3333         case PAPER_JISB4:
3334                 if (purpose == DVIPS || purpose == DVIPDFM)
3335                         return string();
3336                 return "jisb4";
3337         case PAPER_JISB5:
3338                 if (purpose == DVIPS || purpose == DVIPDFM)
3339                         return string();
3340                 return "jisb5";
3341         case PAPER_JISB6:
3342                 if (purpose == DVIPS || purpose == DVIPDFM)
3343                         return string();
3344                 return "jisb6";
3345         case PAPER_USEXECUTIVE:
3346                 // dvipdfm does not know this
3347                 if (purpose == DVIPDFM)
3348                         return string();
3349                 return "foolscap";
3350         case PAPER_USLEGAL:
3351                 return "legal";
3352         case PAPER_USLETTER:
3353         default:
3354                 if (purpose == XDVI)
3355                         return "us";
3356                 return "letter";
3357         }
3358 }
3359
3360
3361 string const BufferParams::dvips_options() const
3362 {
3363         string result;
3364
3365         // If the class loads the geometry package, we do not know which
3366         // paper size is used, since we do not set it (bug 7013).
3367         // Therefore we must not specify any argument here.
3368         // dvips gets the correct paper size via DVI specials in this case
3369         // (if the class uses the geometry package correctly).
3370         if (documentClass().provides("geometry"))
3371                 return result;
3372
3373         if (use_geometry
3374             && papersize == PAPER_CUSTOM
3375             && !lyxrc.print_paper_dimension_flag.empty()
3376             && !paperwidth.empty()
3377             && !paperheight.empty()) {
3378                 // using a custom papersize
3379                 result = lyxrc.print_paper_dimension_flag;
3380                 result += ' ' + paperwidth;
3381                 result += ',' + paperheight;
3382         } else {
3383                 string const paper_option = paperSizeName(DVIPS);
3384                 if (!paper_option.empty() && (paper_option != "letter" ||
3385                     orientation != ORIENTATION_LANDSCAPE)) {
3386                         // dvips won't accept -t letter -t landscape.
3387                         // In all other cases, include the paper size
3388                         // explicitly.
3389                         result = lyxrc.print_paper_flag;
3390                         result += ' ' + paper_option;
3391                 }
3392         }
3393         if (orientation == ORIENTATION_LANDSCAPE &&
3394             papersize != PAPER_CUSTOM)
3395                 result += ' ' + lyxrc.print_landscape_flag;
3396         return result;
3397 }
3398
3399
3400 string const BufferParams::main_font_encoding() const
3401 {
3402         vector<string> const fencs = font_encodings();
3403         if (fencs.empty()) {
3404                 if (ascii_lowercase(language->fontenc(*this)) == "none")
3405                         return "none";
3406                 return "default";
3407         }
3408         return fencs.back();
3409 }
3410
3411
3412 vector<string> const BufferParams::font_encodings() const
3413 {
3414         string doc_fontenc = (fontenc == "auto") ? string() : fontenc;
3415
3416         vector<string> fontencs;
3417
3418         // "default" means "no explicit font encoding"
3419         if (doc_fontenc != "default") {
3420                 if (!doc_fontenc.empty())
3421                         // If we have a custom setting, we use only that!
3422                         return getVectorFromString(doc_fontenc);
3423                 string const lfe = language->fontenc(*this);
3424                 if (!lfe.empty()
3425                     && ascii_lowercase(language->fontenc(*this)) != "none") {
3426                         vector<string> fencs = getVectorFromString(lfe);
3427                         for (auto & fe : fencs) {
3428                                 if (find(fontencs.begin(), fontencs.end(), fe) == fontencs.end())
3429                                         fontencs.push_back(fe);
3430                         }
3431                 }
3432         }
3433
3434         return fontencs;
3435 }
3436
3437
3438 string BufferParams::babelCall(string const & lang_opts, bool const langoptions) const
3439 {
3440         // suppress the babel call if there is no BabelName defined
3441         // for the document language in the lib/languages file and if no
3442         // other languages are used (lang_opts is then empty)
3443         if (lang_opts.empty())
3444                 return string();
3445         // The prefs may require the languages to
3446         // be submitted to babel itself (not the class).
3447         if (langoptions)
3448                 return "\\usepackage[" + lang_opts + "]{babel}";
3449         return "\\usepackage{babel}";
3450 }
3451
3452
3453 docstring BufferParams::getGraphicsDriver(string const & package) const
3454 {
3455         docstring result;
3456
3457         if (package == "geometry") {
3458                 if (graphics_driver == "dvips"
3459                     || graphics_driver == "dvipdfm"
3460                     || graphics_driver == "pdftex"
3461                     || graphics_driver == "vtex")
3462                         result = from_ascii(graphics_driver);
3463                 else if (graphics_driver == "dvipdfmx")
3464                         result = from_ascii("dvipdfm");
3465         }
3466
3467         return result;
3468 }
3469
3470
3471 void BufferParams::writeEncodingPreamble(otexstream & os,
3472                                          LaTeXFeatures & features) const
3473 {
3474         // With no-TeX fonts we use utf8-plain without encoding package.
3475         if (useNonTeXFonts)
3476                 return;
3477
3478         string const doc_encoding = encoding().latexName();
3479         Encoding::Package const package = encoding().package();
3480         // (dvi)lualatex uses luainputenc rather than inputenc
3481         string const inputenc_package = 
3482                 (features.runparams().flavor == Flavor::LuaTeX
3483                  || features.runparams().flavor == Flavor::DviLuaTeX)
3484                 ? "luainputenc" : "inputenc";
3485
3486         if (inputenc == "auto-legacy") {
3487                 // The "japanese" babel language requires the pLaTeX engine
3488                 // which conflicts with "inputenc".
3489                 // See http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129680.html
3490                 if (!features.isRequired("japanese")
3491                     && !features.isProvided("inputenc")) {
3492                         if (package == Encoding::inputenc) {
3493                                 // Main language requires (lua)inputenc
3494                                 os << "\\usepackage[" << doc_encoding << "]{"
3495                                    << inputenc_package << "}\n";
3496                         } else {
3497                                 // We might have an additional language that requires inputenc
3498                                 set<string> encoding_set = features.getEncodingSet(doc_encoding);
3499                                 bool inputenc = false;
3500                                 for (auto const & enc : encoding_set) {
3501                                         if (encodings.fromLaTeXName(enc)
3502                                             && encodings.fromLaTeXName(enc)->package() == Encoding::inputenc) {
3503                                                 inputenc = true;
3504                                                 break;
3505                                         }
3506                                 }
3507                                 if (inputenc)
3508                                         // load (lua)inputenc without options
3509                                         // (the encoding is loaded later)
3510                                         os << "\\usepackage{" << inputenc_package << "}\n";
3511                         }
3512                 }
3513         } else if (inputenc != "auto-legacy-plain") {
3514                 switch (package) {
3515                 case Encoding::none:
3516                 case Encoding::CJK:
3517                 case Encoding::japanese:
3518                         if (encoding().iconvName() != "UTF-8"
3519                             && !features.runparams().isFullUnicode()
3520                             && features.isAvailableAtLeastFrom("LaTeX", 2018, 4))
3521                                 // don't default to [utf8]{inputenc} with LaTeX >= 2018/04
3522                                 os << "\\UseRawInputEncoding\n";
3523                         break;
3524                 case Encoding::inputenc:
3525                         // do not load inputenc if japanese is used
3526                         // or if the class provides inputenc
3527                         if (features.isRequired("japanese")
3528                             || features.isProvided("inputenc"))
3529                                 break;
3530                         // The 2022 release of ucs.sty uses the default utf8
3531                         // inputenc encoding with 'utf8x' inputenc if the ucs
3532                         // package is not loaded before inputenc.
3533                         // This breaks existing documents that use utf8x
3534                         // and also makes utf8x redundant.
3535                         // Thus we load ucs.sty in order to keep functionality
3536                         // that would otherwise be silently dropped.
3537                         if (doc_encoding == "utf8x"
3538                             && features.isAvailableAtLeastFrom("ucs", 2022, 8, 7)
3539                             && !features.isProvided("ucs"))
3540                                 os << "\\usepackage{ucs}\n";
3541                         os << "\\usepackage[" << doc_encoding << "]{"
3542                            << inputenc_package << "}\n";
3543                         break;
3544                 }
3545         }
3546         if ((inputenc == "auto-legacy-plain" || features.isRequired("japanese"))
3547             && features.isAvailableAtLeastFrom("LaTeX", 2018, 4))
3548                 // don't default to [utf8]{inputenc} with LaTeX >= 2018/04
3549                 os << "\\UseRawInputEncoding\n";
3550 }
3551
3552
3553 string const BufferParams::parseFontName(string const & name) const
3554 {
3555         string mangled = name;
3556         size_t const idx = mangled.find('[');
3557         if (idx == string::npos || idx == 0)
3558                 return mangled;
3559         else
3560                 return mangled.substr(0, idx - 1);
3561 }
3562
3563
3564 string const BufferParams::loadFonts(LaTeXFeatures & features) const
3565 {
3566         if (fontsRoman() == "default" && fontsSans() == "default"
3567             && fontsTypewriter() == "default"
3568             && (fontsMath() == "default" || fontsMath() == "auto"))
3569                 //nothing to do
3570                 return string();
3571
3572         ostringstream os;
3573
3574         /* Fontspec (XeTeX, LuaTeX): we provide GUI support for oldstyle
3575          * numbers (Numbers=OldStyle) and sf/tt scaling. The Ligatures=TeX/
3576          * Mapping=tex-text option assures TeX ligatures (such as "--")
3577          * are resolved. Note that tt does not use these ligatures.
3578          * TODO:
3579          *    -- add more GUI options?
3580          *    -- add more fonts (fonts for other scripts)
3581          *    -- if there's a way to find out if a font really supports
3582          *       OldStyle, enable/disable the widget accordingly.
3583         */
3584         if (useNonTeXFonts && features.isAvailable("fontspec")) {
3585                 // "Mapping=tex-text" and "Ligatures=TeX" are equivalent.
3586                 // However, until v.2 (2010/07/11) fontspec only knew
3587                 // Mapping=tex-text (for XeTeX only); then "Ligatures=TeX"
3588                 // was introduced for both XeTeX and LuaTeX (LuaTeX
3589                 // didn't understand "Mapping=tex-text", while XeTeX
3590                 // understood both. With most recent versions, both
3591                 // variants are understood by both engines. However,
3592                 // we want to provide support for at least TeXLive 2009
3593                 // (for XeTeX; LuaTeX is only supported as of v.2)
3594                 // As of 2017/11/03, Babel has its own higher-level
3595                 // interface on top of fontspec that is to be used.
3596                 bool const babelfonts = features.useBabel()
3597                                 && features.isAvailableAtLeastFrom("babel", 2017, 11, 3);
3598                 string const texmapping =
3599                         (features.runparams().flavor == Flavor::XeTeX) ?
3600                         "Mapping=tex-text" : "Ligatures=TeX";
3601                 if (fontsRoman() != "default") {
3602                         if (babelfonts)
3603                                 os << "\\babelfont{rm}[";
3604                         else
3605                                 os << "\\setmainfont[";
3606                         os << texmapping;
3607                         if (fonts_roman_osf)
3608                                 os << ",Numbers=OldStyle";
3609                         if (!font_roman_opts.empty())
3610                                 os << ',' << font_roman_opts;
3611                         os << "]{" << parseFontName(fontsRoman()) << "}\n";
3612                 }
3613                 if (fontsSans() != "default") {
3614                         string const sans = parseFontName(fontsSans());
3615                         if (fontsSansScale() != 100) {
3616                                 if (babelfonts)
3617                                         os << "\\babelfont{sf}";
3618                                 else
3619                                         os << "\\setsansfont";
3620                                 os << "[Scale="
3621                                    << float(fontsSansScale()) / 100 << ',';
3622                                 if (fonts_sans_osf)
3623                                         os << "Numbers=OldStyle,";
3624                                 os << texmapping;
3625                                 if (!font_sans_opts.empty())
3626                                         os << ',' << font_sans_opts;
3627                                 os << "]{" << sans << "}\n";
3628                         } else {
3629                                 if (babelfonts)
3630                                         os << "\\babelfont{sf}[";
3631                                 else
3632                                         os << "\\setsansfont[";
3633                                 if (fonts_sans_osf)
3634                                         os << "Numbers=OldStyle,";
3635                                 os << texmapping;
3636                                 if (!font_sans_opts.empty())
3637                                         os << ',' << font_sans_opts;
3638                                 os << "]{" << sans << "}\n";
3639                         }
3640                 }
3641                 if (fontsTypewriter() != "default") {
3642                         string const mono = parseFontName(fontsTypewriter());
3643                         if (fontsTypewriterScale() != 100) {
3644                                 if (babelfonts)
3645                                         os << "\\babelfont{tt}";
3646                                 else
3647                                         os << "\\setmonofont";
3648                                 os << "[Scale="
3649                                    << float(fontsTypewriterScale()) / 100;
3650                                 if (fonts_typewriter_osf)
3651                                         os << ",Numbers=OldStyle";
3652                                 if (!font_typewriter_opts.empty())
3653                                         os << ',' << font_typewriter_opts;
3654                                 os << "]{"
3655                                    << mono << "}\n";
3656                         } else {
3657                                 if (babelfonts)
3658                                         os << "\\babelfont{tt}";
3659                                 else
3660                                         os << "\\setmonofont";
3661                                 if (!font_typewriter_opts.empty() || fonts_typewriter_osf) {
3662                                         os << '[';
3663                                         if (fonts_typewriter_osf)
3664                                                 os << "Numbers=OldStyle";
3665                                         if (!font_typewriter_opts.empty()) {
3666                                                 if (fonts_typewriter_osf)
3667                                                         os << ',';
3668                                                 os << font_typewriter_opts;
3669                                         }
3670                                         os << ']';
3671                                 }
3672                                 os << '{' << mono << "}\n";
3673                         }
3674                 }
3675                 return os.str();
3676         }
3677
3678         // Tex Fonts
3679         bool const ot1 = (features.runparams().main_fontenc == "default"
3680                           || features.runparams().main_fontenc == "OT1");
3681         bool const dryrun = features.runparams().dryrun;
3682         bool const complete = (fontsSans() == "default" && fontsTypewriter() == "default");
3683         bool const nomath = (fontsMath() != "auto");
3684
3685         // ROMAN FONTS
3686         os << theLaTeXFonts().getLaTeXFont(from_ascii(fontsRoman())).getLaTeXCode(
3687                 dryrun, ot1, complete, fonts_expert_sc, fonts_roman_osf,
3688                 nomath, font_roman_opts);
3689
3690         // SANS SERIF
3691         os << theLaTeXFonts().getLaTeXFont(from_ascii(fontsSans())).getLaTeXCode(
3692                 dryrun, ot1, complete, fonts_expert_sc, fonts_sans_osf,
3693                 nomath, font_sans_opts, fontsSansScale());
3694
3695         // MONOSPACED/TYPEWRITER
3696         os << theLaTeXFonts().getLaTeXFont(from_ascii(fontsTypewriter())).getLaTeXCode(
3697                 dryrun, ot1, complete, fonts_expert_sc, fonts_typewriter_osf,
3698                 nomath, font_typewriter_opts, fontsTypewriterScale());
3699
3700         // MATH
3701         os << theLaTeXFonts().getLaTeXFont(from_ascii(fontsMath())).getLaTeXCode(
3702                 dryrun, ot1, complete, fonts_expert_sc, fonts_roman_osf,
3703                 nomath);
3704
3705         return os.str();
3706 }
3707
3708
3709 Encoding const & BufferParams::encoding() const
3710 {
3711         // Main encoding for LaTeX output.
3712         if (useNonTeXFonts)
3713                 return *(encodings.fromLyXName("utf8-plain"));
3714         if (inputenc == "auto-legacy" || inputenc == "auto-legacy-plain")
3715                 return *language->encoding();
3716         if (inputenc == "utf8" && language->lang() == "japanese")
3717                 return *(encodings.fromLyXName("utf8-platex"));
3718         Encoding const * const enc = encodings.fromLyXName(inputenc);
3719         if (enc)
3720                 return *enc;
3721         LYXERR0("Unknown inputenc value `" << inputenc
3722                << "'. Using `auto' instead.");
3723         return *language->encoding();
3724 }
3725
3726
3727 string const & BufferParams::defaultBiblioStyle() const
3728 {
3729         if (!biblio_style.empty())
3730                 return biblio_style;
3731
3732         map<string, string> const & bs = documentClass().defaultBiblioStyle();
3733         auto cit = bs.find(theCiteEnginesList.getTypeAsString(citeEngineType()));
3734         if (cit != bs.end())
3735                 return cit->second;
3736         else
3737                 return empty_string();
3738 }
3739
3740
3741 bool BufferParams::fullAuthorList() const
3742 {
3743         return documentClass().fullAuthorList();
3744 }
3745
3746
3747 string BufferParams::getCiteAlias(string const & s) const
3748 {
3749         vector<string> commands =
3750                 documentClass().citeCommands(citeEngineType());
3751         // If it is a real command, don't treat it as an alias
3752         if (find(commands.begin(), commands.end(), s) != commands.end())
3753                 return string();
3754         map<string,string> aliases = documentClass().citeCommandAliases();
3755         if (aliases.find(s) != aliases.end())
3756                 return aliases[s];
3757         return string();
3758 }
3759
3760
3761 vector<string> BufferParams::citeCommands() const
3762 {
3763         static CitationStyle const default_style;
3764         vector<string> commands =
3765                 documentClass().citeCommands(citeEngineType());
3766         if (commands.empty())
3767                 commands.push_back(default_style.name);
3768         return commands;
3769 }
3770
3771
3772 vector<CitationStyle> BufferParams::citeStyles() const
3773 {
3774         static CitationStyle const default_style;
3775         vector<CitationStyle> styles =
3776                 documentClass().citeStyles(citeEngineType());
3777         if (styles.empty())
3778                 styles.push_back(default_style);
3779         return styles;
3780 }
3781
3782
3783 string const BufferParams::getBibtexCommand(string const cmd, bool const warn) const
3784 {
3785         // split from options
3786         string command_in;
3787         split(cmd, command_in, ' ');
3788
3789         // Look if the requested command is available. If so, use that.
3790         for (auto const & alts : lyxrc.bibtex_alternatives) {
3791                 string command_prov;
3792                 split(alts, command_prov, ' ');
3793                 if (command_in == command_prov)
3794                         return cmd;
3795         }
3796
3797         // If not, find the most suitable fallback for the current cite framework,
3798         // and warn. Note that we omit options in any such case.
3799         string fallback;
3800         if (useBiblatex()) {
3801                 // For Biblatex, we prefer biber (also for Japanese)
3802                 // and try to fall back to bibtex8
3803                 if (lyxrc.bibtex_alternatives.find("biber") != lyxrc.bibtex_alternatives.end())
3804                         fallback = "biber";
3805                 else if (lyxrc.bibtex_alternatives.find("bibtex8") != lyxrc.bibtex_alternatives.end())
3806                         fallback = "bibtex8";
3807         }
3808         // For classic BibTeX and as last resort for biblatex, try bibtex
3809         if (fallback.empty()) {
3810                 if (lyxrc.bibtex_alternatives.find("bibtex") != lyxrc.bibtex_alternatives.end())
3811                         fallback = "bibtex";
3812         }
3813
3814         if (!warn)
3815                 return fallback;
3816
3817         if (fallback.empty()) {
3818                 frontend::Alert::warning(
3819                         _("No bibliography processor found!"),
3820                         support::bformat(
3821                           _("The bibliography processor requested by this document "
3822                             "(%1$s) is not available and no appropriate "
3823                             "alternative has been found. "
3824                             "No bibliography and references will be generated.\n"
3825                             "Please fix your installation!"),
3826                           from_utf8(cmd)));
3827         } else {
3828                 frontend::Alert::warning(
3829                         _("Requested bibliography processor not found!"),
3830                         support::bformat(
3831                           _("The bibliography processor requested by this document "
3832                             "(%1$s) is not available. "
3833                             "As a fallback, '%2$s' will be used, options are omitted. "
3834                             "This might result in errors or unwanted changes in "
3835                             "the bibliography. Please check carefully!\n"
3836                             "It is suggested to install the missing processor."),
3837                           from_utf8(cmd), from_utf8(fallback)));
3838         }
3839         return fallback;
3840 }
3841
3842
3843 string const BufferParams::bibtexCommand(bool const warn) const
3844 {
3845         // Return document-specific setting if available
3846         if (bibtex_command != "default")
3847                 return getBibtexCommand(bibtex_command, warn);
3848
3849         // If we have "default" in document settings, consult the prefs
3850         // 1. Japanese (uses a specific processor)
3851         if (encoding().package() == Encoding::japanese) {
3852                 if (lyxrc.jbibtex_command != "automatic")
3853                         // Return the specified program, if "automatic" is not set
3854                         return lyxrc.jbibtex_command;
3855                 else if (!useBiblatex()) {
3856                         // With classic BibTeX, return pbibtex, jbibtex, bibtex
3857                         if (lyxrc.jbibtex_alternatives.find("pbibtex") != lyxrc.jbibtex_alternatives.end())
3858                                 return "pbibtex";
3859                         if (lyxrc.jbibtex_alternatives.find("jbibtex") != lyxrc.jbibtex_alternatives.end())
3860                                 return "jbibtex";
3861                         return "bibtex";
3862                 }
3863         }
3864         // 2. All other languages
3865         else if (lyxrc.bibtex_command != "automatic")
3866                 // Return the specified program, if "automatic" is not set
3867                 return getBibtexCommand(lyxrc.bibtex_command, warn);
3868
3869         // 3. Automatic: find the most suitable for the current cite framework
3870         if (useBiblatex()) {
3871                 // For Biblatex, we prefer biber (also for Japanese)
3872                 // and fall back to bibtex8 and, as last resort, bibtex
3873                 if (lyxrc.bibtex_alternatives.find("biber") != lyxrc.bibtex_alternatives.end())
3874                         return "biber";
3875                 else if (lyxrc.bibtex_alternatives.find("bibtex8") != lyxrc.bibtex_alternatives.end())
3876                         return "bibtex8";
3877         }
3878         return "bibtex";
3879 }
3880
3881
3882 bool BufferParams::useBiblatex() const
3883 {
3884         return theCiteEnginesList[citeEngine()]->getCiteFramework() == "biblatex";
3885 }
3886
3887
3888 void BufferParams::invalidateConverterCache() const
3889 {
3890         pimpl_->isExportCacheValid = false;
3891         pimpl_->isViewCacheValid = false;
3892 }
3893
3894
3895 // We shouldn't need to reset the params here, since anything
3896 // we need will be recopied.
3897 void BufferParams::copyForAdvFR(const BufferParams & bp)
3898 {
3899         string const & lang = bp.language->lang();
3900         setLanguage(lang);
3901         quotes_style = bp.quotes_style;
3902         layout_modules_ = bp.layout_modules_;
3903         string const & doc_class = bp.documentClass().name();
3904         setBaseClass(doc_class);
3905 }
3906
3907
3908 void BufferParams::setBibFileEncoding(string const & file, string const & enc)
3909 {
3910         bib_encodings[file] = enc;
3911 }
3912
3913
3914 string const BufferParams::bibFileEncoding(string const & file) const
3915 {
3916         if (bib_encodings.find(file) == bib_encodings.end())
3917                 return string();
3918         return bib_encodings.find(file)->second;
3919 }
3920
3921
3922 BufferParams const & defaultBufferParams()
3923 {
3924         static BufferParams default_params;
3925         return default_params;
3926 }
3927
3928
3929 } // namespace lyx