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