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