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