]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/preamble.cpp
tex2lyx: support for phantom insets
[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                 /* We could do the following for better round trip support,
553                  * but this makes the document less portable, so I skip it:
554                 if (h_fontencoding == lyxrc.fontenc)
555                         h_fontencoding = "global";
556                  */
557                 options.clear();
558         }
559
560         else if (name == "inputenc" || name == "luainputenc") {
561                 // h_inputencoding is only set when there is not more than one
562                 // inputenc option because otherwise h_inputencoding must be
563                 // set to "auto" (the default encoding of the document language)
564                 // Therefore check for the "," character.
565                 // It is also only set when there is not more then one babel
566                 // language option but this is handled in the routine for babel.
567                 if (opts.find(",") == string::npos && one_language == true)
568                         h_inputencoding = opts;
569                 if (!options.empty())
570                         p.setEncoding(options.back());
571                 options.clear();
572         }
573
574         else if (name == "makeidx")
575                 ; // ignore this
576
577         else if (name == "prettyref")
578                 ; // ignore this
579
580         else if (name == "varioref")
581                 ; // ignore this
582
583         else if (name == "verbatim")
584                 ; // ignore this
585
586         else if (name == "nomencl")
587                 ; // ignore this
588
589         else if (name == "textcomp")
590                 ; // ignore this
591
592         else if (name == "url")
593                 ; // ignore this
594
595         else if (name == "subscript")
596                 ; // ignore this
597
598         else if (name == "color") {
599                 // with the following command this package is only loaded when needed for
600                 // undefined colors, since we only support the predefined colors
601                 // only add it if not yet added
602                 if (!ifundefined_color_set)
603                         h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
604         }
605
606         else if (name == "graphicx")
607                 ; // ignore this
608
609         else if (name == "setspace")
610                 ; // ignore this
611
612 #if 0
613         // do not ignore as long as we don't support all commands (e.g. \xout is missing)
614         else if (name == "ulem")
615                 ; // ignore this
616 #endif
617
618         else if (name == "geometry")
619                 ; // Ignore this, the geometry settings are made by the \geometry
620                   // command. This command is handled below.
621
622         else if (is_known(name, known_languages))
623                 h_language = name;
624
625         else if (name == "natbib") {
626                 h_cite_engine = "natbib_authoryear";
627                 vector<string>::iterator it =
628                         find(options.begin(), options.end(), "authoryear");
629                 if (it != options.end())
630                         options.erase(it);
631                 else {
632                         it = find(options.begin(), options.end(), "numbers");
633                         if (it != options.end()) {
634                                 h_cite_engine = "natbib_numerical";
635                                 options.erase(it);
636                         }
637                 }
638         }
639
640         else if (name == "jurabib")
641                 h_cite_engine = "jurabib";
642
643         else if (name == "hyperref")
644                 handle_hyperref(options);
645
646         else if (!in_lyx_preamble) {
647                 if (options.empty())
648                         h_preamble << "\\usepackage{" << name << "}";
649                 else {
650                         h_preamble << "\\usepackage[" << opts << "]{" 
651                                    << name << "}";
652                         options.clear();
653                 }
654         }
655
656         // We need to do something with the options...
657         if (!options.empty())
658                 cerr << "Ignoring options '" << join(options, ",")
659                      << "' of package " << name << '.' << endl;
660
661         // remove the whitespace
662         p.skip_spaces();
663 }
664
665
666 void handle_if(Parser & p, bool in_lyx_preamble)
667 {
668         while (p.good()) {
669                 Token t = p.get_token();
670                 if (t.cat() == catEscape &&
671                     is_known(t.cs(), known_if_commands))
672                         handle_if(p, in_lyx_preamble);
673                 else {
674                         if (!in_lyx_preamble)
675                                 h_preamble << t.asInput();
676                         if (t.cat() == catEscape && t.cs() == "fi")
677                                 return;
678                 }
679         }
680 }
681
682
683 void end_preamble(ostream & os, TextClass const & /*textclass*/)
684 {
685         // translate from babel to LyX names
686         h_language = babel2lyx(h_language);
687
688         // set the quote language
689         // LyX only knows the following quotes languages:
690         // english, swedish, german, polish, french and danish
691         // (quotes for "japanese" and "chinese-traditional" are missing because
692         //  they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
693         // conversion list taken from
694         // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
695         // (quotes for kazakh and interlingua are unknown)
696         // danish
697         if (h_language == "danish")
698                 h_quotes_language = "danish";
699         // french
700         else if (is_known(h_language, known_french_quotes_languages))
701                 h_quotes_language = "french";
702         // german
703         else if (is_known(h_language, known_german_quotes_languages))
704                 h_quotes_language = "german";
705         // polish
706         else if (is_known(h_language, known_polish_quotes_languages))
707                 h_quotes_language = "polish";
708         // swedish
709         else if (is_known(h_language, known_swedish_quotes_languages))
710                 h_quotes_language = "swedish";
711         //english
712         else if (is_known(h_language, known_english_quotes_languages))
713                 h_quotes_language = "english";
714
715         // output the LyX file settings
716         os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
717            << "\\lyxformat " << LYX_FORMAT << '\n'
718            << "\\begin_document\n"
719            << "\\begin_header\n"
720            << "\\textclass " << h_textclass << "\n";
721         if (!h_preamble.str().empty())
722                 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
723         if (!h_options.empty())
724                 os << "\\options " << h_options << "\n";
725         os << "\\use_default_options " << h_use_default_options << "\n"
726            << modules_placeholder
727            << "\\language " << h_language << "\n"
728            << "\\language_package " << h_language_package << "\n"
729            << "\\inputencoding " << h_inputencoding << "\n"
730            << "\\fontencoding " << h_fontencoding << "\n"
731            << "\\font_roman " << h_font_roman << "\n"
732            << "\\font_sans " << h_font_sans << "\n"
733            << "\\font_typewriter " << h_font_typewriter << "\n"
734            << "\\font_default_family " << h_font_default_family << "\n"
735            << "\\font_sc " << h_font_sc << "\n"
736            << "\\font_osf " << h_font_osf << "\n"
737            << "\\font_sf_scale " << h_font_sf_scale << "\n"
738            << "\\font_tt_scale " << h_font_tt_scale << "\n"
739            << "\\graphics " << h_graphics << "\n";
740         if (!h_float_placement.empty())
741                 os << "\\float_placement " << h_float_placement << "\n";
742         os << "\\paperfontsize " << h_paperfontsize << "\n"
743            << "\\spacing " << h_spacing << "\n"
744            << "\\use_hyperref " << h_use_hyperref << '\n';
745         if (h_use_hyperref == "1") {
746                 if (!h_pdf_title.empty())
747                         os << "\\pdf_title \"" << h_pdf_title << "\"\n";
748                 if (!h_pdf_author.empty())
749                         os << "\\pdf_author \"" << h_pdf_author << "\"\n";
750                 if (!h_pdf_subject.empty())
751                         os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
752                 if (!h_pdf_keywords.empty())
753                         os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
754                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
755                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
756                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
757                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
758                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
759                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
760                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
761                       "\\pdf_backref " << h_pdf_backref << "\n"
762                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
763                 if (!h_pdf_pagemode.empty())
764                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
765                 if (!h_pdf_quoted_options.empty())
766                         os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
767         }
768         os << "\\papersize " << h_papersize << "\n"
769            << "\\use_geometry " << h_use_geometry << "\n"
770            << "\\use_amsmath " << h_use_amsmath << "\n"
771            << "\\use_esint " << h_use_esint << "\n"
772            << "\\use_mhchem " << h_use_mhchem << "\n"
773            << "\\use_mathdots " << h_use_mathdots << "\n"
774            << "\\use_undertilde " << h_use_undertilde << "\n"
775            << "\\cite_engine " << h_cite_engine << "\n"
776            << "\\use_bibtopic " << h_use_bibtopic << "\n"
777            << "\\paperorientation " << h_paperorientation << '\n'
778            << "\\suppress_date " << h_suppress_date << '\n'
779            << "\\use_refstyle " << h_use_refstyle << '\n';
780         if (!h_fontcolor.empty())
781                 os << "\\fontcolor " << h_fontcolor << '\n';
782         if (!h_notefontcolor.empty())
783                 os << "\\notefontcolor " << h_notefontcolor << '\n';
784         if (!h_backgroundcolor.empty())
785                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
786         if (!h_boxbgcolor.empty())
787                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
788         os << h_margins
789            << "\\secnumdepth " << h_secnumdepth << "\n"
790            << "\\tocdepth " << h_tocdepth << "\n"
791            << "\\paragraph_separation " << h_paragraph_separation << "\n";
792         if (h_paragraph_separation == "skip")
793                 os << "\\defskip " << h_defskip << "\n";
794         else
795                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
796         os << "\\quotes_language " << h_quotes_language << "\n"
797            << "\\papercolumns " << h_papercolumns << "\n"
798            << "\\papersides " << h_papersides << "\n"
799            << "\\paperpagestyle " << h_paperpagestyle << "\n";
800         if (!h_listings_params.empty())
801                 os << "\\listings_params " << h_listings_params << "\n";
802         os << "\\tracking_changes " << h_tracking_changes << "\n"
803            << "\\output_changes " << h_output_changes << "\n"
804            << "\\html_math_output " << h_html_math_output << "\n"
805            << "\\html_css_as_file " << h_html_css_as_file << "\n"
806            << "\\html_be_strict " << h_html_be_strict << "\n"
807            << "\\end_header\n\n"
808            << "\\begin_body\n";
809         // clear preamble for subdocuments
810         h_preamble.str("");
811 }
812
813 } // anonymous namespace
814
815
816 void parse_preamble(Parser & p, ostream & os, 
817         string const & forceclass, TeX2LyXDocClass & tc)
818 {
819         // initialize fixed types
820         special_columns['D'] = 3;
821         bool is_full_document = false;
822         bool is_lyx_file = false;
823         bool in_lyx_preamble = false;
824
825         // determine whether this is a full document or a fragment for inclusion
826         while (p.good()) {
827                 Token const & t = p.get_token();
828
829                 if (t.cat() == catEscape && t.cs() == "documentclass") {
830                         is_full_document = true;
831                         break;
832                 }
833         }
834         p.reset();
835
836         while (is_full_document && p.good()) {
837                 Token const & t = p.get_token();
838
839 #ifdef FILEDEBUG
840                 cerr << "t: " << t << "\n";
841 #endif
842
843                 //
844                 // cat codes
845                 //
846                 if (!in_lyx_preamble &&
847                     (t.cat() == catLetter ||
848                      t.cat() == catSuper ||
849                      t.cat() == catSub ||
850                      t.cat() == catOther ||
851                      t.cat() == catMath ||
852                      t.cat() == catActive ||
853                      t.cat() == catBegin ||
854                      t.cat() == catEnd ||
855                      t.cat() == catAlign ||
856                      t.cat() == catParameter))
857                         h_preamble << t.cs();
858
859                 else if (!in_lyx_preamble && 
860                          (t.cat() == catSpace || t.cat() == catNewline))
861                         h_preamble << t.asInput();
862
863                 else if (t.cat() == catComment) {
864                         static regex const islyxfile("%% LyX .* created this file");
865                         static regex const usercommands("User specified LaTeX commands");
866
867                         string const comment = t.asInput();
868
869                         // magically switch encoding default if it looks like XeLaTeX
870                         static string const magicXeLaTeX =
871                                 "% This document must be compiled with XeLaTeX ";
872                         if (comment.size() > magicXeLaTeX.size() 
873                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
874                                   && h_inputencoding == "auto") {
875                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
876                                 h_inputencoding = "utf8";
877                         }
878                         smatch sub;
879                         if (regex_search(comment, sub, islyxfile)) {
880                                 is_lyx_file = true;
881                                 in_lyx_preamble = true;
882                         } else if (is_lyx_file
883                                    && regex_search(comment, sub, usercommands))
884                                 in_lyx_preamble = false;
885                         else if (!in_lyx_preamble)
886                                 h_preamble << t.asInput();
887                 }
888
889                 else if (t.cs() == "pagestyle")
890                         h_paperpagestyle = p.verbatim_item();
891
892                 else if (t.cs() == "date") {
893                         if (p.verbatim_item().empty())
894                                 h_suppress_date = "true";
895                 }
896
897                 else if (t.cs() == "makeatletter") {
898                         // LyX takes care of this
899                         p.setCatCode('@', catLetter);
900                 }
901
902                 else if (t.cs() == "makeatother") {
903                         // LyX takes care of this
904                         p.setCatCode('@', catOther);
905                 }
906
907                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
908                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
909                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
910                                 || t.cs() == "DeclareRobustCommand"
911                       || t.cs() == "DeclareRobustCommandx"
912                                 || t.cs() == "ProvideTextCommandDefault"
913                                 || t.cs() == "DeclareMathAccent") {
914                         bool star = false;
915                         if (p.next_token().character() == '*') {
916                                 p.get_token();
917                                 star = true;
918                         }
919                         string const name = p.verbatim_item();
920                         string const opt1 = p.getFullOpt();
921                         string const opt2 = p.getFullOpt();
922                         string const body = p.verbatim_item();
923                         // font settings
924                         if (name == "\\rmdefault")
925                                 if (is_known(body, known_roman_fonts))
926                                         h_font_roman = body;
927                         if (name == "\\sfdefault")
928                                 if (is_known(body, known_sans_fonts))
929                                         h_font_sans = body;
930                         if (name == "\\ttdefault")
931                                 if (is_known(body, known_typewriter_fonts))
932                                         h_font_typewriter = body;
933                         if (name == "\\familydefault") {
934                                 string family = body;
935                                 // remove leading "\"
936                                 h_font_default_family = family.erase(0,1);
937                         }
938
939                         // Add the command to the known commands
940                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
941
942                         // only non-lyxspecific stuff
943                         if (!in_lyx_preamble) {
944                                 ostringstream ss;
945                                 ss << '\\' << t.cs();
946                                 if (star)
947                                         ss << '*';
948                                 ss << '{' << name << '}' << opt1 << opt2
949                                    << '{' << body << "}";
950                                 h_preamble << ss.str();
951 /*
952                                 ostream & out = in_preamble ? h_preamble : os;
953                                 out << "\\" << t.cs() << "{" << name << "}"
954                                     << opts << "{" << body << "}";
955 */
956                         }
957                 }
958
959                 else if (t.cs() == "documentclass") {
960                         vector<string>::iterator it;
961                         vector<string> opts = split_options(p.getArg('[', ']'));
962                         handle_opt(opts, known_fontsizes, h_paperfontsize);
963                         delete_opt(opts, known_fontsizes);
964                         // delete "pt" at the end
965                         string::size_type i = h_paperfontsize.find("pt");
966                         if (i != string::npos)
967                                 h_paperfontsize.erase(i);
968                         // The documentclass options are always parsed before the options
969                         // of the babel call so that a language cannot overwrite the babel
970                         // options.
971                         handle_opt(opts, known_languages, h_language);
972                         delete_opt(opts, known_languages);
973
974                         // paper orientation
975                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
976                                 h_paperorientation = "landscape";
977                                 opts.erase(it);
978                         }
979                         // paper sides
980                         if ((it = find(opts.begin(), opts.end(), "oneside"))
981                                  != opts.end()) {
982                                 h_papersides = "1";
983                                 opts.erase(it);
984                         }
985                         if ((it = find(opts.begin(), opts.end(), "twoside"))
986                                  != opts.end()) {
987                                 h_papersides = "2";
988                                 opts.erase(it);
989                         }
990                         // paper columns
991                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
992                                  != opts.end()) {
993                                 h_papercolumns = "1";
994                                 opts.erase(it);
995                         }
996                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
997                                  != opts.end()) {
998                                 h_papercolumns = "2";
999                                 opts.erase(it);
1000                         }
1001                         // paper sizes
1002                         // some size options are know to any document classes, other sizes
1003                         // are handled by the \geometry command of the geometry package
1004                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1005                         delete_opt(opts, known_class_paper_sizes);
1006                         // the remaining options
1007                         h_options = join(opts, ",");
1008                         // FIXME This does not work for classes that have a
1009                         //       different name in LyX than in LaTeX
1010                         h_textclass = p.getArg('{', '}');
1011                 }
1012
1013                 else if (t.cs() == "usepackage") {
1014                         string const options = p.getArg('[', ']');
1015                         string const name = p.getArg('{', '}');
1016                         vector<string> vecnames;
1017                         split(name, vecnames, ',');
1018                         vector<string>::const_iterator it  = vecnames.begin();
1019                         vector<string>::const_iterator end = vecnames.end();
1020                         for (; it != end; ++it)
1021                                 handle_package(p, trimSpaceAndEol(*it), options, 
1022                                                in_lyx_preamble);
1023                 }
1024
1025                 else if (t.cs() == "inputencoding") {
1026                         string const encoding = p.getArg('{','}');
1027                         h_inputencoding = encoding;
1028                         p.setEncoding(encoding);
1029                 }
1030
1031                 else if (t.cs() == "newenvironment") {
1032                         string const name = p.getArg('{', '}');
1033                         string const opt1 = p.getFullOpt();
1034                         string const opt2 = p.getFullOpt();
1035                         string const beg = p.verbatim_item();
1036                         string const end = p.verbatim_item();
1037                         if (!in_lyx_preamble) {
1038                                 h_preamble << "\\newenvironment{" << name
1039                                            << '}' << opt1 << opt2 << '{'
1040                                            << beg << "}{" << end << '}';
1041                         }
1042                         add_known_environment(name, opt1, !opt2.empty(),
1043                                               from_utf8(beg), from_utf8(end));
1044
1045                 }
1046
1047                 else if (t.cs() == "def") {
1048                         string name = p.get_token().cs();
1049                         while (p.next_token().cat() != catBegin)
1050                                 name += p.get_token().cs();
1051                         if (!in_lyx_preamble)
1052                                 h_preamble << "\\def\\" << name << '{'
1053                                            << p.verbatim_item() << "}";
1054                 }
1055
1056                 else if (t.cs() == "newcolumntype") {
1057                         string const name = p.getArg('{', '}');
1058                         trimSpaceAndEol(name);
1059                         int nargs = 0;
1060                         string opts = p.getOpt();
1061                         if (!opts.empty()) {
1062                                 istringstream is(string(opts, 1));
1063                                 is >> nargs;
1064                         }
1065                         special_columns[name[0]] = nargs;
1066                         h_preamble << "\\newcolumntype{" << name << "}";
1067                         if (nargs)
1068                                 h_preamble << "[" << nargs << "]";
1069                         h_preamble << "{" << p.verbatim_item() << "}";
1070                 }
1071
1072                 else if (t.cs() == "setcounter") {
1073                         string const name = p.getArg('{', '}');
1074                         string const content = p.getArg('{', '}');
1075                         if (name == "secnumdepth")
1076                                 h_secnumdepth = content;
1077                         else if (name == "tocdepth")
1078                                 h_tocdepth = content;
1079                         else
1080                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1081                 }
1082
1083                 else if (t.cs() == "setlength") {
1084                         string const name = p.verbatim_item();
1085                         string const content = p.verbatim_item();
1086                         // the paragraphs are only not indented when \parindent is set to zero
1087                         if (name == "\\parindent" && content != "") {
1088                                 if (content[0] == '0')
1089                                         h_paragraph_separation = "skip";
1090                                 else
1091                                         h_paragraph_indentation = translate_len(content);
1092                         } else if (name == "\\parskip") {
1093                                 if (content == "\\smallskipamount")
1094                                         h_defskip = "smallskip";
1095                                 else if (content == "\\medskipamount")
1096                                         h_defskip = "medskip";
1097                                 else if (content == "\\bigskipamount")
1098                                         h_defskip = "bigskip";
1099                                 else
1100                                         h_defskip = content;
1101                         } else
1102                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1103                 }
1104
1105                 else if (t.cs() == "onehalfspacing")
1106                         h_spacing = "onehalf";
1107
1108                 else if (t.cs() == "doublespacing")
1109                         h_spacing = "double";
1110
1111                 else if (t.cs() == "setstretch")
1112                         h_spacing = "other " + p.verbatim_item();
1113
1114                 else if (t.cs() == "begin") {
1115                         string const name = p.getArg('{', '}');
1116                         if (name == "document")
1117                                 break;
1118                         h_preamble << "\\begin{" << name << "}";
1119                 }
1120
1121                 else if (t.cs() == "geometry") {
1122                         h_use_geometry = "true";
1123                         vector<string> opts = split_options(p.getArg('{', '}'));
1124                         vector<string>::iterator it;
1125                         // paper orientation
1126                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1127                                 h_paperorientation = "landscape";
1128                                 opts.erase(it);
1129                         }
1130                         // paper size
1131                         handle_opt(opts, known_paper_sizes, h_papersize);
1132                         delete_opt(opts, known_paper_sizes);
1133                         // page margins
1134                         char const * const * margin = known_paper_margins;
1135                         int k = -1;
1136                         for (; *margin; ++margin) {
1137                                 k += 1;
1138                                 // search for the "=" in e.g. "lmargin=2cm" to get the value
1139                                 for(size_t i = 0; i != opts.size(); i++) {
1140                                         if (opts.at(i).find(*margin) != string::npos) {
1141                                                 string::size_type pos = opts.at(i).find("=");
1142                                                 string value = opts.at(i).substr(pos + 1);
1143                                                 string name = known_coded_paper_margins[k];
1144                                                 h_margins += "\\" + name + " " + value + "\n";
1145                                         }
1146                                 }
1147                         }
1148                 }
1149
1150                 else if (t.cs() == "definecolor") {
1151                         string const color = p.getArg('{', '}');
1152                         string const space = p.getArg('{', '}');
1153                         string const value = p.getArg('{', '}');
1154                         if (color == "document_fontcolor" && space == "rgb") {
1155                                 RGBColor c(RGBColorFromLaTeX(value));
1156                                 h_fontcolor = X11hexname(c);
1157                         } else if (color == "note_fontcolor" && space == "rgb") {
1158                                 RGBColor c(RGBColorFromLaTeX(value));
1159                                 h_notefontcolor = X11hexname(c);
1160                         } else if (color == "page_backgroundcolor" && space == "rgb") {
1161                                 RGBColor c(RGBColorFromLaTeX(value));
1162                                 h_backgroundcolor = X11hexname(c);
1163                         } else if (color == "shadecolor" && space == "rgb") {
1164                                 RGBColor c(RGBColorFromLaTeX(value));
1165                                 h_boxbgcolor = X11hexname(c);
1166                         } else {
1167                                 h_preamble << "\\definecolor{" << color
1168                                            << "}{" << space << "}{" << value
1169                                            << '}';
1170                         }
1171                 }
1172
1173                 else if (t.cs() == "jurabibsetup") {
1174                         // FIXME p.getArg('{', '}') is most probably wrong (it
1175                         //       does not handle nested braces).
1176                         //       Use p.verbatim_item() instead.
1177                         vector<string> jurabibsetup =
1178                                 split_options(p.getArg('{', '}'));
1179                         // add jurabibsetup to the jurabib package options
1180                         add_package("jurabib", jurabibsetup);
1181                         if (!jurabibsetup.empty()) {
1182                                 h_preamble << "\\jurabibsetup{"
1183                                            << join(jurabibsetup, ",") << '}';
1184                         }
1185                 }
1186
1187                 else if (t.cs() == "hypersetup") {
1188                         vector<string> hypersetup =
1189                                 split_options(p.verbatim_item());
1190                         // add hypersetup to the hyperref package options
1191                         handle_hyperref(hypersetup);
1192                         if (!hypersetup.empty()) {
1193                                 h_preamble << "\\hypersetup{"
1194                                            << join(hypersetup, ",") << '}';
1195                         }
1196                 }
1197
1198                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1199                         // prevent misparsing of \usepackage if it is used
1200                         // as an argument (see e.g. our own output of
1201                         // \@ifundefined above)
1202                         string const arg1 = p.verbatim_item();
1203                         string const arg2 = p.verbatim_item();
1204                         string const arg3 = p.verbatim_item();
1205                         // test case \@ifundefined{date}{}{\date{}}
1206                         if (arg1 == "date" && arg2.empty() && arg3 == "\\date{}") {
1207                                 h_suppress_date = "true";
1208                         // test case \@ifundefined{definecolor}{\usepackage{color}}{}
1209                         // because we could pollute the preamble with it in roundtrips
1210                         } else if (arg1 == "definecolor" && arg2 == "\\usepackage{color}"
1211                                 && arg3.empty()) {
1212                                 ifundefined_color_set = true;
1213                         } else if (!in_lyx_preamble) {
1214                                 h_preamble << t.asInput()
1215                                            << '{' << arg1 << '}'
1216                                            << '{' << arg2 << '}'
1217                                            << '{' << arg3 << '}';
1218                         }
1219                 }
1220
1221                 else if (is_known(t.cs(), known_if_commands)) {
1222                         // must not parse anything in conditional code, since
1223                         // LyX would output the parsed contents unconditionally
1224                         if (!in_lyx_preamble)
1225                                 h_preamble << t.asInput();
1226                         handle_if(p, in_lyx_preamble);
1227                 }
1228
1229                 else if (!t.cs().empty() && !in_lyx_preamble)
1230                         h_preamble << '\\' << t.cs();
1231         }
1232
1233         // remove the whitespace
1234         p.skip_spaces();
1235
1236         // Force textclass if the user wanted it
1237         if (!forceclass.empty())
1238                 h_textclass = forceclass;
1239         if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1240                 h_textclass.insert(0, "literate-");
1241         tc.setName(h_textclass);
1242         if (!tc.load()) {
1243                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1244                 exit(EXIT_FAILURE);
1245         }
1246         if (h_papersides.empty()) {
1247                 ostringstream ss;
1248                 ss << tc.sides();
1249                 h_papersides = ss.str();
1250         }
1251         end_preamble(os, tc);
1252 }
1253
1254
1255 /// translates a babel language name to a LyX language name
1256 string babel2lyx(string const & language)
1257 {
1258         char const * const * where = is_known(language, known_languages);
1259         if (where)
1260                 return known_coded_languages[where - known_languages];
1261         return language;
1262 }
1263
1264 // }])
1265
1266
1267 } // namespace lyx