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