]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/preamble.cpp
Fix part of bug #6127: Roundtrip Lyx->Latex->Lyx fails.
[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 <boost/regex.hpp>
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 using boost::regex;
40 using boost::smatch;
41
42 namespace lyx {
43
44 // special columntypes
45 extern map<char, int> special_columns;
46
47 map<string, vector<string> > used_packages;
48
49 // needed to handle encodings with babel
50 bool one_language = true;
51
52 // to avoid that the babel options overwrite the documentclass options
53 bool documentclass_language;
54
55 namespace {
56
57 const char * const known_languages[] = { "afrikaans", "american", "arabic",
58 "austrian", "bahasa", "basque", "belarusian", "brazil", "brazilian", "breton",
59 "british", "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech",
60 "danish", "dutch", "english", "esperanto", "estonian", "finnish", "francais",
61 "french", "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb",
62 "greek", "hebrew", "icelandic", "irish", "italian", "lsorbian", "magyar",
63 "naustrian", "ngerman", "ngermanb", "norsk", "nynorsk", "polish", "portuges",
64 "portuguese", "romanian", "russian", "russianb", "scottish", "serbian", "slovak",
65 "slovene", "spanish", "swedish", "thai", "turkish", "ukraineb", "ukrainian",
66 "usorbian", "welsh", 0};
67
68 //note this when updating to lyxformat 305:
69 //bahasai, indonesian, and indon = equal to bahasa
70 //malay and meyalu = equal to bahasam
71
72 const char * const known_brazilian_languages[] = {"brazil", "brazilian", 0};
73 const char * const known_french_languages[] = {"french", "frenchb", "francais",
74                                                 "frenchle", "frenchpro", 0};
75 const char * const known_german_languages[] = {"german", "germanb", 0};
76 const char * const known_ngerman_languages[] = {"ngerman", "ngermanb", 0};
77 const char * const known_portuguese_languages[] = {"portuges", "portuguese", 0};
78 const char * const known_russian_languages[] = {"russian", "russianb", 0};
79 const char * const known_ukrainian_languages[] = {"ukrainian", "ukraineb", 0};
80
81 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
82
83 const char * const known_roman_fonts[] = { "ae", "bookman", "charter",
84 "cmr", "fourier", "lmodern", "mathpazo", "mathptmx", "newcent", 0};
85
86 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
87 "helvet", "lmss", 0};
88
89 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
90 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
91 "newcent", 0};
92
93 const char * const known_paper_sizes[] = { "a3paper", "b3paper", "a4paper",
94 "b4paper", "a5paper", "b5paper", "executivepaper", "legalpaper",
95 "letterpaper", 0};
96
97 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
98 "executivepaper", "legalpaper", "letterpaper", 0};
99
100 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin", 
101 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
102
103 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
104 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
105 "columnsep", 0};
106
107 const char * const known_lyx_commands[] = { "binom", "cedilla", "cyrtext",
108 "dacute", "dgrave", "docedilla", "doogonek", "dosubhat", "dosubring",
109 "dosubtilde", "greektext", "guillemotleft", "guillemotright", "guilsinglleft",
110 "guilsinglright", "LyX", "lyxadded", "lyxaddress", "lyxarrow", "lyxcode",
111 "lyxdeleted", "lyxdot", "lyxgreyedout", "lyxline", "lyxlist", "lyxmathsym",
112 "LyXParagraphLeftIndent", "lyxrightaddress", "makenomenclature",
113 "mathcircumflex", "noun", "ogonek", "printnomenclature", "quotedblbase",
114 "quotesinglbase", "rcap", "subhat", "subring", "subtilde", "tabularnewline",
115 "textcyr", "textgreek", 0};
116
117 const char * const known_lyx_comments[] = { 
118 "%% Binom macro for standard LaTeX users\n",
119 "%% For printing a cirumflex inside a formula\n",
120 "%% Because html converters don't know tabularnewline\n",
121 "%% The greyedout annotation environment\n",
122 "%% A simple dot to overcome graphicx limitations\n",
123 "%% Change tracking with ulem\n",
124 "% the following is useful when we have the old nomencl.sty package\n",
125 "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% LyX specific LaTeX commands.\n",
126 "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% User specified LaTeX commands.\n",
127 "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Textclass specific LaTeX commands.\n",
128 0};
129
130 // default settings
131 ostringstream h_preamble;
132 string h_textclass               = "article";
133 string h_options                 = string();
134 string h_language                = "english";
135 string h_inputencoding           = "auto";
136 string h_font_roman              = "default";
137 string h_font_sans               = "default";
138 string h_font_typewriter         = "default";
139 string h_font_default_family     = "default";
140 string h_font_sc                 = "false";
141 string h_font_osf                = "false";
142 string h_font_sf_scale           = "100";
143 string h_font_tt_scale           = "100";
144 string h_graphics                = "default";
145 string h_paperfontsize           = "default";
146 string h_spacing                 = "single";
147 string h_papersize               = "default";
148 string h_use_geometry            = "false";
149 string h_use_amsmath             = "1";
150 string h_use_esint               = "1";
151 string h_cite_engine             = "basic";
152 string h_use_bibtopic            = "false";
153 string h_paperorientation        = "portrait";
154 string h_secnumdepth             = "3";
155 string h_tocdepth                = "3";
156 string h_paragraph_separation    = "indent";
157 string h_defskip                 = "medskip";
158 string h_quotes_language         = "english";
159 string h_papercolumns            = "1";
160 string h_papersides              = string();
161 string h_paperpagestyle          = "default";
162 string h_tracking_changes        = "false";
163 string h_output_changes          = "false";
164 string h_margins                 = "";
165
166
167 void handle_opt(vector<string> & opts, char const * const * what, string & target)
168 {
169         if (opts.empty())
170                 return;
171
172         // the last language option is the document language (for babel and LyX)
173         // the last size option is the document font size
174         vector<string>::iterator it;
175         vector<string>::iterator position = opts.begin();
176         for (; *what; ++what) {
177                 it = find(opts.begin(), opts.end(), *what);
178                 if (it != opts.end()) {
179                         documentclass_language = true;
180                         if (it >= position) {
181                                 target = *what;
182                                 position = it;
183                         }
184                 }
185         }
186 }
187
188
189 void delete_opt(vector<string> & opts, char const * const * what)
190 {
191         if (opts.empty())
192                 return;
193
194         // remove found options from the list
195         // do this after handle_opt to avoid potential memory leaks and to be able
196         // to find in every case the last language option
197         vector<string>::iterator it;
198         for (; *what; ++what) {
199                 it = find(opts.begin(), opts.end(), *what);
200                 if (it != opts.end())
201                         opts.erase(it);
202         }
203 }
204
205
206 /*!
207  * Split a package options string (keyval format) into a vector.
208  * Example input:
209  *   authorformat=smallcaps,
210  *   commabeforerest,
211  *   titleformat=colonsep,
212  *   bibformat={tabular,ibidem,numbered}
213  */
214 vector<string> split_options(string const & input)
215 {
216         vector<string> options;
217         string option;
218         Parser p(input);
219         while (p.good()) {
220                 Token const & t = p.get_token();
221                 if (t.asInput() == ",") {
222                         options.push_back(trim(option));
223                         option.erase();
224                 } else if (t.asInput() == "=") {
225                         option += '=';
226                         p.skip_spaces(true);
227                         if (p.next_token().asInput() == "{")
228                                 option += '{' + p.getArg('{', '}') + '}';
229                 } else if (t.cat() != catSpace)
230                         option += t.asInput();
231         }
232
233         if (!option.empty())
234                 options.push_back(trim(option));
235
236         return options;
237 }
238
239
240 /*!
241  * Add package \p name with options \p options to used_packages.
242  * Remove options from \p options that we don't want to output.
243  */
244 void add_package(string const & name, vector<string> & options)
245 {
246         // every package inherits the global options
247         if (used_packages.find(name) == used_packages.end())
248                 used_packages[name] = split_options(h_options);
249
250         vector<string> & v = used_packages[name];
251         v.insert(v.end(), options.begin(), options.end());
252         if (name == "jurabib") {
253                 // Don't output the order argument (see the cite command
254                 // handling code in text.cpp).
255                 vector<string>::iterator end =
256                         remove(options.begin(), options.end(), "natbiborder");
257                 end = remove(options.begin(), end, "jurabiborder");
258                 options.erase(end, options.end());
259         }
260 }
261
262
263 // Given is a string like "scaled=0.9", return 0.9 * 100
264 string const scale_as_percentage(string const & scale)
265 {
266         string::size_type pos = scale.find('=');
267         if (pos != string::npos) {
268                 string value = scale.substr(pos + 1);
269                 if (isStrDbl(value))
270                         return convert<string>(100 * convert<double>(value));
271         }
272         // If the input string didn't match our expectations.
273         // return the default value "100"
274         return "100";
275 }
276
277
278 void handle_package(Parser &p, string const & name, string const & opts)
279 {
280         vector<string> options = split_options(opts);
281         add_package(name, options);
282         string scale;
283
284         // roman fonts
285         if (is_known(name, known_roman_fonts)) {
286                 h_font_roman = name;
287                 p.skip_spaces();
288         }
289
290         if (name == "fourier") {
291                 h_font_roman = "utopia";
292                 // when font uses real small capitals
293                 if (opts == "expert")
294                         h_font_sc = "true";
295         }
296
297         if (name == "mathpazo")
298                 h_font_roman = "palatino";
299
300         if (name == "mathptmx")
301                 h_font_roman = "times";
302
303         // sansserif fonts
304         if (is_known(name, known_sans_fonts)) {
305                 h_font_sans = name;
306                 if (!opts.empty()) {
307                         scale = opts;
308                         h_font_sf_scale = scale_as_percentage(scale);
309                 }
310         }
311
312         // typewriter fonts
313         if (is_known(name, known_typewriter_fonts)) {
314                 h_font_typewriter = name;
315                 if (!opts.empty()) {
316                         scale = opts;
317                         h_font_tt_scale = scale_as_percentage(scale);
318                 }
319         }
320
321         // font uses old-style figure
322         if (name == "eco")
323                 h_font_osf = "true";
324
325         else if (name == "amsmath" || name == "amssymb")
326                 h_use_amsmath = "2";
327
328         else if (name == "amstext")
329                 ;
330
331         else if (name == "esint")
332                 h_use_esint = "2";
333
334         else if (name == "babel" && !opts.empty()) {
335                 // check if more than one option was used - used later for inputenc
336                 // in case inputenc is parsed before babel, set the encoding to auto
337                 if (options.begin() != options.end() - 1) {
338                         one_language = false;
339                         h_inputencoding = "auto";
340                 }
341                 // only set the document language when there was not already one set
342                 // via the documentclass options
343                 // babel takes the the last language given in the documentclass options
344                 // as document language. If there is no such language option, the last
345                 // option of its \usepackage call is used.
346                 if (documentclass_language == false) {
347                         handle_opt(options, known_languages, h_language);
348                         delete_opt(options, known_languages);
349                         if (is_known(h_language, known_brazilian_languages))
350                                 h_language = "brazilian";
351                         else if (is_known(h_language, known_french_languages))
352                                 h_language = "french";
353                         else if (is_known(h_language, known_german_languages))
354                                 h_language = "german";
355                         else if (is_known(h_language, known_ngerman_languages))
356                                 h_language = "ngerman";
357                         else if (is_known(h_language, known_portuguese_languages))
358                                 h_language = "portuguese";
359                         else if (is_known(h_language, known_russian_languages))
360                                 h_language = "russian";
361                         else if (is_known(h_language, known_ukrainian_languages))
362                                 h_language = "ukrainian";
363                         h_quotes_language = h_language;
364                 }
365         }
366
367         else if (name == "fontenc")
368                  ;// ignore this
369
370         else if (name == "inputenc") {
371                 // only set when there is not more than one inputenc
372                 // option therefore check for the "," character also
373                 // only set when there is not more then one babel
374                 // language option
375                 if (opts.find(",") == string::npos && one_language == true) {
376                         if (opts == "ascii")
377                                 //change ascii to auto to be in the unicode range, see
378                                 //http://bugzilla.lyx.org/show_bug.cgi?id=4719
379                                 h_inputencoding = "auto";
380                         else if (!opts.empty())
381                                 h_inputencoding = opts;
382                 }
383                 if (!options.empty())
384                         p.setEncoding(options.back());
385                 options.clear();
386         }
387
388         else if (name == "makeidx")
389                 ; // ignore this
390
391         else if (name == "prettyref")
392                 ; // ignore this
393
394         else if (name == "varioref")
395                 ; // ignore this
396
397         else if (name == "verbatim")            
398                 ; // ignore this
399
400         else if (name == "nomencl")
401                 ; // ignore this
402
403         else if (name == "textcomp")
404                 ; // ignore this
405
406         else if (name == "url")
407                 ; // ignore this
408
409         else if (name == "color") {
410                 // with the following command this package is only loaded when needed for
411                 // undefined colors, since we only support the predefined colors
412                 h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
413         }
414
415         else if (name == "graphicx")
416                 ; // ignore this
417
418         else if (name == "setspace")
419                 ; // ignore this
420
421         else if (name == "geometry")
422                 ; // Ignore this, the geometry settings are made by the \geometry
423                   // command. This command is handled below.
424
425         else if (is_known(name, known_languages)) {
426                 if (is_known(h_language, known_brazilian_languages))
427                                 h_language = "brazilian";
428                 else if (is_known(name, known_french_languages))
429                         h_language = "french";
430                 else if (is_known(name, known_german_languages))
431                         h_language = "german";
432                 else if (is_known(name, known_ngerman_languages))
433                         h_language = "ngerman";
434                 else if (is_known(h_language, known_portuguese_languages))
435                                 h_language = "portuguese";
436                 else if (is_known(name, known_russian_languages))
437                         h_language = "russian";
438                 else if (is_known(name, known_ukrainian_languages))
439                         h_language = "ukrainian";
440                 else
441                         h_language = name;
442                 h_quotes_language = h_language;
443         }
444
445         else if (name == "natbib") {
446                 h_cite_engine = "natbib_authoryear";
447                 vector<string>::iterator it =
448                         find(options.begin(), options.end(), "authoryear");
449                 if (it != options.end())
450                         options.erase(it);
451                 else {
452                         it = find(options.begin(), options.end(), "numbers");
453                         if (it != options.end()) {
454                                 h_cite_engine = "natbib_numerical";
455                                 options.erase(it);
456                         }
457                 }
458         }
459
460         else if (name == "jurabib")
461                 h_cite_engine = "jurabib";
462
463         else if (name == "babel")
464                 ; // ignore this
465
466         else {
467                 if (options.empty())
468                         h_preamble << "\\usepackage{" << name << "}";
469                 else {
470                         h_preamble << "\\usepackage[" << opts << "]{" 
471                                    << name << "}";
472                         options.clear();
473                 }
474         }
475
476         // We need to do something with the options...
477         if (!options.empty())
478                 cerr << "Ignoring options '" << join(options, ",")
479                      << "' of package " << name << '.' << endl;
480
481         // remove the whitespace
482         p.skip_spaces();
483 }
484
485
486
487 void end_preamble(ostream & os, TextClass const & /*textclass*/)
488 {
489         os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
490            << "\\lyxformat 264\n"
491            << "\\begin_document\n"
492            << "\\begin_header\n"
493            << "\\textclass " << h_textclass << "\n";
494         if (!h_preamble.str().empty())
495                 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
496         if (!h_options.empty())
497                 os << "\\options " << h_options << "\n";
498         os << "\\language " << h_language << "\n"
499            << "\\inputencoding " << h_inputencoding << "\n"
500            << "\\font_roman " << h_font_roman << "\n"
501            << "\\font_sans " << h_font_sans << "\n"
502            << "\\font_typewriter " << h_font_typewriter << "\n"
503            << "\\font_default_family " << h_font_default_family << "\n"
504            << "\\font_sc " << h_font_sc << "\n"
505            << "\\font_osf " << h_font_osf << "\n"
506            << "\\font_sf_scale " << h_font_sf_scale << "\n"
507            << "\\font_tt_scale " << h_font_tt_scale << "\n"
508            << "\\graphics " << h_graphics << "\n"
509            << "\\paperfontsize " << h_paperfontsize << "\n"
510            << "\\spacing " << h_spacing << "\n"
511            << "\\papersize " << h_papersize << "\n"
512            << "\\use_geometry " << h_use_geometry << "\n"
513            << "\\use_amsmath " << h_use_amsmath << "\n"
514            << "\\use_esint " << h_use_esint << "\n"
515            << "\\cite_engine " << h_cite_engine << "\n"
516            << "\\use_bibtopic " << h_use_bibtopic << "\n"
517            << "\\paperorientation " << h_paperorientation << "\n"
518            << h_margins
519            << "\\secnumdepth " << h_secnumdepth << "\n"
520            << "\\tocdepth " << h_tocdepth << "\n"
521            << "\\paragraph_separation " << h_paragraph_separation << "\n"
522            << "\\defskip " << h_defskip << "\n"
523            << "\\quotes_language " << h_quotes_language << "\n"
524            << "\\papercolumns " << h_papercolumns << "\n"
525            << "\\papersides " << h_papersides << "\n"
526            << "\\paperpagestyle " << h_paperpagestyle << "\n"
527            << "\\tracking_changes " << h_tracking_changes << "\n"
528            << "\\output_changes " << h_output_changes << "\n"
529            << "\\end_header\n\n"
530            << "\\begin_body\n";
531         // clear preamble for subdocuments
532         h_preamble.str("");
533 }
534
535 } // anonymous namespace
536
537 void parse_preamble(Parser & p, ostream & os, 
538         string const & forceclass, TeX2LyXDocClass & tc)
539 {
540         // initialize fixed types
541         special_columns['D'] = 3;
542         bool is_full_document = false;
543         bool lyx_specific_preamble = false;
544
545         // determine whether this is a full document or a fragment for inclusion
546         while (p.good()) {
547                 Token const & t = p.get_token();
548
549                 if (t.cat() == catEscape && t.cs() == "documentclass") {
550                         is_full_document = true;
551                         break;
552                 }
553         }
554         p.reset();
555
556         while (is_full_document && p.good()) {
557                 Token const & t = p.get_token();
558
559 #ifdef FILEDEBUG
560                 cerr << "t: " << t << "\n";
561 #endif
562
563                 //
564                 // cat codes
565                 //
566                 if ((t.cat() == catLetter ||
567                      t.cat() == catSuper ||
568                      t.cat() == catSub ||
569                      t.cat() == catOther ||
570                      t.cat() == catMath ||
571                      t.cat() == catActive ||
572                      t.cat() == catBegin ||
573                      t.cat() == catEnd ||
574                      t.cat() == catAlign ||
575                      t.cat() == catParameter))
576                         h_preamble << t.character();
577
578                 else if (t.cat() == catSpace || t.cat() == catNewline)
579                         h_preamble << t.asInput();
580
581                 else if (t.cat() == catComment) {
582                         // regex to parse comments (currently not used)
583                         //static regex const islyxfile("%% LyX .* created this file");
584                         string const comment = t.asInput();
585                         // magically switch encoding default if it looks like XeLaTeX
586                         static string const magicXeLaTeX =
587                                 "% This document must be compiled with XeLaTeX ";
588                         if (comment.size() > magicXeLaTeX.size() 
589                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
590                                   && h_inputencoding == "auto") {
591                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
592                                 h_inputencoding = "utf8";
593                         }
594                         smatch sub;
595                         // don't output LyX specific comments
596                         if (!is_known(comment, known_lyx_comments))
597                                 h_preamble << t.asInput();
598                 }
599
600                 else if (t.cs() == "pagestyle")
601                         h_paperpagestyle = p.verbatim_item();
602
603                 else if (t.cs() == "makeatletter") {
604                         // LyX takes care of this
605                         p.setCatCode('@', catLetter);
606                 }
607
608                 else if (t.cs() == "makeatother") {
609                         // LyX takes care of this
610                         p.setCatCode('@', catOther);
611                 }
612
613                 else if (t.cs() == "newcommand" || t.cs() == "renewcommand"
614                             || t.cs() == "providecommand"
615                                 || t.cs() == "DeclareRobustCommand"
616                                 || t.cs() == "ProvideTextCommandDefault"
617                                 || t.cs() == "DeclareMathAccent") {
618                         bool star = false;
619                         if (p.next_token().character() == '*') {
620                                 p.get_token();
621                                 star = true;
622                         }
623                         string const name = p.verbatim_item();
624                         string const opt1 = p.getOpt();
625                         string const opt2 = p.getFullOpt();
626                         string const body = p.verbatim_item();
627                         // font settings
628                         if (name == "\\rmdefault")
629                                 if (is_known(body, known_roman_fonts))
630                                         h_font_roman = body;
631                         if (name == "\\sfdefault")
632                                 if (is_known(body, known_sans_fonts))
633                                         h_font_sans = body;
634                         if (name == "\\ttdefault")
635                                 if (is_known(body, known_typewriter_fonts))
636                                         h_font_typewriter = body;
637                         if (name == "\\familydefault") {
638                                 string family = body;
639                                 // remove leading "\"
640                                 h_font_default_family = family.erase(0,1);
641                         }
642                         // LyX specific commands that will automatically be set by LyX
643                         string lyx_command = name;
644                         // remove the leading "\"
645                         lyx_command.erase(0,1);
646                         lyx_specific_preamble = false;
647                         // allow redefinitions of LyX specific commands
648                         if (is_known(lyx_command, known_lyx_commands)
649                                 && (t.cs() != "renewcommand"))
650                                 lyx_specific_preamble = true;
651                         // only non-lyxspecific stuff
652                         if (!lyx_specific_preamble) {
653                                 ostringstream ss;
654                                 ss << '\\' << t.cs();
655                                 if (star)
656                                         ss << '*';
657                                 ss << '{' << name << '}' << opt1 << opt2
658                                    << '{' << body << "}";
659                                 h_preamble << ss.str();
660
661                                 // Add the command to the known commands
662                                 add_known_command(name, opt1, !opt2.empty());
663 /*
664                                 ostream & out = in_preamble ? h_preamble : os;
665                                 out << "\\" << t.cs() << "{" << name << "}"
666                                     << opts << "{" << body << "}";
667 */
668                         }
669                 }
670
671                 else if (t.cs() == "documentclass") {
672                         vector<string>::iterator it;
673                         vector<string> opts = split_options(p.getArg('[', ']'));
674                         handle_opt(opts, known_fontsizes, h_paperfontsize);
675                         delete_opt(opts, known_fontsizes);
676                         // delete "pt" at the end
677                         string::size_type i = h_paperfontsize.find("pt");
678                         if (i != string::npos)
679                                 h_paperfontsize.erase(i);
680                         // to avoid that the babel options overwrite the documentclass options
681                         documentclass_language = false;
682                         handle_opt(opts, known_languages, h_language);
683                         delete_opt(opts, known_languages);
684                         if (is_known(h_language, known_brazilian_languages))
685                                 h_language = "brazilian";
686                         else if (is_known(h_language, known_french_languages))
687                                 h_language = "french";
688                         else if (is_known(h_language, known_german_languages))
689                                 h_language = "german";
690                         else if (is_known(h_language, known_ngerman_languages))
691                                 h_language = "ngerman";
692                         else if (is_known(h_language, known_portuguese_languages))
693                                 h_language = "portuguese";
694                         else if (is_known(h_language, known_russian_languages))
695                                 h_language = "russian";
696                         else if (is_known(h_language, known_ukrainian_languages))
697                                 h_language = "ukrainian";
698                         h_quotes_language = h_language;
699                         // paper orientation
700                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
701                                 h_paperorientation = "landscape";
702                                 opts.erase(it);
703                         }
704                         // paper sides
705                         if ((it = find(opts.begin(), opts.end(), "oneside"))
706                                  != opts.end()) {
707                                 h_papersides = "1";
708                                 opts.erase(it);
709                         }
710                         if ((it = find(opts.begin(), opts.end(), "twoside"))
711                                  != opts.end()) {
712                                 h_papersides = "2";
713                                 opts.erase(it);
714                         }
715                         // paper columns
716                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
717                                  != opts.end()) {
718                                 h_papercolumns = "1";
719                                 opts.erase(it);
720                         }
721                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
722                                  != opts.end()) {
723                                 h_papercolumns = "2";
724                                 opts.erase(it);
725                         }
726                         // paper sizes
727                         // some size options are know to any document classes, other sizes
728                         // are handled by the \geometry command of the geometry package
729                         handle_opt(opts, known_class_paper_sizes, h_papersize);
730                         delete_opt(opts, known_class_paper_sizes);
731                         // the remaining options
732                         h_options = join(opts, ",");
733                         h_textclass = p.getArg('{', '}');
734                 }
735
736                 else if (t.cs() == "usepackage") {
737                         string const options = p.getArg('[', ']');
738                         string const name = p.getArg('{', '}');
739                         vector<string> vecnames;
740                         split(name, vecnames, ',');
741                         vector<string>::const_iterator it  = vecnames.begin();
742                         vector<string>::const_iterator end = vecnames.end();
743                         for (; it != end; ++it)
744                                 handle_package(p, trim(*it), options);
745                 }
746
747                 else if (t.cs() == "inputencoding") {
748                         string const encoding = p.getArg('{','}');
749                         h_inputencoding = encoding;
750                         p.setEncoding(encoding);
751                 }
752
753                 else if (t.cs() == "newenvironment") {
754                         string const name = p.getArg('{', '}');
755                         ostringstream ss;
756                         // only non LyX specific stuff is output
757                         ss << "\\newenvironment{" << name << "}";
758                         ss << p.getOpt();
759                         ss << p.getOpt();
760                         ss << '{' << p.verbatim_item() << '}';
761                         ss << '{' << p.verbatim_item() << '}';
762                         if (!is_known(name, known_lyx_commands))
763                                 h_preamble << ss.str();
764                 }
765
766                 else if (t.cs() == "def") {
767                         string name = p.get_token().cs();
768                         while (p.next_token().cat() != catBegin)
769                                 name += p.get_token().asString();
770                         if (!is_known(name, known_lyx_commands))
771                                 h_preamble << "\\def\\" << name << '{'
772                                            << p.verbatim_item() << "}";
773                 }
774
775                 else if (t.cs() == "newcolumntype") {
776                         string const name = p.getArg('{', '}');
777                         trim(name);
778                         int nargs = 0;
779                         string opts = p.getOpt();
780                         if (!opts.empty()) {
781                                 istringstream is(string(opts, 1));
782                                 is >> nargs;
783                         }
784                         special_columns[name[0]] = nargs;
785                         h_preamble << "\\newcolumntype{" << name << "}";
786                         if (nargs)
787                                 h_preamble << "[" << nargs << "]";
788                         h_preamble << "{" << p.verbatim_item() << "}";
789                 }
790
791                 else if (t.cs() == "setcounter") {
792                         string const name = p.getArg('{', '}');
793                         string const content = p.getArg('{', '}');
794                         if (name == "secnumdepth")
795                                 h_secnumdepth = content;
796                         else if (name == "tocdepth")
797                                 h_tocdepth = content;
798                         else
799                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
800                 }
801
802                 else if (t.cs() == "setlength") {
803                         string const name = p.verbatim_item();
804                         string const content = p.verbatim_item();
805                         // the paragraphs are only not indented when \parindent is set to zero
806                         if (name == "\\parindent" && content != "") {
807                                 if (content[0] == '0')
808                                         h_paragraph_separation = "skip";
809                         } else if (name == "\\parskip") {
810                                 if (content == "\\smallskipamount")
811                                         h_defskip = "smallskip";
812                                 else if (content == "\\medskipamount")
813                                         h_defskip = "medskip";
814                                 else if (content == "\\bigskipamount")
815                                         h_defskip = "bigskip";
816                                 else
817                                         h_defskip = content;
818                         } else
819                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
820                 }
821
822                 else if (t.cs() == "onehalfspacing")
823                         h_spacing = "onehalf";
824
825                 else if (t.cs() == "doublespacing")
826                         h_spacing = "double";
827
828                 else if (t.cs() == "setstretch")
829                         h_spacing = "other " + p.verbatim_item();
830
831                 else if (t.cs() == "begin") {
832                         string const name = p.getArg('{', '}');
833                         if (name == "document")
834                                 break;
835                         h_preamble << "\\begin{" << name << "}";
836                 }
837
838                 else if (t.cs() == "geometry") {
839                         h_use_geometry = "true";
840                         vector<string> opts = split_options(p.getArg('{', '}'));
841                         vector<string>::iterator it;
842                         // paper orientation
843                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
844                                 h_paperorientation = "landscape";
845                                 opts.erase(it);
846                         }
847                         // paper size
848                         handle_opt(opts, known_paper_sizes, h_papersize);
849                         delete_opt(opts, known_paper_sizes);
850                         // page margins
851                         char const * const * margin = known_paper_margins;
852                         int k = -1;
853                         for (; *margin; ++margin) {
854                                 k += 1;
855                                 // search for the "=" in e.g. "lmargin=2cm" to get the value
856                                 for(size_t i = 0; i != opts.size(); i++) {
857                                         if (opts.at(i).find(*margin) != string::npos) {
858                                                 string::size_type pos = opts.at(i).find("=");
859                                                 string value = opts.at(i).substr(pos + 1);
860                                                 string name = known_coded_paper_margins[k];
861                                                 h_margins += "\\" + name + " " + value + "\n";
862                                         }
863                                 }
864                         }
865                 }
866
867                 else if (t.cs() == "jurabibsetup") {
868                         vector<string> jurabibsetup =
869                                 split_options(p.getArg('{', '}'));
870                         // add jurabibsetup to the jurabib package options
871                         add_package("jurabib", jurabibsetup);
872                         if (!jurabibsetup.empty()) {
873                                 h_preamble << "\\jurabibsetup{"
874                                            << join(jurabibsetup, ",") << '}';
875                         }
876                 }
877
878                 else if (!t.cs().empty())
879                         h_preamble << '\\' << t.cs();
880
881                 // remove the whitespace
882                 p.skip_spaces();
883         }
884
885         // remove the whitespace
886         p.skip_spaces();
887
888         // Force textclass if the user wanted it
889         if (!forceclass.empty())
890                 h_textclass = forceclass;
891         if (noweb_mode && !prefixIs(h_textclass, "literate-"))
892                 h_textclass.insert(0, "literate-");
893         FileName layoutfilename = libFileSearch("layouts", h_textclass, "layout");
894         if (layoutfilename.empty()) {
895                 cerr << "Error: Could not find layout file for textclass \"" << h_textclass << "\"." << endl;
896                 exit(1);
897         }
898         tc.read(layoutfilename);
899         if (h_papersides.empty()) {
900                 ostringstream ss;
901                 ss << tc.sides();
902                 h_papersides = ss.str();
903         }
904         end_preamble(os, tc);
905 }
906
907 // }])
908
909
910 } // namespace lyx