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