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