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