]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/preamble.cpp
tex2lyx/preamble.cpp: take also care of lyxlist, lyxcode, and lyxaddress
[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 == "esint")
329                 h_use_esint = "2";
330
331         else if (name == "babel" && !opts.empty()) {
332                 // check if more than one option was used - used later for inputenc
333                 // in case inputenc is parsed before babel, set the encoding to auto
334                 if (options.begin() != options.end() - 1) {
335                         one_language = false;
336                         h_inputencoding = "auto";
337                 }
338                 // only set the document language when there was not already one set
339                 // via the documentclass options
340                 // babel takes the the last language given in the documentclass options
341                 // as document language. If there is no such language option, the last
342                 // option of its \usepackage call is used.
343                 if (documentclass_language == false) {
344                         handle_opt(options, known_languages, h_language);
345                         delete_opt(options, known_languages);
346                         if (is_known(h_language, known_brazilian_languages))
347                                 h_language = "brazilian";
348                         else if (is_known(h_language, known_french_languages))
349                                 h_language = "french";
350                         else if (is_known(h_language, known_german_languages))
351                                 h_language = "german";
352                         else if (is_known(h_language, known_ngerman_languages))
353                                 h_language = "ngerman";
354                         else if (is_known(h_language, known_portuguese_languages))
355                                 h_language = "portuguese";
356                         else if (is_known(h_language, known_russian_languages))
357                                 h_language = "russian";
358                         else if (is_known(h_language, known_ukrainian_languages))
359                                 h_language = "ukrainian";
360                         h_quotes_language = h_language;
361                 }
362         }
363
364         else if (name == "fontenc")
365                  ;// ignore this
366
367         else if (name == "inputenc") {
368                 // only set when there is not more than one inputenc
369                 // option therefore check for the "," character also
370                 // only set when there is not more then one babel
371                 // language option
372                 if (opts.find(",") == string::npos && one_language == true) {
373                         if (opts == "ascii")
374                                 //change ascii to auto to be in the unicode range, see
375                                 //http://bugzilla.lyx.org/show_bug.cgi?id=4719
376                                 h_inputencoding = "auto";
377                         else if (!opts.empty())
378                                 h_inputencoding = opts;
379                 }
380                 if (!options.empty())
381                         p.setEncoding(options.back());
382                 options.clear();
383         }
384
385         else if (name == "makeidx")
386                 ; // ignore this
387
388         else if (name == "prettyref")
389                 ; // ignore this
390
391         else if (name == "varioref")
392                 ; // ignore this
393
394         else if (name == "verbatim")            
395                 ; // ignore this
396
397         else if (name == "nomencl")
398                 ; // ignore this
399
400         else if (name == "textcomp")
401                 ; // ignore this
402
403         else if (name == "url")
404                 ; // ignore this
405
406         else if (name == "color") {
407                 // with the following command this package is only loaded when needed for
408                 // undefined colors, since we only support the predefined colors
409                 h_preamble << "\\@ifundefined{definecolor}\n {\\usepackage{color}}{}\n";
410         }
411
412         else if (name == "graphicx")
413                 ; // ignore this
414
415         else if (name == "setspace")
416                 ; // ignore this
417
418         else if (name == "geometry")
419                 ; // Ignore this, the geometry settings are made by the \geometry
420                   // command. This command is handled below.
421
422         else if (is_known(name, known_languages)) {
423                 if (is_known(h_language, known_brazilian_languages))
424                                 h_language = "brazilian";
425                 else if (is_known(name, known_french_languages))
426                         h_language = "french";
427                 else if (is_known(name, known_german_languages))
428                         h_language = "german";
429                 else if (is_known(name, known_ngerman_languages))
430                         h_language = "ngerman";
431                 else if (is_known(h_language, known_portuguese_languages))
432                                 h_language = "portuguese";
433                 else if (is_known(name, known_russian_languages))
434                         h_language = "russian";
435                 else if (is_known(name, known_ukrainian_languages))
436                         h_language = "ukrainian";
437                 else
438                         h_language = name;
439                 h_quotes_language = h_language;
440         }
441
442         else if (name == "natbib") {
443                 h_cite_engine = "natbib_authoryear";
444                 vector<string>::iterator it =
445                         find(options.begin(), options.end(), "authoryear");
446                 if (it != options.end())
447                         options.erase(it);
448                 else {
449                         it = find(options.begin(), options.end(), "numbers");
450                         if (it != options.end()) {
451                                 h_cite_engine = "natbib_numerical";
452                                 options.erase(it);
453                         }
454                 }
455         }
456
457         else if (name == "jurabib")
458                 h_cite_engine = "jurabib";
459
460         else if (name == "babel")
461                 ; // ignore this
462
463         else {
464                 if (options.empty())
465                         h_preamble << "\\usepackage{" << name << "}";
466                 else {
467                         h_preamble << "\\usepackage[" << opts << "]{" 
468                                    << name << "}";
469                         options.clear();
470                 }
471         }
472
473         // We need to do something with the options...
474         if (!options.empty())
475                 cerr << "Ignoring options '" << join(options, ",")
476                      << "' of package " << name << '.' << endl;
477
478         // remove the whitespace
479         p.skip_spaces();
480 }
481
482
483
484 void end_preamble(ostream & os, TextClass const & /*textclass*/)
485 {
486         os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
487            << "\\lyxformat 264\n"
488            << "\\begin_document\n"
489            << "\\begin_header\n"
490            << "\\textclass " << h_textclass << "\n";
491         if (!h_preamble.str().empty())
492                 os << "\\begin_preamble\n" << h_preamble.str() << "\n\\end_preamble\n";
493         if (!h_options.empty())
494                 os << "\\options " << h_options << "\n";
495         os << "\\language " << h_language << "\n"
496            << "\\inputencoding " << h_inputencoding << "\n"
497            << "\\font_roman " << h_font_roman << "\n"
498            << "\\font_sans " << h_font_sans << "\n"
499            << "\\font_typewriter " << h_font_typewriter << "\n"
500            << "\\font_default_family " << h_font_default_family << "\n"
501            << "\\font_sc " << h_font_sc << "\n"
502            << "\\font_osf " << h_font_osf << "\n"
503            << "\\font_sf_scale " << h_font_sf_scale << "\n"
504            << "\\font_tt_scale " << h_font_tt_scale << "\n"
505            << "\\graphics " << h_graphics << "\n"
506            << "\\paperfontsize " << h_paperfontsize << "\n"
507            << "\\spacing " << h_spacing << "\n"
508            << "\\papersize " << h_papersize << "\n"
509            << "\\use_geometry " << h_use_geometry << "\n"
510            << "\\use_amsmath " << h_use_amsmath << "\n"
511            << "\\use_esint " << h_use_esint << "\n"
512            << "\\cite_engine " << h_cite_engine << "\n"
513            << "\\use_bibtopic " << h_use_bibtopic << "\n"
514            << "\\paperorientation " << h_paperorientation << "\n"
515            << h_margins
516            << "\\secnumdepth " << h_secnumdepth << "\n"
517            << "\\tocdepth " << h_tocdepth << "\n"
518            << "\\paragraph_separation " << h_paragraph_separation << "\n"
519            << "\\defskip " << h_defskip << "\n"
520            << "\\quotes_language " << h_quotes_language << "\n"
521            << "\\papercolumns " << h_papercolumns << "\n"
522            << "\\papersides " << h_papersides << "\n"
523            << "\\paperpagestyle " << h_paperpagestyle << "\n"
524            << "\\tracking_changes " << h_tracking_changes << "\n"
525            << "\\output_changes " << h_output_changes << "\n"
526            << "\\end_header\n\n"
527            << "\\begin_body\n";
528         // clear preamble for subdocuments
529         h_preamble.str("");
530 }
531
532 } // anonymous namespace
533
534 void parse_preamble(Parser & p, ostream & os, 
535         string const & forceclass, TeX2LyXDocClass & tc)
536 {
537         // initialize fixed types
538         special_columns['D'] = 3;
539         bool is_full_document = false;
540         bool lyx_specific_preamble = false;
541
542         // determine whether this is a full document or a fragment for inclusion
543         while (p.good()) {
544                 Token const & t = p.get_token();
545
546                 if (t.cat() == catEscape && t.cs() == "documentclass") {
547                         is_full_document = true;
548                         break;
549                 }
550         }
551         p.reset();
552
553         while (is_full_document && p.good()) {
554                 Token const & t = p.get_token();
555
556 #ifdef FILEDEBUG
557                 cerr << "t: " << t << "\n";
558 #endif
559
560                 //
561                 // cat codes
562                 //
563                 if ((t.cat() == catLetter ||
564                      t.cat() == catSuper ||
565                      t.cat() == catSub ||
566                      t.cat() == catOther ||
567                      t.cat() == catMath ||
568                      t.cat() == catActive ||
569                      t.cat() == catBegin ||
570                      t.cat() == catEnd ||
571                      t.cat() == catAlign ||
572                      t.cat() == catParameter))
573                         h_preamble << t.character();
574
575                 else if (t.cat() == catSpace || t.cat() == catNewline)
576                         h_preamble << t.asInput();
577
578                 else if (t.cat() == catComment) {
579                         // regex to parse comments (currently not used)
580                         //static regex const islyxfile("%% LyX .* created this file");
581                         string const comment = t.asInput();
582                         // magically switch encoding default if it looks like XeLaTeX
583                         static string const magicXeLaTeX =
584                                 "% This document must be compiled with XeLaTeX ";
585                         if (comment.size() > magicXeLaTeX.size() 
586                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
587                                   && h_inputencoding == "auto") {
588                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
589                                 h_inputencoding = "utf8";
590                         }
591                         smatch sub;
592                         // don't output LyX specific comments
593                         if (!is_known(comment, known_lyx_comments))
594                                 h_preamble << t.asInput();
595                 }
596
597                 else if (t.cs() == "pagestyle")
598                         h_paperpagestyle = p.verbatim_item();
599
600                 else if (t.cs() == "makeatletter") {
601                         // LyX takes care of this
602                         p.setCatCode('@', catLetter);
603                 }
604
605                 else if (t.cs() == "makeatother") {
606                         // LyX takes care of this
607                         p.setCatCode('@', catOther);
608                 }
609
610                 else if (t.cs() == "newcommand" || t.cs() == "renewcommand"
611                             || t.cs() == "providecommand"
612                                 || t.cs() == "DeclareRobustCommand"
613                                 || t.cs() == "ProvideTextCommandDefault"
614                                 || t.cs() == "DeclareMathAccent") {
615                         bool star = false;
616                         if (p.next_token().character() == '*') {
617                                 p.get_token();
618                                 star = true;
619                         }
620                         string const name = p.verbatim_item();
621                         string const opt1 = p.getOpt();
622                         string const opt2 = p.getFullOpt();
623                         string const body = p.verbatim_item();
624                         // font settings
625                         if (name == "\\rmdefault")
626                                 if (is_known(body, known_roman_fonts))
627                                         h_font_roman = body;
628                         if (name == "\\sfdefault")
629                                 if (is_known(body, known_sans_fonts))
630                                         h_font_sans = body;
631                         if (name == "\\ttdefault")
632                                 if (is_known(body, known_typewriter_fonts))
633                                         h_font_typewriter = body;
634                         if (name == "\\familydefault") {
635                                 string family = body;
636                                 // remove leading "\"
637                                 h_font_default_family = family.erase(0,1);
638                         }
639                         // LyX specific commands that will automatically be set by LyX
640                         string lyx_command = name;
641                         // remove the leading "\"
642                         lyx_command.erase(0,1);
643                         lyx_specific_preamble = false;
644                         // allow redefinitions of LyX specific commands
645                         if (is_known(lyx_command, known_lyx_commands)
646                                 && (t.cs() != "renewcommand"))
647                                 lyx_specific_preamble = true;
648                         // only non-lyxspecific stuff
649                         if (!lyx_specific_preamble) {
650                                 ostringstream ss;
651                                 ss << '\\' << t.cs();
652                                 if (star)
653                                         ss << '*';
654                                 ss << '{' << name << '}' << opt1 << opt2
655                                    << '{' << body << "}";
656                                 h_preamble << ss.str();
657
658                                 // Add the command to the known commands
659                                 add_known_command(name, opt1, !opt2.empty());
660 /*
661                                 ostream & out = in_preamble ? h_preamble : os;
662                                 out << "\\" << t.cs() << "{" << name << "}"
663                                     << opts << "{" << body << "}";
664 */
665                         }
666                 }
667
668                 else if (t.cs() == "documentclass") {
669                         vector<string>::iterator it;
670                         vector<string> opts = split_options(p.getArg('[', ']'));
671                         handle_opt(opts, known_fontsizes, h_paperfontsize);
672                         delete_opt(opts, known_fontsizes);
673                         // delete "pt" at the end
674                         string::size_type i = h_paperfontsize.find("pt");
675                         if (i != string::npos)
676                                 h_paperfontsize.erase(i);
677                         // to avoid that the babel options overwrite the documentclass options
678                         documentclass_language = false;
679                         handle_opt(opts, known_languages, h_language);
680                         delete_opt(opts, known_languages);
681                         if (is_known(h_language, known_brazilian_languages))
682                                 h_language = "brazilian";
683                         else if (is_known(h_language, known_french_languages))
684                                 h_language = "french";
685                         else if (is_known(h_language, known_german_languages))
686                                 h_language = "german";
687                         else if (is_known(h_language, known_ngerman_languages))
688                                 h_language = "ngerman";
689                         else if (is_known(h_language, known_portuguese_languages))
690                                 h_language = "portuguese";
691                         else if (is_known(h_language, known_russian_languages))
692                                 h_language = "russian";
693                         else if (is_known(h_language, known_ukrainian_languages))
694                                 h_language = "ukrainian";
695                         h_quotes_language = h_language;
696                         // paper orientation
697                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
698                                 h_paperorientation = "landscape";
699                                 opts.erase(it);
700                         }
701                         // paper sides
702                         if ((it = find(opts.begin(), opts.end(), "oneside"))
703                                  != opts.end()) {
704                                 h_papersides = "1";
705                                 opts.erase(it);
706                         }
707                         if ((it = find(opts.begin(), opts.end(), "twoside"))
708                                  != opts.end()) {
709                                 h_papersides = "2";
710                                 opts.erase(it);
711                         }
712                         // paper columns
713                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
714                                  != opts.end()) {
715                                 h_papercolumns = "1";
716                                 opts.erase(it);
717                         }
718                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
719                                  != opts.end()) {
720                                 h_papercolumns = "2";
721                                 opts.erase(it);
722                         }
723                         // paper sizes
724                         // some size options are know to any document classes, other sizes
725                         // are handled by the \geometry command of the geometry package
726                         handle_opt(opts, known_class_paper_sizes, h_papersize);
727                         delete_opt(opts, known_class_paper_sizes);
728                         // the remaining options
729                         h_options = join(opts, ",");
730                         h_textclass = p.getArg('{', '}');
731                 }
732
733                 else if (t.cs() == "usepackage") {
734                         string const options = p.getArg('[', ']');
735                         string const name = p.getArg('{', '}');
736                         vector<string> vecnames;
737                         split(name, vecnames, ',');
738                         vector<string>::const_iterator it  = vecnames.begin();
739                         vector<string>::const_iterator end = vecnames.end();
740                         for (; it != end; ++it)
741                                 handle_package(p, trim(*it), options);
742                 }
743
744                 else if (t.cs() == "inputencoding") {
745                         string const encoding = p.getArg('{','}');
746                         h_inputencoding = encoding;
747                         p.setEncoding(encoding);
748                 }
749
750                 else if (t.cs() == "newenvironment") {
751                         string const name = p.getArg('{', '}');
752                         ostringstream ss;
753                         // only non LyX specific stuff is output
754                         ss << "\\newenvironment{" << name << "}";
755                         ss << p.getOpt();
756                         ss << p.getOpt();
757                         ss << '{' << p.verbatim_item() << '}';
758                         ss << '{' << p.verbatim_item() << '}';
759                         if (!is_known(name, known_lyx_commands))
760                                 h_preamble << ss.str();
761                 }
762
763                 else if (t.cs() == "def") {
764                         string name = p.get_token().cs();
765                         while (p.next_token().cat() != catBegin)
766                                 name += p.get_token().asString();
767                         if (!is_known(name, known_lyx_commands))
768                                 h_preamble << "\\def\\" << name << '{'
769                                            << p.verbatim_item() << "}";
770                 }
771
772                 else if (t.cs() == "newcolumntype") {
773                         string const name = p.getArg('{', '}');
774                         trim(name);
775                         int nargs = 0;
776                         string opts = p.getOpt();
777                         if (!opts.empty()) {
778                                 istringstream is(string(opts, 1));
779                                 is >> nargs;
780                         }
781                         special_columns[name[0]] = nargs;
782                         h_preamble << "\\newcolumntype{" << name << "}";
783                         if (nargs)
784                                 h_preamble << "[" << nargs << "]";
785                         h_preamble << "{" << p.verbatim_item() << "}";
786                 }
787
788                 else if (t.cs() == "setcounter") {
789                         string const name = p.getArg('{', '}');
790                         string const content = p.getArg('{', '}');
791                         if (name == "secnumdepth")
792                                 h_secnumdepth = content;
793                         else if (name == "tocdepth")
794                                 h_tocdepth = content;
795                         else
796                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
797                 }
798
799                 else if (t.cs() == "setlength") {
800                         string const name = p.verbatim_item();
801                         string const content = p.verbatim_item();
802                         // the paragraphs are only not indented when \parindent is set to zero
803                         if (name == "\\parindent" && content != "") {
804                                 if (content[0] == '0')
805                                         h_paragraph_separation = "skip";
806                         } else if (name == "\\parskip") {
807                                 if (content == "\\smallskipamount")
808                                         h_defskip = "smallskip";
809                                 else if (content == "\\medskipamount")
810                                         h_defskip = "medskip";
811                                 else if (content == "\\bigskipamount")
812                                         h_defskip = "bigskip";
813                                 else
814                                         h_defskip = content;
815                         } else
816                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
817                 }
818
819                 else if (t.cs() == "onehalfspacing")
820                         h_spacing = "onehalf";
821
822                 else if (t.cs() == "doublespacing")
823                         h_spacing = "double";
824
825                 else if (t.cs() == "setstretch")
826                         h_spacing = "other " + p.verbatim_item();
827
828                 else if (t.cs() == "begin") {
829                         string const name = p.getArg('{', '}');
830                         if (name == "document")
831                                 break;
832                         h_preamble << "\\begin{" << name << "}";
833                 }
834
835                 else if (t.cs() == "geometry") {
836                         h_use_geometry = "true";
837                         vector<string> opts = split_options(p.getArg('{', '}'));
838                         vector<string>::iterator it;
839                         // paper orientation
840                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
841                                 h_paperorientation = "landscape";
842                                 opts.erase(it);
843                         }
844                         // paper size
845                         handle_opt(opts, known_paper_sizes, h_papersize);
846                         delete_opt(opts, known_paper_sizes);
847                         // page margins
848                         char const * const * margin = known_paper_margins;
849                         int k = -1;
850                         for (; *margin; ++margin) {
851                                 k += 1;
852                                 // search for the "=" in e.g. "lmargin=2cm" to get the value
853                                 for(size_t i = 0; i != opts.size(); i++) {
854                                         if (opts.at(i).find(*margin) != string::npos) {
855                                                 string::size_type pos = opts.at(i).find("=");
856                                                 string value = opts.at(i).substr(pos + 1);
857                                                 string name = known_coded_paper_margins[k];
858                                                 h_margins += "\\" + name + " " + value + "\n";
859                                         }
860                                 }
861                         }
862                 }
863
864                 else if (t.cs() == "jurabibsetup") {
865                         vector<string> jurabibsetup =
866                                 split_options(p.getArg('{', '}'));
867                         // add jurabibsetup to the jurabib package options
868                         add_package("jurabib", jurabibsetup);
869                         if (!jurabibsetup.empty()) {
870                                 h_preamble << "\\jurabibsetup{"
871                                            << join(jurabibsetup, ",") << '}';
872                         }
873                 }
874
875                 else if (!t.cs().empty())
876                         h_preamble << '\\' << t.cs();
877
878                 // remove the whitespace
879                 p.skip_spaces();
880         }
881
882         // remove the whitespace
883         p.skip_spaces();
884
885         // Force textclass if the user wanted it
886         if (!forceclass.empty())
887                 h_textclass = forceclass;
888         if (noweb_mode && !prefixIs(h_textclass, "literate-"))
889                 h_textclass.insert(0, "literate-");
890         FileName layoutfilename = libFileSearch("layouts", h_textclass, "layout");
891         if (layoutfilename.empty()) {
892                 cerr << "Error: Could not find layout file for textclass \"" << h_textclass << "\"." << endl;
893                 exit(1);
894         }
895         tc.read(layoutfilename);
896         if (h_papersides.empty()) {
897                 ostringstream ss;
898                 ss << tc.sides();
899                 h_papersides = ss.str();
900         }
901         end_preamble(os, tc);
902 }
903
904 // }])
905
906
907 } // namespace lyx