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