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