]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
Fix functions that used functions but did not defined it
[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                                 // get value, unbraced
1750                                 string length = rtrim(ltrim(token(opts, '=', 1), "{"), "}");
1751                                 // transform glue length if we have one
1752                                 is_glue_length(length);
1753                                 h_defskip = length;
1754                         }
1755                         h_paragraph_separation = "skip";
1756                 }
1757         }
1758
1759         else if (is_known(name, known_lyx_packages) && options.empty()) {
1760                 if (name == "splitidx")
1761                         h_use_indices = "true";
1762                 else if (name == "minted")
1763                         h_use_minted = true;
1764                 else if (name == "refstyle")
1765                         h_use_refstyle = true;
1766                 else if (name == "prettyref")
1767                         h_use_refstyle = false;
1768
1769                 if (!in_lyx_preamble) {
1770                         h_preamble << package_beg_sep << name
1771                                    << package_mid_sep << "\\usepackage{"
1772                                    << name << '}';
1773                         if (p.next_token().cat() == catNewline ||
1774                             (p.next_token().cat() == catSpace &&
1775                              p.next_next_token().cat() == catNewline))
1776                                 h_preamble << '\n';
1777                         h_preamble << package_end_sep;
1778                 }
1779         }
1780
1781         else if (name == "geometry")
1782                 handle_geometry(options);
1783
1784         else if (name == "subfig")
1785                 ; // ignore this FIXME: Use the package separator mechanism instead
1786
1787         else if (char const * const * where = is_known(name, known_languages))
1788                 h_language = known_coded_languages[where - known_languages];
1789
1790         else if (name == "natbib") {
1791                 h_biblio_style = "plainnat";
1792                 h_cite_engine = "natbib";
1793                 h_cite_engine_type = "authoryear";
1794                 vector<string>::iterator it =
1795                         find(options.begin(), options.end(), "authoryear");
1796                 if (it != options.end())
1797                         options.erase(it);
1798                 else {
1799                         it = find(options.begin(), options.end(), "numbers");
1800                         if (it != options.end()) {
1801                                 h_cite_engine_type = "numerical";
1802                                 options.erase(it);
1803                         }
1804                 }
1805                 if (!options.empty())
1806                         h_biblio_options = join(options, ",");
1807         }
1808
1809         else if (name == "biblatex") {
1810                 h_biblio_style = "plainnat";
1811                 h_cite_engine = "biblatex";
1812                 h_cite_engine_type = "authoryear";
1813                 string opt;
1814                 vector<string>::iterator it =
1815                         find(options.begin(), options.end(), "natbib");
1816                 if (it != options.end()) {
1817                         options.erase(it);
1818                         h_cite_engine = "biblatex-natbib";
1819                 } else {
1820                         opt = process_keyval_opt(options, "natbib");
1821                         if (opt == "true")
1822                                 h_cite_engine = "biblatex-natbib";
1823                 }
1824                 opt = process_keyval_opt(options, "style");
1825                 if (!opt.empty()) {
1826                         h_biblatex_citestyle = opt;
1827                         h_biblatex_bibstyle = opt;
1828                 } else {
1829                         opt = process_keyval_opt(options, "citestyle");
1830                         if (!opt.empty())
1831                                 h_biblatex_citestyle = opt;
1832                         opt = process_keyval_opt(options, "bibstyle");
1833                         if (!opt.empty())
1834                                 h_biblatex_bibstyle = opt;
1835                 }
1836                 opt = process_keyval_opt(options, "refsection");
1837                 if (!opt.empty()) {
1838                         if (opt == "none" || opt == "part"
1839                             || opt == "chapter" || opt == "section"
1840                             || opt == "subsection")
1841                                 h_multibib = opt;
1842                         else
1843                                 warning_message("Ignoring unknown refsection value '" + opt + "'.");
1844                 }
1845                 opt = process_keyval_opt(options, "bibencoding");
1846                 if (!opt.empty())
1847                         bibencoding = opt;
1848                 if (!options.empty()) {
1849                         h_biblio_options = join(options, ",");
1850                         options.clear();
1851                 }
1852         }
1853
1854         else if (name == "jurabib") {
1855                 h_biblio_style = "jurabib";
1856                 h_cite_engine = "jurabib";
1857                 h_cite_engine_type = "authoryear";
1858                 if (!options.empty())
1859                         h_biblio_options = join(options, ",");
1860         }
1861
1862         else if (name == "bibtopic")
1863                 h_use_bibtopic = "true";
1864
1865         else if (name == "chapterbib")
1866                 h_multibib = "child";
1867
1868         else if (name == "hyperref")
1869                 handle_hyperref(options);
1870
1871         else if (name == "algorithm2e") {
1872                 // Load "algorithm2e" module
1873                 addModule("algorithm2e");
1874                 // Add the package options to the global document options
1875                 if (!options.empty()) {
1876                         if (h_options.empty())
1877                                 h_options = join(options, ",");
1878                         else
1879                                 h_options += ',' + join(options, ",");
1880                 }
1881         }
1882         else if (name == "microtype") {
1883                 //we internally support only microtype without params
1884                 if (options.empty())
1885                         h_use_microtype = "true";
1886                 else
1887                         h_preamble << "\\usepackage[" << opts << "]{microtype}";
1888         }
1889
1890         else if (name == "lineno") {
1891                 h_use_lineno = "true";
1892                 if (!options.empty()) {
1893                         h_lineno_options = join(options, ",");
1894                         options.clear();
1895                 }
1896         }
1897
1898         else if (name == "changebar")
1899                 h_output_changes = "true";
1900
1901         else if (!in_lyx_preamble) {
1902                 if (options.empty())
1903                         h_preamble << "\\usepackage{" << name << '}';
1904                 else {
1905                         h_preamble << "\\usepackage[" << opts << "]{"
1906                                    << name << '}';
1907                         options.clear();
1908                 }
1909                 if (p.next_token().cat() == catNewline ||
1910                     (p.next_token().cat() == catSpace &&
1911                      p.next_next_token().cat() == catNewline))
1912                         h_preamble << '\n';
1913         }
1914
1915         // We need to do something with the options...
1916         if (!options.empty() && !detectEncoding)
1917                 warning_message("Ignoring options '" + join(options, ",")
1918                                 + "' of package " + name + '.');
1919
1920         // remove the whitespace
1921         p.skip_spaces();
1922 }
1923
1924
1925 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1926 {
1927         while (p.good()) {
1928                 Token t = p.get_token();
1929                 if (t.cat() == catEscape &&
1930                     is_known(t.cs(), known_if_commands))
1931                         handle_if(p, in_lyx_preamble);
1932                 else {
1933                         if (!in_lyx_preamble)
1934                                 h_preamble << t.asInput();
1935                         if (t.cat() == catEscape && t.cs() == "fi")
1936                                 return;
1937                 }
1938         }
1939 }
1940
1941
1942 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1943 {
1944         if (contains(h_float_placement, "H"))
1945                 registerAutomaticallyLoadedPackage("float");
1946         if (h_spacing != "single" && h_spacing != "default")
1947                 registerAutomaticallyLoadedPackage("setspace");
1948         if (h_use_packages["amsmath"] == "2") {
1949                 // amsbsy and amstext are already provided by amsmath
1950                 registerAutomaticallyLoadedPackage("amsbsy");
1951                 registerAutomaticallyLoadedPackage("amstext");
1952         }
1953
1954         // output the LyX file settings
1955         // Important: Keep the version formatting in sync with LyX and
1956         //            lyx2lyx (bug 7951)
1957         string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1958         os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1959            << lyx_version_minor << '\n'
1960            << "\\lyxformat " << LYX_FORMAT << '\n'
1961            << "\\begin_document\n"
1962            << "\\begin_header\n"
1963            << "\\save_transient_properties " << h_save_transient_properties << "\n"
1964            << "\\origin " << origin << "\n"
1965            << "\\textclass " << h_textclass << "\n";
1966         if (!h_doc_metadata.empty()) {
1967                 os << "\\begin_metadata\n"
1968                    << h_doc_metadata
1969                    << "\n\\end_metadata\n";
1970         }
1971         string const raw = subdoc ? empty_string() : h_preamble.str();
1972         if (!raw.empty()) {
1973                 os << "\\begin_preamble\n";
1974                 for (string::size_type i = 0; i < raw.size(); ++i) {
1975                         if (raw[i] == package_beg_sep) {
1976                                 // Here follows some package loading code that
1977                                 // must be skipped if the package is loaded
1978                                 // automatically.
1979                                 string::size_type j = raw.find(package_mid_sep, i);
1980                                 if (j == string::npos)
1981                                         return false;
1982                                 string::size_type k = raw.find(package_end_sep, j);
1983                                 if (k == string::npos)
1984                                         return false;
1985                                 string const package = raw.substr(i + 1, j - i - 1);
1986                                 string const replacement = raw.substr(j + 1, k - j - 1);
1987                                 if (auto_packages.find(package) == auto_packages.end())
1988                                         os << replacement;
1989                                 i = k;
1990                         } else
1991                                 os.put(raw[i]);
1992                 }
1993                 os << "\n\\end_preamble\n";
1994         }
1995         if (!h_options.empty())
1996                 os << "\\options " << h_options << "\n";
1997         os << "\\use_default_options " << h_use_default_options << "\n";
1998         if (!used_modules.empty()) {
1999                 os << "\\begin_modules\n";
2000                 vector<string>::const_iterator const end = used_modules.end();
2001                 vector<string>::const_iterator it = used_modules.begin();
2002                 for (; it != end; ++it)
2003                         os << *it << '\n';
2004                 os << "\\end_modules\n";
2005         }
2006         if (!h_includeonlys.empty()) {
2007                 os << "\\begin_includeonly\n";
2008                 for (auto const & iofile : h_includeonlys)
2009                         os << iofile << '\n';
2010                 os << "\\end_includeonly\n";
2011         }
2012         os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
2013            << "\\language " << h_language << "\n"
2014            << "\\language_package " << h_language_package << "\n"
2015            << "\\inputencoding " << h_inputencoding << "\n"
2016            << "\\fontencoding " << h_fontencoding << "\n"
2017            << "\\font_roman \"" << h_font_roman[0]
2018            << "\" \"" << h_font_roman[1] << "\"\n"
2019            << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
2020            << "\\font_typewriter \"" << h_font_typewriter[0]
2021            << "\" \"" << h_font_typewriter[1] << "\"\n"
2022            << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
2023            << "\\font_default_family " << h_font_default_family << "\n"
2024            << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
2025            << "\\font_sc " << h_font_sc << "\n"
2026            << "\\font_roman_osf " << h_font_roman_osf << "\n"
2027            << "\\font_sans_osf " << h_font_sans_osf << "\n"
2028            << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
2029         if (!h_font_roman_opts.empty())
2030                 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
2031         os << "\\font_sf_scale " << h_font_sf_scale[0]
2032            << ' ' << h_font_sf_scale[1] << '\n';
2033         if (!h_font_sans_opts.empty())
2034                 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
2035         os << "\\font_tt_scale " << h_font_tt_scale[0]
2036            << ' ' << h_font_tt_scale[1] << '\n';
2037         if (!h_font_cjk.empty())
2038                 os << "\\font_cjk " << h_font_cjk << '\n';
2039         if (!h_font_typewriter_opts.empty())
2040                 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
2041         os << "\\use_microtype " << h_use_microtype << '\n'
2042            << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
2043            << "\\graphics " << h_graphics << '\n'
2044            << "\\default_output_format " << h_default_output_format << "\n"
2045            << "\\output_sync " << h_output_sync << "\n";
2046         if (h_output_sync == "1")
2047                 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
2048         os << "\\bibtex_command " << h_bibtex_command << "\n"
2049            << "\\index_command " << h_index_command << "\n";
2050         if (!h_float_placement.empty())
2051                 os << "\\float_placement " << h_float_placement << "\n";
2052         os << "\\paperfontsize " << h_paperfontsize << "\n"
2053            << "\\spacing " << h_spacing << "\n"
2054            << "\\use_hyperref " << h_use_hyperref << '\n';
2055         if (h_use_hyperref == "true") {
2056                 if (!h_pdf_title.empty())
2057                         os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
2058                 if (!h_pdf_author.empty())
2059                         os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
2060                 if (!h_pdf_subject.empty())
2061                         os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
2062                 if (!h_pdf_keywords.empty())
2063                         os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
2064                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
2065                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
2066                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
2067                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
2068                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
2069                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
2070                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
2071                       "\\pdf_backref " << h_pdf_backref << "\n"
2072                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
2073                 if (!h_pdf_pagemode.empty())
2074                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
2075                 if (!h_pdf_quoted_options.empty())
2076                         os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
2077         }
2078         os << "\\papersize " << h_papersize << "\n"
2079            << "\\use_geometry " << h_use_geometry << '\n';
2080         for (map<string, string>::const_iterator it = h_use_packages.begin();
2081              it != h_use_packages.end(); ++it)
2082                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
2083         os << "\\cite_engine " << h_cite_engine << '\n'
2084            << "\\cite_engine_type " << h_cite_engine_type << '\n'
2085            << "\\biblio_style " << h_biblio_style << "\n"
2086            << "\\use_bibtopic " << h_use_bibtopic << "\n";
2087         if (!h_biblio_options.empty())
2088                 os << "\\biblio_options " << h_biblio_options << "\n";
2089         if (!h_biblatex_bibstyle.empty())
2090                 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
2091         if (!h_biblatex_citestyle.empty())
2092                 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
2093         if (!h_multibib.empty())
2094                 os << "\\multibib " << h_multibib << "\n";
2095         os << "\\use_indices " << h_use_indices << "\n"
2096            << "\\paperorientation " << h_paperorientation << '\n'
2097            << "\\suppress_date " << h_suppress_date << '\n'
2098            << "\\justification " << h_justification << '\n'
2099            << "\\use_refstyle " << h_use_refstyle << '\n'
2100            << "\\use_minted " << h_use_minted << '\n'
2101            << "\\use_lineno " << h_use_lineno << '\n';
2102         if (!h_lineno_options.empty())
2103                 os << "\\lineno_options " << h_lineno_options << '\n';
2104         if (!h_fontcolor.empty())
2105                 os << "\\fontcolor " << h_fontcolor << '\n';
2106         if (!h_notefontcolor.empty())
2107                 os << "\\notefontcolor " << h_notefontcolor << '\n';
2108         if (!h_backgroundcolor.empty())
2109                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
2110         if (!h_boxbgcolor.empty())
2111                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
2112         if (index_number != 0)
2113                 for (int i = 0; i < index_number; i++) {
2114                         os << "\\index " << h_index[i] << '\n'
2115                            << "\\shortcut " << h_shortcut[i] << '\n'
2116                            << "\\color " << h_color << '\n'
2117                            << "\\end_index\n";
2118                 }
2119         else {
2120                 os << "\\index " << h_index[0] << '\n'
2121                    << "\\shortcut " << h_shortcut[0] << '\n'
2122                    << "\\color " << h_color << '\n'
2123                    << "\\end_index\n";
2124         }
2125         os << h_margins
2126            << "\\secnumdepth " << h_secnumdepth << "\n"
2127            << "\\tocdepth " << h_tocdepth << "\n"
2128            << "\\paragraph_separation " << h_paragraph_separation << "\n";
2129         if (h_paragraph_separation == "skip")
2130                 os << "\\defskip " << h_defskip << "\n";
2131         else
2132                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2133         os << "\\is_math_indent " << h_is_mathindent << "\n";
2134         if (!h_mathindentation.empty())
2135                 os << "\\math_indentation " << h_mathindentation << "\n";
2136         os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2137         os << "\\quotes_style " << h_quotes_style << "\n"
2138            << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2139            << "\\papercolumns " << h_papercolumns << "\n"
2140            << "\\papersides " << h_papersides << "\n"
2141            << "\\paperpagestyle " << h_paperpagestyle << "\n";
2142         if (!h_listings_params.empty())
2143                 os << "\\listings_params " << h_listings_params << "\n";
2144         os << "\\tracking_changes " << h_tracking_changes << "\n"
2145            << "\\output_changes " << h_output_changes << "\n"
2146            << "\\change_bars " << h_change_bars << "\n"
2147            << "\\html_math_output " << h_html_math_output << "\n"
2148            << "\\html_css_as_file " << h_html_css_as_file << "\n"
2149            << "\\html_be_strict " << h_html_be_strict << "\n"
2150            << "\\docbook_table_output " << h_docbook_table_output << "\n"
2151            << "\\docbook_mathml_prefix " << h_docbook_mathml_prefix << "\n"
2152            << authors_
2153            << "\\end_header\n\n"
2154            << "\\begin_body\n";
2155         return true;
2156 }
2157
2158
2159 void Preamble::parse(Parser & p, string const & forceclass,
2160                      TeX2LyXDocClass & tc)
2161 {
2162         // initialize fixed types
2163         special_columns_['D'] = 3;
2164         parse(p, forceclass, false, tc);
2165 }
2166
2167
2168 void Preamble::parse(Parser & p, string const & forceclass,
2169                      bool detectEncoding, TeX2LyXDocClass & tc)
2170 {
2171         bool is_full_document = false;
2172         bool is_lyx_file = false;
2173         bool in_lyx_preamble = false;
2174         bool class_set = false;
2175
2176         // determine whether this is a full document or a fragment for inclusion
2177         while (p.good()) {
2178                 Token const & t = p.get_token();
2179
2180                 if (t.cat() == catEscape && t.cs() == "documentclass") {
2181                         is_full_document = true;
2182                         break;
2183                 }
2184         }
2185         p.reset();
2186
2187         if (detectEncoding && !is_full_document)
2188                 return;
2189
2190         // Force textclass if the user wanted it
2191         if (!forceclass.empty()) {
2192                 setTextClass(forceclass, tc);
2193                 class_set = true;
2194         }
2195
2196         while (is_full_document && p.good()) {
2197                 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2198                     h_inputencoding != "auto-legacy-plain")
2199                         return;
2200
2201                 Token const & t = p.get_token();
2202
2203                 if (!detectEncoding)
2204                         debug_message("t: " + t.asInput());
2205
2206                 //
2207                 // cat codes
2208                 //
2209                 if (!in_lyx_preamble &&
2210                     (t.cat() == catLetter ||
2211                      t.cat() == catSuper ||
2212                      t.cat() == catSub ||
2213                      t.cat() == catOther ||
2214                      t.cat() == catMath ||
2215                      t.cat() == catActive ||
2216                      t.cat() == catBegin ||
2217                      t.cat() == catEnd ||
2218                      t.cat() == catAlign ||
2219                      t.cat() == catParameter)) {
2220                         h_preamble << t.cs();
2221                         continue;
2222                 }
2223
2224                 if (!in_lyx_preamble &&
2225                     (t.cat() == catSpace || t.cat() == catNewline)) {
2226                         h_preamble << t.asInput();
2227                         continue;
2228                 }
2229
2230                 if (t.cat() == catComment) {
2231                         static regex const islyxfile("%% LyX .* created this file");
2232                         static regex const usercommands("User specified LaTeX commands");
2233
2234                         string const comment = t.asInput();
2235
2236                         // magically switch encoding default if it looks like XeLaTeX
2237                         static string const magicXeLaTeX =
2238                                 "% This document must be compiled with XeLaTeX ";
2239                         if (comment.size() > magicXeLaTeX.size()
2240                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2241                                   && h_inputencoding == "auto-legacy") {
2242                                 if (!detectEncoding)
2243                                         warning_message("XeLaTeX comment found, switching to UTF8");
2244                                 h_inputencoding = "utf8";
2245                         }
2246                         smatch sub;
2247                         if (regex_search(comment, sub, islyxfile)) {
2248                                 is_lyx_file = true;
2249                                 in_lyx_preamble = true;
2250                         } else if (is_lyx_file
2251                                    && regex_search(comment, sub, usercommands))
2252                                 in_lyx_preamble = false;
2253                         else if (!in_lyx_preamble)
2254                                 h_preamble << t.asInput();
2255                         continue;
2256                 }
2257
2258                 if (t.cs() == "PassOptionsToPackage") {
2259                         string const poptions = p.getArg('{', '}');
2260                         string const package = p.verbatim_item();
2261                         extra_package_options_.insert(make_pair(package, poptions));
2262                         continue;
2263                 }
2264
2265                 if (t.cs() == "pagestyle") {
2266                         h_paperpagestyle = p.verbatim_item();
2267                         continue;
2268                 }
2269
2270                 if (t.cs() == "setdefaultlanguage") {
2271                         xetex = true;
2272                         // We don't yet care about non-language variant options
2273                         // because LyX doesn't support this yet, see bug #8214
2274                         if (p.hasOpt()) {
2275                                 string langopts = p.getOpt();
2276                                 // check if the option contains a variant, if yes, extract it
2277                                 string::size_type pos_var = langopts.find("variant");
2278                                 string::size_type i = langopts.find(',', pos_var);
2279                                 string::size_type k = langopts.find('=', pos_var);
2280                                 if (pos_var != string::npos){
2281                                         string variant;
2282                                         if (i == string::npos)
2283                                                 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2284                                         else
2285                                                 variant = langopts.substr(k + 1, i - k - 1);
2286                                         h_language = variant;
2287                                 }
2288                                 p.verbatim_item();
2289                         } else
2290                                 h_language = p.verbatim_item();
2291                         //finally translate the poyglossia name to a LyX name
2292                         h_language = polyglossia2lyx(h_language);
2293                         continue;
2294                 }
2295
2296                 if (t.cs() == "setotherlanguage") {
2297                         // We don't yet care about the option because LyX doesn't
2298                         // support this yet, see bug #8214
2299                         p.hasOpt() ? p.getOpt() : string();
2300                         p.verbatim_item();
2301                         continue;
2302                 }
2303
2304                 if (t.cs() == "setmainfont") {
2305                         string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2306                         h_font_roman[1] = p.getArg('{', '}');
2307                         if (!fontopts.empty()) {
2308                                 vector<string> opts = getVectorFromString(fontopts);
2309                                 fontopts.clear();
2310                                 for (auto const & opt : opts) {
2311                                         if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2312                                                 // ignore
2313                                                 continue;
2314                                         if (!fontopts.empty())
2315                                                 fontopts += ", ";
2316                                         fontopts += opt;
2317                                 }
2318                                 h_font_roman_opts = fontopts;
2319                         }
2320                         continue;
2321                 }
2322
2323                 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2324                         // LyX currently only supports the scale option
2325                         string scale, fontopts;
2326                         if (p.hasOpt()) {
2327                                 fontopts = p.getArg('[', ']');
2328                                 if (!fontopts.empty()) {
2329                                         vector<string> opts = getVectorFromString(fontopts);
2330                                         fontopts.clear();
2331                                         for (auto const & opt : opts) {
2332                                                 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2333                                                         // ignore
2334                                                         continue;
2335                                                 if (prefixIs(opt, "Scale=")) {
2336                                                         scale_as_percentage(opt, scale);
2337                                                         continue;
2338                                                 }
2339                                                 if (!fontopts.empty())
2340                                                         fontopts += ", ";
2341                                                 fontopts += opt;
2342                                         }
2343                                 }
2344                         }
2345                         if (t.cs() == "setsansfont") {
2346                                 if (!scale.empty())
2347                                         h_font_sf_scale[1] = scale;
2348                                 h_font_sans[1] = p.getArg('{', '}');
2349                                 if (!fontopts.empty())
2350                                         h_font_sans_opts = fontopts;
2351                         } else {
2352                                 if (!scale.empty())
2353                                         h_font_tt_scale[1] = scale;
2354                                 h_font_typewriter[1] = p.getArg('{', '}');
2355                                 if (!fontopts.empty())
2356                                         h_font_typewriter_opts = fontopts;
2357                         }
2358                         continue;
2359                 }
2360
2361                 if (t.cs() == "babelfont") {
2362                         xetex = true;
2363                         h_use_non_tex_fonts = true;
2364                         h_language_package = "babel";
2365                         if (h_inputencoding == "auto-legacy")
2366                                 p.setEncoding("UTF-8");
2367                         // we don't care about the lang option
2368                         string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2369                         string const family = p.getArg('{', '}');
2370                         string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2371                         string const fontname = p.getArg('{', '}');
2372                         if (lang.empty() && family == "rm") {
2373                                 h_font_roman[1] = fontname;
2374                                 if (!fontopts.empty()) {
2375                                         vector<string> opts = getVectorFromString(fontopts);
2376                                         fontopts.clear();
2377                                         for (auto const & opt : opts) {
2378                                                 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2379                                                         // ignore
2380                                                         continue;
2381                                                 if (!fontopts.empty())
2382                                                         fontopts += ", ";
2383                                                 fontopts += opt;
2384                                         }
2385                                         h_font_roman_opts = fontopts;
2386                                 }
2387                                 continue;
2388                         } else if (lang.empty() && (family == "sf" || family == "tt")) {
2389                                 string scale;
2390                                 if (!fontopts.empty()) {
2391                                         vector<string> opts = getVectorFromString(fontopts);
2392                                         fontopts.clear();
2393                                         for (auto const & opt : opts) {
2394                                                 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2395                                                         // ignore
2396                                                         continue;
2397                                                 if (prefixIs(opt, "Scale=")) {
2398                                                         scale_as_percentage(opt, scale);
2399                                                         continue;
2400                                                 }
2401                                                 if (!fontopts.empty())
2402                                                         fontopts += ", ";
2403                                                 fontopts += opt;
2404                                         }
2405                                 }
2406                                 if (family == "sf") {
2407                                         if (!scale.empty())
2408                                                 h_font_sf_scale[1] = scale;
2409                                         h_font_sans[1] = fontname;
2410                                         if (!fontopts.empty())
2411                                                 h_font_sans_opts = fontopts;
2412                                 } else {
2413                                         if (!scale.empty())
2414                                                 h_font_tt_scale[1] = scale;
2415                                         h_font_typewriter[1] = fontname;
2416                                         if (!fontopts.empty())
2417                                                 h_font_typewriter_opts = fontopts;
2418                                 }
2419                                 continue;
2420                         } else {
2421                                 // not rm, sf or tt or lang specific
2422                                 h_preamble << '\\' << t.cs();
2423                                 if (!lang.empty())
2424                                         h_preamble << '[' << lang << ']';
2425                                 h_preamble << '{' << family << '}';
2426                                 if (!fontopts.empty())
2427                                         h_preamble << '[' << fontopts << ']';
2428                                 h_preamble << '{' << fontname << '}' << '\n';
2429                                 continue;
2430                         }
2431                 }
2432
2433                 if (t.cs() == "date") {
2434                         string argument = p.getArg('{', '}');
2435                         if (argument.empty())
2436                                 h_suppress_date = "true";
2437                         else
2438                                 h_preamble << t.asInput() << '{' << argument << '}';
2439                         continue;
2440                 }
2441
2442                 if (t.cs() == "color") {
2443                         string const space =
2444                                 (p.hasOpt() ? p.getOpt() : string());
2445                         string argument = p.getArg('{', '}');
2446                         // check the case that a standard color is used
2447                         if (space.empty() && is_known(argument, known_basic_colors)) {
2448                                 h_fontcolor = rgbcolor2code(argument);
2449                                 registerAutomaticallyLoadedPackage("color");
2450                         } else if (space.empty() && argument == "document_fontcolor")
2451                                 registerAutomaticallyLoadedPackage("color");
2452                         // check the case that LyX's document_fontcolor is defined
2453                         // but not used for \color
2454                         else {
2455                                 h_preamble << t.asInput();
2456                                 if (!space.empty())
2457                                         h_preamble << space;
2458                                 h_preamble << '{' << argument << '}';
2459                                 // the color might already be set because \definecolor
2460                                 // is parsed before this
2461                                 h_fontcolor = "";
2462                         }
2463                         continue;
2464                 }
2465
2466                 if (t.cs() == "pagecolor") {
2467                         string argument = p.getArg('{', '}');
2468                         // check the case that a standard color is used
2469                         if (is_known(argument, known_basic_colors)) {
2470                                 h_backgroundcolor = rgbcolor2code(argument);
2471                         } else if (argument == "page_backgroundcolor")
2472                                 registerAutomaticallyLoadedPackage("color");
2473                         // check the case that LyX's page_backgroundcolor is defined
2474                         // but not used for \pagecolor
2475                         else {
2476                                 h_preamble << t.asInput() << '{' << argument << '}';
2477                                 // the color might already be set because \definecolor
2478                                 // is parsed before this
2479                                 h_backgroundcolor = "";
2480                         }
2481                         continue;
2482                 }
2483
2484                 if (t.cs() == "makeatletter") {
2485                         // LyX takes care of this
2486                         p.setCatcode('@', catLetter);
2487                         continue;
2488                 }
2489
2490                 if (t.cs() == "makeatother") {
2491                         // LyX takes care of this
2492                         p.setCatcode('@', catOther);
2493                         continue;
2494                 }
2495
2496                 if (t.cs() == "makeindex") {
2497                         // LyX will re-add this if a print index command is found
2498                         p.skip_spaces();
2499                         continue;
2500                 }
2501
2502                 if (t.cs() == "newindex") {
2503                         string const indexname = p.getArg('[', ']');
2504                         string const shortcut = p.verbatim_item();
2505                         if (!indexname.empty())
2506                                 h_index[index_number] = indexname;
2507                         else
2508                                 h_index[index_number] = shortcut;
2509                         h_shortcut[index_number] = shortcut;
2510                         index_number += 1;
2511                         p.skip_spaces();
2512                         continue;
2513                 }
2514
2515                 if (t.cs() == "addbibresource") {
2516                         string const options =  p.getArg('[', ']');
2517                         string const arg = removeExtension(p.getArg('{', '}'));
2518                         if (!options.empty()) {
2519                                 // check if the option contains a bibencoding, if yes, extract it
2520                                 string::size_type pos = options.find("bibencoding=");
2521                                 string encoding;
2522                                 if (pos != string::npos) {
2523                                         string::size_type i = options.find(',', pos);
2524                                         if (i == string::npos)
2525                                                 encoding = options.substr(pos + 1);
2526                                         else
2527                                                 encoding = options.substr(pos, i - pos);
2528                                         pos = encoding.find('=');
2529                                         if (pos == string::npos)
2530                                                 encoding.clear();
2531                                         else
2532                                                 encoding = encoding.substr(pos + 1);
2533                                 }
2534                                 if (!encoding.empty())
2535                                         biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2536                         }
2537                         biblatex_bibliographies.push_back(arg);
2538                         continue;
2539                 }
2540
2541                 if (t.cs() == "bibliography") {
2542                         vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2543                         biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2544                         continue;
2545                 }
2546
2547                 if (t.cs() == "RS@ifundefined") {
2548                         string const name = p.verbatim_item();
2549                         string const body1 = p.verbatim_item();
2550                         string const body2 = p.verbatim_item();
2551                         // only non-lyxspecific stuff
2552                         if (in_lyx_preamble &&
2553                             (name == "subsecref" || name == "thmref" || name == "lemref"))
2554                                 p.skip_spaces();
2555                         else {
2556                                 ostringstream ss;
2557                                 ss << '\\' << t.cs();
2558                                 ss << '{' << name << '}'
2559                                    << '{' << body1 << '}'
2560                                    << '{' << body2 << '}';
2561                                 h_preamble << ss.str();
2562                         }
2563                         continue;
2564                 }
2565
2566                 if (t.cs() == "AtBeginDocument") {
2567                         string const name = p.verbatim_item();
2568                         // only non-lyxspecific stuff
2569                         if (in_lyx_preamble &&
2570                             (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2571                                 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2572                                 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2573                                 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2574                                 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2575                                 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2576                                 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2577                                 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2578                                 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2579                                 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2580                                 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2581                                 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2582                                 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2583                                 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2584                                 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2585                                 p.skip_spaces();
2586                         else {
2587                                 ostringstream ss;
2588                                 ss << '\\' << t.cs();
2589                                 ss << '{' << name << '}';
2590                                 h_preamble << ss.str();
2591                         }
2592                         continue;
2593                 }
2594
2595                 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2596                     || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2597                     || t.cs() == "providecommand" || t.cs() == "providecommandx"
2598                     || t.cs() == "DeclareRobustCommand"
2599                     || t.cs() == "DeclareRobustCommandx"
2600                     || t.cs() == "ProvideTextCommandDefault"
2601                     || t.cs() == "DeclareMathAccent") {
2602                         bool star = false;
2603                         if (p.next_token().character() == '*') {
2604                                 p.get_token();
2605                                 star = true;
2606                         }
2607                         string const name = p.verbatim_item();
2608                         string const opt1 = p.getFullOpt();
2609                         string const opt2 = p.getFullOpt();
2610                         string const body = p.verbatim_item();
2611                         // store the in_lyx_preamble setting
2612                         bool const was_in_lyx_preamble = in_lyx_preamble;
2613                         // font settings
2614                         if (name == "\\rmdefault")
2615                                 if (is_known(body, known_roman_font_packages)) {
2616                                         h_font_roman[0] = body;
2617                                         p.skip_spaces();
2618                                         in_lyx_preamble = true;
2619                                 }
2620                         if (name == "\\sfdefault") {
2621                                 if (is_known(body, known_sans_font_packages)) {
2622                                         h_font_sans[0] = body;
2623                                         p.skip_spaces();
2624                                         in_lyx_preamble = true;
2625                                 }
2626                                 if (body == "LibertinusSans-OsF") {
2627                                         h_font_sans[0] = "LibertinusSans-LF";
2628                                         h_font_sans_osf = "true";
2629                                         p.skip_spaces();
2630                                         in_lyx_preamble = true;
2631                                 }
2632                         }
2633                         if (name == "\\ttdefault")
2634                                 if (is_known(body, known_typewriter_font_packages)) {
2635                                         h_font_typewriter[0] = body;
2636                                         p.skip_spaces();
2637                                         in_lyx_preamble = true;
2638                                 }
2639                         if (name == "\\familydefault") {
2640                                 string family = body;
2641                                 // remove leading "\"
2642                                 h_font_default_family = family.erase(0,1);
2643                                 p.skip_spaces();
2644                                 in_lyx_preamble = true;
2645                         }
2646                         if (name == "\\LibertinusSans@scale") {
2647                                 if (isStrDbl(body)) {
2648                                         h_font_sf_scale[0] = convert<string>(
2649                                                 static_cast<int>(100 * convert<double>(body)));
2650                                 }
2651                         }
2652                         if (name == "\\LibertinusMono@scale") {
2653                                 if (isStrDbl(body)) {
2654                                         h_font_tt_scale[0] = convert<string>(
2655                                                 static_cast<int>(100 * convert<double>(body)));
2656                                 }
2657                         }
2658
2659                         // remove LyX-specific definitions that are re-added by LyX
2660                         // if necessary
2661                         // \lyxline is an ancient command that is converted by tex2lyx into
2662                         // a \rule therefore remove its preamble code
2663                         if (name == "\\lyxdot" || name == "\\lyxarrow"
2664                             || name == "\\lyxline" || name == "\\LyX") {
2665                                 p.skip_spaces();
2666                                 in_lyx_preamble = true;
2667                         }
2668
2669                         // Add the command to the known commands
2670                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2671
2672                         // only non-lyxspecific stuff
2673                         if (!in_lyx_preamble) {
2674                                 ostringstream ss;
2675                                 ss << '\\' << t.cs();
2676                                 if (star)
2677                                         ss << '*';
2678                                 ss << '{' << name << '}' << opt1 << opt2
2679                                    << '{' << body << "}";
2680                                 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2681                                         h_preamble << ss.str();
2682 /*
2683                                 ostream & out = in_preamble ? h_preamble : os;
2684                                 out << "\\" << t.cs() << "{" << name << "}"
2685                                     << opts << "{" << body << "}";
2686 */
2687                         }
2688                         // restore the in_lyx_preamble setting
2689                         in_lyx_preamble = was_in_lyx_preamble;
2690                         continue;
2691                 }
2692
2693                 if (t.cs() == "documentclass") {
2694                         vector<string>::iterator it;
2695                         vector<string> opts = split_options(p.getArg('[', ']'));
2696                         // FIXME This does not work for classes that have a
2697                         //       different name in LyX than in LaTeX
2698                         string tclass = p.getArg('{', '}');
2699                         if (contains(tclass, '/')) {
2700                                 // It's considered bad practice, but it is still
2701                                 // sometimes done (and possible) to enter the documentclass
2702                                 // as a path, e.g. \documentclass{subdir/class} (#12284)
2703                                 // we strip the name in this case.
2704                                 string dummy;
2705                                 tclass = rsplit(tclass, dummy, '/');
2706                         }
2707                         p.skip_spaces();
2708                         // Only set text class if a class hasn't been forced
2709                         // (this was set above)
2710                         if (!class_set) {
2711                                 // textclass needs to be set at this place (if not already done)
2712                                 // as we need to know it for other parameters
2713                                 // (such as class-dependent paper size)
2714                                 setTextClass(tclass, tc);
2715                                 class_set = true;
2716                         }
2717
2718                         // Font sizes.
2719                         // Try those who are (most likely) known to all packages first
2720                         handle_opt(opts, known_fontsizes, h_paperfontsize);
2721                         delete_opt(opts, known_fontsizes);
2722                         // delete "pt" at the end
2723                         string::size_type i = h_paperfontsize.find("pt");
2724                         if (i != string::npos)
2725                                 h_paperfontsize.erase(i);
2726                         // Now those known specifically to the class
2727                         vector<string> class_fsizes = getVectorFromString(tc.opt_fontsize(), "|");
2728                         string const fsize_format = tc.fontsizeformat();
2729                         for (auto const & fsize : class_fsizes) {
2730                                 string latexsize = subst(fsize_format, "$$s", fsize);
2731                                 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2732                                 if (it != opts.end()) {
2733                                         h_paperfontsize = fsize;
2734                                         opts.erase(it);
2735                                         break;
2736                                 }
2737                         }
2738
2739                         // The documentclass options are always parsed before the options
2740                         // of the babel call so that a language cannot overwrite the babel
2741                         // options.
2742                         handle_opt(opts, known_languages, h_language);
2743                         delete_opt(opts, known_languages);
2744
2745                         // math indentation
2746                         if ((it = find(opts.begin(), opts.end(), "fleqn"))
2747                                  != opts.end()) {
2748                                 h_is_mathindent = "1";
2749                                 opts.erase(it);
2750                         }
2751                         // formula numbering side
2752                         if ((it = find(opts.begin(), opts.end(), "leqno"))
2753                                  != opts.end()) {
2754                                 h_math_numbering_side = "left";
2755                                 opts.erase(it);
2756                         }
2757                         else if ((it = find(opts.begin(), opts.end(), "reqno"))
2758                                  != opts.end()) {
2759                                 h_math_numbering_side = "right";
2760                                 opts.erase(it);
2761                         }
2762
2763                         // paper orientation
2764                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2765                                 h_paperorientation = "landscape";
2766                                 opts.erase(it);
2767                         }
2768                         // paper sides
2769                         if ((it = find(opts.begin(), opts.end(), "oneside"))
2770                                  != opts.end()) {
2771                                 h_papersides = "1";
2772                                 opts.erase(it);
2773                         }
2774                         if ((it = find(opts.begin(), opts.end(), "twoside"))
2775                                  != opts.end()) {
2776                                 h_papersides = "2";
2777                                 opts.erase(it);
2778                         }
2779                         // paper columns
2780                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2781                                  != opts.end()) {
2782                                 h_papercolumns = "1";
2783                                 opts.erase(it);
2784                         }
2785                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2786                                  != opts.end()) {
2787                                 h_papercolumns = "2";
2788                                 opts.erase(it);
2789                         }
2790                         // paper sizes
2791                         // some size options are known by the document class, other sizes
2792                         // are handled by the \geometry command of the geometry package
2793                         vector<string> class_psizes = getVectorFromString(tc.opt_pagesize(), "|");
2794                         string const psize_format = tc.pagesizeformat();
2795                         for (auto const & psize : class_psizes) {
2796                                 string latexsize = subst(psize_format, "$$s", psize);
2797                                 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2798                                 if (it != opts.end()) {
2799                                         h_papersize = psize;
2800                                         opts.erase(it);
2801                                         break;
2802                                 }
2803                                 if (psize_format == "$$spaper")
2804                                         continue;
2805                                 // Also try with the default format since this is understood by
2806                                 // most classes
2807                                 latexsize = psize + "paper";
2808                                 it = find(opts.begin(), opts.end(), latexsize);
2809                                 if (it != opts.end()) {
2810                                         h_papersize = psize;
2811                                         opts.erase(it);
2812                                         break;
2813                                 }
2814                         }
2815                         // the remaining options
2816                         h_options = join(opts, ",");
2817                         continue;
2818                 }
2819
2820                 if (t.cs() == "DocumentMetadata") {
2821                         h_doc_metadata = trimSpaceAndEol(p.getArg('{', '}'));
2822                         continue;
2823                 }
2824
2825                 if (t.cs() == "usepackage") {
2826                         string const options = p.getArg('[', ']');
2827                         string const name = p.getArg('{', '}');
2828                         vector<string> vecnames;
2829                         split(name, vecnames, ',');
2830                         vector<string>::const_iterator it  = vecnames.begin();
2831                         vector<string>::const_iterator end = vecnames.end();
2832                         for (; it != end; ++it)
2833                                 handle_package(p, trimSpaceAndEol(*it), options,
2834                                                in_lyx_preamble, detectEncoding);
2835                         continue;
2836                 }
2837
2838                 if (t.cs() == "inputencoding") {
2839                         string const encoding = p.getArg('{','}');
2840                         Encoding const * const enc = encodings.fromLaTeXName(
2841                                 encoding, Encoding::inputenc, true);
2842                         if (!enc) {
2843                                 if (!detectEncoding)
2844                                         warning_message("Unknown encoding " + encoding + ". Ignoring.");
2845                         } else {
2846                                 if (!enc->unsafe())
2847                                         h_inputencoding = enc->name();
2848                                 p.setEncoding(enc->iconvName());
2849                         }
2850                         continue;
2851                 }
2852
2853                 if (t.cs() == "newenvironment") {
2854                         string const name = p.getArg('{', '}');
2855                         string const opt1 = p.getFullOpt();
2856                         string const opt2 = p.getFullOpt();
2857                         string const beg = p.verbatim_item();
2858                         string const end = p.verbatim_item();
2859                         if (!in_lyx_preamble) {
2860                                 h_preamble << "\\newenvironment{" << name
2861                                            << '}' << opt1 << opt2 << '{'
2862                                            << beg << "}{" << end << '}';
2863                         }
2864                         add_known_environment(name, opt1, !opt2.empty(),
2865                                               from_utf8(beg), from_utf8(end));
2866                         continue;
2867                 }
2868
2869                 if (t.cs() == "newtheorem") {
2870                         bool star = false;
2871                         if (p.next_token().character() == '*') {
2872                                 p.get_token();
2873                                 star = true;
2874                         }
2875                         string const name = p.getArg('{', '}');
2876                         string const opt1 = p.getFullOpt();
2877                         string const opt2 = p.getFullOpt();
2878                         string const body = p.verbatim_item();
2879                         string const opt3 = p.getFullOpt();
2880                         string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2881
2882                         string const complete = cmd + "{" + name + '}' +
2883                                           opt1 + opt2 + '{' + body + '}' + opt3;
2884
2885                         add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2886
2887                         if (!in_lyx_preamble)
2888                                 h_preamble << complete;
2889                         continue;
2890                 }
2891
2892                 if (t.cs() == "def") {
2893                         string name = p.get_token().cs();
2894                         // In fact, name may be more than the name:
2895                         // In the test case of bug 8116
2896                         // name == "csname SF@gobble@opt \endcsname".
2897                         // Therefore, we need to use asInput() instead of cs().
2898                         while (p.next_token().cat() != catBegin)
2899                                 name += p.get_token().asInput();
2900                         if (!in_lyx_preamble)
2901                                 h_preamble << "\\def\\" << name << '{'
2902                                            << p.verbatim_item() << "}";
2903                         continue;
2904                 }
2905
2906                 if (t.cs() == "newcolumntype") {
2907                         string const name = p.getArg('{', '}');
2908                         trimSpaceAndEol(name);
2909                         int nargs = 0;
2910                         string opts = p.getOpt();
2911                         if (!opts.empty()) {
2912                                 istringstream is(string(opts, 1));
2913                                 is >> nargs;
2914                         }
2915                         special_columns_[name[0]] = nargs;
2916                         h_preamble << "\\newcolumntype{" << name << "}";
2917                         if (nargs)
2918                                 h_preamble << "[" << nargs << "]";
2919                         h_preamble << "{" << p.verbatim_item() << "}";
2920                         continue;
2921                 }
2922
2923                 if (t.cs() == "setcounter") {
2924                         string const name = p.getArg('{', '}');
2925                         string const content = p.getArg('{', '}');
2926                         if (name == "secnumdepth")
2927                                 h_secnumdepth = content;
2928                         else if (name == "tocdepth")
2929                                 h_tocdepth = content;
2930                         else
2931                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2932                         continue;
2933                 }
2934
2935                 if (t.cs() == "setlength") {
2936                         string const name = p.verbatim_item();
2937                         string const content = p.verbatim_item();
2938                         // the paragraphs are only not indented when \parindent is set to zero
2939                         if (name == "\\parindent" && content != "")
2940                                 h_paragraph_indentation = translate_len(content);
2941                         else if (name == "\\parskip" && isPackageUsed("parskip")) {
2942                                 if (content == "\\smallskipamount")
2943                                         h_defskip = "smallskip";
2944                                 else if (content == "\\medskipamount")
2945                                         h_defskip = "medskip";
2946                                 else if (content == "\\bigskipamount")
2947                                         h_defskip = "bigskip";
2948                                 else if (content == "\\baselineskip")
2949                                         h_defskip = "fullline";
2950                                 else
2951                                         h_defskip = translate_len(content);
2952                         } else if (name == "\\mathindent") {
2953                                 h_mathindentation = translate_len(content);
2954                         } else
2955                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2956                         continue;
2957                 }
2958
2959                 if (t.cs() == "onehalfspacing") {
2960                         h_spacing = "onehalf";
2961                         continue;
2962                 }
2963
2964                 if (t.cs() == "doublespacing") {
2965                         h_spacing = "double";
2966                         continue;
2967                 }
2968
2969                 if (t.cs() == "setstretch") {
2970                         h_spacing = "other " + p.verbatim_item();
2971                         continue;
2972                 }
2973
2974                 if (t.cs() == "synctex") {
2975                         // the scheme is \synctex=value
2976                         // where value can only be "1" or "-1"
2977                         h_output_sync = "1";
2978                         // there can be any character behind the value (e.g. a linebreak or a '\'
2979                         // therefore we extract it char by char
2980                         p.get_token();
2981                         string value = p.get_token().asInput();
2982                         if (value == "-")
2983                                 value += p.get_token().asInput();
2984                         h_output_sync_macro = "\\synctex=" + value;
2985                         continue;
2986                 }
2987
2988                 if (t.cs() == "begin") {
2989                         string const name = p.getArg('{', '}');
2990                         if (name == "document")
2991                                 break;
2992                         h_preamble << "\\begin{" << name << "}";
2993                         continue;
2994                 }
2995
2996                 if (t.cs() == "geometry") {
2997                         vector<string> opts = split_options(p.getArg('{', '}'));
2998                         handle_geometry(opts);
2999                         continue;
3000                 }
3001
3002                 if (t.cs() == "definecolor") {
3003                         string const color = p.getArg('{', '}');
3004                         string const space = p.getArg('{', '}');
3005                         string const value = p.getArg('{', '}');
3006                         if (color == "document_fontcolor" && space == "rgb") {
3007                                 RGBColor c(RGBColorFromLaTeX(value));
3008                                 h_fontcolor = X11hexname(c);
3009                         } else if (color == "note_fontcolor" && space == "rgb") {
3010                                 RGBColor c(RGBColorFromLaTeX(value));
3011                                 h_notefontcolor = X11hexname(c);
3012                         } else if (color == "page_backgroundcolor" && space == "rgb") {
3013                                 RGBColor c(RGBColorFromLaTeX(value));
3014                                 h_backgroundcolor = X11hexname(c);
3015                         } else if (color == "shadecolor" && space == "rgb") {
3016                                 RGBColor c(RGBColorFromLaTeX(value));
3017                                 h_boxbgcolor = X11hexname(c);
3018                         } else {
3019                                 h_preamble << "\\definecolor{" << color
3020                                            << "}{" << space << "}{" << value
3021                                            << '}';
3022                         }
3023                         continue;
3024                 }
3025
3026                 if (t.cs() == "bibliographystyle") {
3027                         h_biblio_style = p.verbatim_item();
3028                         continue;
3029                 }
3030
3031                 if (t.cs() == "jurabibsetup") {
3032                         // FIXME p.getArg('{', '}') is most probably wrong (it
3033                         //       does not handle nested braces).
3034                         //       Use p.verbatim_item() instead.
3035                         vector<string> jurabibsetup =
3036                                 split_options(p.getArg('{', '}'));
3037                         // add jurabibsetup to the jurabib package options
3038                         add_package("jurabib", jurabibsetup);
3039                         if (!jurabibsetup.empty()) {
3040                                 h_preamble << "\\jurabibsetup{"
3041                                            << join(jurabibsetup, ",") << '}';
3042                         }
3043                         continue;
3044                 }
3045
3046                 if (t.cs() == "hypersetup") {
3047                         vector<string> hypersetup =
3048                                 split_options(p.verbatim_item());
3049                         // add hypersetup to the hyperref package options
3050                         handle_hyperref(hypersetup);
3051                         if (!hypersetup.empty()) {
3052                                 h_preamble << "\\hypersetup{"
3053                                            << join(hypersetup, ",") << '}';
3054                         }
3055                         continue;
3056                 }
3057
3058                 if (t.cs() == "includeonly") {
3059                         vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
3060                         for (auto & iofile : includeonlys) {
3061                                 string filename(normalize_filename(iofile));
3062                                 string const path = getMasterFilePath(true);
3063                                 // We want to preserve relative/absolute filenames,
3064                                 // therefore path is only used for testing
3065                                 if (!makeAbsPath(filename, path).exists()) {
3066                                         // The file extension is probably missing.
3067                                         // Now try to find it out.
3068                                         string const tex_name =
3069                                                 find_file(filename, path,
3070                                                           known_tex_extensions);
3071                                         if (!tex_name.empty())
3072                                                 filename = tex_name;
3073                                 }
3074                                 string outname;
3075                                 if (makeAbsPath(filename, path).exists())
3076                                         fix_child_filename(filename);
3077                                 else
3078                                         warning_message("Warning: Could not find included file '"
3079                                                         + filename + "'.");
3080                                 outname = changeExtension(filename, "lyx");
3081                                 h_includeonlys.push_back(outname);
3082                         }
3083                         continue;
3084                 }
3085
3086                 if (is_known(t.cs(), known_if_3arg_commands)) {
3087                         // prevent misparsing of \usepackage if it is used
3088                         // as an argument (see e.g. our own output of
3089                         // \@ifundefined above)
3090                         string const arg1 = p.verbatim_item();
3091                         string const arg2 = p.verbatim_item();
3092                         string const arg3 = p.verbatim_item();
3093                         // test case \@ifundefined{date}{}{\date{}}
3094                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
3095                             arg2.empty() && arg3 == "\\date{}") {
3096                                 h_suppress_date = "true";
3097                         // older tex2lyx versions did output
3098                         // \@ifundefined{definecolor}{\usepackage{color}}{}
3099                         } else if (t.cs() == "@ifundefined" &&
3100                                    arg1 == "definecolor" &&
3101                                    arg2 == "\\usepackage{color}" &&
3102                                    arg3.empty()) {
3103                                 if (!in_lyx_preamble)
3104                                         h_preamble << package_beg_sep
3105                                                    << "color"
3106                                                    << package_mid_sep
3107                                                    << "\\@ifundefined{definecolor}{color}{}"
3108                                                    << package_end_sep;
3109                         // test for case
3110                         //\@ifundefined{showcaptionsetup}{}{%
3111                         // \PassOptionsToPackage{caption=false}{subfig}}
3112                         // that LyX uses for subfloats
3113                         } else if (t.cs() == "@ifundefined" &&
3114                                    arg1 == "showcaptionsetup" && arg2.empty()
3115                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
3116                                 ; // do nothing
3117                         } else if (!in_lyx_preamble) {
3118                                 h_preamble << t.asInput()
3119                                            << '{' << arg1 << '}'
3120                                            << '{' << arg2 << '}'
3121                                            << '{' << arg3 << '}';
3122                         }
3123                         continue;
3124                 }
3125
3126                 if (is_known(t.cs(), known_if_commands)) {
3127                         // must not parse anything in conditional code, since
3128                         // LyX would output the parsed contents unconditionally
3129                         if (!in_lyx_preamble)
3130                                 h_preamble << t.asInput();
3131                         handle_if(p, in_lyx_preamble);
3132                         continue;
3133                 }
3134
3135                 if (!t.cs().empty() && !in_lyx_preamble) {
3136                         h_preamble << '\\' << t.cs();
3137                         continue;
3138                 }
3139         }
3140
3141         // set textclass if not yet done (snippets without \documentclass and forced class)
3142         if (!class_set)
3143                 setTextClass(h_textclass, tc);
3144
3145         // remove the whitespace
3146         p.skip_spaces();
3147
3148         if (h_papersides.empty()) {
3149                 ostringstream ss;
3150                 ss << tc.sides();
3151                 h_papersides = ss.str();
3152         }
3153
3154         // If the CJK package is used we cannot set the document language from
3155         // the babel options. Instead, we guess which language is used most
3156         // and set this one.
3157         default_language = h_language;
3158         if (is_full_document &&
3159             (auto_packages.find("CJK") != auto_packages.end() ||
3160              auto_packages.find("CJKutf8") != auto_packages.end())) {
3161                 p.pushPosition();
3162                 h_language = guessLanguage(p, default_language);
3163                 p.popPosition();
3164                 if (explicit_babel && h_language != default_language) {
3165                         // We set the document language to a CJK language,
3166                         // but babel is explicitly called in the user preamble
3167                         // without options. LyX will not add the default
3168                         // language to the document options if it is either
3169                         // english, or no text is set as default language.
3170                         // Therefore we need to add a language option explicitly.
3171                         // FIXME: It would be better to remove all babel calls
3172                         //        from the user preamble, but this is difficult
3173                         //        without re-introducing bug 7861.
3174                         if (h_options.empty())
3175                                 h_options = lyx2babel(default_language);
3176                         else
3177                                 h_options += ',' + lyx2babel(default_language);
3178                 }
3179         }
3180
3181         // Finally, set the quote style.
3182         // LyX knows the following quotes styles:
3183         // british, cjk, cjkangle, danish, english, french, german,
3184         // polish, russian, swedish, swiss, and hebrew
3185         // conversion list taken from
3186         // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
3187         // (quotes for kazakh are unknown)
3188         // british
3189         if (is_known(h_language, known_british_quotes_languages))
3190                 h_quotes_style = "british";
3191         // cjk
3192         else if (is_known(h_language, known_cjk_quotes_languages))
3193                 h_quotes_style = "cjk";
3194         // cjkangle
3195         else if (is_known(h_language, known_cjkangle_quotes_languages))
3196                 h_quotes_style = "cjkangle";
3197         // danish
3198         else if (is_known(h_language, known_danish_quotes_languages))
3199                 h_quotes_style = "danish";
3200         // french
3201         else if (is_known(h_language, known_french_quotes_languages))
3202                 h_quotes_style = "french";
3203         // german
3204         else if (is_known(h_language, known_german_quotes_languages))
3205                 h_quotes_style = "german";
3206         // polish
3207         else if (is_known(h_language, known_polish_quotes_languages))
3208                 h_quotes_style = "polish";
3209         // hungarian
3210         else if (is_known(h_language, known_hungarian_quotes_languages))
3211                 h_quotes_style = "hungarian";
3212         // russian
3213         else if (is_known(h_language, known_russian_quotes_languages))
3214                 h_quotes_style = "russian";
3215         // swedish
3216         else if (is_known(h_language, known_swedish_quotes_languages))
3217                 h_quotes_style = "swedish";
3218         // swiss
3219         else if (is_known(h_language, known_swiss_quotes_languages))
3220                 h_quotes_style = "swiss";
3221         // hebrew
3222         else if (is_known(h_language, known_hebrew_quotes_languages))
3223                 h_quotes_style = "hebrew";
3224         
3225         // english
3226         else if (is_known(h_language, known_english_quotes_languages))
3227                 h_quotes_style = "english";
3228 }
3229
3230
3231 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3232 {
3233         TeX2LyXDocClass dummy;
3234         parse(p, forceclass, true, dummy);
3235         if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3236                 return h_inputencoding;
3237         return "";
3238 }
3239
3240
3241 string babel2lyx(string const & language)
3242 {
3243         char const * const * where = is_known(language, known_languages);
3244         if (where)
3245                 return known_coded_languages[where - known_languages];
3246         return language;
3247 }
3248
3249
3250 string lyx2babel(string const & language)
3251 {
3252         char const * const * where = is_known(language, known_coded_languages);
3253         if (where)
3254                 return known_languages[where - known_coded_languages];
3255         return language;
3256 }
3257
3258
3259 string Preamble::polyglossia2lyx(string const & language)
3260 {
3261         char const * const * where = is_known(language, polyglossia_languages);
3262         if (where)
3263                 return coded_polyglossia_languages[where - polyglossia_languages];
3264         return language;
3265 }
3266
3267
3268 string rgbcolor2code(string const & name)
3269 {
3270         char const * const * where = is_known(name, known_basic_colors);
3271         if (where) {
3272                 // "red", "green" etc
3273                 return known_basic_color_codes[where - known_basic_colors];
3274         }
3275         // "255,0,0", "0,255,0" etc
3276         RGBColor c(RGBColorFromLaTeX(name));
3277         return X11hexname(c);
3278 }
3279
3280 // }])
3281
3282
3283 } // namespace lyx