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