]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
Remove 3rdparty/boost/Makefile.am
[lyx.git] / src / tex2lyx / Preamble.cpp
1 /**
2  * \file Preamble.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  * \author Uwe Stöhr
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 // {[(
13
14 #include <config.h>
15
16 #include "Preamble.h"
17 #include "tex2lyx.h"
18
19 #include "Encoding.h"
20 #include "LayoutFile.h"
21 #include "Lexer.h"
22 #include "TextClass.h"
23 #include "version.h"
24
25 #include "support/convert.h"
26 #include "support/FileName.h"
27 #include "support/filetools.h"
28 #include "support/lstrings.h"
29
30 #include "support/regex.h"
31
32 #include <algorithm>
33 #include <iostream>
34
35 using namespace std;
36 using namespace lyx::support;
37
38
39 namespace lyx {
40
41 Preamble preamble;
42
43 namespace {
44
45 // CJK languages are handled in text.cpp, polyglossia languages are listed
46 // further down.
47 /**
48  * known babel language names (including synonyms)
49  * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
50  * please keep this in sync with known_coded_languages line by line!
51  */
52 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
53 "american", "arabic", "arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasai",
54 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
55 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
56 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
57 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
58 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
59 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
60 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
61 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
62 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
63 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
64 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
65 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
66 "vietnam", "welsh",
67 0};
68
69 /**
70  * the same as known_languages with .lyx names
71  * please keep this in sync with known_languages line by line!
72  */
73 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
74 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasa",
75 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
76 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
77 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
78 "french", "french", "french", "french", "friulan", "galician", "german", "german",
79 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
80 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
81 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
82 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
83 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
84 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
85 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
86 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
87 "vietnamese", "welsh",
88 0};
89
90 /// languages with british quotes (.lyx names)
91 const char * const known_british_quotes_languages[] = {"british", "welsh", 0};
92
93 /// languages with cjk quotes (.lyx names)
94 const char * const known_cjk_quotes_languages[] = {"chinese-traditional",
95 "japanese", "japanese-cjk", 0};
96
97 /// languages with cjk-angle quotes (.lyx names)
98 const char * const known_cjkangle_quotes_languages[] = {"korean", 0};
99
100 /// languages with danish quotes (.lyx names)
101 const char * const known_danish_quotes_languages[] = {"danish", 0};
102
103 /// languages with english quotes (.lyx names)
104 const char * const known_english_quotes_languages[] = {"american", "australian",
105 "bahasa", "bahasam", "bengali", "brazilian", "canadian", "chinese-simplified", "english",
106 "esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish",
107 "thai", "turkish", "vietnamese", 0};
108
109 /// languages with french quotes (.lyx names)
110 const char * const known_french_quotes_languages[] = {"ancientgreek",
111 "arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton",
112 "canadien", "catalan", "french", "friulan", "galician", "italian", "occitan",
113 "piedmontese", "portuguese", "spanish", "spanish-mexico", 0};
114
115 /// languages with german quotes (.lyx names)
116 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
117 "czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian",
118 "lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene",
119 "uppersorbian", 0};
120
121 /// languages with polish quotes (.lyx names)
122 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
123 "dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0};
124
125 /// languages with russian quotes (.lyx names)
126 const char * const known_russian_quotes_languages[] = {"azerbaijani", "oldrussian",
127 "russian", "ukrainian", 0};
128
129 /// languages with swedish quotes (.lyx names)
130 const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0};
131
132 /// languages with swiss quotes (.lyx names)
133 const char * const known_swiss_quotes_languages[] = {"albanian",
134 "armenian", "basque", "churchslavonic", "german-ch", "german-ch-old",
135 "norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0};
136
137 /// known language packages from the times before babel
138 const char * const known_old_language_packages[] = {"french", "frenchle",
139 "frenchpro", "german", "ngerman", "pmfrench", 0};
140
141 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
142
143 const char * const known_roman_font_packages[] = { "ae", "beraserif", "bookman",
144 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "CrimsonPro", "DejaVuSerif",
145 "DejaVuSerifCondensed", "fourier", "garamondx", "libertine", "libertineRoman", "libertine-type1",
146 "lmodern", "mathdesign", "mathpazo", "mathptmx", "MinionPro", "newcent", "noto", "noto-serif",
147 "PTSerif", "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
148
149 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
150 "biolinum-type1", "cantarell", "Chivo", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "FiraSans", "helvet", "iwona",
151 "iwonac", "iwonal", "iwonalc", "kurier", "kurierc", "kurierl", "kurierlc", "LibertinusSans-LF", "lmss", "noto-sans", "PTSans",
152 "tgadventor", "tgheros", "uop", 0 };
153
154 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt", "courier", "DejaVuSansMono",
155 "FiraMono", "lmtt", "luximono", "libertineMono", "libertineMono-type1", "LibertinusMono-TLF", "lmodern",
156 "mathpazo", "mathptmx", "newcent", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
157
158 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
159
160 const char * const known_latex_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
161 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
162 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
163 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
164 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
165
166 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
167 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
168
169 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
170 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
171 "columnsep", 0};
172
173 /// commands that can start an \if...\else...\endif sequence
174 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
175 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
176 "ifsidecap", "ifupgreek", 0};
177
178 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
179         "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
180         "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
181
182 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
183         "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
184         "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
185
186 /// conditional commands with three arguments like \@ifundefined{}{}{}
187 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
188 0};
189
190 /*!
191  * Known file extensions for TeX files as used by \\includeonly
192  */
193 char const * const known_tex_extensions[] = {"tex", 0};
194
195 /// packages that work only in xetex
196 /// polyglossia is handled separately
197 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
198 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
199 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
200
201 /// packages that are automatically skipped if loaded by LyX
202 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
203 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
204 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
205 "makeidx", "minted", "multirow", "nomencl", "parskip", "pdfpages", "prettyref", "refstyle",
206 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
207 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
208 "xunicode", 0};
209
210 // codes used to remove packages that are loaded automatically by LyX.
211 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
212 const char package_beg_sep = '\001';
213 const char package_mid_sep = '\002';
214 const char package_end_sep = '\003';
215
216
217 // returns true if at least one of the options in what has been found
218 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
219 {
220         if (opts.empty())
221                 return false;
222
223         bool found = false;
224         // the last language option is the document language (for babel and LyX)
225         // the last size option is the document font size
226         vector<string>::iterator it;
227         vector<string>::iterator position = opts.begin();
228         for (; *what; ++what) {
229                 it = find(opts.begin(), opts.end(), *what);
230                 if (it != opts.end()) {
231                         if (it >= position) {
232                                 found = true;
233                                 target = *what;
234                                 position = it;
235                         }
236                 }
237         }
238         return found;
239 }
240
241
242 void delete_opt(vector<string> & opts, char const * const * what)
243 {
244         if (opts.empty())
245                 return;
246
247         // remove found options from the list
248         // do this after handle_opt to avoid potential memory leaks
249         vector<string>::iterator it;
250         for (; *what; ++what) {
251                 it = find(opts.begin(), opts.end(), *what);
252                 if (it != opts.end())
253                         opts.erase(it);
254         }
255 }
256
257
258 /*!
259  * Split a package options string (keyval format) into a vector.
260  * Example input:
261  *   authorformat=smallcaps,
262  *   commabeforerest,
263  *   titleformat=colonsep,
264  *   bibformat={tabular,ibidem,numbered}
265  */
266 vector<string> split_options(string const & input)
267 {
268         vector<string> options;
269         string option;
270         Parser p(input);
271         while (p.good()) {
272                 Token const & t = p.get_token();
273                 if (t.asInput() == ",") {
274                         options.push_back(trimSpaceAndEol(option));
275                         option.erase();
276                 } else if (t.asInput() == "=") {
277                         option += '=';
278                         p.skip_spaces(true);
279                         if (p.next_token().asInput() == "{")
280                                 option += '{' + p.getArg('{', '}') + '}';
281                 } else if (t.cat() != catSpace && t.cat() != catComment)
282                         option += t.asInput();
283         }
284
285         if (!option.empty())
286                 options.push_back(trimSpaceAndEol(option));
287
288         return options;
289 }
290
291
292 /*!
293  * Retrieve a keyval option "name={value with=sign}" named \p name from
294  * \p options and return the value.
295  * The found option is also removed from \p options.
296  */
297 string process_keyval_opt(vector<string> & options, string const & name)
298 {
299         for (size_t i = 0; i < options.size(); ++i) {
300                 vector<string> option;
301                 split(options[i], option, '=');
302                 if (option.size() < 2)
303                         continue;
304                 if (option[0] == name) {
305                         options.erase(options.begin() + i);
306                         option.erase(option.begin());
307                         return join(option, "=");
308                 }
309         }
310         return "";
311 }
312
313 } // anonymous namespace
314
315
316 /**
317  * known polyglossia language names (including variants)
318  * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
319  */
320 const char * const Preamble::polyglossia_languages[] = {
321 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
322 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
323 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
324 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
325 "galician", "greek", "monotonic", "hebrew", "hindi",
326 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
327 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
328 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
329 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
330 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
331 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
332 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
333 // not yet supported by LyX: "korean", "nko"
334
335 /**
336  * the same as polyglossia_languages with .lyx names
337  * please keep this in sync with polyglossia_languages line by line!
338  */
339 const char * const Preamble::coded_polyglossia_languages[] = {
340 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
341 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
342 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
343 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
344 "galician", "greek", "greek", "hebrew", "hindi",
345 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
346 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
347 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
348 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
349 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
350 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
351 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
352 // not yet supported by LyX: "korean-polyglossia", "nko"
353
354
355 bool Preamble::usePolyglossia() const
356 {
357         return h_use_non_tex_fonts && h_language_package == "default";
358 }
359
360
361 bool Preamble::indentParagraphs() const
362 {
363         return h_paragraph_separation == "indent";
364 }
365
366
367 bool Preamble::isPackageUsed(string const & package) const
368 {
369         return used_packages.find(package) != used_packages.end();
370 }
371
372
373 bool Preamble::isPackageAutoLoaded(string const & package) const
374 {
375         return auto_packages.find(package) != auto_packages.end();
376 }
377
378
379 vector<string> Preamble::getPackageOptions(string const & package) const
380 {
381         map<string, vector<string> >::const_iterator it = used_packages.find(package);
382         if (it != used_packages.end())
383                 return it->second;
384         return vector<string>();
385 }
386
387
388 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
389 {
390         auto_packages.insert(package);
391 }
392
393
394 void Preamble::addModule(string const & module)
395 {
396         for (auto const & m : used_modules) {
397                 if (m == module)
398                         return;
399         }
400         used_modules.push_back(module);
401 }
402
403
404 void Preamble::suppressDate(bool suppress)
405 {
406         if (suppress)
407                 h_suppress_date = "true";
408         else
409                 h_suppress_date = "false";
410 }
411
412
413 void Preamble::registerAuthor(std::string const & name, string const & initials)
414 {
415         Author author(from_utf8(name), empty_docstring(), from_utf8(initials));
416         author.setUsed(true);
417         authors_.record(author);
418         h_tracking_changes = "true";
419         h_output_changes = "true";
420 }
421
422
423 Author const & Preamble::getAuthor(std::string const & name) const
424 {
425         Author author(from_utf8(name), empty_docstring(), empty_docstring());
426         for (AuthorList::Authors::const_iterator it = authors_.begin();
427              it != authors_.end(); ++it)
428                 if (*it == author)
429                         return *it;
430         static Author const dummy;
431         return dummy;
432 }
433
434
435 int Preamble::getSpecialTableColumnArguments(char c) const
436 {
437         map<char, int>::const_iterator it = special_columns_.find(c);
438         if (it == special_columns_.end())
439                 return -1;
440         return it->second;
441 }
442
443
444 void Preamble::add_package(string const & name, vector<string> & options)
445 {
446         // every package inherits the global options
447         used_packages.insert({name, split_options(h_options)});
448
449         // Insert options passed via PassOptionsToPackage
450         for (auto const & p : extra_package_options_) {
451                 if (p.first == name) {
452                         vector<string> eo = getVectorFromString(p.second);
453                         for (auto const & eoi : eo)
454                                 options.push_back(eoi);
455                 }
456
457         }
458         vector<string> & v = used_packages[name];
459         v.insert(v.end(), options.begin(), options.end());
460         if (name == "jurabib") {
461                 // Don't output the order argument (see the cite command
462                 // handling code in text.cpp).
463                 vector<string>::iterator end =
464                         remove(options.begin(), options.end(), "natbiborder");
465                 end = remove(options.begin(), end, "jurabiborder");
466                 options.erase(end, options.end());
467         }
468 }
469
470 void Preamble::setTextClass(string const & tclass, TeX2LyXDocClass & tc)
471 {
472         h_textclass = tclass;
473         tc.setName(h_textclass);
474         if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
475                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
476                 exit(EXIT_FAILURE);
477         }
478 }
479
480
481 namespace {
482
483 // Given is a string like "scaled=0.9" or "scale=0.9", return 0.9 * 100
484 bool scale_as_percentage(string const & scale, string & percentage)
485 {
486         if (contains(scale, '=')) {
487                 string const value = support::split(scale, '=');
488                 if (isStrDbl(value)) {
489                         percentage = convert<string>(
490                                 static_cast<int>(100 * convert<double>(value)));
491                         return true;
492                 }
493         }
494         return false;
495 }
496
497
498 string remove_braces(string const & value)
499 {
500         if (value.empty())
501                 return value;
502         if (value[0] == '{' && value[value.length()-1] == '}')
503                 return value.substr(1, value.length()-2);
504         return value;
505 }
506
507 } // anonymous namespace
508
509
510 Preamble::Preamble() : one_language(true), explicit_babel(false),
511         title_layout_found(false), index_number(0), h_font_cjk_set(false)
512 {
513         //h_backgroundcolor;
514         //h_boxbgcolor;
515         h_biblio_style            = "plain";
516         h_bibtex_command          = "default";
517         h_cite_engine             = "basic";
518         h_cite_engine_type        = "default";
519         h_color                   = "#008000";
520         h_defskip                 = "medskip";
521         h_dynamic_quotes          = false;
522         //h_float_placement;
523         //h_fontcolor;
524         h_fontencoding            = "default";
525         h_font_roman[0]           = "default";
526         h_font_roman[1]           = "default";
527         h_font_sans[0]            = "default";
528         h_font_sans[1]            = "default";
529         h_font_typewriter[0]      = "default";
530         h_font_typewriter[1]      = "default";
531         h_font_math[0]            = "auto";
532         h_font_math[1]            = "auto";
533         h_font_default_family     = "default";
534         h_use_non_tex_fonts       = false;
535         h_font_sc                 = "false";
536         h_font_roman_osf          = "false";
537         h_font_sans_osf           = "false";
538         h_font_typewriter_osf     = "false";
539         h_font_sf_scale[0]        = "100";
540         h_font_sf_scale[1]        = "100";
541         h_font_tt_scale[0]        = "100";
542         h_font_tt_scale[1]        = "100";
543         // h_font_roman_opts;
544         // h_font_sans_opts;
545         // h_font_typewriter_opts;
546         //h_font_cjk
547         h_is_mathindent           = "0";
548         h_math_numbering_side     = "default";
549         h_graphics                = "default";
550         h_default_output_format   = "default";
551         h_html_be_strict          = "false";
552         h_html_css_as_file        = "0";
553         h_html_math_output        = "0";
554         h_docbook_table_output    = "0";
555         h_index[0]                = "Index";
556         h_index_command           = "default";
557         h_inputencoding           = "auto-legacy";
558         h_justification           = "true";
559         h_language                = "english";
560         h_language_package        = "none";
561         //h_listings_params;
562         h_maintain_unincluded_children = "no";
563         //h_margins;
564         //h_notefontcolor;
565         //h_options;
566         h_output_changes          = "false";
567         h_change_bars             = "false";
568         h_output_sync             = "0";
569         //h_output_sync_macro
570         h_papercolumns            = "1";
571         h_paperfontsize           = "default";
572         h_paperorientation        = "portrait";
573         h_paperpagestyle          = "default";
574         //h_papersides;
575         h_papersize               = "default";
576         h_paragraph_indentation   = "default";
577         h_paragraph_separation    = "indent";
578         //h_pdf_title;
579         //h_pdf_author;
580         //h_pdf_subject;
581         //h_pdf_keywords;
582         h_pdf_bookmarks           = "0";
583         h_pdf_bookmarksnumbered   = "0";
584         h_pdf_bookmarksopen       = "0";
585         h_pdf_bookmarksopenlevel  = "1";
586         h_pdf_breaklinks          = "0";
587         h_pdf_pdfborder           = "0";
588         h_pdf_colorlinks          = "0";
589         h_pdf_backref             = "section";
590         h_pdf_pdfusetitle         = "0";
591         //h_pdf_pagemode;
592         //h_pdf_quoted_options;
593         h_quotes_style         = "english";
594         h_secnumdepth             = "3";
595         h_shortcut[0]             = "idx";
596         h_spacing                 = "single";
597         h_save_transient_properties = "true";
598         h_suppress_date           = "false";
599         h_textclass               = "article";
600         h_tocdepth                = "3";
601         h_tracking_changes        = "false";
602         h_use_bibtopic            = "false";
603         h_use_dash_ligatures      = "true";
604         h_use_indices             = "false";
605         h_use_geometry            = "false";
606         h_use_default_options     = "false";
607         h_use_hyperref            = "false";
608         h_use_microtype           = "false";
609         h_use_lineno              = "false";
610         h_use_refstyle            = false;
611         h_use_minted              = false;
612         h_use_packages["amsmath"]    = "1";
613         h_use_packages["amssymb"]    = "0";
614         h_use_packages["cancel"]     = "0";
615         h_use_packages["esint"]      = "1";
616         h_use_packages["mhchem"]     = "0";
617         h_use_packages["mathdots"]   = "0";
618         h_use_packages["mathtools"]  = "0";
619         h_use_packages["stackrel"]   = "0";
620         h_use_packages["stmaryrd"]   = "0";
621         h_use_packages["undertilde"] = "0";
622 }
623
624
625 void Preamble::handle_hyperref(vector<string> & options)
626 {
627         // FIXME swallow inputencoding changes that might surround the
628         //       hyperref setup if it was written by LyX
629         h_use_hyperref = "true";
630         // swallow "unicode=true", since LyX does always write that
631         vector<string>::iterator it =
632                 find(options.begin(), options.end(), "unicode=true");
633         if (it != options.end())
634                 options.erase(it);
635         it = find(options.begin(), options.end(), "pdfusetitle");
636         if (it != options.end()) {
637                 h_pdf_pdfusetitle = "1";
638                 options.erase(it);
639         }
640         string bookmarks = process_keyval_opt(options, "bookmarks");
641         if (bookmarks == "true")
642                 h_pdf_bookmarks = "1";
643         else if (bookmarks == "false")
644                 h_pdf_bookmarks = "0";
645         if (h_pdf_bookmarks == "1") {
646                 string bookmarksnumbered =
647                         process_keyval_opt(options, "bookmarksnumbered");
648                 if (bookmarksnumbered == "true")
649                         h_pdf_bookmarksnumbered = "1";
650                 else if (bookmarksnumbered == "false")
651                         h_pdf_bookmarksnumbered = "0";
652                 string bookmarksopen =
653                         process_keyval_opt(options, "bookmarksopen");
654                 if (bookmarksopen == "true")
655                         h_pdf_bookmarksopen = "1";
656                 else if (bookmarksopen == "false")
657                         h_pdf_bookmarksopen = "0";
658                 if (h_pdf_bookmarksopen == "1") {
659                         string bookmarksopenlevel =
660                                 process_keyval_opt(options, "bookmarksopenlevel");
661                         if (!bookmarksopenlevel.empty())
662                                 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
663                 }
664         }
665         string breaklinks = process_keyval_opt(options, "breaklinks");
666         if (breaklinks == "true")
667                 h_pdf_breaklinks = "1";
668         else if (breaklinks == "false")
669                 h_pdf_breaklinks = "0";
670         string pdfborder = process_keyval_opt(options, "pdfborder");
671         if (pdfborder == "{0 0 0}")
672                 h_pdf_pdfborder = "1";
673         else if (pdfborder == "{0 0 1}")
674                 h_pdf_pdfborder = "0";
675         string backref = process_keyval_opt(options, "backref");
676         if (!backref.empty())
677                 h_pdf_backref = backref;
678         string colorlinks = process_keyval_opt(options, "colorlinks");
679         if (colorlinks == "true")
680                 h_pdf_colorlinks = "1";
681         else if (colorlinks == "false")
682                 h_pdf_colorlinks = "0";
683         string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
684         if (!pdfpagemode.empty())
685                 h_pdf_pagemode = pdfpagemode;
686         string pdftitle = process_keyval_opt(options, "pdftitle");
687         if (!pdftitle.empty()) {
688                 h_pdf_title = remove_braces(pdftitle);
689         }
690         string pdfauthor = process_keyval_opt(options, "pdfauthor");
691         if (!pdfauthor.empty()) {
692                 h_pdf_author = remove_braces(pdfauthor);
693         }
694         string pdfsubject = process_keyval_opt(options, "pdfsubject");
695         if (!pdfsubject.empty())
696                 h_pdf_subject = remove_braces(pdfsubject);
697         string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
698         if (!pdfkeywords.empty())
699                 h_pdf_keywords = remove_braces(pdfkeywords);
700         if (!options.empty()) {
701                 if (!h_pdf_quoted_options.empty())
702                         h_pdf_quoted_options += ',';
703                 h_pdf_quoted_options += join(options, ",");
704                 options.clear();
705         }
706 }
707
708
709 void Preamble::handle_geometry(vector<string> & options)
710 {
711         h_use_geometry = "true";
712         vector<string>::iterator it;
713         // paper orientation
714         if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
715                 h_paperorientation = "landscape";
716                 options.erase(it);
717         }
718         // paper size
719         // keyval version: "paper=letter" or "paper=letterpaper"
720         string paper = process_keyval_opt(options, "paper");
721         if (!paper.empty())
722                 if (suffixIs(paper, "paper"))
723                         paper = subst(paper, "paper", "");
724         // alternative version: "letterpaper"
725         handle_opt(options, known_latex_paper_sizes, paper);
726         if (suffixIs(paper, "paper"))
727                 paper = subst(paper, "paper", "");
728         delete_opt(options, known_latex_paper_sizes);
729         if (!paper.empty())
730                 h_papersize = paper;
731         // page margins
732         char const * const * margin = known_paper_margins;
733         for (; *margin; ++margin) {
734                 string value = process_keyval_opt(options, *margin);
735                 if (!value.empty()) {
736                         int k = margin - known_paper_margins;
737                         string name = known_coded_paper_margins[k];
738                         h_margins += '\\' + name + ' ' + value + '\n';
739                 }
740         }
741 }
742
743
744 void Preamble::handle_package(Parser &p, string const & name,
745                               string const & opts, bool in_lyx_preamble,
746                               bool detectEncoding)
747 {
748         vector<string> options = split_options(opts);
749         add_package(name, options);
750
751         if (is_known(name, known_xetex_packages)) {
752                 xetex = true;
753                 h_use_non_tex_fonts = true;
754                 registerAutomaticallyLoadedPackage("fontspec");
755                 if (h_inputencoding == "auto-legacy")
756                         p.setEncoding("UTF-8");
757         }
758
759         // vector of all options for easier parsing and
760         // skipping
761         vector<string> allopts = getVectorFromString(opts);
762         // this stores the potential extra options
763         string xopts;
764
765         //
766         // roman fonts
767         //
768
769         // By default, we use the package name as LyX font name,
770         // so this only needs to be reset if these names differ
771         if (is_known(name, known_roman_font_packages))
772                 h_font_roman[0] = name;
773
774         if (name == "ccfonts") {
775                 for (auto const & opt : allopts) {
776                         if (!xopts.empty())
777                                 xopts += ", ";
778                         xopts += opt;
779                 }
780                 if (!xopts.empty())
781                         h_font_roman_opts = xopts;
782                 options.clear();
783         }
784
785         if (name == "lmodern") {
786                 for (auto const & opt : allopts) {
787                         if (!xopts.empty())
788                                 xopts += ", ";
789                         xopts += opt;
790                 }
791                 if (!xopts.empty())
792                         h_font_roman_opts = xopts;
793                 options.clear();
794         }
795
796         if (name == "fourier") {
797                 h_font_roman[0] = "utopia";
798                 for (auto const & opt : allopts) {
799                         if (opt == "osf") {
800                                 h_font_roman_osf = "true";
801                                 continue;
802                         }
803                         if (opt == "expert") {
804                                 h_font_sc = "true";
805                                 continue;
806                         }
807                         if (!xopts.empty())
808                                 xopts += ", ";
809                         xopts += opt;
810                 }
811                 if (!xopts.empty())
812                         h_font_roman_opts = xopts;
813                 options.clear();
814         }
815
816         if (name == "garamondx") {
817                 for (auto const & opt : allopts) {
818                         if (opt == "osfI") {
819                                 h_font_roman_osf = "true";
820                                 continue;
821                         }
822                         if (!xopts.empty())
823                                 xopts += ", ";
824                         xopts += opt;
825                 }
826                 if (!xopts.empty())
827                         h_font_roman_opts = xopts;
828                 options.clear();
829         }
830
831         if (name == "libertine") {
832                 // this automatically invokes biolinum
833                 h_font_sans[0] = "biolinum";
834                 // as well as libertineMono
835                 h_font_typewriter[0] = "libertine-mono";
836                 for (auto const & opt : allopts) {
837                         if (opt == "osf") {
838                                 h_font_roman_osf = "true";
839                                 continue;
840                         }
841                         if (opt == "lining") {
842                                 h_font_roman_osf = "false";
843                                 continue;
844                         }
845                         if (!xopts.empty())
846                                 xopts += ", ";
847                         xopts += opt;
848                 }
849                 if (!xopts.empty())
850                         h_font_roman_opts = xopts;
851                 options.clear();
852         }
853
854         if (name == "libertineRoman" || name == "libertine-type1") {
855                 h_font_roman[0] = "libertine";
856                 // NOTE: contrary to libertine.sty, libertineRoman
857                 // and libertine-type1 do not automatically invoke
858                 // biolinum and libertineMono
859                 if (opts == "lining")
860                         h_font_roman_osf = "false";
861                 else if (opts == "osf")
862                         h_font_roman_osf = "true";
863         }
864
865         if (name == "libertinus" || name == "libertinus-type1") {
866                 bool sf = true;
867                 bool tt = true;
868                 bool rm = true;
869                 bool osf = false;
870                 string scalesf;
871                 string scalett;
872                 for (auto const & opt : allopts) {
873                         if (opt == "rm" || opt == "serif") {
874                                 tt = false;
875                                 sf = false;
876                                 continue;
877                         }
878                         if (opt == "sf" || opt == "sans") {
879                                 tt = false;
880                                 rm = false;
881                                 continue;
882                         }
883                         if (opt == "tt=false" || opt == "mono=false") {
884                                 tt = false;
885                                 continue;
886                         }
887                         if (opt == "osf") {
888                                 osf = true;
889                                 continue;
890                         }
891                         if (opt == "scaleSF") {
892                                 scalesf = opt;
893                                 continue;
894                         }
895                         if (opt == "scaleTT") {
896                                 scalett = opt;
897                                 continue;
898                         }
899                         if (opt == "lining") {
900                                 h_font_roman_osf = "false";
901                                 continue;
902                         }
903                         if (!xopts.empty())
904                                 xopts += ", ";
905                         xopts += opt;
906                 }
907                 if (rm) {
908                         h_font_roman[0] = "libertinus";
909                         if (osf)
910                                 h_font_roman_osf = "true";
911                         else
912                                 h_font_roman_osf = "false";
913                 }
914                 if (sf) {
915                         h_font_sans[0] = "LibertinusSans-LF";
916                         if (osf)
917                                 h_font_sans_osf = "true";
918                         else
919                                 h_font_sans_osf = "false";
920                         if (!scalesf.empty())
921                                 scale_as_percentage(scalesf, h_font_sf_scale[0]);
922                 }
923                 if (tt) {
924                         h_font_typewriter[0] = "LibertinusMono-TLF";
925                         if (!scalett.empty())
926                                 scale_as_percentage(scalett, h_font_tt_scale[0]);
927                 }
928                 if (!xopts.empty())
929                         h_font_roman_opts = xopts;
930                 options.clear();
931         }
932
933         if (name == "MinionPro") {
934                 h_font_roman[0] = "minionpro";
935                 h_font_roman_osf = "true";
936                 h_font_math[0] = "auto";
937                 for (auto const & opt : allopts) {
938                         if (opt == "lf") {
939                                 h_font_roman_osf = "false";
940                                 continue;
941                         }
942                         if (opt == "onlytext") {
943                                 h_font_math[0] = "default";
944                                 continue;
945                         }
946                         if (!xopts.empty())
947                                 xopts += ", ";
948                         xopts += opt;
949                 }
950                 if (!xopts.empty())
951                         h_font_roman_opts = xopts;
952                 options.clear();
953         }
954
955         if (name == "mathdesign") {
956                 for (auto const & opt : allopts) {
957                         if (opt == "charter") {
958                                 h_font_roman[0] = "md-charter";
959                                 continue;
960                         }
961                         if (opt == "garamond") {
962                                 h_font_roman[0] = "md-garamond";
963                                 continue;
964                         }
965                         if (opt == "utopia") {
966                                 h_font_roman[0] = "md-utopia";
967                                 continue;
968                         }
969                         if (opt == "expert") {
970                                 h_font_sc = "true";
971                                 h_font_roman_osf = "true";
972                                 continue;
973                         }
974                 }
975         }
976
977         else if (name == "mathpazo") {
978                 h_font_roman[0] = "palatino";
979                 for (auto const & opt : allopts) {
980                         if (opt == "osf") {
981                                 h_font_roman_osf = "true";
982                                 continue;
983                         }
984                         if (opt == "sc") {
985                                 h_font_sc = "true";
986                                 continue;
987                         }
988                         if (!xopts.empty())
989                                 xopts += ", ";
990                         xopts += opt;
991                 }
992                 if (!xopts.empty())
993                         h_font_roman_opts = xopts;
994                 options.clear();
995         }
996
997         else if (name == "mathptmx") {
998                 h_font_roman[0] = "times";
999                 for (auto const & opt : allopts) {
1000                         if (!xopts.empty())
1001                                 xopts += ", ";
1002                         xopts += opt;
1003                 }
1004                 if (!xopts.empty())
1005                         h_font_roman_opts = xopts;
1006                 options.clear();
1007         }
1008
1009         if (name == "crimson")
1010                 h_font_roman[0] = "cochineal";
1011
1012         if (name == "cochineal") {
1013                 for (auto const & opt : allopts) {
1014                         if (opt == "osf" || opt == "oldstyle") {
1015                                 h_font_roman_osf = "true";
1016                                 continue;
1017                         }
1018                         if (opt == "proportional" || opt == "p")
1019                                 continue;
1020                         if (!xopts.empty())
1021                                 xopts += ", ";
1022                         xopts += opt;
1023                 }
1024                 if (!xopts.empty())
1025                         h_font_roman_opts = xopts;
1026                 options.clear();
1027         }
1028
1029         if (name == "CrimsonPro") {
1030                 h_font_roman_osf = "true";
1031                 for (auto const & opt : allopts) {
1032                         if (opt == "lf" || opt == "lining") {
1033                                 h_font_roman_osf = "false";
1034                                 continue;
1035                         }
1036                         if (opt == "proportional" || opt == "p")
1037                                 continue;
1038                         if (opt == "medium") {
1039                                 h_font_roman[0] = "CrimsonProMedium";
1040                                 continue;
1041                         }
1042                         if (opt == "extralight") {
1043                                 h_font_roman[0] = "CrimsonProExtraLight";
1044                                 continue;
1045                         }
1046                         if (opt == "light") {
1047                                 h_font_roman[0] = "CrimsonProLight";
1048                                 continue;
1049                         }
1050                         if (!xopts.empty())
1051                                 xopts += ", ";
1052                         xopts += opt;
1053                 }
1054                 if (!xopts.empty())
1055                         h_font_roman_opts = xopts;
1056                 options.clear();
1057         }
1058
1059
1060         if (name == "eco")
1061                 // font uses old-style figure
1062                 h_font_roman_osf = "true";
1063
1064         if (name == "paratype") {
1065                 // in this case all fonts are ParaType
1066                 h_font_roman[0] = "PTSerif-TLF";
1067                 h_font_sans[0] = "default";
1068                 h_font_typewriter[0] = "default";
1069         }
1070
1071         if (name == "PTSerif")
1072                 h_font_roman[0] = "PTSerif-TLF";
1073
1074         if (name == "XCharter") {
1075                 h_font_roman[0] = "xcharter";
1076                 for (auto const & opt : allopts) {
1077                         if (opt == "osf") {
1078                                 h_font_roman_osf = "true";
1079                                 continue;
1080                         }
1081                         if (!xopts.empty())
1082                                 xopts += ", ";
1083                         xopts += opt;
1084                 }
1085                 if (!xopts.empty())
1086                         h_font_roman_opts = xopts;
1087                 options.clear();
1088         }
1089
1090         if (name == "plex-serif") {
1091                 h_font_roman[0] = "IBMPlexSerif";
1092                 for (auto const & opt : allopts) {
1093                         if (opt == "thin") {
1094                                 h_font_roman[0] = "IBMPlexSerifThin";
1095                                 continue;
1096                         }
1097                         if (opt == "extralight") {
1098                                 h_font_roman[0] = "IBMPlexSerifExtraLight";
1099                                 continue;
1100                         }
1101                         if (opt == "light") {
1102                                 h_font_roman[0] = "IBMPlexSerifLight";
1103                                 continue;
1104                         }
1105                         if (!xopts.empty())
1106                                 xopts += ", ";
1107                         xopts += opt;
1108                 }
1109                 if (!xopts.empty())
1110                         h_font_roman_opts = xopts;
1111                 options.clear();
1112         }
1113
1114         if (name == "noto-serif" || name == "noto") {
1115                 bool rm = false;
1116                 bool rmx = false;
1117                 bool sf = false;
1118                 bool sfx = false;
1119                 bool tt = false;
1120                 bool thin = false;
1121                 bool extralight = false;
1122                 bool light = false;
1123                 bool medium = false;
1124                 bool osf = false;
1125                 string scl;
1126                 if (name == "noto") {
1127                         rm = true;
1128                         sf = true;
1129                         tt = true;
1130                 }
1131                 // Since the options might apply to different shapes,
1132                 // we need to parse all options first and then handle them.
1133                 for (auto const & opt : allopts) {
1134                         if (opt == "regular")
1135                                 // skip
1136                                 continue;
1137                         if (opt == "rm") {
1138                                 rm = true;
1139                                 rmx = true;
1140                                 sf = sfx;
1141                                 tt = false;
1142                                 continue;
1143                         }
1144                         if (opt == "thin") {
1145                                 thin = true;
1146                                 continue;
1147                         }
1148                         if (opt == "extralight") {
1149                                 extralight = true;
1150                                 continue;
1151                         }
1152                         if (opt == "light") {
1153                                 light = true;
1154                                 continue;
1155                         }
1156                         if (opt == "medium") {
1157                                 medium = true;
1158                                 continue;
1159                         }
1160                         if (opt == "sf") {
1161                                 sfx = true;
1162                                 sf = true;
1163                                 rm = rmx;
1164                                 tt = false;
1165                                 continue;
1166                         }
1167                         if (opt == "nott") {
1168                                 tt = false;
1169                                 continue;
1170                         }
1171                         if (opt == "osf") {
1172                                 osf = true;
1173                                 continue;
1174                         }
1175                         if (prefixIs(opt, "scaled=")) {
1176                                 scl = opt;
1177                                 continue;
1178                         }
1179                         if (!xopts.empty())
1180                                 xopts += ", ";
1181                         xopts += opt;
1182                 }
1183                 options.clear();
1184                 // handle options that might affect different shapes
1185                 if (name == "noto-serif" || rm) {
1186                         if (thin)
1187                                 h_font_roman[0] = "NotoSerifThin";
1188                         else if (extralight)
1189                                 h_font_roman[0] = "NotoSerifExtralight";
1190                         else if (light)
1191                                 h_font_roman[0] = "NotoSerifLight";
1192                         else if (medium)
1193                                 h_font_roman[0] = "NotoSerifMedium";
1194                         else
1195                                 h_font_roman[0] = "NotoSerifRegular";
1196                         if (osf)
1197                                 h_font_roman_osf = "true";
1198                         if (!xopts.empty())
1199                                 h_font_roman_opts = xopts;
1200                 }
1201                 if (name == "noto" && sf) {
1202                         if (thin)
1203                                 h_font_sans[0] = "NotoSansThin";
1204                         else if (extralight)
1205                                 h_font_sans[0] = "NotoSansExtralight";
1206                         else if (light)
1207                                 h_font_sans[0] = "NotoSansLight";
1208                         else if (medium)
1209                                 h_font_sans[0] = "NotoSansMedium";
1210                         else
1211                                 h_font_sans[0] = "NotoSansRegular";
1212                         if (osf)
1213                                 h_font_sans_osf = "true";
1214                         if (!scl.empty())
1215                                 scale_as_percentage(scl, h_font_sf_scale[0]);
1216                         if (!xopts.empty())
1217                                 h_font_sans_opts = xopts;
1218                 }
1219                 if (name == "noto" && tt) {
1220                         h_font_typewriter[0] = "NotoMonoRegular";
1221                         if (osf)
1222                                 h_font_typewriter_osf = "true";
1223                         if (!scl.empty())
1224                                 scale_as_percentage(scl, h_font_tt_scale[0]);
1225                         if (!xopts.empty())
1226                                 h_font_typewriter_opts = xopts;
1227                 }
1228         }
1229
1230         if (name == "sourceserifpro") {
1231                 h_font_roman[0] = "ADOBESourceSerifPro";
1232                 for (auto const & opt : allopts) {
1233                         if (opt == "osf") {
1234                                 h_font_roman_osf = "true";
1235                                 continue;
1236                         }
1237                         if (!xopts.empty())
1238                                 xopts += ", ";
1239                         xopts += opt;
1240                 }
1241                 if (!xopts.empty())
1242                         h_font_roman_opts = xopts;
1243                 options.clear();
1244         }
1245
1246         //
1247         // sansserif fonts
1248         //
1249
1250         // By default, we use the package name as LyX font name,
1251         // so this only needs to be reset if these names differ.
1252         // Also, we handle the scaling option here generally.
1253         if (is_known(name, known_sans_font_packages)) {
1254                 h_font_sans[0] = name;
1255                 if (contains(opts, "scale")) {
1256                         vector<string>::iterator it = allopts.begin();
1257                         for (; it != allopts.end() ; ++it) {
1258                                 string const opt = *it;
1259                                 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1260                                         if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1261                                                 allopts.erase(it);
1262                                                 break;
1263                                         }
1264                                 }
1265                         }
1266                 }
1267         }
1268
1269         if (name == "biolinum" || name == "biolinum-type1") {
1270                 h_font_sans[0] = "biolinum";
1271                 for (auto const & opt : allopts) {
1272                         if (prefixIs(opt, "osf")) {
1273                                 h_font_sans_osf = "true";
1274                                 continue;
1275                         }
1276                         if (!xopts.empty())
1277                                 xopts += ", ";
1278                         xopts += opt;
1279                 }
1280                 if (!xopts.empty())
1281                         h_font_sans_opts = xopts;
1282                 options.clear();
1283         }
1284
1285         if (name == "cantarell") {
1286                 for (auto const & opt : allopts) {
1287                         if (opt == "defaultsans")
1288                                 continue;
1289                         if (prefixIs(opt, "oldstyle")) {
1290                                 h_font_sans_osf = "true";
1291                                 continue;
1292                         }
1293                         if (!xopts.empty())
1294                                 xopts += ", ";
1295                         xopts += opt;
1296                 }
1297                 if (!xopts.empty())
1298                         h_font_sans_opts = xopts;
1299                 options.clear();
1300         }
1301
1302         if (name == "Chivo") {
1303                 for (auto const & opt : allopts) {
1304                         if (opt == "thin") {
1305                                 h_font_roman[0] = "ChivoThin";
1306                                 continue;
1307                         }
1308                         if (opt == "light") {
1309                                 h_font_roman[0] = "ChivoLight";
1310                                 continue;
1311                         }
1312                         if (opt == "regular") {
1313                                 h_font_roman[0] = "Chivo";
1314                                 continue;
1315                         }
1316                         if (opt == "medium") {
1317                                 h_font_roman[0] = "ChivoMedium";
1318                                 continue;
1319                         }
1320                         if (prefixIs(opt, "oldstyle")) {
1321                                 h_font_sans_osf = "true";
1322                                 continue;
1323                         }
1324                         if (!xopts.empty())
1325                                 xopts += ", ";
1326                         xopts += opt;
1327                 }
1328                 if (!xopts.empty())
1329                         h_font_sans_opts = xopts;
1330                 options.clear();
1331         }
1332
1333         if (name == "PTSans") {
1334                 h_font_sans[0] = "PTSans-TLF";
1335         }
1336
1337         if (name == "FiraSans") {
1338                 h_font_sans_osf = "true";
1339                 for (auto const & opt : allopts) {
1340                         if (opt == "book") {
1341                                 h_font_sans[0] = "FiraSansBook";
1342                                 continue;
1343                         }
1344                         if (opt == "thin") {
1345                                 continue;
1346                         }
1347                         if (opt == "extralight") {
1348                                 h_font_sans[0] = "FiraSansExtralight";
1349                                 continue;
1350                         }
1351                         if (opt == "light") {
1352                                 h_font_sans[0] = "FiraSansLight";
1353                                 continue;
1354                         }
1355                         if (opt == "ultralight") {
1356                                 h_font_sans[0] = "FiraSansUltralight";
1357                                 continue;
1358                         }
1359                         if (opt == "thin") {
1360                                 h_font_sans[0] = "FiraSansThin";
1361                                 continue;
1362                         }
1363                         if (opt == "lf" || opt == "lining") {
1364                                 h_font_sans_osf = "false";
1365                                 continue;
1366                         }
1367                         if (!xopts.empty())
1368                                 xopts += ", ";
1369                         xopts += opt;
1370                 }
1371                 if (!xopts.empty())
1372                         h_font_sans_opts = xopts;
1373                 options.clear();
1374         }
1375
1376         if (name == "plex-sans") {
1377                 h_font_sans[0] = "IBMPlexSans";
1378                 for (auto const & opt : allopts) {
1379                         if (opt == "condensed") {
1380                                 h_font_sans[0] = "IBMPlexSansCondensed";
1381                                 continue;
1382                         }
1383                         if (opt == "thin") {
1384                                 h_font_sans[0] = "IBMPlexSansThin";
1385                                 continue;
1386                         }
1387                         if (opt == "extralight") {
1388                                 h_font_sans[0] = "IBMPlexSansExtraLight";
1389                                 continue;
1390                         }
1391                         if (opt == "light") {
1392                                 h_font_sans[0] = "IBMPlexSansLight";
1393                                 continue;
1394                         }
1395                         if (!xopts.empty())
1396                                 xopts += ", ";
1397                         xopts += opt;
1398                 }
1399                 if (!xopts.empty())
1400                         h_font_sans_opts = xopts;
1401                 options.clear();
1402         }
1403
1404         if (name == "noto-sans") {
1405                 h_font_sans[0] = "NotoSansRegular";
1406                 for (auto const & opt : allopts) {
1407                         if (opt == "regular")
1408                                 continue;
1409                         if (opt == "medium") {
1410                                 h_font_sans[0] = "NotoSansMedium";
1411                                 continue;
1412                         }
1413                         if (opt == "thin") {
1414                                 h_font_sans[0] = "NotoSansThin";
1415                                 continue;
1416                         }
1417                         if (opt == "extralight") {
1418                                 h_font_sans[0] = "NotoSansExtralight";
1419                                 continue;
1420                         }
1421                         if (opt == "light") {
1422                                 h_font_sans[0] = "NotoSansLight";
1423                                 continue;
1424                         }
1425                         if (opt == "osf") {
1426                                 h_font_sans_osf = "true";
1427                                 continue;
1428                         }
1429                         if (!xopts.empty())
1430                                 xopts += ", ";
1431                         xopts += opt;
1432                 }
1433                 if (!xopts.empty())
1434                         h_font_sans_opts = xopts;
1435                 options.clear();
1436         }
1437
1438         if (name == "sourcesanspro") {
1439                 h_font_sans[0] = "ADOBESourceSansPro";
1440                 for (auto const & opt : allopts) {
1441                         if (opt == "osf") {
1442                                 h_font_sans_osf = "true";
1443                                 continue;
1444                         }
1445                         if (!xopts.empty())
1446                                 xopts += ", ";
1447                         xopts += opt;
1448                 }
1449                 if (!xopts.empty())
1450                         h_font_sans_opts = xopts;
1451                 options.clear();
1452         }
1453
1454         //
1455         // typewriter fonts
1456         //
1457
1458         // By default, we use the package name as LyX font name,
1459         // so this only needs to be reset if these names differ.
1460         // Also, we handle the scaling option here generally.
1461         if (is_known(name, known_typewriter_font_packages)) {
1462                 h_font_typewriter[0] = name;
1463                 if (contains(opts, "scale")) {
1464                         vector<string>::iterator it = allopts.begin();
1465                         for (; it != allopts.end() ; ++it) {
1466                                 string const opt = *it;
1467                                 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1468                                         if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1469                                                 allopts.erase(it);
1470                                                 break;
1471                                         }
1472                                 }
1473                         }
1474                 }
1475         }
1476
1477         if (name == "libertineMono" || name == "libertineMono-type1")
1478                 h_font_typewriter[0] = "libertine-mono";
1479
1480         if (name == "FiraMono") {
1481                 h_font_typewriter_osf = "true";
1482                 for (auto const & opt : allopts) {
1483                         if (opt == "lf" || opt == "lining") {
1484                                 h_font_typewriter_osf = "false";
1485                                 continue;
1486                         }
1487                         if (!xopts.empty())
1488                                 xopts += ", ";
1489                         xopts += opt;
1490                 }
1491                 if (!xopts.empty())
1492                         h_font_typewriter_opts = xopts;
1493                 options.clear();
1494         }
1495
1496         if (name == "PTMono")
1497                 h_font_typewriter[0] = "PTMono-TLF";
1498
1499         if (name == "plex-mono") {
1500                 h_font_typewriter[0] = "IBMPlexMono";
1501                 for (auto const & opt : allopts) {
1502                         if (opt == "thin") {
1503                                 h_font_typewriter[0] = "IBMPlexMonoThin";
1504                                 continue;
1505                         }
1506                         if (opt == "extralight") {
1507                                 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1508                                 continue;
1509                         }
1510                         if (opt == "light") {
1511                                 h_font_typewriter[0] = "IBMPlexMonoLight";
1512                                 continue;
1513                         }
1514                         if (!xopts.empty())
1515                                 xopts += ", ";
1516                         xopts += opt;
1517                 }
1518                 if (!xopts.empty())
1519                         h_font_typewriter_opts = xopts;
1520                 options.clear();
1521         }
1522
1523         if (name == "noto-mono") {
1524                 h_font_typewriter[0] = "NotoMonoRegular";
1525                 for (auto const & opt : allopts) {
1526                         if (opt == "regular")
1527                                 continue;
1528                         if (!xopts.empty())
1529                                 xopts += ", ";
1530                         xopts += opt;
1531                 }
1532                 if (!xopts.empty())
1533                         h_font_typewriter_opts = xopts;
1534                 options.clear();
1535         }
1536
1537         if (name == "sourcecodepro") {
1538                 h_font_typewriter[0] = "ADOBESourceCodePro";
1539                 for (auto const & opt : allopts) {
1540                         if (opt == "osf") {
1541                                 h_font_typewriter_osf = "true";
1542                                 continue;
1543                         }
1544                         if (!xopts.empty())
1545                                 xopts += ", ";
1546                         xopts += opt;
1547                 }
1548                 if (!xopts.empty())
1549                         h_font_typewriter_opts = xopts;
1550                 options.clear();
1551         }
1552
1553         //
1554         // math fonts
1555         //
1556
1557         // By default, we use the package name as LyX font name,
1558         // so this only needs to be reset if these names differ.
1559         if (is_known(name, known_math_font_packages))
1560                 h_font_math[0] = name;
1561
1562         if (name == "newtxmath") {
1563                 if (opts.empty())
1564                         h_font_math[0] = "newtxmath";
1565                 else if (opts == "garamondx")
1566                         h_font_math[0] = "garamondx-ntxm";
1567                 else if (opts == "libertine")
1568                         h_font_math[0] = "libertine-ntxm";
1569                 else if (opts == "minion")
1570                         h_font_math[0] = "minion-ntxm";
1571                 else if (opts == "cochineal")
1572                         h_font_math[0] = "cochineal-ntxm";
1573         }
1574
1575         if (name == "iwona")
1576                 if (opts == "math")
1577                         h_font_math[0] = "iwona-math";
1578
1579         if (name == "kurier")
1580                 if (opts == "math")
1581                         h_font_math[0] = "kurier-math";
1582
1583         // after the detection and handling of special cases, we can remove the
1584         // fonts, otherwise they would appear in the preamble, see bug #7856
1585         if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1586                 ||      is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1587                 ;
1588         //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1589         else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1590                  name == "esint" || name == "mhchem" || name == "mathdots" ||
1591                  name == "mathtools" || name == "stackrel" ||
1592                  name == "stmaryrd" || name == "undertilde") {
1593                 h_use_packages[name] = "2";
1594                 registerAutomaticallyLoadedPackage(name);
1595         }
1596
1597         else if (name == "babel") {
1598                 h_language_package = "default";
1599                 // One might think we would have to do nothing if babel is loaded
1600                 // without any options to prevent pollution of the preamble with this
1601                 // babel call in every roundtrip.
1602                 // But the user could have defined babel-specific things afterwards. So
1603                 // we need to keep it in the preamble to prevent cases like bug #7861.
1604                 if (!opts.empty()) {
1605                         // check if more than one option was used - used later for inputenc
1606                         if (options.begin() != options.end() - 1)
1607                                 one_language = false;
1608                         // babel takes the last language of the option of its \usepackage
1609                         // call as document language. If there is no such language option, the
1610                         // last language in the documentclass options is used.
1611                         handle_opt(options, known_languages, h_language);
1612                         // translate the babel name to a LyX name
1613                         h_language = babel2lyx(h_language);
1614                         if (h_language == "japanese") {
1615                                 // For Japanese, the encoding isn't indicated in the source
1616                                 // file, and there's really not much we can do. We could
1617                                 // 1) offer a list of possible encodings to choose from, or
1618                                 // 2) determine the encoding of the file by inspecting it.
1619                                 // For the time being, we leave the encoding alone so that
1620                                 // we don't get iconv errors when making a wrong guess, and
1621                                 // we will output a note at the top of the document
1622                                 // explaining what to do.
1623                                 Encoding const * const enc = encodings.fromIconvName(
1624                                         p.getEncoding(), Encoding::japanese, false);
1625                                 if (enc)
1626                                         h_inputencoding = enc->name();
1627                                 is_nonCJKJapanese = true;
1628                                 // in this case babel can be removed from the preamble
1629                                 registerAutomaticallyLoadedPackage("babel");
1630                         } else {
1631                                 // If babel is called with options, LyX puts them by default into the
1632                                 // document class options. This works for most languages, except
1633                                 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1634                                 // perhaps in future others.
1635                                 // Therefore keep the babel call as it is as the user might have
1636                                 // reasons for it.
1637                                 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1638                                 if (!contains(h_preamble.str(), babelcall))
1639                                         h_preamble << babelcall;
1640                         }
1641                         delete_opt(options, known_languages);
1642                 } else {
1643                         if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1644                                 h_preamble << "\\usepackage{babel}\n";
1645                         explicit_babel = true;
1646                 }
1647         }
1648
1649         else if (name == "polyglossia") {
1650                 h_language_package = "default";
1651                 h_default_output_format = "pdf4";
1652                 h_use_non_tex_fonts = true;
1653                 xetex = true;
1654                 registerAutomaticallyLoadedPackage("xunicode");
1655                 if (h_inputencoding == "auto-legacy")
1656                         p.setEncoding("UTF-8");
1657         }
1658
1659         else if (name == "CJK") {
1660                 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1661                 // and this would not be correct for CJK
1662                 if (h_inputencoding == "auto-legacy-plain")
1663                         h_inputencoding = "auto-legacy";
1664                 registerAutomaticallyLoadedPackage("CJK");
1665         }
1666
1667         else if (name == "CJKutf8") {
1668                 h_inputencoding = "utf8-cjk";
1669                 p.setEncoding("UTF-8");
1670                 registerAutomaticallyLoadedPackage("CJKutf8");
1671         }
1672
1673         else if (name == "fontenc") {
1674                 h_fontencoding = getStringFromVector(options, ",");
1675                 options.clear();
1676         }
1677
1678         else if (name == "inputenc" || name == "luainputenc") {
1679                 // h_inputencoding is only set when there is not more than one
1680                 // inputenc option because otherwise h_inputencoding must be
1681                 // set to "auto-legacy" (the default encodings of the document's languages)
1682                 // Therefore check that exactly one option is passed to inputenc.
1683                 // It is also only set when there is not more than one babel
1684                 // language option.
1685                 if (!options.empty()) {
1686                         string const encoding = options.back();
1687                         Encoding const * const enc = encodings.fromLaTeXName(
1688                                 encoding, Encoding::inputenc, true);
1689                         if (!enc) {
1690                                 if (!detectEncoding)
1691                                         cerr << "Unknown encoding " << encoding
1692                                              << ". Ignoring." << std::endl;
1693                         } else {
1694                                 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1695                                         h_inputencoding = enc->name();
1696                                 p.setEncoding(enc->iconvName());
1697                         }
1698                         options.clear();
1699                 }
1700         }
1701
1702         else if (name == "srcltx") {
1703                 h_output_sync = "1";
1704                 if (!opts.empty()) {
1705                         h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1706                         options.clear();
1707                 } else
1708                         h_output_sync_macro = "\\usepackage{srcltx}";
1709         }
1710
1711         else if (is_known(name, known_old_language_packages)) {
1712                 // known language packages from the times before babel
1713                 // if they are found and not also babel, they will be used as
1714                 // custom language package
1715                 h_language_package = "\\usepackage{" + name + "}";
1716         }
1717
1718         else if (name == "lyxskak") {
1719                 // ignore this and its options
1720                 const char * const o[] = {"ps", "mover", 0};
1721                 delete_opt(options, o);
1722         }
1723
1724         else if (name == "parskip" && options.size() < 2 && (opts.empty() || prefixIs(opts, "skip="))) {
1725                 if (opts.empty())
1726                         h_paragraph_separation = "halfline";
1727                 else {
1728                         if (opts == "skip=\\smallskipamount")
1729                                 h_defskip = "smallskip";
1730                         else if (opts == "skip=\\medskipamount")
1731                                 h_defskip = "medskip";
1732                         else if (opts == "skip=\\bigskipamount")
1733                                 h_defskip = "bigskip";
1734                         else if (opts == "skip=\\baselineskip")
1735                                 h_defskip = "fullline";
1736                         else
1737                                 h_defskip = "opts";
1738                         h_paragraph_separation = "skip";
1739                 }
1740         }
1741
1742         else if (is_known(name, known_lyx_packages) && options.empty()) {
1743                 if (name == "splitidx")
1744                         h_use_indices = "true";
1745                 else if (name == "minted")
1746                         h_use_minted = true;
1747                 else if (name == "refstyle")
1748                         h_use_refstyle = true;
1749                 else if (name == "prettyref")
1750                         h_use_refstyle = false;
1751
1752                 if (!in_lyx_preamble) {
1753                         h_preamble << package_beg_sep << name
1754                                    << package_mid_sep << "\\usepackage{"
1755                                    << name << '}';
1756                         if (p.next_token().cat() == catNewline ||
1757                             (p.next_token().cat() == catSpace &&
1758                              p.next_next_token().cat() == catNewline))
1759                                 h_preamble << '\n';
1760                         h_preamble << package_end_sep;
1761                 }
1762         }
1763
1764         else if (name == "geometry")
1765                 handle_geometry(options);
1766
1767         else if (name == "subfig")
1768                 ; // ignore this FIXME: Use the package separator mechanism instead
1769
1770         else if (char const * const * where = is_known(name, known_languages))
1771                 h_language = known_coded_languages[where - known_languages];
1772
1773         else if (name == "natbib") {
1774                 h_biblio_style = "plainnat";
1775                 h_cite_engine = "natbib";
1776                 h_cite_engine_type = "authoryear";
1777                 vector<string>::iterator it =
1778                         find(options.begin(), options.end(), "authoryear");
1779                 if (it != options.end())
1780                         options.erase(it);
1781                 else {
1782                         it = find(options.begin(), options.end(), "numbers");
1783                         if (it != options.end()) {
1784                                 h_cite_engine_type = "numerical";
1785                                 options.erase(it);
1786                         }
1787                 }
1788                 if (!options.empty())
1789                         h_biblio_options = join(options, ",");
1790         }
1791
1792         else if (name == "biblatex") {
1793                 h_biblio_style = "plainnat";
1794                 h_cite_engine = "biblatex";
1795                 h_cite_engine_type = "authoryear";
1796                 string opt;
1797                 vector<string>::iterator it =
1798                         find(options.begin(), options.end(), "natbib");
1799                 if (it != options.end()) {
1800                         options.erase(it);
1801                         h_cite_engine = "biblatex-natbib";
1802                 } else {
1803                         opt = process_keyval_opt(options, "natbib");
1804                         if (opt == "true")
1805                                 h_cite_engine = "biblatex-natbib";
1806                 }
1807                 opt = process_keyval_opt(options, "style");
1808                 if (!opt.empty()) {
1809                         h_biblatex_citestyle = opt;
1810                         h_biblatex_bibstyle = opt;
1811                 } else {
1812                         opt = process_keyval_opt(options, "citestyle");
1813                         if (!opt.empty())
1814                                 h_biblatex_citestyle = opt;
1815                         opt = process_keyval_opt(options, "bibstyle");
1816                         if (!opt.empty())
1817                                 h_biblatex_bibstyle = opt;
1818                 }
1819                 opt = process_keyval_opt(options, "refsection");
1820                 if (!opt.empty()) {
1821                         if (opt == "none" || opt == "part"
1822                             || opt == "chapter" || opt == "section"
1823                             || opt == "subsection")
1824                                 h_multibib = opt;
1825                         else
1826                                 cerr << "Ignoring unknown refsection value '"
1827                                      << opt << "'.";
1828                 }
1829                 opt = process_keyval_opt(options, "bibencoding");
1830                 if (!opt.empty())
1831                         bibencoding = opt;
1832                 if (!options.empty()) {
1833                         h_biblio_options = join(options, ",");
1834                         options.clear();
1835                 }
1836         }
1837
1838         else if (name == "jurabib") {
1839                 h_biblio_style = "jurabib";
1840                 h_cite_engine = "jurabib";
1841                 h_cite_engine_type = "authoryear";
1842                 if (!options.empty())
1843                         h_biblio_options = join(options, ",");
1844         }
1845
1846         else if (name == "bibtopic")
1847                 h_use_bibtopic = "true";
1848
1849         else if (name == "chapterbib")
1850                 h_multibib = "child";
1851
1852         else if (name == "hyperref")
1853                 handle_hyperref(options);
1854
1855         else if (name == "algorithm2e") {
1856                 // Load "algorithm2e" module
1857                 addModule("algorithm2e");
1858                 // Add the package options to the global document options
1859                 if (!options.empty()) {
1860                         if (h_options.empty())
1861                                 h_options = join(options, ",");
1862                         else
1863                                 h_options += ',' + join(options, ",");
1864                 }
1865         }
1866         else if (name == "microtype") {
1867                 //we internally support only microtype without params
1868                 if (options.empty())
1869                         h_use_microtype = "true";
1870                 else
1871                         h_preamble << "\\usepackage[" << opts << "]{microtype}";
1872         }
1873
1874         else if (name == "lineno") {
1875                 h_use_lineno = "true";
1876                 if (!options.empty()) {
1877                         h_lineno_options = join(options, ",");
1878                         options.clear();
1879                 }
1880         }
1881
1882         else if (name == "changebar")
1883                 h_output_changes = "true";
1884
1885         else if (!in_lyx_preamble) {
1886                 if (options.empty())
1887                         h_preamble << "\\usepackage{" << name << '}';
1888                 else {
1889                         h_preamble << "\\usepackage[" << opts << "]{"
1890                                    << name << '}';
1891                         options.clear();
1892                 }
1893                 if (p.next_token().cat() == catNewline ||
1894                     (p.next_token().cat() == catSpace &&
1895                      p.next_next_token().cat() == catNewline))
1896                         h_preamble << '\n';
1897         }
1898
1899         // We need to do something with the options...
1900         if (!options.empty() && !detectEncoding)
1901                 cerr << "Ignoring options '" << join(options, ",")
1902                      << "' of package " << name << '.' << endl;
1903
1904         // remove the whitespace
1905         p.skip_spaces();
1906 }
1907
1908
1909 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1910 {
1911         while (p.good()) {
1912                 Token t = p.get_token();
1913                 if (t.cat() == catEscape &&
1914                     is_known(t.cs(), known_if_commands))
1915                         handle_if(p, in_lyx_preamble);
1916                 else {
1917                         if (!in_lyx_preamble)
1918                                 h_preamble << t.asInput();
1919                         if (t.cat() == catEscape && t.cs() == "fi")
1920                                 return;
1921                 }
1922         }
1923 }
1924
1925
1926 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1927 {
1928         if (contains(h_float_placement, "H"))
1929                 registerAutomaticallyLoadedPackage("float");
1930         if (h_spacing != "single" && h_spacing != "default")
1931                 registerAutomaticallyLoadedPackage("setspace");
1932         if (h_use_packages["amsmath"] == "2") {
1933                 // amsbsy and amstext are already provided by amsmath
1934                 registerAutomaticallyLoadedPackage("amsbsy");
1935                 registerAutomaticallyLoadedPackage("amstext");
1936         }
1937
1938         // output the LyX file settings
1939         // Important: Keep the version formatting in sync with LyX and
1940         //            lyx2lyx (bug 7951)
1941         string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1942         os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1943            << lyx_version_minor << '\n'
1944            << "\\lyxformat " << LYX_FORMAT << '\n'
1945            << "\\begin_document\n"
1946            << "\\begin_header\n"
1947            << "\\save_transient_properties " << h_save_transient_properties << "\n"
1948            << "\\origin " << origin << "\n"
1949            << "\\textclass " << h_textclass << "\n";
1950         string const raw = subdoc ? empty_string() : h_preamble.str();
1951         if (!raw.empty()) {
1952                 os << "\\begin_preamble\n";
1953                 for (string::size_type i = 0; i < raw.size(); ++i) {
1954                         if (raw[i] == package_beg_sep) {
1955                                 // Here follows some package loading code that
1956                                 // must be skipped if the package is loaded
1957                                 // automatically.
1958                                 string::size_type j = raw.find(package_mid_sep, i);
1959                                 if (j == string::npos)
1960                                         return false;
1961                                 string::size_type k = raw.find(package_end_sep, j);
1962                                 if (k == string::npos)
1963                                         return false;
1964                                 string const package = raw.substr(i + 1, j - i - 1);
1965                                 string const replacement = raw.substr(j + 1, k - j - 1);
1966                                 if (auto_packages.find(package) == auto_packages.end())
1967                                         os << replacement;
1968                                 i = k;
1969                         } else
1970                                 os.put(raw[i]);
1971                 }
1972                 os << "\n\\end_preamble\n";
1973         }
1974         if (!h_options.empty())
1975                 os << "\\options " << h_options << "\n";
1976         os << "\\use_default_options " << h_use_default_options << "\n";
1977         if (!used_modules.empty()) {
1978                 os << "\\begin_modules\n";
1979                 vector<string>::const_iterator const end = used_modules.end();
1980                 vector<string>::const_iterator it = used_modules.begin();
1981                 for (; it != end; ++it)
1982                         os << *it << '\n';
1983                 os << "\\end_modules\n";
1984         }
1985         if (!h_includeonlys.empty()) {
1986                 os << "\\begin_includeonly\n";
1987                 for (auto const & iofile : h_includeonlys)
1988                         os << iofile << '\n';
1989                 os << "\\end_includeonly\n";
1990         }
1991         os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1992            << "\\language " << h_language << "\n"
1993            << "\\language_package " << h_language_package << "\n"
1994            << "\\inputencoding " << h_inputencoding << "\n"
1995            << "\\fontencoding " << h_fontencoding << "\n"
1996            << "\\font_roman \"" << h_font_roman[0]
1997            << "\" \"" << h_font_roman[1] << "\"\n"
1998            << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1999            << "\\font_typewriter \"" << h_font_typewriter[0]
2000            << "\" \"" << h_font_typewriter[1] << "\"\n"
2001            << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
2002            << "\\font_default_family " << h_font_default_family << "\n"
2003            << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
2004            << "\\font_sc " << h_font_sc << "\n"
2005            << "\\font_roman_osf " << h_font_roman_osf << "\n"
2006            << "\\font_sans_osf " << h_font_sans_osf << "\n"
2007            << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
2008         if (!h_font_roman_opts.empty())
2009                 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
2010         os << "\\font_sf_scale " << h_font_sf_scale[0]
2011            << ' ' << h_font_sf_scale[1] << '\n';
2012         if (!h_font_sans_opts.empty())
2013                 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
2014         os << "\\font_tt_scale " << h_font_tt_scale[0]
2015            << ' ' << h_font_tt_scale[1] << '\n';
2016         if (!h_font_cjk.empty())
2017                 os << "\\font_cjk " << h_font_cjk << '\n';
2018         if (!h_font_typewriter_opts.empty())
2019                 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
2020         os << "\\use_microtype " << h_use_microtype << '\n'
2021            << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
2022            << "\\graphics " << h_graphics << '\n'
2023            << "\\default_output_format " << h_default_output_format << "\n"
2024            << "\\output_sync " << h_output_sync << "\n";
2025         if (h_output_sync == "1")
2026                 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
2027         os << "\\bibtex_command " << h_bibtex_command << "\n"
2028            << "\\index_command " << h_index_command << "\n";
2029         if (!h_float_placement.empty())
2030                 os << "\\float_placement " << h_float_placement << "\n";
2031         os << "\\paperfontsize " << h_paperfontsize << "\n"
2032            << "\\spacing " << h_spacing << "\n"
2033            << "\\use_hyperref " << h_use_hyperref << '\n';
2034         if (h_use_hyperref == "true") {
2035                 if (!h_pdf_title.empty())
2036                         os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
2037                 if (!h_pdf_author.empty())
2038                         os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
2039                 if (!h_pdf_subject.empty())
2040                         os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
2041                 if (!h_pdf_keywords.empty())
2042                         os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
2043                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
2044                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
2045                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
2046                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
2047                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
2048                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
2049                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
2050                       "\\pdf_backref " << h_pdf_backref << "\n"
2051                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
2052                 if (!h_pdf_pagemode.empty())
2053                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
2054                 if (!h_pdf_quoted_options.empty())
2055                         os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
2056         }
2057         os << "\\papersize " << h_papersize << "\n"
2058            << "\\use_geometry " << h_use_geometry << '\n';
2059         for (map<string, string>::const_iterator it = h_use_packages.begin();
2060              it != h_use_packages.end(); ++it)
2061                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
2062         os << "\\cite_engine " << h_cite_engine << '\n'
2063            << "\\cite_engine_type " << h_cite_engine_type << '\n'
2064            << "\\biblio_style " << h_biblio_style << "\n"
2065            << "\\use_bibtopic " << h_use_bibtopic << "\n";
2066         if (!h_biblio_options.empty())
2067                 os << "\\biblio_options " << h_biblio_options << "\n";
2068         if (!h_biblatex_bibstyle.empty())
2069                 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
2070         if (!h_biblatex_citestyle.empty())
2071                 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
2072         if (!h_multibib.empty())
2073                 os << "\\multibib " << h_multibib << "\n";
2074         os << "\\use_indices " << h_use_indices << "\n"
2075            << "\\paperorientation " << h_paperorientation << '\n'
2076            << "\\suppress_date " << h_suppress_date << '\n'
2077            << "\\justification " << h_justification << '\n'
2078            << "\\use_refstyle " << h_use_refstyle << '\n'
2079            << "\\use_minted " << h_use_minted << '\n'
2080            << "\\use_lineno " << h_use_lineno << '\n';
2081         if (!h_lineno_options.empty())
2082                 os << "\\lineno_options " << h_lineno_options << '\n';
2083         if (!h_fontcolor.empty())
2084                 os << "\\fontcolor " << h_fontcolor << '\n';
2085         if (!h_notefontcolor.empty())
2086                 os << "\\notefontcolor " << h_notefontcolor << '\n';
2087         if (!h_backgroundcolor.empty())
2088                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
2089         if (!h_boxbgcolor.empty())
2090                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
2091         if (index_number != 0)
2092                 for (int i = 0; i < index_number; i++) {
2093                         os << "\\index " << h_index[i] << '\n'
2094                            << "\\shortcut " << h_shortcut[i] << '\n'
2095                            << "\\color " << h_color << '\n'
2096                            << "\\end_index\n";
2097                 }
2098         else {
2099                 os << "\\index " << h_index[0] << '\n'
2100                    << "\\shortcut " << h_shortcut[0] << '\n'
2101                    << "\\color " << h_color << '\n'
2102                    << "\\end_index\n";
2103         }
2104         os << h_margins
2105            << "\\secnumdepth " << h_secnumdepth << "\n"
2106            << "\\tocdepth " << h_tocdepth << "\n"
2107            << "\\paragraph_separation " << h_paragraph_separation << "\n";
2108         if (h_paragraph_separation == "skip")
2109                 os << "\\defskip " << h_defskip << "\n";
2110         else
2111                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2112         os << "\\is_math_indent " << h_is_mathindent << "\n";
2113         if (!h_mathindentation.empty())
2114                 os << "\\math_indentation " << h_mathindentation << "\n";
2115         os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2116         os << "\\quotes_style " << h_quotes_style << "\n"
2117            << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2118            << "\\papercolumns " << h_papercolumns << "\n"
2119            << "\\papersides " << h_papersides << "\n"
2120            << "\\paperpagestyle " << h_paperpagestyle << "\n";
2121         if (!h_listings_params.empty())
2122                 os << "\\listings_params " << h_listings_params << "\n";
2123         os << "\\tracking_changes " << h_tracking_changes << "\n"
2124            << "\\output_changes " << h_output_changes << "\n"
2125            << "\\change_bars " << h_change_bars << "\n"
2126            << "\\html_math_output " << h_html_math_output << "\n"
2127            << "\\html_css_as_file " << h_html_css_as_file << "\n"
2128            << "\\html_be_strict " << h_html_be_strict << "\n"
2129            << "\\docbook_table_output " << h_docbook_table_output << "\n"
2130            << authors_
2131            << "\\end_header\n\n"
2132            << "\\begin_body\n";
2133         return true;
2134 }
2135
2136
2137 void Preamble::parse(Parser & p, string const & forceclass,
2138                      TeX2LyXDocClass & tc)
2139 {
2140         // initialize fixed types
2141         special_columns_['D'] = 3;
2142         parse(p, forceclass, false, tc);
2143 }
2144
2145
2146 void Preamble::parse(Parser & p, string const & forceclass,
2147                      bool detectEncoding, TeX2LyXDocClass & tc)
2148 {
2149         bool is_full_document = false;
2150         bool is_lyx_file = false;
2151         bool in_lyx_preamble = false;
2152         bool class_set = false;
2153
2154         // determine whether this is a full document or a fragment for inclusion
2155         while (p.good()) {
2156                 Token const & t = p.get_token();
2157
2158                 if (t.cat() == catEscape && t.cs() == "documentclass") {
2159                         is_full_document = true;
2160                         break;
2161                 }
2162         }
2163         p.reset();
2164
2165         if (detectEncoding && !is_full_document)
2166                 return;
2167
2168         while (is_full_document && p.good()) {
2169                 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2170                     h_inputencoding != "auto-legacy-plain")
2171                         return;
2172
2173                 // Force textclass if the user wanted it
2174                 if (!forceclass.empty()) {
2175                         setTextClass(forceclass, tc);
2176                         class_set = true;
2177                 }
2178
2179                 Token const & t = p.get_token();
2180
2181 #ifdef FILEDEBUG
2182                 if (!detectEncoding)
2183                         cerr << "t: " << t << '\n';
2184 #endif
2185
2186                 //
2187                 // cat codes
2188                 //
2189                 if (!in_lyx_preamble &&
2190                     (t.cat() == catLetter ||
2191                      t.cat() == catSuper ||
2192                      t.cat() == catSub ||
2193                      t.cat() == catOther ||
2194                      t.cat() == catMath ||
2195                      t.cat() == catActive ||
2196                      t.cat() == catBegin ||
2197                      t.cat() == catEnd ||
2198                      t.cat() == catAlign ||
2199                      t.cat() == catParameter)) {
2200                         h_preamble << t.cs();
2201                         continue;
2202                 }
2203
2204                 if (!in_lyx_preamble &&
2205                     (t.cat() == catSpace || t.cat() == catNewline)) {
2206                         h_preamble << t.asInput();
2207                         continue;
2208                 }
2209
2210                 if (t.cat() == catComment) {
2211                         static regex const islyxfile("%% LyX .* created this file");
2212                         static regex const usercommands("User specified LaTeX commands");
2213
2214                         string const comment = t.asInput();
2215
2216                         // magically switch encoding default if it looks like XeLaTeX
2217                         static string const magicXeLaTeX =
2218                                 "% This document must be compiled with XeLaTeX ";
2219                         if (comment.size() > magicXeLaTeX.size()
2220                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2221                                   && h_inputencoding == "auto-legacy") {
2222                                 if (!detectEncoding)
2223                                         cerr << "XeLaTeX comment found, switching to UTF8\n";
2224                                 h_inputencoding = "utf8";
2225                         }
2226                         smatch sub;
2227                         if (regex_search(comment, sub, islyxfile)) {
2228                                 is_lyx_file = true;
2229                                 in_lyx_preamble = true;
2230                         } else if (is_lyx_file
2231                                    && regex_search(comment, sub, usercommands))
2232                                 in_lyx_preamble = false;
2233                         else if (!in_lyx_preamble)
2234                                 h_preamble << t.asInput();
2235                         continue;
2236                 }
2237
2238                 if (t.cs() == "PassOptionsToPackage") {
2239                         string const poptions = p.getArg('{', '}');
2240                         string const package = p.verbatim_item();
2241                         extra_package_options_.insert(make_pair(package, poptions));
2242                         continue;
2243                 }
2244
2245                 if (t.cs() == "pagestyle") {
2246                         h_paperpagestyle = p.verbatim_item();
2247                         continue;
2248                 }
2249
2250                 if (t.cs() == "setdefaultlanguage") {
2251                         xetex = true;
2252                         // We don't yet care about non-language variant options
2253                         // because LyX doesn't support this yet, see bug #8214
2254                         if (p.hasOpt()) {
2255                                 string langopts = p.getOpt();
2256                                 // check if the option contains a variant, if yes, extract it
2257                                 string::size_type pos_var = langopts.find("variant");
2258                                 string::size_type i = langopts.find(',', pos_var);
2259                                 string::size_type k = langopts.find('=', pos_var);
2260                                 if (pos_var != string::npos){
2261                                         string variant;
2262                                         if (i == string::npos)
2263                                                 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2264                                         else
2265                                                 variant = langopts.substr(k + 1, i - k - 1);
2266                                         h_language = variant;
2267                                 }
2268                                 p.verbatim_item();
2269                         } else
2270                                 h_language = p.verbatim_item();
2271                         //finally translate the poyglossia name to a LyX name
2272                         h_language = polyglossia2lyx(h_language);
2273                         continue;
2274                 }
2275
2276                 if (t.cs() == "setotherlanguage") {
2277                         // We don't yet care about the option because LyX doesn't
2278                         // support this yet, see bug #8214
2279                         p.hasOpt() ? p.getOpt() : string();
2280                         p.verbatim_item();
2281                         continue;
2282                 }
2283
2284                 if (t.cs() == "setmainfont") {
2285                         string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2286                         h_font_roman[1] = p.getArg('{', '}');
2287                         if (!fontopts.empty()) {
2288                                 vector<string> opts = getVectorFromString(fontopts);
2289                                 fontopts.clear();
2290                                 for (auto const & opt : opts) {
2291                                         if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2292                                                 // ignore
2293                                                 continue;
2294                                         if (!fontopts.empty())
2295                                                 fontopts += ", ";
2296                                         fontopts += opt;
2297                                 }
2298                                 h_font_roman_opts = fontopts;
2299                         }
2300                         continue;
2301                 }
2302
2303                 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2304                         // LyX currently only supports the scale option
2305                         string scale, fontopts;
2306                         if (p.hasOpt()) {
2307                                 fontopts = p.getArg('[', ']');
2308                                 if (!fontopts.empty()) {
2309                                         vector<string> opts = getVectorFromString(fontopts);
2310                                         fontopts.clear();
2311                                         for (auto const & opt : opts) {
2312                                                 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2313                                                         // ignore
2314                                                         continue;
2315                                                 if (prefixIs(opt, "Scale=")) {
2316                                                         scale_as_percentage(opt, scale);
2317                                                         continue;
2318                                                 }
2319                                                 if (!fontopts.empty())
2320                                                         fontopts += ", ";
2321                                                 fontopts += opt;
2322                                         }
2323                                 }
2324                         }
2325                         if (t.cs() == "setsansfont") {
2326                                 if (!scale.empty())
2327                                         h_font_sf_scale[1] = scale;
2328                                 h_font_sans[1] = p.getArg('{', '}');
2329                                 if (!fontopts.empty())
2330                                         h_font_sans_opts = fontopts;
2331                         } else {
2332                                 if (!scale.empty())
2333                                         h_font_tt_scale[1] = scale;
2334                                 h_font_typewriter[1] = p.getArg('{', '}');
2335                                 if (!fontopts.empty())
2336                                         h_font_typewriter_opts = fontopts;
2337                         }
2338                         continue;
2339                 }
2340
2341                 if (t.cs() == "babelfont") {
2342                         xetex = true;
2343                         h_use_non_tex_fonts = true;
2344                         h_language_package = "babel";
2345                         if (h_inputencoding == "auto-legacy")
2346                         p.setEncoding("UTF-8");
2347                         // we don't care about the lang option
2348                         string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2349                         string const family = p.getArg('{', '}');
2350                         string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2351                         string const fontname = p.getArg('{', '}');
2352                         if (lang.empty() && family == "rm") {
2353                                 h_font_roman[1] = fontname;
2354                                 if (!fontopts.empty()) {
2355                                         vector<string> opts = getVectorFromString(fontopts);
2356                                         fontopts.clear();
2357                                         for (auto const & opt : opts) {
2358                                                 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2359                                                         // ignore
2360                                                         continue;
2361                                                 if (!fontopts.empty())
2362                                                         fontopts += ", ";
2363                                                 fontopts += opt;
2364                                         }
2365                                         h_font_roman_opts = fontopts;
2366                                 }
2367                                 continue;
2368                         } else if (lang.empty() && (family == "sf" || family == "tt")) {
2369                                 string scale;
2370                                 if (!fontopts.empty()) {
2371                                         vector<string> opts = getVectorFromString(fontopts);
2372                                         fontopts.clear();
2373                                         for (auto const & opt : opts) {
2374                                                 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2375                                                         // ignore
2376                                                         continue;
2377                                                 if (prefixIs(opt, "Scale=")) {
2378                                                         scale_as_percentage(opt, scale);
2379                                                         continue;
2380                                                 }
2381                                                 if (!fontopts.empty())
2382                                                         fontopts += ", ";
2383                                                 fontopts += opt;
2384                                         }
2385                                 }
2386                                 if (family == "sf") {
2387                                         if (!scale.empty())
2388                                                 h_font_sf_scale[1] = scale;
2389                                         h_font_sans[1] = fontname;
2390                                         if (!fontopts.empty())
2391                                                 h_font_sans_opts = fontopts;
2392                                 } else {
2393                                         if (!scale.empty())
2394                                                 h_font_tt_scale[1] = scale;
2395                                         h_font_typewriter[1] = fontname;
2396                                         if (!fontopts.empty())
2397                                                 h_font_typewriter_opts = fontopts;
2398                                 }
2399                                 continue;
2400                         } else {
2401                                 // not rm, sf or tt or lang specific
2402                                 h_preamble << '\\' << t.cs();
2403                                 if (!lang.empty())
2404                                         h_preamble << '[' << lang << ']';
2405                                 h_preamble << '{' << family << '}';
2406                                 if (!fontopts.empty())
2407                                         h_preamble << '[' << fontopts << ']';
2408                                 h_preamble << '{' << fontname << '}' << '\n';
2409                                 continue;
2410                         }
2411                 }
2412
2413                 if (t.cs() == "date") {
2414                         string argument = p.getArg('{', '}');
2415                         if (argument.empty())
2416                                 h_suppress_date = "true";
2417                         else
2418                                 h_preamble << t.asInput() << '{' << argument << '}';
2419                         continue;
2420                 }
2421
2422                 if (t.cs() == "color") {
2423                         string const space =
2424                                 (p.hasOpt() ? p.getOpt() : string());
2425                         string argument = p.getArg('{', '}');
2426                         // check the case that a standard color is used
2427                         if (space.empty() && is_known(argument, known_basic_colors)) {
2428                                 h_fontcolor = rgbcolor2code(argument);
2429                                 registerAutomaticallyLoadedPackage("color");
2430                         } else if (space.empty() && argument == "document_fontcolor")
2431                                 registerAutomaticallyLoadedPackage("color");
2432                         // check the case that LyX's document_fontcolor is defined
2433                         // but not used for \color
2434                         else {
2435                                 h_preamble << t.asInput();
2436                                 if (!space.empty())
2437                                         h_preamble << space;
2438                                 h_preamble << '{' << argument << '}';
2439                                 // the color might already be set because \definecolor
2440                                 // is parsed before this
2441                                 h_fontcolor = "";
2442                         }
2443                         continue;
2444                 }
2445
2446                 if (t.cs() == "pagecolor") {
2447                         string argument = p.getArg('{', '}');
2448                         // check the case that a standard color is used
2449                         if (is_known(argument, known_basic_colors)) {
2450                                 h_backgroundcolor = rgbcolor2code(argument);
2451                         } else if (argument == "page_backgroundcolor")
2452                                 registerAutomaticallyLoadedPackage("color");
2453                         // check the case that LyX's page_backgroundcolor is defined
2454                         // but not used for \pagecolor
2455                         else {
2456                                 h_preamble << t.asInput() << '{' << argument << '}';
2457                                 // the color might already be set because \definecolor
2458                                 // is parsed before this
2459                                 h_backgroundcolor = "";
2460                         }
2461                         continue;
2462                 }
2463
2464                 if (t.cs() == "makeatletter") {
2465                         // LyX takes care of this
2466                         p.setCatcode('@', catLetter);
2467                         continue;
2468                 }
2469
2470                 if (t.cs() == "makeatother") {
2471                         // LyX takes care of this
2472                         p.setCatcode('@', catOther);
2473                         continue;
2474                 }
2475
2476                 if (t.cs() == "makeindex") {
2477                         // LyX will re-add this if a print index command is found
2478                         p.skip_spaces();
2479                         continue;
2480                 }
2481
2482                 if (t.cs() == "newindex") {
2483                         string const indexname = p.getArg('[', ']');
2484                         string const shortcut = p.verbatim_item();
2485                         if (!indexname.empty())
2486                                 h_index[index_number] = indexname;
2487                         else
2488                                 h_index[index_number] = shortcut;
2489                         h_shortcut[index_number] = shortcut;
2490                         index_number += 1;
2491                         p.skip_spaces();
2492                         continue;
2493                 }
2494
2495                 if (t.cs() == "addbibresource") {
2496                         string const options =  p.getArg('[', ']');
2497                         string const arg = removeExtension(p.getArg('{', '}'));
2498                         if (!options.empty()) {
2499                                 // check if the option contains a bibencoding, if yes, extract it
2500                                 string::size_type pos = options.find("bibencoding=");
2501                                 string encoding;
2502                                 if (pos != string::npos) {
2503                                         string::size_type i = options.find(',', pos);
2504                                         if (i == string::npos)
2505                                                 encoding = options.substr(pos + 1);
2506                                         else
2507                                                 encoding = options.substr(pos, i - pos);
2508                                         pos = encoding.find('=');
2509                                         if (pos == string::npos)
2510                                                 encoding.clear();
2511                                         else
2512                                                 encoding = encoding.substr(pos + 1);
2513                                 }
2514                                 if (!encoding.empty())
2515                                         biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2516                         }
2517                         biblatex_bibliographies.push_back(arg);
2518                         continue;
2519                 }
2520
2521                 if (t.cs() == "bibliography") {
2522                         vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2523                         biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2524                         continue;
2525                 }
2526
2527                 if (t.cs() == "RS@ifundefined") {
2528                         string const name = p.verbatim_item();
2529                         string const body1 = p.verbatim_item();
2530                         string const body2 = p.verbatim_item();
2531                         // only non-lyxspecific stuff
2532                         if (in_lyx_preamble &&
2533                             (name == "subsecref" || name == "thmref" || name == "lemref"))
2534                                 p.skip_spaces();
2535                         else {
2536                                 ostringstream ss;
2537                                 ss << '\\' << t.cs();
2538                                 ss << '{' << name << '}'
2539                                    << '{' << body1 << '}'
2540                                    << '{' << body2 << '}';
2541                                 h_preamble << ss.str();
2542                         }
2543                         continue;
2544                 }
2545
2546                 if (t.cs() == "AtBeginDocument") {
2547                         string const name = p.verbatim_item();
2548                         // only non-lyxspecific stuff
2549                         if (in_lyx_preamble &&
2550                             (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2551                                 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2552                                 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2553                                 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2554                                 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2555                                 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2556                                 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2557                                 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2558                                 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2559                                 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2560                                 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2561                                 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2562                                 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2563                                 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2564                                 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2565                                 p.skip_spaces();
2566                         else {
2567                                 ostringstream ss;
2568                                 ss << '\\' << t.cs();
2569                                 ss << '{' << name << '}';
2570                                 h_preamble << ss.str();
2571                         }
2572                         continue;
2573                 }
2574
2575                 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2576                     || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2577                     || t.cs() == "providecommand" || t.cs() == "providecommandx"
2578                     || t.cs() == "DeclareRobustCommand"
2579                     || t.cs() == "DeclareRobustCommandx"
2580                     || t.cs() == "ProvideTextCommandDefault"
2581                     || t.cs() == "DeclareMathAccent") {
2582                         bool star = false;
2583                         if (p.next_token().character() == '*') {
2584                                 p.get_token();
2585                                 star = true;
2586                         }
2587                         string const name = p.verbatim_item();
2588                         string const opt1 = p.getFullOpt();
2589                         string const opt2 = p.getFullOpt();
2590                         string const body = p.verbatim_item();
2591                         // store the in_lyx_preamble setting
2592                         bool const was_in_lyx_preamble = in_lyx_preamble;
2593                         // font settings
2594                         if (name == "\\rmdefault")
2595                                 if (is_known(body, known_roman_font_packages)) {
2596                                         h_font_roman[0] = body;
2597                                         p.skip_spaces();
2598                                         in_lyx_preamble = true;
2599                                 }
2600                         if (name == "\\sfdefault") {
2601                                 if (is_known(body, known_sans_font_packages)) {
2602                                         h_font_sans[0] = body;
2603                                         p.skip_spaces();
2604                                         in_lyx_preamble = true;
2605                                 }
2606                                 if (body == "LibertinusSans-OsF") {
2607                                         h_font_sans[0] = "LibertinusSans-LF";
2608                                         h_font_sans_osf = "true";
2609                                         p.skip_spaces();
2610                                         in_lyx_preamble = true;
2611                                 }
2612                         }
2613                         if (name == "\\ttdefault")
2614                                 if (is_known(body, known_typewriter_font_packages)) {
2615                                         h_font_typewriter[0] = body;
2616                                         p.skip_spaces();
2617                                         in_lyx_preamble = true;
2618                                 }
2619                         if (name == "\\familydefault") {
2620                                 string family = body;
2621                                 // remove leading "\"
2622                                 h_font_default_family = family.erase(0,1);
2623                                 p.skip_spaces();
2624                                 in_lyx_preamble = true;
2625                         }
2626                         if (name == "\\LibertinusSans@scale") {
2627                                 if (isStrDbl(body)) {
2628                                         h_font_sf_scale[0] = convert<string>(
2629                                                 static_cast<int>(100 * convert<double>(body)));
2630                                 }
2631                         }
2632                         if (name == "\\LibertinusMono@scale") {
2633                                 if (isStrDbl(body)) {
2634                                         h_font_tt_scale[0] = convert<string>(
2635                                                 static_cast<int>(100 * convert<double>(body)));
2636                                 }
2637                         }
2638
2639                         // remove LyX-specific definitions that are re-added by LyX
2640                         // if necessary
2641                         // \lyxline is an ancient command that is converted by tex2lyx into
2642                         // a \rule therefore remove its preamble code
2643                         if (name == "\\lyxdot" || name == "\\lyxarrow"
2644                             || name == "\\lyxline" || name == "\\LyX") {
2645                                 p.skip_spaces();
2646                                 in_lyx_preamble = true;
2647                         }
2648
2649                         // Add the command to the known commands
2650                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2651
2652                         // only non-lyxspecific stuff
2653                         if (!in_lyx_preamble) {
2654                                 ostringstream ss;
2655                                 ss << '\\' << t.cs();
2656                                 if (star)
2657                                         ss << '*';
2658                                 ss << '{' << name << '}' << opt1 << opt2
2659                                    << '{' << body << "}";
2660                                 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2661                                         h_preamble << ss.str();
2662 /*
2663                                 ostream & out = in_preamble ? h_preamble : os;
2664                                 out << "\\" << t.cs() << "{" << name << "}"
2665                                     << opts << "{" << body << "}";
2666 */
2667                         }
2668                         // restore the in_lyx_preamble setting
2669                         in_lyx_preamble = was_in_lyx_preamble;
2670                         continue;
2671                 }
2672
2673                 if (t.cs() == "documentclass") {
2674                         vector<string>::iterator it;
2675                         vector<string> opts = split_options(p.getArg('[', ']'));
2676                         // FIXME This does not work for classes that have a
2677                         //       different name in LyX than in LaTeX
2678                         string const tclass = p.getArg('{', '}');
2679                         p.skip_spaces();
2680                         // Only set text class if a class hasn't been forced
2681                         // (this was set above)
2682                         if (!class_set) {
2683                                 // textclass needs to be set at this place (if not already done)
2684                                 // as we need to know it for other parameters
2685                                 // (such as class-dependent paper size)
2686                                 setTextClass(tclass, tc);
2687                                 class_set = true;
2688                         }
2689
2690                         // Font sizes.
2691                         // Try those who are (most likely) known to all packages first
2692                         handle_opt(opts, known_fontsizes, h_paperfontsize);
2693                         delete_opt(opts, known_fontsizes);
2694                         // delete "pt" at the end
2695                         string::size_type i = h_paperfontsize.find("pt");
2696                         if (i != string::npos)
2697                                 h_paperfontsize.erase(i);
2698                         // Now those known specifically to the class
2699                         vector<string> class_fsizes = getVectorFromString(tc.opt_fontsize(), "|");
2700                         string const fsize_format = tc.fontsizeformat();
2701                         for (auto const & fsize : class_fsizes) {
2702                                 string latexsize = subst(fsize_format, "$$s", fsize);
2703                                 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2704                                 if (it != opts.end()) {
2705                                         h_paperfontsize = fsize;
2706                                         opts.erase(it);
2707                                         break;
2708                                 }
2709                         }
2710
2711                         // The documentclass options are always parsed before the options
2712                         // of the babel call so that a language cannot overwrite the babel
2713                         // options.
2714                         handle_opt(opts, known_languages, h_language);
2715                         delete_opt(opts, known_languages);
2716
2717                         // math indentation
2718                         if ((it = find(opts.begin(), opts.end(), "fleqn"))
2719                                  != opts.end()) {
2720                                 h_is_mathindent = "1";
2721                                 opts.erase(it);
2722                         }
2723                         // formula numbering side
2724                         if ((it = find(opts.begin(), opts.end(), "leqno"))
2725                                  != opts.end()) {
2726                                 h_math_numbering_side = "left";
2727                                 opts.erase(it);
2728                         }
2729                         else if ((it = find(opts.begin(), opts.end(), "reqno"))
2730                                  != opts.end()) {
2731                                 h_math_numbering_side = "right";
2732                                 opts.erase(it);
2733                         }
2734
2735                         // paper orientation
2736                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2737                                 h_paperorientation = "landscape";
2738                                 opts.erase(it);
2739                         }
2740                         // paper sides
2741                         if ((it = find(opts.begin(), opts.end(), "oneside"))
2742                                  != opts.end()) {
2743                                 h_papersides = "1";
2744                                 opts.erase(it);
2745                         }
2746                         if ((it = find(opts.begin(), opts.end(), "twoside"))
2747                                  != opts.end()) {
2748                                 h_papersides = "2";
2749                                 opts.erase(it);
2750                         }
2751                         // paper columns
2752                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2753                                  != opts.end()) {
2754                                 h_papercolumns = "1";
2755                                 opts.erase(it);
2756                         }
2757                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2758                                  != opts.end()) {
2759                                 h_papercolumns = "2";
2760                                 opts.erase(it);
2761                         }
2762                         // paper sizes
2763                         // some size options are known by the document class, other sizes
2764                         // are handled by the \geometry command of the geometry package
2765                         vector<string> class_psizes = getVectorFromString(tc.opt_pagesize(), "|");
2766                         string const psize_format = tc.pagesizeformat();
2767                         for (auto const & psize : class_psizes) {
2768                                 string latexsize = subst(psize_format, "$$s", psize);
2769                                 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2770                                 if (it != opts.end()) {
2771                                         h_papersize = psize;
2772                                         opts.erase(it);
2773                                         break;
2774                                 }
2775                                 if (psize_format == "$$spaper")
2776                                         continue;
2777                                 // Also try with the default format since this is understood by
2778                                 // most classes
2779                                 latexsize = psize + "paper";
2780                                 it = find(opts.begin(), opts.end(), latexsize);
2781                                 if (it != opts.end()) {
2782                                         h_papersize = psize;
2783                                         opts.erase(it);
2784                                         break;
2785                                 }
2786                         }
2787                         // the remaining options
2788                         h_options = join(opts, ",");
2789                         continue;
2790                 }
2791
2792                 if (t.cs() == "usepackage") {
2793                         string const options = p.getArg('[', ']');
2794                         string const name = p.getArg('{', '}');
2795                         vector<string> vecnames;
2796                         split(name, vecnames, ',');
2797                         vector<string>::const_iterator it  = vecnames.begin();
2798                         vector<string>::const_iterator end = vecnames.end();
2799                         for (; it != end; ++it)
2800                                 handle_package(p, trimSpaceAndEol(*it), options,
2801                                                in_lyx_preamble, detectEncoding);
2802                         continue;
2803                 }
2804
2805                 if (t.cs() == "inputencoding") {
2806                         string const encoding = p.getArg('{','}');
2807                         Encoding const * const enc = encodings.fromLaTeXName(
2808                                 encoding, Encoding::inputenc, true);
2809                         if (!enc) {
2810                                 if (!detectEncoding)
2811                                         cerr << "Unknown encoding " << encoding
2812                                              << ". Ignoring." << std::endl;
2813                         } else {
2814                                 if (!enc->unsafe())
2815                                         h_inputencoding = enc->name();
2816                                 p.setEncoding(enc->iconvName());
2817                         }
2818                         continue;
2819                 }
2820
2821                 if (t.cs() == "newenvironment") {
2822                         string const name = p.getArg('{', '}');
2823                         string const opt1 = p.getFullOpt();
2824                         string const opt2 = p.getFullOpt();
2825                         string const beg = p.verbatim_item();
2826                         string const end = p.verbatim_item();
2827                         if (!in_lyx_preamble) {
2828                                 h_preamble << "\\newenvironment{" << name
2829                                            << '}' << opt1 << opt2 << '{'
2830                                            << beg << "}{" << end << '}';
2831                         }
2832                         add_known_environment(name, opt1, !opt2.empty(),
2833                                               from_utf8(beg), from_utf8(end));
2834                         continue;
2835                 }
2836
2837                 if (t.cs() == "newtheorem") {
2838                         bool star = false;
2839                         if (p.next_token().character() == '*') {
2840                                 p.get_token();
2841                                 star = true;
2842                         }
2843                         string const name = p.getArg('{', '}');
2844                         string const opt1 = p.getFullOpt();
2845                         string const opt2 = p.getFullOpt();
2846                         string const body = p.verbatim_item();
2847                         string const opt3 = p.getFullOpt();
2848                         string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2849
2850                         string const complete = cmd + "{" + name + '}' +
2851                                           opt1 + opt2 + '{' + body + '}' + opt3;
2852
2853                         add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2854
2855                         if (!in_lyx_preamble)
2856                                 h_preamble << complete;
2857                         continue;
2858                 }
2859
2860                 if (t.cs() == "def") {
2861                         string name = p.get_token().cs();
2862                         // In fact, name may be more than the name:
2863                         // In the test case of bug 8116
2864                         // name == "csname SF@gobble@opt \endcsname".
2865                         // Therefore, we need to use asInput() instead of cs().
2866                         while (p.next_token().cat() != catBegin)
2867                                 name += p.get_token().asInput();
2868                         if (!in_lyx_preamble)
2869                                 h_preamble << "\\def\\" << name << '{'
2870                                            << p.verbatim_item() << "}";
2871                         continue;
2872                 }
2873
2874                 if (t.cs() == "newcolumntype") {
2875                         string const name = p.getArg('{', '}');
2876                         trimSpaceAndEol(name);
2877                         int nargs = 0;
2878                         string opts = p.getOpt();
2879                         if (!opts.empty()) {
2880                                 istringstream is(string(opts, 1));
2881                                 is >> nargs;
2882                         }
2883                         special_columns_[name[0]] = nargs;
2884                         h_preamble << "\\newcolumntype{" << name << "}";
2885                         if (nargs)
2886                                 h_preamble << "[" << nargs << "]";
2887                         h_preamble << "{" << p.verbatim_item() << "}";
2888                         continue;
2889                 }
2890
2891                 if (t.cs() == "setcounter") {
2892                         string const name = p.getArg('{', '}');
2893                         string const content = p.getArg('{', '}');
2894                         if (name == "secnumdepth")
2895                                 h_secnumdepth = content;
2896                         else if (name == "tocdepth")
2897                                 h_tocdepth = content;
2898                         else
2899                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2900                         continue;
2901                 }
2902
2903                 if (t.cs() == "setlength") {
2904                         string const name = p.verbatim_item();
2905                         string const content = p.verbatim_item();
2906                         // the paragraphs are only not indented when \parindent is set to zero
2907                         if (name == "\\parindent" && content != "")
2908                                 h_paragraph_indentation = translate_len(content);
2909                         else if (name == "\\parskip" && isPackageUsed("parskip")) {
2910                                 if (content == "\\smallskipamount")
2911                                         h_defskip = "smallskip";
2912                                 else if (content == "\\medskipamount")
2913                                         h_defskip = "medskip";
2914                                 else if (content == "\\bigskipamount")
2915                                         h_defskip = "bigskip";
2916                                 else if (content == "\\baselineskip")
2917                                         h_defskip = "fullline";
2918                                 else
2919                                         h_defskip = translate_len(content);
2920                         } else if (name == "\\mathindent") {
2921                                 h_mathindentation = translate_len(content);
2922                         } else
2923                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2924                         continue;
2925                 }
2926
2927                 if (t.cs() == "onehalfspacing") {
2928                         h_spacing = "onehalf";
2929                         continue;
2930                 }
2931
2932                 if (t.cs() == "doublespacing") {
2933                         h_spacing = "double";
2934                         continue;
2935                 }
2936
2937                 if (t.cs() == "setstretch") {
2938                         h_spacing = "other " + p.verbatim_item();
2939                         continue;
2940                 }
2941
2942                 if (t.cs() == "synctex") {
2943                         // the scheme is \synctex=value
2944                         // where value can only be "1" or "-1"
2945                         h_output_sync = "1";
2946                         // there can be any character behind the value (e.g. a linebreak or a '\'
2947                         // therefore we extract it char by char
2948                         p.get_token();
2949                         string value = p.get_token().asInput();
2950                         if (value == "-")
2951                                 value += p.get_token().asInput();
2952                         h_output_sync_macro = "\\synctex=" + value;
2953                         continue;
2954                 }
2955
2956                 if (t.cs() == "begin") {
2957                         string const name = p.getArg('{', '}');
2958                         if (name == "document")
2959                                 break;
2960                         h_preamble << "\\begin{" << name << "}";
2961                         continue;
2962                 }
2963
2964                 if (t.cs() == "geometry") {
2965                         vector<string> opts = split_options(p.getArg('{', '}'));
2966                         handle_geometry(opts);
2967                         continue;
2968                 }
2969
2970                 if (t.cs() == "definecolor") {
2971                         string const color = p.getArg('{', '}');
2972                         string const space = p.getArg('{', '}');
2973                         string const value = p.getArg('{', '}');
2974                         if (color == "document_fontcolor" && space == "rgb") {
2975                                 RGBColor c(RGBColorFromLaTeX(value));
2976                                 h_fontcolor = X11hexname(c);
2977                         } else if (color == "note_fontcolor" && space == "rgb") {
2978                                 RGBColor c(RGBColorFromLaTeX(value));
2979                                 h_notefontcolor = X11hexname(c);
2980                         } else if (color == "page_backgroundcolor" && space == "rgb") {
2981                                 RGBColor c(RGBColorFromLaTeX(value));
2982                                 h_backgroundcolor = X11hexname(c);
2983                         } else if (color == "shadecolor" && space == "rgb") {
2984                                 RGBColor c(RGBColorFromLaTeX(value));
2985                                 h_boxbgcolor = X11hexname(c);
2986                         } else {
2987                                 h_preamble << "\\definecolor{" << color
2988                                            << "}{" << space << "}{" << value
2989                                            << '}';
2990                         }
2991                         continue;
2992                 }
2993
2994                 if (t.cs() == "bibliographystyle") {
2995                         h_biblio_style = p.verbatim_item();
2996                         continue;
2997                 }
2998
2999                 if (t.cs() == "jurabibsetup") {
3000                         // FIXME p.getArg('{', '}') is most probably wrong (it
3001                         //       does not handle nested braces).
3002                         //       Use p.verbatim_item() instead.
3003                         vector<string> jurabibsetup =
3004                                 split_options(p.getArg('{', '}'));
3005                         // add jurabibsetup to the jurabib package options
3006                         add_package("jurabib", jurabibsetup);
3007                         if (!jurabibsetup.empty()) {
3008                                 h_preamble << "\\jurabibsetup{"
3009                                            << join(jurabibsetup, ",") << '}';
3010                         }
3011                         continue;
3012                 }
3013
3014                 if (t.cs() == "hypersetup") {
3015                         vector<string> hypersetup =
3016                                 split_options(p.verbatim_item());
3017                         // add hypersetup to the hyperref package options
3018                         handle_hyperref(hypersetup);
3019                         if (!hypersetup.empty()) {
3020                                 h_preamble << "\\hypersetup{"
3021                                            << join(hypersetup, ",") << '}';
3022                         }
3023                         continue;
3024                 }
3025
3026                 if (t.cs() == "includeonly") {
3027                         vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
3028                         for (auto & iofile : includeonlys) {
3029                                 string filename(normalize_filename(iofile));
3030                                 string const path = getMasterFilePath(true);
3031                                 // We want to preserve relative/absolute filenames,
3032                                 // therefore path is only used for testing
3033                                 if (!makeAbsPath(filename, path).exists()) {
3034                                         // The file extension is probably missing.
3035                                         // Now try to find it out.
3036                                         string const tex_name =
3037                                                 find_file(filename, path,
3038                                                           known_tex_extensions);
3039                                         if (!tex_name.empty())
3040                                                 filename = tex_name;
3041                                 }
3042                                 string outname;
3043                                 if (makeAbsPath(filename, path).exists())
3044                                         fix_child_filename(filename);
3045                                 else
3046                                         cerr << "Warning: Could not find included file '"
3047                                              << filename << "'." << endl;
3048                                 outname = changeExtension(filename, "lyx");
3049                                 h_includeonlys.push_back(outname);
3050                         }
3051                         continue;
3052                 }
3053
3054                 if (is_known(t.cs(), known_if_3arg_commands)) {
3055                         // prevent misparsing of \usepackage if it is used
3056                         // as an argument (see e.g. our own output of
3057                         // \@ifundefined above)
3058                         string const arg1 = p.verbatim_item();
3059                         string const arg2 = p.verbatim_item();
3060                         string const arg3 = p.verbatim_item();
3061                         // test case \@ifundefined{date}{}{\date{}}
3062                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
3063                             arg2.empty() && arg3 == "\\date{}") {
3064                                 h_suppress_date = "true";
3065                         // older tex2lyx versions did output
3066                         // \@ifundefined{definecolor}{\usepackage{color}}{}
3067                         } else if (t.cs() == "@ifundefined" &&
3068                                    arg1 == "definecolor" &&
3069                                    arg2 == "\\usepackage{color}" &&
3070                                    arg3.empty()) {
3071                                 if (!in_lyx_preamble)
3072                                         h_preamble << package_beg_sep
3073                                                    << "color"
3074                                                    << package_mid_sep
3075                                                    << "\\@ifundefined{definecolor}{color}{}"
3076                                                    << package_end_sep;
3077                         // test for case
3078                         //\@ifundefined{showcaptionsetup}{}{%
3079                         // \PassOptionsToPackage{caption=false}{subfig}}
3080                         // that LyX uses for subfloats
3081                         } else if (t.cs() == "@ifundefined" &&
3082                                    arg1 == "showcaptionsetup" && arg2.empty()
3083                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
3084                                 ; // do nothing
3085                         } else if (!in_lyx_preamble) {
3086                                 h_preamble << t.asInput()
3087                                            << '{' << arg1 << '}'
3088                                            << '{' << arg2 << '}'
3089                                            << '{' << arg3 << '}';
3090                         }
3091                         continue;
3092                 }
3093
3094                 if (is_known(t.cs(), known_if_commands)) {
3095                         // must not parse anything in conditional code, since
3096                         // LyX would output the parsed contents unconditionally
3097                         if (!in_lyx_preamble)
3098                                 h_preamble << t.asInput();
3099                         handle_if(p, in_lyx_preamble);
3100                         continue;
3101                 }
3102
3103                 if (!t.cs().empty() && !in_lyx_preamble) {
3104                         h_preamble << '\\' << t.cs();
3105                         continue;
3106                 }
3107         }
3108
3109         // set textclass if not yet done (snippets without \documentclass and forced class)
3110         if (!class_set)
3111                 setTextClass(h_textclass, tc);
3112
3113         // remove the whitespace
3114         p.skip_spaces();
3115
3116         if (h_papersides.empty()) {
3117                 ostringstream ss;
3118                 ss << tc.sides();
3119                 h_papersides = ss.str();
3120         }
3121
3122         // If the CJK package is used we cannot set the document language from
3123         // the babel options. Instead, we guess which language is used most
3124         // and set this one.
3125         default_language = h_language;
3126         if (is_full_document &&
3127             (auto_packages.find("CJK") != auto_packages.end() ||
3128              auto_packages.find("CJKutf8") != auto_packages.end())) {
3129                 p.pushPosition();
3130                 h_language = guessLanguage(p, default_language);
3131                 p.popPosition();
3132                 if (explicit_babel && h_language != default_language) {
3133                         // We set the document language to a CJK language,
3134                         // but babel is explicitly called in the user preamble
3135                         // without options. LyX will not add the default
3136                         // language to the document options if it is either
3137                         // english, or no text is set as default language.
3138                         // Therefore we need to add a language option explicitly.
3139                         // FIXME: It would be better to remove all babel calls
3140                         //        from the user preamble, but this is difficult
3141                         //        without re-introducing bug 7861.
3142                         if (h_options.empty())
3143                                 h_options = lyx2babel(default_language);
3144                         else
3145                                 h_options += ',' + lyx2babel(default_language);
3146                 }
3147         }
3148
3149         // Finally, set the quote style.
3150         // LyX knows the following quotes styles:
3151         // british, cjk, cjkangle, danish, english, french, german,
3152         // polish, russian, swedish and swiss
3153         // conversion list taken from
3154         // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
3155         // (quotes for kazakh are unknown)
3156         // british
3157         if (is_known(h_language, known_british_quotes_languages))
3158                 h_quotes_style = "british";
3159         // cjk
3160         else if (is_known(h_language, known_cjk_quotes_languages))
3161                 h_quotes_style = "cjk";
3162         // cjkangle
3163         else if (is_known(h_language, known_cjkangle_quotes_languages))
3164                 h_quotes_style = "cjkangle";
3165         // danish
3166         else if (is_known(h_language, known_danish_quotes_languages))
3167                 h_quotes_style = "danish";
3168         // french
3169         else if (is_known(h_language, known_french_quotes_languages))
3170                 h_quotes_style = "french";
3171         // german
3172         else if (is_known(h_language, known_german_quotes_languages))
3173                 h_quotes_style = "german";
3174         // polish
3175         else if (is_known(h_language, known_polish_quotes_languages))
3176                 h_quotes_style = "polish";
3177         // russian
3178         else if (is_known(h_language, known_russian_quotes_languages))
3179                 h_quotes_style = "russian";
3180         // swedish
3181         else if (is_known(h_language, known_swedish_quotes_languages))
3182                 h_quotes_style = "swedish";
3183         // swiss
3184         else if (is_known(h_language, known_swiss_quotes_languages))
3185                 h_quotes_style = "swiss";
3186         // english
3187         else if (is_known(h_language, known_english_quotes_languages))
3188                 h_quotes_style = "english";
3189 }
3190
3191
3192 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3193 {
3194         TeX2LyXDocClass dummy;
3195         parse(p, forceclass, true, dummy);
3196         if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3197                 return h_inputencoding;
3198         return "";
3199 }
3200
3201
3202 string babel2lyx(string const & language)
3203 {
3204         char const * const * where = is_known(language, known_languages);
3205         if (where)
3206                 return known_coded_languages[where - known_languages];
3207         return language;
3208 }
3209
3210
3211 string lyx2babel(string const & language)
3212 {
3213         char const * const * where = is_known(language, known_coded_languages);
3214         if (where)
3215                 return known_languages[where - known_coded_languages];
3216         return language;
3217 }
3218
3219
3220 string Preamble::polyglossia2lyx(string const & language)
3221 {
3222         char const * const * where = is_known(language, polyglossia_languages);
3223         if (where)
3224                 return coded_polyglossia_languages[where - polyglossia_languages];
3225         return language;
3226 }
3227
3228
3229 string rgbcolor2code(string const & name)
3230 {
3231         char const * const * where = is_known(name, known_basic_colors);
3232         if (where) {
3233                 // "red", "green" etc
3234                 return known_basic_color_codes[where - known_basic_colors];
3235         }
3236         // "255,0,0", "0,255,0" etc
3237         RGBColor c(RGBColorFromLaTeX(name));
3238         return X11hexname(c);
3239 }
3240
3241 // }])
3242
3243
3244 } // namespace lyx