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