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