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