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