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