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