]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
4ebfbc1c1a5f736615b447117a4a4b08812555ac
[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 "Preamble.h"
17 #include "tex2lyx.h"
18
19 #include "LayoutFile.h"
20 #include "Layout.h"
21 #include "Lexer.h"
22 #include "TextClass.h"
23
24 #include "support/convert.h"
25 #include "support/FileName.h"
26 #include "support/filetools.h"
27 #include "support/lstrings.h"
28
29 #include "support/regex.h"
30
31 #include <algorithm>
32 #include <iostream>
33
34 using namespace std;
35 using namespace lyx::support;
36
37
38 namespace lyx {
39
40 // special columntypes
41 extern map<char, int> special_columns;
42
43 Preamble preamble;
44
45 namespace {
46
47 //add this to known_languages when updating to lyxformat 266:
48 // "armenian" (needs special handling since not supported by standard babel)
49 //add these to known_languages when updating to lyxformat 268:
50 //"chinese-simplified", "chinese-traditional", "japanese", "korean"
51 // Both changes require first that support for non-babel languages (CJK,
52 // armtex) is added.
53 /**
54  * known babel language names (including synonyms)
55  * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
56  * not yet supported by LyX: kurmanji
57  * please keep this in sync with known_coded_languages line by line!
58  */
59 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
60 "american", "arabic", "arabtex", "austrian", "bahasa", "bahasai", "bahasam",
61 "basque", "belarusian", "brazil", "brazilian", "breton", "british", "bulgarian",
62 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
63 "english", "esperanto", "estonian", "farsi", "finnish", "francais", "french",
64 "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb", "greek",
65 "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
66 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
67 "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian", "newzealand",
68 "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
69 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
70 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
71 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
72 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
73 0};
74
75 /**
76  * the same as known_languages with .lyx names
77  * please keep this in sync with known_languages line by line!
78  */
79 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
80 "american", "arabic_arabi", "arabic_arabtex", "austrian", "bahasa", "bahasa", "bahasam",
81 "basque", "belarusian", "brazilian", "brazilian", "breton", "british", "bulgarian",
82 "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch",
83 "english", "esperanto", "estonian", "farsi", "finnish", "french", "french",
84 "french", "french", "french", "galician", "german", "german", "greek",
85 "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
86 "irish", "italian", "kazakh", "latin", "latvian", "lithuanian", "lowersorbian",
87 "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian", "english",
88 "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
89 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
90 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
91 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
92 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
93 0};
94
95 /// languages with english quotes (.lyx names)
96 const char * const known_english_quotes_languages[] = {"american", "bahasa",
97 "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
98 "esperanto", "hebrew", "irish", "korean", "portuguese", "scottish", "thai", 0};
99
100 /// languages with french quotes (.lyx names)
101 const char * const known_french_quotes_languages[] = {"albanian",
102 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
103 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
104 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
105 "vietnamese", 0};
106
107 /// languages with german quotes (.lyx names)
108 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
109 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
110 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
111
112 /// languages with polish quotes (.lyx names)
113 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
114 "dutch", "estonian", "magyar", "polish", "romanian", 0};
115
116 /// languages with swedish quotes (.lyx names)
117 const char * const known_swedish_quotes_languages[] = {"finnish",
118 "swedish", 0};
119
120 /// known language packages from the times before babel
121 const char * const known_old_language_packages[] = {"french", "frenchle",
122 "frenchpro", "german", "ngerman", "pmfrench", 0};
123
124 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
125
126 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
127 "ccfonts", "chancery", "charter", "cmr", "fourier", "lmodern", "mathpazo",
128 "mathptmx", "newcent", "utopia", 0};
129
130 const char * const known_sans_fonts[] = { "avant", "berasans", "cmbr", "cmss",
131 "helvet", "lmss", 0};
132
133 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
134 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
135 "newcent", 0};
136
137 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
138 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
139 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
140 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
141 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
142
143 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
144 "executivepaper", "legalpaper", "letterpaper", 0};
145
146 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin", 
147 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
148
149 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
150 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
151 "columnsep", 0};
152
153 /// commands that can start an \if...\else...\endif sequence
154 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
155 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
156 "ifsidecap", "ifupgreek", 0};
157
158 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
159 "magenta", "red", "white", "yellow", 0};
160
161 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00",
162 "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
163
164 /// conditional commands with three arguments like \@ifundefined{}{}{}
165 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
166 0};
167
168 /// packages that work only in xetex
169 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
170 "fontbook", "fontwrap", "mathspec", "philokalia", "polyglossia", "unisugar",
171 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
172
173 // codes used to remove packages that are loaded automatically by LyX.
174 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
175 const char package_beg_sep = '\001';
176 const char package_mid_sep = '\002';
177 const char package_end_sep = '\003';
178
179
180 // returns true if at least one of the options in what has been found
181 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
182 {
183         if (opts.empty())
184                 return false;
185
186         bool found = false;
187         // the last language option is the document language (for babel and LyX)
188         // the last size option is the document font size
189         vector<string>::iterator it;
190         vector<string>::iterator position = opts.begin();
191         for (; *what; ++what) {
192                 it = find(opts.begin(), opts.end(), *what);
193                 if (it != opts.end()) {
194                         if (it >= position) {
195                                 found = true;
196                                 target = *what;
197                                 position = it;
198                         }
199                 }
200         }
201         return found;
202 }
203
204
205 void delete_opt(vector<string> & opts, char const * const * what)
206 {
207         if (opts.empty())
208                 return;
209
210         // remove found options from the list
211         // do this after handle_opt to avoid potential memory leaks
212         vector<string>::iterator it;
213         for (; *what; ++what) {
214                 it = find(opts.begin(), opts.end(), *what);
215                 if (it != opts.end())
216                         opts.erase(it);
217         }
218 }
219
220
221 /*!
222  * Split a package options string (keyval format) into a vector.
223  * Example input:
224  *   authorformat=smallcaps,
225  *   commabeforerest,
226  *   titleformat=colonsep,
227  *   bibformat={tabular,ibidem,numbered}
228  */
229 vector<string> split_options(string const & input)
230 {
231         vector<string> options;
232         string option;
233         Parser p(input);
234         while (p.good()) {
235                 Token const & t = p.get_token();
236                 if (t.asInput() == ",") {
237                         options.push_back(trimSpaceAndEol(option));
238                         option.erase();
239                 } else if (t.asInput() == "=") {
240                         option += '=';
241                         p.skip_spaces(true);
242                         if (p.next_token().asInput() == "{")
243                                 option += '{' + p.getArg('{', '}') + '}';
244                 } else if (t.cat() != catSpace)
245                         option += t.asInput();
246         }
247
248         if (!option.empty())
249                 options.push_back(trimSpaceAndEol(option));
250
251         return options;
252 }
253
254
255 /*!
256  * Retrieve a keyval option "name={value with=sign}" named \p name from
257  * \p options and return the value.
258  * The found option is also removed from \p options.
259  */
260 string process_keyval_opt(vector<string> & options, string name)
261 {
262         for (size_t i = 0; i < options.size(); ++i) {
263                 vector<string> option;
264                 split(options[i], option, '=');
265                 if (option.size() < 2)
266                         continue;
267                 if (option[0] == name) {
268                         options.erase(options.begin() + i);
269                         option.erase(option.begin());
270                         return join(option, "=");
271                 }
272         }
273         return "";
274 }
275
276 } // anonymous namespace
277
278
279 bool Preamble::indentParagraphs() const
280 {
281         return h_paragraph_separation == "indent";
282 }
283
284
285 bool Preamble::isPackageUsed(string const & package) const
286 {
287         return used_packages.find(package) != used_packages.end();
288 }
289
290
291 vector<string> Preamble::getPackageOptions(string const & package) const
292 {
293         map<string, vector<string> >::const_iterator it = used_packages.find(package);
294         if (it != used_packages.end())
295                 return it->second;
296         return vector<string>();
297 }
298
299
300 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
301 {
302         auto_packages.insert(package);
303 }
304
305
306 void Preamble::addModule(string const & module)
307 {
308         used_modules.push_back(module);
309 }
310
311
312 void Preamble::suppressDate(bool suppress)
313 {
314         if (suppress)
315                 h_suppress_date = "true";
316         else
317                 h_suppress_date = "false";
318 }
319
320
321 void Preamble::registerAuthor(std::string const & name)
322 {
323         Author author(from_utf8(name), empty_docstring());
324         author.setUsed(true);
325         authors_.record(author);
326         h_tracking_changes = "true";
327         h_output_changes = "true";
328 }
329
330
331 Author const & Preamble::getAuthor(std::string const & name) const
332 {
333         Author author(from_utf8(name), empty_docstring());
334         for (AuthorList::Authors::const_iterator it = authors_.begin();
335              it != authors_.end(); it++)
336                 if (*it == author)
337                         return *it;
338         static Author const dummy;
339         return dummy;
340 }
341
342
343 void Preamble::add_package(string const & name, vector<string> & options)
344 {
345         // every package inherits the global options
346         if (used_packages.find(name) == used_packages.end())
347                 used_packages[name] = split_options(h_options);
348
349         vector<string> & v = used_packages[name];
350         v.insert(v.end(), options.begin(), options.end());
351         if (name == "jurabib") {
352                 // Don't output the order argument (see the cite command
353                 // handling code in text.cpp).
354                 vector<string>::iterator end =
355                         remove(options.begin(), options.end(), "natbiborder");
356                 end = remove(options.begin(), end, "jurabiborder");
357                 options.erase(end, options.end());
358         }
359 }
360
361
362 namespace {
363
364 // Given is a string like "scaled=0.9", return 0.9 * 100
365 string const scale_as_percentage(string const & scale)
366 {
367         string::size_type pos = scale.find('=');
368         if (pos != string::npos) {
369                 string value = scale.substr(pos + 1);
370                 if (isStrDbl(value))
371                         return convert<string>(100 * convert<double>(value));
372         }
373         // If the input string didn't match our expectations.
374         // return the default value "100"
375         return "100";
376 }
377
378
379 string remove_braces(string const & value)
380 {
381         if (value.empty())
382                 return value;
383         if (value[0] == '{' && value[value.length()-1] == '}')
384                 return value.substr(1, value.length()-2);
385         return value;
386 }
387
388 } // anonymous namespace
389
390
391 Preamble::Preamble() : one_language(true)
392 {
393         //h_backgroundcolor;
394         //h_boxbgcolor;
395         h_cite_engine             = "basic";
396         h_defskip                 = "medskip";
397         //h_float_placement;
398         //h_fontcolor;
399         h_fontencoding            = "default";
400         h_font_roman              = "default";
401         h_font_sans               = "default";
402         h_font_typewriter         = "default";
403         h_font_default_family     = "default";
404         h_font_sc                 = "false";
405         h_font_osf                = "false";
406         h_font_sf_scale           = "100";
407         h_font_tt_scale           = "100";
408         h_graphics                = "default";
409         h_html_be_strict          = "false";
410         h_html_css_as_file        = "0";
411         h_html_math_output        = "0";
412         h_inputencoding           = "auto";
413         h_language                = "english";
414         h_language_package        = "none";
415         //h_listings_params;
416         //h_margins;
417         //h_notefontcolor;
418         //h_options;
419         h_output_changes          = "false";
420         h_papercolumns            = "1";
421         h_paperfontsize           = "default";
422         h_paperorientation        = "portrait";
423         h_paperpagestyle          = "default";
424         //h_papersides;
425         h_papersize               = "default";
426         h_paragraph_indentation   = "default";
427         h_paragraph_separation    = "indent";
428         //h_pdf_title;
429         //h_pdf_author;
430         //h_pdf_subject;
431         //h_pdf_keywords;
432         h_pdf_bookmarks           = "1";
433         h_pdf_bookmarksnumbered   = "0";
434         h_pdf_bookmarksopen       = "0";
435         h_pdf_bookmarksopenlevel  = "1";
436         h_pdf_breaklinks          = "0";
437         h_pdf_pdfborder           = "0";
438         h_pdf_colorlinks          = "0";
439         h_pdf_backref             = "section";
440         h_pdf_pdfusetitle         = "1";
441         //h_pdf_pagemode;
442         //h_pdf_quoted_options;
443         h_quotes_language         = "english";
444         h_secnumdepth             = "3";
445         h_spacing                 = "single";
446         h_suppress_date           = "false";
447         h_textclass               = "article";
448         h_tocdepth                = "3";
449         h_tracking_changes        = "false";
450         h_use_bibtopic            = "false";
451         h_use_geometry            = "false";
452         h_use_amsmath             = "1";
453         h_use_default_options     = "false";
454         h_use_esint               = "1";
455         h_use_hyperref            = "0";
456         h_use_mhchem              = "0";
457         h_use_mathdots            = "0";
458         h_use_refstyle            = "0";
459         h_use_undertilde          = "0";
460 }
461
462
463 void Preamble::handle_hyperref(vector<string> & options)
464 {
465         // FIXME swallow inputencoding changes that might surround the
466         //       hyperref setup if it was written by LyX
467         h_use_hyperref = "1";
468         // swallow "unicode=true", since LyX does always write that
469         vector<string>::iterator it =
470                 find(options.begin(), options.end(), "unicode=true");
471         if (it != options.end())
472                 options.erase(it);
473         it = find(options.begin(), options.end(), "pdfusetitle");
474         if (it != options.end()) {
475                 h_pdf_pdfusetitle = "1";
476                 options.erase(it);
477         }
478         string bookmarks = process_keyval_opt(options, "bookmarks");
479         if (bookmarks == "true")
480                 h_pdf_bookmarks = "1";
481         else if (bookmarks == "false")
482                 h_pdf_bookmarks = "0";
483         if (h_pdf_bookmarks == "1") {
484                 string bookmarksnumbered =
485                         process_keyval_opt(options, "bookmarksnumbered");
486                 if (bookmarksnumbered == "true")
487                         h_pdf_bookmarksnumbered = "1";
488                 else if (bookmarksnumbered == "false")
489                         h_pdf_bookmarksnumbered = "0";
490                 string bookmarksopen =
491                         process_keyval_opt(options, "bookmarksopen");
492                 if (bookmarksopen == "true")
493                         h_pdf_bookmarksopen = "1";
494                 else if (bookmarksopen == "false")
495                         h_pdf_bookmarksopen = "0";
496                 if (h_pdf_bookmarksopen == "1") {
497                         string bookmarksopenlevel =
498                                 process_keyval_opt(options, "bookmarksopenlevel");
499                         if (!bookmarksopenlevel.empty())
500                                 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
501                 }
502         }
503         string breaklinks = process_keyval_opt(options, "breaklinks");
504         if (breaklinks == "true")
505                 h_pdf_breaklinks = "1";
506         else if (breaklinks == "false")
507                 h_pdf_breaklinks = "0";
508         string pdfborder = process_keyval_opt(options, "pdfborder");
509         if (pdfborder == "{0 0 0}")
510                 h_pdf_pdfborder = "1";
511         else if (pdfborder == "{0 0 1}")
512                 h_pdf_pdfborder = "0";
513         string backref = process_keyval_opt(options, "backref");
514         if (!backref.empty())
515                 h_pdf_backref = backref;
516         string colorlinks = process_keyval_opt(options, "colorlinks");
517         if (colorlinks == "true")
518                 h_pdf_colorlinks = "1";
519         else if (colorlinks == "false")
520                 h_pdf_colorlinks = "0";
521         string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
522         if (!pdfpagemode.empty())
523                 h_pdf_pagemode = pdfpagemode;
524         string pdftitle = process_keyval_opt(options, "pdftitle");
525         if (!pdftitle.empty()) {
526                 h_pdf_title = remove_braces(pdftitle);
527         }
528         string pdfauthor = process_keyval_opt(options, "pdfauthor");
529         if (!pdfauthor.empty()) {
530                 h_pdf_author = remove_braces(pdfauthor);
531         }
532         string pdfsubject = process_keyval_opt(options, "pdfsubject");
533         if (!pdfsubject.empty())
534                 h_pdf_subject = remove_braces(pdfsubject);
535         string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
536         if (!pdfkeywords.empty())
537                 h_pdf_keywords = remove_braces(pdfkeywords);
538         if (!options.empty()) {
539                 if (!h_pdf_quoted_options.empty())
540                         h_pdf_quoted_options += ',';
541                 h_pdf_quoted_options += join(options, ",");
542                 options.clear();
543         }
544 }
545
546
547 void Preamble::handle_geometry(vector<string> & options)
548 {
549         h_use_geometry = "true";
550         vector<string>::iterator it;
551         // paper orientation
552         if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
553                 h_paperorientation = "landscape";
554                 options.erase(it);
555         }
556         // paper size
557         // keyval version: "paper=letter"
558         string paper = process_keyval_opt(options, "paper");
559         if (!paper.empty())
560                 h_papersize = paper + "paper";
561         // alternative version: "letterpaper"
562         handle_opt(options, known_paper_sizes, h_papersize);
563         delete_opt(options, known_paper_sizes);
564         // page margins
565         char const * const * margin = known_paper_margins;
566         for (; *margin; ++margin) {
567                 string value = process_keyval_opt(options, *margin);
568                 if (!value.empty()) {
569                         int k = margin - known_paper_margins;
570                         string name = known_coded_paper_margins[k];
571                         h_margins += '\\' + name + ' ' + value + '\n';
572                 }
573         }
574 }
575
576
577 void Preamble::handle_package(Parser &p, string const & name,
578                               string const & opts, bool in_lyx_preamble)
579 {
580         vector<string> options = split_options(opts);
581         add_package(name, options);
582         string scale;
583
584         if (is_known(name, known_xetex_packages))
585                 xetex = true;
586
587         // roman fonts
588         if (is_known(name, known_roman_fonts)) {
589                 h_font_roman = name;
590                 p.skip_spaces();
591         }
592
593         if (name == "fourier") {
594                 h_font_roman = "utopia";
595                 // when font uses real small capitals
596                 if (opts == "expert")
597                         h_font_sc = "true";
598         }
599
600         if (name == "mathpazo")
601                 h_font_roman = "palatino";
602
603         if (name == "mathptmx")
604                 h_font_roman = "times";
605
606         // sansserif fonts
607         if (is_known(name, known_sans_fonts)) {
608                 h_font_sans = name;
609                 if (!opts.empty()) {
610                         scale = opts;
611                         h_font_sf_scale = scale_as_percentage(scale);
612                 }
613         }
614
615         // typewriter fonts
616         if (is_known(name, known_typewriter_fonts)) {
617                 // fourier can be set as roman font _only_
618                 // fourier as typewriter is handled in handling of \ttdefault
619                 if (name != "fourier") {
620                         h_font_typewriter = name;
621                         if (!opts.empty()) {
622                                 scale = opts;
623                                 h_font_tt_scale = scale_as_percentage(scale);
624                         }
625                 }
626         }
627
628         // font uses old-style figure
629         if (name == "eco")
630                 h_font_osf = "true";
631
632         // after the detection and handling of special cases, we can remove the
633         // fonts, otherwise they would appear in the preamble, see bug #7856
634         if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
635                 ||      is_known(name, known_typewriter_fonts))
636                 ;
637
638         else if (name == "amsmath" || name == "amssymb")
639                 h_use_amsmath = "2";
640
641         else if (name == "esint")
642                 h_use_esint = "2";
643
644         else if (name == "mhchem")
645                 h_use_mhchem = "2";
646
647         else if (name == "mathdots")
648                 h_use_mathdots = "2";
649
650         else if (name == "undertilde")
651                 h_use_undertilde = "2";
652
653         else if (name == "babel") {
654                 h_language_package = "default";
655                 // One might think we would have to do nothing if babel is loaded
656                 // without any options to prevent pollution of the preamble with this
657                 // babel call in every roundtrip.
658                 // But the user could have defined babel-specific things afterwards. So
659                 // we need to keep it in the preamble to prevent cases like bug #7861.
660                 if (!opts.empty()) {
661                         // check if more than one option was used - used later for inputenc
662                         if (options.begin() != options.end() - 1)
663                                 one_language = false;
664                         // babel takes the last language of the option of its \usepackage
665                         // call as document language. If there is no such language option, the
666                         // last language in the documentclass options is used.
667                         handle_opt(options, known_languages, h_language);
668                         // If babel is called with options, LyX puts them by default into the
669                         // document class options. This works for most languages, except
670                         // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
671                         // perhaps in future others.
672                         // Therefore keep the babel call as it is as the user might have
673                         // reasons for it.
674                         h_preamble << "\\usepackage[" << opts << "]{babel}\n";
675                         delete_opt(options, known_languages);
676                 }
677                 else
678                         h_preamble << "\\usepackage{babel}\n";                                          
679         }
680
681         else if (name == "fontenc") {
682                 h_fontencoding = getStringFromVector(options, ",");
683                 /* We could do the following for better round trip support,
684                  * but this makes the document less portable, so I skip it:
685                 if (h_fontencoding == lyxrc.fontenc)
686                         h_fontencoding = "global";
687                  */
688                 options.clear();
689         }
690
691         else if (name == "inputenc" || name == "luainputenc") {
692                 // h_inputencoding is only set when there is not more than one
693                 // inputenc option because otherwise h_inputencoding must be
694                 // set to "auto" (the default encoding of the document language)
695                 // Therefore check for the "," character.
696                 // It is also only set when there is not more than one babel
697                 // language option.
698                 if (opts.find(",") == string::npos && one_language == true)
699                         h_inputencoding = opts;
700                 if (!options.empty())
701                         p.setEncoding(options.back());
702                 options.clear();
703         }
704
705         else if (is_known(name, known_old_language_packages)) {
706                 // known language packages from the times before babel
707                 // if they are found and not also babel, they will be used as
708                 // cutom language package
709                 h_language_package = "\\usepackage{" + name + "}";
710         }
711
712         else if (name == "makeidx")
713                 ; // ignore this
714
715         else if (name == "prettyref")
716                 ; // ignore this
717
718         else if (name == "varioref")
719                 ; // ignore this
720
721         else if (name == "verbatim")
722                 ; // ignore this
723
724         else if (name == "nomencl")
725                 ; // ignore this
726
727         else if (name == "textcomp")
728                 ; // ignore this
729
730         else if (name == "url")
731                 ; // ignore this
732
733         else if (name == "booktabs" || name == "color" ||
734                  name == "longtable" || name == "subscript" ||
735                  name == "ulem") {
736                 if (!in_lyx_preamble)
737                         h_preamble << package_beg_sep << name
738                                    << package_mid_sep << "\\usepackage{"
739                                    << name << '}' << package_end_sep;
740         }
741
742         else if (name == "graphicx")
743                 ; // ignore this
744
745         else if (name == "setspace")
746                 ; // ignore this
747
748         else if (name == "geometry")
749                 handle_geometry(options);
750
751         else if (name == "rotfloat")
752                 ; // ignore this
753
754         else if (name == "wrapfig")
755                 ; // ignore this
756
757         else if (name == "subfig")
758                 ; // ignore this
759
760         else if (is_known(name, known_languages))
761                 h_language = name;
762
763         else if (name == "natbib") {
764                 h_cite_engine = "natbib_authoryear";
765                 vector<string>::iterator it =
766                         find(options.begin(), options.end(), "authoryear");
767                 if (it != options.end())
768                         options.erase(it);
769                 else {
770                         it = find(options.begin(), options.end(), "numbers");
771                         if (it != options.end()) {
772                                 h_cite_engine = "natbib_numerical";
773                                 options.erase(it);
774                         }
775                 }
776         }
777
778         else if (name == "jurabib")
779                 h_cite_engine = "jurabib";
780
781         else if (name == "hyperref")
782                 handle_hyperref(options);
783
784         else if (!in_lyx_preamble) {
785                 if (options.empty())
786                         h_preamble << "\\usepackage{" << name << "}";
787                 else {
788                         h_preamble << "\\usepackage[" << opts << "]{" 
789                                    << name << "}";
790                         options.clear();
791                 }
792         }
793
794         // We need to do something with the options...
795         if (!options.empty())
796                 cerr << "Ignoring options '" << join(options, ",")
797                      << "' of package " << name << '.' << endl;
798
799         // remove the whitespace
800         p.skip_spaces();
801 }
802
803
804 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
805 {
806         while (p.good()) {
807                 Token t = p.get_token();
808                 if (t.cat() == catEscape &&
809                     is_known(t.cs(), known_if_commands))
810                         handle_if(p, in_lyx_preamble);
811                 else {
812                         if (!in_lyx_preamble)
813                                 h_preamble << t.asInput();
814                         if (t.cat() == catEscape && t.cs() == "fi")
815                                 return;
816                 }
817         }
818 }
819
820
821 bool Preamble::writeLyXHeader(ostream & os)
822 {
823         // translate from babel to LyX names
824         h_language = babel2lyx(h_language);
825
826         // set the quote language
827         // LyX only knows the following quotes languages:
828         // english, swedish, german, polish, french and danish
829         // (quotes for "japanese" and "chinese-traditional" are missing because
830         //  they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
831         // conversion list taken from
832         // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
833         // (quotes for kazakh and interlingua are unknown)
834         // danish
835         if (h_language == "danish")
836                 h_quotes_language = "danish";
837         // french
838         else if (is_known(h_language, known_french_quotes_languages))
839                 h_quotes_language = "french";
840         // german
841         else if (is_known(h_language, known_german_quotes_languages))
842                 h_quotes_language = "german";
843         // polish
844         else if (is_known(h_language, known_polish_quotes_languages))
845                 h_quotes_language = "polish";
846         // swedish
847         else if (is_known(h_language, known_swedish_quotes_languages))
848                 h_quotes_language = "swedish";
849         //english
850         else if (is_known(h_language, known_english_quotes_languages))
851                 h_quotes_language = "english";
852
853         // output the LyX file settings
854         os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
855            << "\\lyxformat " << LYX_FORMAT << '\n'
856            << "\\begin_document\n"
857            << "\\begin_header\n"
858            << "\\textclass " << h_textclass << "\n";
859         string const raw = h_preamble.str();
860         if (!raw.empty()) {
861                 os << "\\begin_preamble\n";
862                 for (string::size_type i = 0; i < raw.size(); ++i) {
863                         if (raw[i] == package_beg_sep) {
864                                 // Here follows some package loading code that
865                                 // must be skipped if the package is loaded
866                                 // automatically.
867                                 string::size_type j = raw.find(package_mid_sep, i);
868                                 if (j == string::npos)
869                                         return false;
870                                 string::size_type k = raw.find(package_end_sep, j);
871                                 if (k == string::npos)
872                                         return false;
873                                 string const package = raw.substr(i + 1, j - i - 1);
874                                 string const replacement = raw.substr(j + 1, k - j - 1);
875                                 if (auto_packages.find(package) == auto_packages.end())
876                                         os << replacement;
877                                 i = k;
878                         } else
879                                 os.put(raw[i]);
880                 }
881                 os << "\n\\end_preamble\n";
882         }
883         if (!h_options.empty())
884                 os << "\\options " << h_options << "\n";
885         os << "\\use_default_options " << h_use_default_options << "\n";
886         if (!used_modules.empty()) {
887                 os << "\\begin_modules\n";
888                 vector<string>::const_iterator const end = used_modules.end();
889                 vector<string>::const_iterator it = used_modules.begin();
890                 for (; it != end; it++)
891                         os << *it << '\n';
892                 os << "\\end_modules\n";
893         }
894         os << "\\language " << h_language << "\n"
895            << "\\language_package " << h_language_package << "\n"
896            << "\\inputencoding " << h_inputencoding << "\n"
897            << "\\fontencoding " << h_fontencoding << "\n"
898            << "\\font_roman " << h_font_roman << "\n"
899            << "\\font_sans " << h_font_sans << "\n"
900            << "\\font_typewriter " << h_font_typewriter << "\n"
901            << "\\font_default_family " << h_font_default_family << "\n"
902            << "\\font_sc " << h_font_sc << "\n"
903            << "\\font_osf " << h_font_osf << "\n"
904            << "\\font_sf_scale " << h_font_sf_scale << "\n"
905            << "\\font_tt_scale " << h_font_tt_scale << "\n"
906            << "\\graphics " << h_graphics << "\n";
907         if (!h_float_placement.empty())
908                 os << "\\float_placement " << h_float_placement << "\n";
909         os << "\\paperfontsize " << h_paperfontsize << "\n"
910            << "\\spacing " << h_spacing << "\n"
911            << "\\use_hyperref " << h_use_hyperref << '\n';
912         if (h_use_hyperref == "1") {
913                 if (!h_pdf_title.empty())
914                         os << "\\pdf_title \"" << h_pdf_title << "\"\n";
915                 if (!h_pdf_author.empty())
916                         os << "\\pdf_author \"" << h_pdf_author << "\"\n";
917                 if (!h_pdf_subject.empty())
918                         os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
919                 if (!h_pdf_keywords.empty())
920                         os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
921                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
922                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
923                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
924                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
925                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
926                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
927                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
928                       "\\pdf_backref " << h_pdf_backref << "\n"
929                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
930                 if (!h_pdf_pagemode.empty())
931                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
932                 if (!h_pdf_quoted_options.empty())
933                         os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
934         }
935         os << "\\papersize " << h_papersize << "\n"
936            << "\\use_geometry " << h_use_geometry << "\n"
937            << "\\use_amsmath " << h_use_amsmath << "\n"
938            << "\\use_esint " << h_use_esint << "\n"
939            << "\\use_mhchem " << h_use_mhchem << "\n"
940            << "\\use_mathdots " << h_use_mathdots << "\n"
941            << "\\use_undertilde " << h_use_undertilde << "\n"
942            << "\\cite_engine " << h_cite_engine << "\n"
943            << "\\use_bibtopic " << h_use_bibtopic << "\n"
944            << "\\paperorientation " << h_paperorientation << '\n'
945            << "\\suppress_date " << h_suppress_date << '\n'
946            << "\\use_refstyle " << h_use_refstyle << '\n';
947         if (!h_fontcolor.empty())
948                 os << "\\fontcolor " << h_fontcolor << '\n';
949         if (!h_notefontcolor.empty())
950                 os << "\\notefontcolor " << h_notefontcolor << '\n';
951         if (!h_backgroundcolor.empty())
952                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
953         if (!h_boxbgcolor.empty())
954                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
955         os << h_margins
956            << "\\secnumdepth " << h_secnumdepth << "\n"
957            << "\\tocdepth " << h_tocdepth << "\n"
958            << "\\paragraph_separation " << h_paragraph_separation << "\n";
959         if (h_paragraph_separation == "skip")
960                 os << "\\defskip " << h_defskip << "\n";
961         else
962                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
963         os << "\\quotes_language " << h_quotes_language << "\n"
964            << "\\papercolumns " << h_papercolumns << "\n"
965            << "\\papersides " << h_papersides << "\n"
966            << "\\paperpagestyle " << h_paperpagestyle << "\n";
967         if (!h_listings_params.empty())
968                 os << "\\listings_params " << h_listings_params << "\n";
969         os << "\\tracking_changes " << h_tracking_changes << "\n"
970            << "\\output_changes " << h_output_changes << "\n"
971            << "\\html_math_output " << h_html_math_output << "\n"
972            << "\\html_css_as_file " << h_html_css_as_file << "\n"
973            << "\\html_be_strict " << h_html_be_strict << "\n"
974            << authors_
975            << "\\end_header\n\n"
976            << "\\begin_body\n";
977         // clear preamble for subdocuments
978         h_preamble.str("");
979         return true;
980 }
981
982
983 void Preamble::parse(Parser & p, string const & forceclass,
984                      TeX2LyXDocClass & tc)
985 {
986         // initialize fixed types
987         special_columns['D'] = 3;
988         bool is_full_document = false;
989         bool is_lyx_file = false;
990         bool in_lyx_preamble = false;
991
992         // determine whether this is a full document or a fragment for inclusion
993         while (p.good()) {
994                 Token const & t = p.get_token();
995
996                 if (t.cat() == catEscape && t.cs() == "documentclass") {
997                         is_full_document = true;
998                         break;
999                 }
1000         }
1001         p.reset();
1002
1003         while (is_full_document && p.good()) {
1004                 Token const & t = p.get_token();
1005
1006 #ifdef FILEDEBUG
1007                 cerr << "t: " << t << "\n";
1008 #endif
1009
1010                 //
1011                 // cat codes
1012                 //
1013                 if (!in_lyx_preamble &&
1014                     (t.cat() == catLetter ||
1015                      t.cat() == catSuper ||
1016                      t.cat() == catSub ||
1017                      t.cat() == catOther ||
1018                      t.cat() == catMath ||
1019                      t.cat() == catActive ||
1020                      t.cat() == catBegin ||
1021                      t.cat() == catEnd ||
1022                      t.cat() == catAlign ||
1023                      t.cat() == catParameter))
1024                         h_preamble << t.cs();
1025
1026                 else if (!in_lyx_preamble && 
1027                          (t.cat() == catSpace || t.cat() == catNewline))
1028                         h_preamble << t.asInput();
1029
1030                 else if (t.cat() == catComment) {
1031                         static regex const islyxfile("%% LyX .* created this file");
1032                         static regex const usercommands("User specified LaTeX commands");
1033
1034                         string const comment = t.asInput();
1035
1036                         // magically switch encoding default if it looks like XeLaTeX
1037                         static string const magicXeLaTeX =
1038                                 "% This document must be compiled with XeLaTeX ";
1039                         if (comment.size() > magicXeLaTeX.size() 
1040                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1041                                   && h_inputencoding == "auto") {
1042                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
1043                                 h_inputencoding = "utf8";
1044                         }
1045                         smatch sub;
1046                         if (regex_search(comment, sub, islyxfile)) {
1047                                 is_lyx_file = true;
1048                                 in_lyx_preamble = true;
1049                         } else if (is_lyx_file
1050                                    && regex_search(comment, sub, usercommands))
1051                                 in_lyx_preamble = false;
1052                         else if (!in_lyx_preamble)
1053                                 h_preamble << t.asInput();
1054                 }
1055
1056                 else if (t.cs() == "pagestyle")
1057                         h_paperpagestyle = p.verbatim_item();
1058
1059                 else if (t.cs() == "date") {
1060                         string argument = p.getArg('{', '}');
1061                         if (argument.empty())
1062                                 h_suppress_date = "true";
1063                         else
1064                                 h_preamble << t.asInput() << '{' << argument << '}';
1065                 }
1066
1067                 else if (t.cs() == "color") {
1068                         string const space =
1069                                 (p.hasOpt() ? p.getOpt() : string());
1070                         string argument = p.getArg('{', '}');
1071                         // check the case that a standard color is used
1072                         if (space.empty() && is_known(argument, known_basic_colors)) {
1073                                 h_fontcolor = rgbcolor2code(argument);
1074                                 preamble.registerAutomaticallyLoadedPackage("color");
1075                         } else if (space.empty() && argument == "document_fontcolor")
1076                                 preamble.registerAutomaticallyLoadedPackage("color");
1077                         // check the case that LyX's document_fontcolor is defined
1078                         // but not used for \color
1079                         else {
1080                                 h_preamble << t.asInput();
1081                                 if (!space.empty())
1082                                         h_preamble << space;
1083                                 h_preamble << '{' << argument << '}';
1084                                 // the color might already be set because \definecolor
1085                                 // is parsed before this
1086                                 h_fontcolor = "";
1087                         }
1088                 }
1089
1090                 else if (t.cs() == "pagecolor") {
1091                         string argument = p.getArg('{', '}');
1092                         // check the case that a standard color is used
1093                         if (is_known(argument, known_basic_colors)) {
1094                                 h_backgroundcolor = rgbcolor2code(argument);
1095                         } else if (argument == "page_backgroundcolor")
1096                                 preamble.registerAutomaticallyLoadedPackage("color");
1097                         // check the case that LyX's page_backgroundcolor is defined
1098                         // but not used for \pagecolor
1099                         else {
1100                                 h_preamble << t.asInput() << '{' << argument << '}';
1101                                 // the color might already be set because \definecolor
1102                                 // is parsed before this
1103                                 h_backgroundcolor = "";
1104                         }
1105                 }
1106
1107                 else if (t.cs() == "makeatletter") {
1108                         // LyX takes care of this
1109                         p.setCatCode('@', catLetter);
1110                 }
1111
1112                 else if (t.cs() == "makeatother") {
1113                         // LyX takes care of this
1114                         p.setCatCode('@', catOther);
1115                 }
1116
1117                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1118                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1119                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
1120                                 || t.cs() == "DeclareRobustCommand"
1121                       || t.cs() == "DeclareRobustCommandx"
1122                                 || t.cs() == "ProvideTextCommandDefault"
1123                                 || t.cs() == "DeclareMathAccent") {
1124                         bool star = false;
1125                         if (p.next_token().character() == '*') {
1126                                 p.get_token();
1127                                 star = true;
1128                         }
1129                         string const name = p.verbatim_item();
1130                         string const opt1 = p.getFullOpt();
1131                         string const opt2 = p.getFullOpt();
1132                         string const body = p.verbatim_item();
1133                         // font settings
1134                         if (name == "\\rmdefault")
1135                                 if (is_known(body, known_roman_fonts))
1136                                         h_font_roman = body;
1137                         if (name == "\\sfdefault")
1138                                 if (is_known(body, known_sans_fonts))
1139                                         h_font_sans = body;
1140                         if (name == "\\ttdefault")
1141                                 if (is_known(body, known_typewriter_fonts))
1142                                         h_font_typewriter = body;
1143                         if (name == "\\familydefault") {
1144                                 string family = body;
1145                                 // remove leading "\"
1146                                 h_font_default_family = family.erase(0,1);
1147                         }
1148
1149                         // Add the command to the known commands
1150                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1151
1152                         // only non-lyxspecific stuff
1153                         if (!in_lyx_preamble) {
1154                                 ostringstream ss;
1155                                 ss << '\\' << t.cs();
1156                                 if (star)
1157                                         ss << '*';
1158                                 ss << '{' << name << '}' << opt1 << opt2
1159                                    << '{' << body << "}";
1160                                 h_preamble << ss.str();
1161 /*
1162                                 ostream & out = in_preamble ? h_preamble : os;
1163                                 out << "\\" << t.cs() << "{" << name << "}"
1164                                     << opts << "{" << body << "}";
1165 */
1166                         }
1167                 }
1168
1169                 else if (t.cs() == "documentclass") {
1170                         vector<string>::iterator it;
1171                         vector<string> opts = split_options(p.getArg('[', ']'));
1172                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1173                         delete_opt(opts, known_fontsizes);
1174                         // delete "pt" at the end
1175                         string::size_type i = h_paperfontsize.find("pt");
1176                         if (i != string::npos)
1177                                 h_paperfontsize.erase(i);
1178                         // The documentclass options are always parsed before the options
1179                         // of the babel call so that a language cannot overwrite the babel
1180                         // options.
1181                         handle_opt(opts, known_languages, h_language);
1182                         delete_opt(opts, known_languages);
1183
1184                         // paper orientation
1185                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1186                                 h_paperorientation = "landscape";
1187                                 opts.erase(it);
1188                         }
1189                         // paper sides
1190                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1191                                  != opts.end()) {
1192                                 h_papersides = "1";
1193                                 opts.erase(it);
1194                         }
1195                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1196                                  != opts.end()) {
1197                                 h_papersides = "2";
1198                                 opts.erase(it);
1199                         }
1200                         // paper columns
1201                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1202                                  != opts.end()) {
1203                                 h_papercolumns = "1";
1204                                 opts.erase(it);
1205                         }
1206                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1207                                  != opts.end()) {
1208                                 h_papercolumns = "2";
1209                                 opts.erase(it);
1210                         }
1211                         // paper sizes
1212                         // some size options are known to any document classes, other sizes
1213                         // are handled by the \geometry command of the geometry package
1214                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1215                         delete_opt(opts, known_class_paper_sizes);
1216                         // the remaining options
1217                         h_options = join(opts, ",");
1218                         // FIXME This does not work for classes that have a
1219                         //       different name in LyX than in LaTeX
1220                         h_textclass = p.getArg('{', '}');
1221                 }
1222
1223                 else if (t.cs() == "usepackage") {
1224                         string const options = p.getArg('[', ']');
1225                         string const name = p.getArg('{', '}');
1226                         vector<string> vecnames;
1227                         split(name, vecnames, ',');
1228                         vector<string>::const_iterator it  = vecnames.begin();
1229                         vector<string>::const_iterator end = vecnames.end();
1230                         for (; it != end; ++it)
1231                                 handle_package(p, trimSpaceAndEol(*it), options, 
1232                                                in_lyx_preamble);
1233                 }
1234
1235                 else if (t.cs() == "inputencoding") {
1236                         string const encoding = p.getArg('{','}');
1237                         h_inputencoding = encoding;
1238                         p.setEncoding(encoding);
1239                 }
1240
1241                 else if (t.cs() == "newenvironment") {
1242                         string const name = p.getArg('{', '}');
1243                         string const opt1 = p.getFullOpt();
1244                         string const opt2 = p.getFullOpt();
1245                         string const beg = p.verbatim_item();
1246                         string const end = p.verbatim_item();
1247                         if (!in_lyx_preamble) {
1248                                 h_preamble << "\\newenvironment{" << name
1249                                            << '}' << opt1 << opt2 << '{'
1250                                            << beg << "}{" << end << '}';
1251                         }
1252                         add_known_environment(name, opt1, !opt2.empty(),
1253                                               from_utf8(beg), from_utf8(end));
1254
1255                 }
1256
1257                 else if (t.cs() == "def") {
1258                         string name = p.get_token().cs();
1259                         while (p.next_token().cat() != catBegin)
1260                                 name += p.get_token().cs();
1261                         if (!in_lyx_preamble)
1262                                 h_preamble << "\\def\\" << name << '{'
1263                                            << p.verbatim_item() << "}";
1264                 }
1265
1266                 else if (t.cs() == "newcolumntype") {
1267                         string const name = p.getArg('{', '}');
1268                         trimSpaceAndEol(name);
1269                         int nargs = 0;
1270                         string opts = p.getOpt();
1271                         if (!opts.empty()) {
1272                                 istringstream is(string(opts, 1));
1273                                 is >> nargs;
1274                         }
1275                         special_columns[name[0]] = nargs;
1276                         h_preamble << "\\newcolumntype{" << name << "}";
1277                         if (nargs)
1278                                 h_preamble << "[" << nargs << "]";
1279                         h_preamble << "{" << p.verbatim_item() << "}";
1280                 }
1281
1282                 else if (t.cs() == "setcounter") {
1283                         string const name = p.getArg('{', '}');
1284                         string const content = p.getArg('{', '}');
1285                         if (name == "secnumdepth")
1286                                 h_secnumdepth = content;
1287                         else if (name == "tocdepth")
1288                                 h_tocdepth = content;
1289                         else
1290                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1291                 }
1292
1293                 else if (t.cs() == "setlength") {
1294                         string const name = p.verbatim_item();
1295                         string const content = p.verbatim_item();
1296                         // the paragraphs are only not indented when \parindent is set to zero
1297                         if (name == "\\parindent" && content != "") {
1298                                 if (content[0] == '0')
1299                                         h_paragraph_separation = "skip";
1300                                 else
1301                                         h_paragraph_indentation = translate_len(content);
1302                         } else if (name == "\\parskip") {
1303                                 if (content == "\\smallskipamount")
1304                                         h_defskip = "smallskip";
1305                                 else if (content == "\\medskipamount")
1306                                         h_defskip = "medskip";
1307                                 else if (content == "\\bigskipamount")
1308                                         h_defskip = "bigskip";
1309                                 else
1310                                         h_defskip = content;
1311                         } else
1312                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1313                 }
1314
1315                 else if (t.cs() == "onehalfspacing")
1316                         h_spacing = "onehalf";
1317
1318                 else if (t.cs() == "doublespacing")
1319                         h_spacing = "double";
1320
1321                 else if (t.cs() == "setstretch")
1322                         h_spacing = "other " + p.verbatim_item();
1323
1324                 else if (t.cs() == "begin") {
1325                         string const name = p.getArg('{', '}');
1326                         if (name == "document")
1327                                 break;
1328                         h_preamble << "\\begin{" << name << "}";
1329                 }
1330
1331                 else if (t.cs() == "geometry") {
1332                         vector<string> opts = split_options(p.getArg('{', '}'));
1333                         handle_geometry(opts);
1334                 }
1335
1336                 else if (t.cs() == "definecolor") {
1337                         string const color = p.getArg('{', '}');
1338                         string const space = p.getArg('{', '}');
1339                         string const value = p.getArg('{', '}');
1340                         if (color == "document_fontcolor" && space == "rgb") {
1341                                 RGBColor c(RGBColorFromLaTeX(value));
1342                                 h_fontcolor = X11hexname(c);
1343                         } else if (color == "note_fontcolor" && space == "rgb") {
1344                                 RGBColor c(RGBColorFromLaTeX(value));
1345                                 h_notefontcolor = X11hexname(c);
1346                         } else if (color == "page_backgroundcolor" && space == "rgb") {
1347                                 RGBColor c(RGBColorFromLaTeX(value));
1348                                 h_backgroundcolor = X11hexname(c);
1349                         } else if (color == "shadecolor" && space == "rgb") {
1350                                 RGBColor c(RGBColorFromLaTeX(value));
1351                                 h_boxbgcolor = X11hexname(c);
1352                         } else {
1353                                 h_preamble << "\\definecolor{" << color
1354                                            << "}{" << space << "}{" << value
1355                                            << '}';
1356                         }
1357                 }
1358
1359                 else if (t.cs() == "jurabibsetup") {
1360                         // FIXME p.getArg('{', '}') is most probably wrong (it
1361                         //       does not handle nested braces).
1362                         //       Use p.verbatim_item() instead.
1363                         vector<string> jurabibsetup =
1364                                 split_options(p.getArg('{', '}'));
1365                         // add jurabibsetup to the jurabib package options
1366                         add_package("jurabib", jurabibsetup);
1367                         if (!jurabibsetup.empty()) {
1368                                 h_preamble << "\\jurabibsetup{"
1369                                            << join(jurabibsetup, ",") << '}';
1370                         }
1371                 }
1372
1373                 else if (t.cs() == "hypersetup") {
1374                         vector<string> hypersetup =
1375                                 split_options(p.verbatim_item());
1376                         // add hypersetup to the hyperref package options
1377                         handle_hyperref(hypersetup);
1378                         if (!hypersetup.empty()) {
1379                                 h_preamble << "\\hypersetup{"
1380                                            << join(hypersetup, ",") << '}';
1381                         }
1382                 }
1383
1384                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1385                         // prevent misparsing of \usepackage if it is used
1386                         // as an argument (see e.g. our own output of
1387                         // \@ifundefined above)
1388                         string const arg1 = p.verbatim_item();
1389                         string const arg2 = p.verbatim_item();
1390                         string const arg3 = p.verbatim_item();
1391                         // test case \@ifundefined{date}{}{\date{}}
1392                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
1393                             arg2.empty() && arg3 == "\\date{}") {
1394                                 h_suppress_date = "true";
1395                         // older tex2lyx versions did output
1396                         // \@ifundefined{definecolor}{\usepackage{color}}{}
1397                         } else if (t.cs() == "@ifundefined" &&
1398                                    arg1 == "definecolor" &&
1399                                    arg2 == "\\usepackage{color}" &&
1400                                    arg3.empty()) {
1401                                 if (!in_lyx_preamble)
1402                                         h_preamble << package_beg_sep
1403                                                    << "color"
1404                                                    << package_mid_sep
1405                                                    << "\\@ifundefined{definecolor}{color}{}"
1406                                                    << package_end_sep;
1407                         // test for case
1408                         //\@ifundefined{showcaptionsetup}{}{%
1409                         // \PassOptionsToPackage{caption=false}{subfig}}
1410                         // that LyX uses for subfloats
1411                         } else if (t.cs() == "@ifundefined" &&
1412                                    arg1 == "showcaptionsetup" && arg2.empty()
1413                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1414                                 ; // do nothing
1415                         } else if (!in_lyx_preamble) {
1416                                 h_preamble << t.asInput()
1417                                            << '{' << arg1 << '}'
1418                                            << '{' << arg2 << '}'
1419                                            << '{' << arg3 << '}';
1420                         }
1421                 }
1422
1423                 else if (is_known(t.cs(), known_if_commands)) {
1424                         // must not parse anything in conditional code, since
1425                         // LyX would output the parsed contents unconditionally
1426                         if (!in_lyx_preamble)
1427                                 h_preamble << t.asInput();
1428                         handle_if(p, in_lyx_preamble);
1429                 }
1430
1431                 else if (!t.cs().empty() && !in_lyx_preamble)
1432                         h_preamble << '\\' << t.cs();
1433         }
1434
1435         // remove the whitespace
1436         p.skip_spaces();
1437
1438         // Force textclass if the user wanted it
1439         if (!forceclass.empty())
1440                 h_textclass = forceclass;
1441         if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1442                 h_textclass.insert(0, "literate-");
1443         tc.setName(h_textclass);
1444         if (!tc.load()) {
1445                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1446                 exit(EXIT_FAILURE);
1447         }
1448         if (h_papersides.empty()) {
1449                 ostringstream ss;
1450                 ss << tc.sides();
1451                 h_papersides = ss.str();
1452         }
1453 }
1454
1455
1456 string babel2lyx(string const & language)
1457 {
1458         char const * const * where = is_known(language, known_languages);
1459         if (where)
1460                 return known_coded_languages[where - known_languages];
1461         return language;
1462 }
1463
1464
1465 string rgbcolor2code(string const & name)
1466 {
1467         char const * const * where = is_known(name, known_basic_colors);
1468         if (where) {
1469                 // "red", "green" etc
1470                 return known_basic_color_codes[where - known_basic_colors];
1471         }
1472         // "255,0,0", "0,255,0" etc
1473         RGBColor c(RGBColorFromLaTeX(name));
1474         return X11hexname(c);
1475 }
1476
1477 // }])
1478
1479
1480 } // namespace lyx