]> git.lyx.org Git - lyx.git/blob - src/BufferParams.cpp
Update toolbar and properly reset focus when find widget is closed (#12396)
[lyx.git] / src / BufferParams.cpp
1 /**
2  * \file BufferParams.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alfredo Braunstein
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author John Levon
10  * \author André Pönitz
11  * \author Martin Vermeer
12  *
13  * Full author contact details are available in file CREDITS.
14  */
15
16 #include <config.h>
17
18 #include "BufferParams.h"
19
20 #include "Author.h"
21 #include "LayoutFile.h"
22 #include "BranchList.h"
23 #include "Buffer.h"
24 #include "Bullet.h"
25 #include "CiteEnginesList.h"
26 #include "Color.h"
27 #include "ColorSet.h"
28 #include "Converter.h"
29 #include "Encoding.h"
30 #include "Format.h"
31 #include "IndicesList.h"
32 #include "Language.h"
33 #include "LaTeXFeatures.h"
34 #include "LaTeXFonts.h"
35 #include "Font.h"
36 #include "Lexer.h"
37 #include "LyXRC.h"
38 #include "OutputParams.h"
39 #include "Spacing.h"
40 #include "texstream.h"
41 #include "TexRow.h"
42 #include "VSpace.h"
43 #include "PDFOptions.h"
44
45 #include "frontends/alert.h"
46
47 #include "insets/InsetListingsParams.h"
48 #include "insets/InsetQuotes.h"
49
50 #include "support/convert.h"
51 #include "support/debug.h"
52 #include "support/FileName.h"
53 #include "support/filetools.h"
54 #include "support/gettext.h"
55 #include "support/Length.h"
56 #include "support/Messages.h"
57 #include "support/mutex.h"
58 #include "support/Package.h"
59 #include "support/Translator.h"
60 #include "support/lstrings.h"
61
62 #include <algorithm>
63 #include <sstream>
64
65 using namespace std;
66 using namespace lyx::support;
67
68
69 static char const * const string_paragraph_separation[] = {
70         "indent", "skip", ""
71 };
72
73
74 static char const * const string_quotes_style[] = {
75         "english", "swedish", "german", "polish", "swiss", "danish", "plain",
76         "british", "swedishg", "french", "frenchin", "russian", "cjk", "cjkangle",
77         "hungarian", ""
78 };
79
80
81 static char const * const string_papersize[] = {
82         "default", "custom", "letter", "legal", "executive",
83         "a0", "a1", "a2", "a3", "a4", "a5", "a6",
84         "b0", "b1", "b2", "b3", "b4", "b5", "b6",
85         "c0", "c1", "c2", "c3", "c4", "c5", "c6",
86         "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", ""
87 };
88
89
90 static char const * const string_papersize_geometry[] = {
91         "default", "custom", "letterpaper", "legalpaper", "executivepaper",
92         "a0paper", "a1paper", "a2paper", "a3paper", "a4paper", "a5paper", "a6paper",
93         "b0paper", "b1paper", "b2paper", "b3paper", "b4paper", "b5paper", "b6paper",
94         "c0paper", "c1paper", "c2paper", "c3paper", "c4paper", "c5paper", "c6paper",
95         "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", ""
96 };
97
98
99 static char const * const string_orientation[] = {
100         "portrait", "landscape", ""
101 };
102
103
104 static char const * const tex_graphics[] = {
105         "default", "dvialw", "dvilaser", "dvipdf", "dvipdfm", "dvipdfmx",
106         "dvips", "dvipsone", "dvitops", "dviwin", "dviwindo", "dvi2ps", "emtex",
107         "ln", "oztex", "pctexhp", "pctexps", "pctexwin", "pctex32", "pdftex",
108         "psprint", "pubps", "tcidvi", "textures", "truetex", "vtex", "xdvi",
109         "xetex", "none", ""
110 };
111
112
113 namespace lyx {
114
115 // Local translators
116 namespace {
117
118 // Paragraph separation
119 typedef Translator<string, BufferParams::ParagraphSeparation> ParSepTranslator;
120
121
122 ParSepTranslator const init_parseptranslator()
123 {
124         ParSepTranslator translator
125                 (string_paragraph_separation[0], BufferParams::ParagraphIndentSeparation);
126         translator.addPair(string_paragraph_separation[1], BufferParams::ParagraphSkipSeparation);
127         return translator;
128 }
129
130
131 ParSepTranslator const & parseptranslator()
132 {
133         static ParSepTranslator const translator =
134                 init_parseptranslator();
135         return translator;
136 }
137
138
139 // Quotes style
140 typedef Translator<string, QuoteStyle> QuotesStyleTranslator;
141
142
143 QuotesStyleTranslator const init_quotesstyletranslator()
144 {
145         QuotesStyleTranslator translator
146                 (string_quotes_style[0], QuoteStyle::English);
147         translator.addPair(string_quotes_style[1], QuoteStyle::Swedish);
148         translator.addPair(string_quotes_style[2], QuoteStyle::German);
149         translator.addPair(string_quotes_style[3], QuoteStyle::Polish);
150         translator.addPair(string_quotes_style[4], QuoteStyle::Swiss);
151         translator.addPair(string_quotes_style[5], QuoteStyle::Danish);
152         translator.addPair(string_quotes_style[6], QuoteStyle::Plain);
153         translator.addPair(string_quotes_style[7], QuoteStyle::British);
154         translator.addPair(string_quotes_style[8], QuoteStyle::SwedishG);
155         translator.addPair(string_quotes_style[9], QuoteStyle::French);
156         translator.addPair(string_quotes_style[10], QuoteStyle::FrenchIN);
157         translator.addPair(string_quotes_style[11], QuoteStyle::Russian);
158         translator.addPair(string_quotes_style[12], QuoteStyle::CJK);
159         translator.addPair(string_quotes_style[13], QuoteStyle::CJKAngle);
160         translator.addPair(string_quotes_style[14], QuoteStyle::Hungarian);
161         return translator;
162 }
163
164
165 QuotesStyleTranslator const & quotesstyletranslator()
166 {
167         static QuotesStyleTranslator const translator =
168                 init_quotesstyletranslator();
169         return translator;
170 }
171
172
173 // Paper size
174 typedef Translator<string, PAPER_SIZE> PaperSizeTranslator;
175
176
177 static PaperSizeTranslator initPaperSizeTranslator()
178 {
179         PaperSizeTranslator translator(string_papersize[0], PAPER_DEFAULT);
180         translator.addPair(string_papersize[1], PAPER_CUSTOM);
181         translator.addPair(string_papersize[2], PAPER_USLETTER);
182         translator.addPair(string_papersize[3], PAPER_USLEGAL);
183         translator.addPair(string_papersize[4], PAPER_USEXECUTIVE);
184         translator.addPair(string_papersize[5], PAPER_A0);
185         translator.addPair(string_papersize[6], PAPER_A1);
186         translator.addPair(string_papersize[7], PAPER_A2);
187         translator.addPair(string_papersize[8], PAPER_A3);
188         translator.addPair(string_papersize[9], PAPER_A4);
189         translator.addPair(string_papersize[10], PAPER_A5);
190         translator.addPair(string_papersize[11], PAPER_A6);
191         translator.addPair(string_papersize[12], PAPER_B0);
192         translator.addPair(string_papersize[13], PAPER_B1);
193         translator.addPair(string_papersize[14], PAPER_B2);
194         translator.addPair(string_papersize[15], PAPER_B3);
195         translator.addPair(string_papersize[16], PAPER_B4);
196         translator.addPair(string_papersize[17], PAPER_B5);
197         translator.addPair(string_papersize[18], PAPER_B6);
198         translator.addPair(string_papersize[19], PAPER_C0);
199         translator.addPair(string_papersize[20], PAPER_C1);
200         translator.addPair(string_papersize[21], PAPER_C2);
201         translator.addPair(string_papersize[22], PAPER_C3);
202         translator.addPair(string_papersize[23], PAPER_C4);
203         translator.addPair(string_papersize[24], PAPER_C5);
204         translator.addPair(string_papersize[25], PAPER_C6);
205         translator.addPair(string_papersize[26], PAPER_JISB0);
206         translator.addPair(string_papersize[27], PAPER_JISB1);
207         translator.addPair(string_papersize[28], PAPER_JISB2);
208         translator.addPair(string_papersize[29], PAPER_JISB3);
209         translator.addPair(string_papersize[30], PAPER_JISB4);
210         translator.addPair(string_papersize[31], PAPER_JISB5);
211         translator.addPair(string_papersize[32], PAPER_JISB6);
212         return translator;
213 }
214
215
216 PaperSizeTranslator const & papersizetranslator()
217 {
218         static PaperSizeTranslator const translator =
219                 initPaperSizeTranslator();
220         return translator;
221 }
222
223
224 // Paper orientation
225 typedef Translator<string, PAPER_ORIENTATION> PaperOrientationTranslator;
226
227
228 PaperOrientationTranslator const init_paperorientationtranslator()
229 {
230         PaperOrientationTranslator translator(string_orientation[0], ORIENTATION_PORTRAIT);
231         translator.addPair(string_orientation[1], ORIENTATION_LANDSCAPE);
232         return translator;
233 }
234
235
236 PaperOrientationTranslator const & paperorientationtranslator()
237 {
238         static PaperOrientationTranslator const translator =
239             init_paperorientationtranslator();
240         return translator;
241 }
242
243
244 // Page sides
245 typedef Translator<int, PageSides> SidesTranslator;
246
247
248 SidesTranslator const init_sidestranslator()
249 {
250         SidesTranslator translator(1, OneSide);
251         translator.addPair(2, TwoSides);
252         return translator;
253 }
254
255
256 SidesTranslator const & sidestranslator()
257 {
258         static SidesTranslator const translator = init_sidestranslator();
259         return translator;
260 }
261
262
263 // LaTeX packages
264 typedef Translator<int, BufferParams::Package> PackageTranslator;
265
266
267 PackageTranslator const init_packagetranslator()
268 {
269         PackageTranslator translator(0, BufferParams::package_off);
270         translator.addPair(1, BufferParams::package_auto);
271         translator.addPair(2, BufferParams::package_on);
272         return translator;
273 }
274
275
276 PackageTranslator const & packagetranslator()
277 {
278         static PackageTranslator const translator =
279                 init_packagetranslator();
280         return translator;
281 }
282
283
284 // Spacing
285 typedef Translator<string, Spacing::Space> SpaceTranslator;
286
287
288 SpaceTranslator const init_spacetranslator()
289 {
290         SpaceTranslator translator("default", Spacing::Default);
291         translator.addPair("single", Spacing::Single);
292         translator.addPair("onehalf", Spacing::Onehalf);
293         translator.addPair("double", Spacing::Double);
294         translator.addPair("other", Spacing::Other);
295         return translator;
296 }
297
298
299 SpaceTranslator const & spacetranslator()
300 {
301         static SpaceTranslator const translator = init_spacetranslator();
302         return translator;
303 }
304
305
306 bool inSystemDir(FileName const & document_dir, string & system_dir)
307 {
308         // A document is assumed to be in a system LyX directory (not
309         // necessarily the system directory of the running instance)
310         // if both "configure.py" and "chkconfig.ltx" are found in
311         // either document_dir/../ or document_dir/../../.
312         // If true, the system directory path is returned in system_dir
313         // with a trailing path separator.
314
315         string const msg = "Checking whether document is in a system dir...";
316
317         string dir = document_dir.absFileName();
318
319         for (int i = 0; i < 3; ++i) {
320                 dir = addPath(dir, "..");
321                 if (!fileSearch(dir, "configure.py").empty() &&
322                     !fileSearch(dir, "chkconfig.ltx").empty()) {
323                         LYXERR(Debug::FILES, msg << " yes");
324                         system_dir = addPath(FileName(dir).realPath(), "");
325                         return true;
326                 }
327         }
328
329         LYXERR(Debug::FILES, msg << " no");
330         system_dir = string();
331         return false;
332 }
333
334 } // namespace
335
336
337 class BufferParams::Impl
338 {
339 public:
340         Impl();
341
342         AuthorList authorlist;
343         BranchList branchlist;
344         WordLangTable spellignore;
345         Bullet temp_bullets[4];
346         Bullet user_defined_bullets[4];
347         IndicesList indiceslist;
348         Spacing spacing;
349         Length parindent;
350         Length mathindent;
351         /** This is the amount of space used for paragraph_separation "skip",
352          * and for detached paragraphs in "indented" documents.
353          */
354         VSpace defskip;
355         PDFOptions pdfoptions;
356         LayoutFileIndex baseClass_;
357         FormatList exportableFormatList;
358         FormatList viewableFormatList;
359         bool isViewCacheValid;
360         bool isExportCacheValid;
361 };
362
363
364 BufferParams::Impl::Impl()
365         : defskip(VSpace::MEDSKIP), baseClass_(string("")),
366           isViewCacheValid(false), isExportCacheValid(false)
367 {
368         // set initial author
369         // FIXME UNICODE
370         authorlist.record(Author(from_utf8(lyxrc.user_name),
371                                  from_utf8(lyxrc.user_email),
372                                  from_utf8(lyxrc.user_initials)));
373 }
374
375
376 BufferParams::Impl *
377 BufferParams::MemoryTraits::clone(BufferParams::Impl const * ptr)
378 {
379         LBUFERR(ptr);
380         return new BufferParams::Impl(*ptr);
381 }
382
383
384 void BufferParams::MemoryTraits::destroy(BufferParams::Impl * ptr)
385 {
386         delete ptr;
387 }
388
389
390 BufferParams::BufferParams()
391         : pimpl_(new Impl)
392 {
393         setBaseClass(defaultBaseclass());
394         cite_engine_ = "basic";
395         cite_engine_type_ = ENGINE_TYPE_DEFAULT;
396         makeDocumentClass();
397         paragraph_separation = ParagraphIndentSeparation;
398         is_math_indent = false;
399         math_numbering_side = DEFAULT;
400         quotes_style = QuoteStyle::English;
401         dynamic_quotes = false;
402         fontsize = "default";
403
404         /*  PaperLayout */
405         papersize = PAPER_DEFAULT;
406         orientation = ORIENTATION_PORTRAIT;
407         use_geometry = false;
408         biblio_style = string();
409         use_bibtopic = false;
410         multibib = string();
411         use_indices = false;
412         save_transient_properties = true;
413         track_changes = false;
414         output_changes = false;
415         change_bars = false;
416         postpone_fragile_content = true;
417         use_default_options = true;
418         maintain_unincluded_children = CM_None;
419         secnumdepth = 3;
420         tocdepth = 3;
421         language = default_language;
422         fontenc = "auto";
423         fonts_roman[0] = "default";
424         fonts_roman[1] = "default";
425         fonts_sans[0] = "default";
426         fonts_sans[1] = "default";
427         fonts_typewriter[0] = "default";
428         fonts_typewriter[1] = "default";
429         fonts_math[0] = "auto";
430         fonts_math[1] = "auto";
431         fonts_default_family = "default";
432         useNonTeXFonts = false;
433         use_microtype = false;
434         use_dash_ligatures = true;
435         fonts_expert_sc = false;
436         fonts_roman_osf = false;
437         fonts_sans_osf = false;
438         fonts_typewriter_osf = false;
439         fonts_sans_scale[0] = 100;
440         fonts_sans_scale[1] = 100;
441         fonts_typewriter_scale[0] = 100;
442         fonts_typewriter_scale[1] = 100;
443         inputenc = "utf8";
444         lang_package = "default";
445         graphics_driver = "default";
446         default_output_format = "default";
447         bibtex_command = "default";
448         index_command = "default";
449         sides = OneSide;
450         columns = 1;
451         listings_params = string();
452         pagestyle = "default";
453         tablestyle = "default";
454         float_alignment = "class";
455         float_placement = "class";
456         suppress_date = false;
457         justification = true;
458         // no color is the default (white)
459         backgroundcolor = lyx::rgbFromHexName("#ffffff");
460         isbackgroundcolor = false;
461         // no color is the default (black)
462         fontcolor = lyx::rgbFromHexName("#000000");
463         isfontcolor = false;
464         // light gray is the default font color for greyed-out notes
465         notefontcolor = lyx::rgbFromHexName("#cccccc");
466         isnotefontcolor = false;
467         boxbgcolor = lyx::rgbFromHexName("#ff0000");
468         isboxbgcolor = false;
469         compressed = lyxrc.save_compressed;
470         for (int iter = 0; iter < 4; ++iter) {
471                 user_defined_bullet(iter) = ITEMIZE_DEFAULTS[iter];
472                 temp_bullet(iter) = ITEMIZE_DEFAULTS[iter];
473         }
474         // default index
475         indiceslist().addDefault(B_("Index"));
476         html_be_strict = false;
477         html_math_output = MathML;
478         html_math_img_scale = 1.0;
479         html_css_as_file = false;
480         docbook_table_output = HTMLTable;
481         docbook_mathml_prefix = MPrefix;
482         display_pixel_ratio = 1.0;
483
484         shell_escape = false;
485         output_sync = false;
486         use_refstyle = true;
487         use_minted = false;
488         use_lineno = false;
489
490         // map current author
491         author_map_[pimpl_->authorlist.get(0).bufferId()] = 0;
492 }
493
494
495 docstring BufferParams::B_(string const & l10n) const
496 {
497         LASSERT(language, return from_utf8(l10n));
498         return getMessages(language->code()).get(l10n);
499 }
500
501
502 BufferParams::Package BufferParams::use_package(std::string const & p) const
503 {
504         PackageMap::const_iterator it = use_packages.find(p);
505         if (it == use_packages.end())
506                 return package_auto;
507         return it->second;
508 }
509
510
511 void BufferParams::use_package(std::string const & p, BufferParams::Package u)
512 {
513         use_packages[p] = u;
514 }
515
516
517 map<string, string> const & BufferParams::auto_packages()
518 {
519         static map<string, string> packages;
520         if (packages.empty()) {
521                 // We could have a race condition here that two threads
522                 // discover an empty map at the same time and want to fill
523                 // it, but that is no problem, since the same contents is
524                 // filled in twice then. Having the locker inside the
525                 // packages.empty() condition has the advantage that we
526                 // don't need the mutex overhead for simple reading.
527                 static Mutex mutex;
528                 Mutex::Locker locker(&mutex);
529                 // adding a package here implies a file format change!
530                 packages["amsmath"] =
531                         N_("The LaTeX package amsmath is only used if AMS formula types or symbols from the AMS math toolbars are inserted into formulas");
532                 packages["amssymb"] =
533                         N_("The LaTeX package amssymb is only used if symbols from the AMS math toolbars are inserted into formulas");
534                 packages["cancel"] =
535                         N_("The LaTeX package cancel is only used if \\cancel commands are used in formulas");
536                 packages["esint"] =
537                         N_("The LaTeX package esint is only used if special integral symbols are inserted into formulas");
538                 packages["mathdots"] =
539                         N_("The LaTeX package mathdots is only used if the command \\iddots is inserted into formulas");
540                 packages["mathtools"] =
541                         N_("The LaTeX package mathtools is only used if some mathematical relations are inserted into formulas");
542                 packages["mhchem"] =
543                         N_("The LaTeX package mhchem is only used if either the command \\ce or \\cf is inserted into formulas");
544                 packages["stackrel"] =
545                         N_("The LaTeX package stackrel is only used if the command \\stackrel with subscript is inserted into formulas");
546                 packages["stmaryrd"] =
547                         N_("The LaTeX package stmaryrd is only used if symbols from the St Mary's Road symbol font for theoretical computer science are inserted into formulas");
548                 packages["undertilde"] =
549                         N_("The LaTeX package undertilde is only used if you use the math frame decoration 'utilde'");
550         }
551         return packages;
552 }
553
554
555 bool BufferParams::useBibtopic() const
556 {
557         if (useBiblatex())
558                 return false;
559         return (use_bibtopic || (!multibib.empty() && multibib != "child"));
560 }
561
562
563 AuthorList & BufferParams::authors()
564 {
565         return pimpl_->authorlist;
566 }
567
568
569 AuthorList const & BufferParams::authors() const
570 {
571         return pimpl_->authorlist;
572 }
573
574
575 void BufferParams::addAuthor(Author const & a)
576 {
577         author_map_[a.bufferId()] = pimpl_->authorlist.record(a);
578 }
579
580
581 BranchList & BufferParams::branchlist()
582 {
583         return pimpl_->branchlist;
584 }
585
586
587 BranchList const & BufferParams::branchlist() const
588 {
589         return pimpl_->branchlist;
590 }
591
592
593 IndicesList & BufferParams::indiceslist()
594 {
595         return pimpl_->indiceslist;
596 }
597
598
599 IndicesList const & BufferParams::indiceslist() const
600 {
601         return pimpl_->indiceslist;
602 }
603
604
605 WordLangTable & BufferParams::spellignore()
606 {
607         return pimpl_->spellignore;
608 }
609
610
611 WordLangTable const & BufferParams::spellignore() const
612 {
613         return pimpl_->spellignore;
614 }
615
616
617 bool BufferParams::spellignored(WordLangTuple const & wl) const
618 {
619         bool has_item = false;
620         vector<WordLangTuple> il = spellignore();
621         vector<WordLangTuple>::const_iterator it = il.begin();
622         for (; it != il.end(); ++it) {
623                 if (it->lang()->code() != wl.lang()->code())
624                         continue;
625                 if (it->word() == wl.word()) {
626                         has_item = true;
627                         break;
628                 }
629         }
630         return has_item;
631 }
632
633
634 Bullet & BufferParams::temp_bullet(lyx::size_type const index)
635 {
636         LASSERT(index < 4, return pimpl_->temp_bullets[0]);
637         return pimpl_->temp_bullets[index];
638 }
639
640
641 Bullet const & BufferParams::temp_bullet(lyx::size_type const index) const
642 {
643         LASSERT(index < 4, return pimpl_->temp_bullets[0]);
644         return pimpl_->temp_bullets[index];
645 }
646
647
648 Bullet & BufferParams::user_defined_bullet(lyx::size_type const index)
649 {
650         LASSERT(index < 4, return pimpl_->temp_bullets[0]);
651         return pimpl_->user_defined_bullets[index];
652 }
653
654
655 Bullet const & BufferParams::user_defined_bullet(lyx::size_type const index) const
656 {
657         LASSERT(index < 4, return pimpl_->temp_bullets[0]);
658         return pimpl_->user_defined_bullets[index];
659 }
660
661
662 Spacing & BufferParams::spacing()
663 {
664         return pimpl_->spacing;
665 }
666
667
668 Spacing const & BufferParams::spacing() const
669 {
670         return pimpl_->spacing;
671 }
672
673
674 PDFOptions & BufferParams::pdfoptions()
675 {
676         return pimpl_->pdfoptions;
677 }
678
679
680 PDFOptions const & BufferParams::pdfoptions() const
681 {
682         return pimpl_->pdfoptions;
683 }
684
685
686 Length const & BufferParams::getMathIndent() const
687 {
688         return pimpl_->mathindent;
689 }
690
691
692 void BufferParams::setMathIndent(Length const & indent)
693 {
694         pimpl_->mathindent = indent;
695 }
696
697
698 Length const & BufferParams::getParIndent() const
699 {
700         return pimpl_->parindent;
701 }
702
703
704 void BufferParams::setParIndent(Length const & indent)
705 {
706         pimpl_->parindent = indent;
707 }
708
709
710 VSpace const & BufferParams::getDefSkip() const
711 {
712         return pimpl_->defskip;
713 }
714
715
716 void BufferParams::setDefSkip(VSpace const & vs)
717 {
718         // DEFSKIP will cause an infinite loop
719         LASSERT(vs.kind() != VSpace::DEFSKIP, return);
720         pimpl_->defskip = vs;
721 }
722
723
724 BufferParams::MathNumber BufferParams::getMathNumber() const
725 {
726         if (math_numbering_side != DEFAULT)
727                 return math_numbering_side;
728         // FIXME: do not hardcode language here
729         else if (language->lang() == "arabic_arabi"
730                  || documentClass().provides("leqno"))
731                 return LEFT;
732         else
733                 return RIGHT;
734 }
735
736
737 string BufferParams::readToken(Lexer & lex, string const & token,
738         FileName const & filename)
739 {
740         string result;
741         FileName const & filepath = filename.onlyPath();
742
743         if (token == "\\textclass") {
744                 lex.next();
745                 string const classname = lex.getString();
746                 // if there exists a local layout file, ignore the system one
747                 // NOTE: in this case, the textclass (.cls file) is assumed to
748                 // be available.
749                 string tcp;
750                 LayoutFileList & bcl = LayoutFileList::get();
751                 if (!filepath.empty()) {
752                         // If classname is an absolute path, the document is
753                         // using a local layout file which could not be accessed
754                         // by a relative path. In this case the path is correct
755                         // even if the document was moved to a different
756                         // location. However, we will have a problem if the
757                         // document was generated on a different platform.
758                         bool isabsolute = FileName::isAbsolute(classname);
759                         string const classpath = onlyPath(classname);
760                         string const path = isabsolute ? classpath
761                                 : FileName(addPath(filepath.absFileName(),
762                                                 classpath)).realPath();
763                         string const oldpath = isabsolute ? string()
764                                 : FileName(addPath(origin, classpath)).realPath();
765                         tcp = bcl.addLocalLayout(onlyFileName(classname), path, oldpath);
766                 }
767                 // that returns non-empty if a "local" layout file is found.
768                 if (!tcp.empty()) {
769                         result = to_utf8(makeRelPath(from_utf8(onlyPath(tcp)),
770                                                 from_utf8(filepath.absFileName())));
771                         if (result.empty())
772                                 result = ".";
773                         setBaseClass(onlyFileName(tcp));
774                 } else
775                         setBaseClass(onlyFileName(classname));
776                 // We assume that a tex class exists for local or unknown
777                 // layouts so this warning, will only be given for system layouts.
778                 if (!baseClass()->isTeXClassAvailable()) {
779                         docstring const desc =
780                                 translateIfPossible(from_utf8(baseClass()->description()));
781                         docstring const prereqs =
782                                 from_utf8(baseClass()->prerequisites());
783                         docstring const msg =
784                                 bformat(_("The selected document class\n"
785                                                  "\t%1$s\n"
786                                                  "requires external files that are not available.\n"
787                                                  "The document class can still be used, but the\n"
788                                                  "document cannot be compiled until the following\n"
789                                                  "prerequisites are installed:\n"
790                                                  "\t%2$s\n"
791                                                  "See section 3.1.2.2 (Class Availability) of the\n"
792                                                  "User's Guide for more information."), desc, prereqs);
793                         frontend::Alert::warning(_("Document class not available"),
794                                        msg, true);
795                 }
796         } else if (token == "\\save_transient_properties") {
797                 lex >> save_transient_properties;
798         } else if (token == "\\origin") {
799                 lex.eatLine();
800                 origin = lex.getString();
801                 string const sysdirprefix = "/systemlyxdir/";
802                 if (prefixIs(origin, sysdirprefix)) {
803                         string docsys;
804                         if (inSystemDir(filepath, docsys))
805                                 origin.replace(0, sysdirprefix.length() - 1, docsys);
806                         else
807                                 origin.replace(0, sysdirprefix.length() - 1,
808                                         package().system_support().absFileName());
809                 }
810         } else if (token == "\\begin_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         bool const ot1 = (main_font_encoding() == "default" || main_font_encoding() == "OT1");
1851         bool const use_newtxmath =
1852                 theLaTeXFonts().getLaTeXFont(from_ascii(fontsMath())).getUsedPackage(
1853                         ot1, false, false) == "newtxmath";
1854         if ((useNonTeXFonts || use_newtxmath) && !ams.empty())
1855                 os << from_ascii(ams);
1856
1857         if (useNonTeXFonts) {
1858                 // Babel (as of 2017/11/03) loads fontspec itself
1859                 if (!features.isProvided("fontspec")
1860                     && !(features.useBabel() && features.isAvailable("babel-2017/11/03")))
1861                         os << "\\usepackage{fontspec}\n";
1862                 if (features.mustProvide("unicode-math")
1863                     && features.isAvailable("unicode-math"))
1864                         os << "\\usepackage{unicode-math}\n";
1865         }
1866
1867         // load CJK support package before font selection
1868         // (see autotests/export/latex/CJK/micro-sign_utf8-cjk-libertine.lyx)
1869         if (!useNonTeXFonts && encoding().package() != Encoding::none && inputenc != "utf8x"
1870                 && (encoding().package() == Encoding::CJK || features.mustProvide("CJK"))) {
1871                 if (inputenc == "utf8-cjk" || inputenc == "utf8")
1872                         os << "\\usepackage{CJKutf8}\n";
1873                 else
1874                         os << "\\usepackage[encapsulated]{CJK}\n";
1875         }
1876
1877         // font selection must be done before loading fontenc.sty
1878         // but after babel with non-TeX fonts
1879         string const fonts = loadFonts(features);
1880         if (!fonts.empty() && (!features.useBabel() || !useNonTeXFonts))
1881                 os << from_utf8(fonts);
1882
1883         if (fonts_default_family != "default")
1884                 os << "\\renewcommand{\\familydefault}{\\"
1885                    << from_ascii(fonts_default_family) << "}\n";
1886
1887         // set font encoding
1888         // non-TeX fonts use font encoding TU (set by fontspec)
1889         if (!useNonTeXFonts && !features.isProvided("fontenc")
1890             && main_font_encoding() != "default") {
1891                 // get main font encodings
1892                 vector<string> fontencs = font_encodings();
1893                 // get font encodings of secondary languages
1894                 // FIXME: some languages (hebrew, ...) assume a standard font encoding as last
1895                 // option (for text in other languages).
1896                 features.getFontEncodings(fontencs);
1897                 if (!fontencs.empty()) {
1898                         os << "\\usepackage["
1899                            << from_ascii(getStringFromVector(fontencs))
1900                            << "]{fontenc}\n";
1901                 }
1902         }
1903
1904         // Load textcomp and pmboxdraw before (lua)inputenc (#11454)
1905         if (features.mustProvide("textcomp"))
1906                 os << "\\usepackage{textcomp}\n";
1907         if (features.mustProvide("pmboxdraw"))
1908                 os << "\\usepackage{pmboxdraw}\n";
1909         
1910         // handle inputenc etc.
1911         // (In documents containing text in Thai language, 
1912         // we must load inputenc after babel, see lib/languages).
1913         if (!contains(features.getBabelPostsettings(), from_ascii("thai.ldf")))
1914                 writeEncodingPreamble(os, features);
1915
1916         // includeonly
1917         if (!features.runparams().includeall && !included_children_.empty()) {
1918                 os << "\\includeonly{";
1919                 bool first = true;
1920                 for (auto incfile : included_children_) {
1921                         FileName inc = makeAbsPath(incfile, filepath.absFileName());
1922                         string mangled = DocFileName(changeExtension(inc.absFileName(), ".tex")).
1923                                         mangledFileName();
1924                         if (!features.runparams().nice)
1925                                 incfile = mangled;
1926                         // \includeonly doesn't want an extension
1927                         incfile = changeExtension(incfile, string());
1928                         incfile = support::latex_path(incfile);
1929                         if (!incfile.empty()) {
1930                                 if (!first)
1931                                         os << ",";
1932                                 os << from_utf8(incfile);
1933                         }
1934                         first = false;
1935                 }
1936                 os << "}\n";
1937         }
1938
1939         if (!features.isProvided("geometry")
1940             && (use_geometry || !class_supported_papersize)) {
1941                 odocstringstream ods;
1942                 if (!getGraphicsDriver("geometry").empty())
1943                         ods << getGraphicsDriver("geometry");
1944                 if (orientation == ORIENTATION_LANDSCAPE)
1945                         ods << ",landscape";
1946                 switch (papersize) {
1947                 case PAPER_CUSTOM:
1948                         if (!paperwidth.empty())
1949                                 ods << ",paperwidth="
1950                                    << from_ascii(paperwidth);
1951                         if (!paperheight.empty())
1952                                 ods << ",paperheight="
1953                                    << from_ascii(paperheight);
1954                         break;
1955                 case PAPER_USLETTER:
1956                 case PAPER_USLEGAL:
1957                 case PAPER_USEXECUTIVE:
1958                 case PAPER_A0:
1959                 case PAPER_A1:
1960                 case PAPER_A2:
1961                 case PAPER_A3:
1962                 case PAPER_A4:
1963                 case PAPER_A5:
1964                 case PAPER_A6:
1965                 case PAPER_B0:
1966                 case PAPER_B1:
1967                 case PAPER_B2:
1968                 case PAPER_B3:
1969                 case PAPER_B4:
1970                 case PAPER_B5:
1971                 case PAPER_B6:
1972                 case PAPER_C0:
1973                 case PAPER_C1:
1974                 case PAPER_C2:
1975                 case PAPER_C3:
1976                 case PAPER_C4:
1977                 case PAPER_C5:
1978                 case PAPER_C6:
1979                 case PAPER_JISB0:
1980                 case PAPER_JISB1:
1981                 case PAPER_JISB2:
1982                 case PAPER_JISB3:
1983                 case PAPER_JISB4:
1984                 case PAPER_JISB5:
1985                 case PAPER_JISB6:
1986                         ods << "," << from_ascii(string_papersize_geometry[papersize]);
1987                         break;
1988                 case PAPER_DEFAULT:
1989                         break;
1990                 }
1991                 docstring g_options = trim(ods.str(), ",");
1992                 os << "\\usepackage";
1993                 // geometry-light means that the class works with geometry, but overwrites
1994                 // the package options and paper sizes (memoir does this).
1995                 // In this case, all options need to go to \geometry
1996                 // and the standard paper sizes need to go to the class options.
1997                 if (!g_options.empty() && !features.isProvided("geometry-light")) {
1998                         os << '[' << g_options << ']';
1999                         g_options.clear();
2000                 }
2001                 os << "{geometry}\n";
2002                 if (use_geometry || features.isProvided("geometry-light")) {
2003                         os << "\\geometry{verbose";
2004                         if (!g_options.empty())
2005                                 // Output general options here with "geometry light".
2006                                 os << "," << g_options;
2007                         // output this only if use_geometry is true
2008                         if (use_geometry) {
2009                                 if (!topmargin.empty())
2010                                         os << ",tmargin=" << from_ascii(Length(topmargin).asLatexString());
2011                                 if (!bottommargin.empty())
2012                                         os << ",bmargin=" << from_ascii(Length(bottommargin).asLatexString());
2013                                 if (!leftmargin.empty())
2014                                         os << ",lmargin=" << from_ascii(Length(leftmargin).asLatexString());
2015                                 if (!rightmargin.empty())
2016                                         os << ",rmargin=" << from_ascii(Length(rightmargin).asLatexString());
2017                                 if (!headheight.empty())
2018                                         os << ",headheight=" << from_ascii(Length(headheight).asLatexString());
2019                                 if (!headsep.empty())
2020                                         os << ",headsep=" << from_ascii(Length(headsep).asLatexString());
2021                                 if (!footskip.empty())
2022                                         os << ",footskip=" << from_ascii(Length(footskip).asLatexString());
2023                                 if (!columnsep.empty())
2024                                         os << ",columnsep=" << from_ascii(Length(columnsep).asLatexString());
2025                         }
2026                 os << "}\n";
2027                 }
2028         } else if (orientation == ORIENTATION_LANDSCAPE
2029                    || papersize != PAPER_DEFAULT) {
2030                 features.require("papersize");
2031         }
2032
2033         if (tokenPos(tclass.opt_pagestyle(), '|', pagestyle) >= 0) {
2034                 if (pagestyle == "fancy")
2035                         os << "\\usepackage{fancyhdr}\n";
2036                 os << "\\pagestyle{" << from_ascii(pagestyle) << "}\n";
2037         }
2038
2039         // only output when the background color is not default
2040         if (isbackgroundcolor) {
2041                 // only require color here, the background color will be defined
2042                 // in LaTeXFeatures.cpp to avoid interferences with the LaTeX
2043                 // package pdfpages
2044                 features.require("color");
2045                 features.require("pagecolor");
2046         }
2047
2048         // only output when the font color is not default
2049         if (isfontcolor) {
2050                 // only require color here, the font color will be defined
2051                 // in LaTeXFeatures.cpp to avoid interferences with the LaTeX
2052                 // package pdfpages
2053                 features.require("color");
2054                 features.require("fontcolor");
2055         }
2056
2057         // Only if class has a ToC hierarchy
2058         if (tclass.hasTocLevels()) {
2059                 if (secnumdepth != tclass.secnumdepth()) {
2060                         os << "\\setcounter{secnumdepth}{"
2061                            << secnumdepth
2062                            << "}\n";
2063                 }
2064                 if (tocdepth != tclass.tocdepth()) {
2065                         os << "\\setcounter{tocdepth}{"
2066                            << tocdepth
2067                            << "}\n";
2068                 }
2069         }
2070
2071         if (paragraph_separation) {
2072                 // when skip separation
2073                 string psopt;
2074                 switch (getDefSkip().kind()) {
2075                 case VSpace::SMALLSKIP:
2076                         psopt = "\\smallskipamount";
2077                         break;
2078                 case VSpace::MEDSKIP:
2079                         psopt = "\\medskipamount";
2080                         break;
2081                 case VSpace::BIGSKIP:
2082                         psopt = "\\bigskipamount";
2083                         break;
2084                 case VSpace::HALFLINE:
2085                         // default (no option)
2086                         break;
2087                 case VSpace::FULLLINE:
2088                         psopt = "\\baselineskip";
2089                         break;
2090                 case VSpace::LENGTH:
2091                         psopt = getDefSkip().length().asLatexString();
2092                         break;
2093                 default:
2094                         break;
2095                 }
2096                 if (!features.isProvided("parskip")) {
2097                         if (!psopt.empty())
2098                                 psopt = "[skip=" + psopt + "]";
2099                         os << "\\usepackage" + psopt + "{parskip}\n";
2100                 } else {
2101                         os << "\\setlength{\\parskip}{" + psopt + "}\n";
2102                 }
2103         } else {
2104                 // when separation by indentation
2105                 // only output something when a width is given
2106                 if (!getParIndent().empty()) {
2107                         os << "\\setlength{\\parindent}{"
2108                            << from_utf8(getParIndent().asLatexString())
2109                            << "}\n";
2110                 }
2111         }
2112
2113         if (is_math_indent) {
2114                 // when formula indentation
2115                 // only output something when it is not the default
2116                 if (!getMathIndent().empty()) {
2117                         os << "\\setlength{\\mathindent}{"
2118                            << from_utf8(getMathIndent().asString())
2119                            << "}\n";
2120                 }
2121         }
2122
2123         // Now insert the LyX specific LaTeX commands...
2124         features.resolveAlternatives();
2125         features.expandMultiples();
2126
2127         if (output_sync) {
2128                 if (!output_sync_macro.empty())
2129                         os << from_utf8(output_sync_macro) +"\n";
2130                 else if (features.runparams().flavor == Flavor::LaTeX)
2131                         os << "\\usepackage[active]{srcltx}\n";
2132                 else if (features.runparams().flavor == Flavor::PdfLaTeX)
2133                         os << "\\synctex=-1\n";
2134         }
2135
2136         // due to interferences with babel and hyperref, the color package has to
2137         // be loaded (when it is not already loaded) before babel when hyperref
2138         // is used with the colorlinks option, see
2139         // http://www.lyx.org/trac/ticket/5291
2140         // we decided therefore to load color always before babel, see
2141         // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg144349.html
2142         os << from_ascii(features.getColorOptions());
2143
2144         // If we use hyperref, jurabib, japanese or varioref,
2145         // we have to call babel before
2146         if (use_babel
2147             && (features.isRequired("jurabib")
2148                 || features.isRequired("hyperref")
2149                 || features.isRequired("varioref")
2150                 || features.isRequired("japanese"))) {
2151                         os << features.getBabelPresettings();
2152                         // FIXME UNICODE
2153                         os << from_utf8(babelCall(language_options.str(),
2154                                                                           !lyxrc.language_global_options)) + '\n';
2155                         os << features.getBabelPostsettings();
2156         }
2157
2158         // The optional packages;
2159         os << from_ascii(features.getPackages());
2160
2161         // Additional Indices
2162         if (features.isRequired("splitidx")) {
2163                 for (auto const & idx : indiceslist()) {
2164                         os << "\\newindex{";
2165                         os << escape(idx.shortcut());
2166                         os << "}\n";
2167                 }
2168         }
2169
2170         // Line spacing
2171         os << from_utf8(spacing().writePreamble(features.isProvided("SetSpace")));
2172
2173         // PDF support.
2174         // * Hyperref manual: "Make sure it comes last of your loaded
2175         //   packages, to give it a fighting chance of not being over-written,
2176         //   since its job is to redefine many LaTeX commands."
2177         // * Email from Heiko Oberdiek: "It is usually better to load babel
2178         //   before hyperref. Then hyperref has a chance to detect babel.
2179         // * Has to be loaded before the "LyX specific LaTeX commands" to
2180         //   avoid errors with algorithm floats.
2181         // use hyperref explicitly if it is required
2182         if (features.isRequired("hyperref")) {
2183                 OutputParams tmp_params = features.runparams();
2184                 pdfoptions().writeLaTeX(tmp_params, os,
2185                                         features.isProvided("hyperref"));
2186                 // correctly break URLs with hyperref and dvi/ps output
2187                 if (features.runparams().hyperref_driver == "dvips"
2188                     && features.isAvailable("breakurl"))
2189                         os << "\\usepackage{breakurl}\n";
2190         } else if (features.isRequired("nameref"))
2191                 // hyperref loads this automatically
2192                 os << "\\usepackage{nameref}\n";
2193
2194         if (use_lineno){
2195                 os << "\\usepackage";
2196                 if (!lineno_opts.empty())
2197                         os << "[" << lineno_opts << "]";
2198                 os << "{lineno}\n";
2199                 os << "\\linenumbers\n";
2200         }
2201
2202         // bibtopic needs to be loaded after hyperref.
2203         // the dot provides the aux file naming which LyX can detect.
2204         if (features.mustProvide("bibtopic"))
2205                 os << "\\usepackage[dot]{bibtopic}\n";
2206
2207         // Will be surrounded by \makeatletter and \makeatother when not empty
2208         otexstringstream atlyxpreamble;
2209
2210         // Some macros LyX will need
2211         {
2212                 TexString tmppreamble = features.getMacros();
2213                 if (!tmppreamble.str.empty())
2214                         atlyxpreamble << "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2215                                          "LyX specific LaTeX commands.\n"
2216                                       << move(tmppreamble)
2217                                       << '\n';
2218         }
2219         // the text class specific preamble
2220         {
2221                 docstring tmppreamble = features.getTClassPreamble();
2222                 if (!tmppreamble.empty())
2223                         atlyxpreamble << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2224                                          "Textclass specific LaTeX commands.\n"
2225                                       << tmppreamble
2226                                       << '\n';
2227         }
2228         // suppress date if selected
2229         // use \@ifundefined because we cannot be sure that every document class
2230         // has a \date command
2231         if (suppress_date)
2232                 atlyxpreamble << "\\@ifundefined{date}{}{\\date{}}\n";
2233
2234         /* the user-defined preamble */
2235         if (!containsOnly(preamble, " \n\t")) {
2236                 // FIXME UNICODE
2237                 atlyxpreamble << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
2238                                  "User specified LaTeX commands.\n";
2239
2240                 // Check if the user preamble contains uncodable glyphs
2241                 odocstringstream user_preamble;
2242                 docstring uncodable_glyphs;
2243                 Encoding const * const enc = features.runparams().encoding;
2244                 if (enc) {
2245                         for (char_type c : preamble) {
2246                                 if (!enc->encodable(c)) {
2247                                         docstring const glyph(1, c);
2248                                         LYXERR0("Uncodable character '"
2249                                                 << glyph
2250                                                 << "' in user preamble!");
2251                                         uncodable_glyphs += glyph;
2252                                         if (features.runparams().dryrun) {
2253                                                 user_preamble << "<" << _("LyX Warning: ")
2254                                                    << _("uncodable character") << " '";
2255                                                 user_preamble.put(c);
2256                                                 user_preamble << "'>";
2257                                         }
2258                                 } else
2259                                         user_preamble.put(c);
2260                         }
2261                 } else
2262                         user_preamble << preamble;
2263
2264                 // On BUFFER_VIEW|UPDATE, warn user if we found uncodable glyphs
2265                 if (!features.runparams().dryrun && !uncodable_glyphs.empty()) {
2266                         frontend::Alert::warning(
2267                                 _("Uncodable character in user preamble"),
2268                                 support::bformat(
2269                                   _("The user preamble of your document contains glyphs "
2270                                     "that are unknown in the current document encoding "
2271                                     "(namely %1$s).\nThese glyphs are omitted "
2272                                     " from the output, which may result in "
2273                                     "incomplete output."
2274                                     "\n\nPlease select an appropriate "
2275                                     "document encoding\n"
2276                                     "(such as utf8) or change the "
2277                                     "preamble code accordingly."),
2278                                   uncodable_glyphs));
2279                 }
2280                 atlyxpreamble << user_preamble.str() << '\n';
2281         }
2282
2283         // footmisc must be loaded after setspace
2284         // Load it here to avoid clashes with footmisc loaded in the user
2285         // preamble. For that reason we also pass the options via
2286         // \PassOptionsToPackage in getPreamble() and not here.
2287         if (features.mustProvide("footmisc"))
2288                 atlyxpreamble << "\\usepackage{footmisc}\n";
2289
2290         // subfig loads internally the LaTeX package "caption". As
2291         // caption is a very popular package, users will load it in
2292         // the preamble. Therefore we must load subfig behind the
2293         // user-defined preamble and check if the caption package was
2294         // loaded or not. For the case that caption is loaded before
2295         // subfig, there is the subfig option "caption=false". This
2296         // option also works when a koma-script class is used and
2297         // koma's own caption commands are used instead of caption. We
2298         // use \PassOptionsToPackage here because the user could have
2299         // already loaded subfig in the preamble.
2300         if (features.mustProvide("subfig"))
2301                 atlyxpreamble << "\\ifdefined\\showcaptionsetup\n"
2302                                  " % Caption package is used. Advise subfig not to load it again.\n"
2303                                  " \\PassOptionsToPackage{caption=false}{subfig}\n"
2304                                  "\\fi\n"
2305                                  "\\usepackage{subfig}\n";
2306
2307         // Itemize bullet settings need to be last in case the user
2308         // defines their own bullets that use a package included
2309         // in the user-defined preamble -- ARRae
2310         // Actually it has to be done much later than that
2311         // since some packages like frenchb make modifications
2312         // at \begin{document} time -- JMarc
2313         docstring bullets_def;
2314         for (int i = 0; i < 4; ++i) {
2315                 if (user_defined_bullet(i) != ITEMIZE_DEFAULTS[i]) {
2316                         if (bullets_def.empty())
2317                                 bullets_def += "\\AtBeginDocument{\n";
2318                         bullets_def += "  \\def\\labelitemi";
2319                         switch (i) {
2320                                 // `i' is one less than the item to modify
2321                         case 0:
2322                                 break;
2323                         case 1:
2324                                 bullets_def += 'i';
2325                                 break;
2326                         case 2:
2327                                 bullets_def += "ii";
2328                                 break;
2329                         case 3:
2330                                 bullets_def += 'v';
2331                                 break;
2332                         }
2333                         bullets_def += '{' +
2334                                 user_defined_bullet(i).getText()
2335                                 + "}\n";
2336                 }
2337         }
2338
2339         if (!bullets_def.empty())
2340                 atlyxpreamble << bullets_def << "}\n\n";
2341
2342         if (!atlyxpreamble.empty())
2343                 os << "\n\\makeatletter\n"
2344                    << atlyxpreamble.release()
2345                    << "\\makeatother\n\n";
2346
2347         // We try to load babel late, in case it interferes with other packages.
2348         // Jurabib, hyperref, varioref, bicaption, menukeys and listings (bug 8995)
2349         // have to be called after babel, though.
2350         if (use_babel && !features.isRequired("jurabib")
2351             && !features.isRequired("hyperref")
2352             && !features.isRequired("varioref")
2353             && !features.isRequired("japanese")) {
2354                 os << features.getBabelPresettings();
2355                 // FIXME UNICODE
2356                 os << from_utf8(babelCall(language_options.str(),
2357                                           !lyxrc.language_global_options)) + '\n';
2358                 os << features.getBabelPostsettings();
2359         }
2360         // In documents containing text in Thai language, 
2361         // we must load inputenc after babel (see lib/languages).
2362         if (contains(features.getBabelPostsettings(), from_ascii("thai.ldf")))
2363                 writeEncodingPreamble(os, features);
2364
2365         // font selection must be done after babel with non-TeX fonts
2366         if (!fonts.empty() && features.useBabel() && useNonTeXFonts)
2367                 os << from_utf8(fonts);
2368
2369         if (features.isRequired("bicaption"))
2370                 os << "\\usepackage{bicaption}\n";
2371         if (!listings_params.empty()
2372             || features.mustProvide("listings")
2373             || features.mustProvide("minted")) {
2374                 if (use_minted)
2375                         os << "\\usepackage{minted}\n";
2376                 else
2377                         os << "\\usepackage{listings}\n";
2378         }
2379         string lst_params = listings_params;
2380         // If minted, do not output the language option (bug 11203)
2381         if (use_minted && contains(lst_params, "language=")) {
2382                 vector<string> opts =
2383                         getVectorFromString(lst_params, ",", false);
2384                 for (size_t i = 0; i < opts.size(); ++i) {
2385                         if (prefixIs(opts[i], "language="))
2386                                 opts.erase(opts.begin() + i--);
2387                 }
2388                 lst_params = getStringFromVector(opts, ",");
2389         }
2390         if (!lst_params.empty()) {
2391                 if (use_minted)
2392                         os << "\\setminted{";
2393                 else
2394                         os << "\\lstset{";
2395                 // do not test validity because listings_params is
2396                 // supposed to be valid
2397                 string par =
2398                         InsetListingsParams(lst_params).separatedParams(true);
2399                 os << from_utf8(par);
2400                 os << "}\n";
2401         }
2402
2403         // xunicode only needs to be loaded if tipa is used
2404         // (the rest is obsoleted by the new TU encoding).
2405         // It needs to be loaded at least after amsmath, amssymb,
2406         // esint and the other packages that provide special glyphs
2407         if (features.mustProvide("tipa") && useNonTeXFonts
2408             && !features.isProvided("xunicode")) {
2409                 // The `xunicode` package officially only supports XeTeX,
2410                 //  but also works with LuaTeX. We work around its XeTeX test.
2411                 if (features.runparams().flavor != Flavor::XeTeX) {
2412                         os << "% Pretend to xunicode that we are XeTeX\n"
2413                            << "\\def\\XeTeXpicfile{}\n";
2414                 }
2415                 os << "\\usepackage{xunicode}\n";
2416         }
2417
2418         // covington must be loaded after beamerarticle
2419         if (features.isRequired("covington"))
2420             os << "\\usepackage{covington}\n";
2421
2422         // Polyglossia must be loaded last ...
2423         if (use_polyglossia) {
2424                 // call the package
2425                 os << "\\usepackage{polyglossia}\n";
2426                 // set the main language
2427                 os << "\\setdefaultlanguage";
2428                 if (!language->polyglossiaOpts().empty())
2429                         os << "[" << from_ascii(language->polyglossiaOpts()) << "]";
2430                 os << "{" << from_ascii(language->polyglossia()) << "}\n";
2431                 // now setup the other languages
2432                 set<string> const polylangs =
2433                         features.getPolyglossiaLanguages();
2434                 for (auto const & pl : polylangs) {
2435                         // We do not output the options here; they are output in
2436                         // the language switch commands. This is safer if multiple
2437                         // varieties are used.
2438                         if (pl == language->polyglossia())
2439                                 continue;
2440                         os << "\\setotherlanguage";
2441                         os << "{" << from_ascii(pl) << "}\n";
2442                 }
2443         }
2444
2445         // ... but before biblatex (see #7065)
2446         if ((features.mustProvide("biblatex")
2447              || features.isRequired("biblatex-chicago"))
2448             && !features.isProvided("biblatex-chicago")
2449             && !features.isProvided("biblatex-natbib")
2450             && !features.isProvided("natbib-internal")
2451             && !features.isProvided("natbib")
2452             && !features.isProvided("jurabib")) {
2453                 // The biblatex-chicago package has a differing interface
2454                 // it uses a wrapper package and loads styles via fixed options
2455                 bool const chicago = features.isRequired("biblatex-chicago");
2456                 string delim = "";
2457                 string opts;
2458                 os << "\\usepackage";
2459                 if (!biblatex_bibstyle.empty()
2460                     && (biblatex_bibstyle == biblatex_citestyle)
2461                     && !chicago) {
2462                         opts = "style=" + biblatex_bibstyle;
2463                         delim = ",";
2464                 } else if (!chicago) {
2465                         if (!biblatex_bibstyle.empty()) {
2466                                 opts = "bibstyle=" + biblatex_bibstyle;
2467                                 delim = ",";
2468                         }
2469                         if (!biblatex_citestyle.empty()) {
2470                                 opts += delim + "citestyle=" + biblatex_citestyle;
2471                                 delim = ",";
2472                         }
2473                 }
2474                 if (!multibib.empty() && multibib != "child") {
2475                         opts += delim + "refsection=" + multibib;
2476                         delim = ",";
2477                 }
2478                 if (bibtexCommand() == "bibtex8"
2479                     || prefixIs(bibtexCommand(), "bibtex8 ")) {
2480                         opts += delim + "backend=bibtex8";
2481                         delim = ",";
2482                 } else if (bibtexCommand() == "bibtex"
2483                            || prefixIs(bibtexCommand(), "bibtex ")) {
2484                         opts += delim + "backend=bibtex";
2485                         delim = ",";
2486                 }
2487                 if (!bib_encoding.empty() && encodings.fromLyXName(bib_encoding)) {
2488                         opts += delim + "bibencoding="
2489                                 + encodings.fromLyXName(bib_encoding)->latexName();
2490                         delim = ",";
2491                 }
2492                 if (!biblio_opts.empty())
2493                         opts += delim + biblio_opts;
2494                 if (!opts.empty())
2495                         os << "[" << opts << "]";
2496                 if (chicago)
2497                         os << "{biblatex-chicago}\n";
2498                 else
2499                         os << "{biblatex}\n";
2500         }
2501
2502
2503         // Load custom language package here
2504         if (features.langPackage() == LaTeXFeatures::LANG_PACK_CUSTOM) {
2505                 if (lang_package == "default")
2506                         os << from_utf8(lyxrc.language_custom_package);
2507                 else
2508                         os << from_utf8(lang_package);
2509                 os << '\n';
2510         }
2511
2512         // Since menukeys uses catoptions, which does some heavy changes on key-value options,
2513         // it is recommended to load menukeys as the last package (even after hyperref)
2514         if (features.isRequired("menukeys"))
2515                 os << "\\usepackage{menukeys}\n";
2516
2517         docstring const i18npreamble =
2518                 features.getTClassI18nPreamble(use_babel, use_polyglossia,
2519                                                use_minted);
2520         if (!i18npreamble.empty())
2521                 os << i18npreamble + '\n';
2522
2523         return use_babel;
2524 }
2525
2526
2527 void BufferParams::useClassDefaults()
2528 {
2529         DocumentClass const & tclass = documentClass();
2530
2531         sides = tclass.sides();
2532         columns = tclass.columns();
2533         pagestyle = tclass.pagestyle();
2534         tablestyle = tclass.tablestyle();
2535         use_default_options = true;
2536         // Only if class has a ToC hierarchy
2537         if (tclass.hasTocLevels()) {
2538                 secnumdepth = tclass.secnumdepth();
2539                 tocdepth = tclass.tocdepth();
2540         }
2541 }
2542
2543
2544 bool BufferParams::hasClassDefaults() const
2545 {
2546         DocumentClass const & tclass = documentClass();
2547
2548         return sides == tclass.sides()
2549                 && columns == tclass.columns()
2550                 && pagestyle == tclass.pagestyle()
2551                 && tablestyle == tclass.tablestyle()
2552                 && use_default_options
2553                 && secnumdepth == tclass.secnumdepth()
2554                 && tocdepth == tclass.tocdepth();
2555 }
2556
2557
2558 DocumentClass const & BufferParams::documentClass() const
2559 {
2560         return *doc_class_;
2561 }
2562
2563
2564 DocumentClassConstPtr BufferParams::documentClassPtr() const
2565 {
2566         return doc_class_;
2567 }
2568
2569
2570 void BufferParams::setDocumentClass(DocumentClassConstPtr tc)
2571 {
2572         // evil, but this function is evil
2573         doc_class_ = const_pointer_cast<DocumentClass>(tc);
2574         invalidateConverterCache();
2575 }
2576
2577
2578 bool BufferParams::setBaseClass(string const & classname, string const & path)
2579 {
2580         LYXERR(Debug::TCLASS, "setBaseClass: " << classname);
2581         LayoutFileList & bcl = LayoutFileList::get();
2582         if (!bcl.haveClass(classname)) {
2583                 docstring s =
2584                         bformat(_("The layout file:\n"
2585                                 "%1$s\n"
2586                                 "could not be found. A default textclass with default\n"
2587                                 "layouts will be used. LyX will not be able to produce\n"
2588                                 "correct output."),
2589                         from_utf8(classname));
2590                 frontend::Alert::error(_("Document class not found"), s);
2591                 bcl.addEmptyClass(classname);
2592         }
2593
2594         bool const success = bcl[classname].load(path);
2595         if (!success) {
2596                 docstring s =
2597                         bformat(_("Due to some error in it, the layout file:\n"
2598                                 "%1$s\n"
2599                                 "could not be loaded. A default textclass with default\n"
2600                                 "layouts will be used. LyX will not be able to produce\n"
2601                                 "correct output."),
2602                         from_utf8(classname));
2603                 frontend::Alert::error(_("Could not load class"), s);
2604                 bcl.addEmptyClass(classname);
2605         }
2606
2607         pimpl_->baseClass_ = classname;
2608         layout_modules_.adaptToBaseClass(baseClass(), removed_modules_);
2609         return true;
2610 }
2611
2612
2613 LayoutFile const * BufferParams::baseClass() const
2614 {
2615         if (LayoutFileList::get().haveClass(pimpl_->baseClass_))
2616                 return &(LayoutFileList::get()[pimpl_->baseClass_]);
2617
2618         return nullptr;
2619 }
2620
2621
2622 LayoutFileIndex const & BufferParams::baseClassID() const
2623 {
2624         return pimpl_->baseClass_;
2625 }
2626
2627
2628 void BufferParams::makeDocumentClass(bool clone, bool internal)
2629 {
2630         if (!baseClass())
2631                 return;
2632
2633         invalidateConverterCache();
2634         LayoutModuleList mods;
2635         for (auto const & mod : layout_modules_)
2636                 mods.push_back(mod);
2637
2638         doc_class_ = getDocumentClass(*baseClass(), mods, cite_engine_, clone, internal);
2639
2640         TextClass::ReturnValues success = TextClass::OK;
2641         if (!forced_local_layout_.empty())
2642                 success = doc_class_->read(to_utf8(forced_local_layout_),
2643                                            TextClass::MODULE);
2644         if (!local_layout_.empty() &&
2645             (success == TextClass::OK || success == TextClass::OK_OLDFORMAT))
2646                 success = doc_class_->read(to_utf8(local_layout_), TextClass::MODULE);
2647         if (success != TextClass::OK && success != TextClass::OK_OLDFORMAT) {
2648                 docstring const msg = _("Error reading internal layout information");
2649                 frontend::Alert::warning(_("Read Error"), msg);
2650         }
2651 }
2652
2653
2654 bool BufferParams::layoutModuleCanBeAdded(string const & modName) const
2655 {
2656         return layout_modules_.moduleCanBeAdded(modName, baseClass());
2657 }
2658
2659
2660 docstring BufferParams::getLocalLayout(bool forced) const
2661 {
2662         if (forced)
2663                 return from_utf8(doc_class_->forcedLayouts());
2664         else
2665                 return local_layout_;
2666 }
2667
2668
2669 void BufferParams::setLocalLayout(docstring const & layout, bool forced)
2670 {
2671         if (forced)
2672                 forced_local_layout_ = layout;
2673         else
2674                 local_layout_ = layout;
2675 }
2676
2677
2678 bool BufferParams::addLayoutModule(string const & modName)
2679 {
2680         for (auto const & mod : layout_modules_)
2681                 if (mod == modName)
2682                         return false;
2683         layout_modules_.push_back(modName);
2684         return true;
2685 }
2686
2687
2688 string BufferParams::bufferFormat() const
2689 {
2690         return documentClass().outputFormat();
2691 }
2692
2693
2694 bool BufferParams::isExportable(string const & format, bool need_viewable) const
2695 {
2696         FormatList const & formats = exportableFormats(need_viewable);
2697         for (auto const & fmt : formats) {
2698                 if (fmt->name() == format)
2699                         return true;
2700         }
2701         return false;
2702 }
2703
2704
2705 FormatList const & BufferParams::exportableFormats(bool only_viewable) const
2706 {
2707         FormatList & cached = only_viewable ?
2708                         pimpl_->viewableFormatList : pimpl_->exportableFormatList;
2709         bool & valid = only_viewable ?
2710                         pimpl_->isViewCacheValid : pimpl_->isExportCacheValid;
2711         if (valid)
2712                 return cached;
2713
2714         vector<string> const backs = backends();
2715         set<string> excludes;
2716         if (useNonTeXFonts) {
2717                 excludes.insert("latex");
2718                 excludes.insert("pdflatex");
2719         } else if (inputenc != "ascii" && inputenc != "utf8-plain") {
2720                   // XeTeX with TeX fonts requires input encoding ascii (#10600).
2721                   excludes.insert("xetex");
2722         }
2723
2724         FormatList result =
2725                 theConverters().getReachable(backs[0], only_viewable, true, excludes);
2726         vector<string>::const_iterator it = backs.begin() + 1;
2727         for (; it != backs.end(); ++it) {
2728                 FormatList r = theConverters().getReachable(*it, only_viewable,
2729                                                                                                         false, excludes);
2730                 result.insert(result.end(), r.begin(), r.end());
2731         }
2732         sort(result.begin(), result.end(), Format::formatSorter);
2733         cached = result;
2734         valid = true;
2735         return cached;
2736 }
2737
2738
2739 vector<string> BufferParams::backends() const
2740 {
2741         vector<string> v;
2742         string const buffmt = bufferFormat();
2743
2744         // FIXME: Don't hardcode format names here, but use a flag
2745         if (buffmt == "latex") {
2746                 if (encoding().package() == Encoding::japanese)
2747                         v.push_back("platex");
2748                 else {
2749                         if (!useNonTeXFonts) {
2750                                 v.push_back("pdflatex");
2751                                 v.push_back("latex");
2752                         }
2753                         if (useNonTeXFonts 
2754                                 || inputenc == "ascii" || inputenc == "utf8-plain")
2755                                 v.push_back("xetex");
2756                         v.push_back("luatex");
2757                         v.push_back("dviluatex");
2758                 }
2759         } else {
2760                 string rbuffmt = buffmt;
2761                 // If we use an OutputFormat in Japanese docs,
2762                 // we need special format in order to get the path
2763                 // via pLaTeX (#8823)
2764                 if (documentClass().hasOutputFormat()
2765                     && encoding().package() == Encoding::japanese)
2766                         rbuffmt += "-ja";
2767                 v.push_back(rbuffmt);
2768         }
2769
2770         v.push_back("xhtml");
2771         v.push_back("docbook5");
2772         v.push_back("text");
2773         v.push_back("lyx");
2774         return v;
2775 }
2776
2777
2778 Flavor BufferParams::getOutputFlavor(string const & format) const
2779 {
2780         string const dformat = (format.empty() || format == "default") ?
2781                 getDefaultOutputFormat() : format;
2782         DefaultFlavorCache::const_iterator it =
2783                 default_flavors_.find(dformat);
2784
2785         if (it != default_flavors_.end())
2786                 return it->second;
2787
2788         Flavor result = Flavor::LaTeX;
2789
2790         // FIXME It'd be better not to hardcode this, but to do
2791         //       something with formats.
2792         if (dformat == "xhtml")
2793                 result = Flavor::Html;
2794         else if (dformat == "docbook5")
2795                 result = Flavor::DocBook5;
2796         else if (dformat == "text")
2797                 result = Flavor::Text;
2798         else if (dformat == "lyx")
2799                 result = Flavor::LyX;
2800         else if (dformat == "pdflatex")
2801                 result = Flavor::PdfLaTeX;
2802         else if (dformat == "xetex")
2803                 result = Flavor::XeTeX;
2804         else if (dformat == "luatex")
2805                 result = Flavor::LuaTeX;
2806         else if (dformat == "dviluatex")
2807                 result = Flavor::DviLuaTeX;
2808         else {
2809                 // Try to determine flavor of default output format
2810                 vector<string> backs = backends();
2811                 if (find(backs.begin(), backs.end(), dformat) == backs.end()) {
2812                         // Get shortest path to format
2813                         Graph::EdgePath path;
2814                         for (auto const & bvar : backs) {
2815                                 Graph::EdgePath p = theConverters().getPath(bvar, dformat);
2816                                 if (!p.empty() && (path.empty() || p.size() < path.size())) {
2817                                         path = p;
2818                                 }
2819                         }
2820                         if (!path.empty())
2821                                 result = theConverters().getFlavor(path);
2822                 }
2823         }
2824         // cache this flavor
2825         default_flavors_[dformat] = result;
2826         return result;
2827 }
2828
2829
2830 string BufferParams::getDefaultOutputFormat() const
2831 {
2832         if (!default_output_format.empty()
2833             && default_output_format != "default")
2834                 return default_output_format;
2835         if (encoding().package() == Encoding::japanese)
2836                 return lyxrc.default_platex_view_format;
2837         if (useNonTeXFonts)
2838                 return lyxrc.default_otf_view_format;
2839         return lyxrc.default_view_format;
2840 }
2841
2842 Font const BufferParams::getFont() const
2843 {
2844         FontInfo f = documentClass().defaultfont();
2845         if (fonts_default_family == "rmdefault")
2846                 f.setFamily(ROMAN_FAMILY);
2847         else if (fonts_default_family == "sfdefault")
2848                 f.setFamily(SANS_FAMILY);
2849         else if (fonts_default_family == "ttdefault")
2850                 f.setFamily(TYPEWRITER_FAMILY);
2851         return Font(f, language);
2852 }
2853
2854
2855 QuoteStyle BufferParams::getQuoteStyle(string const & qs) const
2856 {
2857         return quotesstyletranslator().find(qs);
2858 }
2859
2860
2861 bool BufferParams::isLatex() const
2862 {
2863         return documentClass().outputType() == LATEX;
2864 }
2865
2866
2867 bool BufferParams::isLiterate() const
2868 {
2869         return documentClass().outputType() == LITERATE;
2870 }
2871
2872
2873 void BufferParams::readPreamble(Lexer & lex)
2874 {
2875         if (lex.getString() != "\\begin_preamble")
2876                 lyxerr << "Error (BufferParams::readPreamble):"
2877                         "consistency check failed." << endl;
2878
2879         preamble = lex.getLongString(from_ascii("\\end_preamble"));
2880 }
2881
2882
2883 void BufferParams::readLocalLayout(Lexer & lex, bool forced)
2884 {
2885         string const expected = forced ? "\\begin_forced_local_layout" :
2886                                          "\\begin_local_layout";
2887         if (lex.getString() != expected)
2888                 lyxerr << "Error (BufferParams::readLocalLayout):"
2889                         "consistency check failed." << endl;
2890
2891         if (forced)
2892                 forced_local_layout_ =
2893                         lex.getLongString(from_ascii("\\end_forced_local_layout"));
2894         else
2895                 local_layout_ = lex.getLongString(from_ascii("\\end_local_layout"));
2896 }
2897
2898
2899 bool BufferParams::setLanguage(string const & lang)
2900 {
2901         Language const *new_language = languages.getLanguage(lang);
2902         if (!new_language) {
2903                 // Language lang was not found
2904                 return false;
2905         }
2906         language = new_language;
2907         return true;
2908 }
2909
2910
2911 void BufferParams::readLanguage(Lexer & lex)
2912 {
2913         if (!lex.next()) return;
2914
2915         string const tmptok = lex.getString();
2916
2917         // check if tmptok is part of tex_babel in tex-defs.h
2918         if (!setLanguage(tmptok)) {
2919                 // Language tmptok was not found
2920                 language = default_language;
2921                 lyxerr << "Warning: Setting language `"
2922                        << tmptok << "' to `" << language->lang()
2923                        << "'." << endl;
2924         }
2925 }
2926
2927
2928 void BufferParams::readGraphicsDriver(Lexer & lex)
2929 {
2930         if (!lex.next())
2931                 return;
2932
2933         string const tmptok = lex.getString();
2934         // check if tmptok is part of tex_graphics in tex_defs.h
2935         int n = 0;
2936         while (true) {
2937                 string const test = tex_graphics[n++];
2938
2939                 if (test == tmptok) {
2940                         graphics_driver = tmptok;
2941                         break;
2942                 }
2943                 if (test.empty()) {
2944                         lex.printError(
2945                                 "Warning: graphics driver `$$Token' not recognized!\n"
2946                                 "         Setting graphics driver to `default'.\n");
2947                         graphics_driver = "default";
2948                         break;
2949                 }
2950         }
2951 }
2952
2953
2954 void BufferParams::readBullets(Lexer & lex)
2955 {
2956         if (!lex.next())
2957                 return;
2958
2959         int const index = lex.getInteger();
2960         lex.next();
2961         int temp_int = lex.getInteger();
2962         user_defined_bullet(index).setFont(temp_int);
2963         temp_bullet(index).setFont(temp_int);
2964         lex >> temp_int;
2965         user_defined_bullet(index).setCharacter(temp_int);
2966         temp_bullet(index).setCharacter(temp_int);
2967         lex >> temp_int;
2968         user_defined_bullet(index).setSize(temp_int);
2969         temp_bullet(index).setSize(temp_int);
2970 }
2971
2972
2973 void BufferParams::readBulletsLaTeX(Lexer & lex)
2974 {
2975         // The bullet class should be able to read this.
2976         if (!lex.next())
2977                 return;
2978         int const index = lex.getInteger();
2979         lex.next(true);
2980         docstring const temp_str = lex.getDocString();
2981
2982         user_defined_bullet(index).setText(temp_str);
2983         temp_bullet(index).setText(temp_str);
2984 }
2985
2986
2987 void BufferParams::readModules(Lexer & lex)
2988 {
2989         if (!lex.eatLine()) {
2990                 lyxerr << "Error (BufferParams::readModules):"
2991                                 "Unexpected end of input." << endl;
2992                 return;
2993         }
2994         while (true) {
2995                 string mod = lex.getString();
2996                 if (mod == "\\end_modules")
2997                         break;
2998                 addLayoutModule(mod);
2999                 lex.eatLine();
3000         }
3001 }
3002
3003
3004 void BufferParams::readRemovedModules(Lexer & lex)
3005 {
3006         if (!lex.eatLine()) {
3007                 lyxerr << "Error (BufferParams::readRemovedModules):"
3008                                 "Unexpected end of input." << endl;
3009                 return;
3010         }
3011         while (true) {
3012                 string mod = lex.getString();
3013                 if (mod == "\\end_removed_modules")
3014                         break;
3015                 removed_modules_.push_back(mod);
3016                 lex.eatLine();
3017         }
3018         // now we want to remove any removed modules that were previously
3019         // added. normally, that will be because default modules were added in
3020         // setBaseClass(), which gets called when \textclass is read at the
3021         // start of the read.
3022         for (auto const & rm : removed_modules_) {
3023                 LayoutModuleList::iterator const mit = layout_modules_.begin();
3024                 LayoutModuleList::iterator const men = layout_modules_.end();
3025                 LayoutModuleList::iterator found = find(mit, men, rm);
3026                 if (found == men)
3027                         continue;
3028                 layout_modules_.erase(found);
3029         }
3030 }
3031
3032
3033 void BufferParams::readIncludeonly(Lexer & lex)
3034 {
3035         if (!lex.eatLine()) {
3036                 lyxerr << "Error (BufferParams::readIncludeonly):"
3037                                 "Unexpected end of input." << endl;
3038                 return;
3039         }
3040         while (true) {
3041                 string child = lex.getString();
3042                 if (child == "\\end_includeonly")
3043                         break;
3044                 included_children_.push_back(child);
3045                 lex.eatLine();
3046         }
3047 }
3048
3049
3050 string BufferParams::paperSizeName(PapersizePurpose purpose, string const & psize) const
3051 {
3052         PAPER_SIZE ppsize = psize.empty() ? papersize : papersizetranslator().find(psize);
3053         switch (ppsize) {
3054         case PAPER_DEFAULT:
3055                 if (documentClass().pagesize() == "default")
3056                         // could be anything, so don't guess
3057                         return string();
3058                 return paperSizeName(purpose, documentClass().pagesize());
3059         case PAPER_CUSTOM: {
3060                 if (purpose == XDVI && !paperwidth.empty() &&
3061                     !paperheight.empty()) {
3062                         // heightxwidth<unit>
3063                         string first = paperwidth;
3064                         string second = paperheight;
3065                         if (orientation == ORIENTATION_LANDSCAPE)
3066                                 first.swap(second);
3067                         // cut off unit.
3068                         return first.erase(first.length() - 2)
3069                                 + "x" + second;
3070                 }
3071                 return string();
3072         }
3073         case PAPER_A0:
3074                 // dvips and dvipdfm do not know this
3075                 if (purpose == DVIPS || purpose == DVIPDFM)
3076                         return string();
3077                 return "a0";
3078         case PAPER_A1:
3079                 if (purpose == DVIPS || purpose == DVIPDFM)
3080                         return string();
3081                 return "a1";
3082         case PAPER_A2:
3083                 if (purpose == DVIPS || purpose == DVIPDFM)
3084                         return string();
3085                 return "a2";
3086         case PAPER_A3:
3087                 return "a3";
3088         case PAPER_A4:
3089                 return "a4";
3090         case PAPER_A5:
3091                 return "a5";
3092         case PAPER_A6:
3093                 if (purpose == DVIPS || purpose == DVIPDFM)
3094                         return string();
3095                 return "a6";
3096         case PAPER_B0:
3097                 if (purpose == DVIPS || purpose == DVIPDFM)
3098                         return string();
3099                 return "b0";
3100         case PAPER_B1:
3101                 if (purpose == DVIPS || purpose == DVIPDFM)
3102                         return string();
3103                 return "b1";
3104         case PAPER_B2:
3105                 if (purpose == DVIPS || purpose == DVIPDFM)
3106                         return string();
3107                 return "b2";
3108         case PAPER_B3:
3109                 if (purpose == DVIPS || purpose == DVIPDFM)
3110                         return string();
3111                 return "b3";
3112         case PAPER_B4:
3113                 // dvipdfm does not know this
3114                 if (purpose == DVIPDFM)
3115                         return string();
3116                 return "b4";
3117         case PAPER_B5:
3118                 if (purpose == DVIPDFM)
3119                         return string();
3120                 return "b5";
3121         case PAPER_B6:
3122                 if (purpose == DVIPS || purpose == DVIPDFM)
3123                         return string();
3124                 return "b6";
3125         case PAPER_C0:
3126                 if (purpose == DVIPS || purpose == DVIPDFM)
3127                         return string();
3128                 return "c0";
3129         case PAPER_C1:
3130                 if (purpose == DVIPS || purpose == DVIPDFM)
3131                         return string();
3132                 return "c1";
3133         case PAPER_C2:
3134                 if (purpose == DVIPS || purpose == DVIPDFM)
3135                         return string();
3136                 return "c2";
3137         case PAPER_C3:
3138                 if (purpose == DVIPS || purpose == DVIPDFM)
3139                         return string();
3140                 return "c3";
3141         case PAPER_C4:
3142                 if (purpose == DVIPS || purpose == DVIPDFM)
3143                         return string();
3144                 return "c4";
3145         case PAPER_C5:
3146                 if (purpose == DVIPS || purpose == DVIPDFM)
3147                         return string();
3148                 return "c5";
3149         case PAPER_C6:
3150                 if (purpose == DVIPS || purpose == DVIPDFM)
3151                         return string();
3152                 return "c6";
3153         case PAPER_JISB0:
3154                 if (purpose == DVIPS || purpose == DVIPDFM)
3155                         return string();
3156                 return "jisb0";
3157         case PAPER_JISB1:
3158                 if (purpose == DVIPS || purpose == DVIPDFM)
3159                         return string();
3160                 return "jisb1";
3161         case PAPER_JISB2:
3162                 if (purpose == DVIPS || purpose == DVIPDFM)
3163                         return string();
3164                 return "jisb2";
3165         case PAPER_JISB3:
3166                 if (purpose == DVIPS || purpose == DVIPDFM)
3167                         return string();
3168                 return "jisb3";
3169         case PAPER_JISB4:
3170                 if (purpose == DVIPS || purpose == DVIPDFM)
3171                         return string();
3172                 return "jisb4";
3173         case PAPER_JISB5:
3174                 if (purpose == DVIPS || purpose == DVIPDFM)
3175                         return string();
3176                 return "jisb5";
3177         case PAPER_JISB6:
3178                 if (purpose == DVIPS || purpose == DVIPDFM)
3179                         return string();
3180                 return "jisb6";
3181         case PAPER_USEXECUTIVE:
3182                 // dvipdfm does not know this
3183                 if (purpose == DVIPDFM)
3184                         return string();
3185                 return "foolscap";
3186         case PAPER_USLEGAL:
3187                 return "legal";
3188         case PAPER_USLETTER:
3189         default:
3190                 if (purpose == XDVI)
3191                         return "us";
3192                 return "letter";
3193         }
3194 }
3195
3196
3197 string const BufferParams::dvips_options() const
3198 {
3199         string result;
3200
3201         // If the class loads the geometry package, we do not know which
3202         // paper size is used, since we do not set it (bug 7013).
3203         // Therefore we must not specify any argument here.
3204         // dvips gets the correct paper size via DVI specials in this case
3205         // (if the class uses the geometry package correctly).
3206         if (documentClass().provides("geometry"))
3207                 return result;
3208
3209         if (use_geometry
3210             && papersize == PAPER_CUSTOM
3211             && !lyxrc.print_paper_dimension_flag.empty()
3212             && !paperwidth.empty()
3213             && !paperheight.empty()) {
3214                 // using a custom papersize
3215                 result = lyxrc.print_paper_dimension_flag;
3216                 result += ' ' + paperwidth;
3217                 result += ',' + paperheight;
3218         } else {
3219                 string const paper_option = paperSizeName(DVIPS);
3220                 if (!paper_option.empty() && (paper_option != "letter" ||
3221                     orientation != ORIENTATION_LANDSCAPE)) {
3222                         // dvips won't accept -t letter -t landscape.
3223                         // In all other cases, include the paper size
3224                         // explicitly.
3225                         result = lyxrc.print_paper_flag;
3226                         result += ' ' + paper_option;
3227                 }
3228         }
3229         if (orientation == ORIENTATION_LANDSCAPE &&
3230             papersize != PAPER_CUSTOM)
3231                 result += ' ' + lyxrc.print_landscape_flag;
3232         return result;
3233 }
3234
3235
3236 string const BufferParams::main_font_encoding() const
3237 {
3238         if (font_encodings().empty()) {
3239                 if (ascii_lowercase(language->fontenc(*this)) == "none")
3240                         return "none";
3241                 return "default";
3242         }
3243         return font_encodings().back();
3244 }
3245
3246
3247 vector<string> const BufferParams::font_encodings() const
3248 {
3249         string doc_fontenc = (fontenc == "auto") ? string() : fontenc;
3250
3251         vector<string> fontencs;
3252
3253         // "default" means "no explicit font encoding"
3254         if (doc_fontenc != "default") {
3255                 if (!doc_fontenc.empty())
3256                         // If we have a custom setting, we use only that!
3257                         return getVectorFromString(doc_fontenc);
3258                 if (!language->fontenc(*this).empty()
3259                     && ascii_lowercase(language->fontenc(*this)) != "none") {
3260                         vector<string> fencs = getVectorFromString(language->fontenc(*this));
3261                         for (auto & fe : fencs) {
3262                                 if (find(fontencs.begin(), fontencs.end(), fe) == fontencs.end())
3263                                         fontencs.push_back(fe);
3264                         }
3265                 }
3266         }
3267
3268         return fontencs;
3269 }
3270
3271
3272 string BufferParams::babelCall(string const & lang_opts, bool const langoptions) const
3273 {
3274         // suppress the babel call if there is no BabelName defined
3275         // for the document language in the lib/languages file and if no
3276         // other languages are used (lang_opts is then empty)
3277         if (lang_opts.empty())
3278                 return string();
3279         // The prefs may require the languages to
3280         // be submitted to babel itself (not the class).
3281         if (langoptions)
3282                 return "\\usepackage[" + lang_opts + "]{babel}";
3283         return "\\usepackage{babel}";
3284 }
3285
3286
3287 docstring BufferParams::getGraphicsDriver(string const & package) const
3288 {
3289         docstring result;
3290
3291         if (package == "geometry") {
3292                 if (graphics_driver == "dvips"
3293                     || graphics_driver == "dvipdfm"
3294                     || graphics_driver == "pdftex"
3295                     || graphics_driver == "vtex")
3296                         result = from_ascii(graphics_driver);
3297                 else if (graphics_driver == "dvipdfmx")
3298                         result = from_ascii("dvipdfm");
3299         }
3300
3301         return result;
3302 }
3303
3304
3305 void BufferParams::writeEncodingPreamble(otexstream & os,
3306                                          LaTeXFeatures & features) const
3307 {
3308         // With no-TeX fonts we use utf8-plain without encoding package.
3309         if (useNonTeXFonts)
3310                 return;
3311
3312         if (inputenc == "auto-legacy") {
3313                 string const doc_encoding =
3314                         language->encoding()->latexName();
3315                 Encoding::Package const package =
3316                         language->encoding()->package();
3317
3318                 // Create list of inputenc options:
3319                 set<string> encoding_set;
3320                 // luainputenc fails with more than one encoding
3321                 if (features.runparams().flavor != Flavor::LuaTeX
3322                         && features.runparams().flavor != Flavor::DviLuaTeX)
3323                         // list all input encodings used in the document
3324                         encoding_set = features.getEncodingSet(doc_encoding);
3325
3326                 // The "japanese" babel-language requires  the pLaTeX engine
3327                 // which conflicts with "inputenc".
3328                 // See http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129680.html
3329                 if ((!encoding_set.empty() || package == Encoding::inputenc)
3330                     && !features.isRequired("japanese")
3331                     && !features.isProvided("inputenc")) {
3332                         os << "\\usepackage[";
3333                         set<string>::const_iterator it = encoding_set.begin();
3334                         set<string>::const_iterator const end = encoding_set.end();
3335                         if (it != end) {
3336                                 os << from_ascii(*it);
3337                                 ++it;
3338                         }
3339                         for (; it != end; ++it)
3340                                 os << ',' << from_ascii(*it);
3341                         if (package == Encoding::inputenc) {
3342                                 if (!encoding_set.empty())
3343                                         os << ',';
3344                                 os << from_ascii(doc_encoding);
3345                         }
3346                         if (features.runparams().flavor == Flavor::LuaTeX
3347                             || features.runparams().flavor == Flavor::DviLuaTeX)
3348                                 os << "]{luainputenc}\n";
3349                         else
3350                                 os << "]{inputenc}\n";
3351                 }
3352         } else if (inputenc != "auto-legacy-plain") {
3353                 switch (encoding().package()) {
3354                 case Encoding::none:
3355                 case Encoding::CJK:
3356                 case Encoding::japanese:
3357                         if (encoding().iconvName() != "UTF-8"
3358                                 && !features.runparams().isFullUnicode())
3359                           // don't default to [utf8]{inputenc} with TeXLive >= 18
3360                           os << "\\ifdefined\\UseRawInputEncoding\n"
3361                                  << "  \\UseRawInputEncoding\\fi\n";
3362                         break;
3363                 case Encoding::inputenc:
3364                         // do not load inputenc if japanese is used
3365                         // or if the class provides inputenc
3366                         if (features.isRequired("japanese")
3367                             || features.isProvided("inputenc"))
3368                                 break;
3369                         os << "\\usepackage[" << from_ascii(encoding().latexName());
3370                         if (features.runparams().flavor == Flavor::LuaTeX
3371                             || features.runparams().flavor == Flavor::DviLuaTeX)
3372                                 os << "]{luainputenc}\n";
3373                         else
3374                                 os << "]{inputenc}\n";
3375                         break;
3376                 }
3377         }
3378         if (inputenc == "auto-legacy-plain" || features.isRequired("japanese")) {
3379                 // don't default to [utf8]{inputenc} with TeXLive >= 18
3380                 os << "\\ifdefined\\UseRawInputEncoding\n";
3381                 os << "  \\UseRawInputEncoding\\fi\n";
3382         }
3383 }
3384
3385
3386 string const BufferParams::parseFontName(string const & name) const
3387 {
3388         string mangled = name;
3389         size_t const idx = mangled.find('[');
3390         if (idx == string::npos || idx == 0)
3391                 return mangled;
3392         else
3393                 return mangled.substr(0, idx - 1);
3394 }
3395
3396
3397 string const BufferParams::loadFonts(LaTeXFeatures & features) const
3398 {
3399         if (fontsRoman() == "default" && fontsSans() == "default"
3400             && fontsTypewriter() == "default"
3401             && (fontsMath() == "default" || fontsMath() == "auto"))
3402                 //nothing to do
3403                 return string();
3404
3405         ostringstream os;
3406
3407         /* Fontspec (XeTeX, LuaTeX): we provide GUI support for oldstyle
3408          * numbers (Numbers=OldStyle) and sf/tt scaling. The Ligatures=TeX/
3409          * Mapping=tex-text option assures TeX ligatures (such as "--")
3410          * are resolved. Note that tt does not use these ligatures.
3411          * TODO:
3412          *    -- add more GUI options?
3413          *    -- add more fonts (fonts for other scripts)
3414          *    -- if there's a way to find out if a font really supports
3415          *       OldStyle, enable/disable the widget accordingly.
3416         */
3417         if (useNonTeXFonts && features.isAvailable("fontspec")) {
3418                 // "Mapping=tex-text" and "Ligatures=TeX" are equivalent.
3419                 // However, until v.2 (2010/07/11) fontspec only knew
3420                 // Mapping=tex-text (for XeTeX only); then "Ligatures=TeX"
3421                 // was introduced for both XeTeX and LuaTeX (LuaTeX
3422                 // didn't understand "Mapping=tex-text", while XeTeX
3423                 // understood both. With most recent versions, both
3424                 // variants are understood by both engines. However,
3425                 // we want to provide support for at least TeXLive 2009
3426                 // (for XeTeX; LuaTeX is only supported as of v.2)
3427                 // As of 2017/11/03, Babel has its own higher-level
3428                 // interface on top of fontspec that is to be used.
3429                 bool const babelfonts = features.useBabel()
3430                                 && features.isAvailable("babel-2017/11/03");
3431                 string const texmapping =
3432                         (features.runparams().flavor == Flavor::XeTeX) ?
3433                         "Mapping=tex-text" : "Ligatures=TeX";
3434                 if (fontsRoman() != "default") {
3435                         if (babelfonts)
3436                                 os << "\\babelfont{rm}[";
3437                         else
3438                                 os << "\\setmainfont[";
3439                         if (!font_roman_opts.empty())
3440                                 os << font_roman_opts << ',';
3441                         os << texmapping;
3442                         if (fonts_roman_osf)
3443                                 os << ",Numbers=OldStyle";
3444                         os << "]{" << parseFontName(fontsRoman()) << "}\n";
3445                 }
3446                 if (fontsSans() != "default") {
3447                         string const sans = parseFontName(fontsSans());
3448                         if (fontsSansScale() != 100) {
3449                                 if (babelfonts)
3450                                         os << "\\babelfont{sf}";
3451                                 else
3452                                         os << "\\setsansfont";
3453                                 os << "[Scale="
3454                                    << float(fontsSansScale()) / 100 << ',';
3455                                 if (fonts_sans_osf)
3456                                         os << "Numbers=OldStyle,";
3457                                 if (!font_sans_opts.empty())
3458                                         os << font_sans_opts << ',';
3459                                 os << texmapping << "]{"
3460                                    << sans << "}\n";
3461                         } else {
3462                                 if (babelfonts)
3463                                         os << "\\babelfont{sf}[";
3464                                 else
3465                                         os << "\\setsansfont[";
3466                                 if (fonts_sans_osf)
3467                                         os << "Numbers=OldStyle,";
3468                                 if (!font_sans_opts.empty())
3469                                         os << font_sans_opts << ',';
3470                                 os << texmapping << "]{"
3471                                    << sans << "}\n";
3472                         }
3473                 }
3474                 if (fontsTypewriter() != "default") {
3475                         string const mono = parseFontName(fontsTypewriter());
3476                         if (fontsTypewriterScale() != 100) {
3477                                 if (babelfonts)
3478                                         os << "\\babelfont{tt}";
3479                                 else
3480                                         os << "\\setmonofont";
3481                                 os << "[Scale="
3482                                    << float(fontsTypewriterScale()) / 100;
3483                                 if (fonts_typewriter_osf)
3484                                         os << ",Numbers=OldStyle";
3485                                 if (!font_typewriter_opts.empty())
3486                                         os << ',' << font_typewriter_opts;
3487                                 os << "]{"
3488                                    << mono << "}\n";
3489                         } else {
3490                                 if (babelfonts)
3491                                         os << "\\babelfont{tt}";
3492                                 else
3493                                         os << "\\setmonofont";
3494                                 if (!font_typewriter_opts.empty() || fonts_typewriter_osf) {
3495                                         os << '[';
3496                                         if (fonts_typewriter_osf)
3497                                                 os << "Numbers=OldStyle";
3498                                         if (!font_typewriter_opts.empty()) {
3499                                                 if (fonts_typewriter_osf)
3500                                                         os << ',';
3501                                                 os << font_typewriter_opts;
3502                                         }
3503                                         os << ']';
3504                                 }
3505                                 os << '{' << mono << "}\n";
3506                         }
3507                 }
3508                 return os.str();
3509         }
3510
3511         // Tex Fonts
3512         bool const ot1 = (main_font_encoding() == "default" || main_font_encoding() == "OT1");
3513         bool const dryrun = features.runparams().dryrun;
3514         bool const complete = (fontsSans() == "default" && fontsTypewriter() == "default");
3515         bool const nomath = (fontsMath() != "auto");
3516
3517         // ROMAN FONTS
3518         os << theLaTeXFonts().getLaTeXFont(from_ascii(fontsRoman())).getLaTeXCode(
3519                 dryrun, ot1, complete, fonts_expert_sc, fonts_roman_osf,
3520                 nomath, font_roman_opts);
3521
3522         // SANS SERIF
3523         os << theLaTeXFonts().getLaTeXFont(from_ascii(fontsSans())).getLaTeXCode(
3524                 dryrun, ot1, complete, fonts_expert_sc, fonts_sans_osf,
3525                 nomath, font_sans_opts, fontsSansScale());
3526
3527         // MONOSPACED/TYPEWRITER
3528         os << theLaTeXFonts().getLaTeXFont(from_ascii(fontsTypewriter())).getLaTeXCode(
3529                 dryrun, ot1, complete, fonts_expert_sc, fonts_typewriter_osf,
3530                 nomath, font_typewriter_opts, fontsTypewriterScale());
3531
3532         // MATH
3533         os << theLaTeXFonts().getLaTeXFont(from_ascii(fontsMath())).getLaTeXCode(
3534                 dryrun, ot1, complete, fonts_expert_sc, fonts_roman_osf,
3535                 nomath);
3536
3537         return os.str();
3538 }
3539
3540
3541 Encoding const & BufferParams::encoding() const
3542 {
3543         // Main encoding for LaTeX output.
3544         if (useNonTeXFonts)
3545                 return *(encodings.fromLyXName("utf8-plain"));
3546         if (inputenc == "auto-legacy" || inputenc == "auto-legacy-plain")
3547                 return *language->encoding();
3548         if (inputenc == "utf8" && language->lang() == "japanese")
3549                 return *(encodings.fromLyXName("utf8-platex"));
3550         Encoding const * const enc = encodings.fromLyXName(inputenc);
3551         if (enc)
3552                 return *enc;
3553         LYXERR0("Unknown inputenc value `" << inputenc
3554                << "'. Using `auto' instead.");
3555         return *language->encoding();
3556 }
3557
3558
3559 string const & BufferParams::defaultBiblioStyle() const
3560 {
3561         if (!biblio_style.empty())
3562                 return biblio_style;
3563
3564         map<string, string> const & bs = documentClass().defaultBiblioStyle();
3565         auto cit = bs.find(theCiteEnginesList.getTypeAsString(citeEngineType()));
3566         if (cit != bs.end())
3567                 return cit->second;
3568         else
3569                 return empty_string();
3570 }
3571
3572
3573 bool BufferParams::fullAuthorList() const
3574 {
3575         return documentClass().fullAuthorList();
3576 }
3577
3578
3579 string BufferParams::getCiteAlias(string const & s) const
3580 {
3581         vector<string> commands =
3582                 documentClass().citeCommands(citeEngineType());
3583         // If it is a real command, don't treat it as an alias
3584         if (find(commands.begin(), commands.end(), s) != commands.end())
3585                 return string();
3586         map<string,string> aliases = documentClass().citeCommandAliases();
3587         if (aliases.find(s) != aliases.end())
3588                 return aliases[s];
3589         return string();
3590 }
3591
3592
3593 vector<string> BufferParams::citeCommands() const
3594 {
3595         static CitationStyle const default_style;
3596         vector<string> commands =
3597                 documentClass().citeCommands(citeEngineType());
3598         if (commands.empty())
3599                 commands.push_back(default_style.name);
3600         return commands;
3601 }
3602
3603
3604 vector<CitationStyle> BufferParams::citeStyles() const
3605 {
3606         static CitationStyle const default_style;
3607         vector<CitationStyle> styles =
3608                 documentClass().citeStyles(citeEngineType());
3609         if (styles.empty())
3610                 styles.push_back(default_style);
3611         return styles;
3612 }
3613
3614
3615 string const BufferParams::getBibtexCommand(string const cmd, bool const warn) const
3616 {
3617         // split from options
3618         string command_in;
3619         split(cmd, command_in, ' ');
3620
3621         // Look if the requested command is available. If so, use that.
3622         for (auto const & alts : lyxrc.bibtex_alternatives) {
3623                 string command_prov;
3624                 split(alts, command_prov, ' ');
3625                 if (command_in == command_prov)
3626                         return cmd;
3627         }
3628
3629         // If not, find the most suitable fallback for the current cite framework,
3630         // and warn. Note that we omit options in any such case.
3631         string fallback;
3632         if (useBiblatex()) {
3633                 // For Biblatex, we prefer biber (also for Japanese)
3634                 // and try to fall back to bibtex8
3635                 if (lyxrc.bibtex_alternatives.find("biber") != lyxrc.bibtex_alternatives.end())
3636                         fallback = "biber";
3637                 else if (lyxrc.bibtex_alternatives.find("bibtex8") != lyxrc.bibtex_alternatives.end())
3638                         fallback = "bibtex8";
3639         }
3640         // For classic BibTeX and as last resort for biblatex, try bibtex
3641         if (fallback.empty()) {
3642                 if (lyxrc.bibtex_alternatives.find("bibtex") != lyxrc.bibtex_alternatives.end())
3643                         fallback = "bibtex";
3644         }
3645
3646         if (!warn)
3647                 return fallback;
3648
3649         if (fallback.empty()) {
3650                 frontend::Alert::warning(
3651                         _("No bibliography processor found!"),
3652                         support::bformat(
3653                           _("The bibliography processor requested by this document "
3654                             "(%1$s) is not available and no appropriate "
3655                             "alternative has been found. "
3656                             "No bibliography and references will be generated.\n"
3657                             "Please fix your installation!"),
3658                           from_utf8(cmd)));
3659         } else {
3660                 frontend::Alert::warning(
3661                         _("Requested bibliography processor not found!"),
3662                         support::bformat(
3663                           _("The bibliography processor requested by this document "
3664                             "(%1$s) is not available. "
3665                             "As a fallback, '%2$s' will be used, options are omitted. "
3666                             "This might result in errors or unwanted changes in "
3667                             "the bibliography. Please check carefully!\n"
3668                             "It is suggested to install the missing processor."),
3669                           from_utf8(cmd), from_utf8(fallback)));
3670         }
3671         return fallback;
3672 }
3673
3674
3675 string const BufferParams::bibtexCommand(bool const warn) const
3676 {
3677         // Return document-specific setting if available
3678         if (bibtex_command != "default")
3679                 return getBibtexCommand(bibtex_command, warn);
3680
3681         // If we have "default" in document settings, consult the prefs
3682         // 1. Japanese (uses a specific processor)
3683         if (encoding().package() == Encoding::japanese) {
3684                 if (lyxrc.jbibtex_command != "automatic")
3685                         // Return the specified program, if "automatic" is not set
3686                         return lyxrc.jbibtex_command;
3687                 else if (!useBiblatex()) {
3688                         // With classic BibTeX, return pbibtex, jbibtex, bibtex
3689                         if (lyxrc.jbibtex_alternatives.find("pbibtex") != lyxrc.jbibtex_alternatives.end())
3690                                 return "pbibtex";
3691                         if (lyxrc.jbibtex_alternatives.find("jbibtex") != lyxrc.jbibtex_alternatives.end())
3692                                 return "jbibtex";
3693                         return "bibtex";
3694                 }
3695         }
3696         // 2. All other languages
3697         else if (lyxrc.bibtex_command != "automatic")
3698                 // Return the specified program, if "automatic" is not set
3699                 return getBibtexCommand(lyxrc.bibtex_command, warn);
3700
3701         // 3. Automatic: find the most suitable for the current cite framework
3702         if (useBiblatex()) {
3703                 // For Biblatex, we prefer biber (also for Japanese)
3704                 // and fall back to bibtex8 and, as last resort, bibtex
3705                 if (lyxrc.bibtex_alternatives.find("biber") != lyxrc.bibtex_alternatives.end())
3706                         return "biber";
3707                 else if (lyxrc.bibtex_alternatives.find("bibtex8") != lyxrc.bibtex_alternatives.end())
3708                         return "bibtex8";
3709         }
3710         return "bibtex";
3711 }
3712
3713
3714 bool BufferParams::useBiblatex() const
3715 {
3716         return theCiteEnginesList[citeEngine()]->getCiteFramework() == "biblatex";
3717 }
3718
3719
3720 void BufferParams::invalidateConverterCache() const
3721 {
3722         pimpl_->isExportCacheValid = false;
3723         pimpl_->isViewCacheValid = false;
3724 }
3725
3726
3727 // We shouldn't need to reset the params here, since anything
3728 // we need will be recopied.
3729 void BufferParams::copyForAdvFR(const BufferParams & bp)
3730 {
3731         string const & lang = bp.language->lang();
3732         setLanguage(lang);
3733         layout_modules_ = bp.layout_modules_;
3734         string const & doc_class = bp.documentClass().name();
3735         setBaseClass(doc_class);
3736 }
3737
3738
3739 void BufferParams::setBibFileEncoding(string const & file, string const & enc)
3740 {
3741         bib_encodings[file] = enc;
3742 }
3743
3744
3745 string const BufferParams::bibFileEncoding(string const & file) const
3746 {
3747         if (bib_encodings.find(file) == bib_encodings.end())
3748                 return string();
3749         return bib_encodings.find(file)->second;
3750 }
3751
3752
3753
3754 } // namespace lyx