]> git.lyx.org Git - features.git/blob - src/tex2lyx/Preamble.cpp
Support more languages (file format change).
[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() == "date") {
1720                         string argument = p.getArg('{', '}');
1721                         if (argument.empty())
1722                                 h_suppress_date = "true";
1723                         else
1724                                 h_preamble << t.asInput() << '{' << argument << '}';
1725                         continue;
1726                 }
1727
1728                 if (t.cs() == "color") {
1729                         string const space =
1730                                 (p.hasOpt() ? p.getOpt() : string());
1731                         string argument = p.getArg('{', '}');
1732                         // check the case that a standard color is used
1733                         if (space.empty() && is_known(argument, known_basic_colors)) {
1734                                 h_fontcolor = rgbcolor2code(argument);
1735                                 registerAutomaticallyLoadedPackage("color");
1736                         } else if (space.empty() && argument == "document_fontcolor")
1737                                 registerAutomaticallyLoadedPackage("color");
1738                         // check the case that LyX's document_fontcolor is defined
1739                         // but not used for \color
1740                         else {
1741                                 h_preamble << t.asInput();
1742                                 if (!space.empty())
1743                                         h_preamble << space;
1744                                 h_preamble << '{' << argument << '}';
1745                                 // the color might already be set because \definecolor
1746                                 // is parsed before this
1747                                 h_fontcolor = "";
1748                         }
1749                         continue;
1750                 }
1751
1752                 if (t.cs() == "pagecolor") {
1753                         string argument = p.getArg('{', '}');
1754                         // check the case that a standard color is used
1755                         if (is_known(argument, known_basic_colors)) {
1756                                 h_backgroundcolor = rgbcolor2code(argument);
1757                         } else if (argument == "page_backgroundcolor")
1758                                 registerAutomaticallyLoadedPackage("color");
1759                         // check the case that LyX's page_backgroundcolor is defined
1760                         // but not used for \pagecolor
1761                         else {
1762                                 h_preamble << t.asInput() << '{' << argument << '}';
1763                                 // the color might already be set because \definecolor
1764                                 // is parsed before this
1765                                 h_backgroundcolor = "";
1766                         }
1767                         continue;
1768                 }
1769
1770                 if (t.cs() == "makeatletter") {
1771                         // LyX takes care of this
1772                         p.setCatcode('@', catLetter);
1773                         continue;
1774                 }
1775
1776                 if (t.cs() == "makeatother") {
1777                         // LyX takes care of this
1778                         p.setCatcode('@', catOther);
1779                         continue;
1780                 }
1781
1782                 if (t.cs() == "makeindex") {
1783                         // LyX will re-add this if a print index command is found
1784                         p.skip_spaces();
1785                         continue;
1786                 }
1787
1788                 if (t.cs() == "newindex") {
1789                         string const indexname = p.getArg('[', ']');
1790                         string const shortcut = p.verbatim_item();
1791                         if (!indexname.empty())
1792                                 h_index[index_number] = indexname;
1793                         else
1794                                 h_index[index_number] = shortcut;
1795                         h_shortcut[index_number] = shortcut;
1796                         index_number += 1;
1797                         p.skip_spaces();
1798                         continue;
1799                 }
1800
1801                 if (t.cs() == "addbibresource") {
1802                         string const options =  p.getArg('[', ']');
1803                         string const arg = removeExtension(p.getArg('{', '}'));
1804                         if (!options.empty()) {
1805                                 // check if the option contains a bibencoding, if yes, extract it
1806                                 string::size_type pos = options.find("bibencoding=");
1807                                 string encoding;
1808                                 if (pos != string::npos) {
1809                                         string::size_type i = options.find(',', pos);
1810                                         if (i == string::npos)
1811                                                 encoding = options.substr(pos + 1);
1812                                         else
1813                                                 encoding = options.substr(pos, i - pos);
1814                                         pos = encoding.find('=');
1815                                         if (pos == string::npos)
1816                                                 encoding.clear();
1817                                         else
1818                                                 encoding = encoding.substr(pos + 1);
1819                                 }
1820                                 if (!encoding.empty())
1821                                         biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding);
1822                         }
1823                         biblatex_bibliographies.push_back(arg);
1824                         continue;
1825                 }
1826
1827                 if (t.cs() == "bibliography") {
1828                         vector<string> bibs = getVectorFromString(p.getArg('{', '}'));
1829                         biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end());
1830                         continue;
1831                 }
1832
1833                 if (t.cs() == "RS@ifundefined") {
1834                         string const name = p.verbatim_item();
1835                         string const body1 = p.verbatim_item();
1836                         string const body2 = p.verbatim_item();
1837                         // only non-lyxspecific stuff
1838                         if (in_lyx_preamble &&
1839                             (name == "subsecref" || name == "thmref" || name == "lemref"))
1840                                 p.skip_spaces();
1841                         else {
1842                                 ostringstream ss;
1843                                 ss << '\\' << t.cs();
1844                                 ss << '{' << name << '}'
1845                                    << '{' << body1 << '}'
1846                                    << '{' << body2 << '}';
1847                                 h_preamble << ss.str();
1848                         }
1849                         continue;
1850                 }
1851
1852                 if (t.cs() == "AtBeginDocument") {
1853                         string const name = p.verbatim_item();
1854                         // only non-lyxspecific stuff
1855                         if (in_lyx_preamble &&
1856                             (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1857                                 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1858                                 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1859                                 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1860                                 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1861                                 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1862                                 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1863                                 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1864                                 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1865                                 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1866                                 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1867                                 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1868                                 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1869                                 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1870                                 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1871                                 p.skip_spaces();
1872                         else {
1873                                 ostringstream ss;
1874                                 ss << '\\' << t.cs();
1875                                 ss << '{' << name << '}';
1876                                 h_preamble << ss.str();
1877                         }
1878                         continue;
1879                 }
1880
1881                 if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1882                     || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1883                     || t.cs() == "providecommand" || t.cs() == "providecommandx"
1884                     || t.cs() == "DeclareRobustCommand"
1885                     || t.cs() == "DeclareRobustCommandx"
1886                     || t.cs() == "ProvideTextCommandDefault"
1887                     || t.cs() == "DeclareMathAccent") {
1888                         bool star = false;
1889                         if (p.next_token().character() == '*') {
1890                                 p.get_token();
1891                                 star = true;
1892                         }
1893                         string const name = p.verbatim_item();
1894                         string const opt1 = p.getFullOpt();
1895                         string const opt2 = p.getFullOpt();
1896                         string const body = p.verbatim_item();
1897                         // store the in_lyx_preamble setting
1898                         bool const was_in_lyx_preamble = in_lyx_preamble;
1899                         // font settings
1900                         if (name == "\\rmdefault")
1901                                 if (is_known(body, known_roman_font_packages)) {
1902                                         h_font_roman[0] = body;
1903                                         p.skip_spaces();
1904                                         in_lyx_preamble = true;
1905                                 }
1906                         if (name == "\\sfdefault")
1907                                 if (is_known(body, known_sans_font_packages)) {
1908                                         h_font_sans[0] = body;
1909                                         p.skip_spaces();
1910                                         in_lyx_preamble = true;
1911                                 }
1912                         if (name == "\\ttdefault")
1913                                 if (is_known(body, known_typewriter_font_packages)) {
1914                                         h_font_typewriter[0] = body;
1915                                         p.skip_spaces();
1916                                         in_lyx_preamble = true;
1917                                 }
1918                         if (name == "\\familydefault") {
1919                                 string family = body;
1920                                 // remove leading "\"
1921                                 h_font_default_family = family.erase(0,1);
1922                                 p.skip_spaces();
1923                                 in_lyx_preamble = true;
1924                         }
1925
1926                         // remove LyX-specific definitions that are re-added by LyX
1927                         // if necessary
1928                         // \lyxline is an ancient command that is converted by tex2lyx into
1929                         // a \rule therefore remove its preamble code
1930                         if (name == "\\lyxdot" || name == "\\lyxarrow"
1931                             || name == "\\lyxline" || name == "\\LyX") {
1932                                 p.skip_spaces();
1933                                 in_lyx_preamble = true;
1934                         }
1935
1936                         // Add the command to the known commands
1937                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1938
1939                         // only non-lyxspecific stuff
1940                         if (!in_lyx_preamble) {
1941                                 ostringstream ss;
1942                                 ss << '\\' << t.cs();
1943                                 if (star)
1944                                         ss << '*';
1945                                 ss << '{' << name << '}' << opt1 << opt2
1946                                    << '{' << body << "}";
1947                                 if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str()))
1948                                         h_preamble << ss.str();
1949 /*
1950                                 ostream & out = in_preamble ? h_preamble : os;
1951                                 out << "\\" << t.cs() << "{" << name << "}"
1952                                     << opts << "{" << body << "}";
1953 */
1954                         }
1955                         // restore the in_lyx_preamble setting
1956                         in_lyx_preamble = was_in_lyx_preamble;
1957                         continue;
1958                 }
1959
1960                 if (t.cs() == "documentclass") {
1961                         vector<string>::iterator it;
1962                         vector<string> opts = split_options(p.getArg('[', ']'));
1963                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1964                         delete_opt(opts, known_fontsizes);
1965                         // delete "pt" at the end
1966                         string::size_type i = h_paperfontsize.find("pt");
1967                         if (i != string::npos)
1968                                 h_paperfontsize.erase(i);
1969                         // The documentclass options are always parsed before the options
1970                         // of the babel call so that a language cannot overwrite the babel
1971                         // options.
1972                         handle_opt(opts, known_languages, h_language);
1973                         delete_opt(opts, known_languages);
1974
1975                         // math indentation
1976                         if ((it = find(opts.begin(), opts.end(), "fleqn"))
1977                                  != opts.end()) {
1978                                 h_is_mathindent = "1";
1979                                 opts.erase(it);
1980                         }
1981                         // formula numbering side
1982                         if ((it = find(opts.begin(), opts.end(), "leqno"))
1983                                  != opts.end()) {
1984                                 h_math_numbering_side = "left";
1985                                 opts.erase(it);
1986                         }
1987                         else if ((it = find(opts.begin(), opts.end(), "reqno"))
1988                                  != opts.end()) {
1989                                 h_math_numbering_side = "right";
1990                                 opts.erase(it);
1991                         }
1992
1993                         // paper orientation
1994                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1995                                 h_paperorientation = "landscape";
1996                                 opts.erase(it);
1997                         }
1998                         // paper sides
1999                         if ((it = find(opts.begin(), opts.end(), "oneside"))
2000                                  != opts.end()) {
2001                                 h_papersides = "1";
2002                                 opts.erase(it);
2003                         }
2004                         if ((it = find(opts.begin(), opts.end(), "twoside"))
2005                                  != opts.end()) {
2006                                 h_papersides = "2";
2007                                 opts.erase(it);
2008                         }
2009                         // paper columns
2010                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
2011                                  != opts.end()) {
2012                                 h_papercolumns = "1";
2013                                 opts.erase(it);
2014                         }
2015                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
2016                                  != opts.end()) {
2017                                 h_papercolumns = "2";
2018                                 opts.erase(it);
2019                         }
2020                         // paper sizes
2021                         // some size options are known to any document classes, other sizes
2022                         // are handled by the \geometry command of the geometry package
2023                         handle_opt(opts, known_class_paper_sizes, h_papersize);
2024                         delete_opt(opts, known_class_paper_sizes);
2025                         // the remaining options
2026                         h_options = join(opts, ",");
2027                         // FIXME This does not work for classes that have a
2028                         //       different name in LyX than in LaTeX
2029                         h_textclass = p.getArg('{', '}');
2030                         p.skip_spaces();
2031                         continue;
2032                 }
2033
2034                 if (t.cs() == "usepackage") {
2035                         string const options = p.getArg('[', ']');
2036                         string const name = p.getArg('{', '}');
2037                         vector<string> vecnames;
2038                         split(name, vecnames, ',');
2039                         vector<string>::const_iterator it  = vecnames.begin();
2040                         vector<string>::const_iterator end = vecnames.end();
2041                         for (; it != end; ++it)
2042                                 handle_package(p, trimSpaceAndEol(*it), options,
2043                                                in_lyx_preamble, detectEncoding);
2044                         continue;
2045                 }
2046
2047                 if (t.cs() == "inputencoding") {
2048                         string const encoding = p.getArg('{','}');
2049                         Encoding const * const enc = encodings.fromLaTeXName(
2050                                 encoding, Encoding::inputenc, true);
2051                         if (!enc) {
2052                                 if (!detectEncoding)
2053                                         cerr << "Unknown encoding " << encoding
2054                                              << ". Ignoring." << std::endl;
2055                         } else {
2056                                 if (!enc->unsafe())
2057                                         h_inputencoding = enc->name();
2058                                 p.setEncoding(enc->iconvName());
2059                         }
2060                         continue;
2061                 }
2062
2063                 if (t.cs() == "newenvironment") {
2064                         string const name = p.getArg('{', '}');
2065                         string const opt1 = p.getFullOpt();
2066                         string const opt2 = p.getFullOpt();
2067                         string const beg = p.verbatim_item();
2068                         string const end = p.verbatim_item();
2069                         if (!in_lyx_preamble) {
2070                                 h_preamble << "\\newenvironment{" << name
2071                                            << '}' << opt1 << opt2 << '{'
2072                                            << beg << "}{" << end << '}';
2073                         }
2074                         add_known_environment(name, opt1, !opt2.empty(),
2075                                               from_utf8(beg), from_utf8(end));
2076                         continue;
2077                 }
2078
2079                 if (t.cs() == "newtheorem") {
2080                         bool star = false;
2081                         if (p.next_token().character() == '*') {
2082                                 p.get_token();
2083                                 star = true;
2084                         }
2085                         string const name = p.getArg('{', '}');
2086                         string const opt1 = p.getFullOpt();
2087                         string const opt2 = p.getFullOpt();
2088                         string const body = p.verbatim_item();
2089                         string const opt3 = p.getFullOpt();
2090                         string const cmd = star ? "\\newtheorem*" : "\\newtheorem";
2091
2092                         string const complete = cmd + "{" + name + '}' +
2093                                           opt1 + opt2 + '{' + body + '}' + opt3;
2094
2095                         add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
2096
2097                         if (!in_lyx_preamble)
2098                                 h_preamble << complete;
2099                         continue;
2100                 }
2101
2102                 if (t.cs() == "def") {
2103                         string name = p.get_token().cs();
2104                         // In fact, name may be more than the name:
2105                         // In the test case of bug 8116
2106                         // name == "csname SF@gobble@opt \endcsname".
2107                         // Therefore, we need to use asInput() instead of cs().
2108                         while (p.next_token().cat() != catBegin)
2109                                 name += p.get_token().asInput();
2110                         if (!in_lyx_preamble)
2111                                 h_preamble << "\\def\\" << name << '{'
2112                                            << p.verbatim_item() << "}";
2113                         continue;
2114                 }
2115
2116                 if (t.cs() == "newcolumntype") {
2117                         string const name = p.getArg('{', '}');
2118                         trimSpaceAndEol(name);
2119                         int nargs = 0;
2120                         string opts = p.getOpt();
2121                         if (!opts.empty()) {
2122                                 istringstream is(string(opts, 1));
2123                                 is >> nargs;
2124                         }
2125                         special_columns_[name[0]] = nargs;
2126                         h_preamble << "\\newcolumntype{" << name << "}";
2127                         if (nargs)
2128                                 h_preamble << "[" << nargs << "]";
2129                         h_preamble << "{" << p.verbatim_item() << "}";
2130                         continue;
2131                 }
2132
2133                 if (t.cs() == "setcounter") {
2134                         string const name = p.getArg('{', '}');
2135                         string const content = p.getArg('{', '}');
2136                         if (name == "secnumdepth")
2137                                 h_secnumdepth = content;
2138                         else if (name == "tocdepth")
2139                                 h_tocdepth = content;
2140                         else
2141                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
2142                         continue;
2143                 }
2144
2145                 if (t.cs() == "setlength") {
2146                         string const name = p.verbatim_item();
2147                         string const content = p.verbatim_item();
2148                         // the paragraphs are only not indented when \parindent is set to zero
2149                         if (name == "\\parindent" && content != "") {
2150                                 if (content[0] == '0')
2151                                         h_paragraph_separation = "skip";
2152                                 else
2153                                         h_paragraph_indentation = translate_len(content);
2154                         } else if (name == "\\parskip") {
2155                                 if (content == "\\smallskipamount")
2156                                         h_defskip = "smallskip";
2157                                 else if (content == "\\medskipamount")
2158                                         h_defskip = "medskip";
2159                                 else if (content == "\\bigskipamount")
2160                                         h_defskip = "bigskip";
2161                                 else
2162                                         h_defskip = translate_len(content);
2163                         } else if (name == "\\mathindent") {
2164                                 h_mathindentation = translate_len(content);
2165                         } else
2166                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
2167                         continue;
2168                 }
2169
2170                 if (t.cs() == "onehalfspacing") {
2171                         h_spacing = "onehalf";
2172                         continue;
2173                 }
2174
2175                 if (t.cs() == "doublespacing") {
2176                         h_spacing = "double";
2177                         continue;
2178                 }
2179
2180                 if (t.cs() == "setstretch") {
2181                         h_spacing = "other " + p.verbatim_item();
2182                         continue;
2183                 }
2184
2185                 if (t.cs() == "synctex") {
2186                         // the scheme is \synctex=value
2187                         // where value can only be "1" or "-1"
2188                         h_output_sync = "1";
2189                         // there can be any character behind the value (e.g. a linebreak or a '\'
2190                         // therefore we extract it char by char
2191                         p.get_token();
2192                         string value = p.get_token().asInput();
2193                         if (value == "-")
2194                                 value += p.get_token().asInput();
2195                         h_output_sync_macro = "\\synctex=" + value;
2196                         continue;
2197                 }
2198
2199                 if (t.cs() == "begin") {
2200                         string const name = p.getArg('{', '}');
2201                         if (name == "document")
2202                                 break;
2203                         h_preamble << "\\begin{" << name << "}";
2204                         continue;
2205                 }
2206
2207                 if (t.cs() == "geometry") {
2208                         vector<string> opts = split_options(p.getArg('{', '}'));
2209                         handle_geometry(opts);
2210                         continue;
2211                 }
2212
2213                 if (t.cs() == "definecolor") {
2214                         string const color = p.getArg('{', '}');
2215                         string const space = p.getArg('{', '}');
2216                         string const value = p.getArg('{', '}');
2217                         if (color == "document_fontcolor" && space == "rgb") {
2218                                 RGBColor c(RGBColorFromLaTeX(value));
2219                                 h_fontcolor = X11hexname(c);
2220                         } else if (color == "note_fontcolor" && space == "rgb") {
2221                                 RGBColor c(RGBColorFromLaTeX(value));
2222                                 h_notefontcolor = X11hexname(c);
2223                         } else if (color == "page_backgroundcolor" && space == "rgb") {
2224                                 RGBColor c(RGBColorFromLaTeX(value));
2225                                 h_backgroundcolor = X11hexname(c);
2226                         } else if (color == "shadecolor" && space == "rgb") {
2227                                 RGBColor c(RGBColorFromLaTeX(value));
2228                                 h_boxbgcolor = X11hexname(c);
2229                         } else {
2230                                 h_preamble << "\\definecolor{" << color
2231                                            << "}{" << space << "}{" << value
2232                                            << '}';
2233                         }
2234                         continue;
2235                 }
2236
2237                 if (t.cs() == "bibliographystyle") {
2238                         h_biblio_style = p.verbatim_item();
2239                         continue;
2240                 }
2241
2242                 if (t.cs() == "jurabibsetup") {
2243                         // FIXME p.getArg('{', '}') is most probably wrong (it
2244                         //       does not handle nested braces).
2245                         //       Use p.verbatim_item() instead.
2246                         vector<string> jurabibsetup =
2247                                 split_options(p.getArg('{', '}'));
2248                         // add jurabibsetup to the jurabib package options
2249                         add_package("jurabib", jurabibsetup);
2250                         if (!jurabibsetup.empty()) {
2251                                 h_preamble << "\\jurabibsetup{"
2252                                            << join(jurabibsetup, ",") << '}';
2253                         }
2254                         continue;
2255                 }
2256
2257                 if (t.cs() == "hypersetup") {
2258                         vector<string> hypersetup =
2259                                 split_options(p.verbatim_item());
2260                         // add hypersetup to the hyperref package options
2261                         handle_hyperref(hypersetup);
2262                         if (!hypersetup.empty()) {
2263                                 h_preamble << "\\hypersetup{"
2264                                            << join(hypersetup, ",") << '}';
2265                         }
2266                         continue;
2267                 }
2268
2269                 if (t.cs() == "includeonly") {
2270                         vector<string> includeonlys = getVectorFromString(p.getArg('{', '}'));
2271                         for (auto & iofile : includeonlys) {
2272                                 string filename(normalize_filename(iofile));
2273                                 string const path = getMasterFilePath(true);
2274                                 // We want to preserve relative/absolute filenames,
2275                                 // therefore path is only used for testing
2276                                 if (!makeAbsPath(filename, path).exists()) {
2277                                         // The file extension is probably missing.
2278                                         // Now try to find it out.
2279                                         string const tex_name =
2280                                                 find_file(filename, path,
2281                                                           known_tex_extensions);
2282                                         if (!tex_name.empty())
2283                                                 filename = tex_name;
2284                                 }
2285                                 string outname;
2286                                 if (makeAbsPath(filename, path).exists())
2287                                         fix_child_filename(filename);
2288                                 else
2289                                         cerr << "Warning: Could not find included file '"
2290                                              << filename << "'." << endl;
2291                                 outname = changeExtension(filename, "lyx");
2292                                 h_includeonlys.push_back(outname);
2293                         }
2294                         continue;
2295                 }
2296
2297                 if (is_known(t.cs(), known_if_3arg_commands)) {
2298                         // prevent misparsing of \usepackage if it is used
2299                         // as an argument (see e.g. our own output of
2300                         // \@ifundefined above)
2301                         string const arg1 = p.verbatim_item();
2302                         string const arg2 = p.verbatim_item();
2303                         string const arg3 = p.verbatim_item();
2304                         // test case \@ifundefined{date}{}{\date{}}
2305                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
2306                             arg2.empty() && arg3 == "\\date{}") {
2307                                 h_suppress_date = "true";
2308                         // older tex2lyx versions did output
2309                         // \@ifundefined{definecolor}{\usepackage{color}}{}
2310                         } else if (t.cs() == "@ifundefined" &&
2311                                    arg1 == "definecolor" &&
2312                                    arg2 == "\\usepackage{color}" &&
2313                                    arg3.empty()) {
2314                                 if (!in_lyx_preamble)
2315                                         h_preamble << package_beg_sep
2316                                                    << "color"
2317                                                    << package_mid_sep
2318                                                    << "\\@ifundefined{definecolor}{color}{}"
2319                                                    << package_end_sep;
2320                         // test for case
2321                         //\@ifundefined{showcaptionsetup}{}{%
2322                         // \PassOptionsToPackage{caption=false}{subfig}}
2323                         // that LyX uses for subfloats
2324                         } else if (t.cs() == "@ifundefined" &&
2325                                    arg1 == "showcaptionsetup" && arg2.empty()
2326                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
2327                                 ; // do nothing
2328                         } else if (!in_lyx_preamble) {
2329                                 h_preamble << t.asInput()
2330                                            << '{' << arg1 << '}'
2331                                            << '{' << arg2 << '}'
2332                                            << '{' << arg3 << '}';
2333                         }
2334                         continue;
2335                 }
2336
2337                 if (is_known(t.cs(), known_if_commands)) {
2338                         // must not parse anything in conditional code, since
2339                         // LyX would output the parsed contents unconditionally
2340                         if (!in_lyx_preamble)
2341                                 h_preamble << t.asInput();
2342                         handle_if(p, in_lyx_preamble);
2343                         continue;
2344                 }
2345
2346                 if (!t.cs().empty() && !in_lyx_preamble) {
2347                         h_preamble << '\\' << t.cs();
2348                         continue;
2349                 }
2350         }
2351
2352         // remove the whitespace
2353         p.skip_spaces();
2354
2355         // Force textclass if the user wanted it
2356         if (!forceclass.empty())
2357                 h_textclass = forceclass;
2358         tc.setName(h_textclass);
2359         if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) {
2360                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2361                 exit(EXIT_FAILURE);
2362         }
2363         if (h_papersides.empty()) {
2364                 ostringstream ss;
2365                 ss << tc.sides();
2366                 h_papersides = ss.str();
2367         }
2368
2369         // If the CJK package is used we cannot set the document language from
2370         // the babel options. Instead, we guess which language is used most
2371         // and set this one.
2372         default_language = h_language;
2373         if (is_full_document &&
2374             (auto_packages.find("CJK") != auto_packages.end() ||
2375              auto_packages.find("CJKutf8") != auto_packages.end())) {
2376                 p.pushPosition();
2377                 h_language = guessLanguage(p, default_language);
2378                 p.popPosition();
2379                 if (explicit_babel && h_language != default_language) {
2380                         // We set the document language to a CJK language,
2381                         // but babel is explicitly called in the user preamble
2382                         // without options. LyX will not add the default
2383                         // language to the document options if it is either
2384                         // english, or no text is set as default language.
2385                         // Therefore we need to add a language option explicitly.
2386                         // FIXME: It would be better to remove all babel calls
2387                         //        from the user preamble, but this is difficult
2388                         //        without re-introducing bug 7861.
2389                         if (h_options.empty())
2390                                 h_options = lyx2babel(default_language);
2391                         else
2392                                 h_options += ',' + lyx2babel(default_language);
2393                 }
2394         }
2395
2396         // Finally, set the quote style.
2397         // LyX knows the following quotes styles:
2398         // british, cjk, cjkangle, danish, english, french, german,
2399         // polish, russian, swedish and swiss
2400         // conversion list taken from
2401         // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
2402         // (quotes for kazakh are unknown)
2403         // british
2404         if (is_known(h_language, known_british_quotes_languages))
2405                 h_quotes_style = "british";
2406         // cjk
2407         else if (is_known(h_language, known_cjk_quotes_languages))
2408                 h_quotes_style = "cjk";
2409         // cjkangle
2410         else if (is_known(h_language, known_cjkangle_quotes_languages))
2411                 h_quotes_style = "cjkangle";
2412         // danish
2413         else if (is_known(h_language, known_danish_quotes_languages))
2414                 h_quotes_style = "danish";
2415         // french
2416         else if (is_known(h_language, known_french_quotes_languages))
2417                 h_quotes_style = "french";
2418         // german
2419         else if (is_known(h_language, known_german_quotes_languages))
2420                 h_quotes_style = "german";
2421         // polish
2422         else if (is_known(h_language, known_polish_quotes_languages))
2423                 h_quotes_style = "polish";
2424         // russian
2425         else if (is_known(h_language, known_russian_quotes_languages))
2426                 h_quotes_style = "russian";
2427         // swedish
2428         else if (is_known(h_language, known_swedish_quotes_languages))
2429                 h_quotes_style = "swedish";
2430         // swiss
2431         else if (is_known(h_language, known_swiss_quotes_languages))
2432                 h_quotes_style = "swiss";
2433         // english
2434         else if (is_known(h_language, known_english_quotes_languages))
2435                 h_quotes_style = "english";
2436 }
2437
2438
2439 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2440 {
2441         TeX2LyXDocClass dummy;
2442         parse(p, forceclass, true, dummy);
2443         if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain")
2444                 return h_inputencoding;
2445         return "";
2446 }
2447
2448
2449 string babel2lyx(string const & language)
2450 {
2451         char const * const * where = is_known(language, known_languages);
2452         if (where)
2453                 return known_coded_languages[where - known_languages];
2454         return language;
2455 }
2456
2457
2458 string lyx2babel(string const & language)
2459 {
2460         char const * const * where = is_known(language, known_coded_languages);
2461         if (where)
2462                 return known_languages[where - known_coded_languages];
2463         return language;
2464 }
2465
2466
2467 string Preamble::polyglossia2lyx(string const & language)
2468 {
2469         char const * const * where = is_known(language, polyglossia_languages);
2470         if (where)
2471                 return coded_polyglossia_languages[where - polyglossia_languages];
2472         return language;
2473 }
2474
2475
2476 string rgbcolor2code(string const & name)
2477 {
2478         char const * const * where = is_known(name, known_basic_colors);
2479         if (where) {
2480                 // "red", "green" etc
2481                 return known_basic_color_codes[where - known_basic_colors];
2482         }
2483         // "255,0,0", "0,255,0" etc
2484         RGBColor c(RGBColorFromLaTeX(name));
2485         return X11hexname(c);
2486 }
2487
2488 // }])
2489
2490
2491 } // namespace lyx