]> git.lyx.org Git - features.git/blob - src/tex2lyx/Preamble.cpp
tex2lyx: support for URW Classico, MinionPro and the new Libertine fonts.
[features.git] / src / tex2lyx / Preamble.cpp
1 /**
2  * \file Preamble.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  * \author Uwe Stöhr
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 // {[(
13
14 #include <config.h>
15
16 #include "Preamble.h"
17 #include "tex2lyx.h"
18
19 #include "Encoding.h"
20 #include "LayoutFile.h"
21 #include "Layout.h"
22 #include "Lexer.h"
23 #include "TextClass.h"
24 #include "version.h"
25
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
30
31 #include "support/regex.h"
32
33 #include <algorithm>
34 #include <iostream>
35
36 using namespace std;
37 using namespace lyx::support;
38
39
40 namespace lyx {
41
42 Preamble preamble;
43
44 namespace {
45
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
47 // further down.
48 /**
49  * known babel language names (including synonyms)
50  * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51  * please keep this in sync with known_coded_languages line by line!
52  */
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "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", "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 danish quotes (.lyx names)
92 const char * const known_danish_quotes_languages[] = {"danish", 0};
93
94 /// languages with english quotes (.lyx names)
95 const char * const known_english_quotes_languages[] = {"american", "australian",
96 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
97 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
98 "thai", 0};
99
100 /// languages with french quotes (.lyx names)
101 const char * const known_french_quotes_languages[] = {"albanian",
102 "arabic_arabi", "arabic_arabtex", "asturian", "basque", "canadien", "catalan",
103 "french", "friulan", "galician", "greek", "italian", "norsk", "nynorsk",
104 "piedmontese", "polutonikogreek", "russian", "spanish", "spanish-mexico",
105 "turkish", "turkmen", "ukrainian", "vietnamese", 0};
106
107 /// languages with german quotes (.lyx names)
108 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
109 "czech", "german", "georgian", "icelandic", "lithuanian", "lowersorbian", "macedonian",
110 "naustrian", "ngerman", "romansh", "serbian", "serbian-latin", "slovak", "slovene",
111 "uppersorbian", 0};
112
113 /// languages with polish quotes (.lyx names)
114 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
115 "dutch", "estonian", "magyar", "polish", "romanian", 0};
116
117 /// languages with swedish quotes (.lyx names)
118 const char * const known_swedish_quotes_languages[] = {"finnish",
119 "swedish", 0};
120
121 /// known language packages from the times before babel
122 const char * const known_old_language_packages[] = {"french", "frenchle",
123 "frenchpro", "german", "ngerman", "pmfrench", 0};
124
125 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
126
127 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
128 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "fourier",
129 "garamondx", "libertine", "libertineRoman", "libertine-type1", "lmodern", "mathdesign", "mathpazo",
130 "mathptmx", "MinionPro", "newcent", "NotoSerif-TLF", "PTSerif-TLF", "tgbonum", "tgchorus",
131 "tgpagella", "tgschola", "tgtermes", "utopia", 0 };
132
133 const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum",
134 "biolinum-type1", "cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc",
135 "kurier", "kurierc", "kurierl", "kurierlc", "lmss", "NotoSans-TLF", "PTSans-TLF",
136 "tgadventor", "tgheros", "uop", 0 };
137
138 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
139 "courier", "lmtt", "luximono", "fourier", "libertineMono", "libertineMono-type1", "lmodern",
140 "mathpazo", "mathptmx", "newcent", "NotoMono-TLF", "PTMono-TLF", "tgcursor", "txtt", 0 };
141
142 const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0};
143
144 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
145 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
146 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
147 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
148 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
149
150 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
151 "executivepaper", "legalpaper", "letterpaper", 0};
152
153 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
154 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
155
156 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
157 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
158 "columnsep", 0};
159
160 /// commands that can start an \if...\else...\endif sequence
161 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
162 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
163 "ifsidecap", "ifupgreek", 0};
164
165 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
166         "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
167         "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
168
169 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
170         "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
171         "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
172
173 /// conditional commands with three arguments like \@ifundefined{}{}{}
174 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
175 0};
176
177 /*!
178  * Known file extensions for TeX files as used by \\includeonly
179  */
180 char const * const known_tex_extensions[] = {"tex", 0};
181
182 /// packages that work only in xetex
183 /// polyglossia is handled separately
184 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
185 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
186 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
187
188 /// packages that are automatically skipped if loaded by LyX
189 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
190 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
191 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
192 "makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle",
193 "rotating", "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa",
194 "tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor",
195 "xunicode", 0};
196
197 // codes used to remove packages that are loaded automatically by LyX.
198 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
199 const char package_beg_sep = '\001';
200 const char package_mid_sep = '\002';
201 const char package_end_sep = '\003';
202
203
204 // returns true if at least one of the options in what has been found
205 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
206 {
207         if (opts.empty())
208                 return false;
209
210         bool found = false;
211         // the last language option is the document language (for babel and LyX)
212         // the last size option is the document font size
213         vector<string>::iterator it;
214         vector<string>::iterator position = opts.begin();
215         for (; *what; ++what) {
216                 it = find(opts.begin(), opts.end(), *what);
217                 if (it != opts.end()) {
218                         if (it >= position) {
219                                 found = true;
220                                 target = *what;
221                                 position = it;
222                         }
223                 }
224         }
225         return found;
226 }
227
228
229 void delete_opt(vector<string> & opts, char const * const * what)
230 {
231         if (opts.empty())
232                 return;
233
234         // remove found options from the list
235         // do this after handle_opt to avoid potential memory leaks
236         vector<string>::iterator it;
237         for (; *what; ++what) {
238                 it = find(opts.begin(), opts.end(), *what);
239                 if (it != opts.end())
240                         opts.erase(it);
241         }
242 }
243
244
245 /*!
246  * Split a package options string (keyval format) into a vector.
247  * Example input:
248  *   authorformat=smallcaps,
249  *   commabeforerest,
250  *   titleformat=colonsep,
251  *   bibformat={tabular,ibidem,numbered}
252  */
253 vector<string> split_options(string const & input)
254 {
255         vector<string> options;
256         string option;
257         Parser p(input);
258         while (p.good()) {
259                 Token const & t = p.get_token();
260                 if (t.asInput() == ",") {
261                         options.push_back(trimSpaceAndEol(option));
262                         option.erase();
263                 } else if (t.asInput() == "=") {
264                         option += '=';
265                         p.skip_spaces(true);
266                         if (p.next_token().asInput() == "{")
267                                 option += '{' + p.getArg('{', '}') + '}';
268                 } else if (t.cat() != catSpace && t.cat() != catComment)
269                         option += t.asInput();
270         }
271
272         if (!option.empty())
273                 options.push_back(trimSpaceAndEol(option));
274
275         return options;
276 }
277
278
279 /*!
280  * Retrieve a keyval option "name={value with=sign}" named \p name from
281  * \p options and return the value.
282  * The found option is also removed from \p options.
283  */
284 string process_keyval_opt(vector<string> & options, string name)
285 {
286         for (size_t i = 0; i < options.size(); ++i) {
287                 vector<string> option;
288                 split(options[i], option, '=');
289                 if (option.size() < 2)
290                         continue;
291                 if (option[0] == name) {
292                         options.erase(options.begin() + i);
293                         option.erase(option.begin());
294                         return join(option, "=");
295                 }
296         }
297         return "";
298 }
299
300 } // anonymous namespace
301
302
303 /**
304  * known polyglossia language names (including variants)
305  * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
306  */
307 const char * const Preamble::polyglossia_languages[] = {
308 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
309 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
310 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
311 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
312 "galician", "greek", "monotonic", "hebrew", "hindi",
313 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
314 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
315 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan",
316 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
317 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
318 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
319 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
320 // not yet supported by LyX: "korean", "nko"
321
322 /**
323  * the same as polyglossia_languages with .lyx names
324  * please keep this in sync with polyglossia_languages line by line!
325  */
326 const char * const Preamble::coded_polyglossia_languages[] = {
327 "albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
328 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
329 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
330 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
331 "galician", "greek", "greek", "hebrew", "hindi",
332 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
333 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
334 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan",
335 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
336 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
337 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
338 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
339 // not yet supported by LyX: "korean-polyglossia", "nko"
340
341
342 bool Preamble::usePolyglossia() const
343 {
344         return h_use_non_tex_fonts && h_language_package == "default";
345 }
346
347
348 bool Preamble::indentParagraphs() const
349 {
350         return h_paragraph_separation == "indent";
351 }
352
353
354 bool Preamble::isPackageUsed(string const & package) const
355 {
356         return used_packages.find(package) != used_packages.end();
357 }
358
359
360 vector<string> Preamble::getPackageOptions(string const & package) const
361 {
362         map<string, vector<string> >::const_iterator it = used_packages.find(package);
363         if (it != used_packages.end())
364                 return it->second;
365         return vector<string>();
366 }
367
368
369 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
370 {
371         auto_packages.insert(package);
372 }
373
374
375 void Preamble::addModule(string const & module)
376 {
377         used_modules.push_back(module);
378 }
379
380
381 void Preamble::suppressDate(bool suppress)
382 {
383         if (suppress)
384                 h_suppress_date = "true";
385         else
386                 h_suppress_date = "false";
387 }
388
389
390 void Preamble::registerAuthor(std::string const & name)
391 {
392         Author author(from_utf8(name), empty_docstring());
393         author.setUsed(true);
394         authors_.record(author);
395         h_tracking_changes = "true";
396         h_output_changes = "true";
397 }
398
399
400 Author const & Preamble::getAuthor(std::string const & name) const
401 {
402         Author author(from_utf8(name), empty_docstring());
403         for (AuthorList::Authors::const_iterator it = authors_.begin();
404              it != authors_.end(); ++it)
405                 if (*it == author)
406                         return *it;
407         static Author const dummy;
408         return dummy;
409 }
410
411
412 int Preamble::getSpecialTableColumnArguments(char c) const
413 {
414         map<char, int>::const_iterator it = special_columns_.find(c);
415         if (it == special_columns_.end())
416                 return -1;
417         return it->second;
418 }
419
420
421 void Preamble::add_package(string const & name, vector<string> & options)
422 {
423         // every package inherits the global options
424         if (used_packages.find(name) == used_packages.end())
425                 used_packages[name] = split_options(h_options);
426
427         // Insert options passed via PassOptionsToPackage
428         for (auto const & p : extra_package_options_) {
429                 if (p.first == name) {
430                         vector<string> eo = getVectorFromString(p.second);
431                         for (auto const & eoi : eo)
432                                 options.push_back(eoi);
433                 }
434
435         }
436         vector<string> & v = used_packages[name];
437         v.insert(v.end(), options.begin(), options.end());
438         if (name == "jurabib") {
439                 // Don't output the order argument (see the cite command
440                 // handling code in text.cpp).
441                 vector<string>::iterator end =
442                         remove(options.begin(), options.end(), "natbiborder");
443                 end = remove(options.begin(), end, "jurabiborder");
444                 options.erase(end, options.end());
445         }
446 }
447
448
449 namespace {
450
451 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
452 bool scale_as_percentage(string const & scale, string & percentage)
453 {
454         string::size_type pos = scale.find('=');
455         if (pos != string::npos) {
456                 string value = scale.substr(pos + 1);
457                 if (isStrDbl(value)) {
458                         percentage = convert<string>(
459                                 static_cast<int>(100 * convert<double>(value)));
460                         return true;
461                 }
462         }
463         return false;
464 }
465
466
467 string remove_braces(string const & value)
468 {
469         if (value.empty())
470                 return value;
471         if (value[0] == '{' && value[value.length()-1] == '}')
472                 return value.substr(1, value.length()-2);
473         return value;
474 }
475
476 } // anonymous namespace
477
478
479 Preamble::Preamble() : one_language(true), explicit_babel(false),
480         title_layout_found(false), index_number(0), h_font_cjk_set(false),
481         h_use_microtype("false")
482 {
483         //h_backgroundcolor;
484         //h_boxbgcolor;
485         h_biblio_style            = "plain";
486         h_bibtex_command          = "default";
487         h_cite_engine             = "basic";
488         h_cite_engine_type        = "default";
489         h_color                   = "#008000";
490         h_defskip                 = "medskip";
491         h_dynamic_quotes          = false;
492         //h_float_placement;
493         //h_fontcolor;
494         h_fontencoding            = "default";
495         h_font_roman[0]           = "default";
496         h_font_roman[1]           = "default";
497         h_font_sans[0]            = "default";
498         h_font_sans[1]            = "default";
499         h_font_typewriter[0]      = "default";
500         h_font_typewriter[1]      = "default";
501         h_font_math[0]            = "auto";
502         h_font_math[1]            = "auto";
503         h_font_default_family     = "default";
504         h_use_non_tex_fonts       = false;
505         h_font_sc                 = "false";
506         h_font_osf                = "false";
507         h_font_sf_scale[0]        = "100";
508         h_font_sf_scale[1]        = "100";
509         h_font_tt_scale[0]        = "100";
510         h_font_tt_scale[1]        = "100";
511         //h_font_cjk
512         h_is_mathindent           = "0";
513         h_math_numbering_side     = "default";
514         h_graphics                = "default";
515         h_default_output_format   = "default";
516         h_html_be_strict          = "false";
517         h_html_css_as_file        = "0";
518         h_html_math_output        = "0";
519         h_index[0]                = "Index";
520         h_index_command           = "default";
521         h_inputencoding           = "auto";
522         h_justification           = "true";
523         h_language                = "english";
524         h_language_package        = "none";
525         //h_listings_params;
526         h_maintain_unincluded_children = "false";
527         //h_margins;
528         //h_notefontcolor;
529         //h_options;
530         h_output_changes          = "false";
531         h_output_sync             = "0";
532         //h_output_sync_macro
533         h_papercolumns            = "1";
534         h_paperfontsize           = "default";
535         h_paperorientation        = "portrait";
536         h_paperpagestyle          = "default";
537         //h_papersides;
538         h_papersize               = "default";
539         h_paragraph_indentation   = "default";
540         h_paragraph_separation    = "indent";
541         //h_pdf_title;
542         //h_pdf_author;
543         //h_pdf_subject;
544         //h_pdf_keywords;
545         h_pdf_bookmarks           = "0";
546         h_pdf_bookmarksnumbered   = "0";
547         h_pdf_bookmarksopen       = "0";
548         h_pdf_bookmarksopenlevel  = "1";
549         h_pdf_breaklinks          = "0";
550         h_pdf_pdfborder           = "0";
551         h_pdf_colorlinks          = "0";
552         h_pdf_backref             = "section";
553         h_pdf_pdfusetitle         = "0";
554         //h_pdf_pagemode;
555         //h_pdf_quoted_options;
556         h_quotes_style         = "english";
557         h_secnumdepth             = "3";
558         h_shortcut[0]             = "idx";
559         h_spacing                 = "single";
560         h_save_transient_properties = "true";
561         h_suppress_date           = "false";
562         h_textclass               = "article";
563         h_tocdepth                = "3";
564         h_tracking_changes        = "false";
565         h_use_bibtopic            = "false";
566         h_use_dash_ligatures      = "true";
567         h_use_indices             = "false";
568         h_use_geometry            = "false";
569         h_use_default_options     = "false";
570         h_use_hyperref            = "false";
571         h_use_microtype           = "false";
572         h_use_refstyle            = false;
573         h_use_minted              = false;
574         h_use_packages["amsmath"]    = "1";
575         h_use_packages["amssymb"]    = "0";
576         h_use_packages["cancel"]     = "0";
577         h_use_packages["esint"]      = "1";
578         h_use_packages["mhchem"]     = "0";
579         h_use_packages["mathdots"]   = "0";
580         h_use_packages["mathtools"]  = "0";
581         h_use_packages["stackrel"]   = "0";
582         h_use_packages["stmaryrd"]   = "0";
583         h_use_packages["undertilde"] = "0";
584 }
585
586
587 void Preamble::handle_hyperref(vector<string> & options)
588 {
589         // FIXME swallow inputencoding changes that might surround the
590         //       hyperref setup if it was written by LyX
591         h_use_hyperref = "true";
592         // swallow "unicode=true", since LyX does always write that
593         vector<string>::iterator it =
594                 find(options.begin(), options.end(), "unicode=true");
595         if (it != options.end())
596                 options.erase(it);
597         it = find(options.begin(), options.end(), "pdfusetitle");
598         if (it != options.end()) {
599                 h_pdf_pdfusetitle = "1";
600                 options.erase(it);
601         }
602         string bookmarks = process_keyval_opt(options, "bookmarks");
603         if (bookmarks == "true")
604                 h_pdf_bookmarks = "1";
605         else if (bookmarks == "false")
606                 h_pdf_bookmarks = "0";
607         if (h_pdf_bookmarks == "1") {
608                 string bookmarksnumbered =
609                         process_keyval_opt(options, "bookmarksnumbered");
610                 if (bookmarksnumbered == "true")
611                         h_pdf_bookmarksnumbered = "1";
612                 else if (bookmarksnumbered == "false")
613                         h_pdf_bookmarksnumbered = "0";
614                 string bookmarksopen =
615                         process_keyval_opt(options, "bookmarksopen");
616                 if (bookmarksopen == "true")
617                         h_pdf_bookmarksopen = "1";
618                 else if (bookmarksopen == "false")
619                         h_pdf_bookmarksopen = "0";
620                 if (h_pdf_bookmarksopen == "1") {
621                         string bookmarksopenlevel =
622                                 process_keyval_opt(options, "bookmarksopenlevel");
623                         if (!bookmarksopenlevel.empty())
624                                 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
625                 }
626         }
627         string breaklinks = process_keyval_opt(options, "breaklinks");
628         if (breaklinks == "true")
629                 h_pdf_breaklinks = "1";
630         else if (breaklinks == "false")
631                 h_pdf_breaklinks = "0";
632         string pdfborder = process_keyval_opt(options, "pdfborder");
633         if (pdfborder == "{0 0 0}")
634                 h_pdf_pdfborder = "1";
635         else if (pdfborder == "{0 0 1}")
636                 h_pdf_pdfborder = "0";
637         string backref = process_keyval_opt(options, "backref");
638         if (!backref.empty())
639                 h_pdf_backref = backref;
640         string colorlinks = process_keyval_opt(options, "colorlinks");
641         if (colorlinks == "true")
642                 h_pdf_colorlinks = "1";
643         else if (colorlinks == "false")
644                 h_pdf_colorlinks = "0";
645         string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
646         if (!pdfpagemode.empty())
647                 h_pdf_pagemode = pdfpagemode;
648         string pdftitle = process_keyval_opt(options, "pdftitle");
649         if (!pdftitle.empty()) {
650                 h_pdf_title = remove_braces(pdftitle);
651         }
652         string pdfauthor = process_keyval_opt(options, "pdfauthor");
653         if (!pdfauthor.empty()) {
654                 h_pdf_author = remove_braces(pdfauthor);
655         }
656         string pdfsubject = process_keyval_opt(options, "pdfsubject");
657         if (!pdfsubject.empty())
658                 h_pdf_subject = remove_braces(pdfsubject);
659         string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
660         if (!pdfkeywords.empty())
661                 h_pdf_keywords = remove_braces(pdfkeywords);
662         if (!options.empty()) {
663                 if (!h_pdf_quoted_options.empty())
664                         h_pdf_quoted_options += ',';
665                 h_pdf_quoted_options += join(options, ",");
666                 options.clear();
667         }
668 }
669
670
671 void Preamble::handle_geometry(vector<string> & options)
672 {
673         h_use_geometry = "true";
674         vector<string>::iterator it;
675         // paper orientation
676         if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
677                 h_paperorientation = "landscape";
678                 options.erase(it);
679         }
680         // paper size
681         // keyval version: "paper=letter"
682         string paper = process_keyval_opt(options, "paper");
683         if (!paper.empty())
684                 h_papersize = paper + "paper";
685         // alternative version: "letterpaper"
686         handle_opt(options, known_paper_sizes, h_papersize);
687         delete_opt(options, known_paper_sizes);
688         // page margins
689         char const * const * margin = known_paper_margins;
690         for (; *margin; ++margin) {
691                 string value = process_keyval_opt(options, *margin);
692                 if (!value.empty()) {
693                         int k = margin - known_paper_margins;
694                         string name = known_coded_paper_margins[k];
695                         h_margins += '\\' + name + ' ' + value + '\n';
696                 }
697         }
698 }
699
700
701 void Preamble::handle_package(Parser &p, string const & name,
702                               string const & opts, bool in_lyx_preamble,
703                               bool detectEncoding)
704 {
705         vector<string> options = split_options(opts);
706         add_package(name, options);
707
708         if (is_known(name, known_xetex_packages)) {
709                 xetex = true;
710                 h_use_non_tex_fonts = true;
711                 registerAutomaticallyLoadedPackage("fontspec");
712                 if (h_inputencoding == "auto")
713                         p.setEncoding("UTF-8");
714         }
715
716         // roman fonts
717         if (is_known(name, known_roman_fonts))
718                 h_font_roman[0] = name;
719
720         if (name == "fourier") {
721                 h_font_roman[0] = "utopia";
722                 // when font uses real small capitals
723                 if (opts == "expert")
724                         h_font_sc = "true";
725         }
726
727         if (name == "garamondx") {
728                 h_font_roman[0] = "garamondx";
729                 if (opts == "osfI")
730                         h_font_osf = "true";
731         }
732
733         if (name == "libertine") {
734                 h_font_roman[0] = "libertine";
735                 // this automatically invokes biolinum
736                 h_font_sans[0] = "biolinum";
737                 // as well as libertineMono
738                 h_font_typewriter[0] = "libertine-mono";
739                 if (opts == "osf")
740                         h_font_osf = "true";
741                 else if (opts == "lining")
742                         h_font_osf = "false";
743         }
744
745         if (name == "libertineRoman" || name == "libertine-type1") {
746                 h_font_roman[0] = "libertine";
747                 // NOTE: contrary to libertine.sty, libertineRoman
748                 // and libertine-type1 do not automatically invoke
749                 // biolinum and libertineMono
750                 if (opts == "lining")
751                         h_font_osf = "false";
752                 else if (opts == "osf")
753                         h_font_osf = "true";
754         }
755
756         if (name == "MinionPro") {
757                 h_font_roman[0] = "minionpro";
758                 if (opts.find("lf") != string::npos)
759                         h_font_osf = "false";
760                 else
761                         h_font_osf = "true";
762                 if (opts.find("onlytext") != string::npos)
763                         h_font_math[0] = "default";
764                 else
765                         h_font_math[0] = "auto";
766         }
767
768         if (name == "mathdesign") {
769                 if (opts.find("charter") != string::npos)
770                         h_font_roman[0] = "md-charter";
771                 if (opts.find("garamond") != string::npos)
772                         h_font_roman[0] = "md-garamond";
773                 if (opts.find("utopia") != string::npos)
774                         h_font_roman[0] = "md-utopia";
775                 if (opts.find("expert") != string::npos) {
776                         h_font_sc = "true";
777                         h_font_osf = "true";
778                 }
779         }
780
781         else if (name == "mathpazo")
782                 h_font_roman[0] = "palatino";
783
784         else if (name == "mathptmx")
785                 h_font_roman[0] = "times";
786
787         if (name == "crimson")
788                 h_font_roman[0] = "cochineal";
789
790         if (name == "cochineal") {
791                 h_font_roman[0] = "cochineal";
792                 // cochineal can have several options, e.g. [proportional,osf]
793                 string::size_type pos = opts.find("osf");
794                 if (pos != string::npos)
795                         h_font_osf = "true";
796         }
797
798         if (name == "noto") {
799                 // noto can have several options
800                 if (opts.empty())
801                         h_font_roman[0] = "NotoSerif-TLF";
802                 string::size_type pos = opts.find("rm");
803                 if (pos != string::npos)
804                         h_font_roman[0] = "NotoSerif-TLF";
805                 pos = opts.find("sf");
806                 if (pos != string::npos)
807                         h_font_sans[0] = "NotoSans-TLF";
808                 pos = opts.find("nott");
809                 if (pos != string::npos) {
810                         h_font_roman[0] = "NotoSerif-TLF";
811                         h_font_sans[0] = "NotoSans-TLF";
812                 }
813                 // noto as typewriter is handled in handling of \ttdefault
814                 // special cases are handled in handling of \rmdefault and \sfdefault
815         }
816
817         if (name == "paratype") {
818                 // in this case all fonts are ParaType
819                 h_font_roman[0] = "PTSerif-TLF";
820                 h_font_sans[0] = "default";
821                 h_font_typewriter[0] = "default";
822         }
823
824         if (name == "PTSerif")
825                 h_font_roman[0] = "PTSerif-TLF";
826
827         // sansserif fonts
828         if (is_known(name, known_sans_fonts)) {
829                 h_font_sans[0] = name;
830                 if (options.size() >= 1) {
831                         if (scale_as_percentage(opts, h_font_sf_scale[0]))
832                                 options.clear();
833                 }
834         }
835
836         if (name == "biolinum" || name == "biolinum-type1") {
837                 h_font_sans[0] = "biolinum";
838                 // biolinum can have several options, e.g. [osf,scaled=0.97]
839                 string::size_type pos = opts.find("osf");
840                 if (pos != string::npos)
841                         h_font_osf = "true";
842         }
843
844         if (name == "PTSans") {
845                 h_font_sans[0] = "PTSans-TLF";
846                 if (options.size() >= 1) {
847                         if (scale_as_percentage(opts, h_font_sf_scale[0]))
848                                 options.clear();
849                 }
850         }
851
852         // typewriter fonts
853         if (is_known(name, known_typewriter_fonts)) {
854                 // fourier can be set as roman font _only_
855                 // fourier as typewriter is handled in handling of \ttdefault
856                 if (name != "fourier") {
857                         h_font_typewriter[0] = name;
858                         if (options.size() >= 1) {
859                                 if (scale_as_percentage(opts, h_font_tt_scale[0]))
860                                         options.clear();
861                         }
862                 }
863         }
864
865         if (name == "libertineMono" || name == "libertineMono-type1")
866                 h_font_typewriter[0] = "libertine-mono";
867
868         if (name == "PTMono") {
869                 h_font_typewriter[0] = "PTMono-TLF";
870                 if (options.size() >= 1) {
871                         if (scale_as_percentage(opts, h_font_tt_scale[0]))
872                                 options.clear();
873                 }
874         }
875
876         // font uses old-style figure
877         if (name == "eco")
878                 h_font_osf = "true";
879
880         // math fonts
881         if (is_known(name, known_math_fonts))
882                 h_font_math[0] = name;
883
884         if (name == "newtxmath") {
885                 if (opts.empty())
886                         h_font_math[0] = "newtxmath";
887                 else if (opts == "garamondx")
888                         h_font_math[0] = "garamondx-ntxm";
889                 else if (opts == "libertine")
890                         h_font_math[0] = "libertine-ntxm";
891                 else if (opts == "minion")
892                         h_font_math[0] = "minion-ntxm";
893                 else if (opts == "cochineal")
894                         h_font_math[0] = "cochineal-ntxm";
895         }
896
897         if (name == "iwona")
898                 if (opts == "math")
899                         h_font_math[0] = "iwona-math";
900
901         if (name == "kurier")
902                 if (opts == "math")
903                         h_font_math[0] = "kurier-math";
904
905         // after the detection and handling of special cases, we can remove the
906         // fonts, otherwise they would appear in the preamble, see bug #7856
907         if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
908                 ||      is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
909                 ;
910         //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
911         else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
912                  name == "esint" || name == "mhchem" || name == "mathdots" ||
913                  name == "mathtools" || name == "stackrel" ||
914                  name == "stmaryrd" || name == "undertilde")
915                 h_use_packages[name] = "2";
916
917         else if (name == "babel") {
918                 h_language_package = "default";
919                 // One might think we would have to do nothing if babel is loaded
920                 // without any options to prevent pollution of the preamble with this
921                 // babel call in every roundtrip.
922                 // But the user could have defined babel-specific things afterwards. So
923                 // we need to keep it in the preamble to prevent cases like bug #7861.
924                 if (!opts.empty()) {
925                         // check if more than one option was used - used later for inputenc
926                         if (options.begin() != options.end() - 1)
927                                 one_language = false;
928                         // babel takes the last language of the option of its \usepackage
929                         // call as document language. If there is no such language option, the
930                         // last language in the documentclass options is used.
931                         handle_opt(options, known_languages, h_language);
932                         // translate the babel name to a LyX name
933                         h_language = babel2lyx(h_language);
934                         if (h_language == "japanese") {
935                                 // For Japanese, the encoding isn't indicated in the source
936                                 // file, and there's really not much we can do. We could
937                                 // 1) offer a list of possible encodings to choose from, or
938                                 // 2) determine the encoding of the file by inspecting it.
939                                 // For the time being, we leave the encoding alone so that
940                                 // we don't get iconv errors when making a wrong guess, and
941                                 // we will output a note at the top of the document
942                                 // explaining what to do.
943                                 Encoding const * const enc = encodings.fromIconvName(
944                                         p.getEncoding(), Encoding::japanese, false);
945                                 if (enc)
946                                         h_inputencoding = enc->name();
947                                 is_nonCJKJapanese = true;
948                                 // in this case babel can be removed from the preamble
949                                 registerAutomaticallyLoadedPackage("babel");
950                         } else {
951                                 // If babel is called with options, LyX puts them by default into the
952                                 // document class options. This works for most languages, except
953                                 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
954                                 // perhaps in future others.
955                                 // Therefore keep the babel call as it is as the user might have
956                                 // reasons for it.
957                                 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
958                         }
959                         delete_opt(options, known_languages);
960                 } else {
961                         h_preamble << "\\usepackage{babel}\n";
962                         explicit_babel = true;
963                 }
964         }
965
966         else if (name == "polyglossia") {
967                 h_language_package = "default";
968                 h_default_output_format = "pdf4";
969                 h_use_non_tex_fonts = true;
970                 xetex = true;
971                 registerAutomaticallyLoadedPackage("xunicode");
972                 if (h_inputencoding == "auto")
973                         p.setEncoding("UTF-8");
974         }
975
976         else if (name == "CJK") {
977                 // set the encoding to "auto" because it might be set to "default" by the babel handling
978                 // and this would not be correct for CJK
979                 if (h_inputencoding == "default")
980                         h_inputencoding = "auto";
981                 registerAutomaticallyLoadedPackage("CJK");
982         }
983
984         else if (name == "CJKutf8") {
985                 h_inputencoding = "utf8-cjk";
986                 p.setEncoding("UTF-8");
987                 registerAutomaticallyLoadedPackage("CJKutf8");
988         }
989
990         else if (name == "fontenc") {
991                 h_fontencoding = getStringFromVector(options, ",");
992                 /* We could do the following for better round trip support,
993                  * but this makes the document less portable, so I skip it:
994                 if (h_fontencoding == lyxrc.fontenc)
995                         h_fontencoding = "global";
996                  */
997                 options.clear();
998         }
999
1000         else if (name == "inputenc" || name == "luainputenc") {
1001                 // h_inputencoding is only set when there is not more than one
1002                 // inputenc option because otherwise h_inputencoding must be
1003                 // set to "auto" (the default encoding of the document language)
1004                 // Therefore check that exactly one option is passed to inputenc.
1005                 // It is also only set when there is not more than one babel
1006                 // language option.
1007                 if (!options.empty()) {
1008                         string const encoding = options.back();
1009                         Encoding const * const enc = encodings.fromLaTeXName(
1010                                 encoding, Encoding::inputenc, true);
1011                         if (!enc) {
1012                                 if (!detectEncoding)
1013                                         cerr << "Unknown encoding " << encoding
1014                                              << ". Ignoring." << std::endl;
1015                         } else {
1016                                 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1017                                         h_inputencoding = enc->name();
1018                                 p.setEncoding(enc->iconvName());
1019                         }
1020                         options.clear();
1021                 }
1022         }
1023
1024         else if (name == "srcltx") {
1025                 h_output_sync = "1";
1026                 if (!opts.empty()) {
1027                         h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1028                         options.clear();
1029                 } else
1030                         h_output_sync_macro = "\\usepackage{srcltx}";
1031         }
1032
1033         else if (is_known(name, known_old_language_packages)) {
1034                 // known language packages from the times before babel
1035                 // if they are found and not also babel, they will be used as
1036                 // custom language package
1037                 h_language_package = "\\usepackage{" + name + "}";
1038         }
1039
1040         else if (name == "lyxskak") {
1041                 // ignore this and its options
1042                 const char * const o[] = {"ps", "mover", 0};
1043                 delete_opt(options, o);
1044         }
1045
1046         else if (is_known(name, known_lyx_packages) && options.empty()) {
1047                 if (name == "splitidx")
1048                         h_use_indices = "true";
1049                 else if (name == "minted")
1050                         h_use_minted = true;
1051                 else if (name == "refstyle")
1052                         h_use_refstyle = true;
1053                 else if (name == "prettyref")
1054                         h_use_refstyle = false;
1055                 if (!in_lyx_preamble) {
1056                         h_preamble << package_beg_sep << name
1057                                    << package_mid_sep << "\\usepackage{"
1058                                    << name << '}';
1059                         if (p.next_token().cat() == catNewline ||
1060                             (p.next_token().cat() == catSpace &&
1061                              p.next_next_token().cat() == catNewline))
1062                                 h_preamble << '\n';
1063                         h_preamble << package_end_sep;
1064                 }
1065         }
1066
1067         else if (name == "geometry")
1068                 handle_geometry(options);
1069
1070         else if (name == "subfig")
1071                 ; // ignore this FIXME: Use the package separator mechanism instead
1072
1073         else if (char const * const * where = is_known(name, known_languages))
1074                 h_language = known_coded_languages[where - known_languages];
1075
1076         else if (name == "natbib") {
1077                 h_biblio_style = "plainnat";
1078                 h_cite_engine = "natbib";
1079                 h_cite_engine_type = "authoryear";
1080                 vector<string>::iterator it =
1081                         find(options.begin(), options.end(), "authoryear");
1082                 if (it != options.end())
1083                         options.erase(it);
1084                 else {
1085                         it = find(options.begin(), options.end(), "numbers");
1086                         if (it != options.end()) {
1087                                 h_cite_engine_type = "numerical";
1088                                 options.erase(it);
1089                         }
1090                 }
1091                 if (!options.empty())
1092                         h_biblio_options = join(options, ",");
1093         }
1094
1095         else if (name == "biblatex") {
1096                 h_biblio_style = "plainnat";
1097                 h_cite_engine = "biblatex";
1098                 h_cite_engine_type = "authoryear";
1099                 string opt;
1100                 vector<string>::iterator it =
1101                         find(options.begin(), options.end(), "natbib");
1102                 if (it != options.end()) {
1103                         options.erase(it);
1104                         h_cite_engine = "biblatex-natbib";
1105                 } else {
1106                         opt = process_keyval_opt(options, "natbib");
1107                         if (opt == "true")
1108                                 h_cite_engine = "biblatex-natbib";
1109                 }
1110                 opt = process_keyval_opt(options, "style");
1111                 if (!opt.empty()) {
1112                         h_biblatex_citestyle = opt;
1113                         h_biblatex_bibstyle = opt;
1114                 } else {
1115                         opt = process_keyval_opt(options, "citestyle");
1116                         if (!opt.empty())
1117                                 h_biblatex_citestyle = opt;
1118                         opt = process_keyval_opt(options, "bibstyle");
1119                         if (!opt.empty())
1120                                 h_biblatex_bibstyle = opt;
1121                 }
1122                 opt = process_keyval_opt(options, "refsection");
1123                 if (!opt.empty()) {
1124                         if (opt == "none" || opt == "part"
1125                             || opt == "chapter" || opt == "section"
1126                             || opt == "subsection")
1127                                 h_multibib = opt;
1128                         else
1129                                 cerr << "Ignoring unkown refesection value '"
1130                                      << opt << "'.";
1131                 }
1132                 if (!options.empty()) {
1133                         h_biblio_options = join(options, ",");
1134                         options.clear();
1135                 }
1136         }
1137
1138         else if (name == "jurabib") {
1139                 h_biblio_style = "jurabib";
1140                 h_cite_engine = "jurabib";
1141                 h_cite_engine_type = "authoryear";
1142                 if (!options.empty())
1143                         h_biblio_options = join(options, ",");
1144         }
1145
1146         else if (name == "bibtopic")
1147                 h_use_bibtopic = "true";
1148
1149         else if (name == "chapterbib")
1150                 h_multibib = "child";
1151
1152         else if (name == "hyperref")
1153                 handle_hyperref(options);
1154
1155         else if (name == "algorithm2e") {
1156                 // Load "algorithm2e" module
1157                 addModule("algorithm2e");
1158                 // Add the package options to the global document options
1159                 if (!options.empty()) {
1160                         if (h_options.empty())
1161                                 h_options = join(options, ",");
1162                         else
1163                                 h_options += ',' + join(options, ",");
1164                 }
1165         }
1166         else if (name == "microtype") {
1167                 //we internally support only microtype without params
1168                 if (options.empty())
1169                         h_use_microtype = "true";
1170                 else
1171                         h_preamble << "\\usepackage[" << opts << "]{microtype}";
1172         }
1173
1174         else if (!in_lyx_preamble) {
1175                 if (options.empty())
1176                         h_preamble << "\\usepackage{" << name << '}';
1177                 else {
1178                         h_preamble << "\\usepackage[" << opts << "]{"
1179                                    << name << '}';
1180                         options.clear();
1181                 }
1182                 if (p.next_token().cat() == catNewline ||
1183                     (p.next_token().cat() == catSpace &&
1184                      p.next_next_token().cat() == catNewline))
1185                         h_preamble << '\n';
1186         }
1187
1188         // We need to do something with the options...
1189         if (!options.empty() && !detectEncoding)
1190                 cerr << "Ignoring options '" << join(options, ",")
1191                      << "' of package " << name << '.' << endl;
1192
1193         // remove the whitespace
1194         p.skip_spaces();
1195 }
1196
1197
1198 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1199 {
1200         while (p.good()) {
1201                 Token t = p.get_token();
1202                 if (t.cat() == catEscape &&
1203                     is_known(t.cs(), known_if_commands))
1204                         handle_if(p, in_lyx_preamble);
1205                 else {
1206                         if (!in_lyx_preamble)
1207                                 h_preamble << t.asInput();
1208                         if (t.cat() == catEscape && t.cs() == "fi")
1209                                 return;
1210                 }
1211         }
1212 }
1213
1214
1215 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1216 {
1217         // set the quote language
1218         // LyX only knows the following quotes languages:
1219         // english, swedish, german, polish, french and danish
1220         // (quotes for "japanese" and "chinese-traditional" are missing because
1221         //  they wouldn't be useful: https://www.lyx.org/trac/ticket/6383)
1222         // conversion list taken from
1223         // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1224         // (quotes for kazakh and interlingua are unknown)
1225         // danish
1226         if (is_known(h_language, known_danish_quotes_languages))
1227                 h_quotes_style = "danish";
1228         // french
1229         else if (is_known(h_language, known_french_quotes_languages))
1230                 h_quotes_style = "french";
1231         // german
1232         else if (is_known(h_language, known_german_quotes_languages))
1233                 h_quotes_style = "german";
1234         // polish
1235         else if (is_known(h_language, known_polish_quotes_languages))
1236                 h_quotes_style = "polish";
1237         // swedish
1238         else if (is_known(h_language, known_swedish_quotes_languages))
1239                 h_quotes_style = "swedish";
1240         //english
1241         else if (is_known(h_language, known_english_quotes_languages))
1242                 h_quotes_style = "english";
1243
1244         if (contains(h_float_placement, "H"))
1245                 registerAutomaticallyLoadedPackage("float");
1246         if (h_spacing != "single" && h_spacing != "default")
1247                 registerAutomaticallyLoadedPackage("setspace");
1248         if (h_use_packages["amsmath"] == "2") {
1249                 // amsbsy and amstext are already provided by amsmath
1250                 registerAutomaticallyLoadedPackage("amsbsy");
1251                 registerAutomaticallyLoadedPackage("amstext");
1252         }
1253
1254         // output the LyX file settings
1255         // Important: Keep the version formatting in sync with LyX and
1256         //            lyx2lyx (bug 7951)
1257         string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1258         os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1259            << lyx_version_minor << '\n'
1260            << "\\lyxformat " << LYX_FORMAT << '\n'
1261            << "\\begin_document\n"
1262            << "\\begin_header\n"
1263            << "\\save_transient_properties " << h_save_transient_properties << "\n"
1264            << "\\origin " << origin << "\n"
1265            << "\\textclass " << h_textclass << "\n";
1266         string const raw = subdoc ? empty_string() : h_preamble.str();
1267         if (!raw.empty()) {
1268                 os << "\\begin_preamble\n";
1269                 for (string::size_type i = 0; i < raw.size(); ++i) {
1270                         if (raw[i] == package_beg_sep) {
1271                                 // Here follows some package loading code that
1272                                 // must be skipped if the package is loaded
1273                                 // automatically.
1274                                 string::size_type j = raw.find(package_mid_sep, i);
1275                                 if (j == string::npos)
1276                                         return false;
1277                                 string::size_type k = raw.find(package_end_sep, j);
1278                                 if (k == string::npos)
1279                                         return false;
1280                                 string const package = raw.substr(i + 1, j - i - 1);
1281                                 string const replacement = raw.substr(j + 1, k - j - 1);
1282                                 if (auto_packages.find(package) == auto_packages.end())
1283                                         os << replacement;
1284                                 i = k;
1285                         } else
1286                                 os.put(raw[i]);
1287                 }
1288                 os << "\n\\end_preamble\n";
1289         }
1290         if (!h_options.empty())
1291                 os << "\\options " << h_options << "\n";
1292         os << "\\use_default_options " << h_use_default_options << "\n";
1293         if (!used_modules.empty()) {
1294                 os << "\\begin_modules\n";
1295                 vector<string>::const_iterator const end = used_modules.end();
1296                 vector<string>::const_iterator it = used_modules.begin();
1297                 for (; it != end; ++it)
1298                         os << *it << '\n';
1299                 os << "\\end_modules\n";
1300         }
1301         if (!h_includeonlys.empty()) {
1302                 os << "\\begin_includeonly\n";
1303                 for (auto const & iofile : h_includeonlys)
1304                         os << iofile << '\n';
1305                 os << "\\end_includeonly\n";
1306         }
1307         os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1308            << "\\language " << h_language << "\n"
1309            << "\\language_package " << h_language_package << "\n"
1310            << "\\inputencoding " << h_inputencoding << "\n"
1311            << "\\fontencoding " << h_fontencoding << "\n"
1312            << "\\font_roman \"" << h_font_roman[0]
1313            << "\" \"" << h_font_roman[1] << "\"\n"
1314            << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1315            << "\\font_typewriter \"" << h_font_typewriter[0]
1316            << "\" \"" << h_font_typewriter[1] << "\"\n"
1317            << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1318            << "\\font_default_family " << h_font_default_family << "\n"
1319            << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1320            << "\\font_sc " << h_font_sc << "\n"
1321            << "\\font_osf " << h_font_osf << "\n"
1322            << "\\font_sf_scale " << h_font_sf_scale[0]
1323            << ' ' << h_font_sf_scale[1] << '\n'
1324            << "\\font_tt_scale " << h_font_tt_scale[0]
1325            << ' ' << h_font_tt_scale[1] << '\n';
1326         if (!h_font_cjk.empty())
1327                 os << "\\font_cjk " << h_font_cjk << '\n';
1328         os << "\\use_microtype " << h_use_microtype << '\n'
1329            << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1330            << "\\graphics " << h_graphics << '\n'
1331            << "\\default_output_format " << h_default_output_format << "\n"
1332            << "\\output_sync " << h_output_sync << "\n";
1333         if (h_output_sync == "1")
1334                 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1335         os << "\\bibtex_command " << h_bibtex_command << "\n"
1336            << "\\index_command " << h_index_command << "\n";
1337         if (!h_float_placement.empty())
1338                 os << "\\float_placement " << h_float_placement << "\n";
1339         os << "\\paperfontsize " << h_paperfontsize << "\n"
1340            << "\\spacing " << h_spacing << "\n"
1341            << "\\use_hyperref " << h_use_hyperref << '\n';
1342         if (h_use_hyperref == "true") {
1343                 if (!h_pdf_title.empty())
1344                         os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1345                 if (!h_pdf_author.empty())
1346                         os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1347                 if (!h_pdf_subject.empty())
1348                         os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1349                 if (!h_pdf_keywords.empty())
1350                         os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1351                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1352                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1353                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1354                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1355                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1356                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1357                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1358                       "\\pdf_backref " << h_pdf_backref << "\n"
1359                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1360                 if (!h_pdf_pagemode.empty())
1361                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1362                 if (!h_pdf_quoted_options.empty())
1363                         os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1364         }
1365         os << "\\papersize " << h_papersize << "\n"
1366            << "\\use_geometry " << h_use_geometry << '\n';
1367         for (map<string, string>::const_iterator it = h_use_packages.begin();
1368              it != h_use_packages.end(); ++it)
1369                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1370         os << "\\cite_engine " << h_cite_engine << '\n'
1371            << "\\cite_engine_type " << h_cite_engine_type << '\n'
1372            << "\\biblio_style " << h_biblio_style << "\n"
1373            << "\\use_bibtopic " << h_use_bibtopic << "\n";
1374         if (!h_biblio_options.empty())
1375                 os << "\\biblio_options " << h_biblio_options << "\n";
1376         if (!h_biblatex_bibstyle.empty())
1377                 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1378         if (!h_biblatex_citestyle.empty())
1379                 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1380         if (!h_multibib.empty())
1381                 os << "\\multibib " << h_multibib << "\n";
1382         os << "\\use_indices " << h_use_indices << "\n"
1383            << "\\paperorientation " << h_paperorientation << '\n'
1384            << "\\suppress_date " << h_suppress_date << '\n'
1385            << "\\justification " << h_justification << '\n'
1386            << "\\use_refstyle " << h_use_refstyle << '\n'
1387            << "\\use_minted " << h_use_minted << '\n';
1388         if (!h_fontcolor.empty())
1389                 os << "\\fontcolor " << h_fontcolor << '\n';
1390         if (!h_notefontcolor.empty())
1391                 os << "\\notefontcolor " << h_notefontcolor << '\n';
1392         if (!h_backgroundcolor.empty())
1393                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1394         if (!h_boxbgcolor.empty())
1395                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1396         if (index_number != 0)
1397                 for (int i = 0; i < index_number; i++) {
1398                         os << "\\index " << h_index[i] << '\n'
1399                            << "\\shortcut " << h_shortcut[i] << '\n'
1400                            << "\\color " << h_color << '\n'
1401                            << "\\end_index\n";
1402                 }
1403         else {
1404                 os << "\\index " << h_index[0] << '\n'
1405                    << "\\shortcut " << h_shortcut[0] << '\n'
1406                    << "\\color " << h_color << '\n'
1407                    << "\\end_index\n";
1408         }
1409         os << h_margins
1410            << "\\secnumdepth " << h_secnumdepth << "\n"
1411            << "\\tocdepth " << h_tocdepth << "\n"
1412            << "\\paragraph_separation " << h_paragraph_separation << "\n";
1413         if (h_paragraph_separation == "skip")
1414                 os << "\\defskip " << h_defskip << "\n";
1415         else
1416                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1417         os << "\\is_math_indent " << h_is_mathindent << "\n";
1418         if (!h_mathindentation.empty())
1419                 os << "\\math_indentation " << h_mathindentation << "\n";
1420         os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1421         os << "\\quotes_style " << h_quotes_style << "\n"
1422            << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1423            << "\\papercolumns " << h_papercolumns << "\n"
1424            << "\\papersides " << h_papersides << "\n"
1425            << "\\paperpagestyle " << h_paperpagestyle << "\n";
1426         if (!h_listings_params.empty())
1427                 os << "\\listings_params " << h_listings_params << "\n";
1428         os << "\\tracking_changes " << h_tracking_changes << "\n"
1429            << "\\output_changes " << h_output_changes << "\n"
1430            << "\\html_math_output " << h_html_math_output << "\n"
1431            << "\\html_css_as_file " << h_html_css_as_file << "\n"
1432            << "\\html_be_strict " << h_html_be_strict << "\n"
1433            << authors_
1434            << "\\end_header\n\n"
1435            << "\\begin_body\n";
1436         return true;
1437 }
1438
1439
1440 void Preamble::parse(Parser & p, string const & forceclass,
1441                      TeX2LyXDocClass & tc)
1442 {
1443         // initialize fixed types
1444         special_columns_['D'] = 3;
1445         parse(p, forceclass, false, tc);
1446 }
1447
1448
1449 void Preamble::parse(Parser & p, string const & forceclass,
1450                      bool detectEncoding, TeX2LyXDocClass & tc)
1451 {
1452         bool is_full_document = false;
1453         bool is_lyx_file = false;
1454         bool in_lyx_preamble = false;
1455
1456         // determine whether this is a full document or a fragment for inclusion
1457         while (p.good()) {
1458                 Token const & t = p.get_token();
1459
1460                 if (t.cat() == catEscape && t.cs() == "documentclass") {
1461                         is_full_document = true;
1462                         break;
1463                 }
1464         }
1465         p.reset();
1466
1467         if (detectEncoding && !is_full_document)
1468                 return;
1469
1470         while (is_full_document && p.good()) {
1471                 if (detectEncoding && h_inputencoding != "auto" &&
1472                     h_inputencoding != "default")
1473                         return;
1474
1475                 Token const & t = p.get_token();
1476
1477 #ifdef FILEDEBUG
1478                 if (!detectEncoding)
1479                         cerr << "t: " << t << '\n';
1480 #endif
1481
1482                 //
1483                 // cat codes
1484                 //
1485                 if (!in_lyx_preamble &&
1486                     (t.cat() == catLetter ||
1487                      t.cat() == catSuper ||
1488                      t.cat() == catSub ||
1489                      t.cat() == catOther ||
1490                      t.cat() == catMath ||
1491                      t.cat() == catActive ||
1492                      t.cat() == catBegin ||
1493                      t.cat() == catEnd ||
1494                      t.cat() == catAlign ||
1495                      t.cat() == catParameter)) {
1496                         h_preamble << t.cs();
1497                         continue;
1498                 }
1499
1500                 if (!in_lyx_preamble &&
1501                     (t.cat() == catSpace || t.cat() == catNewline)) {
1502                         h_preamble << t.asInput();
1503                         continue;
1504                 }
1505
1506                 if (t.cat() == catComment) {
1507                         static regex const islyxfile("%% LyX .* created this file");
1508                         static regex const usercommands("User specified LaTeX commands");
1509
1510                         string const comment = t.asInput();
1511
1512                         // magically switch encoding default if it looks like XeLaTeX
1513                         static string const magicXeLaTeX =
1514                                 "% This document must be compiled with XeLaTeX ";
1515                         if (comment.size() > magicXeLaTeX.size()
1516                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1517                                   && h_inputencoding == "auto") {
1518                                 if (!detectEncoding)
1519                                         cerr << "XeLaTeX comment found, switching to UTF8\n";
1520                                 h_inputencoding = "utf8";
1521                         }
1522                         smatch sub;
1523                         if (regex_search(comment, sub, islyxfile)) {
1524                                 is_lyx_file = true;
1525                                 in_lyx_preamble = true;
1526                         } else if (is_lyx_file
1527                                    && regex_search(comment, sub, usercommands))
1528                                 in_lyx_preamble = false;
1529                         else if (!in_lyx_preamble)
1530                                 h_preamble << t.asInput();
1531                         continue;
1532                 }
1533
1534                 if (t.cs() == "PassOptionsToPackage") {
1535                         string const poptions = p.getArg('{', '}');
1536                         string const package = p.verbatim_item();
1537                         extra_package_options_.insert(make_pair(package, poptions));
1538                         continue;
1539                 }
1540
1541                 if (t.cs() == "pagestyle") {
1542                         h_paperpagestyle = p.verbatim_item();
1543                         continue;
1544                 }
1545
1546                 if (t.cs() == "setdefaultlanguage") {
1547                         xetex = true;
1548                         // We don't yet care about non-language variant options
1549                         // because LyX doesn't support this yet, see bug #8214
1550                         if (p.hasOpt()) {
1551                                 string langopts = p.getOpt();
1552                                 // check if the option contains a variant, if yes, extract it
1553                                 string::size_type pos_var = langopts.find("variant");
1554                                 string::size_type i = langopts.find(',', pos_var);
1555                                 string::size_type k = langopts.find('=', pos_var);
1556                                 if (pos_var != string::npos){
1557                                         string variant;
1558                                         if (i == string::npos)
1559                                                 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1560                                         else
1561                                                 variant = langopts.substr(k + 1, i - k - 1);
1562                                         h_language = variant;
1563                                 }
1564                                 p.verbatim_item();
1565                         } else
1566                                 h_language = p.verbatim_item();
1567                         //finally translate the poyglossia name to a LyX name
1568                         h_language = polyglossia2lyx(h_language);
1569                         continue;
1570                 }
1571
1572                 if (t.cs() == "setotherlanguage") {
1573                         // We don't yet care about the option because LyX doesn't
1574                         // support this yet, see bug #8214
1575                         p.hasOpt() ? p.getOpt() : string();
1576                         p.verbatim_item();
1577                         continue;
1578                 }
1579
1580                 if (t.cs() == "setmainfont") {
1581                         // we don't care about the option
1582                         p.hasOpt() ? p.getOpt() : string();
1583                         h_font_roman[1] = p.getArg('{', '}');
1584                         continue;
1585                 }
1586
1587                 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1588                         // LyX currently only supports the scale option
1589                         string scale;
1590                         if (p.hasOpt()) {
1591                                 string fontopts = p.getArg('[', ']');
1592                                 // check if the option contains a scaling, if yes, extract it
1593                                 string::size_type pos = fontopts.find("Scale");
1594                                 if (pos != string::npos) {
1595                                         string::size_type i = fontopts.find(',', pos);
1596                                         if (i == string::npos)
1597                                                 scale_as_percentage(fontopts.substr(pos + 1), scale);
1598                                         else
1599                                                 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1600                                 }
1601                         }
1602                         if (t.cs() == "setsansfont") {
1603                                 if (!scale.empty())
1604                                         h_font_sf_scale[1] = scale;
1605                                 h_font_sans[1] = p.getArg('{', '}');
1606                         } else {
1607                                 if (!scale.empty())
1608                                         h_font_tt_scale[1] = scale;
1609                                 h_font_typewriter[1] = p.getArg('{', '}');
1610                         }
1611                         continue;
1612                 }
1613
1614                 else if (t.cs() == "date") {
1615                         string argument = p.getArg('{', '}');
1616                         if (argument.empty())
1617                                 h_suppress_date = "true";
1618                         else
1619                                 h_preamble << t.asInput() << '{' << argument << '}';
1620                 }
1621
1622                 if (t.cs() == "color") {
1623                         string const space =
1624                                 (p.hasOpt() ? p.getOpt() : string());
1625                         string argument = p.getArg('{', '}');
1626                         // check the case that a standard color is used
1627                         if (space.empty() && is_known(argument, known_basic_colors)) {
1628                                 h_fontcolor = rgbcolor2code(argument);
1629                                 registerAutomaticallyLoadedPackage("color");
1630                         } else if (space.empty() && argument == "document_fontcolor")
1631                                 registerAutomaticallyLoadedPackage("color");
1632                         // check the case that LyX's document_fontcolor is defined
1633                         // but not used for \color
1634                         else {
1635                                 h_preamble << t.asInput();
1636                                 if (!space.empty())
1637                                         h_preamble << space;
1638                                 h_preamble << '{' << argument << '}';
1639                                 // the color might already be set because \definecolor
1640                                 // is parsed before this
1641                                 h_fontcolor = "";
1642                         }
1643                         continue;
1644                 }
1645
1646                 if (t.cs() == "pagecolor") {
1647                         string argument = p.getArg('{', '}');
1648                         // check the case that a standard color is used
1649                         if (is_known(argument, known_basic_colors)) {
1650                                 h_backgroundcolor = rgbcolor2code(argument);
1651                         } else if (argument == "page_backgroundcolor")
1652                                 registerAutomaticallyLoadedPackage("color");
1653                         // check the case that LyX's page_backgroundcolor is defined
1654                         // but not used for \pagecolor
1655                         else {
1656                                 h_preamble << t.asInput() << '{' << argument << '}';
1657                                 // the color might already be set because \definecolor
1658                                 // is parsed before this
1659                                 h_backgroundcolor = "";
1660                         }
1661                         continue;
1662                 }
1663
1664                 if (t.cs() == "makeatletter") {
1665                         // LyX takes care of this
1666                         p.setCatcode('@', catLetter);
1667                         continue;
1668                 }
1669
1670                 if (t.cs() == "makeatother") {
1671                         // LyX takes care of this
1672                         p.setCatcode('@', catOther);
1673                         continue;
1674                 }
1675
1676                 if (t.cs() == "makeindex") {
1677                         // LyX will re-add this if a print index command is found
1678                         p.skip_spaces();
1679                         continue;
1680                 }
1681
1682                 if (t.cs() == "newindex") {
1683                         string const indexname = p.getArg('[', ']');
1684                         string const shortcut = p.verbatim_item();
1685                         if (!indexname.empty())
1686                                 h_index[index_number] = indexname;
1687                         else
1688                                 h_index[index_number] = shortcut;
1689                         h_shortcut[index_number] = shortcut;
1690                         index_number += 1;
1691                         p.skip_spaces();
1692                         continue;
1693                 }
1694
1695                 if (t.cs() == "addbibresource") {
1696                         biblatex_bibliographies.push_back(removeExtension(p.getArg('{', '}')));
1697                         continue;
1698                 }
1699
1700                 if (t.cs() == "bibliography") {
1701                         vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1702                         biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1703                         continue;
1704                 }
1705
1706                 if (t.cs() == "RS@ifundefined") {
1707                         string const name = p.verbatim_item();
1708                         string const body1 = p.verbatim_item();
1709                         string const body2 = p.verbatim_item();
1710                         // only non-lyxspecific stuff
1711                         if (in_lyx_preamble &&
1712                             (name == "subsecref" || name == "thmref" || name == "lemref"))
1713                                 p.skip_spaces();
1714                         else {
1715                                 ostringstream ss;
1716                                 ss << '\\' << t.cs();
1717                                 ss << '{' << name << '}'
1718                                    << '{' << body1 << '}'
1719                                    << '{' << body2 << '}';
1720                                 h_preamble << ss.str();
1721                         }
1722                         continue;
1723                 }
1724
1725                 if (t.cs() == "AtBeginDocument") {
1726                         string const name = p.verbatim_item();
1727                         // only non-lyxspecific stuff
1728                         if (in_lyx_preamble &&
1729                             (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1730                                 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1731                                 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1732                                 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1733                                 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1734                                 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1735                                 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1736                                 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1737                                 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1738                                 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1739                                 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1740                                 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1741                                 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1742                                 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1743                                 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1744                                 p.skip_spaces();
1745                         else {
1746                                 ostringstream ss;
1747                                 ss << '\\' << t.cs();
1748                                 ss << '{' << name << '}';
1749                                 h_preamble << ss.str();
1750                         }
1751                         continue;
1752                 }
1753
1754                 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1755                     || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1756                     || t.cs() == "providecommand" || t.cs() == "providecommandx"
1757                     || t.cs() == "DeclareRobustCommand"
1758                     || t.cs() == "DeclareRobustCommandx"
1759                     || t.cs() == "ProvideTextCommandDefault"
1760                     || t.cs() == "DeclareMathAccent") {
1761                         bool star = false;
1762                         if (p.next_token().character() == '*') {
1763                                 p.get_token();
1764                                 star = true;
1765                         }
1766                         string const name = p.verbatim_item();
1767                         string const opt1 = p.getFullOpt();
1768                         string const opt2 = p.getFullOpt();
1769                         string const body = p.verbatim_item();
1770                         // store the in_lyx_preamble setting
1771                         bool const was_in_lyx_preamble = in_lyx_preamble;
1772                         // font settings
1773                         if (name == "\\rmdefault")
1774                                 if (is_known(body, known_roman_fonts)) {
1775                                         h_font_roman[0] = body;
1776                                         p.skip_spaces();
1777                                         in_lyx_preamble = true;
1778                                 }
1779                         if (name == "\\sfdefault")
1780                                 if (is_known(body, known_sans_fonts)) {
1781                                         h_font_sans[0] = body;
1782                                         p.skip_spaces();
1783                                         in_lyx_preamble = true;
1784                                 }
1785                         if (name == "\\ttdefault")
1786                                 if (is_known(body, known_typewriter_fonts)) {
1787                                         h_font_typewriter[0] = body;
1788                                         p.skip_spaces();
1789                                         in_lyx_preamble = true;
1790                                 }
1791                         if (name == "\\familydefault") {
1792                                 string family = body;
1793                                 // remove leading "\"
1794                                 h_font_default_family = family.erase(0,1);
1795                                 p.skip_spaces();
1796                                 in_lyx_preamble = true;
1797                         }
1798
1799                         // remove LyX-specific definitions that are re-added by LyX
1800                         // if necessary
1801                         // \lyxline is an ancient command that is converted by tex2lyx into
1802                         // a \rule therefore remove its preamble code
1803                         if (name == "\\lyxdot" || name == "\\lyxarrow"
1804                             || name == "\\lyxline" || name == "\\LyX") {
1805                                 p.skip_spaces();
1806                                 in_lyx_preamble = true;
1807                         }
1808
1809                         // Add the command to the known commands
1810                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1811
1812                         // only non-lyxspecific stuff
1813                         if (!in_lyx_preamble) {
1814                                 ostringstream ss;
1815                                 ss << '\\' << t.cs();
1816                                 if (star)
1817                                         ss << '*';
1818                                 ss << '{' << name << '}' << opt1 << opt2
1819                                    << '{' << body << "}";
1820                                 h_preamble << ss.str();
1821 /*
1822                                 ostream & out = in_preamble ? h_preamble : os;
1823                                 out << "\\" << t.cs() << "{" << name << "}"
1824                                     << opts << "{" << body << "}";
1825 */
1826                         }
1827                         // restore the in_lyx_preamble setting
1828                         in_lyx_preamble = was_in_lyx_preamble;
1829                         continue;
1830                 }
1831
1832                 if (t.cs() == "documentclass") {
1833                         vector<string>::iterator it;
1834                         vector<string> opts = split_options(p.getArg('[', ']'));
1835                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1836                         delete_opt(opts, known_fontsizes);
1837                         // delete "pt" at the end
1838                         string::size_type i = h_paperfontsize.find("pt");
1839                         if (i != string::npos)
1840                                 h_paperfontsize.erase(i);
1841                         // The documentclass options are always parsed before the options
1842                         // of the babel call so that a language cannot overwrite the babel
1843                         // options.
1844                         handle_opt(opts, known_languages, h_language);
1845                         delete_opt(opts, known_languages);
1846
1847                         // math indentation
1848                         if ((it = find(opts.begin(), opts.end(), "fleqn"))
1849                                  != opts.end()) {
1850                                 h_is_mathindent = "1";
1851                                 opts.erase(it);
1852                         }
1853                         // formula numbering side
1854                         if ((it = find(opts.begin(), opts.end(), "leqno"))
1855                                  != opts.end()) {
1856                                 h_math_numbering_side = "left";
1857                                 opts.erase(it);
1858                         }
1859                         else if ((it = find(opts.begin(), opts.end(), "reqno"))
1860                                  != opts.end()) {
1861                                 h_math_numbering_side = "right";
1862                                 opts.erase(it);
1863                         }
1864
1865                         // paper orientation
1866                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1867                                 h_paperorientation = "landscape";
1868                                 opts.erase(it);
1869                         }
1870                         // paper sides
1871                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1872                                  != opts.end()) {
1873                                 h_papersides = "1";
1874                                 opts.erase(it);
1875                         }
1876                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1877                                  != opts.end()) {
1878                                 h_papersides = "2";
1879                                 opts.erase(it);
1880                         }
1881                         // paper columns
1882                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1883                                  != opts.end()) {
1884                                 h_papercolumns = "1";
1885                                 opts.erase(it);
1886                         }
1887                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1888                                  != opts.end()) {
1889                                 h_papercolumns = "2";
1890                                 opts.erase(it);
1891                         }
1892                         // paper sizes
1893                         // some size options are known to any document classes, other sizes
1894                         // are handled by the \geometry command of the geometry package
1895                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1896                         delete_opt(opts, known_class_paper_sizes);
1897                         // the remaining options
1898                         h_options = join(opts, ",");
1899                         // FIXME This does not work for classes that have a
1900                         //       different name in LyX than in LaTeX
1901                         h_textclass = p.getArg('{', '}');
1902                         p.skip_spaces();
1903                         continue;
1904                 }
1905
1906                 if (t.cs() == "usepackage") {
1907                         string const options = p.getArg('[', ']');
1908                         string const name = p.getArg('{', '}');
1909                         vector<string> vecnames;
1910                         split(name, vecnames, ',');
1911                         vector<string>::const_iterator it  = vecnames.begin();
1912                         vector<string>::const_iterator end = vecnames.end();
1913                         for (; it != end; ++it)
1914                                 handle_package(p, trimSpaceAndEol(*it), options,
1915                                                in_lyx_preamble, detectEncoding);
1916                         continue;
1917                 }
1918
1919                 if (t.cs() == "inputencoding") {
1920                         string const encoding = p.getArg('{','}');
1921                         Encoding const * const enc = encodings.fromLaTeXName(
1922                                 encoding, Encoding::inputenc, true);
1923                         if (!enc) {
1924                                 if (!detectEncoding)
1925                                         cerr << "Unknown encoding " << encoding
1926                                              << ". Ignoring." << std::endl;
1927                         } else {
1928                                 if (!enc->unsafe())
1929                                         h_inputencoding = enc->name();
1930                                 p.setEncoding(enc->iconvName());
1931                         }
1932                         continue;
1933                 }
1934
1935                 if (t.cs() == "newenvironment") {
1936                         string const name = p.getArg('{', '}');
1937                         string const opt1 = p.getFullOpt();
1938                         string const opt2 = p.getFullOpt();
1939                         string const beg = p.verbatim_item();
1940                         string const end = p.verbatim_item();
1941                         if (!in_lyx_preamble) {
1942                                 h_preamble << "\\newenvironment{" << name
1943                                            << '}' << opt1 << opt2 << '{'
1944                                            << beg << "}{" << end << '}';
1945                         }
1946                         add_known_environment(name, opt1, !opt2.empty(),
1947                                               from_utf8(beg), from_utf8(end));
1948                         continue;
1949                 }
1950
1951                 if (t.cs() == "newtheorem") {
1952                         bool star = false;
1953                         if (p.next_token().character() == '*') {
1954                                 p.get_token();
1955                                 star = true;
1956                         }
1957                         string const name = p.getArg('{', '}');
1958                         string const opt1 = p.getFullOpt();
1959                         string const opt2 = p.getFullOpt();
1960                         string const body = p.verbatim_item();
1961                         string const opt3 = p.getFullOpt();
1962                         string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
1963
1964                         string const complete = cmd + "{" + name + '}' +
1965                                           opt1 + opt2 + '{' + body + '}' + opt3;
1966
1967                         add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1968
1969                         if (!in_lyx_preamble)
1970                                 h_preamble << complete;
1971                         continue;
1972                 }
1973
1974                 if (t.cs() == "def") {
1975                         string name = p.get_token().cs();
1976                         // In fact, name may be more than the name:
1977                         // In the test case of bug 8116
1978                         // name == "csname SF@gobble@opt \endcsname".
1979                         // Therefore, we need to use asInput() instead of cs().
1980                         while (p.next_token().cat() != catBegin)
1981                                 name += p.get_token().asInput();
1982                         if (!in_lyx_preamble)
1983                                 h_preamble << "\\def\\" << name << '{'
1984                                            << p.verbatim_item() << "}";
1985                         continue;
1986                 }
1987
1988                 if (t.cs() == "newcolumntype") {
1989                         string const name = p.getArg('{', '}');
1990                         trimSpaceAndEol(name);
1991                         int nargs = 0;
1992                         string opts = p.getOpt();
1993                         if (!opts.empty()) {
1994                                 istringstream is(string(opts, 1));
1995                                 is >> nargs;
1996                         }
1997                         special_columns_[name[0]] = nargs;
1998                         h_preamble << "\\newcolumntype{" << name << "}";
1999                         if (nargs)
2000                                 h_preamble << "[" << nargs << "]";
2001                         h_preamble << "{" << p.verbatim_item() << "}";
2002                         continue;
2003                 }
2004
2005                 if (t.cs() == "setcounter") {
2006                         string const name = p.getArg('{', '}');
2007                         string const content = p.getArg('{', '}');
2008                         if (name == "secnumdepth")
2009                                 h_secnumdepth = content;
2010                         else if (name == "tocdepth")
2011                                 h_tocdepth = content;
2012                         else
2013                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2014                         continue;
2015                 }
2016
2017                 if (t.cs() == "setlength") {
2018                         string const name = p.verbatim_item();
2019                         string const content = p.verbatim_item();
2020                         // the paragraphs are only not indented when \parindent is set to zero
2021                         if (name == "\\parindent" && content != "") {
2022                                 if (content[0] == '0')
2023                                         h_paragraph_separation = "skip";
2024                                 else
2025                                         h_paragraph_indentation = translate_len(content);
2026                         } else if (name == "\\parskip") {
2027                                 if (content == "\\smallskipamount")
2028                                         h_defskip = "smallskip";
2029                                 else if (content == "\\medskipamount")
2030                                         h_defskip = "medskip";
2031                                 else if (content == "\\bigskipamount")
2032                                         h_defskip = "bigskip";
2033                                 else
2034                                         h_defskip = translate_len(content);
2035                         } else if (name == "\\mathindent") {
2036                                 h_mathindentation = translate_len(content);
2037                         } else
2038                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2039                         continue;
2040                 }
2041
2042                 if (t.cs() == "onehalfspacing") {
2043                         h_spacing = "onehalf";
2044                         continue;
2045                 }
2046
2047                 if (t.cs() == "doublespacing") {
2048                         h_spacing = "double";
2049                         continue;
2050                 }
2051
2052                 if (t.cs() == "setstretch") {
2053                         h_spacing = "other " + p.verbatim_item();
2054                         continue;
2055                 }
2056
2057                 if (t.cs() == "synctex") {
2058                         // the scheme is \synctex=value
2059                         // where value can only be "1" or "-1"
2060                         h_output_sync = "1";
2061                         // there can be any character behind the value (e.g. a linebreak or a '\'
2062                         // therefore we extract it char by char
2063                         p.get_token();
2064                         string value = p.get_token().asInput();
2065                         if (value == "-")
2066                                 value += p.get_token().asInput();
2067                         h_output_sync_macro = "\\synctex=" + value;
2068                         continue;
2069                 }
2070
2071                 if (t.cs() == "begin") {
2072                         string const name = p.getArg('{', '}');
2073                         if (name == "document")
2074                                 break;
2075                         h_preamble << "\\begin{" << name << "}";
2076                         continue;
2077                 }
2078
2079                 if (t.cs() == "geometry") {
2080                         vector<string> opts = split_options(p.getArg('{', '}'));
2081                         handle_geometry(opts);
2082                         continue;
2083                 }
2084
2085                 if (t.cs() == "definecolor") {
2086                         string const color = p.getArg('{', '}');
2087                         string const space = p.getArg('{', '}');
2088                         string const value = p.getArg('{', '}');
2089                         if (color == "document_fontcolor" && space == "rgb") {
2090                                 RGBColor c(RGBColorFromLaTeX(value));
2091                                 h_fontcolor = X11hexname(c);
2092                         } else if (color == "note_fontcolor" && space == "rgb") {
2093                                 RGBColor c(RGBColorFromLaTeX(value));
2094                                 h_notefontcolor = X11hexname(c);
2095                         } else if (color == "page_backgroundcolor" && space == "rgb") {
2096                                 RGBColor c(RGBColorFromLaTeX(value));
2097                                 h_backgroundcolor = X11hexname(c);
2098                         } else if (color == "shadecolor" && space == "rgb") {
2099                                 RGBColor c(RGBColorFromLaTeX(value));
2100                                 h_boxbgcolor = X11hexname(c);
2101                         } else {
2102                                 h_preamble << "\\definecolor{" << color
2103                                            << "}{" << space << "}{" << value
2104                                            << '}';
2105                         }
2106                         continue;
2107                 }
2108
2109                 if (t.cs() == "bibliographystyle") {
2110                         h_biblio_style = p.verbatim_item();
2111                         continue;
2112                 }
2113
2114                 if (t.cs() == "jurabibsetup") {
2115                         // FIXME p.getArg('{', '}') is most probably wrong (it
2116                         //       does not handle nested braces).
2117                         //       Use p.verbatim_item() instead.
2118                         vector<string> jurabibsetup =
2119                                 split_options(p.getArg('{', '}'));
2120                         // add jurabibsetup to the jurabib package options
2121                         add_package("jurabib", jurabibsetup);
2122                         if (!jurabibsetup.empty()) {
2123                                 h_preamble << "\\jurabibsetup{"
2124                                            << join(jurabibsetup, ",") << '}';
2125                         }
2126                         continue;
2127                 }
2128
2129                 if (t.cs() == "hypersetup") {
2130                         vector<string> hypersetup =
2131                                 split_options(p.verbatim_item());
2132                         // add hypersetup to the hyperref package options
2133                         handle_hyperref(hypersetup);
2134                         if (!hypersetup.empty()) {
2135                                 h_preamble << "\\hypersetup{"
2136                                            << join(hypersetup, ",") << '}';
2137                         }
2138                         continue;
2139                 }
2140
2141                 if (t.cs() == "includeonly") {
2142                         vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2143                         for (auto & iofile : includeonlys) {
2144                                 string filename(normalize_filename(iofile));
2145                                 string const path = getMasterFilePath(true);
2146                                 // We want to preserve relative/absolute filenames,
2147                                 // therefore path is only used for testing
2148                                 if (!makeAbsPath(filename, path).exists()) {
2149                                         // The file extension is probably missing.
2150                                         // Now try to find it out.
2151                                         string const tex_name =
2152                                                 find_file(filename, path,
2153                                                           known_tex_extensions);
2154                                         if (!tex_name.empty())
2155                                                 filename = tex_name;
2156                                 }
2157                                 string outname;
2158                                 if (makeAbsPath(filename, path).exists())
2159                                         fix_child_filename(filename);
2160                                 else
2161                                         cerr << "Warning: Could not find included file '"
2162                                              << filename << "'." << endl;
2163                                 outname = changeExtension(filename, "lyx");
2164                                 h_includeonlys.push_back(outname);
2165                         }
2166                         continue;
2167                 }
2168
2169                 if (is_known(t.cs(), known_if_3arg_commands)) {
2170                         // prevent misparsing of \usepackage if it is used
2171                         // as an argument (see e.g. our own output of
2172                         // \@ifundefined above)
2173                         string const arg1 = p.verbatim_item();
2174                         string const arg2 = p.verbatim_item();
2175                         string const arg3 = p.verbatim_item();
2176                         // test case \@ifundefined{date}{}{\date{}}
2177                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
2178                             arg2.empty() && arg3 == "\\date{}") {
2179                                 h_suppress_date = "true";
2180                         // older tex2lyx versions did output
2181                         // \@ifundefined{definecolor}{\usepackage{color}}{}
2182                         } else if (t.cs() == "@ifundefined" &&
2183                                    arg1 == "definecolor" &&
2184                                    arg2 == "\\usepackage{color}" &&
2185                                    arg3.empty()) {
2186                                 if (!in_lyx_preamble)
2187                                         h_preamble << package_beg_sep
2188                                                    << "color"
2189                                                    << package_mid_sep
2190                                                    << "\\@ifundefined{definecolor}{color}{}"
2191                                                    << package_end_sep;
2192                         // test for case
2193                         //\@ifundefined{showcaptionsetup}{}{%
2194                         // \PassOptionsToPackage{caption=false}{subfig}}
2195                         // that LyX uses for subfloats
2196                         } else if (t.cs() == "@ifundefined" &&
2197                                    arg1 == "showcaptionsetup" && arg2.empty()
2198                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2199                                 ; // do nothing
2200                         } else if (!in_lyx_preamble) {
2201                                 h_preamble << t.asInput()
2202                                            << '{' << arg1 << '}'
2203                                            << '{' << arg2 << '}'
2204                                            << '{' << arg3 << '}';
2205                         }
2206                         continue;
2207                 }
2208
2209                 if (is_known(t.cs(), known_if_commands)) {
2210                         // must not parse anything in conditional code, since
2211                         // LyX would output the parsed contents unconditionally
2212                         if (!in_lyx_preamble)
2213                                 h_preamble << t.asInput();
2214                         handle_if(p, in_lyx_preamble);
2215                         continue;
2216                 }
2217
2218                 if (!t.cs().empty() && !in_lyx_preamble) {
2219                         h_preamble << '\\' << t.cs();
2220                         continue;
2221                 }
2222         }
2223
2224         // remove the whitespace
2225         p.skip_spaces();
2226
2227         // Force textclass if the user wanted it
2228         if (!forceclass.empty())
2229                 h_textclass = forceclass;
2230         tc.setName(h_textclass);
2231         if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2232                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2233                 exit(EXIT_FAILURE);
2234         }
2235         if (h_papersides.empty()) {
2236                 ostringstream ss;
2237                 ss << tc.sides();
2238                 h_papersides = ss.str();
2239         }
2240
2241         // If the CJK package is used we cannot set the document language from
2242         // the babel options. Instead, we guess which language is used most
2243         // and set this one.
2244         default_language = h_language;
2245         if (is_full_document &&
2246             (auto_packages.find("CJK") != auto_packages.end() ||
2247              auto_packages.find("CJKutf8") != auto_packages.end())) {
2248                 p.pushPosition();
2249                 h_language = guessLanguage(p, default_language);
2250                 p.popPosition();
2251                 if (explicit_babel && h_language != default_language) {
2252                         // We set the document language to a CJK language,
2253                         // but babel is explicitly called in the user preamble
2254                         // without options. LyX will not add the default
2255                         // language to the document options if it is either
2256                         // english, or no text is set as default language.
2257                         // Therefore we need to add a language option explicitly.
2258                         // FIXME: It would be better to remove all babel calls
2259                         //        from the user preamble, but this is difficult
2260                         //        without re-introducing bug 7861.
2261                         if (h_options.empty())
2262                                 h_options = lyx2babel(default_language);
2263                         else
2264                                 h_options += ',' + lyx2babel(default_language);
2265                 }
2266         }
2267 }
2268
2269
2270 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2271 {
2272         TeX2LyXDocClass dummy;
2273         parse(p, forceclass, true, dummy);
2274         if (h_inputencoding != "auto" && h_inputencoding != "default")
2275                 return h_inputencoding;
2276         return "";
2277 }
2278
2279
2280 string babel2lyx(string const & language)
2281 {
2282         char const * const * where = is_known(language, known_languages);
2283         if (where)
2284                 return known_coded_languages[where - known_languages];
2285         return language;
2286 }
2287
2288
2289 string lyx2babel(string const & language)
2290 {
2291         char const * const * where = is_known(language, known_coded_languages);
2292         if (where)
2293                 return known_languages[where - known_coded_languages];
2294         return language;
2295 }
2296
2297
2298 string Preamble::polyglossia2lyx(string const & language)
2299 {
2300         char const * const * where = is_known(language, polyglossia_languages);
2301         if (where)
2302                 return coded_polyglossia_languages[where - polyglossia_languages];
2303         return language;
2304 }
2305
2306
2307 string rgbcolor2code(string const & name)
2308 {
2309         char const * const * where = is_known(name, known_basic_colors);
2310         if (where) {
2311                 // "red", "green" etc
2312                 return known_basic_color_codes[where - known_basic_colors];
2313         }
2314         // "255,0,0", "0,255,0" etc
2315         RGBColor c(RGBColorFromLaTeX(name));
2316         return X11hexname(c);
2317 }
2318
2319 // }])
2320
2321
2322 } // namespace lyx