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