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