]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
8565d59df3b7576e5e422550534c50ad8c478604
[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)
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
1457                 else if (!in_lyx_preamble &&
1458                          (t.cat() == catSpace || t.cat() == catNewline))
1459                         h_preamble << t.asInput();
1460
1461                 else if (t.cat() == catComment) {
1462                         static regex const islyxfile("%% LyX .* created this file");
1463                         static regex const usercommands("User specified LaTeX commands");
1464
1465                         string const comment = t.asInput();
1466
1467                         // magically switch encoding default if it looks like XeLaTeX
1468                         static string const magicXeLaTeX =
1469                                 "% This document must be compiled with XeLaTeX ";
1470                         if (comment.size() > magicXeLaTeX.size()
1471                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1472                                   && h_inputencoding == "auto") {
1473                                 if (!detectEncoding)
1474                                         cerr << "XeLaTeX comment found, switching to UTF8\n";
1475                                 h_inputencoding = "utf8";
1476                         }
1477                         smatch sub;
1478                         if (regex_search(comment, sub, islyxfile)) {
1479                                 is_lyx_file = true;
1480                                 in_lyx_preamble = true;
1481                         } else if (is_lyx_file
1482                                    && regex_search(comment, sub, usercommands))
1483                                 in_lyx_preamble = false;
1484                         else if (!in_lyx_preamble)
1485                                 h_preamble << t.asInput();
1486                 }
1487
1488                 else if (t.cs() == "PassOptionsToPackage") {
1489                         string const poptions = p.getArg('{', '}');
1490                         string const package = p.verbatim_item();
1491                         extra_package_options_.insert(make_pair(package, poptions));
1492                 }
1493
1494                 else if (t.cs() == "pagestyle")
1495                         h_paperpagestyle = p.verbatim_item();
1496
1497                 else if (t.cs() == "setdefaultlanguage") {
1498                         xetex = true;
1499                         // We don't yet care about non-language variant options
1500                         // because LyX doesn't support this yet, see bug #8214
1501                         if (p.hasOpt()) {
1502                                 string langopts = p.getOpt();
1503                                 // check if the option contains a variant, if yes, extract it
1504                                 string::size_type pos_var = langopts.find("variant");
1505                                 string::size_type i = langopts.find(',', pos_var);
1506                                 string::size_type k = langopts.find('=', pos_var);
1507                                 if (pos_var != string::npos){
1508                                         string variant;
1509                                         if (i == string::npos)
1510                                                 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1511                                         else
1512                                                 variant = langopts.substr(k + 1, i - k - 1);
1513                                         h_language = variant;
1514                                 }
1515                                 p.verbatim_item();
1516                         } else
1517                                 h_language = p.verbatim_item();
1518                         //finally translate the poyglossia name to a LyX name
1519                         h_language = polyglossia2lyx(h_language);
1520                 }
1521
1522                 else if (t.cs() == "setotherlanguage") {
1523                         // We don't yet care about the option because LyX doesn't
1524                         // support this yet, see bug #8214
1525                         p.hasOpt() ? p.getOpt() : string();
1526                         p.verbatim_item();
1527                 }
1528
1529                 else if (t.cs() == "setmainfont") {
1530                         // we don't care about the option
1531                         p.hasOpt() ? p.getOpt() : string();
1532                         h_font_roman[1] = p.getArg('{', '}');
1533                 }
1534
1535                 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1536                         // LyX currently only supports the scale option
1537                         string scale;
1538                         if (p.hasOpt()) {
1539                                 string fontopts = p.getArg('[', ']');
1540                                 // check if the option contains a scaling, if yes, extract it
1541                                 string::size_type pos = fontopts.find("Scale");
1542                                 if (pos != string::npos) {
1543                                         string::size_type i = fontopts.find(',', pos);
1544                                         if (i == string::npos)
1545                                                 scale_as_percentage(fontopts.substr(pos + 1), scale);
1546                                         else
1547                                                 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1548                                 }
1549                         }
1550                         if (t.cs() == "setsansfont") {
1551                                 if (!scale.empty())
1552                                         h_font_sf_scale[1] = scale;
1553                                 h_font_sans[1] = p.getArg('{', '}');
1554                         } else {
1555                                 if (!scale.empty())
1556                                         h_font_tt_scale[1] = scale;
1557                                 h_font_typewriter[1] = p.getArg('{', '}');
1558                         }
1559                 }
1560
1561                 else if (t.cs() == "date") {
1562                         string argument = p.getArg('{', '}');
1563                         if (argument.empty())
1564                                 h_suppress_date = "true";
1565                         else
1566                                 h_preamble << t.asInput() << '{' << argument << '}';
1567                 }
1568
1569                 else if (t.cs() == "color") {
1570                         string const space =
1571                                 (p.hasOpt() ? p.getOpt() : string());
1572                         string argument = p.getArg('{', '}');
1573                         // check the case that a standard color is used
1574                         if (space.empty() && is_known(argument, known_basic_colors)) {
1575                                 h_fontcolor = rgbcolor2code(argument);
1576                                 registerAutomaticallyLoadedPackage("color");
1577                         } else if (space.empty() && argument == "document_fontcolor")
1578                                 registerAutomaticallyLoadedPackage("color");
1579                         // check the case that LyX's document_fontcolor is defined
1580                         // but not used for \color
1581                         else {
1582                                 h_preamble << t.asInput();
1583                                 if (!space.empty())
1584                                         h_preamble << space;
1585                                 h_preamble << '{' << argument << '}';
1586                                 // the color might already be set because \definecolor
1587                                 // is parsed before this
1588                                 h_fontcolor = "";
1589                         }
1590                 }
1591
1592                 else if (t.cs() == "pagecolor") {
1593                         string argument = p.getArg('{', '}');
1594                         // check the case that a standard color is used
1595                         if (is_known(argument, known_basic_colors)) {
1596                                 h_backgroundcolor = rgbcolor2code(argument);
1597                         } else if (argument == "page_backgroundcolor")
1598                                 registerAutomaticallyLoadedPackage("color");
1599                         // check the case that LyX's page_backgroundcolor is defined
1600                         // but not used for \pagecolor
1601                         else {
1602                                 h_preamble << t.asInput() << '{' << argument << '}';
1603                                 // the color might already be set because \definecolor
1604                                 // is parsed before this
1605                                 h_backgroundcolor = "";
1606                         }
1607                 }
1608
1609                 else if (t.cs() == "makeatletter") {
1610                         // LyX takes care of this
1611                         p.setCatcode('@', catLetter);
1612                 }
1613
1614                 else if (t.cs() == "makeatother") {
1615                         // LyX takes care of this
1616                         p.setCatcode('@', catOther);
1617                 }
1618
1619                 else if (t.cs() == "makeindex") {
1620                         // LyX will re-add this if a print index command is found
1621                         p.skip_spaces();
1622                 }
1623
1624                 else if (t.cs() == "newindex") {
1625                         string const indexname = p.getArg('[', ']');
1626                         string const shortcut = p.verbatim_item();
1627                         if (!indexname.empty())
1628                                 h_index[index_number] = indexname;
1629                         else
1630                                 h_index[index_number] = shortcut;
1631                         h_shortcut[index_number] = shortcut;
1632                         index_number += 1;
1633                         p.skip_spaces();
1634                 }
1635
1636                 else if (t.cs() == "addbibresource")
1637                         biblatex_bibliographies.push_back(removeExtension(p.getArg('{', '}')));
1638
1639                 else if (t.cs() == "bibliography") {
1640                         vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1641                         biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1642                 }
1643
1644                 else if (t.cs() == "RS@ifundefined") {
1645                         string const name = p.verbatim_item();
1646                         string const body1 = p.verbatim_item();
1647                         string const body2 = p.verbatim_item();
1648                         // only non-lyxspecific stuff
1649                         if (in_lyx_preamble &&
1650                             (name == "subsecref" || name == "thmref" || name == "lemref"))
1651                                 p.skip_spaces();
1652                         else {
1653                                 ostringstream ss;
1654                                 ss << '\\' << t.cs();
1655                                 ss << '{' << name << '}'
1656                                    << '{' << body1 << '}'
1657                                    << '{' << body2 << '}';
1658                                 h_preamble << ss.str();
1659                         }
1660                 }
1661
1662                 else if (t.cs() == "AtBeginDocument") {
1663                         string const name = p.verbatim_item();
1664                         // only non-lyxspecific stuff
1665                         if (in_lyx_preamble &&
1666                             (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1667                                 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1668                                 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1669                                 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1670                                 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1671                                 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1672                                 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1673                                 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1674                                 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1675                                 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1676                                 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1677                                 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1678                                 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1679                                 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1680                                 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1681                                 p.skip_spaces();
1682                         else {
1683                                 ostringstream ss;
1684                                 ss << '\\' << t.cs();
1685                                 ss << '{' << name << '}';
1686                                 h_preamble << ss.str();
1687                         }
1688                 }
1689
1690                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1691                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1692                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
1693                                 || t.cs() == "DeclareRobustCommand"
1694                       || t.cs() == "DeclareRobustCommandx"
1695                                 || t.cs() == "ProvideTextCommandDefault"
1696                                 || t.cs() == "DeclareMathAccent") {
1697                         bool star = false;
1698                         if (p.next_token().character() == '*') {
1699                                 p.get_token();
1700                                 star = true;
1701                         }
1702                         string const name = p.verbatim_item();
1703                         string const opt1 = p.getFullOpt();
1704                         string const opt2 = p.getFullOpt();
1705                         string const body = p.verbatim_item();
1706                         // store the in_lyx_preamble setting
1707                         bool const was_in_lyx_preamble = in_lyx_preamble;
1708                         // font settings
1709                         if (name == "\\rmdefault")
1710                                 if (is_known(body, known_roman_fonts)) {
1711                                         h_font_roman[0] = body;
1712                                         p.skip_spaces();
1713                                         in_lyx_preamble = true;
1714                                 }
1715                         if (name == "\\sfdefault")
1716                                 if (is_known(body, known_sans_fonts)) {
1717                                         h_font_sans[0] = body;
1718                                         p.skip_spaces();
1719                                         in_lyx_preamble = true;
1720                                 }
1721                         if (name == "\\ttdefault")
1722                                 if (is_known(body, known_typewriter_fonts)) {
1723                                         h_font_typewriter[0] = body;
1724                                         p.skip_spaces();
1725                                         in_lyx_preamble = true;
1726                                 }
1727                         if (name == "\\familydefault") {
1728                                 string family = body;
1729                                 // remove leading "\"
1730                                 h_font_default_family = family.erase(0,1);
1731                                 p.skip_spaces();
1732                                 in_lyx_preamble = true;
1733                         }
1734
1735                         // remove LyX-specific definitions that are re-added by LyX
1736                         // if necessary
1737                         // \lyxline is an ancient command that is converted by tex2lyx into
1738                         // a \rule therefore remove its preamble code
1739                         if (name == "\\lyxdot" || name == "\\lyxarrow"
1740                             || name == "\\lyxline" || name == "\\LyX") {
1741                                 p.skip_spaces();
1742                                 in_lyx_preamble = true;
1743                         }
1744
1745                         // Add the command to the known commands
1746                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1747
1748                         // only non-lyxspecific stuff
1749                         if (!in_lyx_preamble) {
1750                                 ostringstream ss;
1751                                 ss << '\\' << t.cs();
1752                                 if (star)
1753                                         ss << '*';
1754                                 ss << '{' << name << '}' << opt1 << opt2
1755                                    << '{' << body << "}";
1756                                 h_preamble << ss.str();
1757 /*
1758                                 ostream & out = in_preamble ? h_preamble : os;
1759                                 out << "\\" << t.cs() << "{" << name << "}"
1760                                     << opts << "{" << body << "}";
1761 */
1762                         }
1763                         // restore the in_lyx_preamble setting
1764                         in_lyx_preamble = was_in_lyx_preamble;
1765                 }
1766
1767                 else if (t.cs() == "documentclass") {
1768                         vector<string>::iterator it;
1769                         vector<string> opts = split_options(p.getArg('[', ']'));
1770                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1771                         delete_opt(opts, known_fontsizes);
1772                         // delete "pt" at the end
1773                         string::size_type i = h_paperfontsize.find("pt");
1774                         if (i != string::npos)
1775                                 h_paperfontsize.erase(i);
1776                         // The documentclass options are always parsed before the options
1777                         // of the babel call so that a language cannot overwrite the babel
1778                         // options.
1779                         handle_opt(opts, known_languages, h_language);
1780                         delete_opt(opts, known_languages);
1781
1782                         // math indentation
1783                         if ((it = find(opts.begin(), opts.end(), "fleqn"))
1784                                  != opts.end()) {
1785                                 h_is_mathindent = "1";
1786                                 opts.erase(it);
1787                         }
1788                         // formula numbering side
1789                         if ((it = find(opts.begin(), opts.end(), "leqno"))
1790                                  != opts.end()) {
1791                                 h_math_numbering_side = "left";
1792                                 opts.erase(it);
1793                         }
1794                         else if ((it = find(opts.begin(), opts.end(), "reqno"))
1795                                  != opts.end()) {
1796                                 h_math_numbering_side = "right";
1797                                 opts.erase(it);
1798                         }
1799
1800                         // paper orientation
1801                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1802                                 h_paperorientation = "landscape";
1803                                 opts.erase(it);
1804                         }
1805                         // paper sides
1806                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1807                                  != opts.end()) {
1808                                 h_papersides = "1";
1809                                 opts.erase(it);
1810                         }
1811                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1812                                  != opts.end()) {
1813                                 h_papersides = "2";
1814                                 opts.erase(it);
1815                         }
1816                         // paper columns
1817                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1818                                  != opts.end()) {
1819                                 h_papercolumns = "1";
1820                                 opts.erase(it);
1821                         }
1822                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1823                                  != opts.end()) {
1824                                 h_papercolumns = "2";
1825                                 opts.erase(it);
1826                         }
1827                         // paper sizes
1828                         // some size options are known to any document classes, other sizes
1829                         // are handled by the \geometry command of the geometry package
1830                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1831                         delete_opt(opts, known_class_paper_sizes);
1832                         // the remaining options
1833                         h_options = join(opts, ",");
1834                         // FIXME This does not work for classes that have a
1835                         //       different name in LyX than in LaTeX
1836                         h_textclass = p.getArg('{', '}');
1837                         p.skip_spaces();
1838                 }
1839
1840                 else if (t.cs() == "usepackage") {
1841                         string const options = p.getArg('[', ']');
1842                         string const name = p.getArg('{', '}');
1843                         vector<string> vecnames;
1844                         split(name, vecnames, ',');
1845                         vector<string>::const_iterator it  = vecnames.begin();
1846                         vector<string>::const_iterator end = vecnames.end();
1847                         for (; it != end; ++it)
1848                                 handle_package(p, trimSpaceAndEol(*it), options,
1849                                                in_lyx_preamble, detectEncoding);
1850                 }
1851
1852                 else if (t.cs() == "inputencoding") {
1853                         string const encoding = p.getArg('{','}');
1854                         Encoding const * const enc = encodings.fromLaTeXName(
1855                                 encoding, Encoding::inputenc, true);
1856                         if (!enc) {
1857                                 if (!detectEncoding)
1858                                         cerr << "Unknown encoding " << encoding
1859                                              << ". Ignoring." << std::endl;
1860                         } else {
1861                                 if (!enc->unsafe())
1862                                         h_inputencoding = enc->name();
1863                                 p.setEncoding(enc->iconvName());
1864                         }
1865                 }
1866
1867                 else if (t.cs() == "newenvironment") {
1868                         string const name = p.getArg('{', '}');
1869                         string const opt1 = p.getFullOpt();
1870                         string const opt2 = p.getFullOpt();
1871                         string const beg = p.verbatim_item();
1872                         string const end = p.verbatim_item();
1873                         if (!in_lyx_preamble) {
1874                                 h_preamble << "\\newenvironment{" << name
1875                                            << '}' << opt1 << opt2 << '{'
1876                                            << beg << "}{" << end << '}';
1877                         }
1878                         add_known_environment(name, opt1, !opt2.empty(),
1879                                               from_utf8(beg), from_utf8(end));
1880
1881                 }
1882
1883                 else if (t.cs() == "newtheorem") {
1884                         bool star = false;
1885                         if (p.next_token().character() == '*') {
1886                                 p.get_token();
1887                                 star = true;
1888                         }
1889                         string const name = p.getArg('{', '}');
1890                         string const opt1 = p.getFullOpt();
1891                         string const opt2 = p.getFullOpt();
1892                         string const body = p.verbatim_item();
1893                         string const opt3 = p.getFullOpt();
1894                         string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
1895
1896                         string const complete = cmd + "{" + name + '}' +
1897                                           opt1 + opt2 + '{' + body + '}' + opt3;
1898
1899                         add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1900
1901                         if (!in_lyx_preamble)
1902                                 h_preamble << complete;
1903                 }
1904
1905                 else if (t.cs() == "def") {
1906                         string name = p.get_token().cs();
1907                         // In fact, name may be more than the name:
1908                         // In the test case of bug 8116
1909                         // name == "csname SF@gobble@opt \endcsname".
1910                         // Therefore, we need to use asInput() instead of cs().
1911                         while (p.next_token().cat() != catBegin)
1912                                 name += p.get_token().asInput();
1913                         if (!in_lyx_preamble)
1914                                 h_preamble << "\\def\\" << name << '{'
1915                                            << p.verbatim_item() << "}";
1916                 }
1917
1918                 else if (t.cs() == "newcolumntype") {
1919                         string const name = p.getArg('{', '}');
1920                         trimSpaceAndEol(name);
1921                         int nargs = 0;
1922                         string opts = p.getOpt();
1923                         if (!opts.empty()) {
1924                                 istringstream is(string(opts, 1));
1925                                 is >> nargs;
1926                         }
1927                         special_columns_[name[0]] = nargs;
1928                         h_preamble << "\\newcolumntype{" << name << "}";
1929                         if (nargs)
1930                                 h_preamble << "[" << nargs << "]";
1931                         h_preamble << "{" << p.verbatim_item() << "}";
1932                 }
1933
1934                 else if (t.cs() == "setcounter") {
1935                         string const name = p.getArg('{', '}');
1936                         string const content = p.getArg('{', '}');
1937                         if (name == "secnumdepth")
1938                                 h_secnumdepth = content;
1939                         else if (name == "tocdepth")
1940                                 h_tocdepth = content;
1941                         else
1942                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1943                 }
1944
1945                 else if (t.cs() == "setlength") {
1946                         string const name = p.verbatim_item();
1947                         string const content = p.verbatim_item();
1948                         // the paragraphs are only not indented when \parindent is set to zero
1949                         if (name == "\\parindent" && content != "") {
1950                                 if (content[0] == '0')
1951                                         h_paragraph_separation = "skip";
1952                                 else
1953                                         h_paragraph_indentation = translate_len(content);
1954                         } else if (name == "\\parskip") {
1955                                 if (content == "\\smallskipamount")
1956                                         h_defskip = "smallskip";
1957                                 else if (content == "\\medskipamount")
1958                                         h_defskip = "medskip";
1959                                 else if (content == "\\bigskipamount")
1960                                         h_defskip = "bigskip";
1961                                 else
1962                                         h_defskip = translate_len(content);
1963                         } else if (name == "\\mathindent") {
1964                                 h_mathindentation = translate_len(content);
1965                         } else
1966                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1967                 }
1968
1969                 else if (t.cs() == "onehalfspacing")
1970                         h_spacing = "onehalf";
1971
1972                 else if (t.cs() == "doublespacing")
1973                         h_spacing = "double";
1974
1975                 else if (t.cs() == "setstretch")
1976                         h_spacing = "other " + p.verbatim_item();
1977
1978                 else if (t.cs() == "synctex") {
1979                         // the scheme is \synctex=value
1980                         // where value can only be "1" or "-1"
1981                         h_output_sync = "1";
1982                         // there can be any character behind the value (e.g. a linebreak or a '\'
1983                         // therefore we extract it char by char
1984                         p.get_token();
1985                         string value = p.get_token().asInput();
1986                         if (value == "-")
1987                                 value += p.get_token().asInput();
1988                         h_output_sync_macro = "\\synctex=" + value;
1989                 }
1990
1991                 else if (t.cs() == "begin") {
1992                         string const name = p.getArg('{', '}');
1993                         if (name == "document")
1994                                 break;
1995                         h_preamble << "\\begin{" << name << "}";
1996                 }
1997
1998                 else if (t.cs() == "geometry") {
1999                         vector<string> opts = split_options(p.getArg('{', '}'));
2000                         handle_geometry(opts);
2001                 }
2002
2003                 else if (t.cs() == "definecolor") {
2004                         string const color = p.getArg('{', '}');
2005                         string const space = p.getArg('{', '}');
2006                         string const value = p.getArg('{', '}');
2007                         if (color == "document_fontcolor" && space == "rgb") {
2008                                 RGBColor c(RGBColorFromLaTeX(value));
2009                                 h_fontcolor = X11hexname(c);
2010                         } else if (color == "note_fontcolor" && space == "rgb") {
2011                                 RGBColor c(RGBColorFromLaTeX(value));
2012                                 h_notefontcolor = X11hexname(c);
2013                         } else if (color == "page_backgroundcolor" && space == "rgb") {
2014                                 RGBColor c(RGBColorFromLaTeX(value));
2015                                 h_backgroundcolor = X11hexname(c);
2016                         } else if (color == "shadecolor" && space == "rgb") {
2017                                 RGBColor c(RGBColorFromLaTeX(value));
2018                                 h_boxbgcolor = X11hexname(c);
2019                         } else {
2020                                 h_preamble << "\\definecolor{" << color
2021                                            << "}{" << space << "}{" << value
2022                                            << '}';
2023                         }
2024                 }
2025
2026                 else if (t.cs() == "bibliographystyle")
2027                         h_biblio_style = p.verbatim_item();
2028
2029                 else if (t.cs() == "jurabibsetup") {
2030                         // FIXME p.getArg('{', '}') is most probably wrong (it
2031                         //       does not handle nested braces).
2032                         //       Use p.verbatim_item() instead.
2033                         vector<string> jurabibsetup =
2034                                 split_options(p.getArg('{', '}'));
2035                         // add jurabibsetup to the jurabib package options
2036                         add_package("jurabib", jurabibsetup);
2037                         if (!jurabibsetup.empty()) {
2038                                 h_preamble << "\\jurabibsetup{"
2039                                            << join(jurabibsetup, ",") << '}';
2040                         }
2041                 }
2042
2043                 else if (t.cs() == "hypersetup") {
2044                         vector<string> hypersetup =
2045                                 split_options(p.verbatim_item());
2046                         // add hypersetup to the hyperref package options
2047                         handle_hyperref(hypersetup);
2048                         if (!hypersetup.empty()) {
2049                                 h_preamble << "\\hypersetup{"
2050                                            << join(hypersetup, ",") << '}';
2051                         }
2052                 }
2053
2054                 else if (t.cs() == "includeonly") {
2055                         vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2056                         for (auto & iofile : includeonlys) {
2057                                 string filename(normalize_filename(iofile));
2058                                 string const path = getMasterFilePath(true);
2059                                 // We want to preserve relative/absolute filenames,
2060                                 // therefore path is only used for testing
2061                                 if (!makeAbsPath(filename, path).exists()) {
2062                                         // The file extension is probably missing.
2063                                         // Now try to find it out.
2064                                         string const tex_name =
2065                                                 find_file(filename, path,
2066                                                           known_tex_extensions);
2067                                         if (!tex_name.empty())
2068                                                 filename = tex_name;
2069                                 }
2070                                 string outname;
2071                                 if (makeAbsPath(filename, path).exists())
2072                                         fix_child_filename(filename);
2073                                 else
2074                                         cerr << "Warning: Could not find included file '"
2075                                              << filename << "'." << endl;
2076                                 outname = changeExtension(filename, "lyx");
2077                                 h_includeonlys.push_back(outname);
2078                         }
2079                 }
2080
2081                 else if (is_known(t.cs(), known_if_3arg_commands)) {
2082                         // prevent misparsing of \usepackage if it is used
2083                         // as an argument (see e.g. our own output of
2084                         // \@ifundefined above)
2085                         string const arg1 = p.verbatim_item();
2086                         string const arg2 = p.verbatim_item();
2087                         string const arg3 = p.verbatim_item();
2088                         // test case \@ifundefined{date}{}{\date{}}
2089                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
2090                             arg2.empty() && arg3 == "\\date{}") {
2091                                 h_suppress_date = "true";
2092                         // older tex2lyx versions did output
2093                         // \@ifundefined{definecolor}{\usepackage{color}}{}
2094                         } else if (t.cs() == "@ifundefined" &&
2095                                    arg1 == "definecolor" &&
2096                                    arg2 == "\\usepackage{color}" &&
2097                                    arg3.empty()) {
2098                                 if (!in_lyx_preamble)
2099                                         h_preamble << package_beg_sep
2100                                                    << "color"
2101                                                    << package_mid_sep
2102                                                    << "\\@ifundefined{definecolor}{color}{}"
2103                                                    << package_end_sep;
2104                         // test for case
2105                         //\@ifundefined{showcaptionsetup}{}{%
2106                         // \PassOptionsToPackage{caption=false}{subfig}}
2107                         // that LyX uses for subfloats
2108                         } else if (t.cs() == "@ifundefined" &&
2109                                    arg1 == "showcaptionsetup" && arg2.empty()
2110                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2111                                 ; // do nothing
2112                         } else if (!in_lyx_preamble) {
2113                                 h_preamble << t.asInput()
2114                                            << '{' << arg1 << '}'
2115                                            << '{' << arg2 << '}'
2116                                            << '{' << arg3 << '}';
2117                         }
2118                 }
2119
2120                 else if (is_known(t.cs(), known_if_commands)) {
2121                         // must not parse anything in conditional code, since
2122                         // LyX would output the parsed contents unconditionally
2123                         if (!in_lyx_preamble)
2124                                 h_preamble << t.asInput();
2125                         handle_if(p, in_lyx_preamble);
2126                 }
2127
2128                 else if (!t.cs().empty() && !in_lyx_preamble)
2129                         h_preamble << '\\' << t.cs();
2130         }
2131
2132         // remove the whitespace
2133         p.skip_spaces();
2134
2135         // Force textclass if the user wanted it
2136         if (!forceclass.empty())
2137                 h_textclass = forceclass;
2138         tc.setName(h_textclass);
2139         if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2140                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2141                 exit(EXIT_FAILURE);
2142         }
2143         if (h_papersides.empty()) {
2144                 ostringstream ss;
2145                 ss << tc.sides();
2146                 h_papersides = ss.str();
2147         }
2148
2149         // If the CJK package is used we cannot set the document language from
2150         // the babel options. Instead, we guess which language is used most
2151         // and set this one.
2152         default_language = h_language;
2153         if (is_full_document &&
2154             (auto_packages.find("CJK") != auto_packages.end() ||
2155              auto_packages.find("CJKutf8") != auto_packages.end())) {
2156                 p.pushPosition();
2157                 h_language = guessLanguage(p, default_language);
2158                 p.popPosition();
2159                 if (explicit_babel && h_language != default_language) {
2160                         // We set the document language to a CJK language,
2161                         // but babel is explicitly called in the user preamble
2162                         // without options. LyX will not add the default
2163                         // language to the document options if it is either
2164                         // english, or no text is set as default language.
2165                         // Therefore we need to add a language option explicitly.
2166                         // FIXME: It would be better to remove all babel calls
2167                         //        from the user preamble, but this is difficult
2168                         //        without re-introducing bug 7861.
2169                         if (h_options.empty())
2170                                 h_options = lyx2babel(default_language);
2171                         else
2172                                 h_options += ',' + lyx2babel(default_language);
2173                 }
2174         }
2175 }
2176
2177
2178 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2179 {
2180         TeX2LyXDocClass dummy;
2181         parse(p, forceclass, true, dummy);
2182         if (h_inputencoding != "auto" && h_inputencoding != "default")
2183                 return h_inputencoding;
2184         return "";
2185 }
2186
2187
2188 string babel2lyx(string const & language)
2189 {
2190         char const * const * where = is_known(language, known_languages);
2191         if (where)
2192                 return known_coded_languages[where - known_languages];
2193         return language;
2194 }
2195
2196
2197 string lyx2babel(string const & language)
2198 {
2199         char const * const * where = is_known(language, known_coded_languages);
2200         if (where)
2201                 return known_languages[where - known_coded_languages];
2202         return language;
2203 }
2204
2205
2206 string Preamble::polyglossia2lyx(string const & language)
2207 {
2208         char const * const * where = is_known(language, polyglossia_languages);
2209         if (where)
2210                 return coded_polyglossia_languages[where - polyglossia_languages];
2211         return language;
2212 }
2213
2214
2215 string rgbcolor2code(string const & name)
2216 {
2217         char const * const * where = is_known(name, known_basic_colors);
2218         if (where) {
2219                 // "red", "green" etc
2220                 return known_basic_color_codes[where - known_basic_colors];
2221         }
2222         // "255,0,0", "0,255,0" etc
2223         RGBColor c(RGBColorFromLaTeX(name));
2224         return X11hexname(c);
2225 }
2226
2227 // }])
2228
2229
2230 } // namespace lyx