]> git.lyx.org Git - features.git/blob - src/tex2lyx/Preamble.cpp
39298851d153dd68d86bd241e2f0940d1f2281c6
[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
872         // sansserif fonts
873         if (is_known(name, known_sans_fonts)) {
874                 h_font_sans[0] = name;
875                 if (options.size() >= 1) {
876                         if (scale_as_percentage(opts, h_font_sf_scale[0]))
877                                 options.clear();
878                 }
879         }
880
881         if (name == "biolinum" || name == "biolinum-type1") {
882                 h_font_sans[0] = "biolinum";
883                 // biolinum can have several options, e.g. [osf,scaled=0.97]
884                 string::size_type pos = opts.find("osf");
885                 if (pos != string::npos)
886                         h_font_osf = "true";
887         }
888
889         if (name == "PTSans") {
890                 h_font_sans[0] = "PTSans-TLF";
891                 if (options.size() >= 1) {
892                         if (scale_as_percentage(opts, h_font_sf_scale[0]))
893                                 options.clear();
894                 }
895         }
896
897         if (name == "plex-sans") {
898                 if (opts.find("condensed") != string::npos)
899                         h_font_sans[0] = "IBMPlexSansCondensed";
900                 else if (opts.find("thin") != string::npos)
901                         h_font_sans[0] = "IBMPlexSansThin";
902                 else if (opts.find("extralight") != string::npos)
903                         h_font_sans[0] = "IBMPlexSansExtraLight";
904                 else if (opts.find("light") != string::npos)
905                         h_font_sans[0] = "IBMPlexSansLight";
906                 else if (opts.find("semibold") != string::npos)
907                         h_font_sans[0] = "IBMPlexSansSemibold";
908                 else
909                         h_font_sans[0] = "IBMPlexSans";
910                 // check if the option contains scaling, if yes, extract it
911                 string::size_type pos = opts.find("scale");
912                 if (pos != string::npos) {
913                         string scale;
914                         string::size_type i = opts.find(',', pos);
915                         if (i == string::npos)
916                                 scale_as_percentage(opts.substr(pos + 1), scale);
917                         else
918                                 scale_as_percentage(opts.substr(pos, i - pos), scale);
919                         if (!scale.empty())
920                                 h_font_sf_scale[1] = scale;
921                 }
922         }
923
924         // typewriter fonts
925         if (is_known(name, known_typewriter_fonts)) {
926                 // fourier can be set as roman font _only_
927                 // fourier as typewriter is handled in handling of \ttdefault
928                 if (name != "fourier") {
929                         h_font_typewriter[0] = name;
930                         if (options.size() >= 1) {
931                                 if (scale_as_percentage(opts, h_font_tt_scale[0]))
932                                         options.clear();
933                         }
934                 }
935         }
936
937         if (name == "libertineMono" || name == "libertineMono-type1")
938                 h_font_typewriter[0] = "libertine-mono";
939
940         if (name == "PTMono") {
941                 h_font_typewriter[0] = "PTMono-TLF";
942                 if (options.size() >= 1) {
943                         if (scale_as_percentage(opts, h_font_tt_scale[0]))
944                                 options.clear();
945                 }
946         }
947
948         if (name == "plex-mono") {
949                 if (opts.find("thin") != string::npos)
950                         h_font_typewriter[0] = "IBMPlexMonoThin";
951                 else if (opts.find("extralight") != string::npos)
952                         h_font_typewriter[0] = "IBMPlexMonoExtraLight";
953                 else if (opts.find("light") != string::npos)
954                         h_font_typewriter[0] = "IBMPlexMonoLight";
955                 else if (opts.find("semibold") != string::npos)
956                         h_font_typewriter[0] = "IBMPlexMonoSemibold";
957                 else
958                         h_font_typewriter[0] = "IBMPlexMono";
959                 // check if the option contains scaling, if yes, extract it
960                 string::size_type pos = opts.find("scale");
961                 if (pos != string::npos) {
962                         string scale;
963                         string::size_type i = opts.find(',', pos);
964                         if (i == string::npos)
965                                 scale_as_percentage(opts.substr(pos + 1), scale);
966                         else
967                                 scale_as_percentage(opts.substr(pos, i - pos), scale);
968                         if (!scale.empty())
969                                 h_font_tt_scale[1] = scale;
970                 }
971         }
972
973         // font uses old-style figure
974         if (name == "eco")
975                 h_font_osf = "true";
976
977         // math fonts
978         if (is_known(name, known_math_fonts))
979                 h_font_math[0] = name;
980
981         if (name == "newtxmath") {
982                 if (opts.empty())
983                         h_font_math[0] = "newtxmath";
984                 else if (opts == "garamondx")
985                         h_font_math[0] = "garamondx-ntxm";
986                 else if (opts == "libertine")
987                         h_font_math[0] = "libertine-ntxm";
988                 else if (opts == "minion")
989                         h_font_math[0] = "minion-ntxm";
990                 else if (opts == "cochineal")
991                         h_font_math[0] = "cochineal-ntxm";
992         }
993
994         if (name == "iwona")
995                 if (opts == "math")
996                         h_font_math[0] = "iwona-math";
997
998         if (name == "kurier")
999                 if (opts == "math")
1000                         h_font_math[0] = "kurier-math";
1001
1002         // after the detection and handling of special cases, we can remove the
1003         // fonts, otherwise they would appear in the preamble, see bug #7856
1004         if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
1005                 ||      is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
1006                 ;
1007         //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1008         else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1009                  name == "esint" || name == "mhchem" || name == "mathdots" ||
1010                  name == "mathtools" || name == "stackrel" ||
1011                  name == "stmaryrd" || name == "undertilde") {
1012                 h_use_packages[name] = "2";
1013                 registerAutomaticallyLoadedPackage(name);
1014         }
1015
1016         else if (name == "babel") {
1017                 h_language_package = "default";
1018                 // One might think we would have to do nothing if babel is loaded
1019                 // without any options to prevent pollution of the preamble with this
1020                 // babel call in every roundtrip.
1021                 // But the user could have defined babel-specific things afterwards. So
1022                 // we need to keep it in the preamble to prevent cases like bug #7861.
1023                 if (!opts.empty()) {
1024                         // check if more than one option was used - used later for inputenc
1025                         if (options.begin() != options.end() - 1)
1026                                 one_language = false;
1027                         // babel takes the last language of the option of its \usepackage
1028                         // call as document language. If there is no such language option, the
1029                         // last language in the documentclass options is used.
1030                         handle_opt(options, known_languages, h_language);
1031                         // translate the babel name to a LyX name
1032                         h_language = babel2lyx(h_language);
1033                         if (h_language == "japanese") {
1034                                 // For Japanese, the encoding isn't indicated in the source
1035                                 // file, and there's really not much we can do. We could
1036                                 // 1) offer a list of possible encodings to choose from, or
1037                                 // 2) determine the encoding of the file by inspecting it.
1038                                 // For the time being, we leave the encoding alone so that
1039                                 // we don't get iconv errors when making a wrong guess, and
1040                                 // we will output a note at the top of the document
1041                                 // explaining what to do.
1042                                 Encoding const * const enc = encodings.fromIconvName(
1043                                         p.getEncoding(), Encoding::japanese, false);
1044                                 if (enc)
1045                                         h_inputencoding = enc->name();
1046                                 is_nonCJKJapanese = true;
1047                                 // in this case babel can be removed from the preamble
1048                                 registerAutomaticallyLoadedPackage("babel");
1049                         } else {
1050                                 // If babel is called with options, LyX puts them by default into the
1051                                 // document class options. This works for most languages, except
1052                                 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1053                                 // perhaps in future others.
1054                                 // Therefore keep the babel call as it is as the user might have
1055                                 // reasons for it.
1056                                 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
1057                         }
1058                         delete_opt(options, known_languages);
1059                 } else {
1060                         h_preamble << "\\usepackage{babel}\n";
1061                         explicit_babel = true;
1062                 }
1063         }
1064
1065         else if (name == "polyglossia") {
1066                 h_language_package = "default";
1067                 h_default_output_format = "pdf4";
1068                 h_use_non_tex_fonts = true;
1069                 xetex = true;
1070                 registerAutomaticallyLoadedPackage("xunicode");
1071                 if (h_inputencoding == "auto")
1072                         p.setEncoding("UTF-8");
1073         }
1074
1075         else if (name == "CJK") {
1076                 // set the encoding to "auto" because it might be set to "default" by the babel handling
1077                 // and this would not be correct for CJK
1078                 if (h_inputencoding == "default")
1079                         h_inputencoding = "auto";
1080                 registerAutomaticallyLoadedPackage("CJK");
1081         }
1082
1083         else if (name == "CJKutf8") {
1084                 h_inputencoding = "utf8-cjk";
1085                 p.setEncoding("UTF-8");
1086                 registerAutomaticallyLoadedPackage("CJKutf8");
1087         }
1088
1089         else if (name == "fontenc") {
1090                 h_fontencoding = getStringFromVector(options, ",");
1091                 options.clear();
1092         }
1093
1094         else if (name == "inputenc" || name == "luainputenc") {
1095                 // h_inputencoding is only set when there is not more than one
1096                 // inputenc option because otherwise h_inputencoding must be
1097                 // set to "auto" (the default encoding of the document language)
1098                 // Therefore check that exactly one option is passed to inputenc.
1099                 // It is also only set when there is not more than one babel
1100                 // language option.
1101                 if (!options.empty()) {
1102                         string const encoding = options.back();
1103                         Encoding const * const enc = encodings.fromLaTeXName(
1104                                 encoding, Encoding::inputenc, true);
1105                         if (!enc) {
1106                                 if (!detectEncoding)
1107                                         cerr << "Unknown encoding " << encoding
1108                                              << ". Ignoring." << std::endl;
1109                         } else {
1110                                 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1111                                         h_inputencoding = enc->name();
1112                                 p.setEncoding(enc->iconvName());
1113                         }
1114                         options.clear();
1115                 }
1116         }
1117
1118         else if (name == "srcltx") {
1119                 h_output_sync = "1";
1120                 if (!opts.empty()) {
1121                         h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1122                         options.clear();
1123                 } else
1124                         h_output_sync_macro = "\\usepackage{srcltx}";
1125         }
1126
1127         else if (is_known(name, known_old_language_packages)) {
1128                 // known language packages from the times before babel
1129                 // if they are found and not also babel, they will be used as
1130                 // custom language package
1131                 h_language_package = "\\usepackage{" + name + "}";
1132         }
1133
1134         else if (name == "lyxskak") {
1135                 // ignore this and its options
1136                 const char * const o[] = {"ps", "mover", 0};
1137                 delete_opt(options, o);
1138         }
1139
1140         else if (is_known(name, known_lyx_packages) && options.empty()) {
1141                 if (name == "splitidx")
1142                         h_use_indices = "true";
1143                 else if (name == "minted")
1144                         h_use_minted = true;
1145                 else if (name == "refstyle")
1146                         h_use_refstyle = true;
1147                 else if (name == "prettyref")
1148                         h_use_refstyle = false;
1149                 if (!in_lyx_preamble) {
1150                         h_preamble << package_beg_sep << name
1151                                    << package_mid_sep << "\\usepackage{"
1152                                    << name << '}';
1153                         if (p.next_token().cat() == catNewline ||
1154                             (p.next_token().cat() == catSpace &&
1155                              p.next_next_token().cat() == catNewline))
1156                                 h_preamble << '\n';
1157                         h_preamble << package_end_sep;
1158                 }
1159         }
1160
1161         else if (name == "geometry")
1162                 handle_geometry(options);
1163
1164         else if (name == "subfig")
1165                 ; // ignore this FIXME: Use the package separator mechanism instead
1166
1167         else if (char const * const * where = is_known(name, known_languages))
1168                 h_language = known_coded_languages[where - known_languages];
1169
1170         else if (name == "natbib") {
1171                 h_biblio_style = "plainnat";
1172                 h_cite_engine = "natbib";
1173                 h_cite_engine_type = "authoryear";
1174                 vector<string>::iterator it =
1175                         find(options.begin(), options.end(), "authoryear");
1176                 if (it != options.end())
1177                         options.erase(it);
1178                 else {
1179                         it = find(options.begin(), options.end(), "numbers");
1180                         if (it != options.end()) {
1181                                 h_cite_engine_type = "numerical";
1182                                 options.erase(it);
1183                         }
1184                 }
1185                 if (!options.empty())
1186                         h_biblio_options = join(options, ",");
1187         }
1188
1189         else if (name == "biblatex") {
1190                 h_biblio_style = "plainnat";
1191                 h_cite_engine = "biblatex";
1192                 h_cite_engine_type = "authoryear";
1193                 string opt;
1194                 vector<string>::iterator it =
1195                         find(options.begin(), options.end(), "natbib");
1196                 if (it != options.end()) {
1197                         options.erase(it);
1198                         h_cite_engine = "biblatex-natbib";
1199                 } else {
1200                         opt = process_keyval_opt(options, "natbib");
1201                         if (opt == "true")
1202                                 h_cite_engine = "biblatex-natbib";
1203                 }
1204                 opt = process_keyval_opt(options, "style");
1205                 if (!opt.empty()) {
1206                         h_biblatex_citestyle = opt;
1207                         h_biblatex_bibstyle = opt;
1208                 } else {
1209                         opt = process_keyval_opt(options, "citestyle");
1210                         if (!opt.empty())
1211                                 h_biblatex_citestyle = opt;
1212                         opt = process_keyval_opt(options, "bibstyle");
1213                         if (!opt.empty())
1214                                 h_biblatex_bibstyle = opt;
1215                 }
1216                 opt = process_keyval_opt(options, "refsection");
1217                 if (!opt.empty()) {
1218                         if (opt == "none" || opt == "part"
1219                             || opt == "chapter" || opt == "section"
1220                             || opt == "subsection")
1221                                 h_multibib = opt;
1222                         else
1223                                 cerr << "Ignoring unkown refesection value '"
1224                                      << opt << "'.";
1225                 }
1226                 opt = process_keyval_opt(options, "bibencoding");
1227                 if (!opt.empty())
1228                         bibencoding = opt;
1229                 if (!options.empty()) {
1230                         h_biblio_options = join(options, ",");
1231                         options.clear();
1232                 }
1233         }
1234
1235         else if (name == "jurabib") {
1236                 h_biblio_style = "jurabib";
1237                 h_cite_engine = "jurabib";
1238                 h_cite_engine_type = "authoryear";
1239                 if (!options.empty())
1240                         h_biblio_options = join(options, ",");
1241         }
1242
1243         else if (name == "bibtopic")
1244                 h_use_bibtopic = "true";
1245
1246         else if (name == "chapterbib")
1247                 h_multibib = "child";
1248
1249         else if (name == "hyperref")
1250                 handle_hyperref(options);
1251
1252         else if (name == "algorithm2e") {
1253                 // Load "algorithm2e" module
1254                 addModule("algorithm2e");
1255                 // Add the package options to the global document options
1256                 if (!options.empty()) {
1257                         if (h_options.empty())
1258                                 h_options = join(options, ",");
1259                         else
1260                                 h_options += ',' + join(options, ",");
1261                 }
1262         }
1263         else if (name == "microtype") {
1264                 //we internally support only microtype without params
1265                 if (options.empty())
1266                         h_use_microtype = "true";
1267                 else
1268                         h_preamble << "\\usepackage[" << opts << "]{microtype}";
1269         }
1270
1271         else if (!in_lyx_preamble) {
1272                 if (options.empty())
1273                         h_preamble << "\\usepackage{" << name << '}';
1274                 else {
1275                         h_preamble << "\\usepackage[" << opts << "]{"
1276                                    << name << '}';
1277                         options.clear();
1278                 }
1279                 if (p.next_token().cat() == catNewline ||
1280                     (p.next_token().cat() == catSpace &&
1281                      p.next_next_token().cat() == catNewline))
1282                         h_preamble << '\n';
1283         }
1284
1285         // We need to do something with the options...
1286         if (!options.empty() && !detectEncoding)
1287                 cerr << "Ignoring options '" << join(options, ",")
1288                      << "' of package " << name << '.' << endl;
1289
1290         // remove the whitespace
1291         p.skip_spaces();
1292 }
1293
1294
1295 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1296 {
1297         while (p.good()) {
1298                 Token t = p.get_token();
1299                 if (t.cat() == catEscape &&
1300                     is_known(t.cs(), known_if_commands))
1301                         handle_if(p, in_lyx_preamble);
1302                 else {
1303                         if (!in_lyx_preamble)
1304                                 h_preamble << t.asInput();
1305                         if (t.cat() == catEscape && t.cs() == "fi")
1306                                 return;
1307                 }
1308         }
1309 }
1310
1311
1312 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1313 {
1314         if (contains(h_float_placement, "H"))
1315                 registerAutomaticallyLoadedPackage("float");
1316         if (h_spacing != "single" && h_spacing != "default")
1317                 registerAutomaticallyLoadedPackage("setspace");
1318         if (h_use_packages["amsmath"] == "2") {
1319                 // amsbsy and amstext are already provided by amsmath
1320                 registerAutomaticallyLoadedPackage("amsbsy");
1321                 registerAutomaticallyLoadedPackage("amstext");
1322         }
1323
1324         // output the LyX file settings
1325         // Important: Keep the version formatting in sync with LyX and
1326         //            lyx2lyx (bug 7951)
1327         string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1328         os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1329            << lyx_version_minor << '\n'
1330            << "\\lyxformat " << LYX_FORMAT << '\n'
1331            << "\\begin_document\n"
1332            << "\\begin_header\n"
1333            << "\\save_transient_properties " << h_save_transient_properties << "\n"
1334            << "\\origin " << origin << "\n"
1335            << "\\textclass " << h_textclass << "\n";
1336         string const raw = subdoc ? empty_string() : h_preamble.str();
1337         if (!raw.empty()) {
1338                 os << "\\begin_preamble\n";
1339                 for (string::size_type i = 0; i < raw.size(); ++i) {
1340                         if (raw[i] == package_beg_sep) {
1341                                 // Here follows some package loading code that
1342                                 // must be skipped if the package is loaded
1343                                 // automatically.
1344                                 string::size_type j = raw.find(package_mid_sep, i);
1345                                 if (j == string::npos)
1346                                         return false;
1347                                 string::size_type k = raw.find(package_end_sep, j);
1348                                 if (k == string::npos)
1349                                         return false;
1350                                 string const package = raw.substr(i + 1, j - i - 1);
1351                                 string const replacement = raw.substr(j + 1, k - j - 1);
1352                                 if (auto_packages.find(package) == auto_packages.end())
1353                                         os << replacement;
1354                                 i = k;
1355                         } else
1356                                 os.put(raw[i]);
1357                 }
1358                 os << "\n\\end_preamble\n";
1359         }
1360         if (!h_options.empty())
1361                 os << "\\options " << h_options << "\n";
1362         os << "\\use_default_options " << h_use_default_options << "\n";
1363         if (!used_modules.empty()) {
1364                 os << "\\begin_modules\n";
1365                 vector<string>::const_iterator const end = used_modules.end();
1366                 vector<string>::const_iterator it = used_modules.begin();
1367                 for (; it != end; ++it)
1368                         os << *it << '\n';
1369                 os << "\\end_modules\n";
1370         }
1371         if (!h_includeonlys.empty()) {
1372                 os << "\\begin_includeonly\n";
1373                 for (auto const & iofile : h_includeonlys)
1374                         os << iofile << '\n';
1375                 os << "\\end_includeonly\n";
1376         }
1377         os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1378            << "\\language " << h_language << "\n"
1379            << "\\language_package " << h_language_package << "\n"
1380            << "\\inputencoding " << h_inputencoding << "\n"
1381            << "\\fontencoding " << h_fontencoding << "\n"
1382            << "\\font_roman \"" << h_font_roman[0]
1383            << "\" \"" << h_font_roman[1] << "\"\n"
1384            << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1385            << "\\font_typewriter \"" << h_font_typewriter[0]
1386            << "\" \"" << h_font_typewriter[1] << "\"\n"
1387            << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1388            << "\\font_default_family " << h_font_default_family << "\n"
1389            << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1390            << "\\font_sc " << h_font_sc << "\n"
1391            << "\\font_osf " << h_font_osf << "\n"
1392            << "\\font_sf_scale " << h_font_sf_scale[0]
1393            << ' ' << h_font_sf_scale[1] << '\n'
1394            << "\\font_tt_scale " << h_font_tt_scale[0]
1395            << ' ' << h_font_tt_scale[1] << '\n';
1396         if (!h_font_cjk.empty())
1397                 os << "\\font_cjk " << h_font_cjk << '\n';
1398         os << "\\use_microtype " << h_use_microtype << '\n'
1399            << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1400            << "\\graphics " << h_graphics << '\n'
1401            << "\\default_output_format " << h_default_output_format << "\n"
1402            << "\\output_sync " << h_output_sync << "\n";
1403         if (h_output_sync == "1")
1404                 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1405         os << "\\bibtex_command " << h_bibtex_command << "\n"
1406            << "\\index_command " << h_index_command << "\n";
1407         if (!h_float_placement.empty())
1408                 os << "\\float_placement " << h_float_placement << "\n";
1409         os << "\\paperfontsize " << h_paperfontsize << "\n"
1410            << "\\spacing " << h_spacing << "\n"
1411            << "\\use_hyperref " << h_use_hyperref << '\n';
1412         if (h_use_hyperref == "true") {
1413                 if (!h_pdf_title.empty())
1414                         os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1415                 if (!h_pdf_author.empty())
1416                         os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1417                 if (!h_pdf_subject.empty())
1418                         os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1419                 if (!h_pdf_keywords.empty())
1420                         os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1421                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1422                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1423                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1424                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1425                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1426                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1427                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1428                       "\\pdf_backref " << h_pdf_backref << "\n"
1429                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1430                 if (!h_pdf_pagemode.empty())
1431                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1432                 if (!h_pdf_quoted_options.empty())
1433                         os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1434         }
1435         os << "\\papersize " << h_papersize << "\n"
1436            << "\\use_geometry " << h_use_geometry << '\n';
1437         for (map<string, string>::const_iterator it = h_use_packages.begin();
1438              it != h_use_packages.end(); ++it)
1439                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1440         os << "\\cite_engine " << h_cite_engine << '\n'
1441            << "\\cite_engine_type " << h_cite_engine_type << '\n'
1442            << "\\biblio_style " << h_biblio_style << "\n"
1443            << "\\use_bibtopic " << h_use_bibtopic << "\n";
1444         if (!h_biblio_options.empty())
1445                 os << "\\biblio_options " << h_biblio_options << "\n";
1446         if (!h_biblatex_bibstyle.empty())
1447                 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1448         if (!h_biblatex_citestyle.empty())
1449                 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1450         if (!h_multibib.empty())
1451                 os << "\\multibib " << h_multibib << "\n";
1452         os << "\\use_indices " << h_use_indices << "\n"
1453            << "\\paperorientation " << h_paperorientation << '\n'
1454            << "\\suppress_date " << h_suppress_date << '\n'
1455            << "\\justification " << h_justification << '\n'
1456            << "\\use_refstyle " << h_use_refstyle << '\n'
1457            << "\\use_minted " << h_use_minted << '\n';
1458         if (!h_fontcolor.empty())
1459                 os << "\\fontcolor " << h_fontcolor << '\n';
1460         if (!h_notefontcolor.empty())
1461                 os << "\\notefontcolor " << h_notefontcolor << '\n';
1462         if (!h_backgroundcolor.empty())
1463                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1464         if (!h_boxbgcolor.empty())
1465                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1466         if (index_number != 0)
1467                 for (int i = 0; i < index_number; i++) {
1468                         os << "\\index " << h_index[i] << '\n'
1469                            << "\\shortcut " << h_shortcut[i] << '\n'
1470                            << "\\color " << h_color << '\n'
1471                            << "\\end_index\n";
1472                 }
1473         else {
1474                 os << "\\index " << h_index[0] << '\n'
1475                    << "\\shortcut " << h_shortcut[0] << '\n'
1476                    << "\\color " << h_color << '\n'
1477                    << "\\end_index\n";
1478         }
1479         os << h_margins
1480            << "\\secnumdepth " << h_secnumdepth << "\n"
1481            << "\\tocdepth " << h_tocdepth << "\n"
1482            << "\\paragraph_separation " << h_paragraph_separation << "\n";
1483         if (h_paragraph_separation == "skip")
1484                 os << "\\defskip " << h_defskip << "\n";
1485         else
1486                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1487         os << "\\is_math_indent " << h_is_mathindent << "\n";
1488         if (!h_mathindentation.empty())
1489                 os << "\\math_indentation " << h_mathindentation << "\n";
1490         os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1491         os << "\\quotes_style " << h_quotes_style << "\n"
1492            << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1493            << "\\papercolumns " << h_papercolumns << "\n"
1494            << "\\papersides " << h_papersides << "\n"
1495            << "\\paperpagestyle " << h_paperpagestyle << "\n";
1496         if (!h_listings_params.empty())
1497                 os << "\\listings_params " << h_listings_params << "\n";
1498         os << "\\tracking_changes " << h_tracking_changes << "\n"
1499            << "\\output_changes " << h_output_changes << "\n"
1500            << "\\html_math_output " << h_html_math_output << "\n"
1501            << "\\html_css_as_file " << h_html_css_as_file << "\n"
1502            << "\\html_be_strict " << h_html_be_strict << "\n"
1503            << authors_
1504            << "\\end_header\n\n"
1505            << "\\begin_body\n";
1506         return true;
1507 }
1508
1509
1510 void Preamble::parse(Parser & p, string const & forceclass,
1511                      TeX2LyXDocClass & tc)
1512 {
1513         // initialize fixed types
1514         special_columns_['D'] = 3;
1515         parse(p, forceclass, false, tc);
1516 }
1517
1518
1519 void Preamble::parse(Parser & p, string const & forceclass,
1520                      bool detectEncoding, TeX2LyXDocClass & tc)
1521 {
1522         bool is_full_document = false;
1523         bool is_lyx_file = false;
1524         bool in_lyx_preamble = false;
1525
1526         // determine whether this is a full document or a fragment for inclusion
1527         while (p.good()) {
1528                 Token const & t = p.get_token();
1529
1530                 if (t.cat() == catEscape && t.cs() == "documentclass") {
1531                         is_full_document = true;
1532                         break;
1533                 }
1534         }
1535         p.reset();
1536
1537         if (detectEncoding && !is_full_document)
1538                 return;
1539
1540         while (is_full_document && p.good()) {
1541                 if (detectEncoding && h_inputencoding != "auto" &&
1542                     h_inputencoding != "default")
1543                         return;
1544
1545                 Token const & t = p.get_token();
1546
1547 #ifdef FILEDEBUG
1548                 if (!detectEncoding)
1549                         cerr << "t: " << t << '\n';
1550 #endif
1551
1552                 //
1553                 // cat codes
1554                 //
1555                 if (!in_lyx_preamble &&
1556                     (t.cat() == catLetter ||
1557                      t.cat() == catSuper ||
1558                      t.cat() == catSub ||
1559                      t.cat() == catOther ||
1560                      t.cat() == catMath ||
1561                      t.cat() == catActive ||
1562                      t.cat() == catBegin ||
1563                      t.cat() == catEnd ||
1564                      t.cat() == catAlign ||
1565                      t.cat() == catParameter)) {
1566                         h_preamble << t.cs();
1567                         continue;
1568                 }
1569
1570                 if (!in_lyx_preamble &&
1571                     (t.cat() == catSpace || t.cat() == catNewline)) {
1572                         h_preamble << t.asInput();
1573                         continue;
1574                 }
1575
1576                 if (t.cat() == catComment) {
1577                         static regex const islyxfile("%% LyX .* created this file");
1578                         static regex const usercommands("User specified LaTeX commands");
1579
1580                         string const comment = t.asInput();
1581
1582                         // magically switch encoding default if it looks like XeLaTeX
1583                         static string const magicXeLaTeX =
1584                                 "% This document must be compiled with XeLaTeX ";
1585                         if (comment.size() > magicXeLaTeX.size()
1586                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1587                                   && h_inputencoding == "auto") {
1588                                 if (!detectEncoding)
1589                                         cerr << "XeLaTeX comment found, switching to UTF8\n";
1590                                 h_inputencoding = "utf8";
1591                         }
1592                         smatch sub;
1593                         if (regex_search(comment, sub, islyxfile)) {
1594                                 is_lyx_file = true;
1595                                 in_lyx_preamble = true;
1596                         } else if (is_lyx_file
1597                                    && regex_search(comment, sub, usercommands))
1598                                 in_lyx_preamble = false;
1599                         else if (!in_lyx_preamble)
1600                                 h_preamble << t.asInput();
1601                         continue;
1602                 }
1603
1604                 if (t.cs() == "PassOptionsToPackage") {
1605                         string const poptions = p.getArg('{', '}');
1606                         string const package = p.verbatim_item();
1607                         extra_package_options_.insert(make_pair(package, poptions));
1608                         continue;
1609                 }
1610
1611                 if (t.cs() == "pagestyle") {
1612                         h_paperpagestyle = p.verbatim_item();
1613                         continue;
1614                 }
1615
1616                 if (t.cs() == "setdefaultlanguage") {
1617                         xetex = true;
1618                         // We don't yet care about non-language variant options
1619                         // because LyX doesn't support this yet, see bug #8214
1620                         if (p.hasOpt()) {
1621                                 string langopts = p.getOpt();
1622                                 // check if the option contains a variant, if yes, extract it
1623                                 string::size_type pos_var = langopts.find("variant");
1624                                 string::size_type i = langopts.find(',', pos_var);
1625                                 string::size_type k = langopts.find('=', pos_var);
1626                                 if (pos_var != string::npos){
1627                                         string variant;
1628                                         if (i == string::npos)
1629                                                 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1630                                         else
1631                                                 variant = langopts.substr(k + 1, i - k - 1);
1632                                         h_language = variant;
1633                                 }
1634                                 p.verbatim_item();
1635                         } else
1636                                 h_language = p.verbatim_item();
1637                         //finally translate the poyglossia name to a LyX name
1638                         h_language = polyglossia2lyx(h_language);
1639                         continue;
1640                 }
1641
1642                 if (t.cs() == "setotherlanguage") {
1643                         // We don't yet care about the option because LyX doesn't
1644                         // support this yet, see bug #8214
1645                         p.hasOpt() ? p.getOpt() : string();
1646                         p.verbatim_item();
1647                         continue;
1648                 }
1649
1650                 if (t.cs() == "setmainfont") {
1651                         // we don't care about the option
1652                         p.hasOpt() ? p.getOpt() : string();
1653                         h_font_roman[1] = p.getArg('{', '}');
1654                         continue;
1655                 }
1656
1657                 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1658                         // LyX currently only supports the scale option
1659                         string scale;
1660                         if (p.hasOpt()) {
1661                                 string fontopts = p.getArg('[', ']');
1662                                 // check if the option contains a scaling, if yes, extract it
1663                                 string::size_type pos = fontopts.find("Scale");
1664                                 if (pos != string::npos) {
1665                                         string::size_type i = fontopts.find(',', pos);
1666                                         if (i == string::npos)
1667                                                 scale_as_percentage(fontopts.substr(pos + 1), scale);
1668                                         else
1669                                                 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1670                                 }
1671                         }
1672                         if (t.cs() == "setsansfont") {
1673                                 if (!scale.empty())
1674                                         h_font_sf_scale[1] = scale;
1675                                 h_font_sans[1] = p.getArg('{', '}');
1676                         } else {
1677                                 if (!scale.empty())
1678                                         h_font_tt_scale[1] = scale;
1679                                 h_font_typewriter[1] = p.getArg('{', '}');
1680                         }
1681                         continue;
1682                 }
1683
1684                 if (t.cs() == "date") {
1685                         string argument = p.getArg('{', '}');
1686                         if (argument.empty())
1687                                 h_suppress_date = "true";
1688                         else
1689                                 h_preamble << t.asInput() << '{' << argument << '}';
1690                         continue;
1691                 }
1692
1693                 if (t.cs() == "color") {
1694                         string const space =
1695                                 (p.hasOpt() ? p.getOpt() : string());
1696                         string argument = p.getArg('{', '}');
1697                         // check the case that a standard color is used
1698                         if (space.empty() && is_known(argument, known_basic_colors)) {
1699                                 h_fontcolor = rgbcolor2code(argument);
1700                                 registerAutomaticallyLoadedPackage("color");
1701                         } else if (space.empty() && argument == "document_fontcolor")
1702                                 registerAutomaticallyLoadedPackage("color");
1703                         // check the case that LyX's document_fontcolor is defined
1704                         // but not used for \color
1705                         else {
1706                                 h_preamble << t.asInput();
1707                                 if (!space.empty())
1708                                         h_preamble << space;
1709                                 h_preamble << '{' << argument << '}';
1710                                 // the color might already be set because \definecolor
1711                                 // is parsed before this
1712                                 h_fontcolor = "";
1713                         }
1714                         continue;
1715                 }
1716
1717                 if (t.cs() == "pagecolor") {
1718                         string argument = p.getArg('{', '}');
1719                         // check the case that a standard color is used
1720                         if (is_known(argument, known_basic_colors)) {
1721                                 h_backgroundcolor = rgbcolor2code(argument);
1722                         } else if (argument == "page_backgroundcolor")
1723                                 registerAutomaticallyLoadedPackage("color");
1724                         // check the case that LyX's page_backgroundcolor is defined
1725                         // but not used for \pagecolor
1726                         else {
1727                                 h_preamble << t.asInput() << '{' << argument << '}';
1728                                 // the color might already be set because \definecolor
1729                                 // is parsed before this
1730                                 h_backgroundcolor = "";
1731                         }
1732                         continue;
1733                 }
1734
1735                 if (t.cs() == "makeatletter") {
1736                         // LyX takes care of this
1737                         p.setCatcode('@', catLetter);
1738                         continue;
1739                 }
1740
1741                 if (t.cs() == "makeatother") {
1742                         // LyX takes care of this
1743                         p.setCatcode('@', catOther);
1744                         continue;
1745                 }
1746
1747                 if (t.cs() == "makeindex") {
1748                         // LyX will re-add this if a print index command is found
1749                         p.skip_spaces();
1750                         continue;
1751                 }
1752
1753                 if (t.cs() == "newindex") {
1754                         string const indexname = p.getArg('[', ']');
1755                         string const shortcut = p.verbatim_item();
1756                         if (!indexname.empty())
1757                                 h_index[index_number] = indexname;
1758                         else
1759                                 h_index[index_number] = shortcut;
1760                         h_shortcut[index_number] = shortcut;
1761                         index_number += 1;
1762                         p.skip_spaces();
1763                         continue;
1764                 }
1765
1766                 if (t.cs() == "addbibresource") {
1767                         string const options =  p.getArg('[', ']');
1768                         string const arg = removeExtension(p.getArg('{', '}'));
1769                         if (!options.empty()) {
1770                                 // check if the option contains a bibencoding, if yes, extract it
1771                                 string::size_type pos = options.find("bibencoding=");
1772                                 string encoding;
1773                                 if (pos != string::npos) {
1774                                         string::size_type i = options.find(',', pos);
1775                                         if (i == string::npos)
1776                                                 encoding = options.substr(pos + 1);
1777                                         else
1778                                                 encoding = options.substr(pos, i - pos);
1779                                         pos = encoding.find('=');
1780                                         if (pos == string::npos)
1781                                                 encoding.clear();
1782                                         else
1783                                                 encoding = encoding.substr(pos + 1);
1784                                 }
1785                                 if (!encoding.empty())
1786                                         biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
1787                         }
1788                         biblatex_bibliographies.push_back(arg);
1789                         continue;
1790                 }
1791
1792                 if (t.cs() == "bibliography") {
1793                         vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1794                         biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1795                         continue;
1796                 }
1797
1798                 if (t.cs() == "RS@ifundefined") {
1799                         string const name = p.verbatim_item();
1800                         string const body1 = p.verbatim_item();
1801                         string const body2 = p.verbatim_item();
1802                         // only non-lyxspecific stuff
1803                         if (in_lyx_preamble &&
1804                             (name == "subsecref" || name == "thmref" || name == "lemref"))
1805                                 p.skip_spaces();
1806                         else {
1807                                 ostringstream ss;
1808                                 ss << '\\' << t.cs();
1809                                 ss << '{' << name << '}'
1810                                    << '{' << body1 << '}'
1811                                    << '{' << body2 << '}';
1812                                 h_preamble << ss.str();
1813                         }
1814                         continue;
1815                 }
1816
1817                 if (t.cs() == "AtBeginDocument") {
1818                         string const name = p.verbatim_item();
1819                         // only non-lyxspecific stuff
1820                         if (in_lyx_preamble &&
1821                             (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1822                                 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1823                                 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1824                                 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1825                                 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1826                                 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1827                                 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1828                                 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1829                                 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1830                                 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1831                                 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1832                                 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1833                                 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1834                                 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1835                                 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1836                                 p.skip_spaces();
1837                         else {
1838                                 ostringstream ss;
1839                                 ss << '\\' << t.cs();
1840                                 ss << '{' << name << '}';
1841                                 h_preamble << ss.str();
1842                         }
1843                         continue;
1844                 }
1845
1846                 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1847                     || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1848                     || t.cs() == "providecommand" || t.cs() == "providecommandx"
1849                     || t.cs() == "DeclareRobustCommand"
1850                     || t.cs() == "DeclareRobustCommandx"
1851                     || t.cs() == "ProvideTextCommandDefault"
1852                     || t.cs() == "DeclareMathAccent") {
1853                         bool star = false;
1854                         if (p.next_token().character() == '*') {
1855                                 p.get_token();
1856                                 star = true;
1857                         }
1858                         string const name = p.verbatim_item();
1859                         string const opt1 = p.getFullOpt();
1860                         string const opt2 = p.getFullOpt();
1861                         string const body = p.verbatim_item();
1862                         // store the in_lyx_preamble setting
1863                         bool const was_in_lyx_preamble = in_lyx_preamble;
1864                         // font settings
1865                         if (name == "\\rmdefault")
1866                                 if (is_known(body, known_roman_fonts)) {
1867                                         h_font_roman[0] = body;
1868                                         p.skip_spaces();
1869                                         in_lyx_preamble = true;
1870                                 }
1871                         if (name == "\\sfdefault")
1872                                 if (is_known(body, known_sans_fonts)) {
1873                                         h_font_sans[0] = body;
1874                                         p.skip_spaces();
1875                                         in_lyx_preamble = true;
1876                                 }
1877                         if (name == "\\ttdefault")
1878                                 if (is_known(body, known_typewriter_fonts)) {
1879                                         h_font_typewriter[0] = body;
1880                                         p.skip_spaces();
1881                                         in_lyx_preamble = true;
1882                                 }
1883                         if (name == "\\familydefault") {
1884                                 string family = body;
1885                                 // remove leading "\"
1886                                 h_font_default_family = family.erase(0,1);
1887                                 p.skip_spaces();
1888                                 in_lyx_preamble = true;
1889                         }
1890
1891                         // remove LyX-specific definitions that are re-added by LyX
1892                         // if necessary
1893                         // \lyxline is an ancient command that is converted by tex2lyx into
1894                         // a \rule therefore remove its preamble code
1895                         if (name == "\\lyxdot" || name == "\\lyxarrow"
1896                             || name == "\\lyxline" || name == "\\LyX") {
1897                                 p.skip_spaces();
1898                                 in_lyx_preamble = true;
1899                         }
1900
1901                         // Add the command to the known commands
1902                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1903
1904                         // only non-lyxspecific stuff
1905                         if (!in_lyx_preamble) {
1906                                 ostringstream ss;
1907                                 ss << '\\' << t.cs();
1908                                 if (star)
1909                                         ss << '*';
1910                                 ss << '{' << name << '}' << opt1 << opt2
1911                                    << '{' << body << "}";
1912                                 h_preamble << ss.str();
1913 /*
1914                                 ostream & out = in_preamble ? h_preamble : os;
1915                                 out << "\\" << t.cs() << "{" << name << "}"
1916                                     << opts << "{" << body << "}";
1917 */
1918                         }
1919                         // restore the in_lyx_preamble setting
1920                         in_lyx_preamble = was_in_lyx_preamble;
1921                         continue;
1922                 }
1923
1924                 if (t.cs() == "documentclass") {
1925                         vector<string>::iterator it;
1926                         vector<string> opts = split_options(p.getArg('[', ']'));
1927                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1928                         delete_opt(opts, known_fontsizes);
1929                         // delete "pt" at the end
1930                         string::size_type i = h_paperfontsize.find("pt");
1931                         if (i != string::npos)
1932                                 h_paperfontsize.erase(i);
1933                         // The documentclass options are always parsed before the options
1934                         // of the babel call so that a language cannot overwrite the babel
1935                         // options.
1936                         handle_opt(opts, known_languages, h_language);
1937                         delete_opt(opts, known_languages);
1938
1939                         // math indentation
1940                         if ((it = find(opts.begin(), opts.end(), "fleqn"))
1941                                  != opts.end()) {
1942                                 h_is_mathindent = "1";
1943                                 opts.erase(it);
1944                         }
1945                         // formula numbering side
1946                         if ((it = find(opts.begin(), opts.end(), "leqno"))
1947                                  != opts.end()) {
1948                                 h_math_numbering_side = "left";
1949                                 opts.erase(it);
1950                         }
1951                         else if ((it = find(opts.begin(), opts.end(), "reqno"))
1952                                  != opts.end()) {
1953                                 h_math_numbering_side = "right";
1954                                 opts.erase(it);
1955                         }
1956
1957                         // paper orientation
1958                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1959                                 h_paperorientation = "landscape";
1960                                 opts.erase(it);
1961                         }
1962                         // paper sides
1963                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1964                                  != opts.end()) {
1965                                 h_papersides = "1";
1966                                 opts.erase(it);
1967                         }
1968                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1969                                  != opts.end()) {
1970                                 h_papersides = "2";
1971                                 opts.erase(it);
1972                         }
1973                         // paper columns
1974                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1975                                  != opts.end()) {
1976                                 h_papercolumns = "1";
1977                                 opts.erase(it);
1978                         }
1979                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1980                                  != opts.end()) {
1981                                 h_papercolumns = "2";
1982                                 opts.erase(it);
1983                         }
1984                         // paper sizes
1985                         // some size options are known to any document classes, other sizes
1986                         // are handled by the \geometry command of the geometry package
1987                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1988                         delete_opt(opts, known_class_paper_sizes);
1989                         // the remaining options
1990                         h_options = join(opts, ",");
1991                         // FIXME This does not work for classes that have a
1992                         //       different name in LyX than in LaTeX
1993                         h_textclass = p.getArg('{', '}');
1994                         p.skip_spaces();
1995                         continue;
1996                 }
1997
1998                 if (t.cs() == "usepackage") {
1999                         string const options = p.getArg('[', ']');
2000                         string const name = p.getArg('{', '}');
2001                         vector<string> vecnames;
2002                         split(name, vecnames, ',');
2003                         vector<string>::const_iterator it  = vecnames.begin();
2004                         vector<string>::const_iterator end = vecnames.end();
2005                         for (; it != end; ++it)
2006                                 handle_package(p, trimSpaceAndEol(*it), options,
2007                                                in_lyx_preamble, detectEncoding);
2008                         continue;
2009                 }
2010
2011                 if (t.cs() == "inputencoding") {
2012                         string const encoding = p.getArg('{','}');
2013                         Encoding const * const enc = encodings.fromLaTeXName(
2014                                 encoding, Encoding::inputenc, true);
2015                         if (!enc) {
2016                                 if (!detectEncoding)
2017                                         cerr << "Unknown encoding " << encoding
2018                                              << ". Ignoring." << std::endl;
2019                         } else {
2020                                 if (!enc->unsafe())
2021                                         h_inputencoding = enc->name();
2022                                 p.setEncoding(enc->iconvName());
2023                         }
2024                         continue;
2025                 }
2026
2027                 if (t.cs() == "newenvironment") {
2028                         string const name = p.getArg('{', '}');
2029                         string const opt1 = p.getFullOpt();
2030                         string const opt2 = p.getFullOpt();
2031                         string const beg = p.verbatim_item();
2032                         string const end = p.verbatim_item();
2033                         if (!in_lyx_preamble) {
2034                                 h_preamble << "\\newenvironment{" << name
2035                                            << '}' << opt1 << opt2 << '{'
2036                                            << beg << "}{" << end << '}';
2037                         }
2038                         add_known_environment(name, opt1, !opt2.empty(),
2039                                               from_utf8(beg), from_utf8(end));
2040                         continue;
2041                 }
2042
2043                 if (t.cs() == "newtheorem") {
2044                         bool star = false;
2045                         if (p.next_token().character() == '*') {
2046                                 p.get_token();
2047                                 star = true;
2048                         }
2049                         string const name = p.getArg('{', '}');
2050                         string const opt1 = p.getFullOpt();
2051                         string const opt2 = p.getFullOpt();
2052                         string const body = p.verbatim_item();
2053                         string const opt3 = p.getFullOpt();
2054                         string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2055
2056                         string const complete = cmd + "{" + name + '}' +
2057                                           opt1 + opt2 + '{' + body + '}' + opt3;
2058
2059                         add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2060
2061                         if (!in_lyx_preamble)
2062                                 h_preamble << complete;
2063                         continue;
2064                 }
2065
2066                 if (t.cs() == "def") {
2067                         string name = p.get_token().cs();
2068                         // In fact, name may be more than the name:
2069                         // In the test case of bug 8116
2070                         // name == "csname SF@gobble@opt \endcsname".
2071                         // Therefore, we need to use asInput() instead of cs().
2072                         while (p.next_token().cat() != catBegin)
2073                                 name += p.get_token().asInput();
2074                         if (!in_lyx_preamble)
2075                                 h_preamble << "\\def\\" << name << '{'
2076                                            << p.verbatim_item() << "}";
2077                         continue;
2078                 }
2079
2080                 if (t.cs() == "newcolumntype") {
2081                         string const name = p.getArg('{', '}');
2082                         trimSpaceAndEol(name);
2083                         int nargs = 0;
2084                         string opts = p.getOpt();
2085                         if (!opts.empty()) {
2086                                 istringstream is(string(opts, 1));
2087                                 is >> nargs;
2088                         }
2089                         special_columns_[name[0]] = nargs;
2090                         h_preamble << "\\newcolumntype{" << name << "}";
2091                         if (nargs)
2092                                 h_preamble << "[" << nargs << "]";
2093                         h_preamble << "{" << p.verbatim_item() << "}";
2094                         continue;
2095                 }
2096
2097                 if (t.cs() == "setcounter") {
2098                         string const name = p.getArg('{', '}');
2099                         string const content = p.getArg('{', '}');
2100                         if (name == "secnumdepth")
2101                                 h_secnumdepth = content;
2102                         else if (name == "tocdepth")
2103                                 h_tocdepth = content;
2104                         else
2105                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2106                         continue;
2107                 }
2108
2109                 if (t.cs() == "setlength") {
2110                         string const name = p.verbatim_item();
2111                         string const content = p.verbatim_item();
2112                         // the paragraphs are only not indented when \parindent is set to zero
2113                         if (name == "\\parindent" && content != "") {
2114                                 if (content[0] == '0')
2115                                         h_paragraph_separation = "skip";
2116                                 else
2117                                         h_paragraph_indentation = translate_len(content);
2118                         } else if (name == "\\parskip") {
2119                                 if (content == "\\smallskipamount")
2120                                         h_defskip = "smallskip";
2121                                 else if (content == "\\medskipamount")
2122                                         h_defskip = "medskip";
2123                                 else if (content == "\\bigskipamount")
2124                                         h_defskip = "bigskip";
2125                                 else
2126                                         h_defskip = translate_len(content);
2127                         } else if (name == "\\mathindent") {
2128                                 h_mathindentation = translate_len(content);
2129                         } else
2130                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2131                         continue;
2132                 }
2133
2134                 if (t.cs() == "onehalfspacing") {
2135                         h_spacing = "onehalf";
2136                         continue;
2137                 }
2138
2139                 if (t.cs() == "doublespacing") {
2140                         h_spacing = "double";
2141                         continue;
2142                 }
2143
2144                 if (t.cs() == "setstretch") {
2145                         h_spacing = "other " + p.verbatim_item();
2146                         continue;
2147                 }
2148
2149                 if (t.cs() == "synctex") {
2150                         // the scheme is \synctex=value
2151                         // where value can only be "1" or "-1"
2152                         h_output_sync = "1";
2153                         // there can be any character behind the value (e.g. a linebreak or a '\'
2154                         // therefore we extract it char by char
2155                         p.get_token();
2156                         string value = p.get_token().asInput();
2157                         if (value == "-")
2158                                 value += p.get_token().asInput();
2159                         h_output_sync_macro = "\\synctex=" + value;
2160                         continue;
2161                 }
2162
2163                 if (t.cs() == "begin") {
2164                         string const name = p.getArg('{', '}');
2165                         if (name == "document")
2166                                 break;
2167                         h_preamble << "\\begin{" << name << "}";
2168                         continue;
2169                 }
2170
2171                 if (t.cs() == "geometry") {
2172                         vector<string> opts = split_options(p.getArg('{', '}'));
2173                         handle_geometry(opts);
2174                         continue;
2175                 }
2176
2177                 if (t.cs() == "definecolor") {
2178                         string const color = p.getArg('{', '}');
2179                         string const space = p.getArg('{', '}');
2180                         string const value = p.getArg('{', '}');
2181                         if (color == "document_fontcolor" && space == "rgb") {
2182                                 RGBColor c(RGBColorFromLaTeX(value));
2183                                 h_fontcolor = X11hexname(c);
2184                         } else if (color == "note_fontcolor" && space == "rgb") {
2185                                 RGBColor c(RGBColorFromLaTeX(value));
2186                                 h_notefontcolor = X11hexname(c);
2187                         } else if (color == "page_backgroundcolor" && space == "rgb") {
2188                                 RGBColor c(RGBColorFromLaTeX(value));
2189                                 h_backgroundcolor = X11hexname(c);
2190                         } else if (color == "shadecolor" && space == "rgb") {
2191                                 RGBColor c(RGBColorFromLaTeX(value));
2192                                 h_boxbgcolor = X11hexname(c);
2193                         } else {
2194                                 h_preamble << "\\definecolor{" << color
2195                                            << "}{" << space << "}{" << value
2196                                            << '}';
2197                         }
2198                         continue;
2199                 }
2200
2201                 if (t.cs() == "bibliographystyle") {
2202                         h_biblio_style = p.verbatim_item();
2203                         continue;
2204                 }
2205
2206                 if (t.cs() == "jurabibsetup") {
2207                         // FIXME p.getArg('{', '}') is most probably wrong (it
2208                         //       does not handle nested braces).
2209                         //       Use p.verbatim_item() instead.
2210                         vector<string> jurabibsetup =
2211                                 split_options(p.getArg('{', '}'));
2212                         // add jurabibsetup to the jurabib package options
2213                         add_package("jurabib", jurabibsetup);
2214                         if (!jurabibsetup.empty()) {
2215                                 h_preamble << "\\jurabibsetup{"
2216                                            << join(jurabibsetup, ",") << '}';
2217                         }
2218                         continue;
2219                 }
2220
2221                 if (t.cs() == "hypersetup") {
2222                         vector<string> hypersetup =
2223                                 split_options(p.verbatim_item());
2224                         // add hypersetup to the hyperref package options
2225                         handle_hyperref(hypersetup);
2226                         if (!hypersetup.empty()) {
2227                                 h_preamble << "\\hypersetup{"
2228                                            << join(hypersetup, ",") << '}';
2229                         }
2230                         continue;
2231                 }
2232
2233                 if (t.cs() == "includeonly") {
2234                         vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2235                         for (auto & iofile : includeonlys) {
2236                                 string filename(normalize_filename(iofile));
2237                                 string const path = getMasterFilePath(true);
2238                                 // We want to preserve relative/absolute filenames,
2239                                 // therefore path is only used for testing
2240                                 if (!makeAbsPath(filename, path).exists()) {
2241                                         // The file extension is probably missing.
2242                                         // Now try to find it out.
2243                                         string const tex_name =
2244                                                 find_file(filename, path,
2245                                                           known_tex_extensions);
2246                                         if (!tex_name.empty())
2247                                                 filename = tex_name;
2248                                 }
2249                                 string outname;
2250                                 if (makeAbsPath(filename, path).exists())
2251                                         fix_child_filename(filename);
2252                                 else
2253                                         cerr << "Warning: Could not find included file '"
2254                                              << filename << "'." << endl;
2255                                 outname = changeExtension(filename, "lyx");
2256                                 h_includeonlys.push_back(outname);
2257                         }
2258                         continue;
2259                 }
2260
2261                 if (is_known(t.cs(), known_if_3arg_commands)) {
2262                         // prevent misparsing of \usepackage if it is used
2263                         // as an argument (see e.g. our own output of
2264                         // \@ifundefined above)
2265                         string const arg1 = p.verbatim_item();
2266                         string const arg2 = p.verbatim_item();
2267                         string const arg3 = p.verbatim_item();
2268                         // test case \@ifundefined{date}{}{\date{}}
2269                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
2270                             arg2.empty() && arg3 == "\\date{}") {
2271                                 h_suppress_date = "true";
2272                         // older tex2lyx versions did output
2273                         // \@ifundefined{definecolor}{\usepackage{color}}{}
2274                         } else if (t.cs() == "@ifundefined" &&
2275                                    arg1 == "definecolor" &&
2276                                    arg2 == "\\usepackage{color}" &&
2277                                    arg3.empty()) {
2278                                 if (!in_lyx_preamble)
2279                                         h_preamble << package_beg_sep
2280                                                    << "color"
2281                                                    << package_mid_sep
2282                                                    << "\\@ifundefined{definecolor}{color}{}"
2283                                                    << package_end_sep;
2284                         // test for case
2285                         //\@ifundefined{showcaptionsetup}{}{%
2286                         // \PassOptionsToPackage{caption=false}{subfig}}
2287                         // that LyX uses for subfloats
2288                         } else if (t.cs() == "@ifundefined" &&
2289                                    arg1 == "showcaptionsetup" && arg2.empty()
2290                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2291                                 ; // do nothing
2292                         } else if (!in_lyx_preamble) {
2293                                 h_preamble << t.asInput()
2294                                            << '{' << arg1 << '}'
2295                                            << '{' << arg2 << '}'
2296                                            << '{' << arg3 << '}';
2297                         }
2298                         continue;
2299                 }
2300
2301                 if (is_known(t.cs(), known_if_commands)) {
2302                         // must not parse anything in conditional code, since
2303                         // LyX would output the parsed contents unconditionally
2304                         if (!in_lyx_preamble)
2305                                 h_preamble << t.asInput();
2306                         handle_if(p, in_lyx_preamble);
2307                         continue;
2308                 }
2309
2310                 if (!t.cs().empty() && !in_lyx_preamble) {
2311                         h_preamble << '\\' << t.cs();
2312                         continue;
2313                 }
2314         }
2315
2316         // remove the whitespace
2317         p.skip_spaces();
2318
2319         // Force textclass if the user wanted it
2320         if (!forceclass.empty())
2321                 h_textclass = forceclass;
2322         tc.setName(h_textclass);
2323         if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2324                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2325                 exit(EXIT_FAILURE);
2326         }
2327         if (h_papersides.empty()) {
2328                 ostringstream ss;
2329                 ss << tc.sides();
2330                 h_papersides = ss.str();
2331         }
2332
2333         // If the CJK package is used we cannot set the document language from
2334         // the babel options. Instead, we guess which language is used most
2335         // and set this one.
2336         default_language = h_language;
2337         if (is_full_document &&
2338             (auto_packages.find("CJK") != auto_packages.end() ||
2339              auto_packages.find("CJKutf8") != auto_packages.end())) {
2340                 p.pushPosition();
2341                 h_language = guessLanguage(p, default_language);
2342                 p.popPosition();
2343                 if (explicit_babel && h_language != default_language) {
2344                         // We set the document language to a CJK language,
2345                         // but babel is explicitly called in the user preamble
2346                         // without options. LyX will not add the default
2347                         // language to the document options if it is either
2348                         // english, or no text is set as default language.
2349                         // Therefore we need to add a language option explicitly.
2350                         // FIXME: It would be better to remove all babel calls
2351                         //        from the user preamble, but this is difficult
2352                         //        without re-introducing bug 7861.
2353                         if (h_options.empty())
2354                                 h_options = lyx2babel(default_language);
2355                         else
2356                                 h_options += ',' + lyx2babel(default_language);
2357                 }
2358         }
2359
2360         // Finally, set the quote style.
2361         // LyX knows the following quotes styles:
2362         // british, cjk, cjkangle, danish, english, french, german,
2363         // polish, russian, swedish and swiss
2364         // conversion list taken from
2365         // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2366         // (quotes for kazakh are unknown)
2367         // british
2368         if (is_known(h_language, known_british_quotes_languages))
2369                 h_quotes_style = "british";
2370         // cjk
2371         else if (is_known(h_language, known_cjk_quotes_languages))
2372                 h_quotes_style = "cjk";
2373         // cjkangle
2374         else if (is_known(h_language, known_cjkangle_quotes_languages))
2375                 h_quotes_style = "cjkangle";
2376         // danish
2377         else if (is_known(h_language, known_danish_quotes_languages))
2378                 h_quotes_style = "danish";
2379         // french
2380         else if (is_known(h_language, known_french_quotes_languages))
2381                 h_quotes_style = "french";
2382         // german
2383         else if (is_known(h_language, known_german_quotes_languages))
2384                 h_quotes_style = "german";
2385         // polish
2386         else if (is_known(h_language, known_polish_quotes_languages))
2387                 h_quotes_style = "polish";
2388         // russian
2389         else if (is_known(h_language, known_russian_quotes_languages))
2390                 h_quotes_style = "russian";
2391         // swedish
2392         else if (is_known(h_language, known_swedish_quotes_languages))
2393                 h_quotes_style = "swedish";
2394         // swiss
2395         else if (is_known(h_language, known_swiss_quotes_languages))
2396                 h_quotes_style = "swiss";
2397         // english
2398         else if (is_known(h_language, known_english_quotes_languages))
2399                 h_quotes_style = "english";
2400 }
2401
2402
2403 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2404 {
2405         TeX2LyXDocClass dummy;
2406         parse(p, forceclass, true, dummy);
2407         if (h_inputencoding != "auto" && h_inputencoding != "default")
2408                 return h_inputencoding;
2409         return "";
2410 }
2411
2412
2413 string babel2lyx(string const & language)
2414 {
2415         char const * const * where = is_known(language, known_languages);
2416         if (where)
2417                 return known_coded_languages[where - known_languages];
2418         return language;
2419 }
2420
2421
2422 string lyx2babel(string const & language)
2423 {
2424         char const * const * where = is_known(language, known_coded_languages);
2425         if (where)
2426                 return known_languages[where - known_coded_languages];
2427         return language;
2428 }
2429
2430
2431 string Preamble::polyglossia2lyx(string const & language)
2432 {
2433         char const * const * where = is_known(language, polyglossia_languages);
2434         if (where)
2435                 return coded_polyglossia_languages[where - polyglossia_languages];
2436         return language;
2437 }
2438
2439
2440 string rgbcolor2code(string const & name)
2441 {
2442         char const * const * where = is_known(name, known_basic_colors);
2443         if (where) {
2444                 // "red", "green" etc
2445                 return known_basic_color_codes[where - known_basic_colors];
2446         }
2447         // "255,0,0", "0,255,0" etc
2448         RGBColor c(RGBColorFromLaTeX(name));
2449         return X11hexname(c);
2450 }
2451
2452 // }])
2453
2454
2455 } // namespace lyx