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