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