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