]> git.lyx.org Git - features.git/blob - src/tex2lyx/Preamble.cpp
Add support for \babelfont
[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_cjk
539         h_is_mathindent           = "0";
540         h_math_numbering_side     = "default";
541         h_graphics                = "default";
542         h_default_output_format   = "default";
543         h_html_be_strict          = "false";
544         h_html_css_as_file        = "0";
545         h_html_math_output        = "0";
546         h_index[0]                = "Index";
547         h_index_command           = "default";
548         h_inputencoding           = "auto-legacy";
549         h_justification           = "true";
550         h_language                = "english";
551         h_language_package        = "none";
552         //h_listings_params;
553         h_maintain_unincluded_children = "false";
554         //h_margins;
555         //h_notefontcolor;
556         //h_options;
557         h_output_changes          = "false";
558         h_output_sync             = "0";
559         //h_output_sync_macro
560         h_papercolumns            = "1";
561         h_paperfontsize           = "default";
562         h_paperorientation        = "portrait";
563         h_paperpagestyle          = "default";
564         //h_papersides;
565         h_papersize               = "default";
566         h_paragraph_indentation   = "default";
567         h_paragraph_separation    = "indent";
568         //h_pdf_title;
569         //h_pdf_author;
570         //h_pdf_subject;
571         //h_pdf_keywords;
572         h_pdf_bookmarks           = "0";
573         h_pdf_bookmarksnumbered   = "0";
574         h_pdf_bookmarksopen       = "0";
575         h_pdf_bookmarksopenlevel  = "1";
576         h_pdf_breaklinks          = "0";
577         h_pdf_pdfborder           = "0";
578         h_pdf_colorlinks          = "0";
579         h_pdf_backref             = "section";
580         h_pdf_pdfusetitle         = "0";
581         //h_pdf_pagemode;
582         //h_pdf_quoted_options;
583         h_quotes_style         = "english";
584         h_secnumdepth             = "3";
585         h_shortcut[0]             = "idx";
586         h_spacing                 = "single";
587         h_save_transient_properties = "true";
588         h_suppress_date           = "false";
589         h_textclass               = "article";
590         h_tocdepth                = "3";
591         h_tracking_changes        = "false";
592         h_use_bibtopic            = "false";
593         h_use_dash_ligatures      = "true";
594         h_use_indices             = "false";
595         h_use_geometry            = "false";
596         h_use_default_options     = "false";
597         h_use_hyperref            = "false";
598         h_use_microtype           = "false";
599         h_use_refstyle            = false;
600         h_use_minted              = false;
601         h_use_packages["amsmath"]    = "1";
602         h_use_packages["amssymb"]    = "0";
603         h_use_packages["cancel"]     = "0";
604         h_use_packages["esint"]      = "1";
605         h_use_packages["mhchem"]     = "0";
606         h_use_packages["mathdots"]   = "0";
607         h_use_packages["mathtools"]  = "0";
608         h_use_packages["stackrel"]   = "0";
609         h_use_packages["stmaryrd"]   = "0";
610         h_use_packages["undertilde"] = "0";
611 }
612
613
614 void Preamble::handle_hyperref(vector<string> & options)
615 {
616         // FIXME swallow inputencoding changes that might surround the
617         //       hyperref setup if it was written by LyX
618         h_use_hyperref = "true";
619         // swallow "unicode=true", since LyX does always write that
620         vector<string>::iterator it =
621                 find(options.begin(), options.end(), "unicode=true");
622         if (it != options.end())
623                 options.erase(it);
624         it = find(options.begin(), options.end(), "pdfusetitle");
625         if (it != options.end()) {
626                 h_pdf_pdfusetitle = "1";
627                 options.erase(it);
628         }
629         string bookmarks = process_keyval_opt(options, "bookmarks");
630         if (bookmarks == "true")
631                 h_pdf_bookmarks = "1";
632         else if (bookmarks == "false")
633                 h_pdf_bookmarks = "0";
634         if (h_pdf_bookmarks == "1") {
635                 string bookmarksnumbered =
636                         process_keyval_opt(options, "bookmarksnumbered");
637                 if (bookmarksnumbered == "true")
638                         h_pdf_bookmarksnumbered = "1";
639                 else if (bookmarksnumbered == "false")
640                         h_pdf_bookmarksnumbered = "0";
641                 string bookmarksopen =
642                         process_keyval_opt(options, "bookmarksopen");
643                 if (bookmarksopen == "true")
644                         h_pdf_bookmarksopen = "1";
645                 else if (bookmarksopen == "false")
646                         h_pdf_bookmarksopen = "0";
647                 if (h_pdf_bookmarksopen == "1") {
648                         string bookmarksopenlevel =
649                                 process_keyval_opt(options, "bookmarksopenlevel");
650                         if (!bookmarksopenlevel.empty())
651                                 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
652                 }
653         }
654         string breaklinks = process_keyval_opt(options, "breaklinks");
655         if (breaklinks == "true")
656                 h_pdf_breaklinks = "1";
657         else if (breaklinks == "false")
658                 h_pdf_breaklinks = "0";
659         string pdfborder = process_keyval_opt(options, "pdfborder");
660         if (pdfborder == "{0 0 0}")
661                 h_pdf_pdfborder = "1";
662         else if (pdfborder == "{0 0 1}")
663                 h_pdf_pdfborder = "0";
664         string backref = process_keyval_opt(options, "backref");
665         if (!backref.empty())
666                 h_pdf_backref = backref;
667         string colorlinks = process_keyval_opt(options, "colorlinks");
668         if (colorlinks == "true")
669                 h_pdf_colorlinks = "1";
670         else if (colorlinks == "false")
671                 h_pdf_colorlinks = "0";
672         string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
673         if (!pdfpagemode.empty())
674                 h_pdf_pagemode = pdfpagemode;
675         string pdftitle = process_keyval_opt(options, "pdftitle");
676         if (!pdftitle.empty()) {
677                 h_pdf_title = remove_braces(pdftitle);
678         }
679         string pdfauthor = process_keyval_opt(options, "pdfauthor");
680         if (!pdfauthor.empty()) {
681                 h_pdf_author = remove_braces(pdfauthor);
682         }
683         string pdfsubject = process_keyval_opt(options, "pdfsubject");
684         if (!pdfsubject.empty())
685                 h_pdf_subject = remove_braces(pdfsubject);
686         string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
687         if (!pdfkeywords.empty())
688                 h_pdf_keywords = remove_braces(pdfkeywords);
689         if (!options.empty()) {
690                 if (!h_pdf_quoted_options.empty())
691                         h_pdf_quoted_options += ',';
692                 h_pdf_quoted_options += join(options, ",");
693                 options.clear();
694         }
695 }
696
697
698 void Preamble::handle_geometry(vector<string> & options)
699 {
700         h_use_geometry = "true";
701         vector<string>::iterator it;
702         // paper orientation
703         if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
704                 h_paperorientation = "landscape";
705                 options.erase(it);
706         }
707         // paper size
708         // keyval version: "paper=letter"
709         string paper = process_keyval_opt(options, "paper");
710         if (!paper.empty())
711                 h_papersize = paper + "paper";
712         // alternative version: "letterpaper"
713         handle_opt(options, known_paper_sizes, h_papersize);
714         delete_opt(options, known_paper_sizes);
715         // page margins
716         char const * const * margin = known_paper_margins;
717         for (; *margin; ++margin) {
718                 string value = process_keyval_opt(options, *margin);
719                 if (!value.empty()) {
720                         int k = margin - known_paper_margins;
721                         string name = known_coded_paper_margins[k];
722                         h_margins += '\\' + name + ' ' + value + '\n';
723                 }
724         }
725 }
726
727
728 void Preamble::handle_package(Parser &p, string const & name,
729                               string const & opts, bool in_lyx_preamble,
730                               bool detectEncoding)
731 {
732         vector<string> options = split_options(opts);
733         add_package(name, options);
734
735         if (is_known(name, known_xetex_packages)) {
736                 xetex = true;
737                 h_use_non_tex_fonts = true;
738                 registerAutomaticallyLoadedPackage("fontspec");
739                 if (h_inputencoding == "auto-legacy")
740                         p.setEncoding("UTF-8");
741         }
742
743         // roman fonts
744         if (is_known(name, known_roman_font_packages))
745                 h_font_roman[0] = name;
746
747         if (name == "fourier") {
748                 h_font_roman[0] = "utopia";
749                 // when font uses real small capitals
750                 if (opts == "expert")
751                         h_font_sc = "true";
752         }
753
754         if (name == "garamondx") {
755                 h_font_roman[0] = "garamondx";
756                 if (opts == "osfI")
757                         h_font_osf = "true";
758         }
759
760         if (name == "libertine") {
761                 h_font_roman[0] = "libertine";
762                 // this automatically invokes biolinum
763                 h_font_sans[0] = "biolinum";
764                 // as well as libertineMono
765                 h_font_typewriter[0] = "libertine-mono";
766                 if (opts == "osf")
767                         h_font_osf = "true";
768                 else if (opts == "lining")
769                         h_font_osf = "false";
770         }
771
772         if (name == "libertineRoman" || name == "libertine-type1") {
773                 h_font_roman[0] = "libertine";
774                 // NOTE: contrary to libertine.sty, libertineRoman
775                 // and libertine-type1 do not automatically invoke
776                 // biolinum and libertineMono
777                 if (opts == "lining")
778                         h_font_osf = "false";
779                 else if (opts == "osf")
780                         h_font_osf = "true";
781         }
782
783         if (name == "MinionPro") {
784                 h_font_roman[0] = "minionpro";
785                 if (opts.find("lf") != string::npos)
786                         h_font_osf = "false";
787                 else
788                         h_font_osf = "true";
789                 if (opts.find("onlytext") != string::npos)
790                         h_font_math[0] = "default";
791                 else
792                         h_font_math[0] = "auto";
793         }
794
795         if (name == "mathdesign") {
796                 if (opts.find("charter") != string::npos)
797                         h_font_roman[0] = "md-charter";
798                 if (opts.find("garamond") != string::npos)
799                         h_font_roman[0] = "md-garamond";
800                 if (opts.find("utopia") != string::npos)
801                         h_font_roman[0] = "md-utopia";
802                 if (opts.find("expert") != string::npos) {
803                         h_font_sc = "true";
804                         h_font_osf = "true";
805                 }
806         }
807
808         else if (name == "mathpazo")
809                 h_font_roman[0] = "palatino";
810
811         else if (name == "mathptmx")
812                 h_font_roman[0] = "times";
813
814         if (name == "crimson")
815                 h_font_roman[0] = "cochineal";
816
817         if (name == "cochineal") {
818                 h_font_roman[0] = "cochineal";
819                 // cochineal can have several options, e.g. [proportional,osf]
820                 string::size_type pos = opts.find("osf");
821                 if (pos != string::npos)
822                         h_font_osf = "true";
823         }
824
825         if (name == "noto") {
826                 // noto can have several options
827                 if (opts.empty())
828                         h_font_roman[0] = "NotoSerif-TLF";
829                 string::size_type pos = opts.find("rm");
830                 if (pos != string::npos)
831                         h_font_roman[0] = "NotoSerif-TLF";
832                 pos = opts.find("sf");
833                 if (pos != string::npos)
834                         h_font_sans[0] = "NotoSans-TLF";
835                 pos = opts.find("nott");
836                 if (pos != string::npos) {
837                         h_font_roman[0] = "NotoSerif-TLF";
838                         h_font_sans[0] = "NotoSans-TLF";
839                 }
840                 // noto as typewriter is handled in handling of \ttdefault
841                 // special cases are handled in handling of \rmdefault and \sfdefault
842         }
843
844         if (name == "paratype") {
845                 // in this case all fonts are ParaType
846                 h_font_roman[0] = "PTSerif-TLF";
847                 h_font_sans[0] = "default";
848                 h_font_typewriter[0] = "default";
849         }
850
851         if (name == "PTSerif")
852                 h_font_roman[0] = "PTSerif-TLF";
853
854         if (name == "XCharter") {
855                 h_font_roman[0] = "xcharter";
856                 if (opts == "osf")
857                         h_font_osf = "true";
858         }
859
860         if (name == "plex-serif") {
861                 if (opts.empty())
862                         h_font_roman[0] = "IBMPlexSerif";
863                 else if (opts.find("thin") != string::npos)
864                         h_font_roman[0] = "IBMPlexSerifThin";
865                 else if (opts.find("extralight") != string::npos)
866                         h_font_roman[0] = "IBMPlexSerifExtraLight";
867                 else if (opts.find("light") != string::npos)
868                         h_font_roman[0] = "IBMPlexSerifLight";
869                 else if (opts.find("semibold") != string::npos)
870                         h_font_roman[0] = "IBMPlexSerifSemibold";
871         }
872         if (name == "noto-serif") {
873                 h_font_roman[0] = "NotoSerifRegular";
874                 if (!opts.empty()) {
875                         if (opts.find("thin") != string::npos)
876                                 h_font_roman[0] = "NotoSerifThin";
877                         else if (opts.find("medium") != string::npos)
878                                 h_font_roman[0] = "NotoSerifMedium";
879                         else if (opts.find("extralight") != string::npos)
880                                 h_font_roman[0] = "NotoSerifExtralight";
881                         else if (opts.find("light") != string::npos)
882                                 h_font_roman[0] = "NotoSerifLight";
883                 }
884         }
885
886         // sansserif fonts
887         if (is_known(name, known_sans_font_packages)) {
888                 h_font_sans[0] = name;
889                 if (options.size() >= 1) {
890                         if (scale_as_percentage(opts, h_font_sf_scale[0]))
891                                 options.clear();
892                 }
893         }
894
895         if (name == "biolinum" || name == "biolinum-type1") {
896                 h_font_sans[0] = "biolinum";
897                 // biolinum can have several options, e.g. [osf,scaled=0.97]
898                 string::size_type pos = opts.find("osf");
899                 if (pos != string::npos)
900                         h_font_osf = "true";
901         }
902
903         if (name == "PTSans") {
904                 h_font_sans[0] = "PTSans-TLF";
905                 if (options.size() >= 1) {
906                         if (scale_as_percentage(opts, h_font_sf_scale[0]))
907                                 options.clear();
908                 }
909         }
910
911         if (name == "plex-sans") {
912                 if (opts.find("condensed") != string::npos)
913                         h_font_sans[0] = "IBMPlexSansCondensed";
914                 else if (opts.find("thin") != string::npos)
915                         h_font_sans[0] = "IBMPlexSansThin";
916                 else if (opts.find("extralight") != string::npos)
917                         h_font_sans[0] = "IBMPlexSansExtraLight";
918                 else if (opts.find("light") != string::npos)
919                         h_font_sans[0] = "IBMPlexSansLight";
920                 else if (opts.find("semibold") != string::npos)
921                         h_font_sans[0] = "IBMPlexSansSemibold";
922                 else
923                         h_font_sans[0] = "IBMPlexSans";
924                 // check if the option contains scaling, if yes, extract it
925                 string::size_type pos = opts.find("scale");
926                 if (pos != string::npos) {
927                         string scale;
928                         string::size_type i = opts.find(',', pos);
929                         if (i == string::npos)
930                                 scale_as_percentage(opts.substr(pos + 1), scale);
931                         else
932                                 scale_as_percentage(opts.substr(pos, i - pos), scale);
933                         if (!scale.empty())
934                                 h_font_sf_scale[1] = scale;
935                 }
936         }
937         if (name == "noto-sans") {
938                 h_font_sans[0] = "NotoSansRegular";
939                 if (!opts.empty()) {
940                         if (opts.find("medium") != string::npos)
941                                 h_font_sans[0] = "NotoSansMedium";
942                         else if (opts.find("thin") != string::npos)
943                                 h_font_sans[0] = "NotoSansThin";
944                         else if (opts.find("extralight") != string::npos)
945                                 h_font_sans[0] = "NotoSansExtralight";
946                         else if (opts.find("light") != string::npos)
947                                 h_font_sans[0] = "NotoSansLight";
948                 }
949
950         }
951
952         // typewriter fonts
953         if (is_known(name, known_typewriter_font_packages)) {
954                 // fourier can be set as roman font _only_
955                 // fourier as typewriter is handled in handling of \ttdefault
956                 if (name != "fourier") {
957                         h_font_typewriter[0] = name;
958                         if (options.size() >= 1) {
959                                 if (scale_as_percentage(opts, h_font_tt_scale[0]))
960                                         options.clear();
961                         }
962                 }
963         }
964
965         if (name == "libertineMono" || name == "libertineMono-type1")
966                 h_font_typewriter[0] = "libertine-mono";
967
968         if (name == "PTMono") {
969                 h_font_typewriter[0] = "PTMono-TLF";
970                 if (options.size() >= 1) {
971                         if (scale_as_percentage(opts, h_font_tt_scale[0]))
972                                 options.clear();
973                 }
974         }
975
976         if (name == "plex-mono") {
977                 if (opts.find("thin") != string::npos)
978                         h_font_typewriter[0] = "IBMPlexMonoThin";
979                 else if (opts.find("extralight") != string::npos)
980                         h_font_typewriter[0] = "IBMPlexMonoExtraLight";
981                 else if (opts.find("light") != string::npos)
982                         h_font_typewriter[0] = "IBMPlexMonoLight";
983                 else if (opts.find("semibold") != string::npos)
984                         h_font_typewriter[0] = "IBMPlexMonoSemibold";
985                 else
986                         h_font_typewriter[0] = "IBMPlexMono";
987                 // check if the option contains scaling, if yes, extract it
988                 string::size_type pos = opts.find("scale");
989                 if (pos != string::npos) {
990                         string scale;
991                         string::size_type i = opts.find(',', pos);
992                         if (i == string::npos)
993                                 scale_as_percentage(opts.substr(pos + 1), scale);
994                         else
995                                 scale_as_percentage(opts.substr(pos, i - pos), scale);
996                         if (!scale.empty())
997                                 h_font_tt_scale[1] = scale;
998                 }
999         }
1000
1001         if (name == "noto-mono") {
1002                 h_font_typewriter[0] = "NotoMonoRegular";
1003         }
1004
1005         // font uses old-style figure
1006         if (name == "eco")
1007                 h_font_osf = "true";
1008
1009         // math fonts
1010         if (is_known(name, known_math_font_packages))
1011                 h_font_math[0] = name;
1012
1013         if (name == "newtxmath") {
1014                 if (opts.empty())
1015                         h_font_math[0] = "newtxmath";
1016                 else if (opts == "garamondx")
1017                         h_font_math[0] = "garamondx-ntxm";
1018                 else if (opts == "libertine")
1019                         h_font_math[0] = "libertine-ntxm";
1020                 else if (opts == "minion")
1021                         h_font_math[0] = "minion-ntxm";
1022                 else if (opts == "cochineal")
1023                         h_font_math[0] = "cochineal-ntxm";
1024         }
1025
1026         if (name == "iwona")
1027                 if (opts == "math")
1028                         h_font_math[0] = "iwona-math";
1029
1030         if (name == "kurier")
1031                 if (opts == "math")
1032                         h_font_math[0] = "kurier-math";
1033
1034         // after the detection and handling of special cases, we can remove the
1035         // fonts, otherwise they would appear in the preamble, see bug #7856
1036         if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages)
1037                 ||      is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages))
1038                 ;
1039         //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
1040         else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
1041                  name == "esint" || name == "mhchem" || name == "mathdots" ||
1042                  name == "mathtools" || name == "stackrel" ||
1043                  name == "stmaryrd" || name == "undertilde") {
1044                 h_use_packages[name] = "2";
1045                 registerAutomaticallyLoadedPackage(name);
1046         }
1047
1048         else if (name == "babel") {
1049                 h_language_package = "default";
1050                 // One might think we would have to do nothing if babel is loaded
1051                 // without any options to prevent pollution of the preamble with this
1052                 // babel call in every roundtrip.
1053                 // But the user could have defined babel-specific things afterwards. So
1054                 // we need to keep it in the preamble to prevent cases like bug #7861.
1055                 if (!opts.empty()) {
1056                         // check if more than one option was used - used later for inputenc
1057                         if (options.begin() != options.end() - 1)
1058                                 one_language = false;
1059                         // babel takes the last language of the option of its \usepackage
1060                         // call as document language. If there is no such language option, the
1061                         // last language in the documentclass options is used.
1062                         handle_opt(options, known_languages, h_language);
1063                         // translate the babel name to a LyX name
1064                         h_language = babel2lyx(h_language);
1065                         if (h_language == "japanese") {
1066                                 // For Japanese, the encoding isn't indicated in the source
1067                                 // file, and there's really not much we can do. We could
1068                                 // 1) offer a list of possible encodings to choose from, or
1069                                 // 2) determine the encoding of the file by inspecting it.
1070                                 // For the time being, we leave the encoding alone so that
1071                                 // we don't get iconv errors when making a wrong guess, and
1072                                 // we will output a note at the top of the document
1073                                 // explaining what to do.
1074                                 Encoding const * const enc = encodings.fromIconvName(
1075                                         p.getEncoding(), Encoding::japanese, false);
1076                                 if (enc)
1077                                         h_inputencoding = enc->name();
1078                                 is_nonCJKJapanese = true;
1079                                 // in this case babel can be removed from the preamble
1080                                 registerAutomaticallyLoadedPackage("babel");
1081                         } else {
1082                                 // If babel is called with options, LyX puts them by default into the
1083                                 // document class options. This works for most languages, except
1084                                 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
1085                                 // perhaps in future others.
1086                                 // Therefore keep the babel call as it is as the user might have
1087                                 // reasons for it.
1088                                 string const babelcall = "\\usepackage[" + opts + "]{babel}\n";
1089                                 if (!contains(h_preamble.str(), babelcall))
1090                                         h_preamble << babelcall;
1091                         }
1092                         delete_opt(options, known_languages);
1093                 } else {
1094                         if (!contains(h_preamble.str(), "\\usepackage{babel}\n"))
1095                                 h_preamble << "\\usepackage{babel}\n";
1096                         explicit_babel = true;
1097                 }
1098         }
1099
1100         else if (name == "polyglossia") {
1101                 h_language_package = "default";
1102                 h_default_output_format = "pdf4";
1103                 h_use_non_tex_fonts = true;
1104                 xetex = true;
1105                 registerAutomaticallyLoadedPackage("xunicode");
1106                 if (h_inputencoding == "auto-legacy")
1107                         p.setEncoding("UTF-8");
1108         }
1109
1110         else if (name == "CJK") {
1111                 // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling
1112                 // and this would not be correct for CJK
1113                 if (h_inputencoding == "auto-legacy-plain")
1114                         h_inputencoding = "auto-legacy";
1115                 registerAutomaticallyLoadedPackage("CJK");
1116         }
1117
1118         else if (name == "CJKutf8") {
1119                 h_inputencoding = "utf8-cjk";
1120                 p.setEncoding("UTF-8");
1121                 registerAutomaticallyLoadedPackage("CJKutf8");
1122         }
1123
1124         else if (name == "fontenc") {
1125                 h_fontencoding = getStringFromVector(options, ",");
1126                 options.clear();
1127         }
1128
1129         else if (name == "inputenc" || name == "luainputenc") {
1130                 // h_inputencoding is only set when there is not more than one
1131                 // inputenc option because otherwise h_inputencoding must be
1132                 // set to "auto-legacy" (the default encodings of the document's languages)
1133                 // Therefore check that exactly one option is passed to inputenc.
1134                 // It is also only set when there is not more than one babel
1135                 // language option.
1136                 if (!options.empty()) {
1137                         string const encoding = options.back();
1138                         Encoding const * const enc = encodings.fromLaTeXName(
1139                                 encoding, Encoding::inputenc, true);
1140                         if (!enc) {
1141                                 if (!detectEncoding)
1142                                         cerr << "Unknown encoding " << encoding
1143                                              << ". Ignoring." << std::endl;
1144                         } else {
1145                                 if (!enc->unsafe() && options.size() == 1 && one_language == true)
1146                                         h_inputencoding = enc->name();
1147                                 p.setEncoding(enc->iconvName());
1148                         }
1149                         options.clear();
1150                 }
1151         }
1152
1153         else if (name == "srcltx") {
1154                 h_output_sync = "1";
1155                 if (!opts.empty()) {
1156                         h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
1157                         options.clear();
1158                 } else
1159                         h_output_sync_macro = "\\usepackage{srcltx}";
1160         }
1161
1162         else if (is_known(name, known_old_language_packages)) {
1163                 // known language packages from the times before babel
1164                 // if they are found and not also babel, they will be used as
1165                 // custom language package
1166                 h_language_package = "\\usepackage{" + name + "}";
1167         }
1168
1169         else if (name == "lyxskak") {
1170                 // ignore this and its options
1171                 const char * const o[] = {"ps", "mover", 0};
1172                 delete_opt(options, o);
1173         }
1174
1175         else if (is_known(name, known_lyx_packages) && options.empty()) {
1176                 if (name == "splitidx")
1177                         h_use_indices = "true";
1178                 else if (name == "minted")
1179                         h_use_minted = true;
1180                 else if (name == "refstyle")
1181                         h_use_refstyle = true;
1182                 else if (name == "prettyref")
1183                         h_use_refstyle = false;
1184                 if (!in_lyx_preamble) {
1185                         h_preamble << package_beg_sep << name
1186                                    << package_mid_sep << "\\usepackage{"
1187                                    << name << '}';
1188                         if (p.next_token().cat() == catNewline ||
1189                             (p.next_token().cat() == catSpace &&
1190                              p.next_next_token().cat() == catNewline))
1191                                 h_preamble << '\n';
1192                         h_preamble << package_end_sep;
1193                 }
1194         }
1195
1196         else if (name == "geometry")
1197                 handle_geometry(options);
1198
1199         else if (name == "subfig")
1200                 ; // ignore this FIXME: Use the package separator mechanism instead
1201
1202         else if (char const * const * where = is_known(name, known_languages))
1203                 h_language = known_coded_languages[where - known_languages];
1204
1205         else if (name == "natbib") {
1206                 h_biblio_style = "plainnat";
1207                 h_cite_engine = "natbib";
1208                 h_cite_engine_type = "authoryear";
1209                 vector<string>::iterator it =
1210                         find(options.begin(), options.end(), "authoryear");
1211                 if (it != options.end())
1212                         options.erase(it);
1213                 else {
1214                         it = find(options.begin(), options.end(), "numbers");
1215                         if (it != options.end()) {
1216                                 h_cite_engine_type = "numerical";
1217                                 options.erase(it);
1218                         }
1219                 }
1220                 if (!options.empty())
1221                         h_biblio_options = join(options, ",");
1222         }
1223
1224         else if (name == "biblatex") {
1225                 h_biblio_style = "plainnat";
1226                 h_cite_engine = "biblatex";
1227                 h_cite_engine_type = "authoryear";
1228                 string opt;
1229                 vector<string>::iterator it =
1230                         find(options.begin(), options.end(), "natbib");
1231                 if (it != options.end()) {
1232                         options.erase(it);
1233                         h_cite_engine = "biblatex-natbib";
1234                 } else {
1235                         opt = process_keyval_opt(options, "natbib");
1236                         if (opt == "true")
1237                                 h_cite_engine = "biblatex-natbib";
1238                 }
1239                 opt = process_keyval_opt(options, "style");
1240                 if (!opt.empty()) {
1241                         h_biblatex_citestyle = opt;
1242                         h_biblatex_bibstyle = opt;
1243                 } else {
1244                         opt = process_keyval_opt(options, "citestyle");
1245                         if (!opt.empty())
1246                                 h_biblatex_citestyle = opt;
1247                         opt = process_keyval_opt(options, "bibstyle");
1248                         if (!opt.empty())
1249                                 h_biblatex_bibstyle = opt;
1250                 }
1251                 opt = process_keyval_opt(options, "refsection");
1252                 if (!opt.empty()) {
1253                         if (opt == "none" || opt == "part"
1254                             || opt == "chapter" || opt == "section"
1255                             || opt == "subsection")
1256                                 h_multibib = opt;
1257                         else
1258                                 cerr << "Ignoring unkown refesection value '"
1259                                      << opt << "'.";
1260                 }
1261                 opt = process_keyval_opt(options, "bibencoding");
1262                 if (!opt.empty())
1263                         bibencoding = opt;
1264                 if (!options.empty()) {
1265                         h_biblio_options = join(options, ",");
1266                         options.clear();
1267                 }
1268         }
1269
1270         else if (name == "jurabib") {
1271                 h_biblio_style = "jurabib";
1272                 h_cite_engine = "jurabib";
1273                 h_cite_engine_type = "authoryear";
1274                 if (!options.empty())
1275                         h_biblio_options = join(options, ",");
1276         }
1277
1278         else if (name == "bibtopic")
1279                 h_use_bibtopic = "true";
1280
1281         else if (name == "chapterbib")
1282                 h_multibib = "child";
1283
1284         else if (name == "hyperref")
1285                 handle_hyperref(options);
1286
1287         else if (name == "algorithm2e") {
1288                 // Load "algorithm2e" module
1289                 addModule("algorithm2e");
1290                 // Add the package options to the global document options
1291                 if (!options.empty()) {
1292                         if (h_options.empty())
1293                                 h_options = join(options, ",");
1294                         else
1295                                 h_options += ',' + join(options, ",");
1296                 }
1297         }
1298         else if (name == "microtype") {
1299                 //we internally support only microtype without params
1300                 if (options.empty())
1301                         h_use_microtype = "true";
1302                 else
1303                         h_preamble << "\\usepackage[" << opts << "]{microtype}";
1304         }
1305
1306         else if (!in_lyx_preamble) {
1307                 if (options.empty())
1308                         h_preamble << "\\usepackage{" << name << '}';
1309                 else {
1310                         h_preamble << "\\usepackage[" << opts << "]{"
1311                                    << name << '}';
1312                         options.clear();
1313                 }
1314                 if (p.next_token().cat() == catNewline ||
1315                     (p.next_token().cat() == catSpace &&
1316                      p.next_next_token().cat() == catNewline))
1317                         h_preamble << '\n';
1318         }
1319
1320         // We need to do something with the options...
1321         if (!options.empty() && !detectEncoding)
1322                 cerr << "Ignoring options '" << join(options, ",")
1323                      << "' of package " << name << '.' << endl;
1324
1325         // remove the whitespace
1326         p.skip_spaces();
1327 }
1328
1329
1330 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1331 {
1332         while (p.good()) {
1333                 Token t = p.get_token();
1334                 if (t.cat() == catEscape &&
1335                     is_known(t.cs(), known_if_commands))
1336                         handle_if(p, in_lyx_preamble);
1337                 else {
1338                         if (!in_lyx_preamble)
1339                                 h_preamble << t.asInput();
1340                         if (t.cat() == catEscape && t.cs() == "fi")
1341                                 return;
1342                 }
1343         }
1344 }
1345
1346
1347 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1348 {
1349         if (contains(h_float_placement, "H"))
1350                 registerAutomaticallyLoadedPackage("float");
1351         if (h_spacing != "single" && h_spacing != "default")
1352                 registerAutomaticallyLoadedPackage("setspace");
1353         if (h_use_packages["amsmath"] == "2") {
1354                 // amsbsy and amstext are already provided by amsmath
1355                 registerAutomaticallyLoadedPackage("amsbsy");
1356                 registerAutomaticallyLoadedPackage("amstext");
1357         }
1358
1359         // output the LyX file settings
1360         // Important: Keep the version formatting in sync with LyX and
1361         //            lyx2lyx (bug 7951)
1362         string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1363         os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1364            << lyx_version_minor << '\n'
1365            << "\\lyxformat " << LYX_FORMAT << '\n'
1366            << "\\begin_document\n"
1367            << "\\begin_header\n"
1368            << "\\save_transient_properties " << h_save_transient_properties << "\n"
1369            << "\\origin " << origin << "\n"
1370            << "\\textclass " << h_textclass << "\n";
1371         string const raw = subdoc ? empty_string() : h_preamble.str();
1372         if (!raw.empty()) {
1373                 os << "\\begin_preamble\n";
1374                 for (string::size_type i = 0; i < raw.size(); ++i) {
1375                         if (raw[i] == package_beg_sep) {
1376                                 // Here follows some package loading code that
1377                                 // must be skipped if the package is loaded
1378                                 // automatically.
1379                                 string::size_type j = raw.find(package_mid_sep, i);
1380                                 if (j == string::npos)
1381                                         return false;
1382                                 string::size_type k = raw.find(package_end_sep, j);
1383                                 if (k == string::npos)
1384                                         return false;
1385                                 string const package = raw.substr(i + 1, j - i - 1);
1386                                 string const replacement = raw.substr(j + 1, k - j - 1);
1387                                 if (auto_packages.find(package) == auto_packages.end())
1388                                         os << replacement;
1389                                 i = k;
1390                         } else
1391                                 os.put(raw[i]);
1392                 }
1393                 os << "\n\\end_preamble\n";
1394         }
1395         if (!h_options.empty())
1396                 os << "\\options " << h_options << "\n";
1397         os << "\\use_default_options " << h_use_default_options << "\n";
1398         if (!used_modules.empty()) {
1399                 os << "\\begin_modules\n";
1400                 vector<string>::const_iterator const end = used_modules.end();
1401                 vector<string>::const_iterator it = used_modules.begin();
1402                 for (; it != end; ++it)
1403                         os << *it << '\n';
1404                 os << "\\end_modules\n";
1405         }
1406         if (!h_includeonlys.empty()) {
1407                 os << "\\begin_includeonly\n";
1408                 for (auto const & iofile : h_includeonlys)
1409                         os << iofile << '\n';
1410                 os << "\\end_includeonly\n";
1411         }
1412         os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1413            << "\\language " << h_language << "\n"
1414            << "\\language_package " << h_language_package << "\n"
1415            << "\\inputencoding " << h_inputencoding << "\n"
1416            << "\\fontencoding " << h_fontencoding << "\n"
1417            << "\\font_roman \"" << h_font_roman[0]
1418            << "\" \"" << h_font_roman[1] << "\"\n"
1419            << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1420            << "\\font_typewriter \"" << h_font_typewriter[0]
1421            << "\" \"" << h_font_typewriter[1] << "\"\n"
1422            << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1423            << "\\font_default_family " << h_font_default_family << "\n"
1424            << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1425            << "\\font_sc " << h_font_sc << "\n"
1426            << "\\font_osf " << h_font_osf << "\n"
1427            << "\\font_sf_scale " << h_font_sf_scale[0]
1428            << ' ' << h_font_sf_scale[1] << '\n'
1429            << "\\font_tt_scale " << h_font_tt_scale[0]
1430            << ' ' << h_font_tt_scale[1] << '\n';
1431         if (!h_font_cjk.empty())
1432                 os << "\\font_cjk " << h_font_cjk << '\n';
1433         os << "\\use_microtype " << h_use_microtype << '\n'
1434            << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1435            << "\\graphics " << h_graphics << '\n'
1436            << "\\default_output_format " << h_default_output_format << "\n"
1437            << "\\output_sync " << h_output_sync << "\n";
1438         if (h_output_sync == "1")
1439                 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1440         os << "\\bibtex_command " << h_bibtex_command << "\n"
1441            << "\\index_command " << h_index_command << "\n";
1442         if (!h_float_placement.empty())
1443                 os << "\\float_placement " << h_float_placement << "\n";
1444         os << "\\paperfontsize " << h_paperfontsize << "\n"
1445            << "\\spacing " << h_spacing << "\n"
1446            << "\\use_hyperref " << h_use_hyperref << '\n';
1447         if (h_use_hyperref == "true") {
1448                 if (!h_pdf_title.empty())
1449                         os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1450                 if (!h_pdf_author.empty())
1451                         os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1452                 if (!h_pdf_subject.empty())
1453                         os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1454                 if (!h_pdf_keywords.empty())
1455                         os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1456                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1457                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1458                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1459                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1460                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1461                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1462                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1463                       "\\pdf_backref " << h_pdf_backref << "\n"
1464                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1465                 if (!h_pdf_pagemode.empty())
1466                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1467                 if (!h_pdf_quoted_options.empty())
1468                         os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1469         }
1470         os << "\\papersize " << h_papersize << "\n"
1471            << "\\use_geometry " << h_use_geometry << '\n';
1472         for (map<string, string>::const_iterator it = h_use_packages.begin();
1473              it != h_use_packages.end(); ++it)
1474                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1475         os << "\\cite_engine " << h_cite_engine << '\n'
1476            << "\\cite_engine_type " << h_cite_engine_type << '\n'
1477            << "\\biblio_style " << h_biblio_style << "\n"
1478            << "\\use_bibtopic " << h_use_bibtopic << "\n";
1479         if (!h_biblio_options.empty())
1480                 os << "\\biblio_options " << h_biblio_options << "\n";
1481         if (!h_biblatex_bibstyle.empty())
1482                 os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n";
1483         if (!h_biblatex_citestyle.empty())
1484                 os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n";
1485         if (!h_multibib.empty())
1486                 os << "\\multibib " << h_multibib << "\n";
1487         os << "\\use_indices " << h_use_indices << "\n"
1488            << "\\paperorientation " << h_paperorientation << '\n'
1489            << "\\suppress_date " << h_suppress_date << '\n'
1490            << "\\justification " << h_justification << '\n'
1491            << "\\use_refstyle " << h_use_refstyle << '\n'
1492            << "\\use_minted " << h_use_minted << '\n';
1493         if (!h_fontcolor.empty())
1494                 os << "\\fontcolor " << h_fontcolor << '\n';
1495         if (!h_notefontcolor.empty())
1496                 os << "\\notefontcolor " << h_notefontcolor << '\n';
1497         if (!h_backgroundcolor.empty())
1498                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1499         if (!h_boxbgcolor.empty())
1500                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1501         if (index_number != 0)
1502                 for (int i = 0; i < index_number; i++) {
1503                         os << "\\index " << h_index[i] << '\n'
1504                            << "\\shortcut " << h_shortcut[i] << '\n'
1505                            << "\\color " << h_color << '\n'
1506                            << "\\end_index\n";
1507                 }
1508         else {
1509                 os << "\\index " << h_index[0] << '\n'
1510                    << "\\shortcut " << h_shortcut[0] << '\n'
1511                    << "\\color " << h_color << '\n'
1512                    << "\\end_index\n";
1513         }
1514         os << h_margins
1515            << "\\secnumdepth " << h_secnumdepth << "\n"
1516            << "\\tocdepth " << h_tocdepth << "\n"
1517            << "\\paragraph_separation " << h_paragraph_separation << "\n";
1518         if (h_paragraph_separation == "skip")
1519                 os << "\\defskip " << h_defskip << "\n";
1520         else
1521                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1522         os << "\\is_math_indent " << h_is_mathindent << "\n";
1523         if (!h_mathindentation.empty())
1524                 os << "\\math_indentation " << h_mathindentation << "\n";
1525         os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1526         os << "\\quotes_style " << h_quotes_style << "\n"
1527            << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1528            << "\\papercolumns " << h_papercolumns << "\n"
1529            << "\\papersides " << h_papersides << "\n"
1530            << "\\paperpagestyle " << h_paperpagestyle << "\n";
1531         if (!h_listings_params.empty())
1532                 os << "\\listings_params " << h_listings_params << "\n";
1533         os << "\\tracking_changes " << h_tracking_changes << "\n"
1534            << "\\output_changes " << h_output_changes << "\n"
1535            << "\\html_math_output " << h_html_math_output << "\n"
1536            << "\\html_css_as_file " << h_html_css_as_file << "\n"
1537            << "\\html_be_strict " << h_html_be_strict << "\n"
1538            << authors_
1539            << "\\end_header\n\n"
1540            << "\\begin_body\n";
1541         return true;
1542 }
1543
1544
1545 void Preamble::parse(Parser & p, string const & forceclass,
1546                      TeX2LyXDocClass & tc)
1547 {
1548         // initialize fixed types
1549         special_columns_['D'] = 3;
1550         parse(p, forceclass, false, tc);
1551 }
1552
1553
1554 void Preamble::parse(Parser & p, string const & forceclass,
1555                      bool detectEncoding, TeX2LyXDocClass & tc)
1556 {
1557         bool is_full_document = false;
1558         bool is_lyx_file = false;
1559         bool in_lyx_preamble = false;
1560
1561         // determine whether this is a full document or a fragment for inclusion
1562         while (p.good()) {
1563                 Token const & t = p.get_token();
1564
1565                 if (t.cat() == catEscape && t.cs() == "documentclass") {
1566                         is_full_document = true;
1567                         break;
1568                 }
1569         }
1570         p.reset();
1571
1572         if (detectEncoding && !is_full_document)
1573                 return;
1574
1575         while (is_full_document && p.good()) {
1576                 if (detectEncoding && h_inputencoding != "auto-legacy" &&
1577                     h_inputencoding != "auto-legacy-plain")
1578                         return;
1579
1580                 Token const & t = p.get_token();
1581
1582 #ifdef FILEDEBUG
1583                 if (!detectEncoding)
1584                         cerr << "t: " << t << '\n';
1585 #endif
1586
1587                 //
1588                 // cat codes
1589                 //
1590                 if (!in_lyx_preamble &&
1591                     (t.cat() == catLetter ||
1592                      t.cat() == catSuper ||
1593                      t.cat() == catSub ||
1594                      t.cat() == catOther ||
1595                      t.cat() == catMath ||
1596                      t.cat() == catActive ||
1597                      t.cat() == catBegin ||
1598                      t.cat() == catEnd ||
1599                      t.cat() == catAlign ||
1600                      t.cat() == catParameter)) {
1601                         h_preamble << t.cs();
1602                         continue;
1603                 }
1604
1605                 if (!in_lyx_preamble &&
1606                     (t.cat() == catSpace || t.cat() == catNewline)) {
1607                         h_preamble << t.asInput();
1608                         continue;
1609                 }
1610
1611                 if (t.cat() == catComment) {
1612                         static regex const islyxfile("%% LyX .* created this file");
1613                         static regex const usercommands("User specified LaTeX commands");
1614
1615                         string const comment = t.asInput();
1616
1617                         // magically switch encoding default if it looks like XeLaTeX
1618                         static string const magicXeLaTeX =
1619                                 "% This document must be compiled with XeLaTeX ";
1620                         if (comment.size() > magicXeLaTeX.size()
1621                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1622                                   && h_inputencoding == "auto-legacy") {
1623                                 if (!detectEncoding)
1624                                         cerr << "XeLaTeX comment found, switching to UTF8\n";
1625                                 h_inputencoding = "utf8";
1626                         }
1627                         smatch sub;
1628                         if (regex_search(comment, sub, islyxfile)) {
1629                                 is_lyx_file = true;
1630                                 in_lyx_preamble = true;
1631                         } else if (is_lyx_file
1632                                    && regex_search(comment, sub, usercommands))
1633                                 in_lyx_preamble = false;
1634                         else if (!in_lyx_preamble)
1635                                 h_preamble << t.asInput();
1636                         continue;
1637                 }
1638
1639                 if (t.cs() == "PassOptionsToPackage") {
1640                         string const poptions = p.getArg('{', '}');
1641                         string const package = p.verbatim_item();
1642                         extra_package_options_.insert(make_pair(package, poptions));
1643                         continue;
1644                 }
1645
1646                 if (t.cs() == "pagestyle") {
1647                         h_paperpagestyle = p.verbatim_item();
1648                         continue;
1649                 }
1650
1651                 if (t.cs() == "setdefaultlanguage") {
1652                         xetex = true;
1653                         // We don't yet care about non-language variant options
1654                         // because LyX doesn't support this yet, see bug #8214
1655                         if (p.hasOpt()) {
1656                                 string langopts = p.getOpt();
1657                                 // check if the option contains a variant, if yes, extract it
1658                                 string::size_type pos_var = langopts.find("variant");
1659                                 string::size_type i = langopts.find(',', pos_var);
1660                                 string::size_type k = langopts.find('=', pos_var);
1661                                 if (pos_var != string::npos){
1662                                         string variant;
1663                                         if (i == string::npos)
1664                                                 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1665                                         else
1666                                                 variant = langopts.substr(k + 1, i - k - 1);
1667                                         h_language = variant;
1668                                 }
1669                                 p.verbatim_item();
1670                         } else
1671                                 h_language = p.verbatim_item();
1672                         //finally translate the poyglossia name to a LyX name
1673                         h_language = polyglossia2lyx(h_language);
1674                         continue;
1675                 }
1676
1677                 if (t.cs() == "setotherlanguage") {
1678                         // We don't yet care about the option because LyX doesn't
1679                         // support this yet, see bug #8214
1680                         p.hasOpt() ? p.getOpt() : string();
1681                         p.verbatim_item();
1682                         continue;
1683                 }
1684
1685                 if (t.cs() == "setmainfont") {
1686                         // we don't care about the option
1687                         p.hasOpt() ? p.getOpt() : string();
1688                         h_font_roman[1] = p.getArg('{', '}');
1689                         continue;
1690                 }
1691
1692                 if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1693                         // LyX currently only supports the scale option
1694                         string scale;
1695                         if (p.hasOpt()) {
1696                                 string fontopts = p.getArg('[', ']');
1697                                 // check if the option contains a scaling, if yes, extract it
1698                                 string::size_type pos = fontopts.find("Scale");
1699                                 if (pos != string::npos) {
1700                                         string::size_type i = fontopts.find(',', pos);
1701                                         if (i == string::npos)
1702                                                 scale_as_percentage(fontopts.substr(pos + 1), scale);
1703                                         else
1704                                                 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1705                                 }
1706                         }
1707                         if (t.cs() == "setsansfont") {
1708                                 if (!scale.empty())
1709                                         h_font_sf_scale[1] = scale;
1710                                 h_font_sans[1] = p.getArg('{', '}');
1711                         } else {
1712                                 if (!scale.empty())
1713                                         h_font_tt_scale[1] = scale;
1714                                 h_font_typewriter[1] = p.getArg('{', '}');
1715                         }
1716                         continue;
1717                 }
1718
1719                 if (t.cs() == "babelfont") {
1720                         xetex = true;
1721                         h_use_non_tex_fonts = true;
1722                         h_language_package = "babel";
1723                         if (h_inputencoding == "auto-legacy")
1724                         p.setEncoding("UTF-8");
1725                         // we don't care about the lang option
1726                         string const lang = p.hasOpt() ? p.getArg('[', ']') : string();
1727                         string const family = p.getArg('{', '}');
1728                         string const fontopts = p.hasOpt() ? p.getArg('[', ']') : string();
1729                         string const fontname = p.getArg('{', '}');
1730                         if (lang.empty() && family == "rm") {
1731                                 h_font_roman[1] = fontname;
1732                                 continue;
1733                         } else if (lang.empty() && (family == "sf" || family == "tt")) {
1734                                 // LyX currently only supports the scale option
1735                                 string scale;
1736                                 if (!fontopts.empty()) {
1737                                         // check if the option contains a scaling, if yes, extract it
1738                                         string::size_type pos = fontopts.find("Scale");
1739                                         if (pos != string::npos) {
1740                                                 string::size_type i = fontopts.find(',', pos);
1741                                                 if (i == string::npos)
1742                                                         scale_as_percentage(fontopts.substr(pos + 1), scale);
1743                                                 else
1744                                                         scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1745                                         }
1746                                 }
1747                                 if (family == "sf") {
1748                                         if (!scale.empty())
1749                                                 h_font_sf_scale[1] = scale;
1750                                         h_font_sans[1] = fontname;
1751                                 } else {
1752                                         if (!scale.empty())
1753                                                 h_font_tt_scale[1] = scale;
1754                                         h_font_typewriter[1] = fontname;
1755                                 }
1756                                 continue;
1757                         } else {
1758                                 // not rm, sf or tt or lang specific
1759                                 h_preamble << '\\' << t.cs();
1760                                 if (!lang.empty())
1761                                         h_preamble << '[' << lang << ']';
1762                                 h_preamble << '{' << family << '}';
1763                                 if (!fontopts.empty())
1764                                         h_preamble << '[' << fontopts << ']';
1765                                 h_preamble << '{' << fontname << '}' << '\n';
1766                                 continue;
1767                         }
1768                 }
1769
1770                 if (t.cs() == "date") {
1771                         string argument = p.getArg('{', '}');
1772                         if (argument.empty())
1773                                 h_suppress_date = "true";
1774                         else
1775                                 h_preamble << t.asInput() << '{' << argument << '}';
1776                         continue;
1777                 }
1778
1779                 if (t.cs() == "color") {
1780                         string const space =
1781                                 (p.hasOpt() ? p.getOpt() : string());
1782                         string argument = p.getArg('{', '}');
1783                         // check the case that a standard color is used
1784                         if (space.empty() && is_known(argument, known_basic_colors)) {
1785                                 h_fontcolor = rgbcolor2code(argument);
1786                                 registerAutomaticallyLoadedPackage("color");
1787                         } else if (space.empty() && argument == "document_fontcolor")
1788                                 registerAutomaticallyLoadedPackage("color");
1789                         // check the case that LyX's document_fontcolor is defined
1790                         // but not used for \color
1791                         else {
1792                                 h_preamble << t.asInput();
1793                                 if (!space.empty())
1794                                         h_preamble << space;
1795                                 h_preamble << '{' << argument << '}';
1796                                 // the color might already be set because \definecolor
1797                                 // is parsed before this
1798                                 h_fontcolor = "";
1799                         }
1800                         continue;
1801                 }
1802
1803                 if (t.cs() == "pagecolor") {
1804                         string argument = p.getArg('{', '}');
1805                         // check the case that a standard color is used
1806                         if (is_known(argument, known_basic_colors)) {
1807                                 h_backgroundcolor = rgbcolor2code(argument);
1808                         } else if (argument == "page_backgroundcolor")
1809                                 registerAutomaticallyLoadedPackage("color");
1810                         // check the case that LyX's page_backgroundcolor is defined
1811                         // but not used for \pagecolor
1812                         else {
1813                                 h_preamble << t.asInput() << '{' << argument << '}';
1814                                 // the color might already be set because \definecolor
1815                                 // is parsed before this
1816                                 h_backgroundcolor = "";
1817                         }
1818                         continue;
1819                 }
1820
1821                 if (t.cs() == "makeatletter") {
1822                         // LyX takes care of this
1823                         p.setCatcode('@', catLetter);
1824                         continue;
1825                 }
1826
1827                 if (t.cs() == "makeatother") {
1828                         // LyX takes care of this
1829                         p.setCatcode('@', catOther);
1830                         continue;
1831                 }
1832
1833                 if (t.cs() == "makeindex") {
1834                         // LyX will re-add this if a print index command is found
1835                         p.skip_spaces();
1836                         continue;
1837                 }
1838
1839                 if (t.cs() == "newindex") {
1840                         string const indexname = p.getArg('[', ']');
1841                         string const shortcut = p.verbatim_item();
1842                         if (!indexname.empty())
1843                                 h_index[index_number] = indexname;
1844                         else
1845                                 h_index[index_number] = shortcut;
1846                         h_shortcut[index_number] = shortcut;
1847                         index_number += 1;
1848                         p.skip_spaces();
1849                         continue;
1850                 }
1851
1852                 if (t.cs() == "addbibresource") {
1853                         string const options =  p.getArg('[', ']');
1854                         string const arg = removeExtension(p.getArg('{', '}'));
1855                         if (!options.empty()) {
1856                                 // check if the option contains a bibencoding, if yes, extract it
1857                                 string::size_type pos = options.find("bibencoding=");
1858                                 string encoding;
1859                                 if (pos != string::npos) {
1860                                         string::size_type i = options.find(',', pos);
1861                                         if (i == string::npos)
1862                                                 encoding = options.substr(pos + 1);
1863                                         else
1864                                                 encoding = options.substr(pos, i - pos);
1865                                         pos = encoding.find('=');
1866                                         if (pos == string::npos)
1867                                                 encoding.clear();
1868                                         else
1869                                                 encoding = encoding.substr(pos + 1);
1870                                 }
1871                                 if (!encoding.empty())
1872                                         biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
1873                         }
1874                         biblatex_bibliographies.push_back(arg);
1875                         continue;
1876                 }
1877
1878                 if (t.cs() == "bibliography") {
1879                         vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1880                         biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1881                         continue;
1882                 }
1883
1884                 if (t.cs() == "RS@ifundefined") {
1885                         string const name = p.verbatim_item();
1886                         string const body1 = p.verbatim_item();
1887                         string const body2 = p.verbatim_item();
1888                         // only non-lyxspecific stuff
1889                         if (in_lyx_preamble &&
1890                             (name == "subsecref" || name == "thmref" || name == "lemref"))
1891                                 p.skip_spaces();
1892                         else {
1893                                 ostringstream ss;
1894                                 ss << '\\' << t.cs();
1895                                 ss << '{' << name << '}'
1896                                    << '{' << body1 << '}'
1897                                    << '{' << body2 << '}';
1898                                 h_preamble << ss.str();
1899                         }
1900                         continue;
1901                 }
1902
1903                 if (t.cs() == "AtBeginDocument") {
1904                         string const name = p.verbatim_item();
1905                         // only non-lyxspecific stuff
1906                         if (in_lyx_preamble &&
1907                             (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1908                                 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1909                                 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1910                                 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1911                                 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1912                                 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1913                                 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1914                                 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1915                                 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1916                                 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1917                                 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1918                                 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1919                                 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1920                                 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1921                                 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1922                                 p.skip_spaces();
1923                         else {
1924                                 ostringstream ss;
1925                                 ss << '\\' << t.cs();
1926                                 ss << '{' << name << '}';
1927                                 h_preamble << ss.str();
1928                         }
1929                         continue;
1930                 }
1931
1932                 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1933                     || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1934                     || t.cs() == "providecommand" || t.cs() == "providecommandx"
1935                     || t.cs() == "DeclareRobustCommand"
1936                     || t.cs() == "DeclareRobustCommandx"
1937                     || t.cs() == "ProvideTextCommandDefault"
1938                     || t.cs() == "DeclareMathAccent") {
1939                         bool star = false;
1940                         if (p.next_token().character() == '*') {
1941                                 p.get_token();
1942                                 star = true;
1943                         }
1944                         string const name = p.verbatim_item();
1945                         string const opt1 = p.getFullOpt();
1946                         string const opt2 = p.getFullOpt();
1947                         string const body = p.verbatim_item();
1948                         // store the in_lyx_preamble setting
1949                         bool const was_in_lyx_preamble = in_lyx_preamble;
1950                         // font settings
1951                         if (name == "\\rmdefault")
1952                                 if (is_known(body, known_roman_font_packages)) {
1953                                         h_font_roman[0] = body;
1954                                         p.skip_spaces();
1955                                         in_lyx_preamble = true;
1956                                 }
1957                         if (name == "\\sfdefault")
1958                                 if (is_known(body, known_sans_font_packages)) {
1959                                         h_font_sans[0] = body;
1960                                         p.skip_spaces();
1961                                         in_lyx_preamble = true;
1962                                 }
1963                         if (name == "\\ttdefault")
1964                                 if (is_known(body, known_typewriter_font_packages)) {
1965                                         h_font_typewriter[0] = body;
1966                                         p.skip_spaces();
1967                                         in_lyx_preamble = true;
1968                                 }
1969                         if (name == "\\familydefault") {
1970                                 string family = body;
1971                                 // remove leading "\"
1972                                 h_font_default_family = family.erase(0,1);
1973                                 p.skip_spaces();
1974                                 in_lyx_preamble = true;
1975                         }
1976
1977                         // remove LyX-specific definitions that are re-added by LyX
1978                         // if necessary
1979                         // \lyxline is an ancient command that is converted by tex2lyx into
1980                         // a \rule therefore remove its preamble code
1981                         if (name == "\\lyxdot" || name == "\\lyxarrow"
1982                             || name == "\\lyxline" || name == "\\LyX") {
1983                                 p.skip_spaces();
1984                                 in_lyx_preamble = true;
1985                         }
1986
1987                         // Add the command to the known commands
1988                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1989
1990                         // only non-lyxspecific stuff
1991                         if (!in_lyx_preamble) {
1992                                 ostringstream ss;
1993                                 ss << '\\' << t.cs();
1994                                 if (star)
1995                                         ss << '*';
1996                                 ss << '{' << name << '}' << opt1 << opt2
1997                                    << '{' << body << "}";
1998                                 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
1999                                         h_preamble << ss.str();
2000 /*
2001                                 ostream & out = in_preamble ? h_preamble : os;
2002                                 out << "\\" << t.cs() << "{" << name << "}"
2003                                     << opts << "{" << body << "}";
2004 */
2005                         }
2006                         // restore the in_lyx_preamble setting
2007                         in_lyx_preamble = was_in_lyx_preamble;
2008                         continue;
2009                 }
2010
2011                 if (t.cs() == "documentclass") {
2012                         vector<string>::iterator it;
2013                         vector<string> opts = split_options(p.getArg('[', ']'));
2014                         handle_opt(opts, known_fontsizes, h_paperfontsize);
2015                         delete_opt(opts, known_fontsizes);
2016                         // delete "pt" at the end
2017                         string::size_type i = h_paperfontsize.find("pt");
2018                         if (i != string::npos)
2019                                 h_paperfontsize.erase(i);
2020                         // The documentclass options are always parsed before the options
2021                         // of the babel call so that a language cannot overwrite the babel
2022                         // options.
2023                         handle_opt(opts, known_languages, h_language);
2024                         delete_opt(opts, known_languages);
2025
2026                         // math indentation
2027                         if ((it = find(opts.begin(), opts.end(), "fleqn"))
2028                                  != opts.end()) {
2029                                 h_is_mathindent = "1";
2030                                 opts.erase(it);
2031                         }
2032                         // formula numbering side
2033                         if ((it = find(opts.begin(), opts.end(), "leqno"))
2034                                  != opts.end()) {
2035                                 h_math_numbering_side = "left";
2036                                 opts.erase(it);
2037                         }
2038                         else if ((it = find(opts.begin(), opts.end(), "reqno"))
2039                                  != opts.end()) {
2040                                 h_math_numbering_side = "right";
2041                                 opts.erase(it);
2042                         }
2043
2044                         // paper orientation
2045                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
2046                                 h_paperorientation = "landscape";
2047                                 opts.erase(it);
2048                         }
2049                         // paper sides
2050                         if ((it = find(opts.begin(), opts.end(), "oneside"))
2051                                  != opts.end()) {
2052                                 h_papersides = "1";
2053                                 opts.erase(it);
2054                         }
2055                         if ((it = find(opts.begin(), opts.end(), "twoside"))
2056                                  != opts.end()) {
2057                                 h_papersides = "2";
2058                                 opts.erase(it);
2059                         }
2060                         // paper columns
2061                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2062                                  != opts.end()) {
2063                                 h_papercolumns = "1";
2064                                 opts.erase(it);
2065                         }
2066                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2067                                  != opts.end()) {
2068                                 h_papercolumns = "2";
2069                                 opts.erase(it);
2070                         }
2071                         // paper sizes
2072                         // some size options are known to any document classes, other sizes
2073                         // are handled by the \geometry command of the geometry package
2074                         handle_opt(opts, known_class_paper_sizes, h_papersize);
2075                         delete_opt(opts, known_class_paper_sizes);
2076                         // the remaining options
2077                         h_options = join(opts, ",");
2078                         // FIXME This does not work for classes that have a
2079                         //       different name in LyX than in LaTeX
2080                         h_textclass = p.getArg('{', '}');
2081                         p.skip_spaces();
2082                         continue;
2083                 }
2084
2085                 if (t.cs() == "usepackage") {
2086                         string const options = p.getArg('[', ']');
2087                         string const name = p.getArg('{', '}');
2088                         vector<string> vecnames;
2089                         split(name, vecnames, ',');
2090                         vector<string>::const_iterator it  = vecnames.begin();
2091                         vector<string>::const_iterator end = vecnames.end();
2092                         for (; it != end; ++it)
2093                                 handle_package(p, trimSpaceAndEol(*it), options,
2094                                                in_lyx_preamble, detectEncoding);
2095                         continue;
2096                 }
2097
2098                 if (t.cs() == "inputencoding") {
2099                         string const encoding = p.getArg('{','}');
2100                         Encoding const * const enc = encodings.fromLaTeXName(
2101                                 encoding, Encoding::inputenc, true);
2102                         if (!enc) {
2103                                 if (!detectEncoding)
2104                                         cerr << "Unknown encoding " << encoding
2105                                              << ". Ignoring." << std::endl;
2106                         } else {
2107                                 if (!enc->unsafe())
2108                                         h_inputencoding = enc->name();
2109                                 p.setEncoding(enc->iconvName());
2110                         }
2111                         continue;
2112                 }
2113
2114                 if (t.cs() == "newenvironment") {
2115                         string const name = p.getArg('{', '}');
2116                         string const opt1 = p.getFullOpt();
2117                         string const opt2 = p.getFullOpt();
2118                         string const beg = p.verbatim_item();
2119                         string const end = p.verbatim_item();
2120                         if (!in_lyx_preamble) {
2121                                 h_preamble << "\\newenvironment{" << name
2122                                            << '}' << opt1 << opt2 << '{'
2123                                            << beg << "}{" << end << '}';
2124                         }
2125                         add_known_environment(name, opt1, !opt2.empty(),
2126                                               from_utf8(beg), from_utf8(end));
2127                         continue;
2128                 }
2129
2130                 if (t.cs() == "newtheorem") {
2131                         bool star = false;
2132                         if (p.next_token().character() == '*') {
2133                                 p.get_token();
2134                                 star = true;
2135                         }
2136                         string const name = p.getArg('{', '}');
2137                         string const opt1 = p.getFullOpt();
2138                         string const opt2 = p.getFullOpt();
2139                         string const body = p.verbatim_item();
2140                         string const opt3 = p.getFullOpt();
2141                         string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2142
2143                         string const complete = cmd + "{" + name + '}' +
2144                                           opt1 + opt2 + '{' + body + '}' + opt3;
2145
2146                         add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2147
2148                         if (!in_lyx_preamble)
2149                                 h_preamble << complete;
2150                         continue;
2151                 }
2152
2153                 if (t.cs() == "def") {
2154                         string name = p.get_token().cs();
2155                         // In fact, name may be more than the name:
2156                         // In the test case of bug 8116
2157                         // name == "csname SF@gobble@opt \endcsname".
2158                         // Therefore, we need to use asInput() instead of cs().
2159                         while (p.next_token().cat() != catBegin)
2160                                 name += p.get_token().asInput();
2161                         if (!in_lyx_preamble)
2162                                 h_preamble << "\\def\\" << name << '{'
2163                                            << p.verbatim_item() << "}";
2164                         continue;
2165                 }
2166
2167                 if (t.cs() == "newcolumntype") {
2168                         string const name = p.getArg('{', '}');
2169                         trimSpaceAndEol(name);
2170                         int nargs = 0;
2171                         string opts = p.getOpt();
2172                         if (!opts.empty()) {
2173                                 istringstream is(string(opts, 1));
2174                                 is >> nargs;
2175                         }
2176                         special_columns_[name[0]] = nargs;
2177                         h_preamble << "\\newcolumntype{" << name << "}";
2178                         if (nargs)
2179                                 h_preamble << "[" << nargs << "]";
2180                         h_preamble << "{" << p.verbatim_item() << "}";
2181                         continue;
2182                 }
2183
2184                 if (t.cs() == "setcounter") {
2185                         string const name = p.getArg('{', '}');
2186                         string const content = p.getArg('{', '}');
2187                         if (name == "secnumdepth")
2188                                 h_secnumdepth = content;
2189                         else if (name == "tocdepth")
2190                                 h_tocdepth = content;
2191                         else
2192                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2193                         continue;
2194                 }
2195
2196                 if (t.cs() == "setlength") {
2197                         string const name = p.verbatim_item();
2198                         string const content = p.verbatim_item();
2199                         // the paragraphs are only not indented when \parindent is set to zero
2200                         if (name == "\\parindent" && content != "") {
2201                                 if (content[0] == '0')
2202                                         h_paragraph_separation = "skip";
2203                                 else
2204                                         h_paragraph_indentation = translate_len(content);
2205                         } else if (name == "\\parskip") {
2206                                 if (content == "\\smallskipamount")
2207                                         h_defskip = "smallskip";
2208                                 else if (content == "\\medskipamount")
2209                                         h_defskip = "medskip";
2210                                 else if (content == "\\bigskipamount")
2211                                         h_defskip = "bigskip";
2212                                 else
2213                                         h_defskip = translate_len(content);
2214                         } else if (name == "\\mathindent") {
2215                                 h_mathindentation = translate_len(content);
2216                         } else
2217                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2218                         continue;
2219                 }
2220
2221                 if (t.cs() == "onehalfspacing") {
2222                         h_spacing = "onehalf";
2223                         continue;
2224                 }
2225
2226                 if (t.cs() == "doublespacing") {
2227                         h_spacing = "double";
2228                         continue;
2229                 }
2230
2231                 if (t.cs() == "setstretch") {
2232                         h_spacing = "other " + p.verbatim_item();
2233                         continue;
2234                 }
2235
2236                 if (t.cs() == "synctex") {
2237                         // the scheme is \synctex=value
2238                         // where value can only be "1" or "-1"
2239                         h_output_sync = "1";
2240                         // there can be any character behind the value (e.g. a linebreak or a '\'
2241                         // therefore we extract it char by char
2242                         p.get_token();
2243                         string value = p.get_token().asInput();
2244                         if (value == "-")
2245                                 value += p.get_token().asInput();
2246                         h_output_sync_macro = "\\synctex=" + value;
2247                         continue;
2248                 }
2249
2250                 if (t.cs() == "begin") {
2251                         string const name = p.getArg('{', '}');
2252                         if (name == "document")
2253                                 break;
2254                         h_preamble << "\\begin{" << name << "}";
2255                         continue;
2256                 }
2257
2258                 if (t.cs() == "geometry") {
2259                         vector<string> opts = split_options(p.getArg('{', '}'));
2260                         handle_geometry(opts);
2261                         continue;
2262                 }
2263
2264                 if (t.cs() == "definecolor") {
2265                         string const color = p.getArg('{', '}');
2266                         string const space = p.getArg('{', '}');
2267                         string const value = p.getArg('{', '}');
2268                         if (color == "document_fontcolor" && space == "rgb") {
2269                                 RGBColor c(RGBColorFromLaTeX(value));
2270                                 h_fontcolor = X11hexname(c);
2271                         } else if (color == "note_fontcolor" && space == "rgb") {
2272                                 RGBColor c(RGBColorFromLaTeX(value));
2273                                 h_notefontcolor = X11hexname(c);
2274                         } else if (color == "page_backgroundcolor" && space == "rgb") {
2275                                 RGBColor c(RGBColorFromLaTeX(value));
2276                                 h_backgroundcolor = X11hexname(c);
2277                         } else if (color == "shadecolor" && space == "rgb") {
2278                                 RGBColor c(RGBColorFromLaTeX(value));
2279                                 h_boxbgcolor = X11hexname(c);
2280                         } else {
2281                                 h_preamble << "\\definecolor{" << color
2282                                            << "}{" << space << "}{" << value
2283                                            << '}';
2284                         }
2285                         continue;
2286                 }
2287
2288                 if (t.cs() == "bibliographystyle") {
2289                         h_biblio_style = p.verbatim_item();
2290                         continue;
2291                 }
2292
2293                 if (t.cs() == "jurabibsetup") {
2294                         // FIXME p.getArg('{', '}') is most probably wrong (it
2295                         //       does not handle nested braces).
2296                         //       Use p.verbatim_item() instead.
2297                         vector<string> jurabibsetup =
2298                                 split_options(p.getArg('{', '}'));
2299                         // add jurabibsetup to the jurabib package options
2300                         add_package("jurabib", jurabibsetup);
2301                         if (!jurabibsetup.empty()) {
2302                                 h_preamble << "\\jurabibsetup{"
2303                                            << join(jurabibsetup, ",") << '}';
2304                         }
2305                         continue;
2306                 }
2307
2308                 if (t.cs() == "hypersetup") {
2309                         vector<string> hypersetup =
2310                                 split_options(p.verbatim_item());
2311                         // add hypersetup to the hyperref package options
2312                         handle_hyperref(hypersetup);
2313                         if (!hypersetup.empty()) {
2314                                 h_preamble << "\\hypersetup{"
2315                                            << join(hypersetup, ",") << '}';
2316                         }
2317                         continue;
2318                 }
2319
2320                 if (t.cs() == "includeonly") {
2321                         vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2322                         for (auto & iofile : includeonlys) {
2323                                 string filename(normalize_filename(iofile));
2324                                 string const path = getMasterFilePath(true);
2325                                 // We want to preserve relative/absolute filenames,
2326                                 // therefore path is only used for testing
2327                                 if (!makeAbsPath(filename, path).exists()) {
2328                                         // The file extension is probably missing.
2329                                         // Now try to find it out.
2330                                         string const tex_name =
2331                                                 find_file(filename, path,
2332                                                           known_tex_extensions);
2333                                         if (!tex_name.empty())
2334                                                 filename = tex_name;
2335                                 }
2336                                 string outname;
2337                                 if (makeAbsPath(filename, path).exists())
2338                                         fix_child_filename(filename);
2339                                 else
2340                                         cerr << "Warning: Could not find included file '"
2341                                              << filename << "'." << endl;
2342                                 outname = changeExtension(filename, "lyx");
2343                                 h_includeonlys.push_back(outname);
2344                         }
2345                         continue;
2346                 }
2347
2348                 if (is_known(t.cs(), known_if_3arg_commands)) {
2349                         // prevent misparsing of \usepackage if it is used
2350                         // as an argument (see e.g. our own output of
2351                         // \@ifundefined above)
2352                         string const arg1 = p.verbatim_item();
2353                         string const arg2 = p.verbatim_item();
2354                         string const arg3 = p.verbatim_item();
2355                         // test case \@ifundefined{date}{}{\date{}}
2356                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
2357                             arg2.empty() && arg3 == "\\date{}") {
2358                                 h_suppress_date = "true";
2359                         // older tex2lyx versions did output
2360                         // \@ifundefined{definecolor}{\usepackage{color}}{}
2361                         } else if (t.cs() == "@ifundefined" &&
2362                                    arg1 == "definecolor" &&
2363                                    arg2 == "\\usepackage{color}" &&
2364                                    arg3.empty()) {
2365                                 if (!in_lyx_preamble)
2366                                         h_preamble << package_beg_sep
2367                                                    << "color"
2368                                                    << package_mid_sep
2369                                                    << "\\@ifundefined{definecolor}{color}{}"
2370                                                    << package_end_sep;
2371                         // test for case
2372                         //\@ifundefined{showcaptionsetup}{}{%
2373                         // \PassOptionsToPackage{caption=false}{subfig}}
2374                         // that LyX uses for subfloats
2375                         } else if (t.cs() == "@ifundefined" &&
2376                                    arg1 == "showcaptionsetup" && arg2.empty()
2377                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2378                                 ; // do nothing
2379                         } else if (!in_lyx_preamble) {
2380                                 h_preamble << t.asInput()
2381                                            << '{' << arg1 << '}'
2382                                            << '{' << arg2 << '}'
2383                                            << '{' << arg3 << '}';
2384                         }
2385                         continue;
2386                 }
2387
2388                 if (is_known(t.cs(), known_if_commands)) {
2389                         // must not parse anything in conditional code, since
2390                         // LyX would output the parsed contents unconditionally
2391                         if (!in_lyx_preamble)
2392                                 h_preamble << t.asInput();
2393                         handle_if(p, in_lyx_preamble);
2394                         continue;
2395                 }
2396
2397                 if (!t.cs().empty() && !in_lyx_preamble) {
2398                         h_preamble << '\\' << t.cs();
2399                         continue;
2400                 }
2401         }
2402
2403         // remove the whitespace
2404         p.skip_spaces();
2405
2406         // Force textclass if the user wanted it
2407         if (!forceclass.empty())
2408                 h_textclass = forceclass;
2409         tc.setName(h_textclass);
2410         if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2411                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2412                 exit(EXIT_FAILURE);
2413         }
2414         if (h_papersides.empty()) {
2415                 ostringstream ss;
2416                 ss << tc.sides();
2417                 h_papersides = ss.str();
2418         }
2419
2420         // If the CJK package is used we cannot set the document language from
2421         // the babel options. Instead, we guess which language is used most
2422         // and set this one.
2423         default_language = h_language;
2424         if (is_full_document &&
2425             (auto_packages.find("CJK") != auto_packages.end() ||
2426              auto_packages.find("CJKutf8") != auto_packages.end())) {
2427                 p.pushPosition();
2428                 h_language = guessLanguage(p, default_language);
2429                 p.popPosition();
2430                 if (explicit_babel && h_language != default_language) {
2431                         // We set the document language to a CJK language,
2432                         // but babel is explicitly called in the user preamble
2433                         // without options. LyX will not add the default
2434                         // language to the document options if it is either
2435                         // english, or no text is set as default language.
2436                         // Therefore we need to add a language option explicitly.
2437                         // FIXME: It would be better to remove all babel calls
2438                         //        from the user preamble, but this is difficult
2439                         //        without re-introducing bug 7861.
2440                         if (h_options.empty())
2441                                 h_options = lyx2babel(default_language);
2442                         else
2443                                 h_options += ',' + lyx2babel(default_language);
2444                 }
2445         }
2446
2447         // Finally, set the quote style.
2448         // LyX knows the following quotes styles:
2449         // british, cjk, cjkangle, danish, english, french, german,
2450         // polish, russian, swedish and swiss
2451         // conversion list taken from
2452         // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2453         // (quotes for kazakh are unknown)
2454         // british
2455         if (is_known(h_language, known_british_quotes_languages))
2456                 h_quotes_style = "british";
2457         // cjk
2458         else if (is_known(h_language, known_cjk_quotes_languages))
2459                 h_quotes_style = "cjk";
2460         // cjkangle
2461         else if (is_known(h_language, known_cjkangle_quotes_languages))
2462                 h_quotes_style = "cjkangle";
2463         // danish
2464         else if (is_known(h_language, known_danish_quotes_languages))
2465                 h_quotes_style = "danish";
2466         // french
2467         else if (is_known(h_language, known_french_quotes_languages))
2468                 h_quotes_style = "french";
2469         // german
2470         else if (is_known(h_language, known_german_quotes_languages))
2471                 h_quotes_style = "german";
2472         // polish
2473         else if (is_known(h_language, known_polish_quotes_languages))
2474                 h_quotes_style = "polish";
2475         // russian
2476         else if (is_known(h_language, known_russian_quotes_languages))
2477                 h_quotes_style = "russian";
2478         // swedish
2479         else if (is_known(h_language, known_swedish_quotes_languages))
2480                 h_quotes_style = "swedish";
2481         // swiss
2482         else if (is_known(h_language, known_swiss_quotes_languages))
2483                 h_quotes_style = "swiss";
2484         // english
2485         else if (is_known(h_language, known_english_quotes_languages))
2486                 h_quotes_style = "english";
2487 }
2488
2489
2490 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2491 {
2492         TeX2LyXDocClass dummy;
2493         parse(p, forceclass, true, dummy);
2494         if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2495                 return h_inputencoding;
2496         return "";
2497 }
2498
2499
2500 string babel2lyx(string const & language)
2501 {
2502         char const * const * where = is_known(language, known_languages);
2503         if (where)
2504                 return known_coded_languages[where - known_languages];
2505         return language;
2506 }
2507
2508
2509 string lyx2babel(string const & language)
2510 {
2511         char const * const * where = is_known(language, known_coded_languages);
2512         if (where)
2513                 return known_languages[where - known_coded_languages];
2514         return language;
2515 }
2516
2517
2518 string Preamble::polyglossia2lyx(string const & language)
2519 {
2520         char const * const * where = is_known(language, polyglossia_languages);
2521         if (where)
2522                 return coded_polyglossia_languages[where - polyglossia_languages];
2523         return language;
2524 }
2525
2526
2527 string rgbcolor2code(string const & name)
2528 {
2529         char const * const * where = is_known(name, known_basic_colors);
2530         if (where) {
2531                 // "red", "green" etc
2532                 return known_basic_color_codes[where - known_basic_colors];
2533         }
2534         // "255,0,0", "0,255,0" etc
2535         RGBColor c(RGBColorFromLaTeX(name));
2536         return X11hexname(c);
2537 }
2538
2539 // }])
2540
2541
2542 } // namespace lyx