]> git.lyx.org Git - features.git/blob - src/tex2lyx/Preamble.cpp
use package parskip to separate paragraphs with vertical space (#4796)
[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", "parskip", "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 (name == "parskip" && options.size() < 2 && (opts.empty() || prefixIs(opts, "skip="))) {
1648                 if (opts.empty())
1649                         h_paragraph_separation = "halfline";
1650                 else {
1651                         if (opts == "skip=\\smallskipamount")
1652                                 h_defskip = "smallskip";
1653                         else if (opts == "skip=\\medskipamount")
1654                                 h_defskip = "medskip";
1655                         else if (opts == "skip=\\bigskipamount")
1656                                 h_defskip = "bigskip";
1657                         else if (opts == "skip=\\baselineskip")
1658                                 h_defskip = "fullline";
1659                         else
1660                                 h_defskip = "opts";
1661                         h_paragraph_separation = "skip";
1662                 }
1663         }
1664
1665         else if (is_known(name, known_lyx_packages) && options.empty()) {
1666                 if (name == "splitidx")
1667                         h_use_indices = "true";
1668                 else if (name == "minted")
1669                         h_use_minted = true;
1670                 else if (name == "refstyle")
1671                         h_use_refstyle = true;
1672                 else if (name == "prettyref")
1673                         h_use_refstyle = false;
1674
1675                 if (!in_lyx_preamble) {
1676                         h_preamble << package_beg_sep << name
1677                                    << package_mid_sep << "\\usepackage{"
1678                                    << name << '}';
1679                         if (p.next_token().cat() == catNewline ||
1680                             (p.next_token().cat() == catSpace &&
1681                              p.next_next_token().cat() == catNewline))
1682                                 h_preamble << '\n';
1683                         h_preamble << package_end_sep;
1684                 }
1685         }
1686
1687         else if (name == "geometry")
1688                 handle_geometry(options);
1689
1690         else if (name == "subfig")
1691                 ; // ignore this FIXME: Use the package separator mechanism instead
1692
1693         else if (char const * const * where = is_known(name, known_languages))
1694                 h_language = known_coded_languages[where - known_languages];
1695
1696         else if (name == "natbib") {
1697                 h_biblio_style = "plainnat";
1698                 h_cite_engine = "natbib";
1699                 h_cite_engine_type = "authoryear";
1700                 vector<string>::iterator it =
1701                         find(options.begin(), options.end(), "authoryear");
1702                 if (it != options.end())
1703                         options.erase(it);
1704                 else {
1705                         it = find(options.begin(), options.end(), "numbers");
1706                         if (it != options.end()) {
1707                                 h_cite_engine_type = "numerical";
1708                                 options.erase(it);
1709                         }
1710                 }
1711                 if (!options.empty())
1712                         h_biblio_options = join(options, ",");
1713         }
1714
1715         else if (name == "biblatex") {
1716                 h_biblio_style = "plainnat";
1717                 h_cite_engine = "biblatex";
1718                 h_cite_engine_type = "authoryear";
1719                 string opt;
1720                 vector<string>::iterator it =
1721                         find(options.begin(), options.end(), "natbib");
1722                 if (it != options.end()) {
1723                         options.erase(it);
1724                         h_cite_engine = "biblatex-natbib";
1725                 } else {
1726                         opt = process_keyval_opt(options, "natbib");
1727                         if (opt == "true")
1728                                 h_cite_engine = "biblatex-natbib";
1729                 }
1730                 opt = process_keyval_opt(options, "style");
1731                 if (!opt.empty()) {
1732                         h_biblatex_citestyle = opt;
1733                         h_biblatex_bibstyle = opt;
1734                 } else {
1735                         opt = process_keyval_opt(options, "citestyle");
1736                         if (!opt.empty())
1737                                 h_biblatex_citestyle = opt;
1738                         opt = process_keyval_opt(options, "bibstyle");
1739                         if (!opt.empty())
1740                                 h_biblatex_bibstyle = opt;
1741                 }
1742                 opt = process_keyval_opt(options, "refsection");
1743                 if (!opt.empty()) {
1744                         if (opt == "none" || opt == "part"
1745                             || opt == "chapter" || opt == "section"
1746                             || opt == "subsection")
1747                                 h_multibib = opt;
1748                         else
1749                                 cerr << "Ignoring unknown refsection value '"
1750                                      << opt << "'.";
1751                 }
1752                 opt = process_keyval_opt(options, "bibencoding");
1753                 if (!opt.empty())
1754                         bibencoding = opt;
1755                 if (!options.empty()) {
1756                         h_biblio_options = join(options, ",");
1757                         options.clear();
1758                 }
1759         }
1760
1761         else if (name == "jurabib") {
1762                 h_biblio_style = "jurabib";
1763                 h_cite_engine = "jurabib";
1764                 h_cite_engine_type = "authoryear";
1765                 if (!options.empty())
1766                         h_biblio_options = join(options, ",");
1767         }
1768
1769         else if (name == "bibtopic")
1770                 h_use_bibtopic = "true";
1771
1772         else if (name == "chapterbib")
1773                 h_multibib = "child";
1774
1775         else if (name == "hyperref")
1776                 handle_hyperref(options);
1777
1778         else if (name == "algorithm2e") {
1779                 // Load "algorithm2e" module
1780                 addModule("algorithm2e");
1781                 // Add the package options to the global document options
1782                 if (!options.empty()) {
1783                         if (h_options.empty())
1784                                 h_options = join(options, ",");
1785                         else
1786                                 h_options += ',' + join(options, ",");
1787                 }
1788         }
1789         else if (name == "microtype") {
1790                 //we internally support only microtype without params
1791                 if (options.empty())
1792                         h_use_microtype = "true";
1793                 else
1794                         h_preamble << "\\usepackage[" << opts << "]{microtype}";
1795         }
1796
1797         else if (name == "lineno") {
1798                 h_use_lineno = "true";
1799                 if (!options.empty()) {
1800                         h_lineno_options = join(options, ",");
1801                         options.clear();
1802                 }
1803         }
1804
1805         else if (name == "changebar")
1806                 h_output_changes = "true";
1807
1808         else if (!in_lyx_preamble) {
1809                 if (options.empty())
1810                         h_preamble << "\\usepackage{" << name << '}';
1811                 else {
1812                         h_preamble << "\\usepackage[" << opts << "]{"
1813                                    << name << '}';
1814                         options.clear();
1815                 }
1816                 if (p.next_token().cat() == catNewline ||
1817                     (p.next_token().cat() == catSpace &&
1818                      p.next_next_token().cat() == catNewline))
1819                         h_preamble << '\n';
1820         }
1821
1822         // We need to do something with the options...
1823         if (!options.empty() && !detectEncoding)
1824                 cerr << "Ignoring options '" << join(options, ",")
1825                      << "' of package " << name << '.' << endl;
1826
1827         // remove the whitespace
1828         p.skip_spaces();
1829 }
1830
1831
1832 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1833 {
1834         while (p.good()) {
1835                 Token t = p.get_token();
1836                 if (t.cat() == catEscape &&
1837                     is_known(t.cs(), known_if_commands))
1838                         handle_if(p, in_lyx_preamble);
1839                 else {
1840                         if (!in_lyx_preamble)
1841                                 h_preamble << t.asInput();
1842                         if (t.cat() == catEscape && t.cs() == "fi")
1843                                 return;
1844                 }
1845         }
1846 }
1847
1848
1849 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1850 {
1851         if (contains(h_float_placement, "H"))
1852                 registerAutomaticallyLoadedPackage("float");
1853         if (h_spacing != "single" && h_spacing != "default")
1854                 registerAutomaticallyLoadedPackage("setspace");
1855         if (h_use_packages["amsmath"] == "2") {
1856                 // amsbsy and amstext are already provided by amsmath
1857                 registerAutomaticallyLoadedPackage("amsbsy");
1858                 registerAutomaticallyLoadedPackage("amstext");
1859         }
1860
1861         // output the LyX file settings
1862         // Important: Keep the version formatting in sync with LyX and
1863         //            lyx2lyx (bug 7951)
1864         string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1865         os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1866            << lyx_version_minor << '\n'
1867            << "\\lyxformat " << LYX_FORMAT << '\n'
1868            << "\\begin_document\n"
1869            << "\\begin_header\n"
1870            << "\\save_transient_properties " << h_save_transient_properties << "\n"
1871            << "\\origin " << origin << "\n"
1872            << "\\textclass " << h_textclass << "\n";
1873         string const raw = subdoc ? empty_string() : h_preamble.str();
1874         if (!raw.empty()) {
1875                 os << "\\begin_preamble\n";
1876                 for (string::size_type i = 0; i < raw.size(); ++i) {
1877                         if (raw[i] == package_beg_sep) {
1878                                 // Here follows some package loading code that
1879                                 // must be skipped if the package is loaded
1880                                 // automatically.
1881                                 string::size_type j = raw.find(package_mid_sep, i);
1882                                 if (j == string::npos)
1883                                         return false;
1884                                 string::size_type k = raw.find(package_end_sep, j);
1885                                 if (k == string::npos)
1886                                         return false;
1887                                 string const package = raw.substr(i + 1, j - i - 1);
1888                                 string const replacement = raw.substr(j + 1, k - j - 1);
1889                                 if (auto_packages.find(package) == auto_packages.end())
1890                                         os << replacement;
1891                                 i = k;
1892                         } else
1893                                 os.put(raw[i]);
1894                 }
1895                 os << "\n\\end_preamble\n";
1896         }
1897         if (!h_options.empty())
1898                 os << "\\options " << h_options << "\n";
1899         os << "\\use_default_options " << h_use_default_options << "\n";
1900         if (!used_modules.empty()) {
1901                 os << "\\begin_modules\n";
1902                 vector<string>::const_iterator const end = used_modules.end();
1903                 vector<string>::const_iterator it = used_modules.begin();
1904                 for (; it != end; ++it)
1905                         os << *it << '\n';
1906                 os << "\\end_modules\n";
1907         }
1908         if (!h_includeonlys.empty()) {
1909                 os << "\\begin_includeonly\n";
1910                 for (auto const & iofile : h_includeonlys)
1911                         os << iofile << '\n';
1912                 os << "\\end_includeonly\n";
1913         }
1914         os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1915            << "\\language " << h_language << "\n"
1916            << "\\language_package " << h_language_package << "\n"
1917            << "\\inputencoding " << h_inputencoding << "\n"
1918            << "\\fontencoding " << h_fontencoding << "\n"
1919            << "\\font_roman \"" << h_font_roman[0]
1920            << "\" \"" << h_font_roman[1] << "\"\n"
1921            << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1922            << "\\font_typewriter \"" << h_font_typewriter[0]
1923            << "\" \"" << h_font_typewriter[1] << "\"\n"
1924            << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1925            << "\\font_default_family " << h_font_default_family << "\n"
1926            << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1927            << "\\font_sc " << h_font_sc << "\n"
1928            << "\\font_roman_osf " << h_font_roman_osf << "\n"
1929            << "\\font_sans_osf " << h_font_sans_osf << "\n"
1930            << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
1931         if (!h_font_roman_opts.empty())
1932                 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
1933         os << "\\font_sf_scale " << h_font_sf_scale[0]
1934            << ' ' << h_font_sf_scale[1] << '\n';
1935         if (!h_font_sans_opts.empty())
1936                 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
1937         os << "\\font_tt_scale " << h_font_tt_scale[0]
1938            << ' ' << h_font_tt_scale[1] << '\n';
1939         if (!h_font_cjk.empty())
1940                 os << "\\font_cjk " << h_font_cjk << '\n';
1941         if (!h_font_typewriter_opts.empty())
1942                 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
1943         os << "\\use_microtype " << h_use_microtype << '\n'
1944            << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1945            << "\\graphics " << h_graphics << '\n'
1946            << "\\default_output_format " << h_default_output_format << "\n"
1947            << "\\output_sync " << h_output_sync << "\n";
1948         if (h_output_sync == "1")
1949                 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1950         os << "\\bibtex_command " << h_bibtex_command << "\n"
1951            << "\\index_command " << h_index_command << "\n";
1952         if (!h_float_placement.empty())
1953                 os << "\\float_placement " << h_float_placement << "\n";
1954         os << "\\paperfontsize " << h_paperfontsize << "\n"
1955            << "\\spacing " << h_spacing << "\n"
1956            << "\\use_hyperref " << h_use_hyperref << '\n';
1957         if (h_use_hyperref == "true") {
1958                 if (!h_pdf_title.empty())
1959                         os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1960                 if (!h_pdf_author.empty())
1961                         os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1962                 if (!h_pdf_subject.empty())
1963                         os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1964                 if (!h_pdf_keywords.empty())
1965                         os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1966                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1967                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1968                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1969                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1970                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1971                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1972                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1973                       "\\pdf_backref " << h_pdf_backref << "\n"
1974                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1975                 if (!h_pdf_pagemode.empty())
1976                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1977                 if (!h_pdf_quoted_options.empty())
1978                         os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1979         }
1980         os << "\\papersize " << h_papersize << "\n"
1981            << "\\use_geometry " << h_use_geometry << '\n';
1982         for (map<string, string>::const_iterator it = h_use_packages.begin();
1983              it != h_use_packages.end(); ++it)
1984                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1985         os << "\\cite_engine " << h_cite_engine << '\n'
1986            << "\\cite_engine_type " << h_cite_engine_type << '\n'
1987            << "\\biblio_style " << h_biblio_style << "\n"
1988            << "\\use_bibtopic " << h_use_bibtopic << "\n";
1989         if (!h_biblio_options.empty())
1990                 os << "\\biblio_options " << h_biblio_options << "\n";
1991         if (!h_biblatex_bibstyle.empty())
1992                 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1993         if (!h_biblatex_citestyle.empty())
1994                 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1995         if (!h_multibib.empty())
1996                 os << "\\multibib " << h_multibib << "\n";
1997         os << "\\use_indices " << h_use_indices << "\n"
1998            << "\\paperorientation " << h_paperorientation << '\n'
1999            << "\\suppress_date " << h_suppress_date << '\n'
2000            << "\\justification " << h_justification << '\n'
2001            << "\\use_refstyle " << h_use_refstyle << '\n'
2002            << "\\use_minted " << h_use_minted << '\n'
2003            << "\\use_lineno " << h_use_lineno << '\n';
2004         if (!h_lineno_options.empty())
2005                 os << "\\lineno_options " << h_lineno_options << '\n';
2006         if (!h_fontcolor.empty())
2007                 os << "\\fontcolor " << h_fontcolor << '\n';
2008         if (!h_notefontcolor.empty())
2009                 os << "\\notefontcolor " << h_notefontcolor << '\n';
2010         if (!h_backgroundcolor.empty())
2011                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
2012         if (!h_boxbgcolor.empty())
2013                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
2014         if (index_number != 0)
2015                 for (int i = 0; i < index_number; i++) {
2016                         os << "\\index " << h_index[i] << '\n'
2017                            << "\\shortcut " << h_shortcut[i] << '\n'
2018                            << "\\color " << h_color << '\n'
2019                            << "\\end_index\n";
2020                 }
2021         else {
2022                 os << "\\index " << h_index[0] << '\n'
2023                    << "\\shortcut " << h_shortcut[0] << '\n'
2024                    << "\\color " << h_color << '\n'
2025                    << "\\end_index\n";
2026         }
2027         os << h_margins
2028            << "\\secnumdepth " << h_secnumdepth << "\n"
2029            << "\\tocdepth " << h_tocdepth << "\n"
2030            << "\\paragraph_separation " << h_paragraph_separation << "\n";
2031         if (h_paragraph_separation == "skip")
2032                 os << "\\defskip " << h_defskip << "\n";
2033         else
2034                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2035         os << "\\is_math_indent " << h_is_mathindent << "\n";
2036         if (!h_mathindentation.empty())
2037                 os << "\\math_indentation " << h_mathindentation << "\n";
2038         os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2039         os << "\\quotes_style " << h_quotes_style << "\n"
2040            << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2041            << "\\papercolumns " << h_papercolumns << "\n"
2042            << "\\papersides " << h_papersides << "\n"
2043            << "\\paperpagestyle " << h_paperpagestyle << "\n";
2044         if (!h_listings_params.empty())
2045                 os << "\\listings_params " << h_listings_params << "\n";
2046         os << "\\tracking_changes " << h_tracking_changes << "\n"
2047            << "\\output_changes " << h_output_changes << "\n"
2048            << "\\change_bars " << h_change_bars << "\n"
2049            << "\\html_math_output " << h_html_math_output << "\n"
2050            << "\\html_css_as_file " << h_html_css_as_file << "\n"
2051            << "\\html_be_strict " << h_html_be_strict << "\n"
2052            << authors_
2053            << "\\end_header\n\n"
2054            << "\\begin_body\n";
2055         return true;
2056 }
2057
2058
2059 void Preamble::parse(Parser & p, string const & forceclass,
2060                      TeX2LyXDocClass & tc)
2061 {
2062         // initialize fixed types
2063         special_columns_['D'] = 3;
2064         parse(p, forceclass, false, tc);
2065 }
2066
2067
2068 void Preamble::parse(Parser & p, string const & forceclass,
2069                      bool detectEncoding, TeX2LyXDocClass & tc)
2070 {
2071         bool is_full_document = false;
2072         bool is_lyx_file = false;
2073         bool in_lyx_preamble = false;
2074
2075         // determine whether this is a full document or a fragment for inclusion
2076         while (p.good()) {
2077                 Token const & t = p.get_token();
2078
2079                 if (t.cat() == catEscape && t.cs() == "documentclass") {
2080                         is_full_document = true;
2081                         break;
2082                 }
2083         }
2084         p.reset();
2085
2086         if (detectEncoding && !is_full_document)
2087                 return;
2088
2089         while (is_full_document && p.good()) {
2090                 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2091                     h_inputencoding != "auto-legacy-plain")
2092                         return;
2093
2094                 Token const & t = p.get_token();
2095
2096 #ifdef FILEDEBUG
2097                 if (!detectEncoding)
2098                         cerr << "t: " << t << '\n';
2099 #endif
2100
2101                 //
2102                 // cat codes
2103                 //
2104                 if (!in_lyx_preamble &&
2105                     (t.cat() == catLetter ||
2106                      t.cat() == catSuper ||
2107                      t.cat() == catSub ||
2108                      t.cat() == catOther ||
2109                      t.cat() == catMath ||
2110                      t.cat() == catActive ||
2111                      t.cat() == catBegin ||
2112                      t.cat() == catEnd ||
2113                      t.cat() == catAlign ||
2114                      t.cat() == catParameter)) {
2115                         h_preamble << t.cs();
2116                         continue;
2117                 }
2118
2119                 if (!in_lyx_preamble &&
2120                     (t.cat() == catSpace || t.cat() == catNewline)) {
2121                         h_preamble << t.asInput();
2122                         continue;
2123                 }
2124
2125                 if (t.cat() == catComment) {
2126                         static regex const islyxfile("%% LyX .* created this file");
2127                         static regex const usercommands("User specified LaTeX commands");
2128
2129                         string const comment = t.asInput();
2130
2131                         // magically switch encoding default if it looks like XeLaTeX
2132                         static string const magicXeLaTeX =
2133                                 "% This document must be compiled with XeLaTeX ";
2134                         if (comment.size() > magicXeLaTeX.size()
2135                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2136                                   && h_inputencoding == "auto-legacy") {
2137                                 if (!detectEncoding)
2138                                         cerr << "XeLaTeX comment found, switching to UTF8\n";
2139                                 h_inputencoding = "utf8";
2140                         }
2141                         smatch sub;
2142                         if (regex_search(comment, sub, islyxfile)) {
2143                                 is_lyx_file = true;
2144                                 in_lyx_preamble = true;
2145                         } else if (is_lyx_file
2146                                    && regex_search(comment, sub, usercommands))
2147                                 in_lyx_preamble = false;
2148                         else if (!in_lyx_preamble)
2149                                 h_preamble << t.asInput();
2150                         continue;
2151                 }
2152
2153                 if (t.cs() == "PassOptionsToPackage") {
2154                         string const poptions = p.getArg('{', '}');
2155                         string const package = p.verbatim_item();
2156                         extra_package_options_.insert(make_pair(package, poptions));
2157                         continue;
2158                 }
2159
2160                 if (t.cs() == "pagestyle") {
2161                         h_paperpagestyle = p.verbatim_item();
2162                         continue;
2163                 }
2164
2165                 if (t.cs() == "setdefaultlanguage") {
2166                         xetex = true;
2167                         // We don't yet care about non-language variant options
2168                         // because LyX doesn't support this yet, see bug #8214
2169                         if (p.hasOpt()) {
2170                                 string langopts = p.getOpt();
2171                                 // check if the option contains a variant, if yes, extract it
2172                                 string::size_type pos_var = langopts.find("variant");
2173                                 string::size_type i = langopts.find(',', pos_var);
2174                                 string::size_type k = langopts.find('=', pos_var);
2175                                 if (pos_var != string::npos){
2176                                         string variant;
2177                                         if (i == string::npos)
2178                                                 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2179                                         else
2180                                                 variant = langopts.substr(k + 1, i - k - 1);
2181                                         h_language = variant;
2182                                 }
2183                                 p.verbatim_item();
2184                         } else
2185                                 h_language = p.verbatim_item();
2186                         //finally translate the poyglossia name to a LyX name
2187                         h_language = polyglossia2lyx(h_language);
2188                         continue;
2189                 }
2190
2191                 if (t.cs() == "setotherlanguage") {
2192                         // We don't yet care about the option because LyX doesn't
2193                         // support this yet, see bug #8214
2194                         p.hasOpt() ? p.getOpt() : string();
2195                         p.verbatim_item();
2196                         continue;
2197                 }
2198
2199                 if (t.cs() == "setmainfont") {
2200                         string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2201                         h_font_roman[1] = p.getArg('{', '}');
2202                         if (!fontopts.empty()) {
2203                                 vector<string> opts = getVectorFromString(fontopts);
2204                                 fontopts.clear();
2205                                 for (auto const & opt : opts) {
2206                                         if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2207                                                 // ignore
2208                                                 continue;
2209                                         if (!fontopts.empty())
2210                                                 fontopts += ", ";
2211                                         fontopts += opt;
2212                                 }
2213                                 h_font_roman_opts = fontopts;
2214                         }
2215                         continue;
2216                 }
2217
2218                 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2219                         // LyX currently only supports the scale option
2220                         string scale, fontopts;
2221                         if (p.hasOpt()) {
2222                                 fontopts = p.getArg('[', ']');
2223                                 if (!fontopts.empty()) {
2224                                         vector<string> opts = getVectorFromString(fontopts);
2225                                         fontopts.clear();
2226                                         for (auto const & opt : opts) {
2227                                                 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2228                                                         // ignore
2229                                                         continue;
2230                                                 if (prefixIs(opt, "Scale=")) {
2231                                                         scale_as_percentage(opt, scale);
2232                                                         continue;
2233                                                 }
2234                                                 if (!fontopts.empty())
2235                                                         fontopts += ", ";
2236                                                 fontopts += opt;
2237                                         }
2238                                 }
2239                         }
2240                         if (t.cs() == "setsansfont") {
2241                                 if (!scale.empty())
2242                                         h_font_sf_scale[1] = scale;
2243                                 h_font_sans[1] = p.getArg('{', '}');
2244                                 if (!fontopts.empty())
2245                                         h_font_sans_opts = fontopts;
2246                         } else {
2247                                 if (!scale.empty())
2248                                         h_font_tt_scale[1] = scale;
2249                                 h_font_typewriter[1] = p.getArg('{', '}');
2250                                 if (!fontopts.empty())
2251                                         h_font_typewriter_opts = fontopts;
2252                         }
2253                         continue;
2254                 }
2255
2256                 if (t.cs() == "babelfont") {
2257                         xetex = true;
2258                         h_use_non_tex_fonts = true;
2259                         h_language_package = "babel";
2260                         if (h_inputencoding == "auto-legacy")
2261                         p.setEncoding("UTF-8");
2262                         // we don't care about the lang option
2263                         string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2264                         string const family = p.getArg('{', '}');
2265                         string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2266                         string const fontname = p.getArg('{', '}');
2267                         if (lang.empty() && family == "rm") {
2268                                 h_font_roman[1] = fontname;
2269                                 if (!fontopts.empty()) {
2270                                         vector<string> opts = getVectorFromString(fontopts);
2271                                         fontopts.clear();
2272                                         for (auto const & opt : opts) {
2273                                                 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2274                                                         // ignore
2275                                                         continue;
2276                                                 if (!fontopts.empty())
2277                                                         fontopts += ", ";
2278                                                 fontopts += opt;
2279                                         }
2280                                         h_font_roman_opts = fontopts;
2281                                 }
2282                                 continue;
2283                         } else if (lang.empty() && (family == "sf" || family == "tt")) {
2284                                 string scale;
2285                                 if (!fontopts.empty()) {
2286                                         vector<string> opts = getVectorFromString(fontopts);
2287                                         fontopts.clear();
2288                                         for (auto const & opt : opts) {
2289                                                 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2290                                                         // ignore
2291                                                         continue;
2292                                                 if (prefixIs(opt, "Scale=")) {
2293                                                         scale_as_percentage(opt, scale);
2294                                                         continue;
2295                                                 }
2296                                                 if (!fontopts.empty())
2297                                                         fontopts += ", ";
2298                                                 fontopts += opt;
2299                                         }
2300                                 }
2301                                 if (family == "sf") {
2302                                         if (!scale.empty())
2303                                                 h_font_sf_scale[1] = scale;
2304                                         h_font_sans[1] = fontname;
2305                                         if (!fontopts.empty())
2306                                                 h_font_sans_opts = fontopts;
2307                                 } else {
2308                                         if (!scale.empty())
2309                                                 h_font_tt_scale[1] = scale;
2310                                         h_font_typewriter[1] = fontname;
2311                                         if (!fontopts.empty())
2312                                                 h_font_typewriter_opts = fontopts;
2313                                 }
2314                                 continue;
2315                         } else {
2316                                 // not rm, sf or tt or lang specific
2317                                 h_preamble << '\\' << t.cs();
2318                                 if (!lang.empty())
2319                                         h_preamble << '[' << lang << ']';
2320                                 h_preamble << '{' << family << '}';
2321                                 if (!fontopts.empty())
2322                                         h_preamble << '[' << fontopts << ']';
2323                                 h_preamble << '{' << fontname << '}' << '\n';
2324                                 continue;
2325                         }
2326                 }
2327
2328                 if (t.cs() == "date") {
2329                         string argument = p.getArg('{', '}');
2330                         if (argument.empty())
2331                                 h_suppress_date = "true";
2332                         else
2333                                 h_preamble << t.asInput() << '{' << argument << '}';
2334                         continue;
2335                 }
2336
2337                 if (t.cs() == "color") {
2338                         string const space =
2339                                 (p.hasOpt() ? p.getOpt() : string());
2340                         string argument = p.getArg('{', '}');
2341                         // check the case that a standard color is used
2342                         if (space.empty() && is_known(argument, known_basic_colors)) {
2343                                 h_fontcolor = rgbcolor2code(argument);
2344                                 registerAutomaticallyLoadedPackage("color");
2345                         } else if (space.empty() && argument == "document_fontcolor")
2346                                 registerAutomaticallyLoadedPackage("color");
2347                         // check the case that LyX's document_fontcolor is defined
2348                         // but not used for \color
2349                         else {
2350                                 h_preamble << t.asInput();
2351                                 if (!space.empty())
2352                                         h_preamble << space;
2353                                 h_preamble << '{' << argument << '}';
2354                                 // the color might already be set because \definecolor
2355                                 // is parsed before this
2356                                 h_fontcolor = "";
2357                         }
2358                         continue;
2359                 }
2360
2361                 if (t.cs() == "pagecolor") {
2362                         string argument = p.getArg('{', '}');
2363                         // check the case that a standard color is used
2364                         if (is_known(argument, known_basic_colors)) {
2365                                 h_backgroundcolor = rgbcolor2code(argument);
2366                         } else if (argument == "page_backgroundcolor")
2367                                 registerAutomaticallyLoadedPackage("color");
2368                         // check the case that LyX's page_backgroundcolor is defined
2369                         // but not used for \pagecolor
2370                         else {
2371                                 h_preamble << t.asInput() << '{' << argument << '}';
2372                                 // the color might already be set because \definecolor
2373                                 // is parsed before this
2374                                 h_backgroundcolor = "";
2375                         }
2376                         continue;
2377                 }
2378
2379                 if (t.cs() == "makeatletter") {
2380                         // LyX takes care of this
2381                         p.setCatcode('@', catLetter);
2382                         continue;
2383                 }
2384
2385                 if (t.cs() == "makeatother") {
2386                         // LyX takes care of this
2387                         p.setCatcode('@', catOther);
2388                         continue;
2389                 }
2390
2391                 if (t.cs() == "makeindex") {
2392                         // LyX will re-add this if a print index command is found
2393                         p.skip_spaces();
2394                         continue;
2395                 }
2396
2397                 if (t.cs() == "newindex") {
2398                         string const indexname = p.getArg('[', ']');
2399                         string const shortcut = p.verbatim_item();
2400                         if (!indexname.empty())
2401                                 h_index[index_number] = indexname;
2402                         else
2403                                 h_index[index_number] = shortcut;
2404                         h_shortcut[index_number] = shortcut;
2405                         index_number += 1;
2406                         p.skip_spaces();
2407                         continue;
2408                 }
2409
2410                 if (t.cs() == "addbibresource") {
2411                         string const options =  p.getArg('[', ']');
2412                         string const arg = removeExtension(p.getArg('{', '}'));
2413                         if (!options.empty()) {
2414                                 // check if the option contains a bibencoding, if yes, extract it
2415                                 string::size_type pos = options.find("bibencoding=");
2416                                 string encoding;
2417                                 if (pos != string::npos) {
2418                                         string::size_type i = options.find(',', pos);
2419                                         if (i == string::npos)
2420                                                 encoding = options.substr(pos + 1);
2421                                         else
2422                                                 encoding = options.substr(pos, i - pos);
2423                                         pos = encoding.find('=');
2424                                         if (pos == string::npos)
2425                                                 encoding.clear();
2426                                         else
2427                                                 encoding = encoding.substr(pos + 1);
2428                                 }
2429                                 if (!encoding.empty())
2430                                         biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2431                         }
2432                         biblatex_bibliographies.push_back(arg);
2433                         continue;
2434                 }
2435
2436                 if (t.cs() == "bibliography") {
2437                         vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2438                         biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2439                         continue;
2440                 }
2441
2442                 if (t.cs() == "RS@ifundefined") {
2443                         string const name = p.verbatim_item();
2444                         string const body1 = p.verbatim_item();
2445                         string const body2 = p.verbatim_item();
2446                         // only non-lyxspecific stuff
2447                         if (in_lyx_preamble &&
2448                             (name == "subsecref" || name == "thmref" || name == "lemref"))
2449                                 p.skip_spaces();
2450                         else {
2451                                 ostringstream ss;
2452                                 ss << '\\' << t.cs();
2453                                 ss << '{' << name << '}'
2454                                    << '{' << body1 << '}'
2455                                    << '{' << body2 << '}';
2456                                 h_preamble << ss.str();
2457                         }
2458                         continue;
2459                 }
2460
2461                 if (t.cs() == "AtBeginDocument") {
2462                         string const name = p.verbatim_item();
2463                         // only non-lyxspecific stuff
2464                         if (in_lyx_preamble &&
2465                             (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2466                                 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2467                                 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2468                                 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2469                                 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2470                                 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2471                                 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2472                                 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2473                                 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2474                                 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2475                                 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2476                                 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2477                                 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2478                                 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2479                                 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2480                                 p.skip_spaces();
2481                         else {
2482                                 ostringstream ss;
2483                                 ss << '\\' << t.cs();
2484                                 ss << '{' << name << '}';
2485                                 h_preamble << ss.str();
2486                         }
2487                         continue;
2488                 }
2489
2490                 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2491                     || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2492                     || t.cs() == "providecommand" || t.cs() == "providecommandx"
2493                     || t.cs() == "DeclareRobustCommand"
2494                     || t.cs() == "DeclareRobustCommandx"
2495                     || t.cs() == "ProvideTextCommandDefault"
2496                     || t.cs() == "DeclareMathAccent") {
2497                         bool star = false;
2498                         if (p.next_token().character() == '*') {
2499                                 p.get_token();
2500                                 star = true;
2501                         }
2502                         string const name = p.verbatim_item();
2503                         string const opt1 = p.getFullOpt();
2504                         string const opt2 = p.getFullOpt();
2505                         string const body = p.verbatim_item();
2506                         // store the in_lyx_preamble setting
2507                         bool const was_in_lyx_preamble = in_lyx_preamble;
2508                         // font settings
2509                         if (name == "\\rmdefault")
2510                                 if (is_known(body, known_roman_font_packages)) {
2511                                         h_font_roman[0] = body;
2512                                         p.skip_spaces();
2513                                         in_lyx_preamble = true;
2514                                 }
2515                         if (name == "\\sfdefault")
2516                                 if (is_known(body, known_sans_font_packages)) {
2517                                         h_font_sans[0] = body;
2518                                         p.skip_spaces();
2519                                         in_lyx_preamble = true;
2520                                 }
2521                         if (name == "\\ttdefault")
2522                                 if (is_known(body, known_typewriter_font_packages)) {
2523                                         h_font_typewriter[0] = body;
2524                                         p.skip_spaces();
2525                                         in_lyx_preamble = true;
2526                                 }
2527                         if (name == "\\familydefault") {
2528                                 string family = body;
2529                                 // remove leading "\"
2530                                 h_font_default_family = family.erase(0,1);
2531                                 p.skip_spaces();
2532                                 in_lyx_preamble = true;
2533                         }
2534
2535                         // remove LyX-specific definitions that are re-added by LyX
2536                         // if necessary
2537                         // \lyxline is an ancient command that is converted by tex2lyx into
2538                         // a \rule therefore remove its preamble code
2539                         if (name == "\\lyxdot" || name == "\\lyxarrow"
2540                             || name == "\\lyxline" || name == "\\LyX") {
2541                                 p.skip_spaces();
2542                                 in_lyx_preamble = true;
2543                         }
2544
2545                         // Add the command to the known commands
2546                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2547
2548                         // only non-lyxspecific stuff
2549                         if (!in_lyx_preamble) {
2550                                 ostringstream ss;
2551                                 ss << '\\' << t.cs();
2552                                 if (star)
2553                                         ss << '*';
2554                                 ss << '{' << name << '}' << opt1 << opt2
2555                                    << '{' << body << "}";
2556                                 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2557                                         h_preamble << ss.str();
2558 /*
2559                                 ostream & out = in_preamble ? h_preamble : os;
2560                                 out << "\\" << t.cs() << "{" << name << "}"
2561                                     << opts << "{" << body << "}";
2562 */
2563                         }
2564                         // restore the in_lyx_preamble setting
2565                         in_lyx_preamble = was_in_lyx_preamble;
2566                         continue;
2567                 }
2568
2569                 if (t.cs() == "documentclass") {
2570                         vector<string>::iterator it;
2571                         vector<string> opts = split_options(p.getArg('[', ']'));
2572                         // FIXME This does not work for classes that have a
2573                         //       different name in LyX than in LaTeX
2574                         h_textclass = p.getArg('{', '}');
2575                         p.skip_spaces();
2576                         // Force textclass if the user wanted it
2577                         if (!forceclass.empty())
2578                                 h_textclass = forceclass;
2579                         tc.setName(h_textclass);
2580                         if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2581                                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2582                                 exit(EXIT_FAILURE);
2583                         }
2584
2585                         // Font sizes.
2586                         // Try those who are (most likely) known to all packages first
2587                         handle_opt(opts, known_fontsizes, h_paperfontsize);
2588                         delete_opt(opts, known_fontsizes);
2589                         // delete "pt" at the end
2590                         string::size_type i = h_paperfontsize.find("pt");
2591                         if (i != string::npos)
2592                                 h_paperfontsize.erase(i);
2593                         // Now those known specifically to the class
2594                         vector<string> class_fsizes = getVectorFromString(tc.opt_fontsize(), "|");
2595                         string const fsize_format = tc.fontsizeformat();
2596                         for (auto const & fsize : class_fsizes) {
2597                                 string latexsize = subst(fsize_format, "$$s", fsize);
2598                                 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2599                                 if (it != opts.end()) {
2600                                         h_paperfontsize = fsize;
2601                                         opts.erase(it);
2602                                         break;
2603                                 }
2604                         }
2605
2606                         // The documentclass options are always parsed before the options
2607                         // of the babel call so that a language cannot overwrite the babel
2608                         // options.
2609                         handle_opt(opts, known_languages, h_language);
2610                         delete_opt(opts, known_languages);
2611
2612                         // math indentation
2613                         if ((it = find(opts.begin(), opts.end(), "fleqn"))
2614                                  != opts.end()) {
2615                                 h_is_mathindent = "1";
2616                                 opts.erase(it);
2617                         }
2618                         // formula numbering side
2619                         if ((it = find(opts.begin(), opts.end(), "leqno"))
2620                                  != opts.end()) {
2621                                 h_math_numbering_side = "left";
2622                                 opts.erase(it);
2623                         }
2624                         else if ((it = find(opts.begin(), opts.end(), "reqno"))
2625                                  != opts.end()) {
2626                                 h_math_numbering_side = "right";
2627                                 opts.erase(it);
2628                         }
2629
2630                         // paper orientation
2631                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2632                                 h_paperorientation = "landscape";
2633                                 opts.erase(it);
2634                         }
2635                         // paper sides
2636                         if ((it = find(opts.begin(), opts.end(), "oneside"))
2637                                  != opts.end()) {
2638                                 h_papersides = "1";
2639                                 opts.erase(it);
2640                         }
2641                         if ((it = find(opts.begin(), opts.end(), "twoside"))
2642                                  != opts.end()) {
2643                                 h_papersides = "2";
2644                                 opts.erase(it);
2645                         }
2646                         // paper columns
2647                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2648                                  != opts.end()) {
2649                                 h_papercolumns = "1";
2650                                 opts.erase(it);
2651                         }
2652                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2653                                  != opts.end()) {
2654                                 h_papercolumns = "2";
2655                                 opts.erase(it);
2656                         }
2657                         // paper sizes
2658                         // some size options are known by the document class, other sizes
2659                         // are handled by the \geometry command of the geometry package
2660                         string paper;
2661                         vector<string> class_psizes = getVectorFromString(tc.opt_pagesize(), "|");
2662                         string const psize_format = tc.pagesizeformat();
2663                         for (auto const & psize : class_psizes) {
2664                                 string latexsize = subst(psize_format, "$$s", psize);
2665                                 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2666                                 if (it != opts.end()) {
2667                                         h_papersize = psize;
2668                                         opts.erase(it);
2669                                         break;
2670                                 }
2671                                 if (psize_format == "$$spaper")
2672                                         continue;
2673                                 // Also try with the default format since this is understood by
2674                                 // most classes
2675                                 latexsize = psize + "paper";
2676                                 it = find(opts.begin(), opts.end(), latexsize);
2677                                 if (it != opts.end()) {
2678                                         h_papersize = psize;
2679                                         opts.erase(it);
2680                                         break;
2681                                 }
2682                         }
2683                         // the remaining options
2684                         h_options = join(opts, ",");
2685                         continue;
2686                 }
2687
2688                 if (t.cs() == "usepackage") {
2689                         string const options = p.getArg('[', ']');
2690                         string const name = p.getArg('{', '}');
2691                         vector<string> vecnames;
2692                         split(name, vecnames, ',');
2693                         vector<string>::const_iterator it  = vecnames.begin();
2694                         vector<string>::const_iterator end = vecnames.end();
2695                         for (; it != end; ++it)
2696                                 handle_package(p, trimSpaceAndEol(*it), options,
2697                                                in_lyx_preamble, detectEncoding);
2698                         continue;
2699                 }
2700
2701                 if (t.cs() == "inputencoding") {
2702                         string const encoding = p.getArg('{','}');
2703                         Encoding const * const enc = encodings.fromLaTeXName(
2704                                 encoding, Encoding::inputenc, true);
2705                         if (!enc) {
2706                                 if (!detectEncoding)
2707                                         cerr << "Unknown encoding " << encoding
2708                                              << ". Ignoring." << std::endl;
2709                         } else {
2710                                 if (!enc->unsafe())
2711                                         h_inputencoding = enc->name();
2712                                 p.setEncoding(enc->iconvName());
2713                         }
2714                         continue;
2715                 }
2716
2717                 if (t.cs() == "newenvironment") {
2718                         string const name = p.getArg('{', '}');
2719                         string const opt1 = p.getFullOpt();
2720                         string const opt2 = p.getFullOpt();
2721                         string const beg = p.verbatim_item();
2722                         string const end = p.verbatim_item();
2723                         if (!in_lyx_preamble) {
2724                                 h_preamble << "\\newenvironment{" << name
2725                                            << '}' << opt1 << opt2 << '{'
2726                                            << beg << "}{" << end << '}';
2727                         }
2728                         add_known_environment(name, opt1, !opt2.empty(),
2729                                               from_utf8(beg), from_utf8(end));
2730                         continue;
2731                 }
2732
2733                 if (t.cs() == "newtheorem") {
2734                         bool star = false;
2735                         if (p.next_token().character() == '*') {
2736                                 p.get_token();
2737                                 star = true;
2738                         }
2739                         string const name = p.getArg('{', '}');
2740                         string const opt1 = p.getFullOpt();
2741                         string const opt2 = p.getFullOpt();
2742                         string const body = p.verbatim_item();
2743                         string const opt3 = p.getFullOpt();
2744                         string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2745
2746                         string const complete = cmd + "{" + name + '}' +
2747                                           opt1 + opt2 + '{' + body + '}' + opt3;
2748
2749                         add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2750
2751                         if (!in_lyx_preamble)
2752                                 h_preamble << complete;
2753                         continue;
2754                 }
2755
2756                 if (t.cs() == "def") {
2757                         string name = p.get_token().cs();
2758                         // In fact, name may be more than the name:
2759                         // In the test case of bug 8116
2760                         // name == "csname SF@gobble@opt \endcsname".
2761                         // Therefore, we need to use asInput() instead of cs().
2762                         while (p.next_token().cat() != catBegin)
2763                                 name += p.get_token().asInput();
2764                         if (!in_lyx_preamble)
2765                                 h_preamble << "\\def\\" << name << '{'
2766                                            << p.verbatim_item() << "}";
2767                         continue;
2768                 }
2769
2770                 if (t.cs() == "newcolumntype") {
2771                         string const name = p.getArg('{', '}');
2772                         trimSpaceAndEol(name);
2773                         int nargs = 0;
2774                         string opts = p.getOpt();
2775                         if (!opts.empty()) {
2776                                 istringstream is(string(opts, 1));
2777                                 is >> nargs;
2778                         }
2779                         special_columns_[name[0]] = nargs;
2780                         h_preamble << "\\newcolumntype{" << name << "}";
2781                         if (nargs)
2782                                 h_preamble << "[" << nargs << "]";
2783                         h_preamble << "{" << p.verbatim_item() << "}";
2784                         continue;
2785                 }
2786
2787                 if (t.cs() == "setcounter") {
2788                         string const name = p.getArg('{', '}');
2789                         string const content = p.getArg('{', '}');
2790                         if (name == "secnumdepth")
2791                                 h_secnumdepth = content;
2792                         else if (name == "tocdepth")
2793                                 h_tocdepth = content;
2794                         else
2795                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2796                         continue;
2797                 }
2798
2799                 if (t.cs() == "setlength") {
2800                         string const name = p.verbatim_item();
2801                         string const content = p.verbatim_item();
2802                         // the paragraphs are only not indented when \parindent is set to zero
2803                         if (name == "\\parindent" && content != "")
2804                                 h_paragraph_indentation = translate_len(content);
2805                         else if (name == "\\parskip" && isPackageUsed("parskip")) {
2806                                 if (content == "\\smallskipamount")
2807                                         h_defskip = "smallskip";
2808                                 else if (content == "\\medskipamount")
2809                                         h_defskip = "medskip";
2810                                 else if (content == "\\bigskipamount")
2811                                         h_defskip = "bigskip";
2812                                 else if (content == "\\baselineskip")
2813                                         h_defskip = "fullline";
2814                                 else
2815                                         h_defskip = translate_len(content);
2816                         } else if (name == "\\mathindent") {
2817                                 h_mathindentation = translate_len(content);
2818                         } else
2819                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2820                         continue;
2821                 }
2822
2823                 if (t.cs() == "onehalfspacing") {
2824                         h_spacing = "onehalf";
2825                         continue;
2826                 }
2827
2828                 if (t.cs() == "doublespacing") {
2829                         h_spacing = "double";
2830                         continue;
2831                 }
2832
2833                 if (t.cs() == "setstretch") {
2834                         h_spacing = "other " + p.verbatim_item();
2835                         continue;
2836                 }
2837
2838                 if (t.cs() == "synctex") {
2839                         // the scheme is \synctex=value
2840                         // where value can only be "1" or "-1"
2841                         h_output_sync = "1";
2842                         // there can be any character behind the value (e.g. a linebreak or a '\'
2843                         // therefore we extract it char by char
2844                         p.get_token();
2845                         string value = p.get_token().asInput();
2846                         if (value == "-")
2847                                 value += p.get_token().asInput();
2848                         h_output_sync_macro = "\\synctex=" + value;
2849                         continue;
2850                 }
2851
2852                 if (t.cs() == "begin") {
2853                         string const name = p.getArg('{', '}');
2854                         if (name == "document")
2855                                 break;
2856                         h_preamble << "\\begin{" << name << "}";
2857                         continue;
2858                 }
2859
2860                 if (t.cs() == "geometry") {
2861                         vector<string> opts = split_options(p.getArg('{', '}'));
2862                         handle_geometry(opts);
2863                         continue;
2864                 }
2865
2866                 if (t.cs() == "definecolor") {
2867                         string const color = p.getArg('{', '}');
2868                         string const space = p.getArg('{', '}');
2869                         string const value = p.getArg('{', '}');
2870                         if (color == "document_fontcolor" && space == "rgb") {
2871                                 RGBColor c(RGBColorFromLaTeX(value));
2872                                 h_fontcolor = X11hexname(c);
2873                         } else if (color == "note_fontcolor" && space == "rgb") {
2874                                 RGBColor c(RGBColorFromLaTeX(value));
2875                                 h_notefontcolor = X11hexname(c);
2876                         } else if (color == "page_backgroundcolor" && space == "rgb") {
2877                                 RGBColor c(RGBColorFromLaTeX(value));
2878                                 h_backgroundcolor = X11hexname(c);
2879                         } else if (color == "shadecolor" && space == "rgb") {
2880                                 RGBColor c(RGBColorFromLaTeX(value));
2881                                 h_boxbgcolor = X11hexname(c);
2882                         } else {
2883                                 h_preamble << "\\definecolor{" << color
2884                                            << "}{" << space << "}{" << value
2885                                            << '}';
2886                         }
2887                         continue;
2888                 }
2889
2890                 if (t.cs() == "bibliographystyle") {
2891                         h_biblio_style = p.verbatim_item();
2892                         continue;
2893                 }
2894
2895                 if (t.cs() == "jurabibsetup") {
2896                         // FIXME p.getArg('{', '}') is most probably wrong (it
2897                         //       does not handle nested braces).
2898                         //       Use p.verbatim_item() instead.
2899                         vector<string> jurabibsetup =
2900                                 split_options(p.getArg('{', '}'));
2901                         // add jurabibsetup to the jurabib package options
2902                         add_package("jurabib", jurabibsetup);
2903                         if (!jurabibsetup.empty()) {
2904                                 h_preamble << "\\jurabibsetup{"
2905                                            << join(jurabibsetup, ",") << '}';
2906                         }
2907                         continue;
2908                 }
2909
2910                 if (t.cs() == "hypersetup") {
2911                         vector<string> hypersetup =
2912                                 split_options(p.verbatim_item());
2913                         // add hypersetup to the hyperref package options
2914                         handle_hyperref(hypersetup);
2915                         if (!hypersetup.empty()) {
2916                                 h_preamble << "\\hypersetup{"
2917                                            << join(hypersetup, ",") << '}';
2918                         }
2919                         continue;
2920                 }
2921
2922                 if (t.cs() == "includeonly") {
2923                         vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2924                         for (auto & iofile : includeonlys) {
2925                                 string filename(normalize_filename(iofile));
2926                                 string const path = getMasterFilePath(true);
2927                                 // We want to preserve relative/absolute filenames,
2928                                 // therefore path is only used for testing
2929                                 if (!makeAbsPath(filename, path).exists()) {
2930                                         // The file extension is probably missing.
2931                                         // Now try to find it out.
2932                                         string const tex_name =
2933                                                 find_file(filename, path,
2934                                                           known_tex_extensions);
2935                                         if (!tex_name.empty())
2936                                                 filename = tex_name;
2937                                 }
2938                                 string outname;
2939                                 if (makeAbsPath(filename, path).exists())
2940                                         fix_child_filename(filename);
2941                                 else
2942                                         cerr << "Warning: Could not find included file '"
2943                                              << filename << "'." << endl;
2944                                 outname = changeExtension(filename, "lyx");
2945                                 h_includeonlys.push_back(outname);
2946                         }
2947                         continue;
2948                 }
2949
2950                 if (is_known(t.cs(), known_if_3arg_commands)) {
2951                         // prevent misparsing of \usepackage if it is used
2952                         // as an argument (see e.g. our own output of
2953                         // \@ifundefined above)
2954                         string const arg1 = p.verbatim_item();
2955                         string const arg2 = p.verbatim_item();
2956                         string const arg3 = p.verbatim_item();
2957                         // test case \@ifundefined{date}{}{\date{}}
2958                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
2959                             arg2.empty() && arg3 == "\\date{}") {
2960                                 h_suppress_date = "true";
2961                         // older tex2lyx versions did output
2962                         // \@ifundefined{definecolor}{\usepackage{color}}{}
2963                         } else if (t.cs() == "@ifundefined" &&
2964                                    arg1 == "definecolor" &&
2965                                    arg2 == "\\usepackage{color}" &&
2966                                    arg3.empty()) {
2967                                 if (!in_lyx_preamble)
2968                                         h_preamble << package_beg_sep
2969                                                    << "color"
2970                                                    << package_mid_sep
2971                                                    << "\\@ifundefined{definecolor}{color}{}"
2972                                                    << package_end_sep;
2973                         // test for case
2974                         //\@ifundefined{showcaptionsetup}{}{%
2975                         // \PassOptionsToPackage{caption=false}{subfig}}
2976                         // that LyX uses for subfloats
2977                         } else if (t.cs() == "@ifundefined" &&
2978                                    arg1 == "showcaptionsetup" && arg2.empty()
2979                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2980                                 ; // do nothing
2981                         } else if (!in_lyx_preamble) {
2982                                 h_preamble << t.asInput()
2983                                            << '{' << arg1 << '}'
2984                                            << '{' << arg2 << '}'
2985                                            << '{' << arg3 << '}';
2986                         }
2987                         continue;
2988                 }
2989
2990                 if (is_known(t.cs(), known_if_commands)) {
2991                         // must not parse anything in conditional code, since
2992                         // LyX would output the parsed contents unconditionally
2993                         if (!in_lyx_preamble)
2994                                 h_preamble << t.asInput();
2995                         handle_if(p, in_lyx_preamble);
2996                         continue;
2997                 }
2998
2999                 if (!t.cs().empty() && !in_lyx_preamble) {
3000                         h_preamble << '\\' << t.cs();
3001                         continue;
3002                 }
3003         }
3004
3005         // remove the whitespace
3006         p.skip_spaces();
3007
3008         if (h_papersides.empty()) {
3009                 ostringstream ss;
3010                 ss << tc.sides();
3011                 h_papersides = ss.str();
3012         }
3013
3014         // If the CJK package is used we cannot set the document language from
3015         // the babel options. Instead, we guess which language is used most
3016         // and set this one.
3017         default_language = h_language;
3018         if (is_full_document &&
3019             (auto_packages.find("CJK") != auto_packages.end() ||
3020              auto_packages.find("CJKutf8") != auto_packages.end())) {
3021                 p.pushPosition();
3022                 h_language = guessLanguage(p, default_language);
3023                 p.popPosition();
3024                 if (explicit_babel && h_language != default_language) {
3025                         // We set the document language to a CJK language,
3026                         // but babel is explicitly called in the user preamble
3027                         // without options. LyX will not add the default
3028                         // language to the document options if it is either
3029                         // english, or no text is set as default language.
3030                         // Therefore we need to add a language option explicitly.
3031                         // FIXME: It would be better to remove all babel calls
3032                         //        from the user preamble, but this is difficult
3033                         //        without re-introducing bug 7861.
3034                         if (h_options.empty())
3035                                 h_options = lyx2babel(default_language);
3036                         else
3037                                 h_options += ',' + lyx2babel(default_language);
3038                 }
3039         }
3040
3041         // Finally, set the quote style.
3042         // LyX knows the following quotes styles:
3043         // british, cjk, cjkangle, danish, english, french, german,
3044         // polish, russian, swedish and swiss
3045         // conversion list taken from
3046         // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
3047         // (quotes for kazakh are unknown)
3048         // british
3049         if (is_known(h_language, known_british_quotes_languages))
3050                 h_quotes_style = "british";
3051         // cjk
3052         else if (is_known(h_language, known_cjk_quotes_languages))
3053                 h_quotes_style = "cjk";
3054         // cjkangle
3055         else if (is_known(h_language, known_cjkangle_quotes_languages))
3056                 h_quotes_style = "cjkangle";
3057         // danish
3058         else if (is_known(h_language, known_danish_quotes_languages))
3059                 h_quotes_style = "danish";
3060         // french
3061         else if (is_known(h_language, known_french_quotes_languages))
3062                 h_quotes_style = "french";
3063         // german
3064         else if (is_known(h_language, known_german_quotes_languages))
3065                 h_quotes_style = "german";
3066         // polish
3067         else if (is_known(h_language, known_polish_quotes_languages))
3068                 h_quotes_style = "polish";
3069         // russian
3070         else if (is_known(h_language, known_russian_quotes_languages))
3071                 h_quotes_style = "russian";
3072         // swedish
3073         else if (is_known(h_language, known_swedish_quotes_languages))
3074                 h_quotes_style = "swedish";
3075         // swiss
3076         else if (is_known(h_language, known_swiss_quotes_languages))
3077                 h_quotes_style = "swiss";
3078         // english
3079         else if (is_known(h_language, known_english_quotes_languages))
3080                 h_quotes_style = "english";
3081 }
3082
3083
3084 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3085 {
3086         TeX2LyXDocClass dummy;
3087         parse(p, forceclass, true, dummy);
3088         if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3089                 return h_inputencoding;
3090         return "";
3091 }
3092
3093
3094 string babel2lyx(string const & language)
3095 {
3096         char const * const * where = is_known(language, known_languages);
3097         if (where)
3098                 return known_coded_languages[where - known_languages];
3099         return language;
3100 }
3101
3102
3103 string lyx2babel(string const & language)
3104 {
3105         char const * const * where = is_known(language, known_coded_languages);
3106         if (where)
3107                 return known_languages[where - known_coded_languages];
3108         return language;
3109 }
3110
3111
3112 string Preamble::polyglossia2lyx(string const & language)
3113 {
3114         char const * const * where = is_known(language, polyglossia_languages);
3115         if (where)
3116                 return coded_polyglossia_languages[where - polyglossia_languages];
3117         return language;
3118 }
3119
3120
3121 string rgbcolor2code(string const & name)
3122 {
3123         char const * const * where = is_known(name, known_basic_colors);
3124         if (where) {
3125                 // "red", "green" etc
3126                 return known_basic_color_codes[where - known_basic_colors];
3127         }
3128         // "255,0,0", "0,255,0" etc
3129         RGBColor c(RGBColorFromLaTeX(name));
3130         return X11hexname(c);
3131 }
3132
3133 // }])
3134
3135
3136 } // namespace lyx