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