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