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