]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
48dc8d69b61d2c3d2b3688987e576ebd55b6a2af
[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)
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 == "chapterbib")
1104                 h_multibib = "child";
1105
1106         else if (name == "hyperref")
1107                 handle_hyperref(options);
1108
1109         else if (name == "algorithm2e") {
1110                 // Load "algorithm2e" module
1111                 addModule("algorithm2e");
1112                 // Add the package options to the global document options
1113                 if (!options.empty()) {
1114                         if (h_options.empty())
1115                                 h_options = join(options, ",");
1116                         else
1117                                 h_options += ',' + join(options, ",");
1118                 }
1119         }
1120         else if (name == "microtype") {
1121                 //we internally support only microtype without params
1122                 if (options.empty())
1123                         h_use_microtype = "true";
1124                 else
1125                         h_preamble << "\\usepackage[" << opts << "]{microtype}";
1126         }
1127
1128         else if (!in_lyx_preamble) {
1129                 if (options.empty())
1130                         h_preamble << "\\usepackage{" << name << '}';
1131                 else {
1132                         h_preamble << "\\usepackage[" << opts << "]{"
1133                                    << name << '}';
1134                         options.clear();
1135                 }
1136                 if (p.next_token().cat() == catNewline ||
1137                     (p.next_token().cat() == catSpace &&
1138                      p.next_next_token().cat() == catNewline))
1139                         h_preamble << '\n';
1140         }
1141
1142         // We need to do something with the options...
1143         if (!options.empty() && !detectEncoding)
1144                 cerr << "Ignoring options '" << join(options, ",")
1145                      << "' of package " << name << '.' << endl;
1146
1147         // remove the whitespace
1148         p.skip_spaces();
1149 }
1150
1151
1152 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1153 {
1154         while (p.good()) {
1155                 Token t = p.get_token();
1156                 if (t.cat() == catEscape &&
1157                     is_known(t.cs(), known_if_commands))
1158                         handle_if(p, in_lyx_preamble);
1159                 else {
1160                         if (!in_lyx_preamble)
1161                                 h_preamble << t.asInput();
1162                         if (t.cat() == catEscape && t.cs() == "fi")
1163                                 return;
1164                 }
1165         }
1166 }
1167
1168
1169 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1170 {
1171         // set the quote language
1172         // LyX only knows the following quotes languages:
1173         // english, swedish, german, polish, french and danish
1174         // (quotes for "japanese" and "chinese-traditional" are missing because
1175         //  they wouldn't be useful: https://www.lyx.org/trac/ticket/6383)
1176         // conversion list taken from
1177         // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1178         // (quotes for kazakh and interlingua are unknown)
1179         // danish
1180         if (is_known(h_language, known_danish_quotes_languages))
1181                 h_quotes_style = "danish";
1182         // french
1183         else if (is_known(h_language, known_french_quotes_languages))
1184                 h_quotes_style = "french";
1185         // german
1186         else if (is_known(h_language, known_german_quotes_languages))
1187                 h_quotes_style = "german";
1188         // polish
1189         else if (is_known(h_language, known_polish_quotes_languages))
1190                 h_quotes_style = "polish";
1191         // swedish
1192         else if (is_known(h_language, known_swedish_quotes_languages))
1193                 h_quotes_style = "swedish";
1194         //english
1195         else if (is_known(h_language, known_english_quotes_languages))
1196                 h_quotes_style = "english";
1197
1198         if (contains(h_float_placement, "H"))
1199                 registerAutomaticallyLoadedPackage("float");
1200         if (h_spacing != "single" && h_spacing != "default")
1201                 registerAutomaticallyLoadedPackage("setspace");
1202         if (h_use_packages["amsmath"] == "2") {
1203                 // amsbsy and amstext are already provided by amsmath
1204                 registerAutomaticallyLoadedPackage("amsbsy");
1205                 registerAutomaticallyLoadedPackage("amstext");
1206         }
1207
1208         // output the LyX file settings
1209         // Important: Keep the version formatting in sync with LyX and
1210         //            lyx2lyx (bug 7951)
1211         string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1212         os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1213            << lyx_version_minor << '\n'
1214            << "\\lyxformat " << LYX_FORMAT << '\n'
1215            << "\\begin_document\n"
1216            << "\\begin_header\n"
1217            << "\\save_transient_properties " << h_save_transient_properties << "\n"
1218            << "\\origin " << origin << "\n"
1219            << "\\textclass " << h_textclass << "\n";
1220         string const raw = subdoc ? empty_string() : h_preamble.str();
1221         if (!raw.empty()) {
1222                 os << "\\begin_preamble\n";
1223                 for (string::size_type i = 0; i < raw.size(); ++i) {
1224                         if (raw[i] == package_beg_sep) {
1225                                 // Here follows some package loading code that
1226                                 // must be skipped if the package is loaded
1227                                 // automatically.
1228                                 string::size_type j = raw.find(package_mid_sep, i);
1229                                 if (j == string::npos)
1230                                         return false;
1231                                 string::size_type k = raw.find(package_end_sep, j);
1232                                 if (k == string::npos)
1233                                         return false;
1234                                 string const package = raw.substr(i + 1, j - i - 1);
1235                                 string const replacement = raw.substr(j + 1, k - j - 1);
1236                                 if (auto_packages.find(package) == auto_packages.end())
1237                                         os << replacement;
1238                                 i = k;
1239                         } else
1240                                 os.put(raw[i]);
1241                 }
1242                 os << "\n\\end_preamble\n";
1243         }
1244         if (!h_options.empty())
1245                 os << "\\options " << h_options << "\n";
1246         os << "\\use_default_options " << h_use_default_options << "\n";
1247         if (!used_modules.empty()) {
1248                 os << "\\begin_modules\n";
1249                 vector<string>::const_iterator const end = used_modules.end();
1250                 vector<string>::const_iterator it = used_modules.begin();
1251                 for (; it != end; ++it)
1252                         os << *it << '\n';
1253                 os << "\\end_modules\n";
1254         }
1255         os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1256            << "\\language " << h_language << "\n"
1257            << "\\language_package " << h_language_package << "\n"
1258            << "\\inputencoding " << h_inputencoding << "\n"
1259            << "\\fontencoding " << h_fontencoding << "\n"
1260            << "\\font_roman \"" << h_font_roman[0]
1261            << "\" \"" << h_font_roman[1] << "\"\n"
1262            << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1263            << "\\font_typewriter \"" << h_font_typewriter[0]
1264            << "\" \"" << h_font_typewriter[1] << "\"\n"
1265            << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1266            << "\\font_default_family " << h_font_default_family << "\n"
1267            << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1268            << "\\font_sc " << h_font_sc << "\n"
1269            << "\\font_osf " << h_font_osf << "\n"
1270            << "\\font_sf_scale " << h_font_sf_scale[0]
1271            << ' ' << h_font_sf_scale[1] << '\n'
1272            << "\\font_tt_scale " << h_font_tt_scale[0]
1273            << ' ' << h_font_tt_scale[1] << '\n';
1274         if (!h_font_cjk.empty())
1275                 os << "\\font_cjk " << h_font_cjk << '\n';
1276         os << "\\use_microtype " << h_use_microtype << '\n'
1277            << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1278            << "\\graphics " << h_graphics << '\n'
1279            << "\\default_output_format " << h_default_output_format << "\n"
1280            << "\\output_sync " << h_output_sync << "\n";
1281         if (h_output_sync == "1")
1282                 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1283         os << "\\bibtex_command " << h_bibtex_command << "\n"
1284            << "\\index_command " << h_index_command << "\n";
1285         if (!h_float_placement.empty())
1286                 os << "\\float_placement " << h_float_placement << "\n";
1287         os << "\\paperfontsize " << h_paperfontsize << "\n"
1288            << "\\spacing " << h_spacing << "\n"
1289            << "\\use_hyperref " << h_use_hyperref << '\n';
1290         if (h_use_hyperref == "true") {
1291                 if (!h_pdf_title.empty())
1292                         os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1293                 if (!h_pdf_author.empty())
1294                         os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1295                 if (!h_pdf_subject.empty())
1296                         os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1297                 if (!h_pdf_keywords.empty())
1298                         os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1299                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1300                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1301                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1302                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1303                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1304                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1305                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1306                       "\\pdf_backref " << h_pdf_backref << "\n"
1307                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1308                 if (!h_pdf_pagemode.empty())
1309                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1310                 if (!h_pdf_quoted_options.empty())
1311                         os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1312         }
1313         os << "\\papersize " << h_papersize << "\n"
1314            << "\\use_geometry " << h_use_geometry << '\n';
1315         for (map<string, string>::const_iterator it = h_use_packages.begin();
1316              it != h_use_packages.end(); ++it)
1317                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1318         os << "\\cite_engine " << h_cite_engine << '\n'
1319            << "\\cite_engine_type " << h_cite_engine_type << '\n'
1320            << "\\biblio_style " << h_biblio_style << "\n"
1321            << "\\use_bibtopic " << h_use_bibtopic << "\n";
1322         if (!h_biblio_options.empty())
1323                 os << "\\biblio_options " << h_biblio_options << "\n";
1324         if (!h_biblatex_bibstyle.empty())
1325                 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1326         if (!h_biblatex_citestyle.empty())
1327                 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1328         if (!h_multibib.empty())
1329                 os << "\\multibib " << h_multibib << "\n";
1330         os << "\\use_indices " << h_use_indices << "\n"
1331            << "\\paperorientation " << h_paperorientation << '\n'
1332            << "\\suppress_date " << h_suppress_date << '\n'
1333            << "\\justification " << h_justification << '\n'
1334            << "\\use_refstyle " << h_use_refstyle << '\n'
1335            << "\\use_minted " << h_use_minted << '\n';
1336         if (!h_fontcolor.empty())
1337                 os << "\\fontcolor " << h_fontcolor << '\n';
1338         if (!h_notefontcolor.empty())
1339                 os << "\\notefontcolor " << h_notefontcolor << '\n';
1340         if (!h_backgroundcolor.empty())
1341                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1342         if (!h_boxbgcolor.empty())
1343                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1344         if (index_number != 0)
1345                 for (int i = 0; i < index_number; i++) {
1346                         os << "\\index " << h_index[i] << '\n'
1347                            << "\\shortcut " << h_shortcut[i] << '\n'
1348                            << "\\color " << h_color << '\n'
1349                            << "\\end_index\n";
1350                 }
1351         else {
1352                 os << "\\index " << h_index[0] << '\n'
1353                    << "\\shortcut " << h_shortcut[0] << '\n'
1354                    << "\\color " << h_color << '\n'
1355                    << "\\end_index\n";
1356         }
1357         os << h_margins
1358            << "\\secnumdepth " << h_secnumdepth << "\n"
1359            << "\\tocdepth " << h_tocdepth << "\n"
1360            << "\\paragraph_separation " << h_paragraph_separation << "\n";
1361         if (h_paragraph_separation == "skip")
1362                 os << "\\defskip " << h_defskip << "\n";
1363         else
1364                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1365         os << "\\is_math_indent " << h_is_mathindent << "\n";
1366         if (!h_mathindentation.empty())
1367                 os << "\\math_indentation " << h_mathindentation << "\n";
1368         os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1369         os << "\\quotes_style " << h_quotes_style << "\n"
1370            << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1371            << "\\papercolumns " << h_papercolumns << "\n"
1372            << "\\papersides " << h_papersides << "\n"
1373            << "\\paperpagestyle " << h_paperpagestyle << "\n";
1374         if (!h_listings_params.empty())
1375                 os << "\\listings_params " << h_listings_params << "\n";
1376         os << "\\tracking_changes " << h_tracking_changes << "\n"
1377            << "\\output_changes " << h_output_changes << "\n"
1378            << "\\html_math_output " << h_html_math_output << "\n"
1379            << "\\html_css_as_file " << h_html_css_as_file << "\n"
1380            << "\\html_be_strict " << h_html_be_strict << "\n"
1381            << authors_
1382            << "\\end_header\n\n"
1383            << "\\begin_body\n";
1384         return true;
1385 }
1386
1387
1388 void Preamble::parse(Parser & p, string const & forceclass,
1389                      TeX2LyXDocClass & tc)
1390 {
1391         // initialize fixed types
1392         special_columns_['D'] = 3;
1393         parse(p, forceclass, false, tc);
1394 }
1395
1396
1397 void Preamble::parse(Parser & p, string const & forceclass,
1398                      bool detectEncoding, TeX2LyXDocClass & tc)
1399 {
1400         bool is_full_document = false;
1401         bool is_lyx_file = false;
1402         bool in_lyx_preamble = false;
1403
1404         // determine whether this is a full document or a fragment for inclusion
1405         while (p.good()) {
1406                 Token const & t = p.get_token();
1407
1408                 if (t.cat() == catEscape && t.cs() == "documentclass") {
1409                         is_full_document = true;
1410                         break;
1411                 }
1412         }
1413         p.reset();
1414
1415         if (detectEncoding && !is_full_document)
1416                 return;
1417
1418         while (is_full_document && p.good()) {
1419                 if (detectEncoding && h_inputencoding != "auto" &&
1420                     h_inputencoding != "default")
1421                         return;
1422
1423                 Token const & t = p.get_token();
1424
1425 #ifdef FILEDEBUG
1426                 if (!detectEncoding)
1427                         cerr << "t: " << t << '\n';
1428 #endif
1429
1430                 //
1431                 // cat codes
1432                 //
1433                 if (!in_lyx_preamble &&
1434                     (t.cat() == catLetter ||
1435                      t.cat() == catSuper ||
1436                      t.cat() == catSub ||
1437                      t.cat() == catOther ||
1438                      t.cat() == catMath ||
1439                      t.cat() == catActive ||
1440                      t.cat() == catBegin ||
1441                      t.cat() == catEnd ||
1442                      t.cat() == catAlign ||
1443                      t.cat() == catParameter))
1444                         h_preamble << t.cs();
1445
1446                 else if (!in_lyx_preamble &&
1447                          (t.cat() == catSpace || t.cat() == catNewline))
1448                         h_preamble << t.asInput();
1449
1450                 else if (t.cat() == catComment) {
1451                         static regex const islyxfile("%% LyX .* created this file");
1452                         static regex const usercommands("User specified LaTeX commands");
1453
1454                         string const comment = t.asInput();
1455
1456                         // magically switch encoding default if it looks like XeLaTeX
1457                         static string const magicXeLaTeX =
1458                                 "% This document must be compiled with XeLaTeX ";
1459                         if (comment.size() > magicXeLaTeX.size()
1460                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1461                                   && h_inputencoding == "auto") {
1462                                 if (!detectEncoding)
1463                                         cerr << "XeLaTeX comment found, switching to UTF8\n";
1464                                 h_inputencoding = "utf8";
1465                         }
1466                         smatch sub;
1467                         if (regex_search(comment, sub, islyxfile)) {
1468                                 is_lyx_file = true;
1469                                 in_lyx_preamble = true;
1470                         } else if (is_lyx_file
1471                                    && regex_search(comment, sub, usercommands))
1472                                 in_lyx_preamble = false;
1473                         else if (!in_lyx_preamble)
1474                                 h_preamble << t.asInput();
1475                 }
1476
1477                 else if (t.cs() == "PassOptionsToPackage") {
1478                         string const poptions = p.getArg('{', '}');
1479                         string const package = p.verbatim_item();
1480                         extra_package_options_.insert(make_pair(package, poptions));
1481                 }
1482
1483                 else if (t.cs() == "pagestyle")
1484                         h_paperpagestyle = p.verbatim_item();
1485
1486                 else if (t.cs() == "setdefaultlanguage") {
1487                         xetex = true;
1488                         // We don't yet care about non-language variant options
1489                         // because LyX doesn't support this yet, see bug #8214
1490                         if (p.hasOpt()) {
1491                                 string langopts = p.getOpt();
1492                                 // check if the option contains a variant, if yes, extract it
1493                                 string::size_type pos_var = langopts.find("variant");
1494                                 string::size_type i = langopts.find(',', pos_var);
1495                                 string::size_type k = langopts.find('=', pos_var);
1496                                 if (pos_var != string::npos){
1497                                         string variant;
1498                                         if (i == string::npos)
1499                                                 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1500                                         else
1501                                                 variant = langopts.substr(k + 1, i - k - 1);
1502                                         h_language = variant;
1503                                 }
1504                                 p.verbatim_item();
1505                         } else
1506                                 h_language = p.verbatim_item();
1507                         //finally translate the poyglossia name to a LyX name
1508                         h_language = polyglossia2lyx(h_language);
1509                 }
1510
1511                 else if (t.cs() == "setotherlanguage") {
1512                         // We don't yet care about the option because LyX doesn't
1513                         // support this yet, see bug #8214
1514                         p.hasOpt() ? p.getOpt() : string();
1515                         p.verbatim_item();
1516                 }
1517
1518                 else if (t.cs() == "setmainfont") {
1519                         // we don't care about the option
1520                         p.hasOpt() ? p.getOpt() : string();
1521                         h_font_roman[1] = p.getArg('{', '}');
1522                 }
1523
1524                 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1525                         // LyX currently only supports the scale option
1526                         string scale;
1527                         if (p.hasOpt()) {
1528                                 string fontopts = p.getArg('[', ']');
1529                                 // check if the option contains a scaling, if yes, extract it
1530                                 string::size_type pos = fontopts.find("Scale");
1531                                 if (pos != string::npos) {
1532                                         string::size_type i = fontopts.find(',', pos);
1533                                         if (i == string::npos)
1534                                                 scale_as_percentage(fontopts.substr(pos + 1), scale);
1535                                         else
1536                                                 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1537                                 }
1538                         }
1539                         if (t.cs() == "setsansfont") {
1540                                 if (!scale.empty())
1541                                         h_font_sf_scale[1] = scale;
1542                                 h_font_sans[1] = p.getArg('{', '}');
1543                         } else {
1544                                 if (!scale.empty())
1545                                         h_font_tt_scale[1] = scale;
1546                                 h_font_typewriter[1] = p.getArg('{', '}');
1547                         }
1548                 }
1549
1550                 else if (t.cs() == "date") {
1551                         string argument = p.getArg('{', '}');
1552                         if (argument.empty())
1553                                 h_suppress_date = "true";
1554                         else
1555                                 h_preamble << t.asInput() << '{' << argument << '}';
1556                 }
1557
1558                 else if (t.cs() == "color") {
1559                         string const space =
1560                                 (p.hasOpt() ? p.getOpt() : string());
1561                         string argument = p.getArg('{', '}');
1562                         // check the case that a standard color is used
1563                         if (space.empty() && is_known(argument, known_basic_colors)) {
1564                                 h_fontcolor = rgbcolor2code(argument);
1565                                 registerAutomaticallyLoadedPackage("color");
1566                         } else if (space.empty() && argument == "document_fontcolor")
1567                                 registerAutomaticallyLoadedPackage("color");
1568                         // check the case that LyX's document_fontcolor is defined
1569                         // but not used for \color
1570                         else {
1571                                 h_preamble << t.asInput();
1572                                 if (!space.empty())
1573                                         h_preamble << space;
1574                                 h_preamble << '{' << argument << '}';
1575                                 // the color might already be set because \definecolor
1576                                 // is parsed before this
1577                                 h_fontcolor = "";
1578                         }
1579                 }
1580
1581                 else if (t.cs() == "pagecolor") {
1582                         string argument = p.getArg('{', '}');
1583                         // check the case that a standard color is used
1584                         if (is_known(argument, known_basic_colors)) {
1585                                 h_backgroundcolor = rgbcolor2code(argument);
1586                         } else if (argument == "page_backgroundcolor")
1587                                 registerAutomaticallyLoadedPackage("color");
1588                         // check the case that LyX's page_backgroundcolor is defined
1589                         // but not used for \pagecolor
1590                         else {
1591                                 h_preamble << t.asInput() << '{' << argument << '}';
1592                                 // the color might already be set because \definecolor
1593                                 // is parsed before this
1594                                 h_backgroundcolor = "";
1595                         }
1596                 }
1597
1598                 else if (t.cs() == "makeatletter") {
1599                         // LyX takes care of this
1600                         p.setCatcode('@', catLetter);
1601                 }
1602
1603                 else if (t.cs() == "makeatother") {
1604                         // LyX takes care of this
1605                         p.setCatcode('@', catOther);
1606                 }
1607
1608                 else if (t.cs() == "makeindex") {
1609                         // LyX will re-add this if a print index command is found
1610                         p.skip_spaces();
1611                 }
1612
1613                 else if (t.cs() == "newindex") {
1614                         string const indexname = p.getArg('[', ']');
1615                         string const shortcut = p.verbatim_item();
1616                         if (!indexname.empty())
1617                                 h_index[index_number] = indexname;
1618                         else
1619                                 h_index[index_number] = shortcut;
1620                         h_shortcut[index_number] = shortcut;
1621                         index_number += 1;
1622                         p.skip_spaces();
1623                 }
1624
1625                 else if (t.cs() == "addbibresource")
1626                         biblatex_bibliographies.push_back(removeExtension(p.getArg('{', '}')));
1627
1628                 else if (t.cs() == "bibliography") {
1629                         vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1630                         biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1631                 }
1632
1633                 else if (t.cs() == "RS@ifundefined") {
1634                         string const name = p.verbatim_item();
1635                         string const body1 = p.verbatim_item();
1636                         string const body2 = p.verbatim_item();
1637                         // only non-lyxspecific stuff
1638                         if (in_lyx_preamble &&
1639                             (name == "subsecref" || name == "thmref" || name == "lemref"))
1640                                 p.skip_spaces();
1641                         else {
1642                                 ostringstream ss;
1643                                 ss << '\\' << t.cs();
1644                                 ss << '{' << name << '}'
1645                                    << '{' << body1 << '}'
1646                                    << '{' << body2 << '}';
1647                                 h_preamble << ss.str();
1648                         }
1649                 }
1650
1651                 else if (t.cs() == "AtBeginDocument") {
1652                         string const name = p.verbatim_item();
1653                         // only non-lyxspecific stuff
1654                         if (in_lyx_preamble &&
1655                             (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1656                                 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1657                                 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1658                                 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1659                                 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1660                                 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1661                                 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1662                                 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1663                                 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1664                                 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1665                                 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1666                                 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1667                                 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1668                                 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1669                                 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1670                                 p.skip_spaces();
1671                         else {
1672                                 ostringstream ss;
1673                                 ss << '\\' << t.cs();
1674                                 ss << '{' << name << '}';
1675                                 h_preamble << ss.str();
1676                         }
1677                 }
1678
1679                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1680                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1681                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
1682                                 || t.cs() == "DeclareRobustCommand"
1683                       || t.cs() == "DeclareRobustCommandx"
1684                                 || t.cs() == "ProvideTextCommandDefault"
1685                                 || t.cs() == "DeclareMathAccent") {
1686                         bool star = false;
1687                         if (p.next_token().character() == '*') {
1688                                 p.get_token();
1689                                 star = true;
1690                         }
1691                         string const name = p.verbatim_item();
1692                         string const opt1 = p.getFullOpt();
1693                         string const opt2 = p.getFullOpt();
1694                         string const body = p.verbatim_item();
1695                         // store the in_lyx_preamble setting
1696                         bool const was_in_lyx_preamble = in_lyx_preamble;
1697                         // font settings
1698                         if (name == "\\rmdefault")
1699                                 if (is_known(body, known_roman_fonts)) {
1700                                         h_font_roman[0] = body;
1701                                         p.skip_spaces();
1702                                         in_lyx_preamble = true;
1703                                 }
1704                         if (name == "\\sfdefault")
1705                                 if (is_known(body, known_sans_fonts)) {
1706                                         h_font_sans[0] = body;
1707                                         p.skip_spaces();
1708                                         in_lyx_preamble = true;
1709                                 }
1710                         if (name == "\\ttdefault")
1711                                 if (is_known(body, known_typewriter_fonts)) {
1712                                         h_font_typewriter[0] = body;
1713                                         p.skip_spaces();
1714                                         in_lyx_preamble = true;
1715                                 }
1716                         if (name == "\\familydefault") {
1717                                 string family = body;
1718                                 // remove leading "\"
1719                                 h_font_default_family = family.erase(0,1);
1720                                 p.skip_spaces();
1721                                 in_lyx_preamble = true;
1722                         }
1723
1724                         // remove LyX-specific definitions that are re-added by LyX
1725                         // if necessary
1726                         // \lyxline is an ancient command that is converted by tex2lyx into
1727                         // a \rule therefore remove its preamble code
1728                         if (name == "\\lyxdot" || name == "\\lyxarrow"
1729                             || name == "\\lyxline" || name == "\\LyX") {
1730                                 p.skip_spaces();
1731                                 in_lyx_preamble = true;
1732                         }
1733
1734                         // Add the command to the known commands
1735                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1736
1737                         // only non-lyxspecific stuff
1738                         if (!in_lyx_preamble) {
1739                                 ostringstream ss;
1740                                 ss << '\\' << t.cs();
1741                                 if (star)
1742                                         ss << '*';
1743                                 ss << '{' << name << '}' << opt1 << opt2
1744                                    << '{' << body << "}";
1745                                 h_preamble << ss.str();
1746 /*
1747                                 ostream & out = in_preamble ? h_preamble : os;
1748                                 out << "\\" << t.cs() << "{" << name << "}"
1749                                     << opts << "{" << body << "}";
1750 */
1751                         }
1752                         // restore the in_lyx_preamble setting
1753                         in_lyx_preamble = was_in_lyx_preamble;
1754                 }
1755
1756                 else if (t.cs() == "documentclass") {
1757                         vector<string>::iterator it;
1758                         vector<string> opts = split_options(p.getArg('[', ']'));
1759                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1760                         delete_opt(opts, known_fontsizes);
1761                         // delete "pt" at the end
1762                         string::size_type i = h_paperfontsize.find("pt");
1763                         if (i != string::npos)
1764                                 h_paperfontsize.erase(i);
1765                         // The documentclass options are always parsed before the options
1766                         // of the babel call so that a language cannot overwrite the babel
1767                         // options.
1768                         handle_opt(opts, known_languages, h_language);
1769                         delete_opt(opts, known_languages);
1770
1771                         // math indentation
1772                         if ((it = find(opts.begin(), opts.end(), "fleqn"))
1773                                  != opts.end()) {
1774                                 h_is_mathindent = "1";
1775                                 opts.erase(it);
1776                         }
1777                         // formula numbering side
1778                         if ((it = find(opts.begin(), opts.end(), "leqno"))
1779                                  != opts.end()) {
1780                                 h_math_numbering_side = "left";
1781                                 opts.erase(it);
1782                         }
1783                         else if ((it = find(opts.begin(), opts.end(), "reqno"))
1784                                  != opts.end()) {
1785                                 h_math_numbering_side = "right";
1786                                 opts.erase(it);
1787                         }
1788
1789                         // paper orientation
1790                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1791                                 h_paperorientation = "landscape";
1792                                 opts.erase(it);
1793                         }
1794                         // paper sides
1795                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1796                                  != opts.end()) {
1797                                 h_papersides = "1";
1798                                 opts.erase(it);
1799                         }
1800                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1801                                  != opts.end()) {
1802                                 h_papersides = "2";
1803                                 opts.erase(it);
1804                         }
1805                         // paper columns
1806                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1807                                  != opts.end()) {
1808                                 h_papercolumns = "1";
1809                                 opts.erase(it);
1810                         }
1811                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1812                                  != opts.end()) {
1813                                 h_papercolumns = "2";
1814                                 opts.erase(it);
1815                         }
1816                         // paper sizes
1817                         // some size options are known to any document classes, other sizes
1818                         // are handled by the \geometry command of the geometry package
1819                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1820                         delete_opt(opts, known_class_paper_sizes);
1821                         // the remaining options
1822                         h_options = join(opts, ",");
1823                         // FIXME This does not work for classes that have a
1824                         //       different name in LyX than in LaTeX
1825                         h_textclass = p.getArg('{', '}');
1826                         p.skip_spaces();
1827                 }
1828
1829                 else if (t.cs() == "usepackage") {
1830                         string const options = p.getArg('[', ']');
1831                         string const name = p.getArg('{', '}');
1832                         vector<string> vecnames;
1833                         split(name, vecnames, ',');
1834                         vector<string>::const_iterator it  = vecnames.begin();
1835                         vector<string>::const_iterator end = vecnames.end();
1836                         for (; it != end; ++it)
1837                                 handle_package(p, trimSpaceAndEol(*it), options,
1838                                                in_lyx_preamble, detectEncoding);
1839                 }
1840
1841                 else if (t.cs() == "inputencoding") {
1842                         string const encoding = p.getArg('{','}');
1843                         Encoding const * const enc = encodings.fromLaTeXName(
1844                                 encoding, Encoding::inputenc, true);
1845                         if (!enc) {
1846                                 if (!detectEncoding)
1847                                         cerr << "Unknown encoding " << encoding
1848                                              << ". Ignoring." << std::endl;
1849                         } else {
1850                                 if (!enc->unsafe())
1851                                         h_inputencoding = enc->name();
1852                                 p.setEncoding(enc->iconvName());
1853                         }
1854                 }
1855
1856                 else if (t.cs() == "newenvironment") {
1857                         string const name = p.getArg('{', '}');
1858                         string const opt1 = p.getFullOpt();
1859                         string const opt2 = p.getFullOpt();
1860                         string const beg = p.verbatim_item();
1861                         string const end = p.verbatim_item();
1862                         if (!in_lyx_preamble) {
1863                                 h_preamble << "\\newenvironment{" << name
1864                                            << '}' << opt1 << opt2 << '{'
1865                                            << beg << "}{" << end << '}';
1866                         }
1867                         add_known_environment(name, opt1, !opt2.empty(),
1868                                               from_utf8(beg), from_utf8(end));
1869
1870                 }
1871
1872                 else if (t.cs() == "newtheorem") {
1873                         bool star = false;
1874                         if (p.next_token().character() == '*') {
1875                                 p.get_token();
1876                                 star = true;
1877                         }
1878                         string const name = p.getArg('{', '}');
1879                         string const opt1 = p.getFullOpt();
1880                         string const opt2 = p.getFullOpt();
1881                         string const body = p.verbatim_item();
1882                         string const opt3 = p.getFullOpt();
1883                         string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
1884
1885                         string const complete = cmd + "{" + name + '}' +
1886                                           opt1 + opt2 + '{' + body + '}' + opt3;
1887
1888                         add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1889
1890                         if (!in_lyx_preamble)
1891                                 h_preamble << complete;
1892                 }
1893
1894                 else if (t.cs() == "def") {
1895                         string name = p.get_token().cs();
1896                         // In fact, name may be more than the name:
1897                         // In the test case of bug 8116
1898                         // name == "csname SF@gobble@opt \endcsname".
1899                         // Therefore, we need to use asInput() instead of cs().
1900                         while (p.next_token().cat() != catBegin)
1901                                 name += p.get_token().asInput();
1902                         if (!in_lyx_preamble)
1903                                 h_preamble << "\\def\\" << name << '{'
1904                                            << p.verbatim_item() << "}";
1905                 }
1906
1907                 else if (t.cs() == "newcolumntype") {
1908                         string const name = p.getArg('{', '}');
1909                         trimSpaceAndEol(name);
1910                         int nargs = 0;
1911                         string opts = p.getOpt();
1912                         if (!opts.empty()) {
1913                                 istringstream is(string(opts, 1));
1914                                 is >> nargs;
1915                         }
1916                         special_columns_[name[0]] = nargs;
1917                         h_preamble << "\\newcolumntype{" << name << "}";
1918                         if (nargs)
1919                                 h_preamble << "[" << nargs << "]";
1920                         h_preamble << "{" << p.verbatim_item() << "}";
1921                 }
1922
1923                 else if (t.cs() == "setcounter") {
1924                         string const name = p.getArg('{', '}');
1925                         string const content = p.getArg('{', '}');
1926                         if (name == "secnumdepth")
1927                                 h_secnumdepth = content;
1928                         else if (name == "tocdepth")
1929                                 h_tocdepth = content;
1930                         else
1931                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1932                 }
1933
1934                 else if (t.cs() == "setlength") {
1935                         string const name = p.verbatim_item();
1936                         string const content = p.verbatim_item();
1937                         // the paragraphs are only not indented when \parindent is set to zero
1938                         if (name == "\\parindent" && content != "") {
1939                                 if (content[0] == '0')
1940                                         h_paragraph_separation = "skip";
1941                                 else
1942                                         h_paragraph_indentation = translate_len(content);
1943                         } else if (name == "\\parskip") {
1944                                 if (content == "\\smallskipamount")
1945                                         h_defskip = "smallskip";
1946                                 else if (content == "\\medskipamount")
1947                                         h_defskip = "medskip";
1948                                 else if (content == "\\bigskipamount")
1949                                         h_defskip = "bigskip";
1950                                 else
1951                                         h_defskip = translate_len(content);
1952                         } else if (name == "\\mathindent") {
1953                                 h_mathindentation = translate_len(content);
1954                         } else
1955                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1956                 }
1957
1958                 else if (t.cs() == "onehalfspacing")
1959                         h_spacing = "onehalf";
1960
1961                 else if (t.cs() == "doublespacing")
1962                         h_spacing = "double";
1963
1964                 else if (t.cs() == "setstretch")
1965                         h_spacing = "other " + p.verbatim_item();
1966
1967                 else if (t.cs() == "synctex") {
1968                         // the scheme is \synctex=value
1969                         // where value can only be "1" or "-1"
1970                         h_output_sync = "1";
1971                         // there can be any character behind the value (e.g. a linebreak or a '\'
1972                         // therefore we extract it char by char
1973                         p.get_token();
1974                         string value = p.get_token().asInput();
1975                         if (value == "-")
1976                                 value += p.get_token().asInput();
1977                         h_output_sync_macro = "\\synctex=" + value;
1978                 }
1979
1980                 else if (t.cs() == "begin") {
1981                         string const name = p.getArg('{', '}');
1982                         if (name == "document")
1983                                 break;
1984                         h_preamble << "\\begin{" << name << "}";
1985                 }
1986
1987                 else if (t.cs() == "geometry") {
1988                         vector<string> opts = split_options(p.getArg('{', '}'));
1989                         handle_geometry(opts);
1990                 }
1991
1992                 else if (t.cs() == "definecolor") {
1993                         string const color = p.getArg('{', '}');
1994                         string const space = p.getArg('{', '}');
1995                         string const value = p.getArg('{', '}');
1996                         if (color == "document_fontcolor" && space == "rgb") {
1997                                 RGBColor c(RGBColorFromLaTeX(value));
1998                                 h_fontcolor = X11hexname(c);
1999                         } else if (color == "note_fontcolor" && space == "rgb") {
2000                                 RGBColor c(RGBColorFromLaTeX(value));
2001                                 h_notefontcolor = X11hexname(c);
2002                         } else if (color == "page_backgroundcolor" && space == "rgb") {
2003                                 RGBColor c(RGBColorFromLaTeX(value));
2004                                 h_backgroundcolor = X11hexname(c);
2005                         } else if (color == "shadecolor" && space == "rgb") {
2006                                 RGBColor c(RGBColorFromLaTeX(value));
2007                                 h_boxbgcolor = X11hexname(c);
2008                         } else {
2009                                 h_preamble << "\\definecolor{" << color
2010                                            << "}{" << space << "}{" << value
2011                                            << '}';
2012                         }
2013                 }
2014
2015                 else if (t.cs() == "bibliographystyle")
2016                         h_biblio_style = p.verbatim_item();
2017
2018                 else if (t.cs() == "jurabibsetup") {
2019                         // FIXME p.getArg('{', '}') is most probably wrong (it
2020                         //       does not handle nested braces).
2021                         //       Use p.verbatim_item() instead.
2022                         vector<string> jurabibsetup =
2023                                 split_options(p.getArg('{', '}'));
2024                         // add jurabibsetup to the jurabib package options
2025                         add_package("jurabib", jurabibsetup);
2026                         if (!jurabibsetup.empty()) {
2027                                 h_preamble << "\\jurabibsetup{"
2028                                            << join(jurabibsetup, ",") << '}';
2029                         }
2030                 }
2031
2032                 else if (t.cs() == "hypersetup") {
2033                         vector<string> hypersetup =
2034                                 split_options(p.verbatim_item());
2035                         // add hypersetup to the hyperref package options
2036                         handle_hyperref(hypersetup);
2037                         if (!hypersetup.empty()) {
2038                                 h_preamble << "\\hypersetup{"
2039                                            << join(hypersetup, ",") << '}';
2040                         }
2041                 }
2042
2043                 else if (is_known(t.cs(), known_if_3arg_commands)) {
2044                         // prevent misparsing of \usepackage if it is used
2045                         // as an argument (see e.g. our own output of
2046                         // \@ifundefined above)
2047                         string const arg1 = p.verbatim_item();
2048                         string const arg2 = p.verbatim_item();
2049                         string const arg3 = p.verbatim_item();
2050                         // test case \@ifundefined{date}{}{\date{}}
2051                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
2052                             arg2.empty() && arg3 == "\\date{}") {
2053                                 h_suppress_date = "true";
2054                         // older tex2lyx versions did output
2055                         // \@ifundefined{definecolor}{\usepackage{color}}{}
2056                         } else if (t.cs() == "@ifundefined" &&
2057                                    arg1 == "definecolor" &&
2058                                    arg2 == "\\usepackage{color}" &&
2059                                    arg3.empty()) {
2060                                 if (!in_lyx_preamble)
2061                                         h_preamble << package_beg_sep
2062                                                    << "color"
2063                                                    << package_mid_sep
2064                                                    << "\\@ifundefined{definecolor}{color}{}"
2065                                                    << package_end_sep;
2066                         // test for case
2067                         //\@ifundefined{showcaptionsetup}{}{%
2068                         // \PassOptionsToPackage{caption=false}{subfig}}
2069                         // that LyX uses for subfloats
2070                         } else if (t.cs() == "@ifundefined" &&
2071                                    arg1 == "showcaptionsetup" && arg2.empty()
2072                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2073                                 ; // do nothing
2074                         } else if (!in_lyx_preamble) {
2075                                 h_preamble << t.asInput()
2076                                            << '{' << arg1 << '}'
2077                                            << '{' << arg2 << '}'
2078                                            << '{' << arg3 << '}';
2079                         }
2080                 }
2081
2082                 else if (is_known(t.cs(), known_if_commands)) {
2083                         // must not parse anything in conditional code, since
2084                         // LyX would output the parsed contents unconditionally
2085                         if (!in_lyx_preamble)
2086                                 h_preamble << t.asInput();
2087                         handle_if(p, in_lyx_preamble);
2088                 }
2089
2090                 else if (!t.cs().empty() && !in_lyx_preamble)
2091                         h_preamble << '\\' << t.cs();
2092         }
2093
2094         // remove the whitespace
2095         p.skip_spaces();
2096
2097         // Force textclass if the user wanted it
2098         if (!forceclass.empty())
2099                 h_textclass = forceclass;
2100         tc.setName(h_textclass);
2101         if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2102                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2103                 exit(EXIT_FAILURE);
2104         }
2105         if (h_papersides.empty()) {
2106                 ostringstream ss;
2107                 ss << tc.sides();
2108                 h_papersides = ss.str();
2109         }
2110
2111         // If the CJK package is used we cannot set the document language from
2112         // the babel options. Instead, we guess which language is used most
2113         // and set this one.
2114         default_language = h_language;
2115         if (is_full_document &&
2116             (auto_packages.find("CJK") != auto_packages.end() ||
2117              auto_packages.find("CJKutf8") != auto_packages.end())) {
2118                 p.pushPosition();
2119                 h_language = guessLanguage(p, default_language);
2120                 p.popPosition();
2121                 if (explicit_babel && h_language != default_language) {
2122                         // We set the document language to a CJK language,
2123                         // but babel is explicitly called in the user preamble
2124                         // without options. LyX will not add the default
2125                         // language to the document options if it is either
2126                         // english, or no text is set as default language.
2127                         // Therefore we need to add a language option explicitly.
2128                         // FIXME: It would be better to remove all babel calls
2129                         //        from the user preamble, but this is difficult
2130                         //        without re-introducing bug 7861.
2131                         if (h_options.empty())
2132                                 h_options = lyx2babel(default_language);
2133                         else
2134                                 h_options += ',' + lyx2babel(default_language);
2135                 }
2136         }
2137 }
2138
2139
2140 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2141 {
2142         TeX2LyXDocClass dummy;
2143         parse(p, forceclass, true, dummy);
2144         if (h_inputencoding != "auto" && h_inputencoding != "default")
2145                 return h_inputencoding;
2146         return "";
2147 }
2148
2149
2150 string babel2lyx(string const & language)
2151 {
2152         char const * const * where = is_known(language, known_languages);
2153         if (where)
2154                 return known_coded_languages[where - known_languages];
2155         return language;
2156 }
2157
2158
2159 string lyx2babel(string const & language)
2160 {
2161         char const * const * where = is_known(language, known_coded_languages);
2162         if (where)
2163                 return known_languages[where - known_coded_languages];
2164         return language;
2165 }
2166
2167
2168 string Preamble::polyglossia2lyx(string const & language)
2169 {
2170         char const * const * where = is_known(language, polyglossia_languages);
2171         if (where)
2172                 return coded_polyglossia_languages[where - polyglossia_languages];
2173         return language;
2174 }
2175
2176
2177 string rgbcolor2code(string const & name)
2178 {
2179         char const * const * where = is_known(name, known_basic_colors);
2180         if (where) {
2181                 // "red", "green" etc
2182                 return known_basic_color_codes[where - known_basic_colors];
2183         }
2184         // "255,0,0", "0,255,0" etc
2185         RGBColor c(RGBColorFromLaTeX(name));
2186         return X11hexname(c);
2187 }
2188
2189 // }])
2190
2191
2192 } // namespace lyx