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