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