]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/preamble.cpp
tex2lyx/preamble.cpp: fix a bug reported by JMarc
[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 258\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 is_lyx_file = false;
528         bool lyx_specific_preamble = false;
529
530         // determine whether this is a full document or a fragment for inclusion
531         while (p.good()) {
532                 Token const & t = p.get_token();
533
534                 if (t.cat() == catEscape && t.cs() == "documentclass") {
535                         is_full_document = true;
536                         break;
537                 }
538         }
539         p.reset();
540
541         while (is_full_document && p.good()) {
542                 Token const & t = p.get_token();
543
544 #ifdef FILEDEBUG
545                 cerr << "t: " << t << "\n";
546 #endif
547
548                 //
549                 // cat codes
550                 //
551                 if ((t.cat() == catLetter ||
552                      t.cat() == catSuper ||
553                      t.cat() == catSub ||
554                      t.cat() == catOther ||
555                      t.cat() == catMath ||
556                      t.cat() == catActive ||
557                      t.cat() == catBegin ||
558                      t.cat() == catEnd ||
559                      t.cat() == catAlign ||
560                      t.cat() == catParameter))
561                         h_preamble << t.character();
562
563                 else if (t.cat() == catSpace || t.cat() == catNewline)
564                         h_preamble << t.asInput();
565
566                 else if (t.cat() == catComment) {
567                         // regex to parse comments
568                         static regex const islyxfile("%% LyX .* created this file");
569                         static regex const usercommands("User specified LaTeX commands");
570                         
571                         string const comment = t.asInput();
572                         
573                         // magically switch encoding default if it looks like XeLaTeX
574                         static string const magicXeLaTeX =
575                                 "% This document must be compiled with XeLaTeX ";
576                         if (comment.size() > magicXeLaTeX.size() 
577                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
578                                   && h_inputencoding == "auto") {
579                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
580                                 h_inputencoding = "utf8";
581                         }
582
583                         smatch sub;
584                         if (regex_search(comment, sub, islyxfile))
585                                 is_lyx_file = true;
586                         // don't output LyX specific comments
587                         if (!is_known(comment, known_lyx_comments))
588                                 h_preamble << t.asInput();
589                 }
590
591                 else if (t.cs() == "pagestyle")
592                         h_paperpagestyle = p.verbatim_item();
593
594                 else if (t.cs() == "makeatletter") {
595                         // LyX takes care of this
596                         p.setCatCode('@', catLetter);
597                 }
598
599                 else if (t.cs() == "makeatother") {
600                         // LyX takes care of this
601                         p.setCatCode('@', catOther);
602                 }
603
604                 else if (t.cs() == "newcommand" || t.cs() == "renewcommand"
605                             || t.cs() == "providecommand"
606                                 || t.cs() == "DeclareRobustCommand"
607                                 || t.cs() == "ProvideTextCommandDefault"
608                                 || t.cs() == "DeclareMathAccent") {
609                         bool star = false;
610                         if (p.next_token().character() == '*') {
611                                 p.get_token();
612                                 star = true;
613                         }
614                         string const name = p.verbatim_item();
615                         string const opt1 = p.getOpt();
616                         string const opt2 = p.getFullOpt();
617                         string const body = p.verbatim_item();
618                         // font settings
619                         if (name == "\\rmdefault")
620                                 if (is_known(body, known_roman_fonts))
621                                         h_font_roman = body;
622                         if (name == "\\sfdefault")
623                                 if (is_known(body, known_sans_fonts))
624                                         h_font_sans = body;
625                         if (name == "\\ttdefault")
626                                 if (is_known(body, known_typewriter_fonts))
627                                         h_font_typewriter = body;
628                         if (name == "\\familydefault") {
629                                 string family = body;
630                                 // remove leading "\"
631                                 h_font_default_family = family.erase(0,1);
632                         }
633                         // LyX specific commands that will automatically be set by LyX
634                         string lyx_command = name;
635                         // remove the leading "\"
636                         lyx_command.erase(0,1);
637                         lyx_specific_preamble = false;
638                         // allow redefinitions of LyX specific commands
639                         if (is_known(lyx_command, known_lyx_commands)
640                                 && (t.cs() != "renewcommand"))
641                                 lyx_specific_preamble = true;
642                         // only non-lyxspecific stuff
643                         if (!lyx_specific_preamble) {
644                                 ostringstream ss;
645                                 ss << '\\' << t.cs();
646                                 if (star)
647                                         ss << '*';
648                                 ss << '{' << name << '}' << opt1 << opt2
649                                    << '{' << body << "}";
650                                 h_preamble << ss.str();
651
652                                 // Add the command to the known commands
653                                 add_known_command(name, opt1, !opt2.empty());
654 /*
655                                 ostream & out = in_preamble ? h_preamble : os;
656                                 out << "\\" << t.cs() << "{" << name << "}"
657                                     << opts << "{" << body << "}";
658 */
659                         }
660                 }
661
662                 else if (t.cs() == "documentclass") {
663                         vector<string>::iterator it;
664                         vector<string> opts = split_options(p.getArg('[', ']'));
665                         handle_opt(opts, known_fontsizes, h_paperfontsize);
666                         delete_opt(opts, known_fontsizes);
667                         // delete "pt" at the end
668                         string::size_type i = h_paperfontsize.find("pt");
669                         if (i != string::npos)
670                                 h_paperfontsize.erase(i);
671                         // to avoid that the babel options overwrite the documentclass options
672                         documentclass_language = false;
673                         handle_opt(opts, known_languages, h_language);
674                         delete_opt(opts, known_languages);
675                         if (is_known(h_language, known_french_languages))
676                                 h_language = "french";
677                         else if (is_known(h_language, known_german_languages))
678                                 h_language = "german";
679                         else if (is_known(h_language, known_ngerman_languages))
680                                 h_language = "ngerman";
681                         else if (is_known(h_language, known_russian_languages))
682                                 h_language = "russian";
683                         else if (is_known(h_language, known_ukrainian_languages))
684                                 h_language = "ukrainian";
685                         h_quotes_language = h_language;
686                         // paper orientation
687                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
688                                 h_paperorientation = "landscape";
689                                 opts.erase(it);
690                         }
691                         // paper sides
692                         if ((it = find(opts.begin(), opts.end(), "oneside"))
693                                  != opts.end()) {
694                                 h_papersides = "1";
695                                 opts.erase(it);
696                         }
697                         if ((it = find(opts.begin(), opts.end(), "twoside"))
698                                  != opts.end()) {
699                                 h_papersides = "2";
700                                 opts.erase(it);
701                         }
702                         // paper columns
703                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
704                                  != opts.end()) {
705                                 h_papercolumns = "1";
706                                 opts.erase(it);
707                         }
708                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
709                                  != opts.end()) {
710                                 h_papercolumns = "2";
711                                 opts.erase(it);
712                         }
713                         // paper sizes
714                         // some size options are know to any document classes, other sizes
715                         // are handled by the \geometry command of the geometry package
716                         handle_opt(opts, known_class_paper_sizes, h_papersize);
717                         delete_opt(opts, known_class_paper_sizes);
718                         // the remaining options
719                         h_options = join(opts, ",");
720                         h_textclass = p.getArg('{', '}');
721                 }
722
723                 else if (t.cs() == "usepackage") {
724                         string const options = p.getArg('[', ']');
725                         string const name = p.getArg('{', '}');
726                         vector<string> vecnames;
727                         split(name, vecnames, ',');
728                         vector<string>::const_iterator it  = vecnames.begin();
729                         vector<string>::const_iterator end = vecnames.end();
730                         for (; it != end; ++it)
731                                 handle_package(p, trim(*it), options);
732                 }
733
734                 else if (t.cs() == "inputencoding") {
735                         string const encoding = p.getArg('{','}');
736                         h_inputencoding = encoding;
737                         p.setEncoding(encoding);
738                 }
739
740                 else if (t.cs() == "newenvironment") {
741                         string const name = p.getArg('{', '}');
742                         ostringstream ss;
743                         // only non LyX specific stuff is output
744                         ss << "\\newenvironment{" << name << "}";
745                         ss << p.getOpt();
746                         ss << p.getOpt();
747                         ss << '{' << p.verbatim_item() << '}';
748                         ss << '{' << p.verbatim_item() << '}';
749                         if (!is_known(name, known_lyx_commands))
750                                 h_preamble << ss.str();
751                 }
752
753                 else if (t.cs() == "def") {
754                         string name = p.get_token().cs();
755                         while (p.next_token().cat() != catBegin)
756                                 name += p.get_token().asString();
757                         if (!is_known(name, known_lyx_commands))
758                                 h_preamble << "\\def\\" << name << '{'
759                                            << p.verbatim_item() << "}";
760                 }
761
762                 else if (t.cs() == "newcolumntype") {
763                         string const name = p.getArg('{', '}');
764                         trim(name);
765                         int nargs = 0;
766                         string opts = p.getOpt();
767                         if (!opts.empty()) {
768                                 istringstream is(string(opts, 1));
769                                 is >> nargs;
770                         }
771                         special_columns[name[0]] = nargs;
772                         h_preamble << "\\newcolumntype{" << name << "}";
773                         if (nargs)
774                                 h_preamble << "[" << nargs << "]";
775                         h_preamble << "{" << p.verbatim_item() << "}";
776                 }
777
778                 else if (t.cs() == "setcounter") {
779                         string const name = p.getArg('{', '}');
780                         string const content = p.getArg('{', '}');
781                         if (name == "secnumdepth")
782                                 h_secnumdepth = content;
783                         else if (name == "tocdepth")
784                                 h_tocdepth = content;
785                         else
786                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
787                 }
788
789                 else if (t.cs() == "setlength") {
790                         string const name = p.verbatim_item();
791                         string const content = p.verbatim_item();
792                         // the paragraphs are only not indented when \parindent is set to zero
793                         if (name == "\\parindent" && content != "") {
794                                 if (content[0] == '0')
795                                         h_paragraph_separation = "skip";
796                         } else if (name == "\\parskip") {
797                                 if (content == "\\smallskipamount")
798                                         h_defskip = "smallskip";
799                                 else if (content == "\\medskipamount")
800                                         h_defskip = "medskip";
801                                 else if (content == "\\bigskipamount")
802                                         h_defskip = "bigskip";
803                                 else
804                                         h_defskip = content;
805                         } else
806                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
807                 }
808
809                 else if (t.cs() == "onehalfspacing")
810                         h_spacing = "onehalf";
811
812                 else if (t.cs() == "doublespacing")
813                         h_spacing = "double";
814
815                 else if (t.cs() == "setstretch")
816                         h_spacing = "other " + p.verbatim_item();
817
818                 else if (t.cs() == "begin") {
819                         string const name = p.getArg('{', '}');
820                         if (name == "document")
821                                 break;
822                         h_preamble << "\\begin{" << name << "}";
823                 }
824
825                 else if (t.cs() == "geometry") {
826                         h_use_geometry = "true";
827                         vector<string> opts = split_options(p.getArg('{', '}'));
828                         vector<string>::iterator it;
829                         // paper orientation
830                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
831                                 h_paperorientation = "landscape";
832                                 opts.erase(it);
833                         }
834                         // paper size
835                         handle_opt(opts, known_paper_sizes, h_papersize);
836                         delete_opt(opts, known_paper_sizes);
837                         // page margins
838                         char const * const * margin = known_paper_margins;
839                         int k = -1;
840                         for (; *margin; ++margin) {
841                                 k += 1;
842                                 // search for the "=" in e.g. "lmargin=2cm" to get the value
843                                 for(size_t i = 0; i != opts.size(); i++) {
844                                         if (opts.at(i).find(*margin) != string::npos) {
845                                                 string::size_type pos = opts.at(i).find("=");
846                                                 string value = opts.at(i).substr(pos + 1);
847                                                 string name = known_coded_paper_margins[k];
848                                                 h_margins += "\\" + name + " " + value + "\n";
849                                         }
850                                 }
851                         }
852                 }
853
854                 else if (t.cs() == "jurabibsetup") {
855                         vector<string> jurabibsetup =
856                                 split_options(p.getArg('{', '}'));
857                         // add jurabibsetup to the jurabib package options
858                         add_package("jurabib", jurabibsetup);
859                         if (!jurabibsetup.empty()) {
860                                 h_preamble << "\\jurabibsetup{"
861                                            << join(jurabibsetup, ",") << '}';
862                         }
863                 }
864
865                 else if (!t.cs().empty())
866                         h_preamble << '\\' << t.cs();
867
868                 // remove the whitespace
869                 p.skip_spaces();
870         }
871
872         // remove the whitespace
873         p.skip_spaces();
874
875         // Force textclass if the user wanted it
876         if (!forceclass.empty())
877                 h_textclass = forceclass;
878         if (noweb_mode && !prefixIs(h_textclass, "literate-"))
879                 h_textclass.insert(0, "literate-");
880         FileName layoutfilename = libFileSearch("layouts", h_textclass, "layout");
881         if (layoutfilename.empty()) {
882                 cerr << "Error: Could not find layout file for textclass \"" << h_textclass << "\"." << endl;
883                 exit(1);
884         }
885         tc.read(layoutfilename);
886         if (h_papersides.empty()) {
887                 ostringstream ss;
888                 ss << tc.sides();
889                 h_papersides = ss.str();
890         }
891         end_preamble(os, tc);
892 }
893
894 // }])
895
896
897 } // namespace lyx