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