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