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