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