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