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