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