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