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