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