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