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