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