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