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