]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
21106b19b928f1f3bdd095fbf75955bdcfb3dc93
[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_biblio_style            = "plain";
403         h_cite_engine             = "basic";
404         h_cite_engine_type        = "numerical";
405         h_defskip                 = "medskip";
406         //h_float_placement;
407         //h_fontcolor;
408         h_fontencoding            = "default";
409         h_font_roman              = "default";
410         h_font_sans               = "default";
411         h_font_typewriter         = "default";
412         h_font_default_family     = "default";
413         h_font_sc                 = "false";
414         h_font_osf                = "false";
415         h_font_sf_scale           = "100";
416         h_font_tt_scale           = "100";
417         h_graphics                = "default";
418         h_html_be_strict          = "false";
419         h_html_css_as_file        = "0";
420         h_html_math_output        = "0";
421         h_inputencoding           = "auto";
422         h_justification           = "true";
423         h_language                = "english";
424         h_language_package        = "none";
425         //h_listings_params;
426         //h_margins;
427         //h_notefontcolor;
428         //h_options;
429         h_output_changes          = "false";
430         h_papercolumns            = "1";
431         h_paperfontsize           = "default";
432         h_paperorientation        = "portrait";
433         h_paperpagestyle          = "default";
434         //h_papersides;
435         h_papersize               = "default";
436         h_paragraph_indentation   = "default";
437         h_paragraph_separation    = "indent";
438         //h_pdf_title;
439         //h_pdf_author;
440         //h_pdf_subject;
441         //h_pdf_keywords;
442         h_pdf_bookmarks           = "1";
443         h_pdf_bookmarksnumbered   = "0";
444         h_pdf_bookmarksopen       = "0";
445         h_pdf_bookmarksopenlevel  = "1";
446         h_pdf_breaklinks          = "0";
447         h_pdf_pdfborder           = "0";
448         h_pdf_colorlinks          = "0";
449         h_pdf_backref             = "section";
450         h_pdf_pdfusetitle         = "1";
451         //h_pdf_pagemode;
452         //h_pdf_quoted_options;
453         h_quotes_language         = "english";
454         h_secnumdepth             = "3";
455         h_spacing                 = "single";
456         h_suppress_date           = "false";
457         h_textclass               = "article";
458         h_tocdepth                = "3";
459         h_tracking_changes        = "false";
460         h_use_bibtopic            = "false";
461         h_use_indices             = "false";
462         h_use_geometry            = "false";
463         h_use_default_options     = "false";
464         h_use_hyperref            = "0";
465         h_use_refstyle            = "0";
466         h_use_packages["amsmath"]    = "1";
467         h_use_packages["esint"]      = "1";
468         h_use_packages["mhchem"]     = "0";
469         h_use_packages["mathdots"]   = "0";
470         h_use_packages["mathtools"]  = "0";
471         h_use_packages["undertilde"] = "0";
472 }
473
474
475 void Preamble::handle_hyperref(vector<string> & options)
476 {
477         // FIXME swallow inputencoding changes that might surround the
478         //       hyperref setup if it was written by LyX
479         h_use_hyperref = "1";
480         // swallow "unicode=true", since LyX does always write that
481         vector<string>::iterator it =
482                 find(options.begin(), options.end(), "unicode=true");
483         if (it != options.end())
484                 options.erase(it);
485         it = find(options.begin(), options.end(), "pdfusetitle");
486         if (it != options.end()) {
487                 h_pdf_pdfusetitle = "1";
488                 options.erase(it);
489         }
490         string bookmarks = process_keyval_opt(options, "bookmarks");
491         if (bookmarks == "true")
492                 h_pdf_bookmarks = "1";
493         else if (bookmarks == "false")
494                 h_pdf_bookmarks = "0";
495         if (h_pdf_bookmarks == "1") {
496                 string bookmarksnumbered =
497                         process_keyval_opt(options, "bookmarksnumbered");
498                 if (bookmarksnumbered == "true")
499                         h_pdf_bookmarksnumbered = "1";
500                 else if (bookmarksnumbered == "false")
501                         h_pdf_bookmarksnumbered = "0";
502                 string bookmarksopen =
503                         process_keyval_opt(options, "bookmarksopen");
504                 if (bookmarksopen == "true")
505                         h_pdf_bookmarksopen = "1";
506                 else if (bookmarksopen == "false")
507                         h_pdf_bookmarksopen = "0";
508                 if (h_pdf_bookmarksopen == "1") {
509                         string bookmarksopenlevel =
510                                 process_keyval_opt(options, "bookmarksopenlevel");
511                         if (!bookmarksopenlevel.empty())
512                                 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
513                 }
514         }
515         string breaklinks = process_keyval_opt(options, "breaklinks");
516         if (breaklinks == "true")
517                 h_pdf_breaklinks = "1";
518         else if (breaklinks == "false")
519                 h_pdf_breaklinks = "0";
520         string pdfborder = process_keyval_opt(options, "pdfborder");
521         if (pdfborder == "{0 0 0}")
522                 h_pdf_pdfborder = "1";
523         else if (pdfborder == "{0 0 1}")
524                 h_pdf_pdfborder = "0";
525         string backref = process_keyval_opt(options, "backref");
526         if (!backref.empty())
527                 h_pdf_backref = backref;
528         string colorlinks = process_keyval_opt(options, "colorlinks");
529         if (colorlinks == "true")
530                 h_pdf_colorlinks = "1";
531         else if (colorlinks == "false")
532                 h_pdf_colorlinks = "0";
533         string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
534         if (!pdfpagemode.empty())
535                 h_pdf_pagemode = pdfpagemode;
536         string pdftitle = process_keyval_opt(options, "pdftitle");
537         if (!pdftitle.empty()) {
538                 h_pdf_title = remove_braces(pdftitle);
539         }
540         string pdfauthor = process_keyval_opt(options, "pdfauthor");
541         if (!pdfauthor.empty()) {
542                 h_pdf_author = remove_braces(pdfauthor);
543         }
544         string pdfsubject = process_keyval_opt(options, "pdfsubject");
545         if (!pdfsubject.empty())
546                 h_pdf_subject = remove_braces(pdfsubject);
547         string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
548         if (!pdfkeywords.empty())
549                 h_pdf_keywords = remove_braces(pdfkeywords);
550         if (!options.empty()) {
551                 if (!h_pdf_quoted_options.empty())
552                         h_pdf_quoted_options += ',';
553                 h_pdf_quoted_options += join(options, ",");
554                 options.clear();
555         }
556 }
557
558
559 void Preamble::handle_geometry(vector<string> & options)
560 {
561         h_use_geometry = "true";
562         vector<string>::iterator it;
563         // paper orientation
564         if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
565                 h_paperorientation = "landscape";
566                 options.erase(it);
567         }
568         // paper size
569         // keyval version: "paper=letter"
570         string paper = process_keyval_opt(options, "paper");
571         if (!paper.empty())
572                 h_papersize = paper + "paper";
573         // alternative version: "letterpaper"
574         handle_opt(options, known_paper_sizes, h_papersize);
575         delete_opt(options, known_paper_sizes);
576         // page margins
577         char const * const * margin = known_paper_margins;
578         for (; *margin; ++margin) {
579                 string value = process_keyval_opt(options, *margin);
580                 if (!value.empty()) {
581                         int k = margin - known_paper_margins;
582                         string name = known_coded_paper_margins[k];
583                         h_margins += '\\' + name + ' ' + value + '\n';
584                 }
585         }
586 }
587
588
589 void Preamble::handle_package(Parser &p, string const & name,
590                               string const & opts, bool in_lyx_preamble)
591 {
592         vector<string> options = split_options(opts);
593         add_package(name, options);
594         string scale;
595
596         if (is_known(name, known_xetex_packages))
597                 xetex = true;
598
599         // roman fonts
600         if (is_known(name, known_roman_fonts)) {
601                 h_font_roman = name;
602                 p.skip_spaces();
603         }
604
605         if (name == "fourier") {
606                 h_font_roman = "utopia";
607                 // when font uses real small capitals
608                 if (opts == "expert")
609                         h_font_sc = "true";
610         }
611
612         else if (name == "mathpazo")
613                 h_font_roman = "palatino";
614
615         else if (name == "mathptmx")
616                 h_font_roman = "times";
617
618         // sansserif fonts
619         if (is_known(name, known_sans_fonts)) {
620                 h_font_sans = name;
621                 if (!opts.empty()) {
622                         scale = opts;
623                         h_font_sf_scale = scale_as_percentage(scale);
624                 }
625         }
626
627         // typewriter fonts
628         if (is_known(name, known_typewriter_fonts)) {
629                 // fourier can be set as roman font _only_
630                 // fourier as typewriter is handled in handling of \ttdefault
631                 if (name != "fourier") {
632                         h_font_typewriter = name;
633                         if (!opts.empty()) {
634                                 scale = opts;
635                                 h_font_tt_scale = scale_as_percentage(scale);
636                         }
637                 }
638         }
639
640         // font uses old-style figure
641         if (name == "eco")
642                 h_font_osf = "true";
643
644         // after the detection and handling of special cases, we can remove the
645         // fonts, otherwise they would appear in the preamble, see bug #7856
646         if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
647                 ||      is_known(name, known_typewriter_fonts))
648                 ;
649
650         else if (name == "amsmath" || name == "amssymb")
651                 h_use_packages["amsmath"] = "2";
652
653         else if (name == "esint" || name == "mhchem" || name == "mathdots" ||
654                  name == "mathtools" || name == "undertilde")
655                 h_use_packages[name] = "2";
656
657         else if (name == "babel") {
658                 h_language_package = "default";
659                 // One might think we would have to do nothing if babel is loaded
660                 // without any options to prevent pollution of the preamble with this
661                 // babel call in every roundtrip.
662                 // But the user could have defined babel-specific things afterwards. So
663                 // we need to keep it in the preamble to prevent cases like bug #7861.
664                 if (!opts.empty()) {
665                         // check if more than one option was used - used later for inputenc
666                         if (options.begin() != options.end() - 1)
667                                 one_language = false;
668                         // babel takes the last language of the option of its \usepackage
669                         // call as document language. If there is no such language option, the
670                         // last language in the documentclass options is used.
671                         handle_opt(options, known_languages, h_language);
672                         // If babel is called with options, LyX puts them by default into the
673                         // document class options. This works for most languages, except
674                         // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
675                         // perhaps in future others.
676                         // Therefore keep the babel call as it is as the user might have
677                         // reasons for it.
678                         h_preamble << "\\usepackage[" << opts << "]{babel}\n";
679                         delete_opt(options, known_languages);
680                 }
681                 else
682                         h_preamble << "\\usepackage{babel}\n";
683         }
684
685         else if (name == "fontenc") {
686                 h_fontencoding = getStringFromVector(options, ",");
687                 /* We could do the following for better round trip support,
688                  * but this makes the document less portable, so I skip it:
689                 if (h_fontencoding == lyxrc.fontenc)
690                         h_fontencoding = "global";
691                  */
692                 options.clear();
693         }
694
695         else if (name == "inputenc" || name == "luainputenc") {
696                 // h_inputencoding is only set when there is not more than one
697                 // inputenc option because otherwise h_inputencoding must be
698                 // set to "auto" (the default encoding of the document language)
699                 // Therefore check for the "," character.
700                 // It is also only set when there is not more than one babel
701                 // language option.
702                 if (opts.find(",") == string::npos && one_language == true)
703                         h_inputencoding = opts;
704                 if (!options.empty())
705                         p.setEncoding(options.back());
706                 options.clear();
707         }
708
709         else if (is_known(name, known_old_language_packages)) {
710                 // known language packages from the times before babel
711                 // if they are found and not also babel, they will be used as
712                 // custom language package
713                 h_language_package = "\\usepackage{" + name + "}";
714         }
715
716         else if (name == "prettyref")
717                 ; // ignore this FIXME: Use the package separator mechanism instead
718
719         else if (name == "lyxskak") {
720                 // ignore this and its options
721                 const char * const o[] = {"ps", "mover", 0};
722                 delete_opt(options, o);
723         }
724
725         else if (is_known(name, known_lyx_packages) && options.empty()) {
726                 if (!in_lyx_preamble)
727                         h_preamble << package_beg_sep << name
728                                    << package_mid_sep << "\\usepackage{"
729                                    << name << "}\n" << package_end_sep;
730         }
731
732         else if (name == "geometry")
733                 handle_geometry(options);
734
735         else if (name == "subfig")
736                 ; // ignore this FIXME: Use the package separator mechanism instead
737
738         else if (is_known(name, known_languages))
739                 h_language = name;
740
741         else if (name == "natbib") {
742                 h_biblio_style = "plainnat";
743                 h_cite_engine = "natbib";
744                 h_cite_engine_type = "authoryear";
745                 vector<string>::iterator it =
746                         find(options.begin(), options.end(), "authoryear");
747                 if (it != options.end())
748                         options.erase(it);
749                 else {
750                         it = find(options.begin(), options.end(), "numbers");
751                         if (it != options.end()) {
752                                 h_cite_engine_type = "numerical";
753                                 options.erase(it);
754                         }
755                 }
756         }
757
758         else if (name == "jurabib") {
759                 h_biblio_style = "jurabib";
760                 h_cite_engine = "jurabib";
761                 h_cite_engine_type = "authoryear";
762         }
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         for (map<string, string>::const_iterator it = h_use_packages.begin();
926              it != h_use_packages.end(); it++)
927                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
928         os << "\\cite_engine " << h_cite_engine << '\n'
929            << "\\cite_engine_type " << h_cite_engine_type << '\n'
930            << "\\biblio_style " << h_biblio_style << "\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() == "bibliographystyle")
1353                         h_biblio_style = p.verbatim_item();
1354
1355                 else if (t.cs() == "jurabibsetup") {
1356                         // FIXME p.getArg('{', '}') is most probably wrong (it
1357                         //       does not handle nested braces).
1358                         //       Use p.verbatim_item() instead.
1359                         vector<string> jurabibsetup =
1360                                 split_options(p.getArg('{', '}'));
1361                         // add jurabibsetup to the jurabib package options
1362                         add_package("jurabib", jurabibsetup);
1363                         if (!jurabibsetup.empty()) {
1364                                 h_preamble << "\\jurabibsetup{"
1365                                            << join(jurabibsetup, ",") << '}';
1366                         }
1367                 }
1368
1369                 else if (t.cs() == "hypersetup") {
1370                         vector<string> hypersetup =
1371                                 split_options(p.verbatim_item());
1372                         // add hypersetup to the hyperref package options
1373                         handle_hyperref(hypersetup);
1374                         if (!hypersetup.empty()) {
1375                                 h_preamble << "\\hypersetup{"
1376                                            << join(hypersetup, ",") << '}';
1377                         }
1378                 }
1379
1380                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1381                         // prevent misparsing of \usepackage if it is used
1382                         // as an argument (see e.g. our own output of
1383                         // \@ifundefined above)
1384                         string const arg1 = p.verbatim_item();
1385                         string const arg2 = p.verbatim_item();
1386                         string const arg3 = p.verbatim_item();
1387                         // test case \@ifundefined{date}{}{\date{}}
1388                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
1389                             arg2.empty() && arg3 == "\\date{}") {
1390                                 h_suppress_date = "true";
1391                         // older tex2lyx versions did output
1392                         // \@ifundefined{definecolor}{\usepackage{color}}{}
1393                         } else if (t.cs() == "@ifundefined" &&
1394                                    arg1 == "definecolor" &&
1395                                    arg2 == "\\usepackage{color}" &&
1396                                    arg3.empty()) {
1397                                 if (!in_lyx_preamble)
1398                                         h_preamble << package_beg_sep
1399                                                    << "color"
1400                                                    << package_mid_sep
1401                                                    << "\\@ifundefined{definecolor}{color}{}"
1402                                                    << package_end_sep;
1403                         // test for case
1404                         //\@ifundefined{showcaptionsetup}{}{%
1405                         // \PassOptionsToPackage{caption=false}{subfig}}
1406                         // that LyX uses for subfloats
1407                         } else if (t.cs() == "@ifundefined" &&
1408                                    arg1 == "showcaptionsetup" && arg2.empty()
1409                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1410                                 ; // do nothing
1411                         } else if (!in_lyx_preamble) {
1412                                 h_preamble << t.asInput()
1413                                            << '{' << arg1 << '}'
1414                                            << '{' << arg2 << '}'
1415                                            << '{' << arg3 << '}';
1416                         }
1417                 }
1418
1419                 else if (is_known(t.cs(), known_if_commands)) {
1420                         // must not parse anything in conditional code, since
1421                         // LyX would output the parsed contents unconditionally
1422                         if (!in_lyx_preamble)
1423                                 h_preamble << t.asInput();
1424                         handle_if(p, in_lyx_preamble);
1425                 }
1426
1427                 else if (!t.cs().empty() && !in_lyx_preamble)
1428                         h_preamble << '\\' << t.cs();
1429         }
1430
1431         // remove the whitespace
1432         p.skip_spaces();
1433
1434         // Force textclass if the user wanted it
1435         if (!forceclass.empty())
1436                 h_textclass = forceclass;
1437         if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1438                 h_textclass.insert(0, "literate-");
1439         tc.setName(h_textclass);
1440         if (!tc.load()) {
1441                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1442                 exit(EXIT_FAILURE);
1443         }
1444         if (h_papersides.empty()) {
1445                 ostringstream ss;
1446                 ss << tc.sides();
1447                 h_papersides = ss.str();
1448         }
1449 }
1450
1451
1452 string babel2lyx(string const & language)
1453 {
1454         char const * const * where = is_known(language, known_languages);
1455         if (where)
1456                 return known_coded_languages[where - known_languages];
1457         return language;
1458 }
1459
1460
1461 string rgbcolor2code(string const & name)
1462 {
1463         char const * const * where = is_known(name, known_basic_colors);
1464         if (where) {
1465                 // "red", "green" etc
1466                 return known_basic_color_codes[where - known_basic_colors];
1467         }
1468         // "255,0,0", "0,255,0" etc
1469         RGBColor c(RGBColorFromLaTeX(name));
1470         return X11hexname(c);
1471 }
1472
1473 // }])
1474
1475
1476 } // namespace lyx