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