]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
b1b9ded67d8742195618bb955d35792d75c50b9a
[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";
683                 // this automatically invokes biolinum
684                 h_font_sans = "biolinum";
685                 if (opts == "osf")
686                         h_font_osf = "true";
687         }
688
689         if (name == "libertine-type1") {
690                 h_font_roman = "libertine";
691                 // NOTE: contrary to libertine.sty, libertine-type1
692                 // does not automatically invoke biolinum
693                 if (opts == "lining")
694                         h_font_osf = "false";
695                 else
696                         h_font_osf = "true";
697         }
698         
699         if (name == "mathdesign") {
700                 if (opts.find("charter") != string::npos)
701                         h_font_roman = "md-charter";
702                 if (opts.find("garamond") != string::npos)
703                         h_font_roman = "md-garamond";
704                 if (opts.find("utopia") != string::npos)
705                         h_font_roman = "md-utopia";
706                 if (opts.find("expert") != string::npos) {
707                         h_font_sc = "true";
708                         h_font_osf = "true";
709                 }
710         }
711
712         else if (name == "mathpazo")
713                 h_font_roman = "palatino";
714
715         else if (name == "mathptmx")
716                 h_font_roman = "times";
717
718         // sansserif fonts
719         if (is_known(name, known_sans_fonts)) {
720                 h_font_sans = name;
721                 if (options.size() == 1) {
722                         if (scale_as_percentage(opts, h_font_sf_scale))
723                                 options.clear();
724                 }
725         }
726
727         if (name == "biolinum-type1")
728                 h_font_sans = "biolinum";
729
730         // typewriter fonts
731         if (is_known(name, known_typewriter_fonts)) {
732                 // fourier can be set as roman font _only_
733                 // fourier as typewriter is handled in handling of \ttdefault
734                 if (name != "fourier") {
735                         h_font_typewriter = name;
736                         if (options.size() == 1) {
737                                 if (scale_as_percentage(opts, h_font_tt_scale))
738                                         options.clear();
739                         }
740                 }
741         }
742
743         // font uses old-style figure
744         if (name == "eco")
745                 h_font_osf = "true";
746
747         // math fonts
748         if (is_known(name, known_math_fonts))
749                 h_font_math = name;
750
751         if (name == "newtxmath") {
752                 if (opts.empty())
753                         h_font_math = "newtxmath";
754                 else if (opts == "garamondx")
755                         h_font_math = "garamondx-ntxm";
756                 else if (opts == "libertine")
757                         h_font_math = "libertine-ntxm";
758                 else if (opts == "minion")
759                         h_font_math = "minion-ntxm";
760         }
761
762         if (name == "refstyle")
763                 h_use_refstyle = "1";
764
765         // after the detection and handling of special cases, we can remove the
766         // fonts, otherwise they would appear in the preamble, see bug #7856
767         if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
768                 ||      is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
769                 ;
770
771         else if (name == "amsmath" || name == "amssymb" ||
772                  name == "esint" || name == "mhchem" || name == "mathdots" ||
773                  name == "mathtools" || name == "stackrel" ||
774                  name == "stmaryrd" || name == "undertilde")
775                 h_use_packages[name] = "2";
776
777         else if (name == "babel") {
778                 h_language_package = "default";
779                 // One might think we would have to do nothing if babel is loaded
780                 // without any options to prevent pollution of the preamble with this
781                 // babel call in every roundtrip.
782                 // But the user could have defined babel-specific things afterwards. So
783                 // we need to keep it in the preamble to prevent cases like bug #7861.
784                 if (!opts.empty()) {
785                         // check if more than one option was used - used later for inputenc
786                         if (options.begin() != options.end() - 1)
787                                 one_language = false;
788                         // babel takes the last language of the option of its \usepackage
789                         // call as document language. If there is no such language option, the
790                         // last language in the documentclass options is used.
791                         handle_opt(options, known_languages, h_language);
792                         // translate the babel name to a LyX name
793                         h_language = babel2lyx(h_language);
794                         if (h_language == "japanese") {
795                                 // For Japanese, the encoding isn't indicated in the source
796                                 // file, and there's really not much we can do. We could
797                                 // 1) offer a list of possible encodings to choose from, or
798                                 // 2) determine the encoding of the file by inspecting it.
799                                 // For the time being, we leave the encoding alone so that
800                                 // we don't get iconv errors when making a wrong guess, and
801                                 // we will output a note at the top of the document
802                                 // explaining what to do.
803                                 Encoding const * const enc = encodings.fromIconvName(
804                                         p.getEncoding(), Encoding::japanese, false);
805                                 if (enc)
806                                         h_inputencoding = enc->name();
807                                 is_nonCJKJapanese = true;
808                                 // in this case babel can be removed from the preamble
809                                 registerAutomaticallyLoadedPackage("babel");
810                         } else {
811                                 // If babel is called with options, LyX puts them by default into the
812                                 // document class options. This works for most languages, except
813                                 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
814                                 // perhaps in future others.
815                                 // Therefore keep the babel call as it is as the user might have
816                                 // reasons for it.
817                                 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
818                         }
819                         delete_opt(options, known_languages);
820                 }
821                 else
822                         h_preamble << "\\usepackage{babel}\n";
823         }
824
825         else if (name == "polyglossia") {
826                 h_language_package = "default";
827                 h_default_output_format = "pdf4";
828                 h_use_non_tex_fonts = "true";
829                 xetex = true;
830                 registerAutomaticallyLoadedPackage("xunicode");
831                 if (h_inputencoding == "auto")
832                         p.setEncoding("UTF-8");
833         }
834
835         else if (name == "CJK") {
836                 // set the encoding to "auto" because it might be set to "default" by the babel handling
837                 // and this would not be correct for CJK
838                 if (h_inputencoding == "default")
839                         h_inputencoding = "auto";
840                 registerAutomaticallyLoadedPackage("CJK");
841         }
842
843         else if (name == "CJKutf8") {
844                 h_inputencoding = "UTF8";
845                 p.setEncoding("UTF-8");
846                 registerAutomaticallyLoadedPackage("CJKutf8");
847         }
848
849         else if (name == "fontenc") {
850                 h_fontencoding = getStringFromVector(options, ",");
851                 /* We could do the following for better round trip support,
852                  * but this makes the document less portable, so I skip it:
853                 if (h_fontencoding == lyxrc.fontenc)
854                         h_fontencoding = "global";
855                  */
856                 options.clear();
857         }
858
859         else if (name == "inputenc" || name == "luainputenc") {
860                 // h_inputencoding is only set when there is not more than one
861                 // inputenc option because otherwise h_inputencoding must be
862                 // set to "auto" (the default encoding of the document language)
863                 // Therefore check for the "," character.
864                 // It is also only set when there is not more than one babel
865                 // language option.
866                 if (opts.find(",") == string::npos && one_language == true)
867                         h_inputencoding = opts;
868                 if (!options.empty())
869                         p.setEncoding(options.back(), Encoding::inputenc);
870                 options.clear();
871         }
872
873         else if (name == "srcltx") {
874                 h_output_sync = "1";
875                 if (!opts.empty()) {
876                         h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
877                         options.clear();
878                 } else
879                         h_output_sync_macro = "\\usepackage{srcltx}";
880         }
881
882         else if (is_known(name, known_old_language_packages)) {
883                 // known language packages from the times before babel
884                 // if they are found and not also babel, they will be used as
885                 // custom language package
886                 h_language_package = "\\usepackage{" + name + "}";
887         }
888
889         else if (name == "prettyref")
890                 ; // ignore this FIXME: Use the package separator mechanism instead
891
892         else if (name == "lyxskak") {
893                 // ignore this and its options
894                 const char * const o[] = {"ps", "mover", 0};
895                 delete_opt(options, o);
896         }
897
898         else if (is_known(name, known_lyx_packages) && options.empty()) {
899                 if (name == "splitidx")
900                         h_use_indices = "true";
901                 if (!in_lyx_preamble) {
902                         h_preamble << package_beg_sep << name
903                                    << package_mid_sep << "\\usepackage{"
904                                    << name << '}';
905                         if (p.next_token().cat() == catNewline ||
906                             (p.next_token().cat() == catSpace &&
907                              p.next_next_token().cat() == catNewline))
908                                 h_preamble << '\n';
909                         h_preamble << package_end_sep;
910                 }
911         }
912
913         else if (name == "geometry")
914                 handle_geometry(options);
915
916         else if (name == "subfig")
917                 ; // ignore this FIXME: Use the package separator mechanism instead
918
919         else if ((where = is_known(name, known_languages)))
920                 h_language = known_coded_languages[where - known_languages];
921
922         else if (name == "natbib") {
923                 h_biblio_style = "plainnat";
924                 h_cite_engine = "natbib";
925                 h_cite_engine_type = "authoryear";
926                 vector<string>::iterator it =
927                         find(options.begin(), options.end(), "authoryear");
928                 if (it != options.end())
929                         options.erase(it);
930                 else {
931                         it = find(options.begin(), options.end(), "numbers");
932                         if (it != options.end()) {
933                                 h_cite_engine_type = "numerical";
934                                 options.erase(it);
935                         }
936                 }
937         }
938
939         else if (name == "jurabib") {
940                 h_biblio_style = "jurabib";
941                 h_cite_engine = "jurabib";
942                 h_cite_engine_type = "authoryear";
943         }
944
945         else if (name == "hyperref")
946                 handle_hyperref(options);
947
948         else if (!in_lyx_preamble) {
949                 if (options.empty())
950                         h_preamble << "\\usepackage{" << name << '}';
951                 else {
952                         h_preamble << "\\usepackage[" << opts << "]{"
953                                    << name << '}';
954                         options.clear();
955                 }
956                 if (p.next_token().cat() == catNewline ||
957                     (p.next_token().cat() == catSpace &&
958                      p.next_next_token().cat() == catNewline))
959                         h_preamble << '\n';
960         }
961
962         // We need to do something with the options...
963         if (!options.empty())
964                 cerr << "Ignoring options '" << join(options, ",")
965                      << "' of package " << name << '.' << endl;
966
967         // remove the whitespace
968         p.skip_spaces();
969 }
970
971
972 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
973 {
974         while (p.good()) {
975                 Token t = p.get_token();
976                 if (t.cat() == catEscape &&
977                     is_known(t.cs(), known_if_commands))
978                         handle_if(p, in_lyx_preamble);
979                 else {
980                         if (!in_lyx_preamble)
981                                 h_preamble << t.asInput();
982                         if (t.cat() == catEscape && t.cs() == "fi")
983                                 return;
984                 }
985         }
986 }
987
988
989 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
990 {
991         // set the quote language
992         // LyX only knows the following quotes languages:
993         // english, swedish, german, polish, french and danish
994         // (quotes for "japanese" and "chinese-traditional" are missing because
995         //  they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
996         // conversion list taken from
997         // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
998         // (quotes for kazakh and interlingua are unknown)
999         // danish
1000         if (is_known(h_language, known_danish_quotes_languages))
1001                 h_quotes_language = "danish";
1002         // french
1003         else if (is_known(h_language, known_french_quotes_languages))
1004                 h_quotes_language = "french";
1005         // german
1006         else if (is_known(h_language, known_german_quotes_languages))
1007                 h_quotes_language = "german";
1008         // polish
1009         else if (is_known(h_language, known_polish_quotes_languages))
1010                 h_quotes_language = "polish";
1011         // swedish
1012         else if (is_known(h_language, known_swedish_quotes_languages))
1013                 h_quotes_language = "swedish";
1014         //english
1015         else if (is_known(h_language, known_english_quotes_languages))
1016                 h_quotes_language = "english";
1017
1018         if (contains(h_float_placement, "H"))
1019                 registerAutomaticallyLoadedPackage("float");
1020         if (h_spacing != "single" && h_spacing != "default")
1021                 registerAutomaticallyLoadedPackage("setspace");
1022         if (h_use_packages["amsmath"] == "2") {
1023                 // amsbsy and amstext are already provided by amsmath
1024                 registerAutomaticallyLoadedPackage("amsbsy");
1025                 registerAutomaticallyLoadedPackage("amstext");
1026         }
1027
1028         // output the LyX file settings
1029         os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
1030            << "\\lyxformat " << LYX_FORMAT << '\n'
1031            << "\\begin_document\n"
1032            << "\\begin_header\n"
1033            << "\\textclass " << h_textclass << "\n";
1034         string const raw = subdoc ? empty_string() : h_preamble.str();
1035         if (!raw.empty()) {
1036                 os << "\\begin_preamble\n";
1037                 for (string::size_type i = 0; i < raw.size(); ++i) {
1038                         if (raw[i] == package_beg_sep) {
1039                                 // Here follows some package loading code that
1040                                 // must be skipped if the package is loaded
1041                                 // automatically.
1042                                 string::size_type j = raw.find(package_mid_sep, i);
1043                                 if (j == string::npos)
1044                                         return false;
1045                                 string::size_type k = raw.find(package_end_sep, j);
1046                                 if (k == string::npos)
1047                                         return false;
1048                                 string const package = raw.substr(i + 1, j - i - 1);
1049                                 string const replacement = raw.substr(j + 1, k - j - 1);
1050                                 if (auto_packages.find(package) == auto_packages.end())
1051                                         os << replacement;
1052                                 i = k;
1053                         } else
1054                                 os.put(raw[i]);
1055                 }
1056                 os << "\n\\end_preamble\n";
1057         }
1058         if (!h_options.empty())
1059                 os << "\\options " << h_options << "\n";
1060         os << "\\use_default_options " << h_use_default_options << "\n";
1061         if (!used_modules.empty()) {
1062                 os << "\\begin_modules\n";
1063                 vector<string>::const_iterator const end = used_modules.end();
1064                 vector<string>::const_iterator it = used_modules.begin();
1065                 for (; it != end; ++it)
1066                         os << *it << '\n';
1067                 os << "\\end_modules\n";
1068         }
1069         os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1070            << "\\language " << h_language << "\n"
1071            << "\\language_package " << h_language_package << "\n"
1072            << "\\inputencoding " << h_inputencoding << "\n"
1073            << "\\fontencoding " << h_fontencoding << "\n"
1074            << "\\font_roman " << h_font_roman << "\n"
1075            << "\\font_sans " << h_font_sans << "\n"
1076            << "\\font_typewriter " << h_font_typewriter << "\n"
1077            << "\\font_math " << h_font_math << "\n"
1078            << "\\font_default_family " << h_font_default_family << "\n"
1079            << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
1080            << "\\font_sc " << h_font_sc << "\n"
1081            << "\\font_osf " << h_font_osf << "\n"
1082            << "\\font_sf_scale " << h_font_sf_scale << "\n"
1083            << "\\font_tt_scale " << h_font_tt_scale << '\n';
1084         if (!h_font_cjk.empty())
1085                 os << "\\font_cjk " << h_font_cjk << '\n';
1086         os << "\\graphics " << h_graphics << '\n'
1087            << "\\default_output_format " << h_default_output_format << "\n"
1088            << "\\output_sync " << h_output_sync << "\n";
1089         if (h_output_sync == "1")
1090                 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1091         os << "\\bibtex_command " << h_bibtex_command << "\n"
1092            << "\\index_command " << h_index_command << "\n";
1093         if (!h_float_placement.empty())
1094                 os << "\\float_placement " << h_float_placement << "\n";
1095         os << "\\paperfontsize " << h_paperfontsize << "\n"
1096            << "\\spacing " << h_spacing << "\n"
1097            << "\\use_hyperref " << h_use_hyperref << '\n';
1098         if (h_use_hyperref == "true") {
1099                 if (!h_pdf_title.empty())
1100                         os << "\\pdf_title \"" << h_pdf_title << "\"\n";
1101                 if (!h_pdf_author.empty())
1102                         os << "\\pdf_author \"" << h_pdf_author << "\"\n";
1103                 if (!h_pdf_subject.empty())
1104                         os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
1105                 if (!h_pdf_keywords.empty())
1106                         os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
1107                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1108                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1109                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1110                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1111                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1112                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1113                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1114                       "\\pdf_backref " << h_pdf_backref << "\n"
1115                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1116                 if (!h_pdf_pagemode.empty())
1117                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1118                 if (!h_pdf_quoted_options.empty())
1119                         os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
1120         }
1121         os << "\\papersize " << h_papersize << "\n"
1122            << "\\use_geometry " << h_use_geometry << '\n';
1123         for (map<string, string>::const_iterator it = h_use_packages.begin();
1124              it != h_use_packages.end(); ++it)
1125                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1126         os << "\\cite_engine " << h_cite_engine << '\n'
1127            << "\\cite_engine_type " << h_cite_engine_type << '\n'
1128            << "\\biblio_style " << h_biblio_style << "\n"
1129            << "\\use_bibtopic " << h_use_bibtopic << "\n"
1130            << "\\use_indices " << h_use_indices << "\n"
1131            << "\\paperorientation " << h_paperorientation << '\n'
1132            << "\\suppress_date " << h_suppress_date << '\n'
1133            << "\\justification " << h_justification << '\n'
1134            << "\\use_refstyle " << h_use_refstyle << '\n';
1135         if (!h_fontcolor.empty())
1136                 os << "\\fontcolor " << h_fontcolor << '\n';
1137         if (!h_notefontcolor.empty())
1138                 os << "\\notefontcolor " << h_notefontcolor << '\n';
1139         if (!h_backgroundcolor.empty())
1140                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1141         if (!h_boxbgcolor.empty())
1142                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1143         os << "\\index " << h_index << '\n'
1144            << "\\shortcut " << h_shortcut << '\n'
1145            << "\\color " << h_color << '\n'
1146            << "\\end_index\n";
1147         os << h_margins
1148            << "\\secnumdepth " << h_secnumdepth << "\n"
1149            << "\\tocdepth " << h_tocdepth << "\n"
1150            << "\\paragraph_separation " << h_paragraph_separation << "\n";
1151         if (h_paragraph_separation == "skip")
1152                 os << "\\defskip " << h_defskip << "\n";
1153         else
1154                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1155         os << "\\quotes_language " << h_quotes_language << "\n"
1156            << "\\papercolumns " << h_papercolumns << "\n"
1157            << "\\papersides " << h_papersides << "\n"
1158            << "\\paperpagestyle " << h_paperpagestyle << "\n";
1159         if (!h_listings_params.empty())
1160                 os << "\\listings_params " << h_listings_params << "\n";
1161         os << "\\tracking_changes " << h_tracking_changes << "\n"
1162            << "\\output_changes " << h_output_changes << "\n"
1163            << "\\html_math_output " << h_html_math_output << "\n"
1164            << "\\html_css_as_file " << h_html_css_as_file << "\n"
1165            << "\\html_be_strict " << h_html_be_strict << "\n"
1166            << authors_
1167            << "\\end_header\n\n"
1168            << "\\begin_body\n";
1169         return true;
1170 }
1171
1172
1173 void Preamble::parse(Parser & p, string const & forceclass,
1174                      TeX2LyXDocClass & tc)
1175 {
1176         // initialize fixed types
1177         special_columns['D'] = 3;
1178         bool is_full_document = false;
1179         bool is_lyx_file = false;
1180         bool in_lyx_preamble = false;
1181
1182         // determine whether this is a full document or a fragment for inclusion
1183         while (p.good()) {
1184                 Token const & t = p.get_token();
1185
1186                 if (t.cat() == catEscape && t.cs() == "documentclass") {
1187                         is_full_document = true;
1188                         break;
1189                 }
1190         }
1191         p.reset();
1192
1193         while (is_full_document && p.good()) {
1194                 Token const & t = p.get_token();
1195
1196 #ifdef FILEDEBUG
1197                 cerr << "t: " << t << "\n";
1198 #endif
1199
1200                 //
1201                 // cat codes
1202                 //
1203                 if (!in_lyx_preamble &&
1204                     (t.cat() == catLetter ||
1205                      t.cat() == catSuper ||
1206                      t.cat() == catSub ||
1207                      t.cat() == catOther ||
1208                      t.cat() == catMath ||
1209                      t.cat() == catActive ||
1210                      t.cat() == catBegin ||
1211                      t.cat() == catEnd ||
1212                      t.cat() == catAlign ||
1213                      t.cat() == catParameter))
1214                         h_preamble << t.cs();
1215
1216                 else if (!in_lyx_preamble &&
1217                          (t.cat() == catSpace || t.cat() == catNewline))
1218                         h_preamble << t.asInput();
1219
1220                 else if (t.cat() == catComment) {
1221                         static regex const islyxfile("%% LyX .* created this file");
1222                         static regex const usercommands("User specified LaTeX commands");
1223
1224                         string const comment = t.asInput();
1225
1226                         // magically switch encoding default if it looks like XeLaTeX
1227                         static string const magicXeLaTeX =
1228                                 "% This document must be compiled with XeLaTeX ";
1229                         if (comment.size() > magicXeLaTeX.size()
1230                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1231                                   && h_inputencoding == "auto") {
1232                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
1233                                 h_inputencoding = "utf8";
1234                         }
1235                         smatch sub;
1236                         if (regex_search(comment, sub, islyxfile)) {
1237                                 is_lyx_file = true;
1238                                 in_lyx_preamble = true;
1239                         } else if (is_lyx_file
1240                                    && regex_search(comment, sub, usercommands))
1241                                 in_lyx_preamble = false;
1242                         else if (!in_lyx_preamble)
1243                                 h_preamble << t.asInput();
1244                 }
1245
1246                 else if (t.cs() == "pagestyle")
1247                         h_paperpagestyle = p.verbatim_item();
1248
1249                 else if (t.cs() == "setdefaultlanguage") {
1250                         xetex = true;
1251                         // We don't yet care about non-language variant options
1252                         // because LyX doesn't support this yet, see bug #8214
1253                         if (p.hasOpt()) {
1254                                 string langopts = p.getOpt();
1255                                 // check if the option contains a variant, if yes, extract it
1256                                 string::size_type pos_var = langopts.find("variant");
1257                                 string::size_type i = langopts.find(',', pos_var);
1258                                 string::size_type k = langopts.find('=', pos_var);
1259                                 if (pos_var != string::npos){
1260                                         string variant;
1261                                         if (i == string::npos)
1262                                                 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1263                                         else
1264                                                 variant = langopts.substr(k + 1, i - k - 1);
1265                                         h_language = variant;
1266                                 }
1267                                 p.verbatim_item();
1268                         } else
1269                                 h_language = p.verbatim_item();
1270                         //finally translate the poyglossia name to a LyX name
1271                         h_language = polyglossia2lyx(h_language);
1272                 }
1273
1274                 else if (t.cs() == "setotherlanguage") {
1275                         // We don't yet care about the option because LyX doesn't
1276                         // support this yet, see bug #8214
1277                         p.hasOpt() ? p.getOpt() : string();
1278                         p.verbatim_item();
1279                 }
1280
1281                 else if (t.cs() == "setmainfont") {
1282                         // we don't care about the option
1283                         p.hasOpt() ? p.getOpt() : string();
1284                         h_font_roman = p.getArg('{', '}');
1285                 }
1286
1287                 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1288                         // LyX currently only supports the scale option
1289                         string scale;
1290                         if (p.hasOpt()) {
1291                                 string fontopts = p.getArg('[', ']');
1292                                 // check if the option contains a scaling, if yes, extract it
1293                                 string::size_type pos = fontopts.find("Scale");
1294                                 if (pos != string::npos) {
1295                                         string::size_type i = fontopts.find(',', pos);
1296                                         if (i == string::npos)
1297                                                 scale_as_percentage(fontopts.substr(pos + 1), scale);
1298                                         else
1299                                                 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1300                                 }
1301                         }
1302                         if (t.cs() == "setsansfont") {
1303                                 if (!scale.empty())
1304                                         h_font_sf_scale = scale;
1305                                 h_font_sans = p.getArg('{', '}');
1306                         } else {
1307                                 if (!scale.empty())
1308                                         h_font_tt_scale = scale;
1309                                 h_font_typewriter = p.getArg('{', '}');
1310                         }
1311                 }
1312
1313                 else if (t.cs() == "date") {
1314                         string argument = p.getArg('{', '}');
1315                         if (argument.empty())
1316                                 h_suppress_date = "true";
1317                         else
1318                                 h_preamble << t.asInput() << '{' << argument << '}';
1319                 }
1320
1321                 else if (t.cs() == "color") {
1322                         string const space =
1323                                 (p.hasOpt() ? p.getOpt() : string());
1324                         string argument = p.getArg('{', '}');
1325                         // check the case that a standard color is used
1326                         if (space.empty() && is_known(argument, known_basic_colors)) {
1327                                 h_fontcolor = rgbcolor2code(argument);
1328                                 preamble.registerAutomaticallyLoadedPackage("color");
1329                         } else if (space.empty() && argument == "document_fontcolor")
1330                                 preamble.registerAutomaticallyLoadedPackage("color");
1331                         // check the case that LyX's document_fontcolor is defined
1332                         // but not used for \color
1333                         else {
1334                                 h_preamble << t.asInput();
1335                                 if (!space.empty())
1336                                         h_preamble << space;
1337                                 h_preamble << '{' << argument << '}';
1338                                 // the color might already be set because \definecolor
1339                                 // is parsed before this
1340                                 h_fontcolor = "";
1341                         }
1342                 }
1343
1344                 else if (t.cs() == "pagecolor") {
1345                         string argument = p.getArg('{', '}');
1346                         // check the case that a standard color is used
1347                         if (is_known(argument, known_basic_colors)) {
1348                                 h_backgroundcolor = rgbcolor2code(argument);
1349                         } else if (argument == "page_backgroundcolor")
1350                                 preamble.registerAutomaticallyLoadedPackage("color");
1351                         // check the case that LyX's page_backgroundcolor is defined
1352                         // but not used for \pagecolor
1353                         else {
1354                                 h_preamble << t.asInput() << '{' << argument << '}';
1355                                 // the color might already be set because \definecolor
1356                                 // is parsed before this
1357                                 h_backgroundcolor = "";
1358                         }
1359                 }
1360
1361                 else if (t.cs() == "makeatletter") {
1362                         // LyX takes care of this
1363                         p.setCatcode('@', catLetter);
1364                 }
1365
1366                 else if (t.cs() == "makeatother") {
1367                         // LyX takes care of this
1368                         p.setCatcode('@', catOther);
1369                 }
1370
1371                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1372                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1373                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
1374                                 || t.cs() == "DeclareRobustCommand"
1375                       || t.cs() == "DeclareRobustCommandx"
1376                                 || t.cs() == "ProvideTextCommandDefault"
1377                                 || t.cs() == "DeclareMathAccent") {
1378                         bool star = false;
1379                         if (p.next_token().character() == '*') {
1380                                 p.get_token();
1381                                 star = true;
1382                         }
1383                         string const name = p.verbatim_item();
1384                         string const opt1 = p.getFullOpt();
1385                         string const opt2 = p.getFullOpt();
1386                         string const body = p.verbatim_item();
1387                         // store the in_lyx_preamble setting
1388                         bool const was_in_lyx_preamble = in_lyx_preamble;
1389                         // font settings
1390                         if (name == "\\rmdefault")
1391                                 if (is_known(body, known_roman_fonts)) {
1392                                         h_font_roman = body;
1393                                         p.skip_spaces();
1394                                         in_lyx_preamble = true;
1395                                 }
1396                         if (name == "\\sfdefault")
1397                                 if (is_known(body, known_sans_fonts)) {
1398                                         h_font_sans = body;
1399                                         p.skip_spaces();
1400                                         in_lyx_preamble = true;
1401                                 }
1402                         if (name == "\\ttdefault")
1403                                 if (is_known(body, known_typewriter_fonts)) {
1404                                         h_font_typewriter = body;
1405                                         p.skip_spaces();
1406                                         in_lyx_preamble = true;
1407                                 }
1408                         if (name == "\\familydefault") {
1409                                 string family = body;
1410                                 // remove leading "\"
1411                                 h_font_default_family = family.erase(0,1);
1412                                 p.skip_spaces();
1413                                 in_lyx_preamble = true;
1414                         }
1415
1416                         if (name == "\\bfdefault")
1417                                 // LyX re-adds this if a kurier font is used
1418                                 if (is_known(h_font_sans, known_kurier_fonts) && body == "b") {
1419                                         p.skip_spaces();
1420                                         in_lyx_preamble = true;
1421                                 }
1422
1423                         // remove the lyxdot definition that is re-added by LyX
1424                         // if necessary
1425                         if (name == "\\lyxdot") {
1426                                 p.skip_spaces();
1427                                 in_lyx_preamble = true;
1428                         }
1429
1430                         // Add the command to the known commands
1431                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1432
1433                         // only non-lyxspecific stuff
1434                         if (!in_lyx_preamble) {
1435                                 ostringstream ss;
1436                                 ss << '\\' << t.cs();
1437                                 if (star)
1438                                         ss << '*';
1439                                 ss << '{' << name << '}' << opt1 << opt2
1440                                    << '{' << body << "}";
1441                                 h_preamble << ss.str();
1442 /*
1443                                 ostream & out = in_preamble ? h_preamble : os;
1444                                 out << "\\" << t.cs() << "{" << name << "}"
1445                                     << opts << "{" << body << "}";
1446 */
1447                         }
1448                         // restore the in_lyx_preamble setting
1449                         in_lyx_preamble = was_in_lyx_preamble;
1450                 }
1451
1452                 else if (t.cs() == "edef"){
1453                         // we only support this for kurier fonts
1454                         string const command = p.next_token().asInput();
1455                         p.get_token();
1456                         if (command == "\\sfdefault") {
1457                                 p.getArg('{', '}');
1458                                 if (h_font_sans == "kurier")
1459                                         h_font_sans = "kurier-condensed";
1460                                 if (h_font_sans == "kurierl")
1461                                         h_font_sans = "kurier-light-condensed";
1462                                 p.skip_spaces();
1463                         }
1464                         else
1465                                 h_preamble << "\\edef" << command << "{" << p.getArg('{', '}') << "}\n";
1466                 }
1467
1468                 else if (t.cs() == "documentclass") {
1469                         vector<string>::iterator it;
1470                         vector<string> opts = split_options(p.getArg('[', ']'));
1471                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1472                         delete_opt(opts, known_fontsizes);
1473                         // delete "pt" at the end
1474                         string::size_type i = h_paperfontsize.find("pt");
1475                         if (i != string::npos)
1476                                 h_paperfontsize.erase(i);
1477                         // The documentclass options are always parsed before the options
1478                         // of the babel call so that a language cannot overwrite the babel
1479                         // options.
1480                         handle_opt(opts, known_languages, h_language);
1481                         delete_opt(opts, known_languages);
1482
1483                         // paper orientation
1484                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1485                                 h_paperorientation = "landscape";
1486                                 opts.erase(it);
1487                         }
1488                         // paper sides
1489                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1490                                  != opts.end()) {
1491                                 h_papersides = "1";
1492                                 opts.erase(it);
1493                         }
1494                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1495                                  != opts.end()) {
1496                                 h_papersides = "2";
1497                                 opts.erase(it);
1498                         }
1499                         // paper columns
1500                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1501                                  != opts.end()) {
1502                                 h_papercolumns = "1";
1503                                 opts.erase(it);
1504                         }
1505                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1506                                  != opts.end()) {
1507                                 h_papercolumns = "2";
1508                                 opts.erase(it);
1509                         }
1510                         // paper sizes
1511                         // some size options are known to any document classes, other sizes
1512                         // are handled by the \geometry command of the geometry package
1513                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1514                         delete_opt(opts, known_class_paper_sizes);
1515                         // the remaining options
1516                         h_options = join(opts, ",");
1517                         // FIXME This does not work for classes that have a
1518                         //       different name in LyX than in LaTeX
1519                         h_textclass = p.getArg('{', '}');
1520                         p.skip_spaces();
1521                 }
1522
1523                 else if (t.cs() == "usepackage") {
1524                         string const options = p.getArg('[', ']');
1525                         string const name = p.getArg('{', '}');
1526                         vector<string> vecnames;
1527                         split(name, vecnames, ',');
1528                         vector<string>::const_iterator it  = vecnames.begin();
1529                         vector<string>::const_iterator end = vecnames.end();
1530                         for (; it != end; ++it)
1531                                 handle_package(p, trimSpaceAndEol(*it), options,
1532                                                in_lyx_preamble);
1533                 }
1534
1535                 else if (t.cs() == "inputencoding") {
1536                         string const encoding = p.getArg('{','}');
1537                         h_inputencoding = encoding;
1538                         p.setEncoding(encoding, Encoding::inputenc);
1539                 }
1540
1541                 else if (t.cs() == "newenvironment") {
1542                         string const name = p.getArg('{', '}');
1543                         string const opt1 = p.getFullOpt();
1544                         string const opt2 = p.getFullOpt();
1545                         string const beg = p.verbatim_item();
1546                         string const end = p.verbatim_item();
1547                         if (!in_lyx_preamble) {
1548                                 h_preamble << "\\newenvironment{" << name
1549                                            << '}' << opt1 << opt2 << '{'
1550                                            << beg << "}{" << end << '}';
1551                         }
1552                         add_known_environment(name, opt1, !opt2.empty(),
1553                                               from_utf8(beg), from_utf8(end));
1554
1555                 }
1556
1557                 else if (t.cs() == "newtheorem") {
1558                         string const name = p.getArg('{', '}');
1559                         string const opt1 = p.getFullOpt();
1560                         string const opt2 = p.getFullOpt();
1561                         string const body = p.verbatim_item();
1562                         string const opt3 = p.getFullOpt();
1563
1564                         add_known_theorem(name, opt1, !opt2.empty(),
1565                                 from_utf8("\\newtheorem{" + name + '}' +
1566                                           opt1 + opt2 + '{' + body + '}' + opt3));
1567
1568                         if (!in_lyx_preamble)
1569                                 h_preamble << "\\newtheorem{" << name << '}'
1570                                            << opt1 << opt2 << '{' << '}' << opt3;
1571                 }
1572
1573                 else if (t.cs() == "def") {
1574                         string name = p.get_token().cs();
1575                         // In fact, name may be more than the name:
1576                         // In the test case of bug 8116
1577                         // name == "csname SF@gobble@opt \endcsname".
1578                         // Therefore, we need to use asInput() instead of cs().
1579                         while (p.next_token().cat() != catBegin)
1580                                 name += p.get_token().asInput();
1581                         if (!in_lyx_preamble)
1582                                 h_preamble << "\\def\\" << name << '{'
1583                                            << p.verbatim_item() << "}";
1584                 }
1585
1586                 else if (t.cs() == "newcolumntype") {
1587                         string const name = p.getArg('{', '}');
1588                         trimSpaceAndEol(name);
1589                         int nargs = 0;
1590                         string opts = p.getOpt();
1591                         if (!opts.empty()) {
1592                                 istringstream is(string(opts, 1));
1593                                 is >> nargs;
1594                         }
1595                         special_columns[name[0]] = nargs;
1596                         h_preamble << "\\newcolumntype{" << name << "}";
1597                         if (nargs)
1598                                 h_preamble << "[" << nargs << "]";
1599                         h_preamble << "{" << p.verbatim_item() << "}";
1600                 }
1601
1602                 else if (t.cs() == "setcounter") {
1603                         string const name = p.getArg('{', '}');
1604                         string const content = p.getArg('{', '}');
1605                         if (name == "secnumdepth")
1606                                 h_secnumdepth = content;
1607                         else if (name == "tocdepth")
1608                                 h_tocdepth = content;
1609                         else
1610                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1611                 }
1612
1613                 else if (t.cs() == "setlength") {
1614                         string const name = p.verbatim_item();
1615                         string const content = p.verbatim_item();
1616                         // the paragraphs are only not indented when \parindent is set to zero
1617                         if (name == "\\parindent" && content != "") {
1618                                 if (content[0] == '0')
1619                                         h_paragraph_separation = "skip";
1620                                 else
1621                                         h_paragraph_indentation = translate_len(content);
1622                         } else if (name == "\\parskip") {
1623                                 if (content == "\\smallskipamount")
1624                                         h_defskip = "smallskip";
1625                                 else if (content == "\\medskipamount")
1626                                         h_defskip = "medskip";
1627                                 else if (content == "\\bigskipamount")
1628                                         h_defskip = "bigskip";
1629                                 else
1630                                         h_defskip = content;
1631                         } else
1632                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1633                 }
1634
1635                 else if (t.cs() == "onehalfspacing")
1636                         h_spacing = "onehalf";
1637
1638                 else if (t.cs() == "doublespacing")
1639                         h_spacing = "double";
1640
1641                 else if (t.cs() == "setstretch")
1642                         h_spacing = "other " + p.verbatim_item();
1643
1644                 else if (t.cs() == "synctex") {
1645                         // the scheme is \synctex=value
1646                         // where value can only be "1" or "-1"
1647                         h_output_sync = "1";
1648                         // there can be any character behind the value (e.g. a linebreak or a '\'
1649                         // therefore we extract it char by char
1650                         p.get_token();
1651                         string value = p.get_token().asInput();
1652                         if (value == "-")
1653                                 value += p.get_token().asInput();
1654                         h_output_sync_macro = "\\synctex=" + value;
1655                 }
1656
1657                 else if (t.cs() == "begin") {
1658                         string const name = p.getArg('{', '}');
1659                         if (name == "document")
1660                                 break;
1661                         h_preamble << "\\begin{" << name << "}";
1662                 }
1663
1664                 else if (t.cs() == "geometry") {
1665                         vector<string> opts = split_options(p.getArg('{', '}'));
1666                         handle_geometry(opts);
1667                 }
1668
1669                 else if (t.cs() == "definecolor") {
1670                         string const color = p.getArg('{', '}');
1671                         string const space = p.getArg('{', '}');
1672                         string const value = p.getArg('{', '}');
1673                         if (color == "document_fontcolor" && space == "rgb") {
1674                                 RGBColor c(RGBColorFromLaTeX(value));
1675                                 h_fontcolor = X11hexname(c);
1676                         } else if (color == "note_fontcolor" && space == "rgb") {
1677                                 RGBColor c(RGBColorFromLaTeX(value));
1678                                 h_notefontcolor = X11hexname(c);
1679                         } else if (color == "page_backgroundcolor" && space == "rgb") {
1680                                 RGBColor c(RGBColorFromLaTeX(value));
1681                                 h_backgroundcolor = X11hexname(c);
1682                         } else if (color == "shadecolor" && space == "rgb") {
1683                                 RGBColor c(RGBColorFromLaTeX(value));
1684                                 h_boxbgcolor = X11hexname(c);
1685                         } else {
1686                                 h_preamble << "\\definecolor{" << color
1687                                            << "}{" << space << "}{" << value
1688                                            << '}';
1689                         }
1690                 }
1691
1692                 else if (t.cs() == "bibliographystyle")
1693                         h_biblio_style = p.verbatim_item();
1694
1695                 else if (t.cs() == "jurabibsetup") {
1696                         // FIXME p.getArg('{', '}') is most probably wrong (it
1697                         //       does not handle nested braces).
1698                         //       Use p.verbatim_item() instead.
1699                         vector<string> jurabibsetup =
1700                                 split_options(p.getArg('{', '}'));
1701                         // add jurabibsetup to the jurabib package options
1702                         add_package("jurabib", jurabibsetup);
1703                         if (!jurabibsetup.empty()) {
1704                                 h_preamble << "\\jurabibsetup{"
1705                                            << join(jurabibsetup, ",") << '}';
1706                         }
1707                 }
1708
1709                 else if (t.cs() == "hypersetup") {
1710                         vector<string> hypersetup =
1711                                 split_options(p.verbatim_item());
1712                         // add hypersetup to the hyperref package options
1713                         handle_hyperref(hypersetup);
1714                         if (!hypersetup.empty()) {
1715                                 h_preamble << "\\hypersetup{"
1716                                            << join(hypersetup, ",") << '}';
1717                         }
1718                 }
1719
1720                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1721                         // prevent misparsing of \usepackage if it is used
1722                         // as an argument (see e.g. our own output of
1723                         // \@ifundefined above)
1724                         string const arg1 = p.verbatim_item();
1725                         string const arg2 = p.verbatim_item();
1726                         string const arg3 = p.verbatim_item();
1727                         // test case \@ifundefined{date}{}{\date{}}
1728                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
1729                             arg2.empty() && arg3 == "\\date{}") {
1730                                 h_suppress_date = "true";
1731                         // older tex2lyx versions did output
1732                         // \@ifundefined{definecolor}{\usepackage{color}}{}
1733                         } else if (t.cs() == "@ifundefined" &&
1734                                    arg1 == "definecolor" &&
1735                                    arg2 == "\\usepackage{color}" &&
1736                                    arg3.empty()) {
1737                                 if (!in_lyx_preamble)
1738                                         h_preamble << package_beg_sep
1739                                                    << "color"
1740                                                    << package_mid_sep
1741                                                    << "\\@ifundefined{definecolor}{color}{}"
1742                                                    << package_end_sep;
1743                         // test for case
1744                         //\@ifundefined{showcaptionsetup}{}{%
1745                         // \PassOptionsToPackage{caption=false}{subfig}}
1746                         // that LyX uses for subfloats
1747                         } else if (t.cs() == "@ifundefined" &&
1748                                    arg1 == "showcaptionsetup" && arg2.empty()
1749                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1750                                 ; // do nothing
1751                         } else if (!in_lyx_preamble) {
1752                                 h_preamble << t.asInput()
1753                                            << '{' << arg1 << '}'
1754                                            << '{' << arg2 << '}'
1755                                            << '{' << arg3 << '}';
1756                         }
1757                 }
1758
1759                 else if (is_known(t.cs(), known_if_commands)) {
1760                         // must not parse anything in conditional code, since
1761                         // LyX would output the parsed contents unconditionally
1762                         if (!in_lyx_preamble)
1763                                 h_preamble << t.asInput();
1764                         handle_if(p, in_lyx_preamble);
1765                 }
1766
1767                 else if (!t.cs().empty() && !in_lyx_preamble)
1768                         h_preamble << '\\' << t.cs();
1769         }
1770
1771         // remove the whitespace
1772         p.skip_spaces();
1773
1774         // Force textclass if the user wanted it
1775         if (!forceclass.empty())
1776                 h_textclass = forceclass;
1777         tc.setName(h_textclass);
1778         if (!tc.load()) {
1779                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1780                 exit(EXIT_FAILURE);
1781         }
1782         if (h_papersides.empty()) {
1783                 ostringstream ss;
1784                 ss << tc.sides();
1785                 h_papersides = ss.str();
1786         }
1787
1788         // If the CJK package is used we cannot set the document language from
1789         // the babel options. Instead, we guess which language is used most
1790         // and set this one.
1791         default_language = h_language;
1792         if (is_full_document &&
1793             (auto_packages.find("CJK") != auto_packages.end() ||
1794              auto_packages.find("CJKutf8") != auto_packages.end())) {
1795                 p.pushPosition();
1796                 h_language = guessLanguage(p, default_language);
1797                 p.popPosition();
1798         }
1799 }
1800
1801
1802 string babel2lyx(string const & language)
1803 {
1804         char const * const * where = is_known(language, known_languages);
1805         if (where)
1806                 return known_coded_languages[where - known_languages];
1807         return language;
1808 }
1809
1810
1811 string Preamble::polyglossia2lyx(string const & language)
1812 {
1813         char const * const * where = is_known(language, polyglossia_languages);
1814         if (where)
1815                 return coded_polyglossia_languages[where - polyglossia_languages];
1816         return language;
1817 }
1818
1819
1820 string rgbcolor2code(string const & name)
1821 {
1822         char const * const * where = is_known(name, known_basic_colors);
1823         if (where) {
1824                 // "red", "green" etc
1825                 return known_basic_color_codes[where - known_basic_colors];
1826         }
1827         // "255,0,0", "0,255,0" etc
1828         RGBColor c(RGBColorFromLaTeX(name));
1829         return X11hexname(c);
1830 }
1831
1832 // }])
1833
1834
1835 } // namespace lyx