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