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