]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
ad9b76014cf1b6fab38844bb8679426bf6d6a5b3
[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_output_sync_macro
474         h_papercolumns            = "1";
475         h_paperfontsize           = "default";
476         h_paperorientation        = "portrait";
477         h_paperpagestyle          = "default";
478         //h_papersides;
479         h_papersize               = "default";
480         h_paragraph_indentation   = "default";
481         h_paragraph_separation    = "indent";
482         //h_pdf_title;
483         //h_pdf_author;
484         //h_pdf_subject;
485         //h_pdf_keywords;
486         h_pdf_bookmarks           = "1";
487         h_pdf_bookmarksnumbered   = "0";
488         h_pdf_bookmarksopen       = "0";
489         h_pdf_bookmarksopenlevel  = "1";
490         h_pdf_breaklinks          = "0";
491         h_pdf_pdfborder           = "0";
492         h_pdf_colorlinks          = "0";
493         h_pdf_backref             = "section";
494         h_pdf_pdfusetitle         = "1";
495         //h_pdf_pagemode;
496         //h_pdf_quoted_options;
497         h_quotes_language         = "english";
498         h_secnumdepth             = "3";
499         h_shortcut                = "idx";
500         h_spacing                 = "single";
501         h_suppress_date           = "false";
502         h_textclass               = "article";
503         h_tocdepth                = "3";
504         h_tracking_changes        = "false";
505         h_use_bibtopic            = "false";
506         h_use_indices             = "false";
507         h_use_geometry            = "false";
508         h_use_default_options     = "false";
509         h_use_hyperref            = "0";
510         h_use_refstyle            = "1";
511         h_use_packages["amsmath"]    = "1";
512         h_use_packages["amssymb"]    = "1";
513         h_use_packages["esint"]      = "1";
514         h_use_packages["mhchem"]     = "1";
515         h_use_packages["mathdots"]   = "1";
516         h_use_packages["mathtools"]  = "1";
517         h_use_packages["undertilde"] = "1";
518 }
519
520
521 void Preamble::handle_hyperref(vector<string> & options)
522 {
523         // FIXME swallow inputencoding changes that might surround the
524         //       hyperref setup if it was written by LyX
525         h_use_hyperref = "1";
526         // swallow "unicode=true", since LyX does always write that
527         vector<string>::iterator it =
528                 find(options.begin(), options.end(), "unicode=true");
529         if (it != options.end())
530                 options.erase(it);
531         it = find(options.begin(), options.end(), "pdfusetitle");
532         if (it != options.end()) {
533                 h_pdf_pdfusetitle = "1";
534                 options.erase(it);
535         }
536         string bookmarks = process_keyval_opt(options, "bookmarks");
537         if (bookmarks == "true")
538                 h_pdf_bookmarks = "1";
539         else if (bookmarks == "false")
540                 h_pdf_bookmarks = "0";
541         if (h_pdf_bookmarks == "1") {
542                 string bookmarksnumbered =
543                         process_keyval_opt(options, "bookmarksnumbered");
544                 if (bookmarksnumbered == "true")
545                         h_pdf_bookmarksnumbered = "1";
546                 else if (bookmarksnumbered == "false")
547                         h_pdf_bookmarksnumbered = "0";
548                 string bookmarksopen =
549                         process_keyval_opt(options, "bookmarksopen");
550                 if (bookmarksopen == "true")
551                         h_pdf_bookmarksopen = "1";
552                 else if (bookmarksopen == "false")
553                         h_pdf_bookmarksopen = "0";
554                 if (h_pdf_bookmarksopen == "1") {
555                         string bookmarksopenlevel =
556                                 process_keyval_opt(options, "bookmarksopenlevel");
557                         if (!bookmarksopenlevel.empty())
558                                 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
559                 }
560         }
561         string breaklinks = process_keyval_opt(options, "breaklinks");
562         if (breaklinks == "true")
563                 h_pdf_breaklinks = "1";
564         else if (breaklinks == "false")
565                 h_pdf_breaklinks = "0";
566         string pdfborder = process_keyval_opt(options, "pdfborder");
567         if (pdfborder == "{0 0 0}")
568                 h_pdf_pdfborder = "1";
569         else if (pdfborder == "{0 0 1}")
570                 h_pdf_pdfborder = "0";
571         string backref = process_keyval_opt(options, "backref");
572         if (!backref.empty())
573                 h_pdf_backref = backref;
574         string colorlinks = process_keyval_opt(options, "colorlinks");
575         if (colorlinks == "true")
576                 h_pdf_colorlinks = "1";
577         else if (colorlinks == "false")
578                 h_pdf_colorlinks = "0";
579         string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
580         if (!pdfpagemode.empty())
581                 h_pdf_pagemode = pdfpagemode;
582         string pdftitle = process_keyval_opt(options, "pdftitle");
583         if (!pdftitle.empty()) {
584                 h_pdf_title = remove_braces(pdftitle);
585         }
586         string pdfauthor = process_keyval_opt(options, "pdfauthor");
587         if (!pdfauthor.empty()) {
588                 h_pdf_author = remove_braces(pdfauthor);
589         }
590         string pdfsubject = process_keyval_opt(options, "pdfsubject");
591         if (!pdfsubject.empty())
592                 h_pdf_subject = remove_braces(pdfsubject);
593         string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
594         if (!pdfkeywords.empty())
595                 h_pdf_keywords = remove_braces(pdfkeywords);
596         if (!options.empty()) {
597                 if (!h_pdf_quoted_options.empty())
598                         h_pdf_quoted_options += ',';
599                 h_pdf_quoted_options += join(options, ",");
600                 options.clear();
601         }
602 }
603
604
605 void Preamble::handle_geometry(vector<string> & options)
606 {
607         h_use_geometry = "true";
608         vector<string>::iterator it;
609         // paper orientation
610         if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
611                 h_paperorientation = "landscape";
612                 options.erase(it);
613         }
614         // paper size
615         // keyval version: "paper=letter"
616         string paper = process_keyval_opt(options, "paper");
617         if (!paper.empty())
618                 h_papersize = paper + "paper";
619         // alternative version: "letterpaper"
620         handle_opt(options, known_paper_sizes, h_papersize);
621         delete_opt(options, known_paper_sizes);
622         // page margins
623         char const * const * margin = known_paper_margins;
624         for (; *margin; ++margin) {
625                 string value = process_keyval_opt(options, *margin);
626                 if (!value.empty()) {
627                         int k = margin - known_paper_margins;
628                         string name = known_coded_paper_margins[k];
629                         h_margins += '\\' + name + ' ' + value + '\n';
630                 }
631         }
632 }
633
634
635 void Preamble::handle_package(Parser &p, string const & name,
636                               string const & opts, bool in_lyx_preamble)
637 {
638         vector<string> options = split_options(opts);
639         add_package(name, options);
640         string scale;
641
642         if (is_known(name, known_xetex_packages)) {
643                 xetex = true;
644                 h_use_non_tex_fonts = "true";
645                 registerAutomaticallyLoadedPackage("fontspec");
646                 if (h_inputencoding == "auto")
647                         p.setEncoding("utf8");
648         }
649
650         // roman fonts
651         if (is_known(name, known_roman_fonts)) {
652                 h_font_roman = name;
653                 p.skip_spaces();
654         }
655
656         if (name == "fourier") {
657                 h_font_roman = "utopia";
658                 // when font uses real small capitals
659                 if (opts == "expert")
660                         h_font_sc = "true";
661         }
662
663         else if (name == "mathpazo")
664                 h_font_roman = "palatino";
665
666         else if (name == "mathptmx")
667                 h_font_roman = "times";
668
669         // sansserif fonts
670         if (is_known(name, known_sans_fonts)) {
671                 h_font_sans = name;
672                 if (!opts.empty()) {
673                         scale = opts;
674                         h_font_sf_scale = scale_as_percentage(scale);
675                 }
676         }
677
678         // typewriter fonts
679         if (is_known(name, known_typewriter_fonts)) {
680                 // fourier can be set as roman font _only_
681                 // fourier as typewriter is handled in handling of \ttdefault
682                 if (name != "fourier") {
683                         h_font_typewriter = name;
684                         if (!opts.empty()) {
685                                 scale = opts;
686                                 h_font_tt_scale = scale_as_percentage(scale);
687                         }
688                 }
689         }
690
691         // font uses old-style figure
692         if (name == "eco")
693                 h_font_osf = "true";
694
695         // after the detection and handling of special cases, we can remove the
696         // fonts, otherwise they would appear in the preamble, see bug #7856
697         if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
698                 ||      is_known(name, known_typewriter_fonts))
699                 ;
700
701         else if (name == "amsmath" || name == "amssymb" ||
702                  name == "esint" || name == "mhchem" || name == "mathdots" ||
703                  name == "mathtools" || name == "undertilde")
704                 h_use_packages[name] = "2";
705
706         else if (name == "babel") {
707                 h_language_package = "default";
708                 // One might think we would have to do nothing if babel is loaded
709                 // without any options to prevent pollution of the preamble with this
710                 // babel call in every roundtrip.
711                 // But the user could have defined babel-specific things afterwards. So
712                 // we need to keep it in the preamble to prevent cases like bug #7861.
713                 if (!opts.empty()) {
714                         // check if more than one option was used - used later for inputenc
715                         if (options.begin() != options.end() - 1)
716                                 one_language = false;
717                         // babel takes the last language of the option of its \usepackage
718                         // call as document language. If there is no such language option, the
719                         // last language in the documentclass options is used.
720                         handle_opt(options, known_languages, h_language);
721                         // If babel is called with options, LyX puts them by default into the
722                         // document class options. This works for most languages, except
723                         // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
724                         // perhaps in future others.
725                         // Therefore keep the babel call as it is as the user might have
726                         // reasons for it.
727                         h_preamble << "\\usepackage[" << opts << "]{babel}\n";
728                         delete_opt(options, known_languages);
729                         // finally translate the babel name to a LyX name
730                         h_language = babel2lyx(h_language);
731                 }
732                 else
733                         h_preamble << "\\usepackage{babel}\n";
734         }
735
736         else if (name == "polyglossia") {
737                 h_language_package = "default";
738                 h_default_output_format = "pdf4";
739                 h_use_non_tex_fonts = "true";
740                 xetex = true;
741                 registerAutomaticallyLoadedPackage("xunicode");
742                 if (h_inputencoding == "auto")
743                         p.setEncoding("utf8");
744         }
745
746         else if (name == "CJK") {
747                 // It is impossible to determine the document language if CJK is used.
748                 // All we can do is to notify the user that he has to set this by hisself.
749                 have_CJK = true;
750                 // set the encoding to "auto" because it might be set to "default" by the babel handling
751                 // and this would not be correct for CJK
752                 h_inputencoding = "auto";
753                 registerAutomaticallyLoadedPackage("CJK");
754         }
755
756         else if (name == "fontenc") {
757                 h_fontencoding = getStringFromVector(options, ",");
758                 /* We could do the following for better round trip support,
759                  * but this makes the document less portable, so I skip it:
760                 if (h_fontencoding == lyxrc.fontenc)
761                         h_fontencoding = "global";
762                  */
763                 options.clear();
764         }
765
766         else if (name == "inputenc" || name == "luainputenc") {
767                 // h_inputencoding is only set when there is not more than one
768                 // inputenc option because otherwise h_inputencoding must be
769                 // set to "auto" (the default encoding of the document language)
770                 // Therefore check for the "," character.
771                 // It is also only set when there is not more than one babel
772                 // language option.
773                 if (opts.find(",") == string::npos && one_language == true)
774                         h_inputencoding = opts;
775                 if (!options.empty())
776                         p.setEncoding(options.back());
777                 options.clear();
778         }
779
780         else if (name == "srcltx") {
781                 h_output_sync = "1";
782                 if (!opts.empty()) {
783                         h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
784                         options.clear();
785                 } else
786                         h_output_sync_macro = "\\usepackage{srcltx}";
787         }
788
789         else if (is_known(name, known_old_language_packages)) {
790                 // known language packages from the times before babel
791                 // if they are found and not also babel, they will be used as
792                 // custom language package
793                 h_language_package = "\\usepackage{" + name + "}";
794         }
795
796         else if (name == "prettyref")
797                 ; // ignore this FIXME: Use the package separator mechanism instead
798
799         else if (name == "lyxskak") {
800                 // ignore this and its options
801                 const char * const o[] = {"ps", "mover", 0};
802                 delete_opt(options, o);
803         }
804
805         else if (is_known(name, known_lyx_packages) && options.empty()) {
806                 if (name == "splitidx")
807                         h_use_indices = "true";
808                 if (!in_lyx_preamble)
809                         h_preamble << package_beg_sep << name
810                                    << package_mid_sep << "\\usepackage{"
811                                    << name << "}\n" << package_end_sep;
812         }
813
814         else if (name == "geometry")
815                 handle_geometry(options);
816
817         else if (name == "subfig")
818                 ; // ignore this FIXME: Use the package separator mechanism instead
819
820         else if (is_known(name, known_languages))
821                 h_language = name;
822
823         else if (name == "natbib") {
824                 h_biblio_style = "plainnat";
825                 h_cite_engine = "natbib";
826                 h_cite_engine_type = "authoryear";
827                 vector<string>::iterator it =
828                         find(options.begin(), options.end(), "authoryear");
829                 if (it != options.end())
830                         options.erase(it);
831                 else {
832                         it = find(options.begin(), options.end(), "numbers");
833                         if (it != options.end()) {
834                                 h_cite_engine_type = "numerical";
835                                 options.erase(it);
836                         }
837                 }
838         }
839
840         else if (name == "jurabib") {
841                 h_biblio_style = "jurabib";
842                 h_cite_engine = "jurabib";
843                 h_cite_engine_type = "authoryear";
844         }
845
846         else if (name == "hyperref")
847                 handle_hyperref(options);
848
849         else if (!in_lyx_preamble) {
850                 if (options.empty())
851                         h_preamble << "\\usepackage{" << name << "}\n";
852                 else {
853                         h_preamble << "\\usepackage[" << opts << "]{"
854                                    << name << "}\n";
855                         options.clear();
856                 }
857         }
858
859         // We need to do something with the options...
860         if (!options.empty())
861                 cerr << "Ignoring options '" << join(options, ",")
862                      << "' of package " << name << '.' << endl;
863
864         // remove the whitespace
865         p.skip_spaces();
866 }
867
868
869 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
870 {
871         while (p.good()) {
872                 Token t = p.get_token();
873                 if (t.cat() == catEscape &&
874                     is_known(t.cs(), known_if_commands))
875                         handle_if(p, in_lyx_preamble);
876                 else {
877                         if (!in_lyx_preamble)
878                                 h_preamble << t.asInput();
879                         if (t.cat() == catEscape && t.cs() == "fi")
880                                 return;
881                 }
882         }
883 }
884
885
886 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
887 {
888         // set the quote language
889         // LyX only knows the following quotes languages:
890         // english, swedish, german, polish, french and danish
891         // (quotes for "japanese" and "chinese-traditional" are missing because
892         //  they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
893         // conversion list taken from
894         // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
895         // (quotes for kazakh and interlingua are unknown)
896         // danish
897         if (h_language == "danish")
898                 h_quotes_language = "danish";
899         // french
900         else if (is_known(h_language, known_french_quotes_languages))
901                 h_quotes_language = "french";
902         // german
903         else if (is_known(h_language, known_german_quotes_languages))
904                 h_quotes_language = "german";
905         // polish
906         else if (is_known(h_language, known_polish_quotes_languages))
907                 h_quotes_language = "polish";
908         // swedish
909         else if (is_known(h_language, known_swedish_quotes_languages))
910                 h_quotes_language = "swedish";
911         //english
912         else if (is_known(h_language, known_english_quotes_languages))
913                 h_quotes_language = "english";
914
915         if (contains(h_float_placement, "H"))
916                 registerAutomaticallyLoadedPackage("float");
917         if (h_spacing != "single" && h_spacing != "default")
918                 registerAutomaticallyLoadedPackage("setspace");
919         if (h_use_packages["amsmath"] == "2") {
920                 // amsbsy and amstext are already provided by amsmath
921                 registerAutomaticallyLoadedPackage("amsbsy");
922                 registerAutomaticallyLoadedPackage("amstext");
923         }
924
925         // output the LyX file settings
926         os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
927            << "\\lyxformat " << LYX_FORMAT << '\n'
928            << "\\begin_document\n"
929            << "\\begin_header\n"
930            << "\\textclass " << h_textclass << "\n";
931         string const raw = subdoc ? empty_string() : h_preamble.str();
932         if (!raw.empty()) {
933                 os << "\\begin_preamble\n";
934                 for (string::size_type i = 0; i < raw.size(); ++i) {
935                         if (raw[i] == package_beg_sep) {
936                                 // Here follows some package loading code that
937                                 // must be skipped if the package is loaded
938                                 // automatically.
939                                 string::size_type j = raw.find(package_mid_sep, i);
940                                 if (j == string::npos)
941                                         return false;
942                                 string::size_type k = raw.find(package_end_sep, j);
943                                 if (k == string::npos)
944                                         return false;
945                                 string const package = raw.substr(i + 1, j - i - 1);
946                                 string const replacement = raw.substr(j + 1, k - j - 1);
947                                 if (auto_packages.find(package) == auto_packages.end())
948                                         os << replacement;
949                                 i = k;
950                         } else
951                                 os.put(raw[i]);
952                 }
953                 os << "\n\\end_preamble\n";
954         }
955         if (!h_options.empty())
956                 os << "\\options " << h_options << "\n";
957         os << "\\use_default_options " << h_use_default_options << "\n";
958         if (!used_modules.empty()) {
959                 os << "\\begin_modules\n";
960                 vector<string>::const_iterator const end = used_modules.end();
961                 vector<string>::const_iterator it = used_modules.begin();
962                 for (; it != end; ++it)
963                         os << *it << '\n';
964                 os << "\\end_modules\n";
965         }
966         os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
967            << "\\language " << h_language << "\n"
968            << "\\language_package " << h_language_package << "\n"
969            << "\\inputencoding " << h_inputencoding << "\n"
970            << "\\fontencoding " << h_fontencoding << "\n"
971            << "\\font_roman " << h_font_roman << "\n"
972            << "\\font_sans " << h_font_sans << "\n"
973            << "\\font_typewriter " << h_font_typewriter << "\n"
974            << "\\font_default_family " << h_font_default_family << "\n"
975            << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
976            << "\\font_sc " << h_font_sc << "\n"
977            << "\\font_osf " << h_font_osf << "\n"
978            << "\\font_sf_scale " << h_font_sf_scale << "\n"
979            << "\\font_tt_scale " << h_font_tt_scale << "\n\n"
980            << "\\graphics " << h_graphics << "\n"
981            << "\\default_output_format " << h_default_output_format << "\n"
982            << "\\output_sync " << h_output_sync << "\n";
983         if (h_output_sync == "1")
984                 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
985         os << "\\bibtex_command " << h_bibtex_command << "\n"
986            << "\\index_command " << h_index_command << "\n";
987         if (!h_float_placement.empty())
988                 os << "\\float_placement " << h_float_placement << "\n";
989         os << "\\paperfontsize " << h_paperfontsize << "\n"
990            << "\\spacing " << h_spacing << "\n"
991            << "\\use_hyperref " << h_use_hyperref << '\n';
992         if (h_use_hyperref == "1") {
993                 if (!h_pdf_title.empty())
994                         os << "\\pdf_title \"" << h_pdf_title << "\"\n";
995                 if (!h_pdf_author.empty())
996                         os << "\\pdf_author \"" << h_pdf_author << "\"\n";
997                 if (!h_pdf_subject.empty())
998                         os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
999                 if (!h_pdf_keywords.empty())
1000                         os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
1001                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1002                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1003                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1004                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1005                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1006                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1007                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1008                       "\\pdf_backref " << h_pdf_backref << "\n"
1009                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1010                 if (!h_pdf_pagemode.empty())
1011                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1012                 if (!h_pdf_quoted_options.empty())
1013                         os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
1014         }
1015         os << "\\papersize " << h_papersize << "\n"
1016            << "\\use_geometry " << h_use_geometry << '\n';
1017         for (map<string, string>::const_iterator it = h_use_packages.begin();
1018              it != h_use_packages.end(); ++it)
1019                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1020         os << "\\cite_engine " << h_cite_engine << '\n'
1021            << "\\cite_engine_type " << h_cite_engine_type << '\n'
1022            << "\\biblio_style " << h_biblio_style << "\n"
1023            << "\\use_bibtopic " << h_use_bibtopic << "\n"
1024            << "\\use_indices " << h_use_indices << "\n"
1025            << "\\paperorientation " << h_paperorientation << '\n'
1026            << "\\suppress_date " << h_suppress_date << '\n'
1027            << "\\justification " << h_justification << '\n'
1028            << "\\use_refstyle " << h_use_refstyle << '\n';
1029         if (!h_fontcolor.empty())
1030                 os << "\\fontcolor " << h_fontcolor << '\n';
1031         if (!h_notefontcolor.empty())
1032                 os << "\\notefontcolor " << h_notefontcolor << '\n';
1033         if (!h_backgroundcolor.empty())
1034                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1035         if (!h_boxbgcolor.empty())
1036                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1037         os << "\\index " << h_index << '\n'
1038            << "\\shortcut " << h_shortcut << '\n'
1039            << "\\color " << h_color << '\n'
1040            << "\\end_index\n";
1041         os << h_margins
1042            << "\\secnumdepth " << h_secnumdepth << "\n"
1043            << "\\tocdepth " << h_tocdepth << "\n"
1044            << "\\paragraph_separation " << h_paragraph_separation << "\n";
1045         if (h_paragraph_separation == "skip")
1046                 os << "\\defskip " << h_defskip << "\n";
1047         else
1048                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1049         os << "\\quotes_language " << h_quotes_language << "\n"
1050            << "\\papercolumns " << h_papercolumns << "\n"
1051            << "\\papersides " << h_papersides << "\n"
1052            << "\\paperpagestyle " << h_paperpagestyle << "\n";
1053         if (!h_listings_params.empty())
1054                 os << "\\listings_params " << h_listings_params << "\n";
1055         os << "\\tracking_changes " << h_tracking_changes << "\n"
1056            << "\\output_changes " << h_output_changes << "\n"
1057            << "\\html_math_output " << h_html_math_output << "\n"
1058            << "\\html_css_as_file " << h_html_css_as_file << "\n"
1059            << "\\html_be_strict " << h_html_be_strict << "\n"
1060            << authors_
1061            << "\\end_header\n\n"
1062            << "\\begin_body\n";
1063         return true;
1064 }
1065
1066
1067 void Preamble::parse(Parser & p, string const & forceclass,
1068                      TeX2LyXDocClass & tc)
1069 {
1070         // initialize fixed types
1071         special_columns['D'] = 3;
1072         bool is_full_document = false;
1073         bool is_lyx_file = false;
1074         bool in_lyx_preamble = false;
1075
1076         // determine whether this is a full document or a fragment for inclusion
1077         while (p.good()) {
1078                 Token const & t = p.get_token();
1079
1080                 if (t.cat() == catEscape && t.cs() == "documentclass") {
1081                         is_full_document = true;
1082                         break;
1083                 }
1084         }
1085         p.reset();
1086
1087         while (is_full_document && p.good()) {
1088                 Token const & t = p.get_token();
1089
1090 #ifdef FILEDEBUG
1091                 cerr << "t: " << t << "\n";
1092 #endif
1093
1094                 //
1095                 // cat codes
1096                 //
1097                 if (!in_lyx_preamble &&
1098                     (t.cat() == catLetter ||
1099                      t.cat() == catSuper ||
1100                      t.cat() == catSub ||
1101                      t.cat() == catOther ||
1102                      t.cat() == catMath ||
1103                      t.cat() == catActive ||
1104                      t.cat() == catBegin ||
1105                      t.cat() == catEnd ||
1106                      t.cat() == catAlign ||
1107                      t.cat() == catParameter))
1108                         h_preamble << t.cs();
1109
1110                 else if (!in_lyx_preamble &&
1111                          (t.cat() == catSpace || t.cat() == catNewline))
1112                         h_preamble << t.asInput();
1113
1114                 else if (t.cat() == catComment) {
1115                         static regex const islyxfile("%% LyX .* created this file");
1116                         static regex const usercommands("User specified LaTeX commands");
1117
1118                         string const comment = t.asInput();
1119
1120                         // magically switch encoding default if it looks like XeLaTeX
1121                         static string const magicXeLaTeX =
1122                                 "% This document must be compiled with XeLaTeX ";
1123                         if (comment.size() > magicXeLaTeX.size()
1124                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1125                                   && h_inputencoding == "auto") {
1126                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
1127                                 h_inputencoding = "utf8";
1128                         }
1129                         smatch sub;
1130                         if (regex_search(comment, sub, islyxfile)) {
1131                                 is_lyx_file = true;
1132                                 in_lyx_preamble = true;
1133                         } else if (is_lyx_file
1134                                    && regex_search(comment, sub, usercommands))
1135                                 in_lyx_preamble = false;
1136                         else if (!in_lyx_preamble)
1137                                 h_preamble << t.asInput();
1138                 }
1139
1140                 else if (t.cs() == "pagestyle")
1141                         h_paperpagestyle = p.verbatim_item();
1142
1143                 else if (t.cs() == "setdefaultlanguage") {
1144                         xetex = true;
1145                         // We don't yet care about non-language variant options
1146                         // because LyX doesn't support this yet, see bug #8214
1147                         if (p.hasOpt()) {
1148                                 string langopts = p.getOpt();
1149                                 // check if the option contains a variant, if yes, extract it
1150                                 string::size_type pos_var = langopts.find("variant");
1151                                 string::size_type i = langopts.find(',', pos_var);
1152                                 if (pos_var != string::npos){
1153                                         string variant;
1154                                         if (i == string::npos)
1155                                                 variant = langopts.substr(pos_var + 8, langopts.length() - pos_var - 9);
1156                                         else
1157                                                 variant = langopts.substr(pos_var + 8, i - pos_var - 8);
1158                                         h_language = variant;
1159                                 }
1160                                 p.verbatim_item();
1161                         } else
1162                                 h_language = p.verbatim_item();
1163                         //finally translate the poyglossia name to a LyX name
1164                         h_language = polyglossia2lyx(h_language);
1165                 }
1166
1167                 else if (t.cs() == "setotherlanguage") {
1168                         // We don't yet care about the option because LyX doesn't
1169                         // support this yet, see bug #8214
1170                         p.hasOpt() ? p.getOpt() : string();
1171                         p.verbatim_item();
1172                 }
1173
1174                 else if (t.cs() == "setmainfont") {
1175                         // we don't care about the option
1176                         p.hasOpt() ? p.getOpt() : string();
1177                         h_font_roman = p.getArg('{', '}');
1178                 }
1179
1180                 else if (t.cs() == "setsansfont") {
1181                         // we don't care about the option
1182                         p.hasOpt() ? p.getOpt() : string();
1183                         h_font_sans = p.getArg('{', '}');
1184                 }
1185
1186                 else if (t.cs() == "setmonofont") {
1187                         // we don't care about the option
1188                         p.hasOpt() ? p.getOpt() : string();
1189                         h_font_typewriter = p.getArg('{', '}');
1190                 }
1191
1192                 else if (t.cs() == "date") {
1193                         string argument = p.getArg('{', '}');
1194                         if (argument.empty())
1195                                 h_suppress_date = "true";
1196                         else
1197                                 h_preamble << t.asInput() << '{' << argument << '}';
1198                 }
1199
1200                 else if (t.cs() == "color") {
1201                         string const space =
1202                                 (p.hasOpt() ? p.getOpt() : string());
1203                         string argument = p.getArg('{', '}');
1204                         // check the case that a standard color is used
1205                         if (space.empty() && is_known(argument, known_basic_colors)) {
1206                                 h_fontcolor = rgbcolor2code(argument);
1207                                 preamble.registerAutomaticallyLoadedPackage("color");
1208                         } else if (space.empty() && argument == "document_fontcolor")
1209                                 preamble.registerAutomaticallyLoadedPackage("color");
1210                         // check the case that LyX's document_fontcolor is defined
1211                         // but not used for \color
1212                         else {
1213                                 h_preamble << t.asInput();
1214                                 if (!space.empty())
1215                                         h_preamble << space;
1216                                 h_preamble << '{' << argument << '}';
1217                                 // the color might already be set because \definecolor
1218                                 // is parsed before this
1219                                 h_fontcolor = "";
1220                         }
1221                 }
1222
1223                 else if (t.cs() == "pagecolor") {
1224                         string argument = p.getArg('{', '}');
1225                         // check the case that a standard color is used
1226                         if (is_known(argument, known_basic_colors)) {
1227                                 h_backgroundcolor = rgbcolor2code(argument);
1228                         } else if (argument == "page_backgroundcolor")
1229                                 preamble.registerAutomaticallyLoadedPackage("color");
1230                         // check the case that LyX's page_backgroundcolor is defined
1231                         // but not used for \pagecolor
1232                         else {
1233                                 h_preamble << t.asInput() << '{' << argument << '}';
1234                                 // the color might already be set because \definecolor
1235                                 // is parsed before this
1236                                 h_backgroundcolor = "";
1237                         }
1238                 }
1239
1240                 else if (t.cs() == "makeatletter") {
1241                         // LyX takes care of this
1242                         p.setCatCode('@', catLetter);
1243                 }
1244
1245                 else if (t.cs() == "makeatother") {
1246                         // LyX takes care of this
1247                         p.setCatCode('@', catOther);
1248                 }
1249
1250                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1251                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1252                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
1253                                 || t.cs() == "DeclareRobustCommand"
1254                       || t.cs() == "DeclareRobustCommandx"
1255                                 || t.cs() == "ProvideTextCommandDefault"
1256                                 || t.cs() == "DeclareMathAccent") {
1257                         bool star = false;
1258                         if (p.next_token().character() == '*') {
1259                                 p.get_token();
1260                                 star = true;
1261                         }
1262                         string const name = p.verbatim_item();
1263                         string const opt1 = p.getFullOpt();
1264                         string const opt2 = p.getFullOpt();
1265                         string const body = p.verbatim_item();
1266                         // font settings
1267                         if (name == "\\rmdefault")
1268                                 if (is_known(body, known_roman_fonts))
1269                                         h_font_roman = body;
1270                         if (name == "\\sfdefault")
1271                                 if (is_known(body, known_sans_fonts))
1272                                         h_font_sans = body;
1273                         if (name == "\\ttdefault")
1274                                 if (is_known(body, known_typewriter_fonts))
1275                                         h_font_typewriter = body;
1276                         if (name == "\\familydefault") {
1277                                 string family = body;
1278                                 // remove leading "\"
1279                                 h_font_default_family = family.erase(0,1);
1280                         }
1281
1282                         // remove the lyxdot definition that is re-added by LyX
1283                         // if necessary
1284                         if (name == "\\lyxdot")
1285                                 in_lyx_preamble = true;
1286
1287                         // Add the command to the known commands
1288                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1289
1290                         // only non-lyxspecific stuff
1291                         if (!in_lyx_preamble) {
1292                                 ostringstream ss;
1293                                 ss << '\\' << t.cs();
1294                                 if (star)
1295                                         ss << '*';
1296                                 ss << '{' << name << '}' << opt1 << opt2
1297                                    << '{' << body << "}";
1298                                 h_preamble << ss.str();
1299 /*
1300                                 ostream & out = in_preamble ? h_preamble : os;
1301                                 out << "\\" << t.cs() << "{" << name << "}"
1302                                     << opts << "{" << body << "}";
1303 */
1304                         }
1305                 }
1306
1307                 else if (t.cs() == "documentclass") {
1308                         vector<string>::iterator it;
1309                         vector<string> opts = split_options(p.getArg('[', ']'));
1310                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1311                         delete_opt(opts, known_fontsizes);
1312                         // delete "pt" at the end
1313                         string::size_type i = h_paperfontsize.find("pt");
1314                         if (i != string::npos)
1315                                 h_paperfontsize.erase(i);
1316                         // The documentclass options are always parsed before the options
1317                         // of the babel call so that a language cannot overwrite the babel
1318                         // options.
1319                         handle_opt(opts, known_languages, h_language);
1320                         delete_opt(opts, known_languages);
1321
1322                         // paper orientation
1323                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1324                                 h_paperorientation = "landscape";
1325                                 opts.erase(it);
1326                         }
1327                         // paper sides
1328                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1329                                  != opts.end()) {
1330                                 h_papersides = "1";
1331                                 opts.erase(it);
1332                         }
1333                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1334                                  != opts.end()) {
1335                                 h_papersides = "2";
1336                                 opts.erase(it);
1337                         }
1338                         // paper columns
1339                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1340                                  != opts.end()) {
1341                                 h_papercolumns = "1";
1342                                 opts.erase(it);
1343                         }
1344                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1345                                  != opts.end()) {
1346                                 h_papercolumns = "2";
1347                                 opts.erase(it);
1348                         }
1349                         // paper sizes
1350                         // some size options are known to any document classes, other sizes
1351                         // are handled by the \geometry command of the geometry package
1352                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1353                         delete_opt(opts, known_class_paper_sizes);
1354                         // the remaining options
1355                         h_options = join(opts, ",");
1356                         // FIXME This does not work for classes that have a
1357                         //       different name in LyX than in LaTeX
1358                         h_textclass = p.getArg('{', '}');
1359                 }
1360
1361                 else if (t.cs() == "usepackage") {
1362                         string const options = p.getArg('[', ']');
1363                         string const name = p.getArg('{', '}');
1364                         vector<string> vecnames;
1365                         split(name, vecnames, ',');
1366                         vector<string>::const_iterator it  = vecnames.begin();
1367                         vector<string>::const_iterator end = vecnames.end();
1368                         for (; it != end; ++it)
1369                                 handle_package(p, trimSpaceAndEol(*it), options,
1370                                                in_lyx_preamble);
1371                 }
1372
1373                 else if (t.cs() == "inputencoding") {
1374                         string const encoding = p.getArg('{','}');
1375                         h_inputencoding = encoding;
1376                         p.setEncoding(encoding);
1377                 }
1378
1379                 else if (t.cs() == "newenvironment") {
1380                         string const name = p.getArg('{', '}');
1381                         string const opt1 = p.getFullOpt();
1382                         string const opt2 = p.getFullOpt();
1383                         string const beg = p.verbatim_item();
1384                         string const end = p.verbatim_item();
1385                         if (!in_lyx_preamble) {
1386                                 h_preamble << "\\newenvironment{" << name
1387                                            << '}' << opt1 << opt2 << '{'
1388                                            << beg << "}{" << end << '}';
1389                         }
1390                         add_known_environment(name, opt1, !opt2.empty(),
1391                                               from_utf8(beg), from_utf8(end));
1392
1393                 }
1394
1395                 else if (t.cs() == "def") {
1396                         string name = p.get_token().cs();
1397                         // In fact, name may be more than the name:
1398                         // In the test case of bug 8116
1399                         // name == "csname SF@gobble@opt \endcsname".
1400                         // Therefore, we need to use asInput() instead of cs().
1401                         while (p.next_token().cat() != catBegin)
1402                                 name += p.get_token().asInput();
1403                         if (!in_lyx_preamble)
1404                                 h_preamble << "\\def\\" << name << '{'
1405                                            << p.verbatim_item() << "}";
1406                 }
1407
1408                 else if (t.cs() == "newcolumntype") {
1409                         string const name = p.getArg('{', '}');
1410                         trimSpaceAndEol(name);
1411                         int nargs = 0;
1412                         string opts = p.getOpt();
1413                         if (!opts.empty()) {
1414                                 istringstream is(string(opts, 1));
1415                                 is >> nargs;
1416                         }
1417                         special_columns[name[0]] = nargs;
1418                         h_preamble << "\\newcolumntype{" << name << "}";
1419                         if (nargs)
1420                                 h_preamble << "[" << nargs << "]";
1421                         h_preamble << "{" << p.verbatim_item() << "}";
1422                 }
1423
1424                 else if (t.cs() == "setcounter") {
1425                         string const name = p.getArg('{', '}');
1426                         string const content = p.getArg('{', '}');
1427                         if (name == "secnumdepth")
1428                                 h_secnumdepth = content;
1429                         else if (name == "tocdepth")
1430                                 h_tocdepth = content;
1431                         else
1432                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1433                 }
1434
1435                 else if (t.cs() == "setlength") {
1436                         string const name = p.verbatim_item();
1437                         string const content = p.verbatim_item();
1438                         // the paragraphs are only not indented when \parindent is set to zero
1439                         if (name == "\\parindent" && content != "") {
1440                                 if (content[0] == '0')
1441                                         h_paragraph_separation = "skip";
1442                                 else
1443                                         h_paragraph_indentation = translate_len(content);
1444                         } else if (name == "\\parskip") {
1445                                 if (content == "\\smallskipamount")
1446                                         h_defskip = "smallskip";
1447                                 else if (content == "\\medskipamount")
1448                                         h_defskip = "medskip";
1449                                 else if (content == "\\bigskipamount")
1450                                         h_defskip = "bigskip";
1451                                 else
1452                                         h_defskip = content;
1453                         } else
1454                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1455                 }
1456
1457                 else if (t.cs() == "onehalfspacing")
1458                         h_spacing = "onehalf";
1459
1460                 else if (t.cs() == "doublespacing")
1461                         h_spacing = "double";
1462
1463                 else if (t.cs() == "setstretch")
1464                         h_spacing = "other " + p.verbatim_item();
1465
1466                 else if (t.cs() == "synctex") {
1467                         // the scheme is \synctex=value
1468                         // where value can only be "1" or "-1"
1469                         h_output_sync = "1";
1470                         // there can be any character behind the value (e.g. a linebreak or a '\'
1471                         // therefore we extract it char by char
1472                         p.get_token();
1473                         string value = p.get_token().asInput();
1474                         if (value == "-")
1475                                 value += p.get_token().asInput();
1476                         h_output_sync_macro = "\\synctex=" + value;
1477                 }
1478
1479                 else if (t.cs() == "begin") {
1480                         string const name = p.getArg('{', '}');
1481                         if (name == "document")
1482                                 break;
1483                         h_preamble << "\\begin{" << name << "}";
1484                 }
1485
1486                 else if (t.cs() == "geometry") {
1487                         vector<string> opts = split_options(p.getArg('{', '}'));
1488                         handle_geometry(opts);
1489                 }
1490
1491                 else if (t.cs() == "definecolor") {
1492                         string const color = p.getArg('{', '}');
1493                         string const space = p.getArg('{', '}');
1494                         string const value = p.getArg('{', '}');
1495                         if (color == "document_fontcolor" && space == "rgb") {
1496                                 RGBColor c(RGBColorFromLaTeX(value));
1497                                 h_fontcolor = X11hexname(c);
1498                         } else if (color == "note_fontcolor" && space == "rgb") {
1499                                 RGBColor c(RGBColorFromLaTeX(value));
1500                                 h_notefontcolor = X11hexname(c);
1501                         } else if (color == "page_backgroundcolor" && space == "rgb") {
1502                                 RGBColor c(RGBColorFromLaTeX(value));
1503                                 h_backgroundcolor = X11hexname(c);
1504                         } else if (color == "shadecolor" && space == "rgb") {
1505                                 RGBColor c(RGBColorFromLaTeX(value));
1506                                 h_boxbgcolor = X11hexname(c);
1507                         } else {
1508                                 h_preamble << "\\definecolor{" << color
1509                                            << "}{" << space << "}{" << value
1510                                            << '}';
1511                         }
1512                 }
1513
1514                 else if (t.cs() == "bibliographystyle")
1515                         h_biblio_style = p.verbatim_item();
1516
1517                 else if (t.cs() == "jurabibsetup") {
1518                         // FIXME p.getArg('{', '}') is most probably wrong (it
1519                         //       does not handle nested braces).
1520                         //       Use p.verbatim_item() instead.
1521                         vector<string> jurabibsetup =
1522                                 split_options(p.getArg('{', '}'));
1523                         // add jurabibsetup to the jurabib package options
1524                         add_package("jurabib", jurabibsetup);
1525                         if (!jurabibsetup.empty()) {
1526                                 h_preamble << "\\jurabibsetup{"
1527                                            << join(jurabibsetup, ",") << '}';
1528                         }
1529                 }
1530
1531                 else if (t.cs() == "hypersetup") {
1532                         vector<string> hypersetup =
1533                                 split_options(p.verbatim_item());
1534                         // add hypersetup to the hyperref package options
1535                         handle_hyperref(hypersetup);
1536                         if (!hypersetup.empty()) {
1537                                 h_preamble << "\\hypersetup{"
1538                                            << join(hypersetup, ",") << '}';
1539                         }
1540                 }
1541
1542                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1543                         // prevent misparsing of \usepackage if it is used
1544                         // as an argument (see e.g. our own output of
1545                         // \@ifundefined above)
1546                         string const arg1 = p.verbatim_item();
1547                         string const arg2 = p.verbatim_item();
1548                         string const arg3 = p.verbatim_item();
1549                         // test case \@ifundefined{date}{}{\date{}}
1550                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
1551                             arg2.empty() && arg3 == "\\date{}") {
1552                                 h_suppress_date = "true";
1553                         // older tex2lyx versions did output
1554                         // \@ifundefined{definecolor}{\usepackage{color}}{}
1555                         } else if (t.cs() == "@ifundefined" &&
1556                                    arg1 == "definecolor" &&
1557                                    arg2 == "\\usepackage{color}" &&
1558                                    arg3.empty()) {
1559                                 if (!in_lyx_preamble)
1560                                         h_preamble << package_beg_sep
1561                                                    << "color"
1562                                                    << package_mid_sep
1563                                                    << "\\@ifundefined{definecolor}{color}{}"
1564                                                    << package_end_sep;
1565                         // test for case
1566                         //\@ifundefined{showcaptionsetup}{}{%
1567                         // \PassOptionsToPackage{caption=false}{subfig}}
1568                         // that LyX uses for subfloats
1569                         } else if (t.cs() == "@ifundefined" &&
1570                                    arg1 == "showcaptionsetup" && arg2.empty()
1571                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1572                                 ; // do nothing
1573                         } else if (!in_lyx_preamble) {
1574                                 h_preamble << t.asInput()
1575                                            << '{' << arg1 << '}'
1576                                            << '{' << arg2 << '}'
1577                                            << '{' << arg3 << '}';
1578                         }
1579                 }
1580
1581                 else if (is_known(t.cs(), known_if_commands)) {
1582                         // must not parse anything in conditional code, since
1583                         // LyX would output the parsed contents unconditionally
1584                         if (!in_lyx_preamble)
1585                                 h_preamble << t.asInput();
1586                         handle_if(p, in_lyx_preamble);
1587                 }
1588
1589                 else if (!t.cs().empty() && !in_lyx_preamble)
1590                         h_preamble << '\\' << t.cs();
1591         }
1592
1593         // remove the whitespace
1594         p.skip_spaces();
1595
1596         // Force textclass if the user wanted it
1597         if (!forceclass.empty())
1598                 h_textclass = forceclass;
1599         if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1600                 h_textclass.insert(0, "literate-");
1601         tc.setName(h_textclass);
1602         if (!tc.load()) {
1603                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1604                 exit(EXIT_FAILURE);
1605         }
1606         if (h_papersides.empty()) {
1607                 ostringstream ss;
1608                 ss << tc.sides();
1609                 h_papersides = ss.str();
1610         }
1611 }
1612
1613
1614 string babel2lyx(string const & language)
1615 {
1616         char const * const * where = is_known(language, known_languages);
1617         if (where)
1618                 return known_coded_languages[where - known_languages];
1619         return language;
1620 }
1621
1622
1623 string polyglossia2lyx(string const & language)
1624 {
1625         char const * const * where = is_known(language, polyglossia_languages);
1626         if (where)
1627                 return coded_polyglossia_languages[where - polyglossia_languages];
1628         return language;
1629 }
1630
1631
1632 string rgbcolor2code(string const & name)
1633 {
1634         char const * const * where = is_known(name, known_basic_colors);
1635         if (where) {
1636                 // "red", "green" etc
1637                 return known_basic_color_codes[where - known_basic_colors];
1638         }
1639         // "255,0,0", "0,255,0" etc
1640         RGBColor c(RGBColorFromLaTeX(name));
1641         return X11hexname(c);
1642 }
1643
1644 // }])
1645
1646
1647 } // namespace lyx