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