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