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