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