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