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