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