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