]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
tex2lyx: cleanup font handling
[lyx.git] / src / tex2lyx / Preamble.cpp
1 /**
2  * \file Preamble.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  * \author Uwe Stöhr
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 // {[(
13
14 #include <config.h>
15
16 #include "Preamble.h"
17 #include "tex2lyx.h"
18
19 #include "Encoding.h"
20 #include "LayoutFile.h"
21 #include "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 pasing and
748         // skipping
749         vector<string> allopts = getVectorFromString(opts);
750         // this stored 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                 if (options.size() >= 1) {
1310                         if (scale_as_percentage(opts, h_font_tt_scale[0]))
1311                                 options.clear();
1312                 }
1313         }
1314
1315         if (name == "plex-mono") {
1316                 h_font_typewriter[0] = "IBMPlexMono";
1317                 for (auto const & opt : allopts) {
1318                         if (opt == "thin") {
1319                                 h_font_typewriter[0] = "IBMPlexMonoThin";
1320                                 continue;
1321                         }
1322                         if (opt == "extralight") {
1323                                 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1324                                 continue;
1325                         }
1326                         if (opt == "light") {
1327                                 h_font_typewriter[0] = "IBMPlexMonoLight";
1328                                 continue;
1329                         }
1330                         if (opt == "semibold"){
1331                                 h_font_typewriter[0] = "IBMPlexMonoSemibold";
1332                                 continue;
1333                         }
1334                         if (!xopts.empty())
1335                                 xopts += ", ";
1336                         xopts += opt;
1337                 }
1338                 if (!xopts.empty())
1339                         h_font_typewriter_opts = xopts;
1340                 options.clear();
1341         }
1342
1343         if (name == "noto-mono") {
1344                 h_font_typewriter[0] = "NotoMonoRegular";
1345                 for (auto const & opt : allopts) {
1346                         if (opt == "regular")
1347                                 continue;
1348                         if (!xopts.empty())
1349                                 xopts += ", ";
1350                         xopts += opt;
1351                 }
1352                 if (!xopts.empty())
1353                         h_font_typewriter_opts = xopts;
1354                 options.clear();
1355         }
1356
1357         if (name == "sourcecodepro") {
1358                 h_font_typewriter[0] = "ADOBESourceCodePro";
1359                 for (auto const & opt : allopts) {
1360                         if (opt == "osf") {
1361                                 h_font_typewriter_osf = "true";
1362                                 continue;
1363                         }
1364                         if (!xopts.empty())
1365                                 xopts += ", ";
1366                         xopts += opt;
1367                 }
1368                 if (!xopts.empty())
1369                         h_font_typewriter_opts = xopts;
1370                 options.clear();
1371         }
1372
1373         //
1374         // math fonts
1375         //
1376
1377         // By default, we use the package name as LyX font name,
1378         // so this only needs to be reset if these names differ.
1379         if (is_known(name, known_math_font_packages))
1380                 h_font_math[0] = name;
1381
1382         if (name == "newtxmath") {
1383                 if (opts.empty())
1384                         h_font_math[0] = "newtxmath";
1385                 else if (opts == "garamondx")
1386                         h_font_math[0] = "garamondx-ntxm";
1387                 else if (opts == "libertine")
1388                         h_font_math[0] = "libertine-ntxm";
1389                 else if (opts == "minion")
1390                         h_font_math[0] = "minion-ntxm";
1391                 else if (opts == "cochineal")
1392                         h_font_math[0] = "cochineal-ntxm";
1393         }
1394
1395         if (name == "iwona")
1396                 if (opts == "math")
1397                         h_font_math[0] = "iwona-math";
1398
1399         if (name == "kurier")
1400                 if (opts == "math")
1401                         h_font_math[0] = "kurier-math";
1402
1403         // after the detection and handling of special cases, we can remove the
1404         // fonts, otherwise they would appear in the preamble, see bug #7856
1405         if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1406                 ||      is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1407                 ;
1408         //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1409         else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1410                  name == "esint" || name == "mhchem" || name == "mathdots" ||
1411                  name == "mathtools" || name == "stackrel" ||
1412                  name == "stmaryrd" || name == "undertilde") {
1413                 h_use_packages[name] = "2";
1414                 registerAutomaticallyLoadedPackage(name);
1415         }
1416
1417         else if (name == "babel") {
1418                 h_language_package = "default";
1419                 // One might think we would have to do nothing if babel is loaded
1420                 // without any options to prevent pollution of the preamble with this
1421                 // babel call in every roundtrip.
1422                 // But the user could have defined babel-specific things afterwards. So
1423                 // we need to keep it in the preamble to prevent cases like bug #7861.
1424                 if (!opts.empty()) {
1425                         // check if more than one option was used - used later for inputenc
1426                         if (options.begin() != options.end() - 1)
1427                                 one_language = false;
1428                         // babel takes the last language of the option of its \usepackage
1429                         // call as document language. If there is no such language option, the
1430                         // last language in the documentclass options is used.
1431                         handle_opt(options, known_languages, h_language);
1432                         // translate the babel name to a LyX name
1433                         h_language = babel2lyx(h_language);
1434                         if (h_language == "japanese") {
1435                                 // For Japanese, the encoding isn't indicated in the source
1436                                 // file, and there's really not much we can do. We could
1437                                 // 1) offer a list of possible encodings to choose from, or
1438                                 // 2) determine the encoding of the file by inspecting it.
1439                                 // For the time being, we leave the encoding alone so that
1440                                 // we don't get iconv errors when making a wrong guess, and
1441                                 // we will output a note at the top of the document
1442                                 // explaining what to do.
1443                                 Encoding const * const enc = encodings.fromIconvName(
1444                                         p.getEncoding(), Encoding::japanese, false);
1445                                 if (enc)
1446                                         h_inputencoding = enc->name();
1447                                 is_nonCJKJapanese = true;
1448                                 // in this case babel can be removed from the preamble
1449                                 registerAutomaticallyLoadedPackage("babel");
1450                         } else {
1451                                 // If babel is called with options, LyX puts them by default into the
1452                                 // document class options. This works for most languages, except
1453                                 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1454                                 // perhaps in future others.
1455                                 // Therefore keep the babel call as it is as the user might have
1456                                 // reasons for it.
1457                                 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1458                                 if (!contains(h_preamble.str(), babelcall))
1459                                         h_preamble << babelcall;
1460                         }
1461                         delete_opt(options, known_languages);
1462                 } else {
1463                         if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1464                                 h_preamble << "\\usepackage{babel}\n";
1465                         explicit_babel = true;
1466                 }
1467         }
1468
1469         else if (name == "polyglossia") {
1470                 h_language_package = "default";
1471                 h_default_output_format = "pdf4";
1472                 h_use_non_tex_fonts = true;
1473                 xetex = true;
1474                 registerAutomaticallyLoadedPackage("xunicode");
1475                 if (h_inputencoding == "auto-legacy")
1476                         p.setEncoding("UTF-8");
1477         }
1478
1479         else if (name == "CJK") {
1480                 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1481                 // and this would not be correct for CJK
1482                 if (h_inputencoding == "auto-legacy-plain")
1483                         h_inputencoding = "auto-legacy";
1484                 registerAutomaticallyLoadedPackage("CJK");
1485         }
1486
1487         else if (name == "CJKutf8") {
1488                 h_inputencoding = "utf8-cjk";
1489                 p.setEncoding("UTF-8");
1490                 registerAutomaticallyLoadedPackage("CJKutf8");
1491         }
1492
1493         else if (name == "fontenc") {
1494                 h_fontencoding = getStringFromVector(options, ",");
1495                 options.clear();
1496         }
1497
1498         else if (name == "inputenc" || name == "luainputenc") {
1499                 // h_inputencoding is only set when there is not more than one
1500                 // inputenc option because otherwise h_inputencoding must be
1501                 // set to "auto-legacy" (the default encodings of the document's languages)
1502                 // Therefore check that exactly one option is passed to inputenc.
1503                 // It is also only set when there is not more than one babel
1504                 // language option.
1505                 if (!options.empty()) {
1506                         string const encoding = options.back();
1507                         Encoding const * const enc = encodings.fromLaTeXName(
1508                                 encoding, Encoding::inputenc, true);
1509                         if (!enc) {
1510                                 if (!detectEncoding)
1511                                         cerr << "Unknown encoding " << encoding
1512                                              << ". Ignoring." << std::endl;
1513                         } else {
1514                                 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1515                                         h_inputencoding = enc->name();
1516                                 p.setEncoding(enc->iconvName());
1517                         }
1518                         options.clear();
1519                 }
1520         }
1521
1522         else if (name == "srcltx") {
1523                 h_output_sync = "1";
1524                 if (!opts.empty()) {
1525                         h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1526                         options.clear();
1527                 } else
1528                         h_output_sync_macro = "\\usepackage{srcltx}";
1529         }
1530
1531         else if (is_known(name, known_old_language_packages)) {
1532                 // known language packages from the times before babel
1533                 // if they are found and not also babel, they will be used as
1534                 // custom language package
1535                 h_language_package = "\\usepackage{" + name + "}";
1536         }
1537
1538         else if (name == "lyxskak") {
1539                 // ignore this and its options
1540                 const char * const o[] = {"ps", "mover", 0};
1541                 delete_opt(options, o);
1542         }
1543
1544         else if (is_known(name, known_lyx_packages) && options.empty()) {
1545                 if (name == "splitidx")
1546                         h_use_indices = "true";
1547                 else if (name == "minted")
1548                         h_use_minted = true;
1549                 else if (name == "refstyle")
1550                         h_use_refstyle = true;
1551                 else if (name == "prettyref")
1552                         h_use_refstyle = false;
1553                 if (!in_lyx_preamble) {
1554                         h_preamble << package_beg_sep << name
1555                                    << package_mid_sep << "\\usepackage{"
1556                                    << name << '}';
1557                         if (p.next_token().cat() == catNewline ||
1558                             (p.next_token().cat() == catSpace &&
1559                              p.next_next_token().cat() == catNewline))
1560                                 h_preamble << '\n';
1561                         h_preamble << package_end_sep;
1562                 }
1563         }
1564
1565         else if (name == "geometry")
1566                 handle_geometry(options);
1567
1568         else if (name == "subfig")
1569                 ; // ignore this FIXME: Use the package separator mechanism instead
1570
1571         else if (char const * const * where = is_known(name, known_languages))
1572                 h_language = known_coded_languages[where - known_languages];
1573
1574         else if (name == "natbib") {
1575                 h_biblio_style = "plainnat";
1576                 h_cite_engine = "natbib";
1577                 h_cite_engine_type = "authoryear";
1578                 vector<string>::iterator it =
1579                         find(options.begin(), options.end(), "authoryear");
1580                 if (it != options.end())
1581                         options.erase(it);
1582                 else {
1583                         it = find(options.begin(), options.end(), "numbers");
1584                         if (it != options.end()) {
1585                                 h_cite_engine_type = "numerical";
1586                                 options.erase(it);
1587                         }
1588                 }
1589                 if (!options.empty())
1590                         h_biblio_options = join(options, ",");
1591         }
1592
1593         else if (name == "biblatex") {
1594                 h_biblio_style = "plainnat";
1595                 h_cite_engine = "biblatex";
1596                 h_cite_engine_type = "authoryear";
1597                 string opt;
1598                 vector<string>::iterator it =
1599                         find(options.begin(), options.end(), "natbib");
1600                 if (it != options.end()) {
1601                         options.erase(it);
1602                         h_cite_engine = "biblatex-natbib";
1603                 } else {
1604                         opt = process_keyval_opt(options, "natbib");
1605                         if (opt == "true")
1606                                 h_cite_engine = "biblatex-natbib";
1607                 }
1608                 opt = process_keyval_opt(options, "style");
1609                 if (!opt.empty()) {
1610                         h_biblatex_citestyle = opt;
1611                         h_biblatex_bibstyle = opt;
1612                 } else {
1613                         opt = process_keyval_opt(options, "citestyle");
1614                         if (!opt.empty())
1615                                 h_biblatex_citestyle = opt;
1616                         opt = process_keyval_opt(options, "bibstyle");
1617                         if (!opt.empty())
1618                                 h_biblatex_bibstyle = opt;
1619                 }
1620                 opt = process_keyval_opt(options, "refsection");
1621                 if (!opt.empty()) {
1622                         if (opt == "none" || opt == "part"
1623                             || opt == "chapter" || opt == "section"
1624                             || opt == "subsection")
1625                                 h_multibib = opt;
1626                         else
1627                                 cerr << "Ignoring unkown refesection value '"
1628                                      << opt << "'.";
1629                 }
1630                 opt = process_keyval_opt(options, "bibencoding");
1631                 if (!opt.empty())
1632                         bibencoding = opt;
1633                 if (!options.empty()) {
1634                         h_biblio_options = join(options, ",");
1635                         options.clear();
1636                 }
1637         }
1638
1639         else if (name == "jurabib") {
1640                 h_biblio_style = "jurabib";
1641                 h_cite_engine = "jurabib";
1642                 h_cite_engine_type = "authoryear";
1643                 if (!options.empty())
1644                         h_biblio_options = join(options, ",");
1645         }
1646
1647         else if (name == "bibtopic")
1648                 h_use_bibtopic = "true";
1649
1650         else if (name == "chapterbib")
1651                 h_multibib = "child";
1652
1653         else if (name == "hyperref")
1654                 handle_hyperref(options);
1655
1656         else if (name == "algorithm2e") {
1657                 // Load "algorithm2e" module
1658                 addModule("algorithm2e");
1659                 // Add the package options to the global document options
1660                 if (!options.empty()) {
1661                         if (h_options.empty())
1662                                 h_options = join(options, ",");
1663                         else
1664                                 h_options += ',' + join(options, ",");
1665                 }
1666         }
1667         else if (name == "microtype") {
1668                 //we internally support only microtype without params
1669                 if (options.empty())
1670                         h_use_microtype = "true";
1671                 else
1672                         h_preamble << "\\usepackage[" << opts << "]{microtype}";
1673         }
1674
1675         else if (name == "lineno") {
1676                 h_use_lineno = "true";
1677                 if (!options.empty()) {
1678                         h_lineno_options = join(options, ",");
1679                         options.clear();
1680                 }
1681         }
1682
1683         else if (!in_lyx_preamble) {
1684                 if (options.empty())
1685                         h_preamble << "\\usepackage{" << name << '}';
1686                 else {
1687                         h_preamble << "\\usepackage[" << opts << "]{"
1688                                    << name << '}';
1689                         options.clear();
1690                 }
1691                 if (p.next_token().cat() == catNewline ||
1692                     (p.next_token().cat() == catSpace &&
1693                      p.next_next_token().cat() == catNewline))
1694                         h_preamble << '\n';
1695         }
1696
1697         // We need to do something with the options...
1698         if (!options.empty() && !detectEncoding)
1699                 cerr << "Ignoring options '" << join(options, ",")
1700                      << "' of package " << name << '.' << endl;
1701
1702         // remove the whitespace
1703         p.skip_spaces();
1704 }
1705
1706
1707 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1708 {
1709         while (p.good()) {
1710                 Token t = p.get_token();
1711                 if (t.cat() == catEscape &&
1712                     is_known(t.cs(), known_if_commands))
1713                         handle_if(p, in_lyx_preamble);
1714                 else {
1715                         if (!in_lyx_preamble)
1716                                 h_preamble << t.asInput();
1717                         if (t.cat() == catEscape && t.cs() == "fi")
1718                                 return;
1719                 }
1720         }
1721 }
1722
1723
1724 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1725 {
1726         if (contains(h_float_placement, "H"))
1727                 registerAutomaticallyLoadedPackage("float");
1728         if (h_spacing != "single" && h_spacing != "default")
1729                 registerAutomaticallyLoadedPackage("setspace");
1730         if (h_use_packages["amsmath"] == "2") {
1731                 // amsbsy and amstext are already provided by amsmath
1732                 registerAutomaticallyLoadedPackage("amsbsy");
1733                 registerAutomaticallyLoadedPackage("amstext");
1734         }
1735
1736         // output the LyX file settings
1737         // Important: Keep the version formatting in sync with LyX and
1738         //            lyx2lyx (bug 7951)
1739         string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1740         os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1741            << lyx_version_minor << '\n'
1742            << "\\lyxformat " << LYX_FORMAT << '\n'
1743            << "\\begin_document\n"
1744            << "\\begin_header\n"
1745            << "\\save_transient_properties " << h_save_transient_properties << "\n"
1746            << "\\origin " << origin << "\n"
1747            << "\\textclass " << h_textclass << "\n";
1748         string const raw = subdoc ? empty_string() : h_preamble.str();
1749         if (!raw.empty()) {
1750                 os << "\\begin_preamble\n";
1751                 for (string::size_type i = 0; i < raw.size(); ++i) {
1752                         if (raw[i] == package_beg_sep) {
1753                                 // Here follows some package loading code that
1754                                 // must be skipped if the package is loaded
1755                                 // automatically.
1756                                 string::size_type j = raw.find(package_mid_sep, i);
1757                                 if (j == string::npos)
1758                                         return false;
1759                                 string::size_type k = raw.find(package_end_sep, j);
1760                                 if (k == string::npos)
1761                                         return false;
1762                                 string const package = raw.substr(i + 1, j - i - 1);
1763                                 string const replacement = raw.substr(j + 1, k - j - 1);
1764                                 if (auto_packages.find(package) == auto_packages.end())
1765                                         os << replacement;
1766                                 i = k;
1767                         } else
1768                                 os.put(raw[i]);
1769                 }
1770                 os << "\n\\end_preamble\n";
1771         }
1772         if (!h_options.empty())
1773                 os << "\\options " << h_options << "\n";
1774         os << "\\use_default_options " << h_use_default_options << "\n";
1775         if (!used_modules.empty()) {
1776                 os << "\\begin_modules\n";
1777                 vector<string>::const_iterator const end = used_modules.end();
1778                 vector<string>::const_iterator it = used_modules.begin();
1779                 for (; it != end; ++it)
1780                         os << *it << '\n';
1781                 os << "\\end_modules\n";
1782         }
1783         if (!h_includeonlys.empty()) {
1784                 os << "\\begin_includeonly\n";
1785                 for (auto const & iofile : h_includeonlys)
1786                         os << iofile << '\n';
1787                 os << "\\end_includeonly\n";
1788         }
1789         os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1790            << "\\language " << h_language << "\n"
1791            << "\\language_package " << h_language_package << "\n"
1792            << "\\inputencoding " << h_inputencoding << "\n"
1793            << "\\fontencoding " << h_fontencoding << "\n"
1794            << "\\font_roman \"" << h_font_roman[0]
1795            << "\" \"" << h_font_roman[1] << "\"\n"
1796            << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1797            << "\\font_typewriter \"" << h_font_typewriter[0]
1798            << "\" \"" << h_font_typewriter[1] << "\"\n"
1799            << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1800            << "\\font_default_family " << h_font_default_family << "\n"
1801            << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1802            << "\\font_sc " << h_font_sc << "\n"
1803            << "\\font_roman_osf " << h_font_roman_osf << "\n"
1804            << "\\font_sans_osf " << h_font_sans_osf << "\n"
1805            << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
1806         if (!h_font_roman_opts.empty())
1807                 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1808         os << "\\font_sf_scale " << h_font_sf_scale[0]
1809            << ' ' << h_font_sf_scale[1] << '\n';
1810         if (!h_font_sans_opts.empty())
1811                 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1812         os << "\\font_tt_scale " << h_font_tt_scale[0]
1813            << ' ' << h_font_tt_scale[1] << '\n';
1814         if (!h_font_cjk.empty())
1815                 os << "\\font_cjk " << h_font_cjk << '\n';
1816         if (!h_font_typewriter_opts.empty())
1817                 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1818         os << "\\use_microtype " << h_use_microtype << '\n'
1819            << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1820            << "\\graphics " << h_graphics << '\n'
1821            << "\\default_output_format " << h_default_output_format << "\n"
1822            << "\\output_sync " << h_output_sync << "\n";
1823         if (h_output_sync == "1")
1824                 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1825         os << "\\bibtex_command " << h_bibtex_command << "\n"
1826            << "\\index_command " << h_index_command << "\n";
1827         if (!h_float_placement.empty())
1828                 os << "\\float_placement " << h_float_placement << "\n";
1829         os << "\\paperfontsize " << h_paperfontsize << "\n"
1830            << "\\spacing " << h_spacing << "\n"
1831            << "\\use_hyperref " << h_use_hyperref << '\n';
1832         if (h_use_hyperref == "true") {
1833                 if (!h_pdf_title.empty())
1834                         os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1835                 if (!h_pdf_author.empty())
1836                         os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1837                 if (!h_pdf_subject.empty())
1838                         os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1839                 if (!h_pdf_keywords.empty())
1840                         os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1841                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1842                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1843                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1844                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1845                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1846                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1847                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1848                       "\\pdf_backref " << h_pdf_backref << "\n"
1849                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1850                 if (!h_pdf_pagemode.empty())
1851                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1852                 if (!h_pdf_quoted_options.empty())
1853                         os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1854         }
1855         os << "\\papersize " << h_papersize << "\n"
1856            << "\\use_geometry " << h_use_geometry << '\n';
1857         for (map<string, string>::const_iterator it = h_use_packages.begin();
1858              it != h_use_packages.end(); ++it)
1859                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1860         os << "\\cite_engine " << h_cite_engine << '\n'
1861            << "\\cite_engine_type " << h_cite_engine_type << '\n'
1862            << "\\biblio_style " << h_biblio_style << "\n"
1863            << "\\use_bibtopic " << h_use_bibtopic << "\n";
1864         if (!h_biblio_options.empty())
1865                 os << "\\biblio_options " << h_biblio_options << "\n";
1866         if (!h_biblatex_bibstyle.empty())
1867                 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1868         if (!h_biblatex_citestyle.empty())
1869                 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1870         if (!h_multibib.empty())
1871                 os << "\\multibib " << h_multibib << "\n";
1872         os << "\\use_indices " << h_use_indices << "\n"
1873            << "\\paperorientation " << h_paperorientation << '\n'
1874            << "\\suppress_date " << h_suppress_date << '\n'
1875            << "\\justification " << h_justification << '\n'
1876            << "\\use_refstyle " << h_use_refstyle << '\n'
1877            << "\\use_minted " << h_use_minted << '\n'
1878            << "\\use_lineno " << h_use_lineno << '\n';
1879         if (!h_lineno_options.empty())
1880                 os << "\\lineno_options " << h_lineno_options << '\n';
1881         if (!h_fontcolor.empty())
1882                 os << "\\fontcolor " << h_fontcolor << '\n';
1883         if (!h_notefontcolor.empty())
1884                 os << "\\notefontcolor " << h_notefontcolor << '\n';
1885         if (!h_backgroundcolor.empty())
1886                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1887         if (!h_boxbgcolor.empty())
1888                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1889         if (index_number != 0)
1890                 for (int i = 0; i < index_number; i++) {
1891                         os << "\\index " << h_index[i] << '\n'
1892                            << "\\shortcut " << h_shortcut[i] << '\n'
1893                            << "\\color " << h_color << '\n'
1894                            << "\\end_index\n";
1895                 }
1896         else {
1897                 os << "\\index " << h_index[0] << '\n'
1898                    << "\\shortcut " << h_shortcut[0] << '\n'
1899                    << "\\color " << h_color << '\n'
1900                    << "\\end_index\n";
1901         }
1902         os << h_margins
1903            << "\\secnumdepth " << h_secnumdepth << "\n"
1904            << "\\tocdepth " << h_tocdepth << "\n"
1905            << "\\paragraph_separation " << h_paragraph_separation << "\n";
1906         if (h_paragraph_separation == "skip")
1907                 os << "\\defskip " << h_defskip << "\n";
1908         else
1909                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1910         os << "\\is_math_indent " << h_is_mathindent << "\n";
1911         if (!h_mathindentation.empty())
1912                 os << "\\math_indentation " << h_mathindentation << "\n";
1913         os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1914         os << "\\quotes_style " << h_quotes_style << "\n"
1915            << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1916            << "\\papercolumns " << h_papercolumns << "\n"
1917            << "\\papersides " << h_papersides << "\n"
1918            << "\\paperpagestyle " << h_paperpagestyle << "\n";
1919         if (!h_listings_params.empty())
1920                 os << "\\listings_params " << h_listings_params << "\n";
1921         os << "\\tracking_changes " << h_tracking_changes << "\n"
1922            << "\\output_changes " << h_output_changes << "\n"
1923            << "\\html_math_output " << h_html_math_output << "\n"
1924            << "\\html_css_as_file " << h_html_css_as_file << "\n"
1925            << "\\html_be_strict " << h_html_be_strict << "\n"
1926            << authors_
1927            << "\\end_header\n\n"
1928            << "\\begin_body\n";
1929         return true;
1930 }
1931
1932
1933 void Preamble::parse(Parser & p, string const & forceclass,
1934                      TeX2LyXDocClass & tc)
1935 {
1936         // initialize fixed types
1937         special_columns_['D'] = 3;
1938         parse(p, forceclass, false, tc);
1939 }
1940
1941
1942 void Preamble::parse(Parser & p, string const & forceclass,
1943                      bool detectEncoding, TeX2LyXDocClass & tc)
1944 {
1945         bool is_full_document = false;
1946         bool is_lyx_file = false;
1947         bool in_lyx_preamble = false;
1948
1949         // determine whether this is a full document or a fragment for inclusion
1950         while (p.good()) {
1951                 Token const & t = p.get_token();
1952
1953                 if (t.cat() == catEscape && t.cs() == "documentclass") {
1954                         is_full_document = true;
1955                         break;
1956                 }
1957         }
1958         p.reset();
1959
1960         if (detectEncoding && !is_full_document)
1961                 return;
1962
1963         while (is_full_document && p.good()) {
1964                 if (detectEncoding && h_inputencoding != "auto-legacy" &&
1965                     h_inputencoding != "auto-legacy-plain")
1966                         return;
1967
1968                 Token const & t = p.get_token();
1969
1970 #ifdef FILEDEBUG
1971                 if (!detectEncoding)
1972                         cerr << "t: " << t << '\n';
1973 #endif
1974
1975                 //
1976                 // cat codes
1977                 //
1978                 if (!in_lyx_preamble &&
1979                     (t.cat() == catLetter ||
1980                      t.cat() == catSuper ||
1981                      t.cat() == catSub ||
1982                      t.cat() == catOther ||
1983                      t.cat() == catMath ||
1984                      t.cat() == catActive ||
1985                      t.cat() == catBegin ||
1986                      t.cat() == catEnd ||
1987                      t.cat() == catAlign ||
1988                      t.cat() == catParameter)) {
1989                         h_preamble << t.cs();
1990                         continue;
1991                 }
1992
1993                 if (!in_lyx_preamble &&
1994                     (t.cat() == catSpace || t.cat() == catNewline)) {
1995                         h_preamble << t.asInput();
1996                         continue;
1997                 }
1998
1999                 if (t.cat() == catComment) {
2000                         static regex const islyxfile("%% LyX .* created this file");
2001                         static regex const usercommands("User specified LaTeX commands");
2002
2003                         string const comment = t.asInput();
2004
2005                         // magically switch encoding default if it looks like XeLaTeX
2006                         static string const magicXeLaTeX =
2007                                 "% This document must be compiled with XeLaTeX ";
2008                         if (comment.size() > magicXeLaTeX.size()
2009                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2010                                   && h_inputencoding == "auto-legacy") {
2011                                 if (!detectEncoding)
2012                                         cerr << "XeLaTeX comment found, switching to UTF8\n";
2013                                 h_inputencoding = "utf8";
2014                         }
2015                         smatch sub;
2016                         if (regex_search(comment, sub, islyxfile)) {
2017                                 is_lyx_file = true;
2018                                 in_lyx_preamble = true;
2019                         } else if (is_lyx_file
2020                                    && regex_search(comment, sub, usercommands))
2021                                 in_lyx_preamble = false;
2022                         else if (!in_lyx_preamble)
2023                                 h_preamble << t.asInput();
2024                         continue;
2025                 }
2026
2027                 if (t.cs() == "PassOptionsToPackage") {
2028                         string const poptions = p.getArg('{', '}');
2029                         string const package = p.verbatim_item();
2030                         extra_package_options_.insert(make_pair(package, poptions));
2031                         continue;
2032                 }
2033
2034                 if (t.cs() == "pagestyle") {
2035                         h_paperpagestyle = p.verbatim_item();
2036                         continue;
2037                 }
2038
2039                 if (t.cs() == "setdefaultlanguage") {
2040                         xetex = true;
2041                         // We don't yet care about non-language variant options
2042                         // because LyX doesn't support this yet, see bug #8214
2043                         if (p.hasOpt()) {
2044                                 string langopts = p.getOpt();
2045                                 // check if the option contains a variant, if yes, extract it
2046                                 string::size_type pos_var = langopts.find("variant");
2047                                 string::size_type i = langopts.find(',', pos_var);
2048                                 string::size_type k = langopts.find('=', pos_var);
2049                                 if (pos_var != string::npos){
2050                                         string variant;
2051                                         if (i == string::npos)
2052                                                 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2053                                         else
2054                                                 variant = langopts.substr(k + 1, i - k - 1);
2055                                         h_language = variant;
2056                                 }
2057                                 p.verbatim_item();
2058                         } else
2059                                 h_language = p.verbatim_item();
2060                         //finally translate the poyglossia name to a LyX name
2061                         h_language = polyglossia2lyx(h_language);
2062                         continue;
2063                 }
2064
2065                 if (t.cs() == "setotherlanguage") {
2066                         // We don't yet care about the option because LyX doesn't
2067                         // support this yet, see bug #8214
2068                         p.hasOpt() ? p.getOpt() : string();
2069                         p.verbatim_item();
2070                         continue;
2071                 }
2072
2073                 if (t.cs() == "setmainfont") {
2074                         string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2075                         h_font_roman[1] = p.getArg('{', '}');
2076                         if (!fontopts.empty()) {
2077                                 vector<string> opts = getVectorFromString(fontopts);
2078                                 fontopts.clear();
2079                                 for (auto const & opt : opts) {
2080                                         if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2081                                                 // ignore
2082                                                 continue;
2083                                         if (!fontopts.empty())
2084                                                 fontopts += ", ";
2085                                         fontopts += opt;
2086                                 }
2087                                 h_font_roman_opts = fontopts;
2088                         }
2089                         continue;
2090                 }
2091
2092                 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2093                         // LyX currently only supports the scale option
2094                         string scale, fontopts;
2095                         if (p.hasOpt()) {
2096                                 fontopts = p.getArg('[', ']');
2097                                 if (!fontopts.empty()) {
2098                                         vector<string> opts = getVectorFromString(fontopts);
2099                                         fontopts.clear();
2100                                         for (auto const & opt : opts) {
2101                                                 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2102                                                         // ignore
2103                                                         continue;
2104                                                 if (prefixIs(opt, "Scale=")) {
2105                                                         scale_as_percentage(opt, scale);
2106                                                         continue;
2107                                                 }
2108                                                 if (!fontopts.empty())
2109                                                         fontopts += ", ";
2110                                                 fontopts += opt;
2111                                         }
2112                                 }
2113                         }
2114                         if (t.cs() == "setsansfont") {
2115                                 if (!scale.empty())
2116                                         h_font_sf_scale[1] = scale;
2117                                 h_font_sans[1] = p.getArg('{', '}');
2118                                 if (!fontopts.empty())
2119                                         h_font_sans_opts = fontopts;
2120                         } else {
2121                                 if (!scale.empty())
2122                                         h_font_tt_scale[1] = scale;
2123                                 h_font_typewriter[1] = p.getArg('{', '}');
2124                                 if (!fontopts.empty())
2125                                         h_font_typewriter_opts = fontopts;
2126                         }
2127                         continue;
2128                 }
2129
2130                 if (t.cs() == "babelfont") {
2131                         xetex = true;
2132                         h_use_non_tex_fonts = true;
2133                         h_language_package = "babel";
2134                         if (h_inputencoding == "auto-legacy")
2135                         p.setEncoding("UTF-8");
2136                         // we don't care about the lang option
2137                         string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2138                         string const family = p.getArg('{', '}');
2139                         string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2140                         string const fontname = p.getArg('{', '}');
2141                         if (lang.empty() && family == "rm") {
2142                                 h_font_roman[1] = fontname;
2143                                 if (!fontopts.empty()) {
2144                                         vector<string> opts = getVectorFromString(fontopts);
2145                                         fontopts.clear();
2146                                         for (auto const & opt : opts) {
2147                                                 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2148                                                         // ignore
2149                                                         continue;
2150                                                 if (!fontopts.empty())
2151                                                         fontopts += ", ";
2152                                                 fontopts += opt;
2153                                         }
2154                                         h_font_roman_opts = fontopts;
2155                                 }
2156                                 continue;
2157                         } else if (lang.empty() && (family == "sf" || family == "tt")) {
2158                                 // LyX currently only supports the scale option
2159                                 string scale;
2160                                 if (!fontopts.empty()) {
2161                                         vector<string> opts = getVectorFromString(fontopts);
2162                                         fontopts.clear();
2163                                         for (auto const & opt : opts) {
2164                                                 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2165                                                         // ignore
2166                                                         continue;
2167                                                 if (prefixIs(opt, "Scale=")) {
2168                                                         scale_as_percentage(opt, scale);
2169                                                         continue;
2170                                                 }
2171                                                 if (!fontopts.empty())
2172                                                         fontopts += ", ";
2173                                                 fontopts += opt;
2174                                         }
2175                                 }
2176                                 if (family == "sf") {
2177                                         if (!scale.empty())
2178                                                 h_font_sf_scale[1] = scale;
2179                                         h_font_sans[1] = fontname;
2180                                         if (!fontopts.empty())
2181                                                 h_font_sans_opts = fontopts;
2182                                 } else {
2183                                         if (!scale.empty())
2184                                                 h_font_tt_scale[1] = scale;
2185                                         h_font_typewriter[1] = fontname;
2186                                         if (!fontopts.empty())
2187                                                 h_font_typewriter_opts = fontopts;
2188                                 }
2189                                 continue;
2190                         } else {
2191                                 // not rm, sf or tt or lang specific
2192                                 h_preamble << '\\' << t.cs();
2193                                 if (!lang.empty())
2194                                         h_preamble << '[' << lang << ']';
2195                                 h_preamble << '{' << family << '}';
2196                                 if (!fontopts.empty())
2197                                         h_preamble << '[' << fontopts << ']';
2198                                 h_preamble << '{' << fontname << '}' << '\n';
2199                                 continue;
2200                         }
2201                 }
2202
2203                 if (t.cs() == "date") {
2204                         string argument = p.getArg('{', '}');
2205                         if (argument.empty())
2206                                 h_suppress_date = "true";
2207                         else
2208                                 h_preamble << t.asInput() << '{' << argument << '}';
2209                         continue;
2210                 }
2211
2212                 if (t.cs() == "color") {
2213                         string const space =
2214                                 (p.hasOpt() ? p.getOpt() : string());
2215                         string argument = p.getArg('{', '}');
2216                         // check the case that a standard color is used
2217                         if (space.empty() && is_known(argument, known_basic_colors)) {
2218                                 h_fontcolor = rgbcolor2code(argument);
2219                                 registerAutomaticallyLoadedPackage("color");
2220                         } else if (space.empty() && argument == "document_fontcolor")
2221                                 registerAutomaticallyLoadedPackage("color");
2222                         // check the case that LyX's document_fontcolor is defined
2223                         // but not used for \color
2224                         else {
2225                                 h_preamble << t.asInput();
2226                                 if (!space.empty())
2227                                         h_preamble << space;
2228                                 h_preamble << '{' << argument << '}';
2229                                 // the color might already be set because \definecolor
2230                                 // is parsed before this
2231                                 h_fontcolor = "";
2232                         }
2233                         continue;
2234                 }
2235
2236                 if (t.cs() == "pagecolor") {
2237                         string argument = p.getArg('{', '}');
2238                         // check the case that a standard color is used
2239                         if (is_known(argument, known_basic_colors)) {
2240                                 h_backgroundcolor = rgbcolor2code(argument);
2241                         } else if (argument == "page_backgroundcolor")
2242                                 registerAutomaticallyLoadedPackage("color");
2243                         // check the case that LyX's page_backgroundcolor is defined
2244                         // but not used for \pagecolor
2245                         else {
2246                                 h_preamble << t.asInput() << '{' << argument << '}';
2247                                 // the color might already be set because \definecolor
2248                                 // is parsed before this
2249                                 h_backgroundcolor = "";
2250                         }
2251                         continue;
2252                 }
2253
2254                 if (t.cs() == "makeatletter") {
2255                         // LyX takes care of this
2256                         p.setCatcode('@', catLetter);
2257                         continue;
2258                 }
2259
2260                 if (t.cs() == "makeatother") {
2261                         // LyX takes care of this
2262                         p.setCatcode('@', catOther);
2263                         continue;
2264                 }
2265
2266                 if (t.cs() == "makeindex") {
2267                         // LyX will re-add this if a print index command is found
2268                         p.skip_spaces();
2269                         continue;
2270                 }
2271
2272                 if (t.cs() == "newindex") {
2273                         string const indexname = p.getArg('[', ']');
2274                         string const shortcut = p.verbatim_item();
2275                         if (!indexname.empty())
2276                                 h_index[index_number] = indexname;
2277                         else
2278                                 h_index[index_number] = shortcut;
2279                         h_shortcut[index_number] = shortcut;
2280                         index_number += 1;
2281                         p.skip_spaces();
2282                         continue;
2283                 }
2284
2285                 if (t.cs() == "addbibresource") {
2286                         string const options =  p.getArg('[', ']');
2287                         string const arg = removeExtension(p.getArg('{', '}'));
2288                         if (!options.empty()) {
2289                                 // check if the option contains a bibencoding, if yes, extract it
2290                                 string::size_type pos = options.find("bibencoding=");
2291                                 string encoding;
2292                                 if (pos != string::npos) {
2293                                         string::size_type i = options.find(',', pos);
2294                                         if (i == string::npos)
2295                                                 encoding = options.substr(pos + 1);
2296                                         else
2297                                                 encoding = options.substr(pos, i - pos);
2298                                         pos = encoding.find('=');
2299                                         if (pos == string::npos)
2300                                                 encoding.clear();
2301                                         else
2302                                                 encoding = encoding.substr(pos + 1);
2303                                 }
2304                                 if (!encoding.empty())
2305                                         biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2306                         }
2307                         biblatex_bibliographies.push_back(arg);
2308                         continue;
2309                 }
2310
2311                 if (t.cs() == "bibliography") {
2312                         vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2313                         biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2314                         continue;
2315                 }
2316
2317                 if (t.cs() == "RS@ifundefined") {
2318                         string const name = p.verbatim_item();
2319                         string const body1 = p.verbatim_item();
2320                         string const body2 = p.verbatim_item();
2321                         // only non-lyxspecific stuff
2322                         if (in_lyx_preamble &&
2323                             (name == "subsecref" || name == "thmref" || name == "lemref"))
2324                                 p.skip_spaces();
2325                         else {
2326                                 ostringstream ss;
2327                                 ss << '\\' << t.cs();
2328                                 ss << '{' << name << '}'
2329                                    << '{' << body1 << '}'
2330                                    << '{' << body2 << '}';
2331                                 h_preamble << ss.str();
2332                         }
2333                         continue;
2334                 }
2335
2336                 if (t.cs() == "AtBeginDocument") {
2337                         string const name = p.verbatim_item();
2338                         // only non-lyxspecific stuff
2339                         if (in_lyx_preamble &&
2340                             (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2341                                 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2342                                 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2343                                 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2344                                 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2345                                 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2346                                 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2347                                 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2348                                 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2349                                 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2350                                 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2351                                 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2352                                 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2353                                 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2354                                 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2355                                 p.skip_spaces();
2356                         else {
2357                                 ostringstream ss;
2358                                 ss << '\\' << t.cs();
2359                                 ss << '{' << name << '}';
2360                                 h_preamble << ss.str();
2361                         }
2362                         continue;
2363                 }
2364
2365                 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2366                     || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2367                     || t.cs() == "providecommand" || t.cs() == "providecommandx"
2368                     || t.cs() == "DeclareRobustCommand"
2369                     || t.cs() == "DeclareRobustCommandx"
2370                     || t.cs() == "ProvideTextCommandDefault"
2371                     || t.cs() == "DeclareMathAccent") {
2372                         bool star = false;
2373                         if (p.next_token().character() == '*') {
2374                                 p.get_token();
2375                                 star = true;
2376                         }
2377                         string const name = p.verbatim_item();
2378                         string const opt1 = p.getFullOpt();
2379                         string const opt2 = p.getFullOpt();
2380                         string const body = p.verbatim_item();
2381                         // store the in_lyx_preamble setting
2382                         bool const was_in_lyx_preamble = in_lyx_preamble;
2383                         // font settings
2384                         if (name == "\\rmdefault")
2385                                 if (is_known(body, known_roman_font_packages)) {
2386                                         h_font_roman[0] = body;
2387                                         p.skip_spaces();
2388                                         in_lyx_preamble = true;
2389                                 }
2390                         if (name == "\\sfdefault")
2391                                 if (is_known(body, known_sans_font_packages)) {
2392                                         h_font_sans[0] = body;
2393                                         p.skip_spaces();
2394                                         in_lyx_preamble = true;
2395                                 }
2396                         if (name == "\\ttdefault")
2397                                 if (is_known(body, known_typewriter_font_packages)) {
2398                                         h_font_typewriter[0] = body;
2399                                         p.skip_spaces();
2400                                         in_lyx_preamble = true;
2401                                 }
2402                         if (name == "\\familydefault") {
2403                                 string family = body;
2404                                 // remove leading "\"
2405                                 h_font_default_family = family.erase(0,1);
2406                                 p.skip_spaces();
2407                                 in_lyx_preamble = true;
2408                         }
2409
2410                         // remove LyX-specific definitions that are re-added by LyX
2411                         // if necessary
2412                         // \lyxline is an ancient command that is converted by tex2lyx into
2413                         // a \rule therefore remove its preamble code
2414                         if (name == "\\lyxdot" || name == "\\lyxarrow"
2415                             || name == "\\lyxline" || name == "\\LyX") {
2416                                 p.skip_spaces();
2417                                 in_lyx_preamble = true;
2418                         }
2419
2420                         // Add the command to the known commands
2421                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2422
2423                         // only non-lyxspecific stuff
2424                         if (!in_lyx_preamble) {
2425                                 ostringstream ss;
2426                                 ss << '\\' << t.cs();
2427                                 if (star)
2428                                         ss << '*';
2429                                 ss << '{' << name << '}' << opt1 << opt2
2430                                    << '{' << body << "}";
2431                                 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2432                                         h_preamble << ss.str();
2433 /*
2434                                 ostream & out = in_preamble ? h_preamble : os;
2435                                 out << "\\" << t.cs() << "{" << name << "}"
2436                                     << opts << "{" << body << "}";
2437 */
2438                         }
2439                         // restore the in_lyx_preamble setting
2440                         in_lyx_preamble = was_in_lyx_preamble;
2441                         continue;
2442                 }
2443
2444                 if (t.cs() == "documentclass") {
2445                         vector<string>::iterator it;
2446                         vector<string> opts = split_options(p.getArg('[', ']'));
2447                         handle_opt(opts, known_fontsizes, h_paperfontsize);
2448                         delete_opt(opts, known_fontsizes);
2449                         // delete "pt" at the end
2450                         string::size_type i = h_paperfontsize.find("pt");
2451                         if (i != string::npos)
2452                                 h_paperfontsize.erase(i);
2453                         // The documentclass options are always parsed before the options
2454                         // of the babel call so that a language cannot overwrite the babel
2455                         // options.
2456                         handle_opt(opts, known_languages, h_language);
2457                         delete_opt(opts, known_languages);
2458
2459                         // math indentation
2460                         if ((it = find(opts.begin(), opts.end(), "fleqn"))
2461                                  != opts.end()) {
2462                                 h_is_mathindent = "1";
2463                                 opts.erase(it);
2464                         }
2465                         // formula numbering side
2466                         if ((it = find(opts.begin(), opts.end(), "leqno"))
2467                                  != opts.end()) {
2468                                 h_math_numbering_side = "left";
2469                                 opts.erase(it);
2470                         }
2471                         else if ((it = find(opts.begin(), opts.end(), "reqno"))
2472                                  != opts.end()) {
2473                                 h_math_numbering_side = "right";
2474                                 opts.erase(it);
2475                         }
2476
2477                         // paper orientation
2478                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2479                                 h_paperorientation = "landscape";
2480                                 opts.erase(it);
2481                         }
2482                         // paper sides
2483                         if ((it = find(opts.begin(), opts.end(), "oneside"))
2484                                  != opts.end()) {
2485                                 h_papersides = "1";
2486                                 opts.erase(it);
2487                         }
2488                         if ((it = find(opts.begin(), opts.end(), "twoside"))
2489                                  != opts.end()) {
2490                                 h_papersides = "2";
2491                                 opts.erase(it);
2492                         }
2493                         // paper columns
2494                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2495                                  != opts.end()) {
2496                                 h_papercolumns = "1";
2497                                 opts.erase(it);
2498                         }
2499                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2500                                  != opts.end()) {
2501                                 h_papercolumns = "2";
2502                                 opts.erase(it);
2503                         }
2504                         // paper sizes
2505                         // some size options are known to any document classes, other sizes
2506                         // are handled by the \geometry command of the geometry package
2507                         handle_opt(opts, known_class_paper_sizes, h_papersize);
2508                         delete_opt(opts, known_class_paper_sizes);
2509                         // the remaining options
2510                         h_options = join(opts, ",");
2511                         // FIXME This does not work for classes that have a
2512                         //       different name in LyX than in LaTeX
2513                         h_textclass = p.getArg('{', '}');
2514                         p.skip_spaces();
2515                         continue;
2516                 }
2517
2518                 if (t.cs() == "usepackage") {
2519                         string const options = p.getArg('[', ']');
2520                         string const name = p.getArg('{', '}');
2521                         vector<string> vecnames;
2522                         split(name, vecnames, ',');
2523                         vector<string>::const_iterator it  = vecnames.begin();
2524                         vector<string>::const_iterator end = vecnames.end();
2525                         for (; it != end; ++it)
2526                                 handle_package(p, trimSpaceAndEol(*it), options,
2527                                                in_lyx_preamble, detectEncoding);
2528                         continue;
2529                 }
2530
2531                 if (t.cs() == "inputencoding") {
2532                         string const encoding = p.getArg('{','}');
2533                         Encoding const * const enc = encodings.fromLaTeXName(
2534                                 encoding, Encoding::inputenc, true);
2535                         if (!enc) {
2536                                 if (!detectEncoding)
2537                                         cerr << "Unknown encoding " << encoding
2538                                              << ". Ignoring." << std::endl;
2539                         } else {
2540                                 if (!enc->unsafe())
2541                                         h_inputencoding = enc->name();
2542                                 p.setEncoding(enc->iconvName());
2543                         }
2544                         continue;
2545                 }
2546
2547                 if (t.cs() == "newenvironment") {
2548                         string const name = p.getArg('{', '}');
2549                         string const opt1 = p.getFullOpt();
2550                         string const opt2 = p.getFullOpt();
2551                         string const beg = p.verbatim_item();
2552                         string const end = p.verbatim_item();
2553                         if (!in_lyx_preamble) {
2554                                 h_preamble << "\\newenvironment{" << name
2555                                            << '}' << opt1 << opt2 << '{'
2556                                            << beg << "}{" << end << '}';
2557                         }
2558                         add_known_environment(name, opt1, !opt2.empty(),
2559                                               from_utf8(beg), from_utf8(end));
2560                         continue;
2561                 }
2562
2563                 if (t.cs() == "newtheorem") {
2564                         bool star = false;
2565                         if (p.next_token().character() == '*') {
2566                                 p.get_token();
2567                                 star = true;
2568                         }
2569                         string const name = p.getArg('{', '}');
2570                         string const opt1 = p.getFullOpt();
2571                         string const opt2 = p.getFullOpt();
2572                         string const body = p.verbatim_item();
2573                         string const opt3 = p.getFullOpt();
2574                         string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2575
2576                         string const complete = cmd + "{" + name + '}' +
2577                                           opt1 + opt2 + '{' + body + '}' + opt3;
2578
2579                         add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2580
2581                         if (!in_lyx_preamble)
2582                                 h_preamble << complete;
2583                         continue;
2584                 }
2585
2586                 if (t.cs() == "def") {
2587                         string name = p.get_token().cs();
2588                         // In fact, name may be more than the name:
2589                         // In the test case of bug 8116
2590                         // name == "csname SF@gobble@opt \endcsname".
2591                         // Therefore, we need to use asInput() instead of cs().
2592                         while (p.next_token().cat() != catBegin)
2593                                 name += p.get_token().asInput();
2594                         if (!in_lyx_preamble)
2595                                 h_preamble << "\\def\\" << name << '{'
2596                                            << p.verbatim_item() << "}";
2597                         continue;
2598                 }
2599
2600                 if (t.cs() == "newcolumntype") {
2601                         string const name = p.getArg('{', '}');
2602                         trimSpaceAndEol(name);
2603                         int nargs = 0;
2604                         string opts = p.getOpt();
2605                         if (!opts.empty()) {
2606                                 istringstream is(string(opts, 1));
2607                                 is >> nargs;
2608                         }
2609                         special_columns_[name[0]] = nargs;
2610                         h_preamble << "\\newcolumntype{" << name << "}";
2611                         if (nargs)
2612                                 h_preamble << "[" << nargs << "]";
2613                         h_preamble << "{" << p.verbatim_item() << "}";
2614                         continue;
2615                 }
2616
2617                 if (t.cs() == "setcounter") {
2618                         string const name = p.getArg('{', '}');
2619                         string const content = p.getArg('{', '}');
2620                         if (name == "secnumdepth")
2621                                 h_secnumdepth = content;
2622                         else if (name == "tocdepth")
2623                                 h_tocdepth = content;
2624                         else
2625                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2626                         continue;
2627                 }
2628
2629                 if (t.cs() == "setlength") {
2630                         string const name = p.verbatim_item();
2631                         string const content = p.verbatim_item();
2632                         // the paragraphs are only not indented when \parindent is set to zero
2633                         if (name == "\\parindent" && content != "") {
2634                                 if (content[0] == '0')
2635                                         h_paragraph_separation = "skip";
2636                                 else
2637                                         h_paragraph_indentation = translate_len(content);
2638                         } else if (name == "\\parskip") {
2639                                 if (content == "\\smallskipamount")
2640                                         h_defskip = "smallskip";
2641                                 else if (content == "\\medskipamount")
2642                                         h_defskip = "medskip";
2643                                 else if (content == "\\bigskipamount")
2644                                         h_defskip = "bigskip";
2645                                 else
2646                                         h_defskip = translate_len(content);
2647                         } else if (name == "\\mathindent") {
2648                                 h_mathindentation = translate_len(content);
2649                         } else
2650                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2651                         continue;
2652                 }
2653
2654                 if (t.cs() == "onehalfspacing") {
2655                         h_spacing = "onehalf";
2656                         continue;
2657                 }
2658
2659                 if (t.cs() == "doublespacing") {
2660                         h_spacing = "double";
2661                         continue;
2662                 }
2663
2664                 if (t.cs() == "setstretch") {
2665                         h_spacing = "other " + p.verbatim_item();
2666                         continue;
2667                 }
2668
2669                 if (t.cs() == "synctex") {
2670                         // the scheme is \synctex=value
2671                         // where value can only be "1" or "-1"
2672                         h_output_sync = "1";
2673                         // there can be any character behind the value (e.g. a linebreak or a '\'
2674                         // therefore we extract it char by char
2675                         p.get_token();
2676                         string value = p.get_token().asInput();
2677                         if (value == "-")
2678                                 value += p.get_token().asInput();
2679                         h_output_sync_macro = "\\synctex=" + value;
2680                         continue;
2681                 }
2682
2683                 if (t.cs() == "begin") {
2684                         string const name = p.getArg('{', '}');
2685                         if (name == "document")
2686                                 break;
2687                         h_preamble << "\\begin{" << name << "}";
2688                         continue;
2689                 }
2690
2691                 if (t.cs() == "geometry") {
2692                         vector<string> opts = split_options(p.getArg('{', '}'));
2693                         handle_geometry(opts);
2694                         continue;
2695                 }
2696
2697                 if (t.cs() == "definecolor") {
2698                         string const color = p.getArg('{', '}');
2699                         string const space = p.getArg('{', '}');
2700                         string const value = p.getArg('{', '}');
2701                         if (color == "document_fontcolor" && space == "rgb") {
2702                                 RGBColor c(RGBColorFromLaTeX(value));
2703                                 h_fontcolor = X11hexname(c);
2704                         } else if (color == "note_fontcolor" && space == "rgb") {
2705                                 RGBColor c(RGBColorFromLaTeX(value));
2706                                 h_notefontcolor = X11hexname(c);
2707                         } else if (color == "page_backgroundcolor" && space == "rgb") {
2708                                 RGBColor c(RGBColorFromLaTeX(value));
2709                                 h_backgroundcolor = X11hexname(c);
2710                         } else if (color == "shadecolor" && space == "rgb") {
2711                                 RGBColor c(RGBColorFromLaTeX(value));
2712                                 h_boxbgcolor = X11hexname(c);
2713                         } else {
2714                                 h_preamble << "\\definecolor{" << color
2715                                            << "}{" << space << "}{" << value
2716                                            << '}';
2717                         }
2718                         continue;
2719                 }
2720
2721                 if (t.cs() == "bibliographystyle") {
2722                         h_biblio_style = p.verbatim_item();
2723                         continue;
2724                 }
2725
2726                 if (t.cs() == "jurabibsetup") {
2727                         // FIXME p.getArg('{', '}') is most probably wrong (it
2728                         //       does not handle nested braces).
2729                         //       Use p.verbatim_item() instead.
2730                         vector<string> jurabibsetup =
2731                                 split_options(p.getArg('{', '}'));
2732                         // add jurabibsetup to the jurabib package options
2733                         add_package("jurabib", jurabibsetup);
2734                         if (!jurabibsetup.empty()) {
2735                                 h_preamble << "\\jurabibsetup{"
2736                                            << join(jurabibsetup, ",") << '}';
2737                         }
2738                         continue;
2739                 }
2740
2741                 if (t.cs() == "hypersetup") {
2742                         vector<string> hypersetup =
2743                                 split_options(p.verbatim_item());
2744                         // add hypersetup to the hyperref package options
2745                         handle_hyperref(hypersetup);
2746                         if (!hypersetup.empty()) {
2747                                 h_preamble << "\\hypersetup{"
2748                                            << join(hypersetup, ",") << '}';
2749                         }
2750                         continue;
2751                 }
2752
2753                 if (t.cs() == "includeonly") {
2754                         vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2755                         for (auto & iofile : includeonlys) {
2756                                 string filename(normalize_filename(iofile));
2757                                 string const path = getMasterFilePath(true);
2758                                 // We want to preserve relative/absolute filenames,
2759                                 // therefore path is only used for testing
2760                                 if (!makeAbsPath(filename, path).exists()) {
2761                                         // The file extension is probably missing.
2762                                         // Now try to find it out.
2763                                         string const tex_name =
2764                                                 find_file(filename, path,
2765                                                           known_tex_extensions);
2766                                         if (!tex_name.empty())
2767                                                 filename = tex_name;
2768                                 }
2769                                 string outname;
2770                                 if (makeAbsPath(filename, path).exists())
2771                                         fix_child_filename(filename);
2772                                 else
2773                                         cerr << "Warning: Could not find included file '"
2774                                              << filename << "'." << endl;
2775                                 outname = changeExtension(filename, "lyx");
2776                                 h_includeonlys.push_back(outname);
2777                         }
2778                         continue;
2779                 }
2780
2781                 if (is_known(t.cs(), known_if_3arg_commands)) {
2782                         // prevent misparsing of \usepackage if it is used
2783                         // as an argument (see e.g. our own output of
2784                         // \@ifundefined above)
2785                         string const arg1 = p.verbatim_item();
2786                         string const arg2 = p.verbatim_item();
2787                         string const arg3 = p.verbatim_item();
2788                         // test case \@ifundefined{date}{}{\date{}}
2789                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
2790                             arg2.empty() && arg3 == "\\date{}") {
2791                                 h_suppress_date = "true";
2792                         // older tex2lyx versions did output
2793                         // \@ifundefined{definecolor}{\usepackage{color}}{}
2794                         } else if (t.cs() == "@ifundefined" &&
2795                                    arg1 == "definecolor" &&
2796                                    arg2 == "\\usepackage{color}" &&
2797                                    arg3.empty()) {
2798                                 if (!in_lyx_preamble)
2799                                         h_preamble << package_beg_sep
2800                                                    << "color"
2801                                                    << package_mid_sep
2802                                                    << "\\@ifundefined{definecolor}{color}{}"
2803                                                    << package_end_sep;
2804                         // test for case
2805                         //\@ifundefined{showcaptionsetup}{}{%
2806                         // \PassOptionsToPackage{caption=false}{subfig}}
2807                         // that LyX uses for subfloats
2808                         } else if (t.cs() == "@ifundefined" &&
2809                                    arg1 == "showcaptionsetup" && arg2.empty()
2810                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2811                                 ; // do nothing
2812                         } else if (!in_lyx_preamble) {
2813                                 h_preamble << t.asInput()
2814                                            << '{' << arg1 << '}'
2815                                            << '{' << arg2 << '}'
2816                                            << '{' << arg3 << '}';
2817                         }
2818                         continue;
2819                 }
2820
2821                 if (is_known(t.cs(), known_if_commands)) {
2822                         // must not parse anything in conditional code, since
2823                         // LyX would output the parsed contents unconditionally
2824                         if (!in_lyx_preamble)
2825                                 h_preamble << t.asInput();
2826                         handle_if(p, in_lyx_preamble);
2827                         continue;
2828                 }
2829
2830                 if (!t.cs().empty() && !in_lyx_preamble) {
2831                         h_preamble << '\\' << t.cs();
2832                         continue;
2833                 }
2834         }
2835
2836         // remove the whitespace
2837         p.skip_spaces();
2838
2839         // Force textclass if the user wanted it
2840         if (!forceclass.empty())
2841                 h_textclass = forceclass;
2842         tc.setName(h_textclass);
2843         if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2844                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2845                 exit(EXIT_FAILURE);
2846         }
2847         if (h_papersides.empty()) {
2848                 ostringstream ss;
2849                 ss << tc.sides();
2850                 h_papersides = ss.str();
2851         }
2852
2853         // If the CJK package is used we cannot set the document language from
2854         // the babel options. Instead, we guess which language is used most
2855         // and set this one.
2856         default_language = h_language;
2857         if (is_full_document &&
2858             (auto_packages.find("CJK") != auto_packages.end() ||
2859              auto_packages.find("CJKutf8") != auto_packages.end())) {
2860                 p.pushPosition();
2861                 h_language = guessLanguage(p, default_language);
2862                 p.popPosition();
2863                 if (explicit_babel && h_language != default_language) {
2864                         // We set the document language to a CJK language,
2865                         // but babel is explicitly called in the user preamble
2866                         // without options. LyX will not add the default
2867                         // language to the document options if it is either
2868                         // english, or no text is set as default language.
2869                         // Therefore we need to add a language option explicitly.
2870                         // FIXME: It would be better to remove all babel calls
2871                         //        from the user preamble, but this is difficult
2872                         //        without re-introducing bug 7861.
2873                         if (h_options.empty())
2874                                 h_options = lyx2babel(default_language);
2875                         else
2876                                 h_options += ',' + lyx2babel(default_language);
2877                 }
2878         }
2879
2880         // Finally, set the quote style.
2881         // LyX knows the following quotes styles:
2882         // british, cjk, cjkangle, danish, english, french, german,
2883         // polish, russian, swedish and swiss
2884         // conversion list taken from
2885         // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2886         // (quotes for kazakh are unknown)
2887         // british
2888         if (is_known(h_language, known_british_quotes_languages))
2889                 h_quotes_style = "british";
2890         // cjk
2891         else if (is_known(h_language, known_cjk_quotes_languages))
2892                 h_quotes_style = "cjk";
2893         // cjkangle
2894         else if (is_known(h_language, known_cjkangle_quotes_languages))
2895                 h_quotes_style = "cjkangle";
2896         // danish
2897         else if (is_known(h_language, known_danish_quotes_languages))
2898                 h_quotes_style = "danish";
2899         // french
2900         else if (is_known(h_language, known_french_quotes_languages))
2901                 h_quotes_style = "french";
2902         // german
2903         else if (is_known(h_language, known_german_quotes_languages))
2904                 h_quotes_style = "german";
2905         // polish
2906         else if (is_known(h_language, known_polish_quotes_languages))
2907                 h_quotes_style = "polish";
2908         // russian
2909         else if (is_known(h_language, known_russian_quotes_languages))
2910                 h_quotes_style = "russian";
2911         // swedish
2912         else if (is_known(h_language, known_swedish_quotes_languages))
2913                 h_quotes_style = "swedish";
2914         // swiss
2915         else if (is_known(h_language, known_swiss_quotes_languages))
2916                 h_quotes_style = "swiss";
2917         // english
2918         else if (is_known(h_language, known_english_quotes_languages))
2919                 h_quotes_style = "english";
2920 }
2921
2922
2923 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2924 {
2925         TeX2LyXDocClass dummy;
2926         parse(p, forceclass, true, dummy);
2927         if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2928                 return h_inputencoding;
2929         return "";
2930 }
2931
2932
2933 string babel2lyx(string const & language)
2934 {
2935         char const * const * where = is_known(language, known_languages);
2936         if (where)
2937                 return known_coded_languages[where - known_languages];
2938         return language;
2939 }
2940
2941
2942 string lyx2babel(string const & language)
2943 {
2944         char const * const * where = is_known(language, known_coded_languages);
2945         if (where)
2946                 return known_languages[where - known_coded_languages];
2947         return language;
2948 }
2949
2950
2951 string Preamble::polyglossia2lyx(string const & language)
2952 {
2953         char const * const * where = is_known(language, polyglossia_languages);
2954         if (where)
2955                 return coded_polyglossia_languages[where - polyglossia_languages];
2956         return language;
2957 }
2958
2959
2960 string rgbcolor2code(string const & name)
2961 {
2962         char const * const * where = is_known(name, known_basic_colors);
2963         if (where) {
2964                 // "red", "green" etc
2965                 return known_basic_color_codes[where - known_basic_colors];
2966         }
2967         // "255,0,0", "0,255,0" etc
2968         RGBColor c(RGBColorFromLaTeX(name));
2969         return X11hexname(c);
2970 }
2971
2972 // }])
2973
2974
2975 } // namespace lyx