]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
get rid of the modules_placeholder hack
[lyx.git] / src / tex2lyx / Preamble.cpp
1 /**
2  * \file Preamble.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  * \author Uwe Stöhr
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 // {[(
13
14 #include <config.h>
15
16 #include "Preamble.h"
17 #include "tex2lyx.h"
18
19 #include "LayoutFile.h"
20 #include "Layout.h"
21 #include "Lexer.h"
22 #include "TextClass.h"
23
24 #include "support/convert.h"
25 #include "support/FileName.h"
26 #include "support/filetools.h"
27 #include "support/lstrings.h"
28
29 #include "support/regex.h"
30
31 #include <algorithm>
32 #include <iostream>
33
34 using namespace std;
35 using namespace lyx::support;
36
37
38 namespace lyx {
39
40 // special columntypes
41 extern map<char, int> special_columns;
42
43 Preamble preamble;
44
45 namespace {
46
47 //add this to known_languages when updating to lyxformat 266:
48 // "armenian" (needs special handling since not supported by standard babel)
49 //add these to known_languages when updating to lyxformat 268:
50 //"chinese-simplified", "chinese-traditional", "japanese", "korean"
51 // Both changes require first that support for non-babel languages (CJK,
52 // armtex) is added.
53 /**
54  * known babel language names (including synonyms)
55  * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
56  * not yet supported by LyX: kurmanji
57  * please keep this in sync with known_coded_languages line by line!
58  */
59 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
60 "american", "arabic", "arabtex", "austrian", "bahasa", "bahasai", "bahasam",
61 "basque", "belarusian", "brazil", "brazilian", "breton", "british", "bulgarian",
62 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
63 "english", "esperanto", "estonian", "farsi", "finnish", "francais", "french",
64 "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb", "greek",
65 "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
66 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
67 "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian", "newzealand",
68 "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
69 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
70 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
71 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
72 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
73 0};
74
75 /**
76  * the same as known_languages with .lyx names
77  * please keep this in sync with known_languages line by line!
78  */
79 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
80 "american", "arabic_arabi", "arabic_arabtex", "austrian", "bahasa", "bahasa", "bahasam",
81 "basque", "belarusian", "brazilian", "brazilian", "breton", "british", "bulgarian",
82 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
83 "english", "esperanto", "estonian", "farsi", "finnish", "french", "french",
84 "french", "french", "french", "galician", "german", "german", "greek",
85 "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
86 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
87 "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian", "english",
88 "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
89 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
90 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
91 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
92 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
93 0};
94
95 /// languages with english quotes (.lyx names)
96 const char * const known_english_quotes_languages[] = {"american", "bahasa",
97 "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
98 "esperanto", "hebrew", "irish", "korean", "portuguese", "scottish", "thai", 0};
99
100 /// languages with french quotes (.lyx names)
101 const char * const known_french_quotes_languages[] = {"albanian",
102 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
103 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
104 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
105 "vietnamese", 0};
106
107 /// languages with german quotes (.lyx names)
108 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
109 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
110 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
111
112 /// languages with polish quotes (.lyx names)
113 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
114 "dutch", "estonian", "magyar", "polish", "romanian", 0};
115
116 /// languages with swedish quotes (.lyx names)
117 const char * const known_swedish_quotes_languages[] = {"finnish",
118 "swedish", 0};
119
120 /// known language packages from the times before babel
121 const char * const known_old_language_packages[] = {"french", "frenchle",
122 "frenchpro", "german", "ngerman", "pmfrench", 0};
123
124 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
125
126 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
127 "ccfonts", "chancery", "charter", "cmr", "fourier", "lmodern", "mathpazo",
128 "mathptmx", "newcent", "utopia", 0};
129
130 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
131 "helvet", "lmss", 0};
132
133 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
134 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
135 "newcent", 0};
136
137 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
138 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
139 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
140 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
141 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
142
143 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
144 "executivepaper", "legalpaper", "letterpaper", 0};
145
146 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin", 
147 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
148
149 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
150 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
151 "columnsep", 0};
152
153 /// commands that can start an \if...\else...\endif sequence
154 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
155 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
156 "ifsidecap", "ifupgreek", 0};
157
158 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
159 "magenta", "red", "white", "yellow", 0};
160
161 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00",
162 "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
163
164 /// conditional commands with three arguments like \@ifundefined{}{}{}
165 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
166 0};
167
168
169 // returns true if at least one of the options in what has been found
170 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
171 {
172         if (opts.empty())
173                 return false;
174
175         bool found = false;
176         // the last language option is the document language (for babel and LyX)
177         // the last size option is the document font size
178         vector<string>::iterator it;
179         vector<string>::iterator position = opts.begin();
180         for (; *what; ++what) {
181                 it = find(opts.begin(), opts.end(), *what);
182                 if (it != opts.end()) {
183                         if (it >= position) {
184                                 found = true;
185                                 target = *what;
186                                 position = it;
187                         }
188                 }
189         }
190         return found;
191 }
192
193
194 void delete_opt(vector<string> & opts, char const * const * what)
195 {
196         if (opts.empty())
197                 return;
198
199         // remove found options from the list
200         // do this after handle_opt to avoid potential memory leaks
201         vector<string>::iterator it;
202         for (; *what; ++what) {
203                 it = find(opts.begin(), opts.end(), *what);
204                 if (it != opts.end())
205                         opts.erase(it);
206         }
207 }
208
209
210 /*!
211  * Split a package options string (keyval format) into a vector.
212  * Example input:
213  *   authorformat=smallcaps,
214  *   commabeforerest,
215  *   titleformat=colonsep,
216  *   bibformat={tabular,ibidem,numbered}
217  */
218 vector<string> split_options(string const & input)
219 {
220         vector<string> options;
221         string option;
222         Parser p(input);
223         while (p.good()) {
224                 Token const & t = p.get_token();
225                 if (t.asInput() == ",") {
226                         options.push_back(trimSpaceAndEol(option));
227                         option.erase();
228                 } else if (t.asInput() == "=") {
229                         option += '=';
230                         p.skip_spaces(true);
231                         if (p.next_token().asInput() == "{")
232                                 option += '{' + p.getArg('{', '}') + '}';
233                 } else if (t.cat() != catSpace)
234                         option += t.asInput();
235         }
236
237         if (!option.empty())
238                 options.push_back(trimSpaceAndEol(option));
239
240         return options;
241 }
242
243
244 /*!
245  * Retrieve a keyval option "name={value with=sign}" named \p name from
246  * \p options and return the value.
247  * The found option is also removed from \p options.
248  */
249 string process_keyval_opt(vector<string> & options, string name)
250 {
251         for (size_t i = 0; i < options.size(); ++i) {
252                 vector<string> option;
253                 split(options[i], option, '=');
254                 if (option.size() < 2)
255                         continue;
256                 if (option[0] == name) {
257                         options.erase(options.begin() + i);
258                         option.erase(option.begin());
259                         return join(option, "=");
260                 }
261         }
262         return "";
263 }
264
265 } // anonymous namespace
266
267
268 bool Preamble::indentParagraphs() const
269 {
270         return h_paragraph_separation == "indent";
271 }
272
273
274 bool Preamble::isPackageUsed(string const & package) const
275 {
276         return used_packages.find(package) != used_packages.end();
277 }
278
279
280 vector<string> Preamble::getPackageOptions(string const & package) const
281 {
282         map<string, vector<string> >::const_iterator it = used_packages.find(package);
283         if (it != used_packages.end())
284                 return it->second;
285         return vector<string>();
286 }
287
288
289 void Preamble::addModule(string const & module)
290 {
291         used_modules.push_back(module);
292 }
293
294
295 void Preamble::suppressDate(bool suppress)
296 {
297         if (suppress)
298                 h_suppress_date = "true";
299         else
300                 h_suppress_date = "false";
301 }
302
303
304 void Preamble::add_package(string const & name, vector<string> & options)
305 {
306         // every package inherits the global options
307         if (used_packages.find(name) == used_packages.end())
308                 used_packages[name] = split_options(h_options);
309
310         vector<string> & v = used_packages[name];
311         v.insert(v.end(), options.begin(), options.end());
312         if (name == "jurabib") {
313                 // Don't output the order argument (see the cite command
314                 // handling code in text.cpp).
315                 vector<string>::iterator end =
316                         remove(options.begin(), options.end(), "natbiborder");
317                 end = remove(options.begin(), end, "jurabiborder");
318                 options.erase(end, options.end());
319         }
320 }
321
322
323 namespace {
324
325 // Given is a string like "scaled=0.9", return 0.9 * 100
326 string const scale_as_percentage(string const & scale)
327 {
328         string::size_type pos = scale.find('=');
329         if (pos != string::npos) {
330                 string value = scale.substr(pos + 1);
331                 if (isStrDbl(value))
332                         return convert<string>(100 * convert<double>(value));
333         }
334         // If the input string didn't match our expectations.
335         // return the default value "100"
336         return "100";
337 }
338
339
340 string remove_braces(string const & value)
341 {
342         if (value.empty())
343                 return value;
344         if (value[0] == '{' && value[value.length()-1] == '}')
345                 return value.substr(1, value.length()-2);
346         return value;
347 }
348
349 } // anonymous namespace
350
351
352 Preamble::Preamble() : one_language(true), ifundefined_color_set(false)
353 {
354         //h_backgroundcolor;
355         //h_boxbgcolor;
356         h_cite_engine             = "basic";
357         h_defskip                 = "medskip";
358         //h_float_placement;
359         //h_fontcolor;
360         h_fontencoding            = "default";
361         h_font_roman              = "default";
362         h_font_sans               = "default";
363         h_font_typewriter         = "default";
364         h_font_default_family     = "default";
365         h_font_sc                 = "false";
366         h_font_osf                = "false";
367         h_font_sf_scale           = "100";
368         h_font_tt_scale           = "100";
369         h_graphics                = "default";
370         h_html_be_strict          = "false";
371         h_html_css_as_file        = "0";
372         h_html_math_output        = "0";
373         h_inputencoding           = "auto";
374         h_language                = "english";
375         h_language_package        = "none";
376         //h_listings_params;
377         //h_margins;
378         //h_notefontcolor;
379         //h_options;
380         h_output_changes          = "false";
381         h_papercolumns            = "1";
382         h_paperfontsize           = "default";
383         h_paperorientation        = "portrait";
384         h_paperpagestyle          = "default";
385         //h_papersides;
386         h_papersize               = "default";
387         h_paragraph_indentation   = "default";
388         h_paragraph_separation    = "indent";
389         //h_pdf_title;
390         //h_pdf_author;
391         //h_pdf_subject;
392         //h_pdf_keywords;
393         h_pdf_bookmarks           = "1";
394         h_pdf_bookmarksnumbered   = "0";
395         h_pdf_bookmarksopen       = "0";
396         h_pdf_bookmarksopenlevel  = "1";
397         h_pdf_breaklinks          = "0";
398         h_pdf_pdfborder           = "0";
399         h_pdf_colorlinks          = "0";
400         h_pdf_backref             = "section";
401         h_pdf_pdfusetitle         = "1";
402         //h_pdf_pagemode;
403         //h_pdf_quoted_options;
404         h_quotes_language         = "english";
405         h_secnumdepth             = "3";
406         h_spacing                 = "single";
407         h_suppress_date           = "false";
408         h_textclass               = "article";
409         h_tocdepth                = "3";
410         h_tracking_changes        = "false";
411         h_use_bibtopic            = "false";
412         h_use_geometry            = "false";
413         h_use_amsmath             = "1";
414         h_use_default_options     = "false";
415         h_use_esint               = "1";
416         h_use_hyperref            = "0";
417         h_use_mhchem              = "0";
418         h_use_mathdots            = "0";
419         h_use_refstyle            = "0";
420         h_use_undertilde          = "0";
421 }
422
423
424 void Preamble::handle_hyperref(vector<string> & options)
425 {
426         // FIXME swallow inputencoding changes that might surround the
427         //       hyperref setup if it was written by LyX
428         h_use_hyperref = "1";
429         // swallow "unicode=true", since LyX does always write that
430         vector<string>::iterator it =
431                 find(options.begin(), options.end(), "unicode=true");
432         if (it != options.end())
433                 options.erase(it);
434         it = find(options.begin(), options.end(), "pdfusetitle");
435         if (it != options.end()) {
436                 h_pdf_pdfusetitle = "1";
437                 options.erase(it);
438         }
439         string bookmarks = process_keyval_opt(options, "bookmarks");
440         if (bookmarks == "true")
441                 h_pdf_bookmarks = "1";
442         else if (bookmarks == "false")
443                 h_pdf_bookmarks = "0";
444         if (h_pdf_bookmarks == "1") {
445                 string bookmarksnumbered =
446                         process_keyval_opt(options, "bookmarksnumbered");
447                 if (bookmarksnumbered == "true")
448                         h_pdf_bookmarksnumbered = "1";
449                 else if (bookmarksnumbered == "false")
450                         h_pdf_bookmarksnumbered = "0";
451                 string bookmarksopen =
452                         process_keyval_opt(options, "bookmarksopen");
453                 if (bookmarksopen == "true")
454                         h_pdf_bookmarksopen = "1";
455                 else if (bookmarksopen == "false")
456                         h_pdf_bookmarksopen = "0";
457                 if (h_pdf_bookmarksopen == "1") {
458                         string bookmarksopenlevel =
459                                 process_keyval_opt(options, "bookmarksopenlevel");
460                         if (!bookmarksopenlevel.empty())
461                                 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
462                 }
463         }
464         string breaklinks = process_keyval_opt(options, "breaklinks");
465         if (breaklinks == "true")
466                 h_pdf_breaklinks = "1";
467         else if (breaklinks == "false")
468                 h_pdf_breaklinks = "0";
469         string pdfborder = process_keyval_opt(options, "pdfborder");
470         if (pdfborder == "{0 0 0}")
471                 h_pdf_pdfborder = "1";
472         else if (pdfborder == "{0 0 1}")
473                 h_pdf_pdfborder = "0";
474         string backref = process_keyval_opt(options, "backref");
475         if (!backref.empty())
476                 h_pdf_backref = backref;
477         string colorlinks = process_keyval_opt(options, "colorlinks");
478         if (colorlinks == "true")
479                 h_pdf_colorlinks = "1";
480         else if (colorlinks == "false")
481                 h_pdf_colorlinks = "0";
482         string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
483         if (!pdfpagemode.empty())
484                 h_pdf_pagemode = pdfpagemode;
485         string pdftitle = process_keyval_opt(options, "pdftitle");
486         if (!pdftitle.empty()) {
487                 h_pdf_title = remove_braces(pdftitle);
488         }
489         string pdfauthor = process_keyval_opt(options, "pdfauthor");
490         if (!pdfauthor.empty()) {
491                 h_pdf_author = remove_braces(pdfauthor);
492         }
493         string pdfsubject = process_keyval_opt(options, "pdfsubject");
494         if (!pdfsubject.empty())
495                 h_pdf_subject = remove_braces(pdfsubject);
496         string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
497         if (!pdfkeywords.empty())
498                 h_pdf_keywords = remove_braces(pdfkeywords);
499         if (!options.empty()) {
500                 if (!h_pdf_quoted_options.empty())
501                         h_pdf_quoted_options += ',';
502                 h_pdf_quoted_options += join(options, ",");
503                 options.clear();
504         }
505 }
506
507
508 void Preamble::handle_package(Parser &p, string const & name,
509                               string const & opts, bool in_lyx_preamble)
510 {
511         vector<string> options = split_options(opts);
512         add_package(name, options);
513         string scale;
514
515         // roman fonts
516         if (is_known(name, known_roman_fonts)) {
517                 h_font_roman = name;
518                 p.skip_spaces();
519         }
520
521         if (name == "fourier") {
522                 h_font_roman = "utopia";
523                 // when font uses real small capitals
524                 if (opts == "expert")
525                         h_font_sc = "true";
526         }
527
528         if (name == "mathpazo")
529                 h_font_roman = "palatino";
530
531         if (name == "mathptmx")
532                 h_font_roman = "times";
533
534         // sansserif fonts
535         if (is_known(name, known_sans_fonts)) {
536                 h_font_sans = name;
537                 if (!opts.empty()) {
538                         scale = opts;
539                         h_font_sf_scale = scale_as_percentage(scale);
540                 }
541         }
542
543         // typewriter fonts
544         if (is_known(name, known_typewriter_fonts)) {
545                 // fourier can be set as roman font _only_
546                 // fourier as typewriter is handled in handling of \ttdefault
547                 if (name != "fourier") {
548                         h_font_typewriter = name;
549                         if (!opts.empty()) {
550                                 scale = opts;
551                                 h_font_tt_scale = scale_as_percentage(scale);
552                         }
553                 }
554         }
555
556         // font uses old-style figure
557         if (name == "eco")
558                 h_font_osf = "true";
559
560         // after the detection and handling of special cases, we can remove the
561         // fonts, otherwise they would appear in the preamble, see bug #7856
562         if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
563                 ||      is_known(name, known_typewriter_fonts))
564                 ;
565
566         else if (name == "amsmath" || name == "amssymb")
567                 h_use_amsmath = "2";
568
569         else if (name == "esint")
570                 h_use_esint = "2";
571
572         else if (name == "mhchem")
573                 h_use_mhchem = "2";
574
575         else if (name == "mathdots")
576                 h_use_mathdots = "2";
577
578         else if (name == "undertilde")
579                 h_use_undertilde = "2";
580
581         else if (name == "babel") {
582                 h_language_package = "default";
583                 // One might think we would have to do nothing if babel is loaded
584                 // without any options to prevent pollution of the preamble with this
585                 // babel call in every roundtrip.
586                 // But the user could have defined babel-specific things afterwards. So
587                 // we need to keep it in the preamble to prevent cases like bug #7861.
588                 if (!opts.empty()) {
589                         // check if more than one option was used - used later for inputenc
590                         // in case inputenc is parsed before babel, set the encoding to auto
591                         if (options.begin() != options.end() - 1)
592                                 one_language = false;
593                         // babel takes the last language of the option of its \usepackage
594                         // call as document language. If there is no such language option, the
595                         // last language in the documentclass options is used.
596                         handle_opt(options, known_languages, h_language);
597                         // If babel is called with options, LyX puts them by default into the
598                         // document class options. This works for most languages, except
599                         // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
600                         // perhaps in future others.
601                         // Therefore keep the babel call as it is as the user might have
602                         // reasons for it.
603                         h_preamble << "\\usepackage[" << opts << "]{babel}\n";
604                         delete_opt(options, known_languages);
605                 }
606                 else
607                         h_preamble << "\\usepackage{babel}\n";                                          
608         }
609
610         else if (name == "fontenc") {
611                 h_fontencoding = getStringFromVector(options, ",");
612                 /* We could do the following for better round trip support,
613                  * but this makes the document less portable, so I skip it:
614                 if (h_fontencoding == lyxrc.fontenc)
615                         h_fontencoding = "global";
616                  */
617                 options.clear();
618         }
619
620         else if (name == "inputenc" || name == "luainputenc") {
621                 // h_inputencoding is only set when there is not more than one
622                 // inputenc option because otherwise h_inputencoding must be
623                 // set to "auto" (the default encoding of the document language)
624                 // Therefore check for the "," character.
625                 // It is also only set when there is not more then one babel
626                 // language option but this is handled in the routine for babel.
627                 if (opts.find(",") == string::npos && one_language == true)
628                         h_inputencoding = opts;
629                 if (!options.empty())
630                         p.setEncoding(options.back());
631                 options.clear();
632         }
633
634         else if (is_known(name, known_old_language_packages)) {
635                 // known language packages from the times before babel
636                 // if they are found and not also babel, they will be used as
637                 // cutom language package
638                 h_language_package = "\\usepackage{" + name + "}";
639         }
640
641         else if (name == "makeidx")
642                 ; // ignore this
643
644         else if (name == "prettyref")
645                 ; // ignore this
646
647         else if (name == "varioref")
648                 ; // ignore this
649
650         else if (name == "verbatim")
651                 ; // ignore this
652
653         else if (name == "nomencl")
654                 ; // ignore this
655
656         else if (name == "textcomp")
657                 ; // ignore this
658
659         else if (name == "url")
660                 ; // ignore this
661
662         else if (name == "subscript")
663                 ; // ignore this
664
665         else if (name == "color") {
666                 // with the following command this package is only loaded when needed for
667                 // undefined colors, since we only support the predefined colors
668                 // only add it if not yet added
669                 if (!ifundefined_color_set)
670                         h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
671         }
672
673         else if (name == "graphicx")
674                 ; // ignore this
675
676         else if (name == "setspace")
677                 ; // ignore this
678
679 #if 0
680         // do not ignore as long as we don't support all commands (e.g. \xout is missing)
681         // and as long as we don't support change tracking
682         else if (name == "ulem")
683                 ; // ignore this
684 #endif
685
686         else if (name == "geometry")
687                 ; // Ignore this, the geometry settings are made by the \geometry
688                   // command. This command is handled below.
689
690         else if (name == "rotfloat")
691                 ; // ignore this
692
693         else if (name == "wrapfig")
694                 ; // ignore this
695
696         else if (name == "subfig")
697                 ; // ignore this
698
699         else if (is_known(name, known_languages))
700                 h_language = name;
701
702         else if (name == "natbib") {
703                 h_cite_engine = "natbib_authoryear";
704                 vector<string>::iterator it =
705                         find(options.begin(), options.end(), "authoryear");
706                 if (it != options.end())
707                         options.erase(it);
708                 else {
709                         it = find(options.begin(), options.end(), "numbers");
710                         if (it != options.end()) {
711                                 h_cite_engine = "natbib_numerical";
712                                 options.erase(it);
713                         }
714                 }
715         }
716
717         else if (name == "jurabib")
718                 h_cite_engine = "jurabib";
719
720         else if (name == "hyperref")
721                 handle_hyperref(options);
722
723         else if (!in_lyx_preamble) {
724                 if (options.empty())
725                         h_preamble << "\\usepackage{" << name << "}";
726                 else {
727                         h_preamble << "\\usepackage[" << opts << "]{" 
728                                    << name << "}";
729                         options.clear();
730                 }
731         }
732
733         // We need to do something with the options...
734         if (!options.empty())
735                 cerr << "Ignoring options '" << join(options, ",")
736                      << "' of package " << name << '.' << endl;
737
738         // remove the whitespace
739         p.skip_spaces();
740 }
741
742
743 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
744 {
745         while (p.good()) {
746                 Token t = p.get_token();
747                 if (t.cat() == catEscape &&
748                     is_known(t.cs(), known_if_commands))
749                         handle_if(p, in_lyx_preamble);
750                 else {
751                         if (!in_lyx_preamble)
752                                 h_preamble << t.asInput();
753                         if (t.cat() == catEscape && t.cs() == "fi")
754                                 return;
755                 }
756         }
757 }
758
759
760 void Preamble::writeLyXHeader(ostream & os)
761 {
762         // translate from babel to LyX names
763         h_language = babel2lyx(h_language);
764
765         // set the quote language
766         // LyX only knows the following quotes languages:
767         // english, swedish, german, polish, french and danish
768         // (quotes for "japanese" and "chinese-traditional" are missing because
769         //  they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
770         // conversion list taken from
771         // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
772         // (quotes for kazakh and interlingua are unknown)
773         // danish
774         if (h_language == "danish")
775                 h_quotes_language = "danish";
776         // french
777         else if (is_known(h_language, known_french_quotes_languages))
778                 h_quotes_language = "french";
779         // german
780         else if (is_known(h_language, known_german_quotes_languages))
781                 h_quotes_language = "german";
782         // polish
783         else if (is_known(h_language, known_polish_quotes_languages))
784                 h_quotes_language = "polish";
785         // swedish
786         else if (is_known(h_language, known_swedish_quotes_languages))
787                 h_quotes_language = "swedish";
788         //english
789         else if (is_known(h_language, known_english_quotes_languages))
790                 h_quotes_language = "english";
791
792         // output the LyX file settings
793         os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
794            << "\\lyxformat " << LYX_FORMAT << '\n'
795            << "\\begin_document\n"
796            << "\\begin_header\n"
797            << "\\textclass " << h_textclass << "\n";
798         if (!h_preamble.str().empty())
799                 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
800         if (!h_options.empty())
801                 os << "\\options " << h_options << "\n";
802         os << "\\use_default_options " << h_use_default_options << "\n";
803         if (!used_modules.empty()) {
804                 os << "\\begin_modules\n";
805                 vector<string>::const_iterator const end = used_modules.end();
806                 vector<string>::const_iterator it = used_modules.begin();
807                 for (; it != end; it++)
808                         os << *it << '\n';
809                 os << "\\end_modules\n";
810         }
811         os << "\\language " << h_language << "\n"
812            << "\\language_package " << h_language_package << "\n"
813            << "\\inputencoding " << h_inputencoding << "\n"
814            << "\\fontencoding " << h_fontencoding << "\n"
815            << "\\font_roman " << h_font_roman << "\n"
816            << "\\font_sans " << h_font_sans << "\n"
817            << "\\font_typewriter " << h_font_typewriter << "\n"
818            << "\\font_default_family " << h_font_default_family << "\n"
819            << "\\font_sc " << h_font_sc << "\n"
820            << "\\font_osf " << h_font_osf << "\n"
821            << "\\font_sf_scale " << h_font_sf_scale << "\n"
822            << "\\font_tt_scale " << h_font_tt_scale << "\n"
823            << "\\graphics " << h_graphics << "\n";
824         if (!h_float_placement.empty())
825                 os << "\\float_placement " << h_float_placement << "\n";
826         os << "\\paperfontsize " << h_paperfontsize << "\n"
827            << "\\spacing " << h_spacing << "\n"
828            << "\\use_hyperref " << h_use_hyperref << '\n';
829         if (h_use_hyperref == "1") {
830                 if (!h_pdf_title.empty())
831                         os << "\\pdf_title \"" << h_pdf_title << "\"\n";
832                 if (!h_pdf_author.empty())
833                         os << "\\pdf_author \"" << h_pdf_author << "\"\n";
834                 if (!h_pdf_subject.empty())
835                         os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
836                 if (!h_pdf_keywords.empty())
837                         os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
838                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
839                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
840                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
841                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
842                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
843                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
844                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
845                       "\\pdf_backref " << h_pdf_backref << "\n"
846                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
847                 if (!h_pdf_pagemode.empty())
848                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
849                 if (!h_pdf_quoted_options.empty())
850                         os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
851         }
852         os << "\\papersize " << h_papersize << "\n"
853            << "\\use_geometry " << h_use_geometry << "\n"
854            << "\\use_amsmath " << h_use_amsmath << "\n"
855            << "\\use_esint " << h_use_esint << "\n"
856            << "\\use_mhchem " << h_use_mhchem << "\n"
857            << "\\use_mathdots " << h_use_mathdots << "\n"
858            << "\\use_undertilde " << h_use_undertilde << "\n"
859            << "\\cite_engine " << h_cite_engine << "\n"
860            << "\\use_bibtopic " << h_use_bibtopic << "\n"
861            << "\\paperorientation " << h_paperorientation << '\n'
862            << "\\suppress_date " << h_suppress_date << '\n'
863            << "\\use_refstyle " << h_use_refstyle << '\n';
864         if (!h_fontcolor.empty())
865                 os << "\\fontcolor " << h_fontcolor << '\n';
866         if (!h_notefontcolor.empty())
867                 os << "\\notefontcolor " << h_notefontcolor << '\n';
868         if (!h_backgroundcolor.empty())
869                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
870         if (!h_boxbgcolor.empty())
871                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
872         os << h_margins
873            << "\\secnumdepth " << h_secnumdepth << "\n"
874            << "\\tocdepth " << h_tocdepth << "\n"
875            << "\\paragraph_separation " << h_paragraph_separation << "\n";
876         if (h_paragraph_separation == "skip")
877                 os << "\\defskip " << h_defskip << "\n";
878         else
879                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
880         os << "\\quotes_language " << h_quotes_language << "\n"
881            << "\\papercolumns " << h_papercolumns << "\n"
882            << "\\papersides " << h_papersides << "\n"
883            << "\\paperpagestyle " << h_paperpagestyle << "\n";
884         if (!h_listings_params.empty())
885                 os << "\\listings_params " << h_listings_params << "\n";
886         os << "\\tracking_changes " << h_tracking_changes << "\n"
887            << "\\output_changes " << h_output_changes << "\n"
888            << "\\html_math_output " << h_html_math_output << "\n"
889            << "\\html_css_as_file " << h_html_css_as_file << "\n"
890            << "\\html_be_strict " << h_html_be_strict << "\n"
891            << "\\end_header\n\n"
892            << "\\begin_body\n";
893         // clear preamble for subdocuments
894         h_preamble.str("");
895 }
896
897
898 void Preamble::parse(Parser & p, string const & forceclass,
899                      TeX2LyXDocClass & tc)
900 {
901         // initialize fixed types
902         special_columns['D'] = 3;
903         bool is_full_document = false;
904         bool is_lyx_file = false;
905         bool in_lyx_preamble = false;
906
907         // determine whether this is a full document or a fragment for inclusion
908         while (p.good()) {
909                 Token const & t = p.get_token();
910
911                 if (t.cat() == catEscape && t.cs() == "documentclass") {
912                         is_full_document = true;
913                         break;
914                 }
915         }
916         p.reset();
917
918         while (is_full_document && p.good()) {
919                 Token const & t = p.get_token();
920
921 #ifdef FILEDEBUG
922                 cerr << "t: " << t << "\n";
923 #endif
924
925                 //
926                 // cat codes
927                 //
928                 if (!in_lyx_preamble &&
929                     (t.cat() == catLetter ||
930                      t.cat() == catSuper ||
931                      t.cat() == catSub ||
932                      t.cat() == catOther ||
933                      t.cat() == catMath ||
934                      t.cat() == catActive ||
935                      t.cat() == catBegin ||
936                      t.cat() == catEnd ||
937                      t.cat() == catAlign ||
938                      t.cat() == catParameter))
939                         h_preamble << t.cs();
940
941                 else if (!in_lyx_preamble && 
942                          (t.cat() == catSpace || t.cat() == catNewline))
943                         h_preamble << t.asInput();
944
945                 else if (t.cat() == catComment) {
946                         static regex const islyxfile("%% LyX .* created this file");
947                         static regex const usercommands("User specified LaTeX commands");
948
949                         string const comment = t.asInput();
950
951                         // magically switch encoding default if it looks like XeLaTeX
952                         static string const magicXeLaTeX =
953                                 "% This document must be compiled with XeLaTeX ";
954                         if (comment.size() > magicXeLaTeX.size() 
955                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
956                                   && h_inputencoding == "auto") {
957                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
958                                 h_inputencoding = "utf8";
959                         }
960                         smatch sub;
961                         if (regex_search(comment, sub, islyxfile)) {
962                                 is_lyx_file = true;
963                                 in_lyx_preamble = true;
964                         } else if (is_lyx_file
965                                    && regex_search(comment, sub, usercommands))
966                                 in_lyx_preamble = false;
967                         else if (!in_lyx_preamble)
968                                 h_preamble << t.asInput();
969                 }
970
971                 else if (t.cs() == "pagestyle")
972                         h_paperpagestyle = p.verbatim_item();
973
974                 else if (t.cs() == "date") {
975                         string argument = p.getArg('{', '}');
976                         if (argument.empty())
977                                 h_suppress_date = "true";
978                         else
979                                 h_preamble << t.asInput() << '{' << argument << '}';
980                 }
981
982                 else if (t.cs() == "color") {
983                         string argument = p.getArg('{', '}');
984                         // check the case that a standard color is used
985                         if (is_known(argument, known_basic_colors))
986                                 h_fontcolor = color2code(argument);
987                         // check the case that LyX's document_fontcolor is defined
988                         // but not used for \color
989                         if (argument != "document_fontcolor"
990                                 && !is_known(argument, known_basic_colors)) {
991                                 h_preamble << t.asInput() << '{' << argument << '}';
992                                 // the color might already be set because \definecolor
993                                 // is parsed before this
994                                 h_fontcolor = "";
995                         }
996                 }
997
998                 else if (t.cs() == "pagecolor") {
999                         string argument = p.getArg('{', '}');
1000                         // check the case that a standard color is used
1001                         if (is_known(argument, known_basic_colors))
1002                                 h_backgroundcolor = color2code(argument);
1003                         // check the case that LyX's page_backgroundcolor is defined
1004                         // but not used for \pagecolor
1005                         if (argument != "page_backgroundcolor"
1006                                 && !is_known(argument, known_basic_colors)) {
1007                                 h_preamble << t.asInput() << '{' << argument << '}';
1008                                 // the color might already be set because \definecolor
1009                                 // is parsed before this
1010                                 h_backgroundcolor = "";
1011                         }
1012                 }
1013
1014                 else if (t.cs() == "makeatletter") {
1015                         // LyX takes care of this
1016                         p.setCatCode('@', catLetter);
1017                 }
1018
1019                 else if (t.cs() == "makeatother") {
1020                         // LyX takes care of this
1021                         p.setCatCode('@', catOther);
1022                 }
1023
1024                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1025                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1026                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
1027                                 || t.cs() == "DeclareRobustCommand"
1028                       || t.cs() == "DeclareRobustCommandx"
1029                                 || t.cs() == "ProvideTextCommandDefault"
1030                                 || t.cs() == "DeclareMathAccent") {
1031                         bool star = false;
1032                         if (p.next_token().character() == '*') {
1033                                 p.get_token();
1034                                 star = true;
1035                         }
1036                         string const name = p.verbatim_item();
1037                         string const opt1 = p.getFullOpt();
1038                         string const opt2 = p.getFullOpt();
1039                         string const body = p.verbatim_item();
1040                         // font settings
1041                         if (name == "\\rmdefault")
1042                                 if (is_known(body, known_roman_fonts))
1043                                         h_font_roman = body;
1044                         if (name == "\\sfdefault")
1045                                 if (is_known(body, known_sans_fonts))
1046                                         h_font_sans = body;
1047                         if (name == "\\ttdefault")
1048                                 if (is_known(body, known_typewriter_fonts))
1049                                         h_font_typewriter = body;
1050                         if (name == "\\familydefault") {
1051                                 string family = body;
1052                                 // remove leading "\"
1053                                 h_font_default_family = family.erase(0,1);
1054                         }
1055
1056                         // Add the command to the known commands
1057                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1058
1059                         // only non-lyxspecific stuff
1060                         if (!in_lyx_preamble) {
1061                                 ostringstream ss;
1062                                 ss << '\\' << t.cs();
1063                                 if (star)
1064                                         ss << '*';
1065                                 ss << '{' << name << '}' << opt1 << opt2
1066                                    << '{' << body << "}";
1067                                 h_preamble << ss.str();
1068 /*
1069                                 ostream & out = in_preamble ? h_preamble : os;
1070                                 out << "\\" << t.cs() << "{" << name << "}"
1071                                     << opts << "{" << body << "}";
1072 */
1073                         }
1074                 }
1075
1076                 else if (t.cs() == "documentclass") {
1077                         vector<string>::iterator it;
1078                         vector<string> opts = split_options(p.getArg('[', ']'));
1079                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1080                         delete_opt(opts, known_fontsizes);
1081                         // delete "pt" at the end
1082                         string::size_type i = h_paperfontsize.find("pt");
1083                         if (i != string::npos)
1084                                 h_paperfontsize.erase(i);
1085                         // The documentclass options are always parsed before the options
1086                         // of the babel call so that a language cannot overwrite the babel
1087                         // options.
1088                         handle_opt(opts, known_languages, h_language);
1089                         delete_opt(opts, known_languages);
1090
1091                         // paper orientation
1092                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1093                                 h_paperorientation = "landscape";
1094                                 opts.erase(it);
1095                         }
1096                         // paper sides
1097                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1098                                  != opts.end()) {
1099                                 h_papersides = "1";
1100                                 opts.erase(it);
1101                         }
1102                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1103                                  != opts.end()) {
1104                                 h_papersides = "2";
1105                                 opts.erase(it);
1106                         }
1107                         // paper columns
1108                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1109                                  != opts.end()) {
1110                                 h_papercolumns = "1";
1111                                 opts.erase(it);
1112                         }
1113                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1114                                  != opts.end()) {
1115                                 h_papercolumns = "2";
1116                                 opts.erase(it);
1117                         }
1118                         // paper sizes
1119                         // some size options are know to any document classes, other sizes
1120                         // are handled by the \geometry command of the geometry package
1121                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1122                         delete_opt(opts, known_class_paper_sizes);
1123                         // the remaining options
1124                         h_options = join(opts, ",");
1125                         // FIXME This does not work for classes that have a
1126                         //       different name in LyX than in LaTeX
1127                         h_textclass = p.getArg('{', '}');
1128                 }
1129
1130                 else if (t.cs() == "usepackage") {
1131                         string const options = p.getArg('[', ']');
1132                         string const name = p.getArg('{', '}');
1133                         vector<string> vecnames;
1134                         split(name, vecnames, ',');
1135                         vector<string>::const_iterator it  = vecnames.begin();
1136                         vector<string>::const_iterator end = vecnames.end();
1137                         for (; it != end; ++it)
1138                                 handle_package(p, trimSpaceAndEol(*it), options, 
1139                                                in_lyx_preamble);
1140                 }
1141
1142                 else if (t.cs() == "inputencoding") {
1143                         string const encoding = p.getArg('{','}');
1144                         h_inputencoding = encoding;
1145                         p.setEncoding(encoding);
1146                 }
1147
1148                 else if (t.cs() == "newenvironment") {
1149                         string const name = p.getArg('{', '}');
1150                         string const opt1 = p.getFullOpt();
1151                         string const opt2 = p.getFullOpt();
1152                         string const beg = p.verbatim_item();
1153                         string const end = p.verbatim_item();
1154                         if (!in_lyx_preamble) {
1155                                 h_preamble << "\\newenvironment{" << name
1156                                            << '}' << opt1 << opt2 << '{'
1157                                            << beg << "}{" << end << '}';
1158                         }
1159                         add_known_environment(name, opt1, !opt2.empty(),
1160                                               from_utf8(beg), from_utf8(end));
1161
1162                 }
1163
1164                 else if (t.cs() == "def") {
1165                         string name = p.get_token().cs();
1166                         while (p.next_token().cat() != catBegin)
1167                                 name += p.get_token().cs();
1168                         if (!in_lyx_preamble)
1169                                 h_preamble << "\\def\\" << name << '{'
1170                                            << p.verbatim_item() << "}";
1171                 }
1172
1173                 else if (t.cs() == "newcolumntype") {
1174                         string const name = p.getArg('{', '}');
1175                         trimSpaceAndEol(name);
1176                         int nargs = 0;
1177                         string opts = p.getOpt();
1178                         if (!opts.empty()) {
1179                                 istringstream is(string(opts, 1));
1180                                 is >> nargs;
1181                         }
1182                         special_columns[name[0]] = nargs;
1183                         h_preamble << "\\newcolumntype{" << name << "}";
1184                         if (nargs)
1185                                 h_preamble << "[" << nargs << "]";
1186                         h_preamble << "{" << p.verbatim_item() << "}";
1187                 }
1188
1189                 else if (t.cs() == "setcounter") {
1190                         string const name = p.getArg('{', '}');
1191                         string const content = p.getArg('{', '}');
1192                         if (name == "secnumdepth")
1193                                 h_secnumdepth = content;
1194                         else if (name == "tocdepth")
1195                                 h_tocdepth = content;
1196                         else
1197                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1198                 }
1199
1200                 else if (t.cs() == "setlength") {
1201                         string const name = p.verbatim_item();
1202                         string const content = p.verbatim_item();
1203                         // the paragraphs are only not indented when \parindent is set to zero
1204                         if (name == "\\parindent" && content != "") {
1205                                 if (content[0] == '0')
1206                                         h_paragraph_separation = "skip";
1207                                 else
1208                                         h_paragraph_indentation = translate_len(content);
1209                         } else if (name == "\\parskip") {
1210                                 if (content == "\\smallskipamount")
1211                                         h_defskip = "smallskip";
1212                                 else if (content == "\\medskipamount")
1213                                         h_defskip = "medskip";
1214                                 else if (content == "\\bigskipamount")
1215                                         h_defskip = "bigskip";
1216                                 else
1217                                         h_defskip = content;
1218                         } else
1219                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1220                 }
1221
1222                 else if (t.cs() == "onehalfspacing")
1223                         h_spacing = "onehalf";
1224
1225                 else if (t.cs() == "doublespacing")
1226                         h_spacing = "double";
1227
1228                 else if (t.cs() == "setstretch")
1229                         h_spacing = "other " + p.verbatim_item();
1230
1231                 else if (t.cs() == "begin") {
1232                         string const name = p.getArg('{', '}');
1233                         if (name == "document")
1234                                 break;
1235                         h_preamble << "\\begin{" << name << "}";
1236                 }
1237
1238                 else if (t.cs() == "geometry") {
1239                         h_use_geometry = "true";
1240                         vector<string> opts = split_options(p.getArg('{', '}'));
1241                         vector<string>::iterator it;
1242                         // paper orientation
1243                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1244                                 h_paperorientation = "landscape";
1245                                 opts.erase(it);
1246                         }
1247                         // paper size
1248                         handle_opt(opts, known_paper_sizes, h_papersize);
1249                         delete_opt(opts, known_paper_sizes);
1250                         // page margins
1251                         char const * const * margin = known_paper_margins;
1252                         int k = -1;
1253                         for (; *margin; ++margin) {
1254                                 k += 1;
1255                                 // search for the "=" in e.g. "lmargin=2cm" to get the value
1256                                 for(size_t i = 0; i != opts.size(); i++) {
1257                                         if (opts.at(i).find(*margin) != string::npos) {
1258                                                 string::size_type pos = opts.at(i).find("=");
1259                                                 string value = opts.at(i).substr(pos + 1);
1260                                                 string name = known_coded_paper_margins[k];
1261                                                 h_margins += "\\" + name + " " + value + "\n";
1262                                         }
1263                                 }
1264                         }
1265                 }
1266
1267                 else if (t.cs() == "definecolor") {
1268                         string const color = p.getArg('{', '}');
1269                         string const space = p.getArg('{', '}');
1270                         string const value = p.getArg('{', '}');
1271                         if (color == "document_fontcolor" && space == "rgb") {
1272                                 RGBColor c(RGBColorFromLaTeX(value));
1273                                 h_fontcolor = X11hexname(c);
1274                         } else if (color == "note_fontcolor" && space == "rgb") {
1275                                 RGBColor c(RGBColorFromLaTeX(value));
1276                                 h_notefontcolor = X11hexname(c);
1277                         } else if (color == "page_backgroundcolor" && space == "rgb") {
1278                                 RGBColor c(RGBColorFromLaTeX(value));
1279                                 h_backgroundcolor = X11hexname(c);
1280                         } else if (color == "shadecolor" && space == "rgb") {
1281                                 RGBColor c(RGBColorFromLaTeX(value));
1282                                 h_boxbgcolor = X11hexname(c);
1283                         } else {
1284                                 h_preamble << "\\definecolor{" << color
1285                                            << "}{" << space << "}{" << value
1286                                            << '}';
1287                         }
1288                 }
1289
1290                 else if (t.cs() == "jurabibsetup") {
1291                         // FIXME p.getArg('{', '}') is most probably wrong (it
1292                         //       does not handle nested braces).
1293                         //       Use p.verbatim_item() instead.
1294                         vector<string> jurabibsetup =
1295                                 split_options(p.getArg('{', '}'));
1296                         // add jurabibsetup to the jurabib package options
1297                         add_package("jurabib", jurabibsetup);
1298                         if (!jurabibsetup.empty()) {
1299                                 h_preamble << "\\jurabibsetup{"
1300                                            << join(jurabibsetup, ",") << '}';
1301                         }
1302                 }
1303
1304                 else if (t.cs() == "hypersetup") {
1305                         vector<string> hypersetup =
1306                                 split_options(p.verbatim_item());
1307                         // add hypersetup to the hyperref package options
1308                         handle_hyperref(hypersetup);
1309                         if (!hypersetup.empty()) {
1310                                 h_preamble << "\\hypersetup{"
1311                                            << join(hypersetup, ",") << '}';
1312                         }
1313                 }
1314
1315                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1316                         // prevent misparsing of \usepackage if it is used
1317                         // as an argument (see e.g. our own output of
1318                         // \@ifundefined above)
1319                         string const arg1 = p.verbatim_item();
1320                         string const arg2 = p.verbatim_item();
1321                         string const arg3 = p.verbatim_item();
1322                         // test case \@ifundefined{date}{}{\date{}}
1323                         if (arg1 == "date" && arg2.empty() && arg3 == "\\date{}") {
1324                                 h_suppress_date = "true";
1325                         // test case \@ifundefined{definecolor}{\usepackage{color}}{}
1326                         // because we could pollute the preamble with it in roundtrips
1327                         } else if (arg1 == "definecolor" && arg2 == "\\usepackage{color}"
1328                                 && arg3.empty()) {
1329                                 ifundefined_color_set = true;
1330                         // test for case
1331                         //\@ifundefined{showcaptionsetup}{}{%
1332                         // \PassOptionsToPackage{caption=false}{subfig}}
1333                         // that LyX uses for subfloats
1334                         } else if (arg1 == "showcaptionsetup" && arg2.empty()
1335                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1336                                 ; // do nothing
1337                         } else if (!in_lyx_preamble) {
1338                                 h_preamble << t.asInput()
1339                                            << '{' << arg1 << '}'
1340                                            << '{' << arg2 << '}'
1341                                            << '{' << arg3 << '}';
1342                         }
1343                 }
1344
1345                 else if (is_known(t.cs(), known_if_commands)) {
1346                         // must not parse anything in conditional code, since
1347                         // LyX would output the parsed contents unconditionally
1348                         if (!in_lyx_preamble)
1349                                 h_preamble << t.asInput();
1350                         handle_if(p, in_lyx_preamble);
1351                 }
1352
1353                 else if (!t.cs().empty() && !in_lyx_preamble)
1354                         h_preamble << '\\' << t.cs();
1355         }
1356
1357         // remove the whitespace
1358         p.skip_spaces();
1359
1360         // Force textclass if the user wanted it
1361         if (!forceclass.empty())
1362                 h_textclass = forceclass;
1363         if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1364                 h_textclass.insert(0, "literate-");
1365         tc.setName(h_textclass);
1366         if (!tc.load()) {
1367                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1368                 exit(EXIT_FAILURE);
1369         }
1370         if (h_papersides.empty()) {
1371                 ostringstream ss;
1372                 ss << tc.sides();
1373                 h_papersides = ss.str();
1374         }
1375 }
1376
1377
1378 string babel2lyx(string const & language)
1379 {
1380         char const * const * where = is_known(language, known_languages);
1381         if (where)
1382                 return known_coded_languages[where - known_languages];
1383         return language;
1384 }
1385
1386
1387 string color2code(string const & name)
1388 {
1389         char const * const * where = is_known(name, known_basic_colors);
1390         if (where)
1391                 return known_basic_color_codes[where - known_basic_colors];
1392         return name;
1393 }
1394
1395 // }])
1396
1397
1398 } // namespace lyx