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