]> git.lyx.org Git - features.git/blob - src/tex2lyx/Preamble.cpp
tex2lyx: properly set textclass if no class is given
[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", "CrimsonPro", "DejaVuSerif",
146 "DejaVuSerifCondensed", "fourier", "garamondx", "libertine", "libertineRoman", "libertine-type1",
147 "lmodern", "mathdesign", "mathpazo", "mathptmx", "MinionPro", "newcent", "noto", "noto-serif",
148 "PTSerif", "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 };
149
150 const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum",
151 "biolinum-type1", "cantarell", "Chivo", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "FiraSans", "helvet", "iwona",
152 "iwonac", "iwonal", "iwonalc", "kurier", "kurierc", "kurierl", "kurierlc", "LibertinusSans-LF", "lmss", "noto-sans", "PTSans",
153 "tgadventor", "tgheros", "uop", 0 };
154
155 const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt", "courier", "DejaVuSansMono",
156 "FiraMono", "lmtt", "luximono", "libertineMono", "libertineMono-type1", "LibertinusMono-TLF", "lmodern",
157 "mathpazo", "mathptmx", "newcent", "noto-mono", "PTMono", "tgcursor", "txtt", 0 };
158
159 const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0};
160
161 const char * const known_latex_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_paper_margins[] = { "lmargin", "tmargin", "rmargin",
168 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
169
170 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
171 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
172 "columnsep", 0};
173
174 /// commands that can start an \if...\else...\endif sequence
175 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
176 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
177 "ifsidecap", "ifupgreek", 0};
178
179 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
180         "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
181         "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
182
183 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
184         "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
185         "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
186
187 /// conditional commands with three arguments like \@ifundefined{}{}{}
188 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
189 0};
190
191 /*!
192  * Known file extensions for TeX files as used by \\includeonly
193  */
194 char const * const known_tex_extensions[] = {"tex", 0};
195
196 /// packages that work only in xetex
197 /// polyglossia is handled separately
198 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
199 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
200 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
201
202 /// packages that are automatically skipped if loaded by LyX
203 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
204 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
205 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
206 "makeidx", "minted", "multirow", "nomencl", "parskip", "pdfpages", "prettyref", "refstyle",
207 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa",
208 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular",
209 "xunicode", 0};
210
211 // codes used to remove packages that are loaded automatically by LyX.
212 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
213 const char package_beg_sep = '\001';
214 const char package_mid_sep = '\002';
215 const char package_end_sep = '\003';
216
217
218 // returns true if at least one of the options in what has been found
219 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
220 {
221         if (opts.empty())
222                 return false;
223
224         bool found = false;
225         // the last language option is the document language (for babel and LyX)
226         // the last size option is the document font size
227         vector<string>::iterator it;
228         vector<string>::iterator position = opts.begin();
229         for (; *what; ++what) {
230                 it = find(opts.begin(), opts.end(), *what);
231                 if (it != opts.end()) {
232                         if (it >= position) {
233                                 found = true;
234                                 target = *what;
235                                 position = it;
236                         }
237                 }
238         }
239         return found;
240 }
241
242
243 void delete_opt(vector<string> & opts, char const * const * what)
244 {
245         if (opts.empty())
246                 return;
247
248         // remove found options from the list
249         // do this after handle_opt to avoid potential memory leaks
250         vector<string>::iterator it;
251         for (; *what; ++what) {
252                 it = find(opts.begin(), opts.end(), *what);
253                 if (it != opts.end())
254                         opts.erase(it);
255         }
256 }
257
258
259 /*!
260  * Split a package options string (keyval format) into a vector.
261  * Example input:
262  *   authorformat=smallcaps,
263  *   commabeforerest,
264  *   titleformat=colonsep,
265  *   bibformat={tabular,ibidem,numbered}
266  */
267 vector<string> split_options(string const & input)
268 {
269         vector<string> options;
270         string option;
271         Parser p(input);
272         while (p.good()) {
273                 Token const & t = p.get_token();
274                 if (t.asInput() == ",") {
275                         options.push_back(trimSpaceAndEol(option));
276                         option.erase();
277                 } else if (t.asInput() == "=") {
278                         option += '=';
279                         p.skip_spaces(true);
280                         if (p.next_token().asInput() == "{")
281                                 option += '{' + p.getArg('{', '}') + '}';
282                 } else if (t.cat() != catSpace && t.cat() != catComment)
283                         option += t.asInput();
284         }
285
286         if (!option.empty())
287                 options.push_back(trimSpaceAndEol(option));
288
289         return options;
290 }
291
292
293 /*!
294  * Retrieve a keyval option "name={value with=sign}" named \p name from
295  * \p options and return the value.
296  * The found option is also removed from \p options.
297  */
298 string process_keyval_opt(vector<string> & options, string name)
299 {
300         for (size_t i = 0; i < options.size(); ++i) {
301                 vector<string> option;
302                 split(options[i], option, '=');
303                 if (option.size() < 2)
304                         continue;
305                 if (option[0] == name) {
306                         options.erase(options.begin() + i);
307                         option.erase(option.begin());
308                         return join(option, "=");
309                 }
310         }
311         return "";
312 }
313
314 } // anonymous namespace
315
316
317 /**
318  * known polyglossia language names (including variants)
319  * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
320  */
321 const char * const Preamble::polyglossia_languages[] = {
322 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
323 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
324 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
325 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
326 "galician", "greek", "monotonic", "hebrew", "hindi",
327 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
328 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
329 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian",
330 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
331 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
332 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
333 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
334 // not yet supported by LyX: "korean", "nko"
335
336 /**
337  * the same as polyglossia_languages with .lyx names
338  * please keep this in sync with polyglossia_languages line by line!
339  */
340 const char * const Preamble::coded_polyglossia_languages[] = {
341 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
342 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
343 "catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
344 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
345 "galician", "greek", "greek", "hebrew", "hindi",
346 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean",
347 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
348 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian",
349 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
350 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
351 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
352 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
353 // not yet supported by LyX: "korean-polyglossia", "nko"
354
355
356 bool Preamble::usePolyglossia() const
357 {
358         return h_use_non_tex_fonts && h_language_package == "default";
359 }
360
361
362 bool Preamble::indentParagraphs() const
363 {
364         return h_paragraph_separation == "indent";
365 }
366
367
368 bool Preamble::isPackageUsed(string const & package) const
369 {
370         return used_packages.find(package) != used_packages.end();
371 }
372
373
374 bool Preamble::isPackageAutoLoaded(string const & package) const
375 {
376         return auto_packages.find(package) != auto_packages.end();
377 }
378
379
380 vector<string> Preamble::getPackageOptions(string const & package) const
381 {
382         map<string, vector<string> >::const_iterator it = used_packages.find(package);
383         if (it != used_packages.end())
384                 return it->second;
385         return vector<string>();
386 }
387
388
389 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
390 {
391         auto_packages.insert(package);
392 }
393
394
395 void Preamble::addModule(string const & module)
396 {
397         for (auto const & m : used_modules) {
398                 if (m == module)
399                         return;
400         }
401         used_modules.push_back(module);
402 }
403
404
405 void Preamble::suppressDate(bool suppress)
406 {
407         if (suppress)
408                 h_suppress_date = "true";
409         else
410                 h_suppress_date = "false";
411 }
412
413
414 void Preamble::registerAuthor(std::string const & name, string const & initials)
415 {
416         Author author(from_utf8(name), empty_docstring(), from_utf8(initials));
417         author.setUsed(true);
418         authors_.record(author);
419         h_tracking_changes = "true";
420         h_output_changes = "true";
421 }
422
423
424 Author const & Preamble::getAuthor(std::string const & name) const
425 {
426         Author author(from_utf8(name), empty_docstring(), empty_docstring());
427         for (AuthorList::Authors::const_iterator it = authors_.begin();
428              it != authors_.end(); ++it)
429                 if (*it == author)
430                         return *it;
431         static Author const dummy;
432         return dummy;
433 }
434
435
436 int Preamble::getSpecialTableColumnArguments(char c) const
437 {
438         map<char, int>::const_iterator it = special_columns_.find(c);
439         if (it == special_columns_.end())
440                 return -1;
441         return it->second;
442 }
443
444
445 void Preamble::add_package(string const & name, vector<string> & options)
446 {
447         // every package inherits the global options
448         if (used_packages.find(name) == used_packages.end())
449                 used_packages[name] = split_options(h_options);
450
451         // Insert options passed via PassOptionsToPackage
452         for (auto const & p : extra_package_options_) {
453                 if (p.first == name) {
454                         vector<string> eo = getVectorFromString(p.second);
455                         for (auto const & eoi : eo)
456                                 options.push_back(eoi);
457                 }
458
459         }
460         vector<string> & v = used_packages[name];
461         v.insert(v.end(), options.begin(), options.end());
462         if (name == "jurabib") {
463                 // Don't output the order argument (see the cite command
464                 // handling code in text.cpp).
465                 vector<string>::iterator end =
466                         remove(options.begin(), options.end(), "natbiborder");
467                 end = remove(options.begin(), end, "jurabiborder");
468                 options.erase(end, options.end());
469         }
470 }
471
472
473 namespace {
474
475 // Given is a string like "scaled=0.9" or "scale=0.9", return 0.9 * 100
476 bool scale_as_percentage(string const & scale, string & percentage)
477 {
478         if (contains(scale, '=')) {
479                 string const value = support::split(scale, '=');
480                 if (isStrDbl(value)) {
481                         percentage = convert<string>(
482                                 static_cast<int>(100 * convert<double>(value)));
483                         return true;
484                 }
485         }
486         return false;
487 }
488
489
490 string remove_braces(string const & value)
491 {
492         if (value.empty())
493                 return value;
494         if (value[0] == '{' && value[value.length()-1] == '}')
495                 return value.substr(1, value.length()-2);
496         return value;
497 }
498
499 } // anonymous namespace
500
501
502 Preamble::Preamble() : one_language(true), explicit_babel(false),
503         title_layout_found(false), index_number(0), h_font_cjk_set(false)
504 {
505         //h_backgroundcolor;
506         //h_boxbgcolor;
507         h_biblio_style            = "plain";
508         h_bibtex_command          = "default";
509         h_cite_engine             = "basic";
510         h_cite_engine_type        = "default";
511         h_color                   = "#008000";
512         h_defskip                 = "medskip";
513         h_dynamic_quotes          = false;
514         //h_float_placement;
515         //h_fontcolor;
516         h_fontencoding            = "default";
517         h_font_roman[0]           = "default";
518         h_font_roman[1]           = "default";
519         h_font_sans[0]            = "default";
520         h_font_sans[1]            = "default";
521         h_font_typewriter[0]      = "default";
522         h_font_typewriter[1]      = "default";
523         h_font_math[0]            = "auto";
524         h_font_math[1]            = "auto";
525         h_font_default_family     = "default";
526         h_use_non_tex_fonts       = false;
527         h_font_sc                 = "false";
528         h_font_roman_osf          = "false";
529         h_font_sans_osf           = "false";
530         h_font_typewriter_osf     = "false";
531         h_font_sf_scale[0]        = "100";
532         h_font_sf_scale[1]        = "100";
533         h_font_tt_scale[0]        = "100";
534         h_font_tt_scale[1]        = "100";
535         // h_font_roman_opts;
536         // h_font_sans_opts;
537         // h_font_typewriter_opts;
538         //h_font_cjk
539         h_is_mathindent           = "0";
540         h_math_numbering_side     = "default";
541         h_graphics                = "default";
542         h_default_output_format   = "default";
543         h_html_be_strict          = "false";
544         h_html_css_as_file        = "0";
545         h_html_math_output        = "0";
546         h_docbook_table_output    = "0";
547         h_index[0]                = "Index";
548         h_index_command           = "default";
549         h_inputencoding           = "auto-legacy";
550         h_justification           = "true";
551         h_language                = "english";
552         h_language_package        = "none";
553         //h_listings_params;
554         h_maintain_unincluded_children = "no";
555         //h_margins;
556         //h_notefontcolor;
557         //h_options;
558         h_output_changes          = "false";
559         h_change_bars             = "false";
560         h_output_sync             = "0";
561         //h_output_sync_macro
562         h_papercolumns            = "1";
563         h_paperfontsize           = "default";
564         h_paperorientation        = "portrait";
565         h_paperpagestyle          = "default";
566         //h_papersides;
567         h_papersize               = "default";
568         h_paragraph_indentation   = "default";
569         h_paragraph_separation    = "indent";
570         //h_pdf_title;
571         //h_pdf_author;
572         //h_pdf_subject;
573         //h_pdf_keywords;
574         h_pdf_bookmarks           = "0";
575         h_pdf_bookmarksnumbered   = "0";
576         h_pdf_bookmarksopen       = "0";
577         h_pdf_bookmarksopenlevel  = "1";
578         h_pdf_breaklinks          = "0";
579         h_pdf_pdfborder           = "0";
580         h_pdf_colorlinks          = "0";
581         h_pdf_backref             = "section";
582         h_pdf_pdfusetitle         = "0";
583         //h_pdf_pagemode;
584         //h_pdf_quoted_options;
585         h_quotes_style         = "english";
586         h_secnumdepth             = "3";
587         h_shortcut[0]             = "idx";
588         h_spacing                 = "single";
589         h_save_transient_properties = "true";
590         h_suppress_date           = "false";
591         h_textclass               = "article";
592         h_tocdepth                = "3";
593         h_tracking_changes        = "false";
594         h_use_bibtopic            = "false";
595         h_use_dash_ligatures      = "true";
596         h_use_indices             = "false";
597         h_use_geometry            = "false";
598         h_use_default_options     = "false";
599         h_use_hyperref            = "false";
600         h_use_microtype           = "false";
601         h_use_lineno              = "false";
602         h_use_refstyle            = false;
603         h_use_minted              = false;
604         h_use_packages["amsmath"]    = "1";
605         h_use_packages["amssymb"]    = "0";
606         h_use_packages["cancel"]     = "0";
607         h_use_packages["esint"]      = "1";
608         h_use_packages["mhchem"]     = "0";
609         h_use_packages["mathdots"]   = "0";
610         h_use_packages["mathtools"]  = "0";
611         h_use_packages["stackrel"]   = "0";
612         h_use_packages["stmaryrd"]   = "0";
613         h_use_packages["undertilde"] = "0";
614 }
615
616
617 void Preamble::handle_hyperref(vector<string> & options)
618 {
619         // FIXME swallow inputencoding changes that might surround the
620         //       hyperref setup if it was written by LyX
621         h_use_hyperref = "true";
622         // swallow "unicode=true", since LyX does always write that
623         vector<string>::iterator it =
624                 find(options.begin(), options.end(), "unicode=true");
625         if (it != options.end())
626                 options.erase(it);
627         it = find(options.begin(), options.end(), "pdfusetitle");
628         if (it != options.end()) {
629                 h_pdf_pdfusetitle = "1";
630                 options.erase(it);
631         }
632         string bookmarks = process_keyval_opt(options, "bookmarks");
633         if (bookmarks == "true")
634                 h_pdf_bookmarks = "1";
635         else if (bookmarks == "false")
636                 h_pdf_bookmarks = "0";
637         if (h_pdf_bookmarks == "1") {
638                 string bookmarksnumbered =
639                         process_keyval_opt(options, "bookmarksnumbered");
640                 if (bookmarksnumbered == "true")
641                         h_pdf_bookmarksnumbered = "1";
642                 else if (bookmarksnumbered == "false")
643                         h_pdf_bookmarksnumbered = "0";
644                 string bookmarksopen =
645                         process_keyval_opt(options, "bookmarksopen");
646                 if (bookmarksopen == "true")
647                         h_pdf_bookmarksopen = "1";
648                 else if (bookmarksopen == "false")
649                         h_pdf_bookmarksopen = "0";
650                 if (h_pdf_bookmarksopen == "1") {
651                         string bookmarksopenlevel =
652                                 process_keyval_opt(options, "bookmarksopenlevel");
653                         if (!bookmarksopenlevel.empty())
654                                 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
655                 }
656         }
657         string breaklinks = process_keyval_opt(options, "breaklinks");
658         if (breaklinks == "true")
659                 h_pdf_breaklinks = "1";
660         else if (breaklinks == "false")
661                 h_pdf_breaklinks = "0";
662         string pdfborder = process_keyval_opt(options, "pdfborder");
663         if (pdfborder == "{0 0 0}")
664                 h_pdf_pdfborder = "1";
665         else if (pdfborder == "{0 0 1}")
666                 h_pdf_pdfborder = "0";
667         string backref = process_keyval_opt(options, "backref");
668         if (!backref.empty())
669                 h_pdf_backref = backref;
670         string colorlinks = process_keyval_opt(options, "colorlinks");
671         if (colorlinks == "true")
672                 h_pdf_colorlinks = "1";
673         else if (colorlinks == "false")
674                 h_pdf_colorlinks = "0";
675         string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
676         if (!pdfpagemode.empty())
677                 h_pdf_pagemode = pdfpagemode;
678         string pdftitle = process_keyval_opt(options, "pdftitle");
679         if (!pdftitle.empty()) {
680                 h_pdf_title = remove_braces(pdftitle);
681         }
682         string pdfauthor = process_keyval_opt(options, "pdfauthor");
683         if (!pdfauthor.empty()) {
684                 h_pdf_author = remove_braces(pdfauthor);
685         }
686         string pdfsubject = process_keyval_opt(options, "pdfsubject");
687         if (!pdfsubject.empty())
688                 h_pdf_subject = remove_braces(pdfsubject);
689         string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
690         if (!pdfkeywords.empty())
691                 h_pdf_keywords = remove_braces(pdfkeywords);
692         if (!options.empty()) {
693                 if (!h_pdf_quoted_options.empty())
694                         h_pdf_quoted_options += ',';
695                 h_pdf_quoted_options += join(options, ",");
696                 options.clear();
697         }
698 }
699
700
701 void Preamble::handle_geometry(vector<string> & options)
702 {
703         h_use_geometry = "true";
704         vector<string>::iterator it;
705         // paper orientation
706         if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
707                 h_paperorientation = "landscape";
708                 options.erase(it);
709         }
710         // paper size
711         // keyval version: "paper=letter" or "paper=letterpaper"
712         string paper = process_keyval_opt(options, "paper");
713         if (!paper.empty())
714                 if (suffixIs(paper, "paper"))
715                         paper = subst(paper, "paper", "");
716         // alternative version: "letterpaper"
717         handle_opt(options, known_latex_paper_sizes, paper);
718         if (suffixIs(paper, "paper"))
719                 paper = subst(paper, "paper", "");
720         delete_opt(options, known_latex_paper_sizes);
721         if (!paper.empty())
722                 h_papersize = paper;
723         // page margins
724         char const * const * margin = known_paper_margins;
725         for (; *margin; ++margin) {
726                 string value = process_keyval_opt(options, *margin);
727                 if (!value.empty()) {
728                         int k = margin - known_paper_margins;
729                         string name = known_coded_paper_margins[k];
730                         h_margins += '\\' + name + ' ' + value + '\n';
731                 }
732         }
733 }
734
735
736 void Preamble::handle_package(Parser &p, string const & name,
737                               string const & opts, bool in_lyx_preamble,
738                               bool detectEncoding)
739 {
740         vector<string> options = split_options(opts);
741         add_package(name, options);
742
743         if (is_known(name, known_xetex_packages)) {
744                 xetex = true;
745                 h_use_non_tex_fonts = true;
746                 registerAutomaticallyLoadedPackage("fontspec");
747                 if (h_inputencoding == "auto-legacy")
748                         p.setEncoding("UTF-8");
749         }
750
751         // vector of all options for easier parsing and
752         // skipping
753         vector<string> allopts = getVectorFromString(opts);
754         // this stores the potential extra options
755         string xopts;
756
757         //
758         // roman fonts
759         //
760
761         // By default, we use the package name as LyX font name,
762         // so this only needs to be reset if these names differ
763         if (is_known(name, known_roman_font_packages))
764                 h_font_roman[0] = name;
765
766         if (name == "ccfonts") {
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 == "lmodern") {
778                 for (auto const & opt : allopts) {
779                         if (!xopts.empty())
780                                 xopts += ", ";
781                         xopts += opt;
782                 }
783                 if (!xopts.empty())
784                         h_font_roman_opts = xopts;
785                 options.clear();
786         }
787
788         if (name == "fourier") {
789                 h_font_roman[0] = "utopia";
790                 for (auto const & opt : allopts) {
791                         if (opt == "osf") {
792                                 h_font_roman_osf = "true";
793                                 continue;
794                         }
795                         if (opt == "expert") {
796                                 h_font_sc = "true";
797                                 continue;
798                         }
799                         if (!xopts.empty())
800                                 xopts += ", ";
801                         xopts += opt;
802                 }
803                 if (!xopts.empty())
804                         h_font_roman_opts = xopts;
805                 options.clear();
806         }
807
808         if (name == "garamondx") {
809                 for (auto const & opt : allopts) {
810                         if (opt == "osfI") {
811                                 h_font_roman_osf = "true";
812                                 continue;
813                         }
814                         if (!xopts.empty())
815                                 xopts += ", ";
816                         xopts += opt;
817                 }
818                 if (!xopts.empty())
819                         h_font_roman_opts = xopts;
820                 options.clear();
821         }
822
823         if (name == "libertine") {
824                 // this automatically invokes biolinum
825                 h_font_sans[0] = "biolinum";
826                 // as well as libertineMono
827                 h_font_typewriter[0] = "libertine-mono";
828                 for (auto const & opt : allopts) {
829                         if (opt == "osf") {
830                                 h_font_roman_osf = "true";
831                                 continue;
832                         }
833                         if (opt == "lining") {
834                                 h_font_roman_osf = "false";
835                                 continue;
836                         }
837                         if (!xopts.empty())
838                                 xopts += ", ";
839                         xopts += opt;
840                 }
841                 if (!xopts.empty())
842                         h_font_roman_opts = xopts;
843                 options.clear();
844         }
845
846         if (name == "libertineRoman" || name == "libertine-type1") {
847                 h_font_roman[0] = "libertine";
848                 // NOTE: contrary to libertine.sty, libertineRoman
849                 // and libertine-type1 do not automatically invoke
850                 // biolinum and libertineMono
851                 if (opts == "lining")
852                         h_font_roman_osf = "false";
853                 else if (opts == "osf")
854                         h_font_roman_osf = "true";
855         }
856
857         if (name == "libertinus" || name == "libertinus-type1") {
858                 bool sf = true;
859                 bool tt = true;
860                 bool rm = true;
861                 bool osf = false;
862                 string scalesf;
863                 string scalett;
864                 for (auto const & opt : allopts) {
865                         if (opt == "rm" || opt == "serif") {
866                                 tt = false;
867                                 sf = false;
868                                 continue;
869                         }
870                         if (opt == "sf" || opt == "sans") {
871                                 tt = false;
872                                 rm = false;
873                                 continue;
874                         }
875                         if (opt == "tt=false" || opt == "mono=false") {
876                                 tt = false;
877                                 continue;
878                         }
879                         if (opt == "osf") {
880                                 osf = true;
881                                 continue;
882                         }
883                         if (opt == "scaleSF") {
884                                 scalesf = opt;
885                                 continue;
886                         }
887                         if (opt == "scaleTT") {
888                                 scalett = opt;
889                                 continue;
890                         }
891                         if (opt == "lining") {
892                                 h_font_roman_osf = "false";
893                                 continue;
894                         }
895                         if (!xopts.empty())
896                                 xopts += ", ";
897                         xopts += opt;
898                 }
899                 if (rm) {
900                         h_font_roman[0] = "libertinus";
901                         if (osf)
902                                 h_font_roman_osf = "true";
903                         else
904                                 h_font_roman_osf = "false";
905                 }
906                 if (sf) {
907                         h_font_sans[0] = "LibertinusSans-LF";
908                         if (osf)
909                                 h_font_sans_osf = "true";
910                         else
911                                 h_font_sans_osf = "false";
912                         if (!scalesf.empty())
913                                 scale_as_percentage(scalesf, h_font_sf_scale[0]);
914                 }
915                 if (tt) {
916                         h_font_typewriter[0] = "LibertinusMono-TLF";
917                         if (!scalett.empty())
918                                 scale_as_percentage(scalett, h_font_tt_scale[0]);
919                 }
920                 if (!xopts.empty())
921                         h_font_roman_opts = xopts;
922                 options.clear();
923         }
924
925         if (name == "MinionPro") {
926                 h_font_roman[0] = "minionpro";
927                 h_font_roman_osf = "true";
928                 h_font_math[0] = "auto";
929                 for (auto const & opt : allopts) {
930                         if (opt == "lf") {
931                                 h_font_roman_osf = "false";
932                                 continue;
933                         }
934                         if (opt == "onlytext") {
935                                 h_font_math[0] = "default";
936                                 continue;
937                         }
938                         if (!xopts.empty())
939                                 xopts += ", ";
940                         xopts += opt;
941                 }
942                 if (!xopts.empty())
943                         h_font_roman_opts = xopts;
944                 options.clear();
945         }
946
947         if (name == "mathdesign") {
948                 for (auto const & opt : allopts) {
949                         if (opt == "charter") {
950                                 h_font_roman[0] = "md-charter";
951                                 continue;
952                         }
953                         if (opt == "garamond") {
954                                 h_font_roman[0] = "md-garamond";
955                                 continue;
956                         }
957                         if (opt == "utopia") {
958                                 h_font_roman[0] = "md-utopia";
959                                 continue;
960                         }
961                         if (opt == "expert") {
962                                 h_font_sc = "true";
963                                 h_font_roman_osf = "true";
964                                 continue;
965                         }
966                 }
967         }
968
969         else if (name == "mathpazo") {
970                 h_font_roman[0] = "palatino";
971                 for (auto const & opt : allopts) {
972                         if (opt == "osf") {
973                                 h_font_roman_osf = "true";
974                                 continue;
975                         }
976                         if (opt == "sc") {
977                                 h_font_sc = "true";
978                                 continue;
979                         }
980                         if (!xopts.empty())
981                                 xopts += ", ";
982                         xopts += opt;
983                 }
984                 if (!xopts.empty())
985                         h_font_roman_opts = xopts;
986                 options.clear();
987         }
988
989         else if (name == "mathptmx") {
990                 h_font_roman[0] = "times";
991                 for (auto const & opt : allopts) {
992                         if (!xopts.empty())
993                                 xopts += ", ";
994                         xopts += opt;
995                 }
996                 if (!xopts.empty())
997                         h_font_roman_opts = xopts;
998                 options.clear();
999         }
1000
1001         if (name == "crimson")
1002                 h_font_roman[0] = "cochineal";
1003
1004         if (name == "cochineal") {
1005                 for (auto const & opt : allopts) {
1006                         if (opt == "osf" || opt == "oldstyle") {
1007                                 h_font_roman_osf = "true";
1008                                 continue;
1009                         }
1010                         if (opt == "proportional" || opt == "p")
1011                                 continue;
1012                         if (!xopts.empty())
1013                                 xopts += ", ";
1014                         xopts += opt;
1015                 }
1016                 if (!xopts.empty())
1017                         h_font_roman_opts = xopts;
1018                 options.clear();
1019         }
1020
1021         if (name == "CrimsonPro") {
1022                 h_font_roman_osf = "true";
1023                 for (auto const & opt : allopts) {
1024                         if (opt == "lf" || opt == "lining") {
1025                                 h_font_roman_osf = "false";
1026                                 continue;
1027                         }
1028                         if (opt == "proportional" || opt == "p")
1029                                 continue;
1030                         if (opt == "medium") {
1031                                 h_font_roman[0] = "CrimsonProMedium";
1032                                 continue;
1033                         }
1034                         if (opt == "extralight") {
1035                                 h_font_roman[0] = "CrimsonProExtraLight";
1036                                 continue;
1037                         }
1038                         if (opt == "light") {
1039                                 h_font_roman[0] = "CrimsonProLight";
1040                                 continue;
1041                         }
1042                         if (!xopts.empty())
1043                                 xopts += ", ";
1044                         xopts += opt;
1045                 }
1046                 if (!xopts.empty())
1047                         h_font_roman_opts = xopts;
1048                 options.clear();
1049         }
1050
1051
1052         if (name == "eco")
1053                 // font uses old-style figure
1054                 h_font_roman_osf = "true";
1055
1056         if (name == "paratype") {
1057                 // in this case all fonts are ParaType
1058                 h_font_roman[0] = "PTSerif-TLF";
1059                 h_font_sans[0] = "default";
1060                 h_font_typewriter[0] = "default";
1061         }
1062
1063         if (name == "PTSerif")
1064                 h_font_roman[0] = "PTSerif-TLF";
1065
1066         if (name == "XCharter") {
1067                 h_font_roman[0] = "xcharter";
1068                 for (auto const & opt : allopts) {
1069                         if (opt == "osf") {
1070                                 h_font_roman_osf = "true";
1071                                 continue;
1072                         }
1073                         if (!xopts.empty())
1074                                 xopts += ", ";
1075                         xopts += opt;
1076                 }
1077                 if (!xopts.empty())
1078                         h_font_roman_opts = xopts;
1079                 options.clear();
1080         }
1081
1082         if (name == "plex-serif") {
1083                 h_font_roman[0] = "IBMPlexSerif";
1084                 for (auto const & opt : allopts) {
1085                         if (opt == "thin") {
1086                                 h_font_roman[0] = "IBMPlexSerifThin";
1087                                 continue;
1088                         }
1089                         if (opt == "extralight") {
1090                                 h_font_roman[0] = "IBMPlexSerifExtraLight";
1091                                 continue;
1092                         }
1093                         if (opt == "light") {
1094                                 h_font_roman[0] = "IBMPlexSerifLight";
1095                                 continue;
1096                         }
1097                         if (!xopts.empty())
1098                                 xopts += ", ";
1099                         xopts += opt;
1100                 }
1101                 if (!xopts.empty())
1102                         h_font_roman_opts = xopts;
1103                 options.clear();
1104         }
1105
1106         if (name == "noto-serif" || name == "noto") {
1107                 bool rm = false;
1108                 bool rmx = false;
1109                 bool sf = false;
1110                 bool sfx = false;
1111                 bool tt = false;
1112                 bool thin = false;
1113                 bool extralight = false;
1114                 bool light = false;
1115                 bool medium = false;
1116                 bool osf = false;
1117                 string scl;
1118                 if (name == "noto") {
1119                         rm = true;
1120                         sf = true;
1121                         tt = true;
1122                 }
1123                 // Since the options might apply to different shapes,
1124                 // we need to parse all options first and then handle them.
1125                 for (auto const & opt : allopts) {
1126                         if (opt == "regular")
1127                                 // skip
1128                                 continue;
1129                         if (opt == "rm") {
1130                                 rm = true;
1131                                 rmx = true;
1132                                 sf = sfx;
1133                                 tt = false;
1134                                 continue;
1135                         }
1136                         if (opt == "thin") {
1137                                 thin = true;
1138                                 continue;
1139                         }
1140                         if (opt == "extralight") {
1141                                 extralight = true;
1142                                 continue;
1143                         }
1144                         if (opt == "light") {
1145                                 light = true;
1146                                 continue;
1147                         }
1148                         if (opt == "medium") {
1149                                 medium = true;
1150                                 continue;
1151                         }
1152                         if (opt == "sf") {
1153                                 sfx = true;
1154                                 sf = true;
1155                                 rm = rmx;
1156                                 tt = false;
1157                                 continue;
1158                         }
1159                         if (opt == "nott") {
1160                                 tt = false;
1161                                 continue;
1162                         }
1163                         if (opt == "osf") {
1164                                 osf = true;
1165                                 continue;
1166                         }
1167                         if (prefixIs(opt, "scaled=")) {
1168                                 scl = opt;
1169                                 continue;
1170                         }
1171                         if (!xopts.empty())
1172                                 xopts += ", ";
1173                         xopts += opt;
1174                 }
1175                 options.clear();
1176                 // handle options that might affect different shapes
1177                 if (name == "noto-serif" || rm) {
1178                         if (thin)
1179                                 h_font_roman[0] = "NotoSerifThin";
1180                         else if (extralight)
1181                                 h_font_roman[0] = "NotoSerifExtralight";
1182                         else if (light)
1183                                 h_font_roman[0] = "NotoSerifLight";
1184                         else if (medium)
1185                                 h_font_roman[0] = "NotoSerifMedium";
1186                         else
1187                                 h_font_roman[0] = "NotoSerifRegular";
1188                         if (osf)
1189                                 h_font_roman_osf = "true";
1190                         if (!xopts.empty())
1191                                 h_font_roman_opts = xopts;
1192                 }
1193                 if (name == "noto" && sf) {
1194                         if (thin)
1195                                 h_font_sans[0] = "NotoSansThin";
1196                         else if (extralight)
1197                                 h_font_sans[0] = "NotoSansExtralight";
1198                         else if (light)
1199                                 h_font_sans[0] = "NotoSansLight";
1200                         else if (medium)
1201                                 h_font_sans[0] = "NotoSansMedium";
1202                         else
1203                                 h_font_sans[0] = "NotoSansRegular";
1204                         if (osf)
1205                                 h_font_sans_osf = "true";
1206                         if (!scl.empty())
1207                                 scale_as_percentage(scl, h_font_sf_scale[0]);
1208                         if (!xopts.empty())
1209                                 h_font_sans_opts = xopts;
1210                 }
1211                 if (name == "noto" && tt) {
1212                         h_font_typewriter[0] = "NotoMonoRegular";
1213                         if (osf)
1214                                 h_font_typewriter_osf = "true";
1215                         if (!scl.empty())
1216                                 scale_as_percentage(scl, h_font_tt_scale[0]);
1217                         if (!xopts.empty())
1218                                 h_font_typewriter_opts = xopts;
1219                 }
1220         }
1221
1222         if (name == "sourceserifpro") {
1223                 h_font_roman[0] = "ADOBESourceSerifPro";
1224                 for (auto const & opt : allopts) {
1225                         if (opt == "osf") {
1226                                 h_font_roman_osf = "true";
1227                                 continue;
1228                         }
1229                         if (!xopts.empty())
1230                                 xopts += ", ";
1231                         xopts += opt;
1232                 }
1233                 if (!xopts.empty())
1234                         h_font_roman_opts = xopts;
1235                 options.clear();
1236         }
1237
1238         //
1239         // sansserif fonts
1240         //
1241
1242         // By default, we use the package name as LyX font name,
1243         // so this only needs to be reset if these names differ.
1244         // Also, we handle the scaling option here generally.
1245         if (is_known(name, known_sans_font_packages)) {
1246                 h_font_sans[0] = name;
1247                 if (contains(opts, "scale")) {
1248                         vector<string>::iterator it = allopts.begin();
1249                         for (; it != allopts.end() ; ++it) {
1250                                 string const opt = *it;
1251                                 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1252                                         if (scale_as_percentage(opt, h_font_sf_scale[0])) {
1253                                                 allopts.erase(it);
1254                                                 break;
1255                                         }
1256                                 }
1257                         }
1258                 }
1259         }
1260
1261         if (name == "biolinum" || name == "biolinum-type1") {
1262                 h_font_sans[0] = "biolinum";
1263                 for (auto const & opt : allopts) {
1264                         if (prefixIs(opt, "osf")) {
1265                                 h_font_sans_osf = "true";
1266                                 continue;
1267                         }
1268                         if (!xopts.empty())
1269                                 xopts += ", ";
1270                         xopts += opt;
1271                 }
1272                 if (!xopts.empty())
1273                         h_font_sans_opts = xopts;
1274                 options.clear();
1275         }
1276
1277         if (name == "cantarell") {
1278                 for (auto const & opt : allopts) {
1279                         if (opt == "defaultsans")
1280                                 continue;
1281                         if (prefixIs(opt, "oldstyle")) {
1282                                 h_font_sans_osf = "true";
1283                                 continue;
1284                         }
1285                         if (!xopts.empty())
1286                                 xopts += ", ";
1287                         xopts += opt;
1288                 }
1289                 if (!xopts.empty())
1290                         h_font_sans_opts = xopts;
1291                 options.clear();
1292         }
1293
1294         if (name == "Chivo") {
1295                 for (auto const & opt : allopts) {
1296                         if (opt == "thin") {
1297                                 h_font_roman[0] = "ChivoThin";
1298                                 continue;
1299                         }
1300                         if (opt == "light") {
1301                                 h_font_roman[0] = "ChivoLight";
1302                                 continue;
1303                         }
1304                         if (opt == "regular") {
1305                                 h_font_roman[0] = "Chivo";
1306                                 continue;
1307                         }
1308                         if (opt == "medium") {
1309                                 h_font_roman[0] = "ChivoMedium";
1310                                 continue;
1311                         }
1312                         if (prefixIs(opt, "oldstyle")) {
1313                                 h_font_sans_osf = "true";
1314                                 continue;
1315                         }
1316                         if (!xopts.empty())
1317                                 xopts += ", ";
1318                         xopts += opt;
1319                 }
1320                 if (!xopts.empty())
1321                         h_font_sans_opts = xopts;
1322                 options.clear();
1323         }
1324
1325         if (name == "PTSans") {
1326                 h_font_sans[0] = "PTSans-TLF";
1327         }
1328
1329         if (name == "FiraSans") {
1330                 h_font_sans_osf = "true";
1331                 for (auto const & opt : allopts) {
1332                         if (opt == "book") {
1333                                 h_font_sans[0] = "FiraSansBook";
1334                                 continue;
1335                         }
1336                         if (opt == "thin") {
1337                                 continue;
1338                         }
1339                         if (opt == "extralight") {
1340                                 h_font_sans[0] = "FiraSansExtralight";
1341                                 continue;
1342                         }
1343                         if (opt == "light") {
1344                                 h_font_sans[0] = "FiraSansLight";
1345                                 continue;
1346                         }
1347                         if (opt == "ultralight") {
1348                                 h_font_sans[0] = "FiraSansUltralight";
1349                                 continue;
1350                         }
1351                         if (opt == "thin") {
1352                                 h_font_sans[0] = "FiraSansThin";
1353                                 continue;
1354                         }
1355                         if (opt == "lf" || opt == "lining") {
1356                                 h_font_sans_osf = "false";
1357                                 continue;
1358                         }
1359                         if (!xopts.empty())
1360                                 xopts += ", ";
1361                         xopts += opt;
1362                 }
1363                 if (!xopts.empty())
1364                         h_font_sans_opts = xopts;
1365                 options.clear();
1366         }
1367
1368         if (name == "plex-sans") {
1369                 h_font_sans[0] = "IBMPlexSans";
1370                 for (auto const & opt : allopts) {
1371                         if (opt == "condensed") {
1372                                 h_font_sans[0] = "IBMPlexSansCondensed";
1373                                 continue;
1374                         }
1375                         if (opt == "thin") {
1376                                 h_font_sans[0] = "IBMPlexSansThin";
1377                                 continue;
1378                         }
1379                         if (opt == "extralight") {
1380                                 h_font_sans[0] = "IBMPlexSansExtraLight";
1381                                 continue;
1382                         }
1383                         if (opt == "light") {
1384                                 h_font_sans[0] = "IBMPlexSansLight";
1385                                 continue;
1386                         }
1387                         if (!xopts.empty())
1388                                 xopts += ", ";
1389                         xopts += opt;
1390                 }
1391                 if (!xopts.empty())
1392                         h_font_sans_opts = xopts;
1393                 options.clear();
1394         }
1395
1396         if (name == "noto-sans") {
1397                 h_font_sans[0] = "NotoSansRegular";
1398                 for (auto const & opt : allopts) {
1399                         if (opt == "regular")
1400                                 continue;
1401                         if (opt == "medium") {
1402                                 h_font_sans[0] = "NotoSansMedium";
1403                                 continue;
1404                         }
1405                         if (opt == "thin") {
1406                                 h_font_sans[0] = "NotoSansThin";
1407                                 continue;
1408                         }
1409                         if (opt == "extralight") {
1410                                 h_font_sans[0] = "NotoSansExtralight";
1411                                 continue;
1412                         }
1413                         if (opt == "light") {
1414                                 h_font_sans[0] = "NotoSansLight";
1415                                 continue;
1416                         }
1417                         if (opt == "osf") {
1418                                 h_font_sans_osf = "true";
1419                                 continue;
1420                         }
1421                         if (!xopts.empty())
1422                                 xopts += ", ";
1423                         xopts += opt;
1424                 }
1425                 if (!xopts.empty())
1426                         h_font_sans_opts = xopts;
1427                 options.clear();
1428         }
1429
1430         if (name == "sourcesanspro") {
1431                 h_font_sans[0] = "ADOBESourceSansPro";
1432                 for (auto const & opt : allopts) {
1433                         if (opt == "osf") {
1434                                 h_font_sans_osf = "true";
1435                                 continue;
1436                         }
1437                         if (!xopts.empty())
1438                                 xopts += ", ";
1439                         xopts += opt;
1440                 }
1441                 if (!xopts.empty())
1442                         h_font_sans_opts = xopts;
1443                 options.clear();
1444         }
1445
1446         //
1447         // typewriter fonts
1448         //
1449
1450         // By default, we use the package name as LyX font name,
1451         // so this only needs to be reset if these names differ.
1452         // Also, we handle the scaling option here generally.
1453         if (is_known(name, known_typewriter_font_packages)) {
1454                 h_font_typewriter[0] = name;
1455                 if (contains(opts, "scale")) {
1456                         vector<string>::iterator it = allopts.begin();
1457                         for (; it != allopts.end() ; ++it) {
1458                                 string const opt = *it;
1459                                 if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) {
1460                                         if (scale_as_percentage(opt, h_font_tt_scale[0])) {
1461                                                 allopts.erase(it);
1462                                                 break;
1463                                         }
1464                                 }
1465                         }
1466                 }
1467         }
1468
1469         if (name == "libertineMono" || name == "libertineMono-type1")
1470                 h_font_typewriter[0] = "libertine-mono";
1471
1472         if (name == "FiraMono") {
1473                 h_font_typewriter_osf = "true";
1474                 for (auto const & opt : allopts) {
1475                         if (opt == "lf" || opt == "lining") {
1476                                 h_font_typewriter_osf = "false";
1477                                 continue;
1478                         }
1479                         if (!xopts.empty())
1480                                 xopts += ", ";
1481                         xopts += opt;
1482                 }
1483                 if (!xopts.empty())
1484                         h_font_typewriter_opts = xopts;
1485                 options.clear();
1486         }
1487
1488         if (name == "PTMono")
1489                 h_font_typewriter[0] = "PTMono-TLF";
1490
1491         if (name == "plex-mono") {
1492                 h_font_typewriter[0] = "IBMPlexMono";
1493                 for (auto const & opt : allopts) {
1494                         if (opt == "thin") {
1495                                 h_font_typewriter[0] = "IBMPlexMonoThin";
1496                                 continue;
1497                         }
1498                         if (opt == "extralight") {
1499                                 h_font_typewriter[0] = "IBMPlexMonoExtraLight";
1500                                 continue;
1501                         }
1502                         if (opt == "light") {
1503                                 h_font_typewriter[0] = "IBMPlexMonoLight";
1504                                 continue;
1505                         }
1506                         if (!xopts.empty())
1507                                 xopts += ", ";
1508                         xopts += opt;
1509                 }
1510                 if (!xopts.empty())
1511                         h_font_typewriter_opts = xopts;
1512                 options.clear();
1513         }
1514
1515         if (name == "noto-mono") {
1516                 h_font_typewriter[0] = "NotoMonoRegular";
1517                 for (auto const & opt : allopts) {
1518                         if (opt == "regular")
1519                                 continue;
1520                         if (!xopts.empty())
1521                                 xopts += ", ";
1522                         xopts += opt;
1523                 }
1524                 if (!xopts.empty())
1525                         h_font_typewriter_opts = xopts;
1526                 options.clear();
1527         }
1528
1529         if (name == "sourcecodepro") {
1530                 h_font_typewriter[0] = "ADOBESourceCodePro";
1531                 for (auto const & opt : allopts) {
1532                         if (opt == "osf") {
1533                                 h_font_typewriter_osf = "true";
1534                                 continue;
1535                         }
1536                         if (!xopts.empty())
1537                                 xopts += ", ";
1538                         xopts += opt;
1539                 }
1540                 if (!xopts.empty())
1541                         h_font_typewriter_opts = xopts;
1542                 options.clear();
1543         }
1544
1545         //
1546         // math fonts
1547         //
1548
1549         // By default, we use the package name as LyX font name,
1550         // so this only needs to be reset if these names differ.
1551         if (is_known(name, known_math_font_packages))
1552                 h_font_math[0] = name;
1553
1554         if (name == "newtxmath") {
1555                 if (opts.empty())
1556                         h_font_math[0] = "newtxmath";
1557                 else if (opts == "garamondx")
1558                         h_font_math[0] = "garamondx-ntxm";
1559                 else if (opts == "libertine")
1560                         h_font_math[0] = "libertine-ntxm";
1561                 else if (opts == "minion")
1562                         h_font_math[0] = "minion-ntxm";
1563                 else if (opts == "cochineal")
1564                         h_font_math[0] = "cochineal-ntxm";
1565         }
1566
1567         if (name == "iwona")
1568                 if (opts == "math")
1569                         h_font_math[0] = "iwona-math";
1570
1571         if (name == "kurier")
1572                 if (opts == "math")
1573                         h_font_math[0] = "kurier-math";
1574
1575         // after the detection and handling of special cases, we can remove the
1576         // fonts, otherwise they would appear in the preamble, see bug #7856
1577         if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1578                 ||      is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1579                 ;
1580         //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1581         else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1582                  name == "esint" || name == "mhchem" || name == "mathdots" ||
1583                  name == "mathtools" || name == "stackrel" ||
1584                  name == "stmaryrd" || name == "undertilde") {
1585                 h_use_packages[name] = "2";
1586                 registerAutomaticallyLoadedPackage(name);
1587         }
1588
1589         else if (name == "babel") {
1590                 h_language_package = "default";
1591                 // One might think we would have to do nothing if babel is loaded
1592                 // without any options to prevent pollution of the preamble with this
1593                 // babel call in every roundtrip.
1594                 // But the user could have defined babel-specific things afterwards. So
1595                 // we need to keep it in the preamble to prevent cases like bug #7861.
1596                 if (!opts.empty()) {
1597                         // check if more than one option was used - used later for inputenc
1598                         if (options.begin() != options.end() - 1)
1599                                 one_language = false;
1600                         // babel takes the last language of the option of its \usepackage
1601                         // call as document language. If there is no such language option, the
1602                         // last language in the documentclass options is used.
1603                         handle_opt(options, known_languages, h_language);
1604                         // translate the babel name to a LyX name
1605                         h_language = babel2lyx(h_language);
1606                         if (h_language == "japanese") {
1607                                 // For Japanese, the encoding isn't indicated in the source
1608                                 // file, and there's really not much we can do. We could
1609                                 // 1) offer a list of possible encodings to choose from, or
1610                                 // 2) determine the encoding of the file by inspecting it.
1611                                 // For the time being, we leave the encoding alone so that
1612                                 // we don't get iconv errors when making a wrong guess, and
1613                                 // we will output a note at the top of the document
1614                                 // explaining what to do.
1615                                 Encoding const * const enc = encodings.fromIconvName(
1616                                         p.getEncoding(), Encoding::japanese, false);
1617                                 if (enc)
1618                                         h_inputencoding = enc->name();
1619                                 is_nonCJKJapanese = true;
1620                                 // in this case babel can be removed from the preamble
1621                                 registerAutomaticallyLoadedPackage("babel");
1622                         } else {
1623                                 // If babel is called with options, LyX puts them by default into the
1624                                 // document class options. This works for most languages, except
1625                                 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1626                                 // perhaps in future others.
1627                                 // Therefore keep the babel call as it is as the user might have
1628                                 // reasons for it.
1629                                 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1630                                 if (!contains(h_preamble.str(), babelcall))
1631                                         h_preamble << babelcall;
1632                         }
1633                         delete_opt(options, known_languages);
1634                 } else {
1635                         if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1636                                 h_preamble << "\\usepackage{babel}\n";
1637                         explicit_babel = true;
1638                 }
1639         }
1640
1641         else if (name == "polyglossia") {
1642                 h_language_package = "default";
1643                 h_default_output_format = "pdf4";
1644                 h_use_non_tex_fonts = true;
1645                 xetex = true;
1646                 registerAutomaticallyLoadedPackage("xunicode");
1647                 if (h_inputencoding == "auto-legacy")
1648                         p.setEncoding("UTF-8");
1649         }
1650
1651         else if (name == "CJK") {
1652                 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1653                 // and this would not be correct for CJK
1654                 if (h_inputencoding == "auto-legacy-plain")
1655                         h_inputencoding = "auto-legacy";
1656                 registerAutomaticallyLoadedPackage("CJK");
1657         }
1658
1659         else if (name == "CJKutf8") {
1660                 h_inputencoding = "utf8-cjk";
1661                 p.setEncoding("UTF-8");
1662                 registerAutomaticallyLoadedPackage("CJKutf8");
1663         }
1664
1665         else if (name == "fontenc") {
1666                 h_fontencoding = getStringFromVector(options, ",");
1667                 options.clear();
1668         }
1669
1670         else if (name == "inputenc" || name == "luainputenc") {
1671                 // h_inputencoding is only set when there is not more than one
1672                 // inputenc option because otherwise h_inputencoding must be
1673                 // set to "auto-legacy" (the default encodings of the document's languages)
1674                 // Therefore check that exactly one option is passed to inputenc.
1675                 // It is also only set when there is not more than one babel
1676                 // language option.
1677                 if (!options.empty()) {
1678                         string const encoding = options.back();
1679                         Encoding const * const enc = encodings.fromLaTeXName(
1680                                 encoding, Encoding::inputenc, true);
1681                         if (!enc) {
1682                                 if (!detectEncoding)
1683                                         cerr << "Unknown encoding " << encoding
1684                                              << ". Ignoring." << std::endl;
1685                         } else {
1686                                 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1687                                         h_inputencoding = enc->name();
1688                                 p.setEncoding(enc->iconvName());
1689                         }
1690                         options.clear();
1691                 }
1692         }
1693
1694         else if (name == "srcltx") {
1695                 h_output_sync = "1";
1696                 if (!opts.empty()) {
1697                         h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1698                         options.clear();
1699                 } else
1700                         h_output_sync_macro = "\\usepackage{srcltx}";
1701         }
1702
1703         else if (is_known(name, known_old_language_packages)) {
1704                 // known language packages from the times before babel
1705                 // if they are found and not also babel, they will be used as
1706                 // custom language package
1707                 h_language_package = "\\usepackage{" + name + "}";
1708         }
1709
1710         else if (name == "lyxskak") {
1711                 // ignore this and its options
1712                 const char * const o[] = {"ps", "mover", 0};
1713                 delete_opt(options, o);
1714         }
1715
1716         else if (name == "parskip" && options.size() < 2 && (opts.empty() || prefixIs(opts, "skip="))) {
1717                 if (opts.empty())
1718                         h_paragraph_separation = "halfline";
1719                 else {
1720                         if (opts == "skip=\\smallskipamount")
1721                                 h_defskip = "smallskip";
1722                         else if (opts == "skip=\\medskipamount")
1723                                 h_defskip = "medskip";
1724                         else if (opts == "skip=\\bigskipamount")
1725                                 h_defskip = "bigskip";
1726                         else if (opts == "skip=\\baselineskip")
1727                                 h_defskip = "fullline";
1728                         else
1729                                 h_defskip = "opts";
1730                         h_paragraph_separation = "skip";
1731                 }
1732         }
1733
1734         else if (is_known(name, known_lyx_packages) && options.empty()) {
1735                 if (name == "splitidx")
1736                         h_use_indices = "true";
1737                 else if (name == "minted")
1738                         h_use_minted = true;
1739                 else if (name == "refstyle")
1740                         h_use_refstyle = true;
1741                 else if (name == "prettyref")
1742                         h_use_refstyle = false;
1743
1744                 if (!in_lyx_preamble) {
1745                         h_preamble << package_beg_sep << name
1746                                    << package_mid_sep << "\\usepackage{"
1747                                    << name << '}';
1748                         if (p.next_token().cat() == catNewline ||
1749                             (p.next_token().cat() == catSpace &&
1750                              p.next_next_token().cat() == catNewline))
1751                                 h_preamble << '\n';
1752                         h_preamble << package_end_sep;
1753                 }
1754         }
1755
1756         else if (name == "geometry")
1757                 handle_geometry(options);
1758
1759         else if (name == "subfig")
1760                 ; // ignore this FIXME: Use the package separator mechanism instead
1761
1762         else if (char const * const * where = is_known(name, known_languages))
1763                 h_language = known_coded_languages[where - known_languages];
1764
1765         else if (name == "natbib") {
1766                 h_biblio_style = "plainnat";
1767                 h_cite_engine = "natbib";
1768                 h_cite_engine_type = "authoryear";
1769                 vector<string>::iterator it =
1770                         find(options.begin(), options.end(), "authoryear");
1771                 if (it != options.end())
1772                         options.erase(it);
1773                 else {
1774                         it = find(options.begin(), options.end(), "numbers");
1775                         if (it != options.end()) {
1776                                 h_cite_engine_type = "numerical";
1777                                 options.erase(it);
1778                         }
1779                 }
1780                 if (!options.empty())
1781                         h_biblio_options = join(options, ",");
1782         }
1783
1784         else if (name == "biblatex") {
1785                 h_biblio_style = "plainnat";
1786                 h_cite_engine = "biblatex";
1787                 h_cite_engine_type = "authoryear";
1788                 string opt;
1789                 vector<string>::iterator it =
1790                         find(options.begin(), options.end(), "natbib");
1791                 if (it != options.end()) {
1792                         options.erase(it);
1793                         h_cite_engine = "biblatex-natbib";
1794                 } else {
1795                         opt = process_keyval_opt(options, "natbib");
1796                         if (opt == "true")
1797                                 h_cite_engine = "biblatex-natbib";
1798                 }
1799                 opt = process_keyval_opt(options, "style");
1800                 if (!opt.empty()) {
1801                         h_biblatex_citestyle = opt;
1802                         h_biblatex_bibstyle = opt;
1803                 } else {
1804                         opt = process_keyval_opt(options, "citestyle");
1805                         if (!opt.empty())
1806                                 h_biblatex_citestyle = opt;
1807                         opt = process_keyval_opt(options, "bibstyle");
1808                         if (!opt.empty())
1809                                 h_biblatex_bibstyle = opt;
1810                 }
1811                 opt = process_keyval_opt(options, "refsection");
1812                 if (!opt.empty()) {
1813                         if (opt == "none" || opt == "part"
1814                             || opt == "chapter" || opt == "section"
1815                             || opt == "subsection")
1816                                 h_multibib = opt;
1817                         else
1818                                 cerr << "Ignoring unknown refsection value '"
1819                                      << opt << "'.";
1820                 }
1821                 opt = process_keyval_opt(options, "bibencoding");
1822                 if (!opt.empty())
1823                         bibencoding = opt;
1824                 if (!options.empty()) {
1825                         h_biblio_options = join(options, ",");
1826                         options.clear();
1827                 }
1828         }
1829
1830         else if (name == "jurabib") {
1831                 h_biblio_style = "jurabib";
1832                 h_cite_engine = "jurabib";
1833                 h_cite_engine_type = "authoryear";
1834                 if (!options.empty())
1835                         h_biblio_options = join(options, ",");
1836         }
1837
1838         else if (name == "bibtopic")
1839                 h_use_bibtopic = "true";
1840
1841         else if (name == "chapterbib")
1842                 h_multibib = "child";
1843
1844         else if (name == "hyperref")
1845                 handle_hyperref(options);
1846
1847         else if (name == "algorithm2e") {
1848                 // Load "algorithm2e" module
1849                 addModule("algorithm2e");
1850                 // Add the package options to the global document options
1851                 if (!options.empty()) {
1852                         if (h_options.empty())
1853                                 h_options = join(options, ",");
1854                         else
1855                                 h_options += ',' + join(options, ",");
1856                 }
1857         }
1858         else if (name == "microtype") {
1859                 //we internally support only microtype without params
1860                 if (options.empty())
1861                         h_use_microtype = "true";
1862                 else
1863                         h_preamble << "\\usepackage[" << opts << "]{microtype}";
1864         }
1865
1866         else if (name == "lineno") {
1867                 h_use_lineno = "true";
1868                 if (!options.empty()) {
1869                         h_lineno_options = join(options, ",");
1870                         options.clear();
1871                 }
1872         }
1873
1874         else if (name == "changebar")
1875                 h_output_changes = "true";
1876
1877         else if (!in_lyx_preamble) {
1878                 if (options.empty())
1879                         h_preamble << "\\usepackage{" << name << '}';
1880                 else {
1881                         h_preamble << "\\usepackage[" << opts << "]{"
1882                                    << name << '}';
1883                         options.clear();
1884                 }
1885                 if (p.next_token().cat() == catNewline ||
1886                     (p.next_token().cat() == catSpace &&
1887                      p.next_next_token().cat() == catNewline))
1888                         h_preamble << '\n';
1889         }
1890
1891         // We need to do something with the options...
1892         if (!options.empty() && !detectEncoding)
1893                 cerr << "Ignoring options '" << join(options, ",")
1894                      << "' of package " << name << '.' << endl;
1895
1896         // remove the whitespace
1897         p.skip_spaces();
1898 }
1899
1900
1901 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1902 {
1903         while (p.good()) {
1904                 Token t = p.get_token();
1905                 if (t.cat() == catEscape &&
1906                     is_known(t.cs(), known_if_commands))
1907                         handle_if(p, in_lyx_preamble);
1908                 else {
1909                         if (!in_lyx_preamble)
1910                                 h_preamble << t.asInput();
1911                         if (t.cat() == catEscape && t.cs() == "fi")
1912                                 return;
1913                 }
1914         }
1915 }
1916
1917
1918 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1919 {
1920         if (contains(h_float_placement, "H"))
1921                 registerAutomaticallyLoadedPackage("float");
1922         if (h_spacing != "single" && h_spacing != "default")
1923                 registerAutomaticallyLoadedPackage("setspace");
1924         if (h_use_packages["amsmath"] == "2") {
1925                 // amsbsy and amstext are already provided by amsmath
1926                 registerAutomaticallyLoadedPackage("amsbsy");
1927                 registerAutomaticallyLoadedPackage("amstext");
1928         }
1929
1930         // output the LyX file settings
1931         // Important: Keep the version formatting in sync with LyX and
1932         //            lyx2lyx (bug 7951)
1933         string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1934         os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1935            << lyx_version_minor << '\n'
1936            << "\\lyxformat " << LYX_FORMAT << '\n'
1937            << "\\begin_document\n"
1938            << "\\begin_header\n"
1939            << "\\save_transient_properties " << h_save_transient_properties << "\n"
1940            << "\\origin " << origin << "\n"
1941            << "\\textclass " << h_textclass << "\n";
1942         string const raw = subdoc ? empty_string() : h_preamble.str();
1943         if (!raw.empty()) {
1944                 os << "\\begin_preamble\n";
1945                 for (string::size_type i = 0; i < raw.size(); ++i) {
1946                         if (raw[i] == package_beg_sep) {
1947                                 // Here follows some package loading code that
1948                                 // must be skipped if the package is loaded
1949                                 // automatically.
1950                                 string::size_type j = raw.find(package_mid_sep, i);
1951                                 if (j == string::npos)
1952                                         return false;
1953                                 string::size_type k = raw.find(package_end_sep, j);
1954                                 if (k == string::npos)
1955                                         return false;
1956                                 string const package = raw.substr(i + 1, j - i - 1);
1957                                 string const replacement = raw.substr(j + 1, k - j - 1);
1958                                 if (auto_packages.find(package) == auto_packages.end())
1959                                         os << replacement;
1960                                 i = k;
1961                         } else
1962                                 os.put(raw[i]);
1963                 }
1964                 os << "\n\\end_preamble\n";
1965         }
1966         if (!h_options.empty())
1967                 os << "\\options " << h_options << "\n";
1968         os << "\\use_default_options " << h_use_default_options << "\n";
1969         if (!used_modules.empty()) {
1970                 os << "\\begin_modules\n";
1971                 vector<string>::const_iterator const end = used_modules.end();
1972                 vector<string>::const_iterator it = used_modules.begin();
1973                 for (; it != end; ++it)
1974                         os << *it << '\n';
1975                 os << "\\end_modules\n";
1976         }
1977         if (!h_includeonlys.empty()) {
1978                 os << "\\begin_includeonly\n";
1979                 for (auto const & iofile : h_includeonlys)
1980                         os << iofile << '\n';
1981                 os << "\\end_includeonly\n";
1982         }
1983         os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1984            << "\\language " << h_language << "\n"
1985            << "\\language_package " << h_language_package << "\n"
1986            << "\\inputencoding " << h_inputencoding << "\n"
1987            << "\\fontencoding " << h_fontencoding << "\n"
1988            << "\\font_roman \"" << h_font_roman[0]
1989            << "\" \"" << h_font_roman[1] << "\"\n"
1990            << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1991            << "\\font_typewriter \"" << h_font_typewriter[0]
1992            << "\" \"" << h_font_typewriter[1] << "\"\n"
1993            << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1994            << "\\font_default_family " << h_font_default_family << "\n"
1995            << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1996            << "\\font_sc " << h_font_sc << "\n"
1997            << "\\font_roman_osf " << h_font_roman_osf << "\n"
1998            << "\\font_sans_osf " << h_font_sans_osf << "\n"
1999            << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n";
2000         if (!h_font_roman_opts.empty())
2001                 os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n';
2002         os << "\\font_sf_scale " << h_font_sf_scale[0]
2003            << ' ' << h_font_sf_scale[1] << '\n';
2004         if (!h_font_sans_opts.empty())
2005                 os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n';
2006         os << "\\font_tt_scale " << h_font_tt_scale[0]
2007            << ' ' << h_font_tt_scale[1] << '\n';
2008         if (!h_font_cjk.empty())
2009                 os << "\\font_cjk " << h_font_cjk << '\n';
2010         if (!h_font_typewriter_opts.empty())
2011                 os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n';
2012         os << "\\use_microtype " << h_use_microtype << '\n'
2013            << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
2014            << "\\graphics " << h_graphics << '\n'
2015            << "\\default_output_format " << h_default_output_format << "\n"
2016            << "\\output_sync " << h_output_sync << "\n";
2017         if (h_output_sync == "1")
2018                 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
2019         os << "\\bibtex_command " << h_bibtex_command << "\n"
2020            << "\\index_command " << h_index_command << "\n";
2021         if (!h_float_placement.empty())
2022                 os << "\\float_placement " << h_float_placement << "\n";
2023         os << "\\paperfontsize " << h_paperfontsize << "\n"
2024            << "\\spacing " << h_spacing << "\n"
2025            << "\\use_hyperref " << h_use_hyperref << '\n';
2026         if (h_use_hyperref == "true") {
2027                 if (!h_pdf_title.empty())
2028                         os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
2029                 if (!h_pdf_author.empty())
2030                         os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
2031                 if (!h_pdf_subject.empty())
2032                         os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
2033                 if (!h_pdf_keywords.empty())
2034                         os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
2035                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
2036                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
2037                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
2038                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
2039                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
2040                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
2041                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
2042                       "\\pdf_backref " << h_pdf_backref << "\n"
2043                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
2044                 if (!h_pdf_pagemode.empty())
2045                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
2046                 if (!h_pdf_quoted_options.empty())
2047                         os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
2048         }
2049         os << "\\papersize " << h_papersize << "\n"
2050            << "\\use_geometry " << h_use_geometry << '\n';
2051         for (map<string, string>::const_iterator it = h_use_packages.begin();
2052              it != h_use_packages.end(); ++it)
2053                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
2054         os << "\\cite_engine " << h_cite_engine << '\n'
2055            << "\\cite_engine_type " << h_cite_engine_type << '\n'
2056            << "\\biblio_style " << h_biblio_style << "\n"
2057            << "\\use_bibtopic " << h_use_bibtopic << "\n";
2058         if (!h_biblio_options.empty())
2059                 os << "\\biblio_options " << h_biblio_options << "\n";
2060         if (!h_biblatex_bibstyle.empty())
2061                 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
2062         if (!h_biblatex_citestyle.empty())
2063                 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
2064         if (!h_multibib.empty())
2065                 os << "\\multibib " << h_multibib << "\n";
2066         os << "\\use_indices " << h_use_indices << "\n"
2067            << "\\paperorientation " << h_paperorientation << '\n'
2068            << "\\suppress_date " << h_suppress_date << '\n'
2069            << "\\justification " << h_justification << '\n'
2070            << "\\use_refstyle " << h_use_refstyle << '\n'
2071            << "\\use_minted " << h_use_minted << '\n'
2072            << "\\use_lineno " << h_use_lineno << '\n';
2073         if (!h_lineno_options.empty())
2074                 os << "\\lineno_options " << h_lineno_options << '\n';
2075         if (!h_fontcolor.empty())
2076                 os << "\\fontcolor " << h_fontcolor << '\n';
2077         if (!h_notefontcolor.empty())
2078                 os << "\\notefontcolor " << h_notefontcolor << '\n';
2079         if (!h_backgroundcolor.empty())
2080                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
2081         if (!h_boxbgcolor.empty())
2082                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
2083         if (index_number != 0)
2084                 for (int i = 0; i < index_number; i++) {
2085                         os << "\\index " << h_index[i] << '\n'
2086                            << "\\shortcut " << h_shortcut[i] << '\n'
2087                            << "\\color " << h_color << '\n'
2088                            << "\\end_index\n";
2089                 }
2090         else {
2091                 os << "\\index " << h_index[0] << '\n'
2092                    << "\\shortcut " << h_shortcut[0] << '\n'
2093                    << "\\color " << h_color << '\n'
2094                    << "\\end_index\n";
2095         }
2096         os << h_margins
2097            << "\\secnumdepth " << h_secnumdepth << "\n"
2098            << "\\tocdepth " << h_tocdepth << "\n"
2099            << "\\paragraph_separation " << h_paragraph_separation << "\n";
2100         if (h_paragraph_separation == "skip")
2101                 os << "\\defskip " << h_defskip << "\n";
2102         else
2103                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
2104         os << "\\is_math_indent " << h_is_mathindent << "\n";
2105         if (!h_mathindentation.empty())
2106                 os << "\\math_indentation " << h_mathindentation << "\n";
2107         os << "\\math_numbering_side " << h_math_numbering_side << "\n";
2108         os << "\\quotes_style " << h_quotes_style << "\n"
2109            << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
2110            << "\\papercolumns " << h_papercolumns << "\n"
2111            << "\\papersides " << h_papersides << "\n"
2112            << "\\paperpagestyle " << h_paperpagestyle << "\n";
2113         if (!h_listings_params.empty())
2114                 os << "\\listings_params " << h_listings_params << "\n";
2115         os << "\\tracking_changes " << h_tracking_changes << "\n"
2116            << "\\output_changes " << h_output_changes << "\n"
2117            << "\\change_bars " << h_change_bars << "\n"
2118            << "\\html_math_output " << h_html_math_output << "\n"
2119            << "\\html_css_as_file " << h_html_css_as_file << "\n"
2120            << "\\html_be_strict " << h_html_be_strict << "\n"
2121            << "\\docbook_table_output " << h_docbook_table_output << "\n"
2122            << authors_
2123            << "\\end_header\n\n"
2124            << "\\begin_body\n";
2125         return true;
2126 }
2127
2128
2129 void Preamble::parse(Parser & p, string const & forceclass,
2130                      TeX2LyXDocClass & tc)
2131 {
2132         // initialize fixed types
2133         special_columns_['D'] = 3;
2134         parse(p, forceclass, false, tc);
2135 }
2136
2137
2138 void Preamble::parse(Parser & p, string const & forceclass,
2139                      bool detectEncoding, TeX2LyXDocClass & tc)
2140 {
2141         bool is_full_document = false;
2142         bool is_lyx_file = false;
2143         bool in_lyx_preamble = false;
2144         bool class_set = false;
2145
2146         // determine whether this is a full document or a fragment for inclusion
2147         while (p.good()) {
2148                 Token const & t = p.get_token();
2149
2150                 if (t.cat() == catEscape && t.cs() == "documentclass") {
2151                         is_full_document = true;
2152                         break;
2153                 }
2154         }
2155         p.reset();
2156
2157         if (detectEncoding && !is_full_document)
2158                 return;
2159
2160         while (is_full_document && p.good()) {
2161                 if (detectEncoding && h_inputencoding != "auto-legacy" &&
2162                     h_inputencoding != "auto-legacy-plain")
2163                         return;
2164
2165                 // Force textclass if the user wanted it
2166                 if (!forceclass.empty()) {
2167                         h_textclass = forceclass;
2168                         tc.setName(h_textclass);
2169                         if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2170                                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2171                                 exit(EXIT_FAILURE);
2172                         }
2173                         class_set = true;
2174                 }
2175
2176                 Token const & t = p.get_token();
2177
2178 #ifdef FILEDEBUG
2179                 if (!detectEncoding)
2180                         cerr << "t: " << t << '\n';
2181 #endif
2182
2183                 //
2184                 // cat codes
2185                 //
2186                 if (!in_lyx_preamble &&
2187                     (t.cat() == catLetter ||
2188                      t.cat() == catSuper ||
2189                      t.cat() == catSub ||
2190                      t.cat() == catOther ||
2191                      t.cat() == catMath ||
2192                      t.cat() == catActive ||
2193                      t.cat() == catBegin ||
2194                      t.cat() == catEnd ||
2195                      t.cat() == catAlign ||
2196                      t.cat() == catParameter)) {
2197                         h_preamble << t.cs();
2198                         continue;
2199                 }
2200
2201                 if (!in_lyx_preamble &&
2202                     (t.cat() == catSpace || t.cat() == catNewline)) {
2203                         h_preamble << t.asInput();
2204                         continue;
2205                 }
2206
2207                 if (t.cat() == catComment) {
2208                         static regex const islyxfile("%% LyX .* created this file");
2209                         static regex const usercommands("User specified LaTeX commands");
2210
2211                         string const comment = t.asInput();
2212
2213                         // magically switch encoding default if it looks like XeLaTeX
2214                         static string const magicXeLaTeX =
2215                                 "% This document must be compiled with XeLaTeX ";
2216                         if (comment.size() > magicXeLaTeX.size()
2217                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
2218                                   && h_inputencoding == "auto-legacy") {
2219                                 if (!detectEncoding)
2220                                         cerr << "XeLaTeX comment found, switching to UTF8\n";
2221                                 h_inputencoding = "utf8";
2222                         }
2223                         smatch sub;
2224                         if (regex_search(comment, sub, islyxfile)) {
2225                                 is_lyx_file = true;
2226                                 in_lyx_preamble = true;
2227                         } else if (is_lyx_file
2228                                    && regex_search(comment, sub, usercommands))
2229                                 in_lyx_preamble = false;
2230                         else if (!in_lyx_preamble)
2231                                 h_preamble << t.asInput();
2232                         continue;
2233                 }
2234
2235                 if (t.cs() == "PassOptionsToPackage") {
2236                         string const poptions = p.getArg('{', '}');
2237                         string const package = p.verbatim_item();
2238                         extra_package_options_.insert(make_pair(package, poptions));
2239                         continue;
2240                 }
2241
2242                 if (t.cs() == "pagestyle") {
2243                         h_paperpagestyle = p.verbatim_item();
2244                         continue;
2245                 }
2246
2247                 if (t.cs() == "setdefaultlanguage") {
2248                         xetex = true;
2249                         // We don't yet care about non-language variant options
2250                         // because LyX doesn't support this yet, see bug #8214
2251                         if (p.hasOpt()) {
2252                                 string langopts = p.getOpt();
2253                                 // check if the option contains a variant, if yes, extract it
2254                                 string::size_type pos_var = langopts.find("variant");
2255                                 string::size_type i = langopts.find(',', pos_var);
2256                                 string::size_type k = langopts.find('=', pos_var);
2257                                 if (pos_var != string::npos){
2258                                         string variant;
2259                                         if (i == string::npos)
2260                                                 variant = langopts.substr(k + 1, langopts.length() - k - 2);
2261                                         else
2262                                                 variant = langopts.substr(k + 1, i - k - 1);
2263                                         h_language = variant;
2264                                 }
2265                                 p.verbatim_item();
2266                         } else
2267                                 h_language = p.verbatim_item();
2268                         //finally translate the poyglossia name to a LyX name
2269                         h_language = polyglossia2lyx(h_language);
2270                         continue;
2271                 }
2272
2273                 if (t.cs() == "setotherlanguage") {
2274                         // We don't yet care about the option because LyX doesn't
2275                         // support this yet, see bug #8214
2276                         p.hasOpt() ? p.getOpt() : string();
2277                         p.verbatim_item();
2278                         continue;
2279                 }
2280
2281                 if (t.cs() == "setmainfont") {
2282                         string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2283                         h_font_roman[1] = p.getArg('{', '}');
2284                         if (!fontopts.empty()) {
2285                                 vector<string> opts = getVectorFromString(fontopts);
2286                                 fontopts.clear();
2287                                 for (auto const & opt : opts) {
2288                                         if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2289                                                 // ignore
2290                                                 continue;
2291                                         if (!fontopts.empty())
2292                                                 fontopts += ", ";
2293                                         fontopts += opt;
2294                                 }
2295                                 h_font_roman_opts = fontopts;
2296                         }
2297                         continue;
2298                 }
2299
2300                 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
2301                         // LyX currently only supports the scale option
2302                         string scale, fontopts;
2303                         if (p.hasOpt()) {
2304                                 fontopts = p.getArg('[', ']');
2305                                 if (!fontopts.empty()) {
2306                                         vector<string> opts = getVectorFromString(fontopts);
2307                                         fontopts.clear();
2308                                         for (auto const & opt : opts) {
2309                                                 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2310                                                         // ignore
2311                                                         continue;
2312                                                 if (prefixIs(opt, "Scale=")) {
2313                                                         scale_as_percentage(opt, scale);
2314                                                         continue;
2315                                                 }
2316                                                 if (!fontopts.empty())
2317                                                         fontopts += ", ";
2318                                                 fontopts += opt;
2319                                         }
2320                                 }
2321                         }
2322                         if (t.cs() == "setsansfont") {
2323                                 if (!scale.empty())
2324                                         h_font_sf_scale[1] = scale;
2325                                 h_font_sans[1] = p.getArg('{', '}');
2326                                 if (!fontopts.empty())
2327                                         h_font_sans_opts = fontopts;
2328                         } else {
2329                                 if (!scale.empty())
2330                                         h_font_tt_scale[1] = scale;
2331                                 h_font_typewriter[1] = p.getArg('{', '}');
2332                                 if (!fontopts.empty())
2333                                         h_font_typewriter_opts = fontopts;
2334                         }
2335                         continue;
2336                 }
2337
2338                 if (t.cs() == "babelfont") {
2339                         xetex = true;
2340                         h_use_non_tex_fonts = true;
2341                         h_language_package = "babel";
2342                         if (h_inputencoding == "auto-legacy")
2343                         p.setEncoding("UTF-8");
2344                         // we don't care about the lang option
2345                         string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
2346                         string const family = p.getArg('{', '}');
2347                         string fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
2348                         string const fontname = p.getArg('{', '}');
2349                         if (lang.empty() && family == "rm") {
2350                                 h_font_roman[1] = fontname;
2351                                 if (!fontopts.empty()) {
2352                                         vector<string> opts = getVectorFromString(fontopts);
2353                                         fontopts.clear();
2354                                         for (auto const & opt : opts) {
2355                                                 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2356                                                         // ignore
2357                                                         continue;
2358                                                 if (!fontopts.empty())
2359                                                         fontopts += ", ";
2360                                                 fontopts += opt;
2361                                         }
2362                                         h_font_roman_opts = fontopts;
2363                                 }
2364                                 continue;
2365                         } else if (lang.empty() && (family == "sf" || family == "tt")) {
2366                                 string scale;
2367                                 if (!fontopts.empty()) {
2368                                         vector<string> opts = getVectorFromString(fontopts);
2369                                         fontopts.clear();
2370                                         for (auto const & opt : opts) {
2371                                                 if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX")
2372                                                         // ignore
2373                                                         continue;
2374                                                 if (prefixIs(opt, "Scale=")) {
2375                                                         scale_as_percentage(opt, scale);
2376                                                         continue;
2377                                                 }
2378                                                 if (!fontopts.empty())
2379                                                         fontopts += ", ";
2380                                                 fontopts += opt;
2381                                         }
2382                                 }
2383                                 if (family == "sf") {
2384                                         if (!scale.empty())
2385                                                 h_font_sf_scale[1] = scale;
2386                                         h_font_sans[1] = fontname;
2387                                         if (!fontopts.empty())
2388                                                 h_font_sans_opts = fontopts;
2389                                 } else {
2390                                         if (!scale.empty())
2391                                                 h_font_tt_scale[1] = scale;
2392                                         h_font_typewriter[1] = fontname;
2393                                         if (!fontopts.empty())
2394                                                 h_font_typewriter_opts = fontopts;
2395                                 }
2396                                 continue;
2397                         } else {
2398                                 // not rm, sf or tt or lang specific
2399                                 h_preamble << '\\' << t.cs();
2400                                 if (!lang.empty())
2401                                         h_preamble << '[' << lang << ']';
2402                                 h_preamble << '{' << family << '}';
2403                                 if (!fontopts.empty())
2404                                         h_preamble << '[' << fontopts << ']';
2405                                 h_preamble << '{' << fontname << '}' << '\n';
2406                                 continue;
2407                         }
2408                 }
2409
2410                 if (t.cs() == "date") {
2411                         string argument = p.getArg('{', '}');
2412                         if (argument.empty())
2413                                 h_suppress_date = "true";
2414                         else
2415                                 h_preamble << t.asInput() << '{' << argument << '}';
2416                         continue;
2417                 }
2418
2419                 if (t.cs() == "color") {
2420                         string const space =
2421                                 (p.hasOpt() ? p.getOpt() : string());
2422                         string argument = p.getArg('{', '}');
2423                         // check the case that a standard color is used
2424                         if (space.empty() && is_known(argument, known_basic_colors)) {
2425                                 h_fontcolor = rgbcolor2code(argument);
2426                                 registerAutomaticallyLoadedPackage("color");
2427                         } else if (space.empty() && argument == "document_fontcolor")
2428                                 registerAutomaticallyLoadedPackage("color");
2429                         // check the case that LyX's document_fontcolor is defined
2430                         // but not used for \color
2431                         else {
2432                                 h_preamble << t.asInput();
2433                                 if (!space.empty())
2434                                         h_preamble << space;
2435                                 h_preamble << '{' << argument << '}';
2436                                 // the color might already be set because \definecolor
2437                                 // is parsed before this
2438                                 h_fontcolor = "";
2439                         }
2440                         continue;
2441                 }
2442
2443                 if (t.cs() == "pagecolor") {
2444                         string argument = p.getArg('{', '}');
2445                         // check the case that a standard color is used
2446                         if (is_known(argument, known_basic_colors)) {
2447                                 h_backgroundcolor = rgbcolor2code(argument);
2448                         } else if (argument == "page_backgroundcolor")
2449                                 registerAutomaticallyLoadedPackage("color");
2450                         // check the case that LyX's page_backgroundcolor is defined
2451                         // but not used for \pagecolor
2452                         else {
2453                                 h_preamble << t.asInput() << '{' << argument << '}';
2454                                 // the color might already be set because \definecolor
2455                                 // is parsed before this
2456                                 h_backgroundcolor = "";
2457                         }
2458                         continue;
2459                 }
2460
2461                 if (t.cs() == "makeatletter") {
2462                         // LyX takes care of this
2463                         p.setCatcode('@', catLetter);
2464                         continue;
2465                 }
2466
2467                 if (t.cs() == "makeatother") {
2468                         // LyX takes care of this
2469                         p.setCatcode('@', catOther);
2470                         continue;
2471                 }
2472
2473                 if (t.cs() == "makeindex") {
2474                         // LyX will re-add this if a print index command is found
2475                         p.skip_spaces();
2476                         continue;
2477                 }
2478
2479                 if (t.cs() == "newindex") {
2480                         string const indexname = p.getArg('[', ']');
2481                         string const shortcut = p.verbatim_item();
2482                         if (!indexname.empty())
2483                                 h_index[index_number] = indexname;
2484                         else
2485                                 h_index[index_number] = shortcut;
2486                         h_shortcut[index_number] = shortcut;
2487                         index_number += 1;
2488                         p.skip_spaces();
2489                         continue;
2490                 }
2491
2492                 if (t.cs() == "addbibresource") {
2493                         string const options =  p.getArg('[', ']');
2494                         string const arg = removeExtension(p.getArg('{', '}'));
2495                         if (!options.empty()) {
2496                                 // check if the option contains a bibencoding, if yes, extract it
2497                                 string::size_type pos = options.find("bibencoding=");
2498                                 string encoding;
2499                                 if (pos != string::npos) {
2500                                         string::size_type i = options.find(',', pos);
2501                                         if (i == string::npos)
2502                                                 encoding = options.substr(pos + 1);
2503                                         else
2504                                                 encoding = options.substr(pos, i - pos);
2505                                         pos = encoding.find('=');
2506                                         if (pos == string::npos)
2507                                                 encoding.clear();
2508                                         else
2509                                                 encoding = encoding.substr(pos + 1);
2510                                 }
2511                                 if (!encoding.empty())
2512                                         biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
2513                         }
2514                         biblatex_bibliographies.push_back(arg);
2515                         continue;
2516                 }
2517
2518                 if (t.cs() == "bibliography") {
2519                         vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
2520                         biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
2521                         continue;
2522                 }
2523
2524                 if (t.cs() == "RS@ifundefined") {
2525                         string const name = p.verbatim_item();
2526                         string const body1 = p.verbatim_item();
2527                         string const body2 = p.verbatim_item();
2528                         // only non-lyxspecific stuff
2529                         if (in_lyx_preamble &&
2530                             (name == "subsecref" || name == "thmref" || name == "lemref"))
2531                                 p.skip_spaces();
2532                         else {
2533                                 ostringstream ss;
2534                                 ss << '\\' << t.cs();
2535                                 ss << '{' << name << '}'
2536                                    << '{' << body1 << '}'
2537                                    << '{' << body2 << '}';
2538                                 h_preamble << ss.str();
2539                         }
2540                         continue;
2541                 }
2542
2543                 if (t.cs() == "AtBeginDocument") {
2544                         string const name = p.verbatim_item();
2545                         // only non-lyxspecific stuff
2546                         if (in_lyx_preamble &&
2547                             (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
2548                                 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
2549                                 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
2550                                 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
2551                                 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
2552                                 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
2553                                 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
2554                                 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
2555                                 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
2556                                 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
2557                                 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
2558                                 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
2559                                 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
2560                                 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
2561                                 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
2562                                 p.skip_spaces();
2563                         else {
2564                                 ostringstream ss;
2565                                 ss << '\\' << t.cs();
2566                                 ss << '{' << name << '}';
2567                                 h_preamble << ss.str();
2568                         }
2569                         continue;
2570                 }
2571
2572                 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
2573                     || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
2574                     || t.cs() == "providecommand" || t.cs() == "providecommandx"
2575                     || t.cs() == "DeclareRobustCommand"
2576                     || t.cs() == "DeclareRobustCommandx"
2577                     || t.cs() == "ProvideTextCommandDefault"
2578                     || t.cs() == "DeclareMathAccent") {
2579                         bool star = false;
2580                         if (p.next_token().character() == '*') {
2581                                 p.get_token();
2582                                 star = true;
2583                         }
2584                         string const name = p.verbatim_item();
2585                         string const opt1 = p.getFullOpt();
2586                         string const opt2 = p.getFullOpt();
2587                         string const body = p.verbatim_item();
2588                         // store the in_lyx_preamble setting
2589                         bool const was_in_lyx_preamble = in_lyx_preamble;
2590                         // font settings
2591                         if (name == "\\rmdefault")
2592                                 if (is_known(body, known_roman_font_packages)) {
2593                                         h_font_roman[0] = body;
2594                                         p.skip_spaces();
2595                                         in_lyx_preamble = true;
2596                                 }
2597                         if (name == "\\sfdefault") {
2598                                 if (is_known(body, known_sans_font_packages)) {
2599                                         h_font_sans[0] = body;
2600                                         p.skip_spaces();
2601                                         in_lyx_preamble = true;
2602                                 }
2603                                 if (body == "LibertinusSans-OsF") {
2604                                         h_font_sans[0] = "LibertinusSans-LF";
2605                                         h_font_sans_osf = "true";
2606                                         p.skip_spaces();
2607                                         in_lyx_preamble = true;
2608                                 }
2609                         }
2610                         if (name == "\\ttdefault")
2611                                 if (is_known(body, known_typewriter_font_packages)) {
2612                                         h_font_typewriter[0] = body;
2613                                         p.skip_spaces();
2614                                         in_lyx_preamble = true;
2615                                 }
2616                         if (name == "\\familydefault") {
2617                                 string family = body;
2618                                 // remove leading "\"
2619                                 h_font_default_family = family.erase(0,1);
2620                                 p.skip_spaces();
2621                                 in_lyx_preamble = true;
2622                         }
2623                         if (name == "\\LibertinusSans@scale") {
2624                                 if (isStrDbl(body)) {
2625                                         h_font_sf_scale[0] = convert<string>(
2626                                                 static_cast<int>(100 * convert<double>(body)));
2627                                 }
2628                         }
2629                         if (name == "\\LibertinusMono@scale") {
2630                                 if (isStrDbl(body)) {
2631                                         h_font_tt_scale[0] = convert<string>(
2632                                                 static_cast<int>(100 * convert<double>(body)));
2633                                 }
2634                         }
2635
2636                         // remove LyX-specific definitions that are re-added by LyX
2637                         // if necessary
2638                         // \lyxline is an ancient command that is converted by tex2lyx into
2639                         // a \rule therefore remove its preamble code
2640                         if (name == "\\lyxdot" || name == "\\lyxarrow"
2641                             || name == "\\lyxline" || name == "\\LyX") {
2642                                 p.skip_spaces();
2643                                 in_lyx_preamble = true;
2644                         }
2645
2646                         // Add the command to the known commands
2647                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
2648
2649                         // only non-lyxspecific stuff
2650                         if (!in_lyx_preamble) {
2651                                 ostringstream ss;
2652                                 ss << '\\' << t.cs();
2653                                 if (star)
2654                                         ss << '*';
2655                                 ss << '{' << name << '}' << opt1 << opt2
2656                                    << '{' << body << "}";
2657                                 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
2658                                         h_preamble << ss.str();
2659 /*
2660                                 ostream & out = in_preamble ? h_preamble : os;
2661                                 out << "\\" << t.cs() << "{" << name << "}"
2662                                     << opts << "{" << body << "}";
2663 */
2664                         }
2665                         // restore the in_lyx_preamble setting
2666                         in_lyx_preamble = was_in_lyx_preamble;
2667                         continue;
2668                 }
2669
2670                 if (t.cs() == "documentclass") {
2671                         vector<string>::iterator it;
2672                         vector<string> opts = split_options(p.getArg('[', ']'));
2673                         // FIXME This does not work for classes that have a
2674                         //       different name in LyX than in LaTeX
2675                         string const tclass = p.getArg('{', '}');
2676                         p.skip_spaces();
2677                         // Only set text class if a class hasn't been forced
2678                         // (this was set above)
2679                         if (!class_set) {
2680                                 h_textclass = tclass;
2681                                 // textclass needs to be set at this place as we need to know
2682                                 // it for other parameters (such as class-dependent paper size)
2683                                 tc.setName(h_textclass);
2684                                 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2685                                         cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2686                                         exit(EXIT_FAILURE);
2687                                 }
2688                                 class_set = true;
2689                         }
2690
2691                         // Font sizes.
2692                         // Try those who are (most likely) known to all packages first
2693                         handle_opt(opts, known_fontsizes, h_paperfontsize);
2694                         delete_opt(opts, known_fontsizes);
2695                         // delete "pt" at the end
2696                         string::size_type i = h_paperfontsize.find("pt");
2697                         if (i != string::npos)
2698                                 h_paperfontsize.erase(i);
2699                         // Now those known specifically to the class
2700                         vector<string> class_fsizes = getVectorFromString(tc.opt_fontsize(), "|");
2701                         string const fsize_format = tc.fontsizeformat();
2702                         for (auto const & fsize : class_fsizes) {
2703                                 string latexsize = subst(fsize_format, "$$s", fsize);
2704                                 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2705                                 if (it != opts.end()) {
2706                                         h_paperfontsize = fsize;
2707                                         opts.erase(it);
2708                                         break;
2709                                 }
2710                         }
2711
2712                         // The documentclass options are always parsed before the options
2713                         // of the babel call so that a language cannot overwrite the babel
2714                         // options.
2715                         handle_opt(opts, known_languages, h_language);
2716                         delete_opt(opts, known_languages);
2717
2718                         // math indentation
2719                         if ((it = find(opts.begin(), opts.end(), "fleqn"))
2720                                  != opts.end()) {
2721                                 h_is_mathindent = "1";
2722                                 opts.erase(it);
2723                         }
2724                         // formula numbering side
2725                         if ((it = find(opts.begin(), opts.end(), "leqno"))
2726                                  != opts.end()) {
2727                                 h_math_numbering_side = "left";
2728                                 opts.erase(it);
2729                         }
2730                         else if ((it = find(opts.begin(), opts.end(), "reqno"))
2731                                  != opts.end()) {
2732                                 h_math_numbering_side = "right";
2733                                 opts.erase(it);
2734                         }
2735
2736                         // paper orientation
2737                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2738                                 h_paperorientation = "landscape";
2739                                 opts.erase(it);
2740                         }
2741                         // paper sides
2742                         if ((it = find(opts.begin(), opts.end(), "oneside"))
2743                                  != opts.end()) {
2744                                 h_papersides = "1";
2745                                 opts.erase(it);
2746                         }
2747                         if ((it = find(opts.begin(), opts.end(), "twoside"))
2748                                  != opts.end()) {
2749                                 h_papersides = "2";
2750                                 opts.erase(it);
2751                         }
2752                         // paper columns
2753                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2754                                  != opts.end()) {
2755                                 h_papercolumns = "1";
2756                                 opts.erase(it);
2757                         }
2758                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2759                                  != opts.end()) {
2760                                 h_papercolumns = "2";
2761                                 opts.erase(it);
2762                         }
2763                         // paper sizes
2764                         // some size options are known by the document class, other sizes
2765                         // are handled by the \geometry command of the geometry package
2766                         string paper;
2767                         vector<string> class_psizes = getVectorFromString(tc.opt_pagesize(), "|");
2768                         string const psize_format = tc.pagesizeformat();
2769                         for (auto const & psize : class_psizes) {
2770                                 string latexsize = subst(psize_format, "$$s", psize);
2771                                 vector<string>::iterator it = find(opts.begin(), opts.end(), latexsize);
2772                                 if (it != opts.end()) {
2773                                         h_papersize = psize;
2774                                         opts.erase(it);
2775                                         break;
2776                                 }
2777                                 if (psize_format == "$$spaper")
2778                                         continue;
2779                                 // Also try with the default format since this is understood by
2780                                 // most classes
2781                                 latexsize = psize + "paper";
2782                                 it = find(opts.begin(), opts.end(), latexsize);
2783                                 if (it != opts.end()) {
2784                                         h_papersize = psize;
2785                                         opts.erase(it);
2786                                         break;
2787                                 }
2788                         }
2789                         // the remaining options
2790                         h_options = join(opts, ",");
2791                         continue;
2792                 }
2793
2794                 if (t.cs() == "usepackage") {
2795                         string const options = p.getArg('[', ']');
2796                         string const name = p.getArg('{', '}');
2797                         vector<string> vecnames;
2798                         split(name, vecnames, ',');
2799                         vector<string>::const_iterator it  = vecnames.begin();
2800                         vector<string>::const_iterator end = vecnames.end();
2801                         for (; it != end; ++it)
2802                                 handle_package(p, trimSpaceAndEol(*it), options,
2803                                                in_lyx_preamble, detectEncoding);
2804                         continue;
2805                 }
2806
2807                 if (t.cs() == "inputencoding") {
2808                         string const encoding = p.getArg('{','}');
2809                         Encoding const * const enc = encodings.fromLaTeXName(
2810                                 encoding, Encoding::inputenc, true);
2811                         if (!enc) {
2812                                 if (!detectEncoding)
2813                                         cerr << "Unknown encoding " << encoding
2814                                              << ". Ignoring." << std::endl;
2815                         } else {
2816                                 if (!enc->unsafe())
2817                                         h_inputencoding = enc->name();
2818                                 p.setEncoding(enc->iconvName());
2819                         }
2820                         continue;
2821                 }
2822
2823                 if (t.cs() == "newenvironment") {
2824                         string const name = p.getArg('{', '}');
2825                         string const opt1 = p.getFullOpt();
2826                         string const opt2 = p.getFullOpt();
2827                         string const beg = p.verbatim_item();
2828                         string const end = p.verbatim_item();
2829                         if (!in_lyx_preamble) {
2830                                 h_preamble << "\\newenvironment{" << name
2831                                            << '}' << opt1 << opt2 << '{'
2832                                            << beg << "}{" << end << '}';
2833                         }
2834                         add_known_environment(name, opt1, !opt2.empty(),
2835                                               from_utf8(beg), from_utf8(end));
2836                         continue;
2837                 }
2838
2839                 if (t.cs() == "newtheorem") {
2840                         bool star = false;
2841                         if (p.next_token().character() == '*') {
2842                                 p.get_token();
2843                                 star = true;
2844                         }
2845                         string const name = p.getArg('{', '}');
2846                         string const opt1 = p.getFullOpt();
2847                         string const opt2 = p.getFullOpt();
2848                         string const body = p.verbatim_item();
2849                         string const opt3 = p.getFullOpt();
2850                         string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2851
2852                         string const complete = cmd + "{" + name + '}' +
2853                                           opt1 + opt2 + '{' + body + '}' + opt3;
2854
2855                         add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2856
2857                         if (!in_lyx_preamble)
2858                                 h_preamble << complete;
2859                         continue;
2860                 }
2861
2862                 if (t.cs() == "def") {
2863                         string name = p.get_token().cs();
2864                         // In fact, name may be more than the name:
2865                         // In the test case of bug 8116
2866                         // name == "csname SF@gobble@opt \endcsname".
2867                         // Therefore, we need to use asInput() instead of cs().
2868                         while (p.next_token().cat() != catBegin)
2869                                 name += p.get_token().asInput();
2870                         if (!in_lyx_preamble)
2871                                 h_preamble << "\\def\\" << name << '{'
2872                                            << p.verbatim_item() << "}";
2873                         continue;
2874                 }
2875
2876                 if (t.cs() == "newcolumntype") {
2877                         string const name = p.getArg('{', '}');
2878                         trimSpaceAndEol(name);
2879                         int nargs = 0;
2880                         string opts = p.getOpt();
2881                         if (!opts.empty()) {
2882                                 istringstream is(string(opts, 1));
2883                                 is >> nargs;
2884                         }
2885                         special_columns_[name[0]] = nargs;
2886                         h_preamble << "\\newcolumntype{" << name << "}";
2887                         if (nargs)
2888                                 h_preamble << "[" << nargs << "]";
2889                         h_preamble << "{" << p.verbatim_item() << "}";
2890                         continue;
2891                 }
2892
2893                 if (t.cs() == "setcounter") {
2894                         string const name = p.getArg('{', '}');
2895                         string const content = p.getArg('{', '}');
2896                         if (name == "secnumdepth")
2897                                 h_secnumdepth = content;
2898                         else if (name == "tocdepth")
2899                                 h_tocdepth = content;
2900                         else
2901                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2902                         continue;
2903                 }
2904
2905                 if (t.cs() == "setlength") {
2906                         string const name = p.verbatim_item();
2907                         string const content = p.verbatim_item();
2908                         // the paragraphs are only not indented when \parindent is set to zero
2909                         if (name == "\\parindent" && content != "")
2910                                 h_paragraph_indentation = translate_len(content);
2911                         else if (name == "\\parskip" && isPackageUsed("parskip")) {
2912                                 if (content == "\\smallskipamount")
2913                                         h_defskip = "smallskip";
2914                                 else if (content == "\\medskipamount")
2915                                         h_defskip = "medskip";
2916                                 else if (content == "\\bigskipamount")
2917                                         h_defskip = "bigskip";
2918                                 else if (content == "\\baselineskip")
2919                                         h_defskip = "fullline";
2920                                 else
2921                                         h_defskip = translate_len(content);
2922                         } else if (name == "\\mathindent") {
2923                                 h_mathindentation = translate_len(content);
2924                         } else
2925                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2926                         continue;
2927                 }
2928
2929                 if (t.cs() == "onehalfspacing") {
2930                         h_spacing = "onehalf";
2931                         continue;
2932                 }
2933
2934                 if (t.cs() == "doublespacing") {
2935                         h_spacing = "double";
2936                         continue;
2937                 }
2938
2939                 if (t.cs() == "setstretch") {
2940                         h_spacing = "other " + p.verbatim_item();
2941                         continue;
2942                 }
2943
2944                 if (t.cs() == "synctex") {
2945                         // the scheme is \synctex=value
2946                         // where value can only be "1" or "-1"
2947                         h_output_sync = "1";
2948                         // there can be any character behind the value (e.g. a linebreak or a '\'
2949                         // therefore we extract it char by char
2950                         p.get_token();
2951                         string value = p.get_token().asInput();
2952                         if (value == "-")
2953                                 value += p.get_token().asInput();
2954                         h_output_sync_macro = "\\synctex=" + value;
2955                         continue;
2956                 }
2957
2958                 if (t.cs() == "begin") {
2959                         string const name = p.getArg('{', '}');
2960                         if (name == "document")
2961                                 break;
2962                         h_preamble << "\\begin{" << name << "}";
2963                         continue;
2964                 }
2965
2966                 if (t.cs() == "geometry") {
2967                         vector<string> opts = split_options(p.getArg('{', '}'));
2968                         handle_geometry(opts);
2969                         continue;
2970                 }
2971
2972                 if (t.cs() == "definecolor") {
2973                         string const color = p.getArg('{', '}');
2974                         string const space = p.getArg('{', '}');
2975                         string const value = p.getArg('{', '}');
2976                         if (color == "document_fontcolor" && space == "rgb") {
2977                                 RGBColor c(RGBColorFromLaTeX(value));
2978                                 h_fontcolor = X11hexname(c);
2979                         } else if (color == "note_fontcolor" && space == "rgb") {
2980                                 RGBColor c(RGBColorFromLaTeX(value));
2981                                 h_notefontcolor = X11hexname(c);
2982                         } else if (color == "page_backgroundcolor" && space == "rgb") {
2983                                 RGBColor c(RGBColorFromLaTeX(value));
2984                                 h_backgroundcolor = X11hexname(c);
2985                         } else if (color == "shadecolor" && space == "rgb") {
2986                                 RGBColor c(RGBColorFromLaTeX(value));
2987                                 h_boxbgcolor = X11hexname(c);
2988                         } else {
2989                                 h_preamble << "\\definecolor{" << color
2990                                            << "}{" << space << "}{" << value
2991                                            << '}';
2992                         }
2993                         continue;
2994                 }
2995
2996                 if (t.cs() == "bibliographystyle") {
2997                         h_biblio_style = p.verbatim_item();
2998                         continue;
2999                 }
3000
3001                 if (t.cs() == "jurabibsetup") {
3002                         // FIXME p.getArg('{', '}') is most probably wrong (it
3003                         //       does not handle nested braces).
3004                         //       Use p.verbatim_item() instead.
3005                         vector<string> jurabibsetup =
3006                                 split_options(p.getArg('{', '}'));
3007                         // add jurabibsetup to the jurabib package options
3008                         add_package("jurabib", jurabibsetup);
3009                         if (!jurabibsetup.empty()) {
3010                                 h_preamble << "\\jurabibsetup{"
3011                                            << join(jurabibsetup, ",") << '}';
3012                         }
3013                         continue;
3014                 }
3015
3016                 if (t.cs() == "hypersetup") {
3017                         vector<string> hypersetup =
3018                                 split_options(p.verbatim_item());
3019                         // add hypersetup to the hyperref package options
3020                         handle_hyperref(hypersetup);
3021                         if (!hypersetup.empty()) {
3022                                 h_preamble << "\\hypersetup{"
3023                                            << join(hypersetup, ",") << '}';
3024                         }
3025                         continue;
3026                 }
3027
3028                 if (t.cs() == "includeonly") {
3029                         vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
3030                         for (auto & iofile : includeonlys) {
3031                                 string filename(normalize_filename(iofile));
3032                                 string const path = getMasterFilePath(true);
3033                                 // We want to preserve relative/absolute filenames,
3034                                 // therefore path is only used for testing
3035                                 if (!makeAbsPath(filename, path).exists()) {
3036                                         // The file extension is probably missing.
3037                                         // Now try to find it out.
3038                                         string const tex_name =
3039                                                 find_file(filename, path,
3040                                                           known_tex_extensions);
3041                                         if (!tex_name.empty())
3042                                                 filename = tex_name;
3043                                 }
3044                                 string outname;
3045                                 if (makeAbsPath(filename, path).exists())
3046                                         fix_child_filename(filename);
3047                                 else
3048                                         cerr << "Warning: Could not find included file '"
3049                                              << filename << "'." << endl;
3050                                 outname = changeExtension(filename, "lyx");
3051                                 h_includeonlys.push_back(outname);
3052                         }
3053                         continue;
3054                 }
3055
3056                 if (is_known(t.cs(), known_if_3arg_commands)) {
3057                         // prevent misparsing of \usepackage if it is used
3058                         // as an argument (see e.g. our own output of
3059                         // \@ifundefined above)
3060                         string const arg1 = p.verbatim_item();
3061                         string const arg2 = p.verbatim_item();
3062                         string const arg3 = p.verbatim_item();
3063                         // test case \@ifundefined{date}{}{\date{}}
3064                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
3065                             arg2.empty() && arg3 == "\\date{}") {
3066                                 h_suppress_date = "true";
3067                         // older tex2lyx versions did output
3068                         // \@ifundefined{definecolor}{\usepackage{color}}{}
3069                         } else if (t.cs() == "@ifundefined" &&
3070                                    arg1 == "definecolor" &&
3071                                    arg2 == "\\usepackage{color}" &&
3072                                    arg3.empty()) {
3073                                 if (!in_lyx_preamble)
3074                                         h_preamble << package_beg_sep
3075                                                    << "color"
3076                                                    << package_mid_sep
3077                                                    << "\\@ifundefined{definecolor}{color}{}"
3078                                                    << package_end_sep;
3079                         // test for case
3080                         //\@ifundefined{showcaptionsetup}{}{%
3081                         // \PassOptionsToPackage{caption=false}{subfig}}
3082                         // that LyX uses for subfloats
3083                         } else if (t.cs() == "@ifundefined" &&
3084                                    arg1 == "showcaptionsetup" && arg2.empty()
3085                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
3086                                 ; // do nothing
3087                         } else if (!in_lyx_preamble) {
3088                                 h_preamble << t.asInput()
3089                                            << '{' << arg1 << '}'
3090                                            << '{' << arg2 << '}'
3091                                            << '{' << arg3 << '}';
3092                         }
3093                         continue;
3094                 }
3095
3096                 if (is_known(t.cs(), known_if_commands)) {
3097                         // must not parse anything in conditional code, since
3098                         // LyX would output the parsed contents unconditionally
3099                         if (!in_lyx_preamble)
3100                                 h_preamble << t.asInput();
3101                         handle_if(p, in_lyx_preamble);
3102                         continue;
3103                 }
3104
3105                 if (!t.cs().empty() && !in_lyx_preamble) {
3106                         h_preamble << '\\' << t.cs();
3107                         continue;
3108                 }
3109         }
3110
3111         // set textclass if not yet done (snippets without \documentclass and forced class)
3112         if (!class_set) {
3113                 tc.setName(h_textclass);
3114                 if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
3115                         cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
3116                         exit(EXIT_FAILURE);
3117                 }
3118         }
3119         
3120         // remove the whitespace
3121         p.skip_spaces();
3122
3123         if (h_papersides.empty()) {
3124                 ostringstream ss;
3125                 ss << tc.sides();
3126                 h_papersides = ss.str();
3127         }
3128
3129         // If the CJK package is used we cannot set the document language from
3130         // the babel options. Instead, we guess which language is used most
3131         // and set this one.
3132         default_language = h_language;
3133         if (is_full_document &&
3134             (auto_packages.find("CJK") != auto_packages.end() ||
3135              auto_packages.find("CJKutf8") != auto_packages.end())) {
3136                 p.pushPosition();
3137                 h_language = guessLanguage(p, default_language);
3138                 p.popPosition();
3139                 if (explicit_babel && h_language != default_language) {
3140                         // We set the document language to a CJK language,
3141                         // but babel is explicitly called in the user preamble
3142                         // without options. LyX will not add the default
3143                         // language to the document options if it is either
3144                         // english, or no text is set as default language.
3145                         // Therefore we need to add a language option explicitly.
3146                         // FIXME: It would be better to remove all babel calls
3147                         //        from the user preamble, but this is difficult
3148                         //        without re-introducing bug 7861.
3149                         if (h_options.empty())
3150                                 h_options = lyx2babel(default_language);
3151                         else
3152                                 h_options += ',' + lyx2babel(default_language);
3153                 }
3154         }
3155
3156         // Finally, set the quote style.
3157         // LyX knows the following quotes styles:
3158         // british, cjk, cjkangle, danish, english, french, german,
3159         // polish, russian, swedish and swiss
3160         // conversion list taken from
3161         // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
3162         // (quotes for kazakh are unknown)
3163         // british
3164         if (is_known(h_language, known_british_quotes_languages))
3165                 h_quotes_style = "british";
3166         // cjk
3167         else if (is_known(h_language, known_cjk_quotes_languages))
3168                 h_quotes_style = "cjk";
3169         // cjkangle
3170         else if (is_known(h_language, known_cjkangle_quotes_languages))
3171                 h_quotes_style = "cjkangle";
3172         // danish
3173         else if (is_known(h_language, known_danish_quotes_languages))
3174                 h_quotes_style = "danish";
3175         // french
3176         else if (is_known(h_language, known_french_quotes_languages))
3177                 h_quotes_style = "french";
3178         // german
3179         else if (is_known(h_language, known_german_quotes_languages))
3180                 h_quotes_style = "german";
3181         // polish
3182         else if (is_known(h_language, known_polish_quotes_languages))
3183                 h_quotes_style = "polish";
3184         // russian
3185         else if (is_known(h_language, known_russian_quotes_languages))
3186                 h_quotes_style = "russian";
3187         // swedish
3188         else if (is_known(h_language, known_swedish_quotes_languages))
3189                 h_quotes_style = "swedish";
3190         // swiss
3191         else if (is_known(h_language, known_swiss_quotes_languages))
3192                 h_quotes_style = "swiss";
3193         // english
3194         else if (is_known(h_language, known_english_quotes_languages))
3195                 h_quotes_style = "english";
3196 }
3197
3198
3199 string Preamble::parseEncoding(Parser & p, string const & forceclass)
3200 {
3201         TeX2LyXDocClass dummy;
3202         parse(p, forceclass, true, dummy);
3203         if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
3204                 return h_inputencoding;
3205         return "";
3206 }
3207
3208
3209 string babel2lyx(string const & language)
3210 {
3211         char const * const * where = is_known(language, known_languages);
3212         if (where)
3213                 return known_coded_languages[where - known_languages];
3214         return language;
3215 }
3216
3217
3218 string lyx2babel(string const & language)
3219 {
3220         char const * const * where = is_known(language, known_coded_languages);
3221         if (where)
3222                 return known_languages[where - known_coded_languages];
3223         return language;
3224 }
3225
3226
3227 string Preamble::polyglossia2lyx(string const & language)
3228 {
3229         char const * const * where = is_known(language, polyglossia_languages);
3230         if (where)
3231                 return coded_polyglossia_languages[where - polyglossia_languages];
3232         return language;
3233 }
3234
3235
3236 string rgbcolor2code(string const & name)
3237 {
3238         char const * const * where = is_known(name, known_basic_colors);
3239         if (where) {
3240                 // "red", "green" etc
3241                 return known_basic_color_codes[where - known_basic_colors];
3242         }
3243         // "255,0,0", "0,255,0" etc
3244         RGBColor c(RGBColorFromLaTeX(name));
3245         return X11hexname(c);
3246 }
3247
3248 // }])
3249
3250
3251 } // namespace lyx