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