]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
f3f4b5c98812b95e74bbdf61950c7eabcf54df6b
[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_indices             = "false";
452         h_use_geometry            = "false";
453         h_use_amsmath             = "1";
454         h_use_default_options     = "false";
455         h_use_esint               = "1";
456         h_use_hyperref            = "0";
457         h_use_mhchem              = "0";
458         h_use_mathdots            = "0";
459         h_use_refstyle            = "0";
460         h_use_undertilde          = "0";
461 }
462
463
464 void Preamble::handle_hyperref(vector<string> & options)
465 {
466         // FIXME swallow inputencoding changes that might surround the
467         //       hyperref setup if it was written by LyX
468         h_use_hyperref = "1";
469         // swallow "unicode=true", since LyX does always write that
470         vector<string>::iterator it =
471                 find(options.begin(), options.end(), "unicode=true");
472         if (it != options.end())
473                 options.erase(it);
474         it = find(options.begin(), options.end(), "pdfusetitle");
475         if (it != options.end()) {
476                 h_pdf_pdfusetitle = "1";
477                 options.erase(it);
478         }
479         string bookmarks = process_keyval_opt(options, "bookmarks");
480         if (bookmarks == "true")
481                 h_pdf_bookmarks = "1";
482         else if (bookmarks == "false")
483                 h_pdf_bookmarks = "0";
484         if (h_pdf_bookmarks == "1") {
485                 string bookmarksnumbered =
486                         process_keyval_opt(options, "bookmarksnumbered");
487                 if (bookmarksnumbered == "true")
488                         h_pdf_bookmarksnumbered = "1";
489                 else if (bookmarksnumbered == "false")
490                         h_pdf_bookmarksnumbered = "0";
491                 string bookmarksopen =
492                         process_keyval_opt(options, "bookmarksopen");
493                 if (bookmarksopen == "true")
494                         h_pdf_bookmarksopen = "1";
495                 else if (bookmarksopen == "false")
496                         h_pdf_bookmarksopen = "0";
497                 if (h_pdf_bookmarksopen == "1") {
498                         string bookmarksopenlevel =
499                                 process_keyval_opt(options, "bookmarksopenlevel");
500                         if (!bookmarksopenlevel.empty())
501                                 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
502                 }
503         }
504         string breaklinks = process_keyval_opt(options, "breaklinks");
505         if (breaklinks == "true")
506                 h_pdf_breaklinks = "1";
507         else if (breaklinks == "false")
508                 h_pdf_breaklinks = "0";
509         string pdfborder = process_keyval_opt(options, "pdfborder");
510         if (pdfborder == "{0 0 0}")
511                 h_pdf_pdfborder = "1";
512         else if (pdfborder == "{0 0 1}")
513                 h_pdf_pdfborder = "0";
514         string backref = process_keyval_opt(options, "backref");
515         if (!backref.empty())
516                 h_pdf_backref = backref;
517         string colorlinks = process_keyval_opt(options, "colorlinks");
518         if (colorlinks == "true")
519                 h_pdf_colorlinks = "1";
520         else if (colorlinks == "false")
521                 h_pdf_colorlinks = "0";
522         string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
523         if (!pdfpagemode.empty())
524                 h_pdf_pagemode = pdfpagemode;
525         string pdftitle = process_keyval_opt(options, "pdftitle");
526         if (!pdftitle.empty()) {
527                 h_pdf_title = remove_braces(pdftitle);
528         }
529         string pdfauthor = process_keyval_opt(options, "pdfauthor");
530         if (!pdfauthor.empty()) {
531                 h_pdf_author = remove_braces(pdfauthor);
532         }
533         string pdfsubject = process_keyval_opt(options, "pdfsubject");
534         if (!pdfsubject.empty())
535                 h_pdf_subject = remove_braces(pdfsubject);
536         string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
537         if (!pdfkeywords.empty())
538                 h_pdf_keywords = remove_braces(pdfkeywords);
539         if (!options.empty()) {
540                 if (!h_pdf_quoted_options.empty())
541                         h_pdf_quoted_options += ',';
542                 h_pdf_quoted_options += join(options, ",");
543                 options.clear();
544         }
545 }
546
547
548 void Preamble::handle_geometry(vector<string> & options)
549 {
550         h_use_geometry = "true";
551         vector<string>::iterator it;
552         // paper orientation
553         if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
554                 h_paperorientation = "landscape";
555                 options.erase(it);
556         }
557         // paper size
558         // keyval version: "paper=letter"
559         string paper = process_keyval_opt(options, "paper");
560         if (!paper.empty())
561                 h_papersize = paper + "paper";
562         // alternative version: "letterpaper"
563         handle_opt(options, known_paper_sizes, h_papersize);
564         delete_opt(options, known_paper_sizes);
565         // page margins
566         char const * const * margin = known_paper_margins;
567         for (; *margin; ++margin) {
568                 string value = process_keyval_opt(options, *margin);
569                 if (!value.empty()) {
570                         int k = margin - known_paper_margins;
571                         string name = known_coded_paper_margins[k];
572                         h_margins += '\\' + name + ' ' + value + '\n';
573                 }
574         }
575 }
576
577
578 void Preamble::handle_package(Parser &p, string const & name,
579                               string const & opts, bool in_lyx_preamble)
580 {
581         vector<string> options = split_options(opts);
582         add_package(name, options);
583         string scale;
584
585         if (is_known(name, known_xetex_packages))
586                 xetex = true;
587
588         // roman fonts
589         if (is_known(name, known_roman_fonts)) {
590                 h_font_roman = name;
591                 p.skip_spaces();
592         }
593
594         if (name == "fourier") {
595                 h_font_roman = "utopia";
596                 // when font uses real small capitals
597                 if (opts == "expert")
598                         h_font_sc = "true";
599         }
600
601         if (name == "mathpazo")
602                 h_font_roman = "palatino";
603
604         if (name == "mathptmx")
605                 h_font_roman = "times";
606
607         // sansserif fonts
608         if (is_known(name, known_sans_fonts)) {
609                 h_font_sans = name;
610                 if (!opts.empty()) {
611                         scale = opts;
612                         h_font_sf_scale = scale_as_percentage(scale);
613                 }
614         }
615
616         // typewriter fonts
617         if (is_known(name, known_typewriter_fonts)) {
618                 // fourier can be set as roman font _only_
619                 // fourier as typewriter is handled in handling of \ttdefault
620                 if (name != "fourier") {
621                         h_font_typewriter = name;
622                         if (!opts.empty()) {
623                                 scale = opts;
624                                 h_font_tt_scale = scale_as_percentage(scale);
625                         }
626                 }
627         }
628
629         // font uses old-style figure
630         if (name == "eco")
631                 h_font_osf = "true";
632
633         // after the detection and handling of special cases, we can remove the
634         // fonts, otherwise they would appear in the preamble, see bug #7856
635         if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
636                 ||      is_known(name, known_typewriter_fonts))
637                 ;
638
639         else if (name == "amsmath" || name == "amssymb")
640                 h_use_amsmath = "2";
641
642         else if (name == "esint")
643                 h_use_esint = "2";
644
645         else if (name == "mhchem")
646                 h_use_mhchem = "2";
647
648         else if (name == "mathdots")
649                 h_use_mathdots = "2";
650
651         else if (name == "undertilde")
652                 h_use_undertilde = "2";
653
654         else if (name == "babel") {
655                 h_language_package = "default";
656                 // One might think we would have to do nothing if babel is loaded
657                 // without any options to prevent pollution of the preamble with this
658                 // babel call in every roundtrip.
659                 // But the user could have defined babel-specific things afterwards. So
660                 // we need to keep it in the preamble to prevent cases like bug #7861.
661                 if (!opts.empty()) {
662                         // check if more than one option was used - used later for inputenc
663                         if (options.begin() != options.end() - 1)
664                                 one_language = false;
665                         // babel takes the last language of the option of its \usepackage
666                         // call as document language. If there is no such language option, the
667                         // last language in the documentclass options is used.
668                         handle_opt(options, known_languages, h_language);
669                         // If babel is called with options, LyX puts them by default into the
670                         // document class options. This works for most languages, except
671                         // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
672                         // perhaps in future others.
673                         // Therefore keep the babel call as it is as the user might have
674                         // reasons for it.
675                         h_preamble << "\\usepackage[" << opts << "]{babel}\n";
676                         delete_opt(options, known_languages);
677                 }
678                 else
679                         h_preamble << "\\usepackage{babel}\n";                                          
680         }
681
682         else if (name == "fontenc") {
683                 h_fontencoding = getStringFromVector(options, ",");
684                 /* We could do the following for better round trip support,
685                  * but this makes the document less portable, so I skip it:
686                 if (h_fontencoding == lyxrc.fontenc)
687                         h_fontencoding = "global";
688                  */
689                 options.clear();
690         }
691
692         else if (name == "inputenc" || name == "luainputenc") {
693                 // h_inputencoding is only set when there is not more than one
694                 // inputenc option because otherwise h_inputencoding must be
695                 // set to "auto" (the default encoding of the document language)
696                 // Therefore check for the "," character.
697                 // It is also only set when there is not more than one babel
698                 // language option.
699                 if (opts.find(",") == string::npos && one_language == true)
700                         h_inputencoding = opts;
701                 if (!options.empty())
702                         p.setEncoding(options.back());
703                 options.clear();
704         }
705
706         else if (is_known(name, known_old_language_packages)) {
707                 // known language packages from the times before babel
708                 // if they are found and not also babel, they will be used as
709                 // custom language package
710                 h_language_package = "\\usepackage{" + name + "}";
711         }
712
713         else if (name == "prettyref")
714                 ; // ignore this FIXME: Use the package separator mechanism instead
715
716         else if (name == "varioref")
717                 ; // ignore this FIXME: Use the package separator mechanism instead
718
719         else if (name == "verbatim")
720                 ; // ignore this FIXME: Use the package separator mechanism instead
721
722         else if (name == "textcomp")
723                 ; // ignore this FIXME: Use the package separator mechanism instead
724
725         else if (name == "pdfpages")
726                 ; // ignore this
727
728         else if (name == "lyxskak") {
729                 // ignore this and its options
730                 if (!options.empty())
731                         options.clear();
732         }
733
734         else if (name == "array" || name == "booktabs" || name == "float" ||
735                  name == "color" || name == "hhline" || name == "longtable" ||
736                  name == "makeidx" || name == "nomencl" || name == "splitidx" ||
737                  name == "setspace" || name == "subscript" || name == "ulem" ||
738                  name == "url") {
739                 if (!in_lyx_preamble)
740                         h_preamble << package_beg_sep << name
741                                    << package_mid_sep << "\\usepackage{"
742                                    << name << '}' << package_end_sep;
743         }
744
745         else if (name == "graphicx")
746                 ; // ignore this FIXME: Use the package separator mechanism instead
747
748         else if (name == "geometry")
749                 handle_geometry(options);
750
751         else if (name == "rotfloat")
752                 ; // ignore this FIXME: Use the package separator mechanism instead
753
754         else if (name == "wrapfig")
755                 ; // ignore this FIXME: Use the package separator mechanism instead
756
757         else if (name == "subfig")
758                 ; // ignore this FIXME: Use the package separator mechanism instead
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, bool subdoc)
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         if (contains(h_float_placement, "H"))
854                 registerAutomaticallyLoadedPackage("float");
855         if (h_spacing != "single" && h_spacing != "default")
856                 registerAutomaticallyLoadedPackage("setspace");
857
858         // output the LyX file settings
859         os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
860            << "\\lyxformat " << LYX_FORMAT << '\n'
861            << "\\begin_document\n"
862            << "\\begin_header\n"
863            << "\\textclass " << h_textclass << "\n";
864         string const raw = subdoc ? empty_string() : h_preamble.str();
865         if (!raw.empty()) {
866                 os << "\\begin_preamble\n";
867                 for (string::size_type i = 0; i < raw.size(); ++i) {
868                         if (raw[i] == package_beg_sep) {
869                                 // Here follows some package loading code that
870                                 // must be skipped if the package is loaded
871                                 // automatically.
872                                 string::size_type j = raw.find(package_mid_sep, i);
873                                 if (j == string::npos)
874                                         return false;
875                                 string::size_type k = raw.find(package_end_sep, j);
876                                 if (k == string::npos)
877                                         return false;
878                                 string const package = raw.substr(i + 1, j - i - 1);
879                                 string const replacement = raw.substr(j + 1, k - j - 1);
880                                 if (auto_packages.find(package) == auto_packages.end())
881                                         os << replacement;
882                                 i = k;
883                         } else
884                                 os.put(raw[i]);
885                 }
886                 os << "\n\\end_preamble\n";
887         }
888         if (!h_options.empty())
889                 os << "\\options " << h_options << "\n";
890         os << "\\use_default_options " << h_use_default_options << "\n";
891         if (!used_modules.empty()) {
892                 os << "\\begin_modules\n";
893                 vector<string>::const_iterator const end = used_modules.end();
894                 vector<string>::const_iterator it = used_modules.begin();
895                 for (; it != end; it++)
896                         os << *it << '\n';
897                 os << "\\end_modules\n";
898         }
899         os << "\\language " << h_language << "\n"
900            << "\\language_package " << h_language_package << "\n"
901            << "\\inputencoding " << h_inputencoding << "\n"
902            << "\\fontencoding " << h_fontencoding << "\n"
903            << "\\font_roman " << h_font_roman << "\n"
904            << "\\font_sans " << h_font_sans << "\n"
905            << "\\font_typewriter " << h_font_typewriter << "\n"
906            << "\\font_default_family " << h_font_default_family << "\n"
907            << "\\font_sc " << h_font_sc << "\n"
908            << "\\font_osf " << h_font_osf << "\n"
909            << "\\font_sf_scale " << h_font_sf_scale << "\n"
910            << "\\font_tt_scale " << h_font_tt_scale << "\n"
911            << "\\graphics " << h_graphics << "\n";
912         if (!h_float_placement.empty())
913                 os << "\\float_placement " << h_float_placement << "\n";
914         os << "\\paperfontsize " << h_paperfontsize << "\n"
915            << "\\spacing " << h_spacing << "\n"
916            << "\\use_hyperref " << h_use_hyperref << '\n';
917         if (h_use_hyperref == "1") {
918                 if (!h_pdf_title.empty())
919                         os << "\\pdf_title \"" << h_pdf_title << "\"\n";
920                 if (!h_pdf_author.empty())
921                         os << "\\pdf_author \"" << h_pdf_author << "\"\n";
922                 if (!h_pdf_subject.empty())
923                         os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
924                 if (!h_pdf_keywords.empty())
925                         os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
926                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
927                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
928                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
929                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
930                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
931                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
932                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
933                       "\\pdf_backref " << h_pdf_backref << "\n"
934                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
935                 if (!h_pdf_pagemode.empty())
936                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
937                 if (!h_pdf_quoted_options.empty())
938                         os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
939         }
940         os << "\\papersize " << h_papersize << "\n"
941            << "\\use_geometry " << h_use_geometry << "\n"
942            << "\\use_amsmath " << h_use_amsmath << "\n"
943            << "\\use_esint " << h_use_esint << "\n"
944            << "\\use_mhchem " << h_use_mhchem << "\n"
945            << "\\use_mathdots " << h_use_mathdots << "\n"
946            << "\\use_undertilde " << h_use_undertilde << "\n"
947            << "\\cite_engine " << h_cite_engine << "\n"
948            << "\\use_bibtopic " << h_use_bibtopic << "\n"
949            << "\\use_indices " << h_use_indices << "\n"
950            << "\\paperorientation " << h_paperorientation << '\n'
951            << "\\suppress_date " << h_suppress_date << '\n'
952            << "\\use_refstyle " << h_use_refstyle << '\n';
953         if (!h_fontcolor.empty())
954                 os << "\\fontcolor " << h_fontcolor << '\n';
955         if (!h_notefontcolor.empty())
956                 os << "\\notefontcolor " << h_notefontcolor << '\n';
957         if (!h_backgroundcolor.empty())
958                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
959         if (!h_boxbgcolor.empty())
960                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
961         os << h_margins
962            << "\\secnumdepth " << h_secnumdepth << "\n"
963            << "\\tocdepth " << h_tocdepth << "\n"
964            << "\\paragraph_separation " << h_paragraph_separation << "\n";
965         if (h_paragraph_separation == "skip")
966                 os << "\\defskip " << h_defskip << "\n";
967         else
968                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
969         os << "\\quotes_language " << h_quotes_language << "\n"
970            << "\\papercolumns " << h_papercolumns << "\n"
971            << "\\papersides " << h_papersides << "\n"
972            << "\\paperpagestyle " << h_paperpagestyle << "\n";
973         if (!h_listings_params.empty())
974                 os << "\\listings_params " << h_listings_params << "\n";
975         os << "\\tracking_changes " << h_tracking_changes << "\n"
976            << "\\output_changes " << h_output_changes << "\n"
977            << "\\html_math_output " << h_html_math_output << "\n"
978            << "\\html_css_as_file " << h_html_css_as_file << "\n"
979            << "\\html_be_strict " << h_html_be_strict << "\n"
980            << authors_
981            << "\\end_header\n\n"
982            << "\\begin_body\n";
983         return true;
984 }
985
986
987 void Preamble::parse(Parser & p, string const & forceclass,
988                      TeX2LyXDocClass & tc)
989 {
990         // initialize fixed types
991         special_columns['D'] = 3;
992         bool is_full_document = false;
993         bool is_lyx_file = false;
994         bool in_lyx_preamble = false;
995
996         // determine whether this is a full document or a fragment for inclusion
997         while (p.good()) {
998                 Token const & t = p.get_token();
999
1000                 if (t.cat() == catEscape && t.cs() == "documentclass") {
1001                         is_full_document = true;
1002                         break;
1003                 }
1004         }
1005         p.reset();
1006
1007         while (is_full_document && p.good()) {
1008                 Token const & t = p.get_token();
1009
1010 #ifdef FILEDEBUG
1011                 cerr << "t: " << t << "\n";
1012 #endif
1013
1014                 //
1015                 // cat codes
1016                 //
1017                 if (!in_lyx_preamble &&
1018                     (t.cat() == catLetter ||
1019                      t.cat() == catSuper ||
1020                      t.cat() == catSub ||
1021                      t.cat() == catOther ||
1022                      t.cat() == catMath ||
1023                      t.cat() == catActive ||
1024                      t.cat() == catBegin ||
1025                      t.cat() == catEnd ||
1026                      t.cat() == catAlign ||
1027                      t.cat() == catParameter))
1028                         h_preamble << t.cs();
1029
1030                 else if (!in_lyx_preamble && 
1031                          (t.cat() == catSpace || t.cat() == catNewline))
1032                         h_preamble << t.asInput();
1033
1034                 else if (t.cat() == catComment) {
1035                         static regex const islyxfile("%% LyX .* created this file");
1036                         static regex const usercommands("User specified LaTeX commands");
1037
1038                         string const comment = t.asInput();
1039
1040                         // magically switch encoding default if it looks like XeLaTeX
1041                         static string const magicXeLaTeX =
1042                                 "% This document must be compiled with XeLaTeX ";
1043                         if (comment.size() > magicXeLaTeX.size() 
1044                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1045                                   && h_inputencoding == "auto") {
1046                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
1047                                 h_inputencoding = "utf8";
1048                         }
1049                         smatch sub;
1050                         if (regex_search(comment, sub, islyxfile)) {
1051                                 is_lyx_file = true;
1052                                 in_lyx_preamble = true;
1053                         } else if (is_lyx_file
1054                                    && regex_search(comment, sub, usercommands))
1055                                 in_lyx_preamble = false;
1056                         else if (!in_lyx_preamble)
1057                                 h_preamble << t.asInput();
1058                 }
1059
1060                 else if (t.cs() == "pagestyle")
1061                         h_paperpagestyle = p.verbatim_item();
1062
1063                 else if (t.cs() == "date") {
1064                         string argument = p.getArg('{', '}');
1065                         if (argument.empty())
1066                                 h_suppress_date = "true";
1067                         else
1068                                 h_preamble << t.asInput() << '{' << argument << '}';
1069                 }
1070
1071                 else if (t.cs() == "color") {
1072                         string const space =
1073                                 (p.hasOpt() ? p.getOpt() : string());
1074                         string argument = p.getArg('{', '}');
1075                         // check the case that a standard color is used
1076                         if (space.empty() && is_known(argument, known_basic_colors)) {
1077                                 h_fontcolor = rgbcolor2code(argument);
1078                                 preamble.registerAutomaticallyLoadedPackage("color");
1079                         } else if (space.empty() && argument == "document_fontcolor")
1080                                 preamble.registerAutomaticallyLoadedPackage("color");
1081                         // check the case that LyX's document_fontcolor is defined
1082                         // but not used for \color
1083                         else {
1084                                 h_preamble << t.asInput();
1085                                 if (!space.empty())
1086                                         h_preamble << space;
1087                                 h_preamble << '{' << argument << '}';
1088                                 // the color might already be set because \definecolor
1089                                 // is parsed before this
1090                                 h_fontcolor = "";
1091                         }
1092                 }
1093
1094                 else if (t.cs() == "pagecolor") {
1095                         string argument = p.getArg('{', '}');
1096                         // check the case that a standard color is used
1097                         if (is_known(argument, known_basic_colors)) {
1098                                 h_backgroundcolor = rgbcolor2code(argument);
1099                         } else if (argument == "page_backgroundcolor")
1100                                 preamble.registerAutomaticallyLoadedPackage("color");
1101                         // check the case that LyX's page_backgroundcolor is defined
1102                         // but not used for \pagecolor
1103                         else {
1104                                 h_preamble << t.asInput() << '{' << argument << '}';
1105                                 // the color might already be set because \definecolor
1106                                 // is parsed before this
1107                                 h_backgroundcolor = "";
1108                         }
1109                 }
1110
1111                 else if (t.cs() == "makeatletter") {
1112                         // LyX takes care of this
1113                         p.setCatCode('@', catLetter);
1114                 }
1115
1116                 else if (t.cs() == "makeatother") {
1117                         // LyX takes care of this
1118                         p.setCatCode('@', catOther);
1119                 }
1120
1121                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1122                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1123                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
1124                                 || t.cs() == "DeclareRobustCommand"
1125                       || t.cs() == "DeclareRobustCommandx"
1126                                 || t.cs() == "ProvideTextCommandDefault"
1127                                 || t.cs() == "DeclareMathAccent") {
1128                         bool star = false;
1129                         if (p.next_token().character() == '*') {
1130                                 p.get_token();
1131                                 star = true;
1132                         }
1133                         string const name = p.verbatim_item();
1134                         string const opt1 = p.getFullOpt();
1135                         string const opt2 = p.getFullOpt();
1136                         string const body = p.verbatim_item();
1137                         // font settings
1138                         if (name == "\\rmdefault")
1139                                 if (is_known(body, known_roman_fonts))
1140                                         h_font_roman = body;
1141                         if (name == "\\sfdefault")
1142                                 if (is_known(body, known_sans_fonts))
1143                                         h_font_sans = body;
1144                         if (name == "\\ttdefault")
1145                                 if (is_known(body, known_typewriter_fonts))
1146                                         h_font_typewriter = body;
1147                         if (name == "\\familydefault") {
1148                                 string family = body;
1149                                 // remove leading "\"
1150                                 h_font_default_family = family.erase(0,1);
1151                         }
1152
1153                         // remove the lyxdot definition that is re-added by LyX
1154                         // if necessary
1155                         if (name == "\\lyxdot")
1156                                 in_lyx_preamble = true;
1157
1158                         // Add the command to the known commands
1159                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1160
1161                         // only non-lyxspecific stuff
1162                         if (!in_lyx_preamble) {
1163                                 ostringstream ss;
1164                                 ss << '\\' << t.cs();
1165                                 if (star)
1166                                         ss << '*';
1167                                 ss << '{' << name << '}' << opt1 << opt2
1168                                    << '{' << body << "}";
1169                                 h_preamble << ss.str();
1170 /*
1171                                 ostream & out = in_preamble ? h_preamble : os;
1172                                 out << "\\" << t.cs() << "{" << name << "}"
1173                                     << opts << "{" << body << "}";
1174 */
1175                         }
1176                 }
1177
1178                 else if (t.cs() == "documentclass") {
1179                         vector<string>::iterator it;
1180                         vector<string> opts = split_options(p.getArg('[', ']'));
1181                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1182                         delete_opt(opts, known_fontsizes);
1183                         // delete "pt" at the end
1184                         string::size_type i = h_paperfontsize.find("pt");
1185                         if (i != string::npos)
1186                                 h_paperfontsize.erase(i);
1187                         // The documentclass options are always parsed before the options
1188                         // of the babel call so that a language cannot overwrite the babel
1189                         // options.
1190                         handle_opt(opts, known_languages, h_language);
1191                         delete_opt(opts, known_languages);
1192
1193                         // paper orientation
1194                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1195                                 h_paperorientation = "landscape";
1196                                 opts.erase(it);
1197                         }
1198                         // paper sides
1199                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1200                                  != opts.end()) {
1201                                 h_papersides = "1";
1202                                 opts.erase(it);
1203                         }
1204                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1205                                  != opts.end()) {
1206                                 h_papersides = "2";
1207                                 opts.erase(it);
1208                         }
1209                         // paper columns
1210                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1211                                  != opts.end()) {
1212                                 h_papercolumns = "1";
1213                                 opts.erase(it);
1214                         }
1215                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1216                                  != opts.end()) {
1217                                 h_papercolumns = "2";
1218                                 opts.erase(it);
1219                         }
1220                         // paper sizes
1221                         // some size options are known to any document classes, other sizes
1222                         // are handled by the \geometry command of the geometry package
1223                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1224                         delete_opt(opts, known_class_paper_sizes);
1225                         // the remaining options
1226                         h_options = join(opts, ",");
1227                         // FIXME This does not work for classes that have a
1228                         //       different name in LyX than in LaTeX
1229                         h_textclass = p.getArg('{', '}');
1230                 }
1231
1232                 else if (t.cs() == "usepackage") {
1233                         string const options = p.getArg('[', ']');
1234                         string const name = p.getArg('{', '}');
1235                         vector<string> vecnames;
1236                         split(name, vecnames, ',');
1237                         vector<string>::const_iterator it  = vecnames.begin();
1238                         vector<string>::const_iterator end = vecnames.end();
1239                         for (; it != end; ++it)
1240                                 handle_package(p, trimSpaceAndEol(*it), options, 
1241                                                in_lyx_preamble);
1242                 }
1243
1244                 else if (t.cs() == "inputencoding") {
1245                         string const encoding = p.getArg('{','}');
1246                         h_inputencoding = encoding;
1247                         p.setEncoding(encoding);
1248                 }
1249
1250                 else if (t.cs() == "newenvironment") {
1251                         string const name = p.getArg('{', '}');
1252                         string const opt1 = p.getFullOpt();
1253                         string const opt2 = p.getFullOpt();
1254                         string const beg = p.verbatim_item();
1255                         string const end = p.verbatim_item();
1256                         if (!in_lyx_preamble) {
1257                                 h_preamble << "\\newenvironment{" << name
1258                                            << '}' << opt1 << opt2 << '{'
1259                                            << beg << "}{" << end << '}';
1260                         }
1261                         add_known_environment(name, opt1, !opt2.empty(),
1262                                               from_utf8(beg), from_utf8(end));
1263
1264                 }
1265
1266                 else if (t.cs() == "def") {
1267                         string name = p.get_token().cs();
1268                         while (p.next_token().cat() != catBegin)
1269                                 name += p.get_token().cs();
1270                         if (!in_lyx_preamble)
1271                                 h_preamble << "\\def\\" << name << '{'
1272                                            << p.verbatim_item() << "}";
1273                 }
1274
1275                 else if (t.cs() == "newcolumntype") {
1276                         string const name = p.getArg('{', '}');
1277                         trimSpaceAndEol(name);
1278                         int nargs = 0;
1279                         string opts = p.getOpt();
1280                         if (!opts.empty()) {
1281                                 istringstream is(string(opts, 1));
1282                                 is >> nargs;
1283                         }
1284                         special_columns[name[0]] = nargs;
1285                         h_preamble << "\\newcolumntype{" << name << "}";
1286                         if (nargs)
1287                                 h_preamble << "[" << nargs << "]";
1288                         h_preamble << "{" << p.verbatim_item() << "}";
1289                 }
1290
1291                 else if (t.cs() == "setcounter") {
1292                         string const name = p.getArg('{', '}');
1293                         string const content = p.getArg('{', '}');
1294                         if (name == "secnumdepth")
1295                                 h_secnumdepth = content;
1296                         else if (name == "tocdepth")
1297                                 h_tocdepth = content;
1298                         else
1299                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1300                 }
1301
1302                 else if (t.cs() == "setlength") {
1303                         string const name = p.verbatim_item();
1304                         string const content = p.verbatim_item();
1305                         // the paragraphs are only not indented when \parindent is set to zero
1306                         if (name == "\\parindent" && content != "") {
1307                                 if (content[0] == '0')
1308                                         h_paragraph_separation = "skip";
1309                                 else
1310                                         h_paragraph_indentation = translate_len(content);
1311                         } else if (name == "\\parskip") {
1312                                 if (content == "\\smallskipamount")
1313                                         h_defskip = "smallskip";
1314                                 else if (content == "\\medskipamount")
1315                                         h_defskip = "medskip";
1316                                 else if (content == "\\bigskipamount")
1317                                         h_defskip = "bigskip";
1318                                 else
1319                                         h_defskip = content;
1320                         } else
1321                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1322                 }
1323
1324                 else if (t.cs() == "onehalfspacing")
1325                         h_spacing = "onehalf";
1326
1327                 else if (t.cs() == "doublespacing")
1328                         h_spacing = "double";
1329
1330                 else if (t.cs() == "setstretch")
1331                         h_spacing = "other " + p.verbatim_item();
1332
1333                 else if (t.cs() == "begin") {
1334                         string const name = p.getArg('{', '}');
1335                         if (name == "document")
1336                                 break;
1337                         h_preamble << "\\begin{" << name << "}";
1338                 }
1339
1340                 else if (t.cs() == "geometry") {
1341                         vector<string> opts = split_options(p.getArg('{', '}'));
1342                         handle_geometry(opts);
1343                 }
1344
1345                 else if (t.cs() == "definecolor") {
1346                         string const color = p.getArg('{', '}');
1347                         string const space = p.getArg('{', '}');
1348                         string const value = p.getArg('{', '}');
1349                         if (color == "document_fontcolor" && space == "rgb") {
1350                                 RGBColor c(RGBColorFromLaTeX(value));
1351                                 h_fontcolor = X11hexname(c);
1352                         } else if (color == "note_fontcolor" && space == "rgb") {
1353                                 RGBColor c(RGBColorFromLaTeX(value));
1354                                 h_notefontcolor = X11hexname(c);
1355                         } else if (color == "page_backgroundcolor" && space == "rgb") {
1356                                 RGBColor c(RGBColorFromLaTeX(value));
1357                                 h_backgroundcolor = X11hexname(c);
1358                         } else if (color == "shadecolor" && space == "rgb") {
1359                                 RGBColor c(RGBColorFromLaTeX(value));
1360                                 h_boxbgcolor = X11hexname(c);
1361                         } else {
1362                                 h_preamble << "\\definecolor{" << color
1363                                            << "}{" << space << "}{" << value
1364                                            << '}';
1365                         }
1366                 }
1367
1368                 else if (t.cs() == "jurabibsetup") {
1369                         // FIXME p.getArg('{', '}') is most probably wrong (it
1370                         //       does not handle nested braces).
1371                         //       Use p.verbatim_item() instead.
1372                         vector<string> jurabibsetup =
1373                                 split_options(p.getArg('{', '}'));
1374                         // add jurabibsetup to the jurabib package options
1375                         add_package("jurabib", jurabibsetup);
1376                         if (!jurabibsetup.empty()) {
1377                                 h_preamble << "\\jurabibsetup{"
1378                                            << join(jurabibsetup, ",") << '}';
1379                         }
1380                 }
1381
1382                 else if (t.cs() == "hypersetup") {
1383                         vector<string> hypersetup =
1384                                 split_options(p.verbatim_item());
1385                         // add hypersetup to the hyperref package options
1386                         handle_hyperref(hypersetup);
1387                         if (!hypersetup.empty()) {
1388                                 h_preamble << "\\hypersetup{"
1389                                            << join(hypersetup, ",") << '}';
1390                         }
1391                 }
1392
1393                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1394                         // prevent misparsing of \usepackage if it is used
1395                         // as an argument (see e.g. our own output of
1396                         // \@ifundefined above)
1397                         string const arg1 = p.verbatim_item();
1398                         string const arg2 = p.verbatim_item();
1399                         string const arg3 = p.verbatim_item();
1400                         // test case \@ifundefined{date}{}{\date{}}
1401                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
1402                             arg2.empty() && arg3 == "\\date{}") {
1403                                 h_suppress_date = "true";
1404                         // older tex2lyx versions did output
1405                         // \@ifundefined{definecolor}{\usepackage{color}}{}
1406                         } else if (t.cs() == "@ifundefined" &&
1407                                    arg1 == "definecolor" &&
1408                                    arg2 == "\\usepackage{color}" &&
1409                                    arg3.empty()) {
1410                                 if (!in_lyx_preamble)
1411                                         h_preamble << package_beg_sep
1412                                                    << "color"
1413                                                    << package_mid_sep
1414                                                    << "\\@ifundefined{definecolor}{color}{}"
1415                                                    << package_end_sep;
1416                         // test for case
1417                         //\@ifundefined{showcaptionsetup}{}{%
1418                         // \PassOptionsToPackage{caption=false}{subfig}}
1419                         // that LyX uses for subfloats
1420                         } else if (t.cs() == "@ifundefined" &&
1421                                    arg1 == "showcaptionsetup" && arg2.empty()
1422                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1423                                 ; // do nothing
1424                         } else if (!in_lyx_preamble) {
1425                                 h_preamble << t.asInput()
1426                                            << '{' << arg1 << '}'
1427                                            << '{' << arg2 << '}'
1428                                            << '{' << arg3 << '}';
1429                         }
1430                 }
1431
1432                 else if (is_known(t.cs(), known_if_commands)) {
1433                         // must not parse anything in conditional code, since
1434                         // LyX would output the parsed contents unconditionally
1435                         if (!in_lyx_preamble)
1436                                 h_preamble << t.asInput();
1437                         handle_if(p, in_lyx_preamble);
1438                 }
1439
1440                 else if (!t.cs().empty() && !in_lyx_preamble)
1441                         h_preamble << '\\' << t.cs();
1442         }
1443
1444         // remove the whitespace
1445         p.skip_spaces();
1446
1447         // Force textclass if the user wanted it
1448         if (!forceclass.empty())
1449                 h_textclass = forceclass;
1450         if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1451                 h_textclass.insert(0, "literate-");
1452         tc.setName(h_textclass);
1453         if (!tc.load()) {
1454                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1455                 exit(EXIT_FAILURE);
1456         }
1457         if (h_papersides.empty()) {
1458                 ostringstream ss;
1459                 ss << tc.sides();
1460                 h_papersides = ss.str();
1461         }
1462 }
1463
1464
1465 string babel2lyx(string const & language)
1466 {
1467         char const * const * where = is_known(language, known_languages);
1468         if (where)
1469                 return known_coded_languages[where - known_languages];
1470         return language;
1471 }
1472
1473
1474 string rgbcolor2code(string const & name)
1475 {
1476         char const * const * where = is_known(name, known_basic_colors);
1477         if (where) {
1478                 // "red", "green" etc
1479                 return known_basic_color_codes[where - known_basic_colors];
1480         }
1481         // "255,0,0", "0,255,0" etc
1482         RGBColor c(RGBColorFromLaTeX(name));
1483         return X11hexname(c);
1484 }
1485
1486 // }])
1487
1488
1489 } // namespace lyx