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