]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/preamble.cpp
tex2lyx: fix bug #7856
[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() == "makeatletter") {
919                         // LyX takes care of this
920                         p.setCatCode('@', catLetter);
921                 }
922
923                 else if (t.cs() == "makeatother") {
924                         // LyX takes care of this
925                         p.setCatCode('@', catOther);
926                 }
927
928                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
929                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
930                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
931                                 || t.cs() == "DeclareRobustCommand"
932                       || t.cs() == "DeclareRobustCommandx"
933                                 || t.cs() == "ProvideTextCommandDefault"
934                                 || t.cs() == "DeclareMathAccent") {
935                         bool star = false;
936                         if (p.next_token().character() == '*') {
937                                 p.get_token();
938                                 star = true;
939                         }
940                         string const name = p.verbatim_item();
941                         string const opt1 = p.getFullOpt();
942                         string const opt2 = p.getFullOpt();
943                         string const body = p.verbatim_item();
944                         // font settings
945                         if (name == "\\rmdefault")
946                                 if (is_known(body, known_roman_fonts))
947                                         h_font_roman = body;
948                         if (name == "\\sfdefault")
949                                 if (is_known(body, known_sans_fonts))
950                                         h_font_sans = body;
951                         if (name == "\\ttdefault")
952                                 if (is_known(body, known_typewriter_fonts))
953                                         h_font_typewriter = body;
954                         if (name == "\\familydefault") {
955                                 string family = body;
956                                 // remove leading "\"
957                                 h_font_default_family = family.erase(0,1);
958                         }
959
960                         // Add the command to the known commands
961                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
962
963                         // only non-lyxspecific stuff
964                         if (!in_lyx_preamble) {
965                                 ostringstream ss;
966                                 ss << '\\' << t.cs();
967                                 if (star)
968                                         ss << '*';
969                                 ss << '{' << name << '}' << opt1 << opt2
970                                    << '{' << body << "}";
971                                 h_preamble << ss.str();
972 /*
973                                 ostream & out = in_preamble ? h_preamble : os;
974                                 out << "\\" << t.cs() << "{" << name << "}"
975                                     << opts << "{" << body << "}";
976 */
977                         }
978                 }
979
980                 else if (t.cs() == "documentclass") {
981                         vector<string>::iterator it;
982                         vector<string> opts = split_options(p.getArg('[', ']'));
983                         handle_opt(opts, known_fontsizes, h_paperfontsize);
984                         delete_opt(opts, known_fontsizes);
985                         // delete "pt" at the end
986                         string::size_type i = h_paperfontsize.find("pt");
987                         if (i != string::npos)
988                                 h_paperfontsize.erase(i);
989                         // The documentclass options are always parsed before the options
990                         // of the babel call so that a language cannot overwrite the babel
991                         // options.
992                         handle_opt(opts, known_languages, h_language);
993                         delete_opt(opts, known_languages);
994
995                         // paper orientation
996                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
997                                 h_paperorientation = "landscape";
998                                 opts.erase(it);
999                         }
1000                         // paper sides
1001                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1002                                  != opts.end()) {
1003                                 h_papersides = "1";
1004                                 opts.erase(it);
1005                         }
1006                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1007                                  != opts.end()) {
1008                                 h_papersides = "2";
1009                                 opts.erase(it);
1010                         }
1011                         // paper columns
1012                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1013                                  != opts.end()) {
1014                                 h_papercolumns = "1";
1015                                 opts.erase(it);
1016                         }
1017                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1018                                  != opts.end()) {
1019                                 h_papercolumns = "2";
1020                                 opts.erase(it);
1021                         }
1022                         // paper sizes
1023                         // some size options are know to any document classes, other sizes
1024                         // are handled by the \geometry command of the geometry package
1025                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1026                         delete_opt(opts, known_class_paper_sizes);
1027                         // the remaining options
1028                         h_options = join(opts, ",");
1029                         // FIXME This does not work for classes that have a
1030                         //       different name in LyX than in LaTeX
1031                         h_textclass = p.getArg('{', '}');
1032                 }
1033
1034                 else if (t.cs() == "usepackage") {
1035                         string const options = p.getArg('[', ']');
1036                         string const name = p.getArg('{', '}');
1037                         vector<string> vecnames;
1038                         split(name, vecnames, ',');
1039                         vector<string>::const_iterator it  = vecnames.begin();
1040                         vector<string>::const_iterator end = vecnames.end();
1041                         for (; it != end; ++it)
1042                                 handle_package(p, trimSpaceAndEol(*it), options, 
1043                                                in_lyx_preamble);
1044                 }
1045
1046                 else if (t.cs() == "inputencoding") {
1047                         string const encoding = p.getArg('{','}');
1048                         h_inputencoding = encoding;
1049                         p.setEncoding(encoding);
1050                 }
1051
1052                 else if (t.cs() == "newenvironment") {
1053                         string const name = p.getArg('{', '}');
1054                         string const opt1 = p.getFullOpt();
1055                         string const opt2 = p.getFullOpt();
1056                         string const beg = p.verbatim_item();
1057                         string const end = p.verbatim_item();
1058                         if (!in_lyx_preamble) {
1059                                 h_preamble << "\\newenvironment{" << name
1060                                            << '}' << opt1 << opt2 << '{'
1061                                            << beg << "}{" << end << '}';
1062                         }
1063                         add_known_environment(name, opt1, !opt2.empty(),
1064                                               from_utf8(beg), from_utf8(end));
1065
1066                 }
1067
1068                 else if (t.cs() == "def") {
1069                         string name = p.get_token().cs();
1070                         while (p.next_token().cat() != catBegin)
1071                                 name += p.get_token().cs();
1072                         if (!in_lyx_preamble)
1073                                 h_preamble << "\\def\\" << name << '{'
1074                                            << p.verbatim_item() << "}";
1075                 }
1076
1077                 else if (t.cs() == "newcolumntype") {
1078                         string const name = p.getArg('{', '}');
1079                         trimSpaceAndEol(name);
1080                         int nargs = 0;
1081                         string opts = p.getOpt();
1082                         if (!opts.empty()) {
1083                                 istringstream is(string(opts, 1));
1084                                 is >> nargs;
1085                         }
1086                         special_columns[name[0]] = nargs;
1087                         h_preamble << "\\newcolumntype{" << name << "}";
1088                         if (nargs)
1089                                 h_preamble << "[" << nargs << "]";
1090                         h_preamble << "{" << p.verbatim_item() << "}";
1091                 }
1092
1093                 else if (t.cs() == "setcounter") {
1094                         string const name = p.getArg('{', '}');
1095                         string const content = p.getArg('{', '}');
1096                         if (name == "secnumdepth")
1097                                 h_secnumdepth = content;
1098                         else if (name == "tocdepth")
1099                                 h_tocdepth = content;
1100                         else
1101                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1102                 }
1103
1104                 else if (t.cs() == "setlength") {
1105                         string const name = p.verbatim_item();
1106                         string const content = p.verbatim_item();
1107                         // the paragraphs are only not indented when \parindent is set to zero
1108                         if (name == "\\parindent" && content != "") {
1109                                 if (content[0] == '0')
1110                                         h_paragraph_separation = "skip";
1111                                 else
1112                                         h_paragraph_indentation = translate_len(content);
1113                         } else if (name == "\\parskip") {
1114                                 if (content == "\\smallskipamount")
1115                                         h_defskip = "smallskip";
1116                                 else if (content == "\\medskipamount")
1117                                         h_defskip = "medskip";
1118                                 else if (content == "\\bigskipamount")
1119                                         h_defskip = "bigskip";
1120                                 else
1121                                         h_defskip = content;
1122                         } else
1123                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1124                 }
1125
1126                 else if (t.cs() == "onehalfspacing")
1127                         h_spacing = "onehalf";
1128
1129                 else if (t.cs() == "doublespacing")
1130                         h_spacing = "double";
1131
1132                 else if (t.cs() == "setstretch")
1133                         h_spacing = "other " + p.verbatim_item();
1134
1135                 else if (t.cs() == "begin") {
1136                         string const name = p.getArg('{', '}');
1137                         if (name == "document")
1138                                 break;
1139                         h_preamble << "\\begin{" << name << "}";
1140                 }
1141
1142                 else if (t.cs() == "geometry") {
1143                         h_use_geometry = "true";
1144                         vector<string> opts = split_options(p.getArg('{', '}'));
1145                         vector<string>::iterator it;
1146                         // paper orientation
1147                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1148                                 h_paperorientation = "landscape";
1149                                 opts.erase(it);
1150                         }
1151                         // paper size
1152                         handle_opt(opts, known_paper_sizes, h_papersize);
1153                         delete_opt(opts, known_paper_sizes);
1154                         // page margins
1155                         char const * const * margin = known_paper_margins;
1156                         int k = -1;
1157                         for (; *margin; ++margin) {
1158                                 k += 1;
1159                                 // search for the "=" in e.g. "lmargin=2cm" to get the value
1160                                 for(size_t i = 0; i != opts.size(); i++) {
1161                                         if (opts.at(i).find(*margin) != string::npos) {
1162                                                 string::size_type pos = opts.at(i).find("=");
1163                                                 string value = opts.at(i).substr(pos + 1);
1164                                                 string name = known_coded_paper_margins[k];
1165                                                 h_margins += "\\" + name + " " + value + "\n";
1166                                         }
1167                                 }
1168                         }
1169                 }
1170
1171                 else if (t.cs() == "definecolor") {
1172                         string const color = p.getArg('{', '}');
1173                         string const space = p.getArg('{', '}');
1174                         string const value = p.getArg('{', '}');
1175                         if (color == "document_fontcolor" && space == "rgb") {
1176                                 RGBColor c(RGBColorFromLaTeX(value));
1177                                 h_fontcolor = X11hexname(c);
1178                         } else if (color == "note_fontcolor" && space == "rgb") {
1179                                 RGBColor c(RGBColorFromLaTeX(value));
1180                                 h_notefontcolor = X11hexname(c);
1181                         } else if (color == "page_backgroundcolor" && space == "rgb") {
1182                                 RGBColor c(RGBColorFromLaTeX(value));
1183                                 h_backgroundcolor = X11hexname(c);
1184                         } else if (color == "shadecolor" && space == "rgb") {
1185                                 RGBColor c(RGBColorFromLaTeX(value));
1186                                 h_boxbgcolor = X11hexname(c);
1187                         } else {
1188                                 h_preamble << "\\definecolor{" << color
1189                                            << "}{" << space << "}{" << value
1190                                            << '}';
1191                         }
1192                 }
1193
1194                 else if (t.cs() == "jurabibsetup") {
1195                         // FIXME p.getArg('{', '}') is most probably wrong (it
1196                         //       does not handle nested braces).
1197                         //       Use p.verbatim_item() instead.
1198                         vector<string> jurabibsetup =
1199                                 split_options(p.getArg('{', '}'));
1200                         // add jurabibsetup to the jurabib package options
1201                         add_package("jurabib", jurabibsetup);
1202                         if (!jurabibsetup.empty()) {
1203                                 h_preamble << "\\jurabibsetup{"
1204                                            << join(jurabibsetup, ",") << '}';
1205                         }
1206                 }
1207
1208                 else if (t.cs() == "hypersetup") {
1209                         vector<string> hypersetup =
1210                                 split_options(p.verbatim_item());
1211                         // add hypersetup to the hyperref package options
1212                         handle_hyperref(hypersetup);
1213                         if (!hypersetup.empty()) {
1214                                 h_preamble << "\\hypersetup{"
1215                                            << join(hypersetup, ",") << '}';
1216                         }
1217                 }
1218
1219                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1220                         // prevent misparsing of \usepackage if it is used
1221                         // as an argument (see e.g. our own output of
1222                         // \@ifundefined above)
1223                         string const arg1 = p.verbatim_item();
1224                         string const arg2 = p.verbatim_item();
1225                         string const arg3 = p.verbatim_item();
1226                         // test case \@ifundefined{date}{}{\date{}}
1227                         if (arg1 == "date" && arg2.empty() && arg3 == "\\date{}") {
1228                                 h_suppress_date = "true";
1229                         // test case \@ifundefined{definecolor}{\usepackage{color}}{}
1230                         // because we could pollute the preamble with it in roundtrips
1231                         } else if (arg1 == "definecolor" && arg2 == "\\usepackage{color}"
1232                                 && arg3.empty()) {
1233                                 ifundefined_color_set = true;
1234                         } else if (!in_lyx_preamble) {
1235                                 h_preamble << t.asInput()
1236                                            << '{' << arg1 << '}'
1237                                            << '{' << arg2 << '}'
1238                                            << '{' << arg3 << '}';
1239                         }
1240                 }
1241
1242                 else if (is_known(t.cs(), known_if_commands)) {
1243                         // must not parse anything in conditional code, since
1244                         // LyX would output the parsed contents unconditionally
1245                         if (!in_lyx_preamble)
1246                                 h_preamble << t.asInput();
1247                         handle_if(p, in_lyx_preamble);
1248                 }
1249
1250                 else if (!t.cs().empty() && !in_lyx_preamble)
1251                         h_preamble << '\\' << t.cs();
1252         }
1253
1254         // remove the whitespace
1255         p.skip_spaces();
1256
1257         // Force textclass if the user wanted it
1258         if (!forceclass.empty())
1259                 h_textclass = forceclass;
1260         if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1261                 h_textclass.insert(0, "literate-");
1262         tc.setName(h_textclass);
1263         if (!tc.load()) {
1264                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1265                 exit(EXIT_FAILURE);
1266         }
1267         if (h_papersides.empty()) {
1268                 ostringstream ss;
1269                 ss << tc.sides();
1270                 h_papersides = ss.str();
1271         }
1272         end_preamble(os, tc);
1273 }
1274
1275
1276 /// translates a babel language name to a LyX language name
1277 string babel2lyx(string const & language)
1278 {
1279         char const * const * where = is_known(language, known_languages);
1280         if (where)
1281                 return known_coded_languages[where - known_languages];
1282         return language;
1283 }
1284
1285 // }])
1286
1287
1288 } // namespace lyx