]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
tex2lyx: refsection and bibbysection support (biblatex)
[lyx.git] / src / tex2lyx / Preamble.cpp
1 /**
2  * \file Preamble.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  * \author Uwe Stöhr
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 // {[(
13
14 #include <config.h>
15
16 #include "Preamble.h"
17 #include "tex2lyx.h"
18
19 #include "Encoding.h"
20 #include "LayoutFile.h"
21 #include "Layout.h"
22 #include "Lexer.h"
23 #include "TextClass.h"
24 #include "version.h"
25
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
30
31 #include "support/regex.h"
32
33 #include <algorithm>
34 #include <iostream>
35
36 using namespace std;
37 using namespace lyx::support;
38
39
40 namespace lyx {
41
42 Preamble preamble;
43
44 namespace {
45
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
47 // further down.
48 /**
49  * known babel language names (including synonyms)
50  * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51  * please keep this in sync with known_coded_languages line by line!
52  */
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
55 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
56 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
57 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
58 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
64 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
65 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
66 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
67 "vietnam", "welsh",
68 0};
69
70 /**
71  * the same as known_languages with .lyx names
72  * please keep this in sync with known_languages line by line!
73  */
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
77 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
78 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
79 "french", "french", "french", "french", "friulan", "galician", "german", "german",
80 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
85 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
86 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
87 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
88 "vietnamese", "welsh",
89 0};
90
91 /// languages with danish quotes (.lyx names)
92 const char * const known_danish_quotes_languages[] = {"danish", 0};
93
94 /// languages with english quotes (.lyx names)
95 const char * const known_english_quotes_languages[] = {"american", "australian",
96 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
97 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
98 "thai", 0};
99
100 /// languages with french quotes (.lyx names)
101 const char * const known_french_quotes_languages[] = {"albanian",
102 "arabic_arabi", "arabic_arabtex", "asturian", "basque", "canadien", "catalan",
103 "french", "friulan", "galician", "greek", "italian", "norsk", "nynorsk",
104 "piedmontese", "polutonikogreek", "russian", "spanish", "spanish-mexico",
105 "turkish", "turkmen", "ukrainian", "vietnamese", 0};
106
107 /// languages with german quotes (.lyx names)
108 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
109 "czech", "german", "georgian", "icelandic", "lithuanian", "lowersorbian", "macedonian",
110 "naustrian", "ngerman", "romansh", "serbian", "serbian-latin", "slovak", "slovene",
111 "uppersorbian", 0};
112
113 /// languages with polish quotes (.lyx names)
114 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
115 "dutch", "estonian", "magyar", "polish", "romanian", 0};
116
117 /// languages with swedish quotes (.lyx names)
118 const char * const known_swedish_quotes_languages[] = {"finnish",
119 "swedish", 0};
120
121 /// known language packages from the times before babel
122 const char * const known_old_language_packages[] = {"french", "frenchle",
123 "frenchpro", "german", "ngerman", "pmfrench", 0};
124
125 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
126
127 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
128 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "fourier",
129 "garamondx", "libertine", "libertine-type1", "lmodern", "mathdesign", "mathpazo",
130 "mathptmx", "newcent", "NotoSerif-TLF", "tgbonum", "tgchorus", "tgpagella", "tgschola",
131 "tgtermes", "utopia", 0};
132
133 const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum-type1",
134 "cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc", "kurier",
135 "kurierc", "kurierl", "kurierlc", "lmss", "NotoSans-TLF", "tgadventor", "tgheros", 0};
136
137 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
138 "courier", "lmtt", "luximono", "fourier", "libertineMono-type1", "lmodern",
139 "mathpazo", "mathptmx", "newcent", "NotoMono-TLF", "tgcursor", "txtt", 0};
140
141 const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0};
142
143 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
144 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
145 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
146 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
147 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
148
149 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
150 "executivepaper", "legalpaper", "letterpaper", 0};
151
152 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
153 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
154
155 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
156 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
157 "columnsep", 0};
158
159 /// commands that can start an \if...\else...\endif sequence
160 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
161 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
162 "ifsidecap", "ifupgreek", 0};
163
164 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
165         "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
166         "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
167
168 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
169         "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
170         "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
171
172 /// conditional commands with three arguments like \@ifundefined{}{}{}
173 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
174 0};
175
176 /// 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                 opt = process_keyval_opt(options, "refsection");
1077                 if (!opt.empty()) {
1078                         if (opt == "none" || opt == "part"
1079                             || opt == "chapter" || opt == "section"
1080                             || opt == "subsection")
1081                                 h_multibib = opt;
1082                         else
1083                                 cerr << "Ignoring unkown refesection value '"
1084                                      << opt << "'.";
1085                 }
1086                 if (!options.empty()) {
1087                         h_biblio_options = join(options, ",");
1088                         options.clear();
1089                 }
1090         }
1091
1092         else if (name == "jurabib") {
1093                 h_biblio_style = "jurabib";
1094                 h_cite_engine = "jurabib";
1095                 h_cite_engine_type = "authoryear";
1096                 if (!options.empty())
1097                         h_biblio_options = join(options, ",");
1098         }
1099
1100         else if (name == "bibtopic")
1101                 h_use_bibtopic = "true";
1102
1103         else if (name == "hyperref")
1104                 handle_hyperref(options);
1105
1106         else if (name == "algorithm2e") {
1107                 // Load "algorithm2e" module
1108                 addModule("algorithm2e");
1109                 // Add the package options to the global document options
1110                 if (!options.empty()) {
1111                         if (h_options.empty())
1112                                 h_options = join(options, ",");
1113                         else
1114                                 h_options += ',' + join(options, ",");
1115                 }
1116         }
1117         else if (name == "microtype") {
1118                 //we internally support only microtype without params
1119                 if (options.empty())
1120                         h_use_microtype = "true";
1121                 else
1122                         h_preamble << "\\usepackage[" << opts << "]{microtype}";
1123         }
1124
1125         else if (!in_lyx_preamble) {
1126                 if (options.empty())
1127                         h_preamble << "\\usepackage{" << name << '}';
1128                 else {
1129                         h_preamble << "\\usepackage[" << opts << "]{"
1130                                    << name << '}';
1131                         options.clear();
1132                 }
1133                 if (p.next_token().cat() == catNewline ||
1134                     (p.next_token().cat() == catSpace &&
1135                      p.next_next_token().cat() == catNewline))
1136                         h_preamble << '\n';
1137         }
1138
1139         // We need to do something with the options...
1140         if (!options.empty() && !detectEncoding)
1141                 cerr << "Ignoring options '" << join(options, ",")
1142                      << "' of package " << name << '.' << endl;
1143
1144         // remove the whitespace
1145         p.skip_spaces();
1146 }
1147
1148
1149 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1150 {
1151         while (p.good()) {
1152                 Token t = p.get_token();
1153                 if (t.cat() == catEscape &&
1154                     is_known(t.cs(), known_if_commands))
1155                         handle_if(p, in_lyx_preamble);
1156                 else {
1157                         if (!in_lyx_preamble)
1158                                 h_preamble << t.asInput();
1159                         if (t.cat() == catEscape && t.cs() == "fi")
1160                                 return;
1161                 }
1162         }
1163 }
1164
1165
1166 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1167 {
1168         // set the quote language
1169         // LyX only knows the following quotes languages:
1170         // english, swedish, german, polish, french and danish
1171         // (quotes for "japanese" and "chinese-traditional" are missing because
1172         //  they wouldn't be useful: https://www.lyx.org/trac/ticket/6383)
1173         // conversion list taken from
1174         // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1175         // (quotes for kazakh and interlingua are unknown)
1176         // danish
1177         if (is_known(h_language, known_danish_quotes_languages))
1178                 h_quotes_style = "danish";
1179         // french
1180         else if (is_known(h_language, known_french_quotes_languages))
1181                 h_quotes_style = "french";
1182         // german
1183         else if (is_known(h_language, known_german_quotes_languages))
1184                 h_quotes_style = "german";
1185         // polish
1186         else if (is_known(h_language, known_polish_quotes_languages))
1187                 h_quotes_style = "polish";
1188         // swedish
1189         else if (is_known(h_language, known_swedish_quotes_languages))
1190                 h_quotes_style = "swedish";
1191         //english
1192         else if (is_known(h_language, known_english_quotes_languages))
1193                 h_quotes_style = "english";
1194
1195         if (contains(h_float_placement, "H"))
1196                 registerAutomaticallyLoadedPackage("float");
1197         if (h_spacing != "single" && h_spacing != "default")
1198                 registerAutomaticallyLoadedPackage("setspace");
1199         if (h_use_packages["amsmath"] == "2") {
1200                 // amsbsy and amstext are already provided by amsmath
1201                 registerAutomaticallyLoadedPackage("amsbsy");
1202                 registerAutomaticallyLoadedPackage("amstext");
1203         }
1204
1205         // output the LyX file settings
1206         // Important: Keep the version formatting in sync with LyX and
1207         //            lyx2lyx (bug 7951)
1208         string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1209         os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1210            << lyx_version_minor << '\n'
1211            << "\\lyxformat " << LYX_FORMAT << '\n'
1212            << "\\begin_document\n"
1213            << "\\begin_header\n"
1214            << "\\save_transient_properties " << h_save_transient_properties << "\n"
1215            << "\\origin " << origin << "\n"
1216            << "\\textclass " << h_textclass << "\n";
1217         string const raw = subdoc ? empty_string() : h_preamble.str();
1218         if (!raw.empty()) {
1219                 os << "\\begin_preamble\n";
1220                 for (string::size_type i = 0; i < raw.size(); ++i) {
1221                         if (raw[i] == package_beg_sep) {
1222                                 // Here follows some package loading code that
1223                                 // must be skipped if the package is loaded
1224                                 // automatically.
1225                                 string::size_type j = raw.find(package_mid_sep, i);
1226                                 if (j == string::npos)
1227                                         return false;
1228                                 string::size_type k = raw.find(package_end_sep, j);
1229                                 if (k == string::npos)
1230                                         return false;
1231                                 string const package = raw.substr(i + 1, j - i - 1);
1232                                 string const replacement = raw.substr(j + 1, k - j - 1);
1233                                 if (auto_packages.find(package) == auto_packages.end())
1234                                         os << replacement;
1235                                 i = k;
1236                         } else
1237                                 os.put(raw[i]);
1238                 }
1239                 os << "\n\\end_preamble\n";
1240         }
1241         if (!h_options.empty())
1242                 os << "\\options " << h_options << "\n";
1243         os << "\\use_default_options " << h_use_default_options << "\n";
1244         if (!used_modules.empty()) {
1245                 os << "\\begin_modules\n";
1246                 vector<string>::const_iterator const end = used_modules.end();
1247                 vector<string>::const_iterator it = used_modules.begin();
1248                 for (; it != end; ++it)
1249                         os << *it << '\n';
1250                 os << "\\end_modules\n";
1251         }
1252         os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1253            << "\\language " << h_language << "\n"
1254            << "\\language_package " << h_language_package << "\n"
1255            << "\\inputencoding " << h_inputencoding << "\n"
1256            << "\\fontencoding " << h_fontencoding << "\n"
1257            << "\\font_roman \"" << h_font_roman[0]
1258            << "\" \"" << h_font_roman[1] << "\"\n"
1259            << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1260            << "\\font_typewriter \"" << h_font_typewriter[0]
1261            << "\" \"" << h_font_typewriter[1] << "\"\n"
1262            << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1263            << "\\font_default_family " << h_font_default_family << "\n"
1264            << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1265            << "\\font_sc " << h_font_sc << "\n"
1266            << "\\font_osf " << h_font_osf << "\n"
1267            << "\\font_sf_scale " << h_font_sf_scale[0]
1268            << ' ' << h_font_sf_scale[1] << '\n'
1269            << "\\font_tt_scale " << h_font_tt_scale[0]
1270            << ' ' << h_font_tt_scale[1] << '\n';
1271         if (!h_font_cjk.empty())
1272                 os << "\\font_cjk " << h_font_cjk << '\n';
1273         os << "\\use_microtype " << h_use_microtype << '\n'
1274            << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1275            << "\\graphics " << h_graphics << '\n'
1276            << "\\default_output_format " << h_default_output_format << "\n"
1277            << "\\output_sync " << h_output_sync << "\n";
1278         if (h_output_sync == "1")
1279                 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1280         os << "\\bibtex_command " << h_bibtex_command << "\n"
1281            << "\\index_command " << h_index_command << "\n";
1282         if (!h_float_placement.empty())
1283                 os << "\\float_placement " << h_float_placement << "\n";
1284         os << "\\paperfontsize " << h_paperfontsize << "\n"
1285            << "\\spacing " << h_spacing << "\n"
1286            << "\\use_hyperref " << h_use_hyperref << '\n';
1287         if (h_use_hyperref == "true") {
1288                 if (!h_pdf_title.empty())
1289                         os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1290                 if (!h_pdf_author.empty())
1291                         os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1292                 if (!h_pdf_subject.empty())
1293                         os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1294                 if (!h_pdf_keywords.empty())
1295                         os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1296                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1297                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1298                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1299                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1300                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1301                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1302                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1303                       "\\pdf_backref " << h_pdf_backref << "\n"
1304                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1305                 if (!h_pdf_pagemode.empty())
1306                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1307                 if (!h_pdf_quoted_options.empty())
1308                         os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1309         }
1310         os << "\\papersize " << h_papersize << "\n"
1311            << "\\use_geometry " << h_use_geometry << '\n';
1312         for (map<string, string>::const_iterator it = h_use_packages.begin();
1313              it != h_use_packages.end(); ++it)
1314                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1315         os << "\\cite_engine " << h_cite_engine << '\n'
1316            << "\\cite_engine_type " << h_cite_engine_type << '\n'
1317            << "\\biblio_style " << h_biblio_style << "\n"
1318            << "\\use_bibtopic " << h_use_bibtopic << "\n";
1319         if (!h_biblio_options.empty())
1320                 os << "\\biblio_options " << h_biblio_options << "\n";
1321         if (!h_biblatex_bibstyle.empty())
1322                 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1323         if (!h_biblatex_citestyle.empty())
1324                 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1325         if (!h_multibib.empty())
1326                 os << "\\multibib " << h_multibib << "\n";
1327         os << "\\use_indices " << h_use_indices << "\n"
1328            << "\\paperorientation " << h_paperorientation << '\n'
1329            << "\\suppress_date " << h_suppress_date << '\n'
1330            << "\\justification " << h_justification << '\n'
1331            << "\\use_refstyle " << h_use_refstyle << '\n'
1332            << "\\use_minted " << h_use_minted << '\n';
1333         if (!h_fontcolor.empty())
1334                 os << "\\fontcolor " << h_fontcolor << '\n';
1335         if (!h_notefontcolor.empty())
1336                 os << "\\notefontcolor " << h_notefontcolor << '\n';
1337         if (!h_backgroundcolor.empty())
1338                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1339         if (!h_boxbgcolor.empty())
1340                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1341         if (index_number != 0)
1342                 for (int i = 0; i < index_number; i++) {
1343                         os << "\\index " << h_index[i] << '\n'
1344                            << "\\shortcut " << h_shortcut[i] << '\n'
1345                            << "\\color " << h_color << '\n'
1346                            << "\\end_index\n";
1347                 }
1348         else {
1349                 os << "\\index " << h_index[0] << '\n'
1350                    << "\\shortcut " << h_shortcut[0] << '\n'
1351                    << "\\color " << h_color << '\n'
1352                    << "\\end_index\n";
1353         }
1354         os << h_margins
1355            << "\\secnumdepth " << h_secnumdepth << "\n"
1356            << "\\tocdepth " << h_tocdepth << "\n"
1357            << "\\paragraph_separation " << h_paragraph_separation << "\n";
1358         if (h_paragraph_separation == "skip")
1359                 os << "\\defskip " << h_defskip << "\n";
1360         else
1361                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1362         os << "\\is_math_indent " << h_is_mathindent << "\n";
1363         if (!h_mathindentation.empty())
1364                 os << "\\math_indentation " << h_mathindentation << "\n";
1365         os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1366         os << "\\quotes_style " << h_quotes_style << "\n"
1367            << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1368            << "\\papercolumns " << h_papercolumns << "\n"
1369            << "\\papersides " << h_papersides << "\n"
1370            << "\\paperpagestyle " << h_paperpagestyle << "\n";
1371         if (!h_listings_params.empty())
1372                 os << "\\listings_params " << h_listings_params << "\n";
1373         os << "\\tracking_changes " << h_tracking_changes << "\n"
1374            << "\\output_changes " << h_output_changes << "\n"
1375            << "\\html_math_output " << h_html_math_output << "\n"
1376            << "\\html_css_as_file " << h_html_css_as_file << "\n"
1377            << "\\html_be_strict " << h_html_be_strict << "\n"
1378            << authors_
1379            << "\\end_header\n\n"
1380            << "\\begin_body\n";
1381         return true;
1382 }
1383
1384
1385 void Preamble::parse(Parser & p, string const & forceclass,
1386                      TeX2LyXDocClass & tc)
1387 {
1388         // initialize fixed types
1389         special_columns_['D'] = 3;
1390         parse(p, forceclass, false, tc);
1391 }
1392
1393
1394 void Preamble::parse(Parser & p, string const & forceclass,
1395                      bool detectEncoding, TeX2LyXDocClass & tc)
1396 {
1397         bool is_full_document = false;
1398         bool is_lyx_file = false;
1399         bool in_lyx_preamble = false;
1400
1401         // determine whether this is a full document or a fragment for inclusion
1402         while (p.good()) {
1403                 Token const & t = p.get_token();
1404
1405                 if (t.cat() == catEscape && t.cs() == "documentclass") {
1406                         is_full_document = true;
1407                         break;
1408                 }
1409         }
1410         p.reset();
1411
1412         if (detectEncoding && !is_full_document)
1413                 return;
1414
1415         while (is_full_document && p.good()) {
1416                 if (detectEncoding && h_inputencoding != "auto" &&
1417                     h_inputencoding != "default")
1418                         return;
1419
1420                 Token const & t = p.get_token();
1421
1422 #ifdef FILEDEBUG
1423                 if (!detectEncoding)
1424                         cerr << "t: " << t << '\n';
1425 #endif
1426
1427                 //
1428                 // cat codes
1429                 //
1430                 if (!in_lyx_preamble &&
1431                     (t.cat() == catLetter ||
1432                      t.cat() == catSuper ||
1433                      t.cat() == catSub ||
1434                      t.cat() == catOther ||
1435                      t.cat() == catMath ||
1436                      t.cat() == catActive ||
1437                      t.cat() == catBegin ||
1438                      t.cat() == catEnd ||
1439                      t.cat() == catAlign ||
1440                      t.cat() == catParameter))
1441                         h_preamble << t.cs();
1442
1443                 else if (!in_lyx_preamble &&
1444                          (t.cat() == catSpace || t.cat() == catNewline))
1445                         h_preamble << t.asInput();
1446
1447                 else if (t.cat() == catComment) {
1448                         static regex const islyxfile("%% LyX .* created this file");
1449                         static regex const usercommands("User specified LaTeX commands");
1450
1451                         string const comment = t.asInput();
1452
1453                         // magically switch encoding default if it looks like XeLaTeX
1454                         static string const magicXeLaTeX =
1455                                 "% This document must be compiled with XeLaTeX ";
1456                         if (comment.size() > magicXeLaTeX.size()
1457                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1458                                   && h_inputencoding == "auto") {
1459                                 if (!detectEncoding)
1460                                         cerr << "XeLaTeX comment found, switching to UTF8\n";
1461                                 h_inputencoding = "utf8";
1462                         }
1463                         smatch sub;
1464                         if (regex_search(comment, sub, islyxfile)) {
1465                                 is_lyx_file = true;
1466                                 in_lyx_preamble = true;
1467                         } else if (is_lyx_file
1468                                    && regex_search(comment, sub, usercommands))
1469                                 in_lyx_preamble = false;
1470                         else if (!in_lyx_preamble)
1471                                 h_preamble << t.asInput();
1472                 }
1473
1474                 else if (t.cs() == "PassOptionsToPackage") {
1475                         string const poptions = p.getArg('{', '}');
1476                         string const package = p.verbatim_item();
1477                         extra_package_options_.insert(make_pair(package, poptions));
1478                 }
1479
1480                 else if (t.cs() == "pagestyle")
1481                         h_paperpagestyle = p.verbatim_item();
1482
1483                 else if (t.cs() == "setdefaultlanguage") {
1484                         xetex = true;
1485                         // We don't yet care about non-language variant options
1486                         // because LyX doesn't support this yet, see bug #8214
1487                         if (p.hasOpt()) {
1488                                 string langopts = p.getOpt();
1489                                 // check if the option contains a variant, if yes, extract it
1490                                 string::size_type pos_var = langopts.find("variant");
1491                                 string::size_type i = langopts.find(',', pos_var);
1492                                 string::size_type k = langopts.find('=', pos_var);
1493                                 if (pos_var != string::npos){
1494                                         string variant;
1495                                         if (i == string::npos)
1496                                                 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1497                                         else
1498                                                 variant = langopts.substr(k + 1, i - k - 1);
1499                                         h_language = variant;
1500                                 }
1501                                 p.verbatim_item();
1502                         } else
1503                                 h_language = p.verbatim_item();
1504                         //finally translate the poyglossia name to a LyX name
1505                         h_language = polyglossia2lyx(h_language);
1506                 }
1507
1508                 else if (t.cs() == "setotherlanguage") {
1509                         // We don't yet care about the option because LyX doesn't
1510                         // support this yet, see bug #8214
1511                         p.hasOpt() ? p.getOpt() : string();
1512                         p.verbatim_item();
1513                 }
1514
1515                 else if (t.cs() == "setmainfont") {
1516                         // we don't care about the option
1517                         p.hasOpt() ? p.getOpt() : string();
1518                         h_font_roman[1] = p.getArg('{', '}');
1519                 }
1520
1521                 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1522                         // LyX currently only supports the scale option
1523                         string scale;
1524                         if (p.hasOpt()) {
1525                                 string fontopts = p.getArg('[', ']');
1526                                 // check if the option contains a scaling, if yes, extract it
1527                                 string::size_type pos = fontopts.find("Scale");
1528                                 if (pos != string::npos) {
1529                                         string::size_type i = fontopts.find(',', pos);
1530                                         if (i == string::npos)
1531                                                 scale_as_percentage(fontopts.substr(pos + 1), scale);
1532                                         else
1533                                                 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1534                                 }
1535                         }
1536                         if (t.cs() == "setsansfont") {
1537                                 if (!scale.empty())
1538                                         h_font_sf_scale[1] = scale;
1539                                 h_font_sans[1] = p.getArg('{', '}');
1540                         } else {
1541                                 if (!scale.empty())
1542                                         h_font_tt_scale[1] = scale;
1543                                 h_font_typewriter[1] = p.getArg('{', '}');
1544                         }
1545                 }
1546
1547                 else if (t.cs() == "date") {
1548                         string argument = p.getArg('{', '}');
1549                         if (argument.empty())
1550                                 h_suppress_date = "true";
1551                         else
1552                                 h_preamble << t.asInput() << '{' << argument << '}';
1553                 }
1554
1555                 else if (t.cs() == "color") {
1556                         string const space =
1557                                 (p.hasOpt() ? p.getOpt() : string());
1558                         string argument = p.getArg('{', '}');
1559                         // check the case that a standard color is used
1560                         if (space.empty() && is_known(argument, known_basic_colors)) {
1561                                 h_fontcolor = rgbcolor2code(argument);
1562                                 registerAutomaticallyLoadedPackage("color");
1563                         } else if (space.empty() && argument == "document_fontcolor")
1564                                 registerAutomaticallyLoadedPackage("color");
1565                         // check the case that LyX's document_fontcolor is defined
1566                         // but not used for \color
1567                         else {
1568                                 h_preamble << t.asInput();
1569                                 if (!space.empty())
1570                                         h_preamble << space;
1571                                 h_preamble << '{' << argument << '}';
1572                                 // the color might already be set because \definecolor
1573                                 // is parsed before this
1574                                 h_fontcolor = "";
1575                         }
1576                 }
1577
1578                 else if (t.cs() == "pagecolor") {
1579                         string argument = p.getArg('{', '}');
1580                         // check the case that a standard color is used
1581                         if (is_known(argument, known_basic_colors)) {
1582                                 h_backgroundcolor = rgbcolor2code(argument);
1583                         } else if (argument == "page_backgroundcolor")
1584                                 registerAutomaticallyLoadedPackage("color");
1585                         // check the case that LyX's page_backgroundcolor is defined
1586                         // but not used for \pagecolor
1587                         else {
1588                                 h_preamble << t.asInput() << '{' << argument << '}';
1589                                 // the color might already be set because \definecolor
1590                                 // is parsed before this
1591                                 h_backgroundcolor = "";
1592                         }
1593                 }
1594
1595                 else if (t.cs() == "makeatletter") {
1596                         // LyX takes care of this
1597                         p.setCatcode('@', catLetter);
1598                 }
1599
1600                 else if (t.cs() == "makeatother") {
1601                         // LyX takes care of this
1602                         p.setCatcode('@', catOther);
1603                 }
1604
1605                 else if (t.cs() == "makeindex") {
1606                         // LyX will re-add this if a print index command is found
1607                         p.skip_spaces();
1608                 }
1609
1610                 else if (t.cs() == "newindex") {
1611                         string const indexname = p.getArg('[', ']');
1612                         string const shortcut = p.verbatim_item();
1613                         if (!indexname.empty())
1614                                 h_index[index_number] = indexname;
1615                         else
1616                                 h_index[index_number] = shortcut;
1617                         h_shortcut[index_number] = shortcut;
1618                         index_number += 1;
1619                         p.skip_spaces();
1620                 }
1621
1622                 else if (t.cs() == "addbibresource")
1623                         biblatex_bibliographies.push_back(removeExtension(p.getArg('{', '}')));
1624
1625                 else if (t.cs() == "bibliography") {
1626                         vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1627                         biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1628                 }
1629
1630                 else if (t.cs() == "RS@ifundefined") {
1631                         string const name = p.verbatim_item();
1632                         string const body1 = p.verbatim_item();
1633                         string const body2 = p.verbatim_item();
1634                         // only non-lyxspecific stuff
1635                         if (in_lyx_preamble &&
1636                             (name == "subsecref" || name == "thmref" || name == "lemref"))
1637                                 p.skip_spaces();
1638                         else {
1639                                 ostringstream ss;
1640                                 ss << '\\' << t.cs();
1641                                 ss << '{' << name << '}'
1642                                    << '{' << body1 << '}'
1643                                    << '{' << body2 << '}';
1644                                 h_preamble << ss.str();
1645                         }
1646                 }
1647
1648                 else if (t.cs() == "AtBeginDocument") {
1649                         string const name = p.verbatim_item();
1650                         // only non-lyxspecific stuff
1651                         if (in_lyx_preamble &&
1652                             (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1653                                 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1654                                 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1655                                 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1656                                 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1657                                 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1658                                 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1659                                 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1660                                 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1661                                 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1662                                 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1663                                 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1664                                 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1665                                 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1666                                 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1667                                 p.skip_spaces();
1668                         else {
1669                                 ostringstream ss;
1670                                 ss << '\\' << t.cs();
1671                                 ss << '{' << name << '}';
1672                                 h_preamble << ss.str();
1673                         }
1674                 }
1675
1676                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1677                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1678                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
1679                                 || t.cs() == "DeclareRobustCommand"
1680                       || t.cs() == "DeclareRobustCommandx"
1681                                 || t.cs() == "ProvideTextCommandDefault"
1682                                 || t.cs() == "DeclareMathAccent") {
1683                         bool star = false;
1684                         if (p.next_token().character() == '*') {
1685                                 p.get_token();
1686                                 star = true;
1687                         }
1688                         string const name = p.verbatim_item();
1689                         string const opt1 = p.getFullOpt();
1690                         string const opt2 = p.getFullOpt();
1691                         string const body = p.verbatim_item();
1692                         // store the in_lyx_preamble setting
1693                         bool const was_in_lyx_preamble = in_lyx_preamble;
1694                         // font settings
1695                         if (name == "\\rmdefault")
1696                                 if (is_known(body, known_roman_fonts)) {
1697                                         h_font_roman[0] = body;
1698                                         p.skip_spaces();
1699                                         in_lyx_preamble = true;
1700                                 }
1701                         if (name == "\\sfdefault")
1702                                 if (is_known(body, known_sans_fonts)) {
1703                                         h_font_sans[0] = body;
1704                                         p.skip_spaces();
1705                                         in_lyx_preamble = true;
1706                                 }
1707                         if (name == "\\ttdefault")
1708                                 if (is_known(body, known_typewriter_fonts)) {
1709                                         h_font_typewriter[0] = body;
1710                                         p.skip_spaces();
1711                                         in_lyx_preamble = true;
1712                                 }
1713                         if (name == "\\familydefault") {
1714                                 string family = body;
1715                                 // remove leading "\"
1716                                 h_font_default_family = family.erase(0,1);
1717                                 p.skip_spaces();
1718                                 in_lyx_preamble = true;
1719                         }
1720
1721                         // remove LyX-specific definitions that are re-added by LyX
1722                         // if necessary
1723                         // \lyxline is an ancient command that is converted by tex2lyx into
1724                         // a \rule therefore remove its preamble code
1725                         if (name == "\\lyxdot" || name == "\\lyxarrow"
1726                             || name == "\\lyxline" || name == "\\LyX") {
1727                                 p.skip_spaces();
1728                                 in_lyx_preamble = true;
1729                         }
1730
1731                         // Add the command to the known commands
1732                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1733
1734                         // only non-lyxspecific stuff
1735                         if (!in_lyx_preamble) {
1736                                 ostringstream ss;
1737                                 ss << '\\' << t.cs();
1738                                 if (star)
1739                                         ss << '*';
1740                                 ss << '{' << name << '}' << opt1 << opt2
1741                                    << '{' << body << "}";
1742                                 h_preamble << ss.str();
1743 /*
1744                                 ostream & out = in_preamble ? h_preamble : os;
1745                                 out << "\\" << t.cs() << "{" << name << "}"
1746                                     << opts << "{" << body << "}";
1747 */
1748                         }
1749                         // restore the in_lyx_preamble setting
1750                         in_lyx_preamble = was_in_lyx_preamble;
1751                 }
1752
1753                 else if (t.cs() == "documentclass") {
1754                         vector<string>::iterator it;
1755                         vector<string> opts = split_options(p.getArg('[', ']'));
1756                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1757                         delete_opt(opts, known_fontsizes);
1758                         // delete "pt" at the end
1759                         string::size_type i = h_paperfontsize.find("pt");
1760                         if (i != string::npos)
1761                                 h_paperfontsize.erase(i);
1762                         // The documentclass options are always parsed before the options
1763                         // of the babel call so that a language cannot overwrite the babel
1764                         // options.
1765                         handle_opt(opts, known_languages, h_language);
1766                         delete_opt(opts, known_languages);
1767
1768                         // math indentation
1769                         if ((it = find(opts.begin(), opts.end(), "fleqn"))
1770                                  != opts.end()) {
1771                                 h_is_mathindent = "1";
1772                                 opts.erase(it);
1773                         }
1774                         // formula numbering side
1775                         if ((it = find(opts.begin(), opts.end(), "leqno"))
1776                                  != opts.end()) {
1777                                 h_math_numbering_side = "left";
1778                                 opts.erase(it);
1779                         }
1780                         else if ((it = find(opts.begin(), opts.end(), "reqno"))
1781                                  != opts.end()) {
1782                                 h_math_numbering_side = "right";
1783                                 opts.erase(it);
1784                         }
1785
1786                         // paper orientation
1787                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1788                                 h_paperorientation = "landscape";
1789                                 opts.erase(it);
1790                         }
1791                         // paper sides
1792                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1793                                  != opts.end()) {
1794                                 h_papersides = "1";
1795                                 opts.erase(it);
1796                         }
1797                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1798                                  != opts.end()) {
1799                                 h_papersides = "2";
1800                                 opts.erase(it);
1801                         }
1802                         // paper columns
1803                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1804                                  != opts.end()) {
1805                                 h_papercolumns = "1";
1806                                 opts.erase(it);
1807                         }
1808                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1809                                  != opts.end()) {
1810                                 h_papercolumns = "2";
1811                                 opts.erase(it);
1812                         }
1813                         // paper sizes
1814                         // some size options are known to any document classes, other sizes
1815                         // are handled by the \geometry command of the geometry package
1816                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1817                         delete_opt(opts, known_class_paper_sizes);
1818                         // the remaining options
1819                         h_options = join(opts, ",");
1820                         // FIXME This does not work for classes that have a
1821                         //       different name in LyX than in LaTeX
1822                         h_textclass = p.getArg('{', '}');
1823                         p.skip_spaces();
1824                 }
1825
1826                 else if (t.cs() == "usepackage") {
1827                         string const options = p.getArg('[', ']');
1828                         string const name = p.getArg('{', '}');
1829                         vector<string> vecnames;
1830                         split(name, vecnames, ',');
1831                         vector<string>::const_iterator it  = vecnames.begin();
1832                         vector<string>::const_iterator end = vecnames.end();
1833                         for (; it != end; ++it)
1834                                 handle_package(p, trimSpaceAndEol(*it), options,
1835                                                in_lyx_preamble, detectEncoding);
1836                 }
1837
1838                 else if (t.cs() == "inputencoding") {
1839                         string const encoding = p.getArg('{','}');
1840                         Encoding const * const enc = encodings.fromLaTeXName(
1841                                 encoding, Encoding::inputenc, true);
1842                         if (!enc) {
1843                                 if (!detectEncoding)
1844                                         cerr << "Unknown encoding " << encoding
1845                                              << ". Ignoring." << std::endl;
1846                         } else {
1847                                 if (!enc->unsafe())
1848                                         h_inputencoding = enc->name();
1849                                 p.setEncoding(enc->iconvName());
1850                         }
1851                 }
1852
1853                 else if (t.cs() == "newenvironment") {
1854                         string const name = p.getArg('{', '}');
1855                         string const opt1 = p.getFullOpt();
1856                         string const opt2 = p.getFullOpt();
1857                         string const beg = p.verbatim_item();
1858                         string const end = p.verbatim_item();
1859                         if (!in_lyx_preamble) {
1860                                 h_preamble << "\\newenvironment{" << name
1861                                            << '}' << opt1 << opt2 << '{'
1862                                            << beg << "}{" << end << '}';
1863                         }
1864                         add_known_environment(name, opt1, !opt2.empty(),
1865                                               from_utf8(beg), from_utf8(end));
1866
1867                 }
1868
1869                 else if (t.cs() == "newtheorem") {
1870                         bool star = false;
1871                         if (p.next_token().character() == '*') {
1872                                 p.get_token();
1873                                 star = true;
1874                         }
1875                         string const name = p.getArg('{', '}');
1876                         string const opt1 = p.getFullOpt();
1877                         string const opt2 = p.getFullOpt();
1878                         string const body = p.verbatim_item();
1879                         string const opt3 = p.getFullOpt();
1880                         string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
1881
1882                         string const complete = cmd + "{" + name + '}' +
1883                                           opt1 + opt2 + '{' + body + '}' + opt3;
1884
1885                         add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1886
1887                         if (!in_lyx_preamble)
1888                                 h_preamble << complete;
1889                 }
1890
1891                 else if (t.cs() == "def") {
1892                         string name = p.get_token().cs();
1893                         // In fact, name may be more than the name:
1894                         // In the test case of bug 8116
1895                         // name == "csname SF@gobble@opt \endcsname".
1896                         // Therefore, we need to use asInput() instead of cs().
1897                         while (p.next_token().cat() != catBegin)
1898                                 name += p.get_token().asInput();
1899                         if (!in_lyx_preamble)
1900                                 h_preamble << "\\def\\" << name << '{'
1901                                            << p.verbatim_item() << "}";
1902                 }
1903
1904                 else if (t.cs() == "newcolumntype") {
1905                         string const name = p.getArg('{', '}');
1906                         trimSpaceAndEol(name);
1907                         int nargs = 0;
1908                         string opts = p.getOpt();
1909                         if (!opts.empty()) {
1910                                 istringstream is(string(opts, 1));
1911                                 is >> nargs;
1912                         }
1913                         special_columns_[name[0]] = nargs;
1914                         h_preamble << "\\newcolumntype{" << name << "}";
1915                         if (nargs)
1916                                 h_preamble << "[" << nargs << "]";
1917                         h_preamble << "{" << p.verbatim_item() << "}";
1918                 }
1919
1920                 else if (t.cs() == "setcounter") {
1921                         string const name = p.getArg('{', '}');
1922                         string const content = p.getArg('{', '}');
1923                         if (name == "secnumdepth")
1924                                 h_secnumdepth = content;
1925                         else if (name == "tocdepth")
1926                                 h_tocdepth = content;
1927                         else
1928                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1929                 }
1930
1931                 else if (t.cs() == "setlength") {
1932                         string const name = p.verbatim_item();
1933                         string const content = p.verbatim_item();
1934                         // the paragraphs are only not indented when \parindent is set to zero
1935                         if (name == "\\parindent" && content != "") {
1936                                 if (content[0] == '0')
1937                                         h_paragraph_separation = "skip";
1938                                 else
1939                                         h_paragraph_indentation = translate_len(content);
1940                         } else if (name == "\\parskip") {
1941                                 if (content == "\\smallskipamount")
1942                                         h_defskip = "smallskip";
1943                                 else if (content == "\\medskipamount")
1944                                         h_defskip = "medskip";
1945                                 else if (content == "\\bigskipamount")
1946                                         h_defskip = "bigskip";
1947                                 else
1948                                         h_defskip = translate_len(content);
1949                         } else if (name == "\\mathindent") {
1950                                 h_mathindentation = translate_len(content);
1951                         } else
1952                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1953                 }
1954
1955                 else if (t.cs() == "onehalfspacing")
1956                         h_spacing = "onehalf";
1957
1958                 else if (t.cs() == "doublespacing")
1959                         h_spacing = "double";
1960
1961                 else if (t.cs() == "setstretch")
1962                         h_spacing = "other " + p.verbatim_item();
1963
1964                 else if (t.cs() == "synctex") {
1965                         // the scheme is \synctex=value
1966                         // where value can only be "1" or "-1"
1967                         h_output_sync = "1";
1968                         // there can be any character behind the value (e.g. a linebreak or a '\'
1969                         // therefore we extract it char by char
1970                         p.get_token();
1971                         string value = p.get_token().asInput();
1972                         if (value == "-")
1973                                 value += p.get_token().asInput();
1974                         h_output_sync_macro = "\\synctex=" + value;
1975                 }
1976
1977                 else if (t.cs() == "begin") {
1978                         string const name = p.getArg('{', '}');
1979                         if (name == "document")
1980                                 break;
1981                         h_preamble << "\\begin{" << name << "}";
1982                 }
1983
1984                 else if (t.cs() == "geometry") {
1985                         vector<string> opts = split_options(p.getArg('{', '}'));
1986                         handle_geometry(opts);
1987                 }
1988
1989                 else if (t.cs() == "definecolor") {
1990                         string const color = p.getArg('{', '}');
1991                         string const space = p.getArg('{', '}');
1992                         string const value = p.getArg('{', '}');
1993                         if (color == "document_fontcolor" && space == "rgb") {
1994                                 RGBColor c(RGBColorFromLaTeX(value));
1995                                 h_fontcolor = X11hexname(c);
1996                         } else if (color == "note_fontcolor" && space == "rgb") {
1997                                 RGBColor c(RGBColorFromLaTeX(value));
1998                                 h_notefontcolor = X11hexname(c);
1999                         } else if (color == "page_backgroundcolor" && space == "rgb") {
2000                                 RGBColor c(RGBColorFromLaTeX(value));
2001                                 h_backgroundcolor = X11hexname(c);
2002                         } else if (color == "shadecolor" && space == "rgb") {
2003                                 RGBColor c(RGBColorFromLaTeX(value));
2004                                 h_boxbgcolor = X11hexname(c);
2005                         } else {
2006                                 h_preamble << "\\definecolor{" << color
2007                                            << "}{" << space << "}{" << value
2008                                            << '}';
2009                         }
2010                 }
2011
2012                 else if (t.cs() == "bibliographystyle")
2013                         h_biblio_style = p.verbatim_item();
2014
2015                 else if (t.cs() == "jurabibsetup") {
2016                         // FIXME p.getArg('{', '}') is most probably wrong (it
2017                         //       does not handle nested braces).
2018                         //       Use p.verbatim_item() instead.
2019                         vector<string> jurabibsetup =
2020                                 split_options(p.getArg('{', '}'));
2021                         // add jurabibsetup to the jurabib package options
2022                         add_package("jurabib", jurabibsetup);
2023                         if (!jurabibsetup.empty()) {
2024                                 h_preamble << "\\jurabibsetup{"
2025                                            << join(jurabibsetup, ",") << '}';
2026                         }
2027                 }
2028
2029                 else if (t.cs() == "hypersetup") {
2030                         vector<string> hypersetup =
2031                                 split_options(p.verbatim_item());
2032                         // add hypersetup to the hyperref package options
2033                         handle_hyperref(hypersetup);
2034                         if (!hypersetup.empty()) {
2035                                 h_preamble << "\\hypersetup{"
2036                                            << join(hypersetup, ",") << '}';
2037                         }
2038                 }
2039
2040                 else if (is_known(t.cs(), known_if_3arg_commands)) {
2041                         // prevent misparsing of \usepackage if it is used
2042                         // as an argument (see e.g. our own output of
2043                         // \@ifundefined above)
2044                         string const arg1 = p.verbatim_item();
2045                         string const arg2 = p.verbatim_item();
2046                         string const arg3 = p.verbatim_item();
2047                         // test case \@ifundefined{date}{}{\date{}}
2048                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
2049                             arg2.empty() && arg3 == "\\date{}") {
2050                                 h_suppress_date = "true";
2051                         // older tex2lyx versions did output
2052                         // \@ifundefined{definecolor}{\usepackage{color}}{}
2053                         } else if (t.cs() == "@ifundefined" &&
2054                                    arg1 == "definecolor" &&
2055                                    arg2 == "\\usepackage{color}" &&
2056                                    arg3.empty()) {
2057                                 if (!in_lyx_preamble)
2058                                         h_preamble << package_beg_sep
2059                                                    << "color"
2060                                                    << package_mid_sep
2061                                                    << "\\@ifundefined{definecolor}{color}{}"
2062                                                    << package_end_sep;
2063                         // test for case
2064                         //\@ifundefined{showcaptionsetup}{}{%
2065                         // \PassOptionsToPackage{caption=false}{subfig}}
2066                         // that LyX uses for subfloats
2067                         } else if (t.cs() == "@ifundefined" &&
2068                                    arg1 == "showcaptionsetup" && arg2.empty()
2069                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2070                                 ; // do nothing
2071                         } else if (!in_lyx_preamble) {
2072                                 h_preamble << t.asInput()
2073                                            << '{' << arg1 << '}'
2074                                            << '{' << arg2 << '}'
2075                                            << '{' << arg3 << '}';
2076                         }
2077                 }
2078
2079                 else if (is_known(t.cs(), known_if_commands)) {
2080                         // must not parse anything in conditional code, since
2081                         // LyX would output the parsed contents unconditionally
2082                         if (!in_lyx_preamble)
2083                                 h_preamble << t.asInput();
2084                         handle_if(p, in_lyx_preamble);
2085                 }
2086
2087                 else if (!t.cs().empty() && !in_lyx_preamble)
2088                         h_preamble << '\\' << t.cs();
2089         }
2090
2091         // remove the whitespace
2092         p.skip_spaces();
2093
2094         // Force textclass if the user wanted it
2095         if (!forceclass.empty())
2096                 h_textclass = forceclass;
2097         tc.setName(h_textclass);
2098         if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2099                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2100                 exit(EXIT_FAILURE);
2101         }
2102         if (h_papersides.empty()) {
2103                 ostringstream ss;
2104                 ss << tc.sides();
2105                 h_papersides = ss.str();
2106         }
2107
2108         // If the CJK package is used we cannot set the document language from
2109         // the babel options. Instead, we guess which language is used most
2110         // and set this one.
2111         default_language = h_language;
2112         if (is_full_document &&
2113             (auto_packages.find("CJK") != auto_packages.end() ||
2114              auto_packages.find("CJKutf8") != auto_packages.end())) {
2115                 p.pushPosition();
2116                 h_language = guessLanguage(p, default_language);
2117                 p.popPosition();
2118                 if (explicit_babel && h_language != default_language) {
2119                         // We set the document language to a CJK language,
2120                         // but babel is explicitly called in the user preamble
2121                         // without options. LyX will not add the default
2122                         // language to the document options if it is either
2123                         // english, or no text is set as default language.
2124                         // Therefore we need to add a language option explicitly.
2125                         // FIXME: It would be better to remove all babel calls
2126                         //        from the user preamble, but this is difficult
2127                         //        without re-introducing bug 7861.
2128                         if (h_options.empty())
2129                                 h_options = lyx2babel(default_language);
2130                         else
2131                                 h_options += ',' + lyx2babel(default_language);
2132                 }
2133         }
2134 }
2135
2136
2137 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2138 {
2139         TeX2LyXDocClass dummy;
2140         parse(p, forceclass, true, dummy);
2141         if (h_inputencoding != "auto" && h_inputencoding != "default")
2142                 return h_inputencoding;
2143         return "";
2144 }
2145
2146
2147 string babel2lyx(string const & language)
2148 {
2149         char const * const * where = is_known(language, known_languages);
2150         if (where)
2151                 return known_coded_languages[where - known_languages];
2152         return language;
2153 }
2154
2155
2156 string lyx2babel(string const & language)
2157 {
2158         char const * const * where = is_known(language, known_coded_languages);
2159         if (where)
2160                 return known_languages[where - known_coded_languages];
2161         return language;
2162 }
2163
2164
2165 string Preamble::polyglossia2lyx(string const & language)
2166 {
2167         char const * const * where = is_known(language, polyglossia_languages);
2168         if (where)
2169                 return coded_polyglossia_languages[where - polyglossia_languages];
2170         return language;
2171 }
2172
2173
2174 string rgbcolor2code(string const & name)
2175 {
2176         char const * const * where = is_known(name, known_basic_colors);
2177         if (where) {
2178                 // "red", "green" etc
2179                 return known_basic_color_codes[where - known_basic_colors];
2180         }
2181         // "255,0,0", "0,255,0" etc
2182         RGBColor c(RGBColorFromLaTeX(name));
2183         return X11hexname(c);
2184 }
2185
2186 // }])
2187
2188
2189 } // namespace lyx