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