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