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