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