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