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