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