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