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