]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
72800eca9579739082531a6ab3870cbfd5f06a40
[lyx.git] / src / tex2lyx / Preamble.cpp
1 /**
2  * \file Preamble.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  * \author Uwe Stöhr
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 // {[(
13
14 #include <config.h>
15
16 #include "Preamble.h"
17 #include "tex2lyx.h"
18
19 #include "Encoding.h"
20 #include "LayoutFile.h"
21 #include "Layout.h"
22 #include "Lexer.h"
23 #include "TextClass.h"
24
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["esint"]      = "1";
529         h_use_packages["mhchem"]     = "0";
530         h_use_packages["mathdots"]   = "0";
531         h_use_packages["mathtools"]  = "0";
532         h_use_packages["stackrel"]   = "0";
533         h_use_packages["stmaryrd"]   = "0";
534         h_use_packages["undertilde"] = "0";
535 }
536
537
538 void Preamble::handle_hyperref(vector<string> & options)
539 {
540         // FIXME swallow inputencoding changes that might surround the
541         //       hyperref setup if it was written by LyX
542         h_use_hyperref = "true";
543         // swallow "unicode=true", since LyX does always write that
544         vector<string>::iterator it =
545                 find(options.begin(), options.end(), "unicode=true");
546         if (it != options.end())
547                 options.erase(it);
548         it = find(options.begin(), options.end(), "pdfusetitle");
549         if (it != options.end()) {
550                 h_pdf_pdfusetitle = "1";
551                 options.erase(it);
552         }
553         string bookmarks = process_keyval_opt(options, "bookmarks");
554         if (bookmarks == "true")
555                 h_pdf_bookmarks = "1";
556         else if (bookmarks == "false")
557                 h_pdf_bookmarks = "0";
558         if (h_pdf_bookmarks == "1") {
559                 string bookmarksnumbered =
560                         process_keyval_opt(options, "bookmarksnumbered");
561                 if (bookmarksnumbered == "true")
562                         h_pdf_bookmarksnumbered = "1";
563                 else if (bookmarksnumbered == "false")
564                         h_pdf_bookmarksnumbered = "0";
565                 string bookmarksopen =
566                         process_keyval_opt(options, "bookmarksopen");
567                 if (bookmarksopen == "true")
568                         h_pdf_bookmarksopen = "1";
569                 else if (bookmarksopen == "false")
570                         h_pdf_bookmarksopen = "0";
571                 if (h_pdf_bookmarksopen == "1") {
572                         string bookmarksopenlevel =
573                                 process_keyval_opt(options, "bookmarksopenlevel");
574                         if (!bookmarksopenlevel.empty())
575                                 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
576                 }
577         }
578         string breaklinks = process_keyval_opt(options, "breaklinks");
579         if (breaklinks == "true")
580                 h_pdf_breaklinks = "1";
581         else if (breaklinks == "false")
582                 h_pdf_breaklinks = "0";
583         string pdfborder = process_keyval_opt(options, "pdfborder");
584         if (pdfborder == "{0 0 0}")
585                 h_pdf_pdfborder = "1";
586         else if (pdfborder == "{0 0 1}")
587                 h_pdf_pdfborder = "0";
588         string backref = process_keyval_opt(options, "backref");
589         if (!backref.empty())
590                 h_pdf_backref = backref;
591         string colorlinks = process_keyval_opt(options, "colorlinks");
592         if (colorlinks == "true")
593                 h_pdf_colorlinks = "1";
594         else if (colorlinks == "false")
595                 h_pdf_colorlinks = "0";
596         string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
597         if (!pdfpagemode.empty())
598                 h_pdf_pagemode = pdfpagemode;
599         string pdftitle = process_keyval_opt(options, "pdftitle");
600         if (!pdftitle.empty()) {
601                 h_pdf_title = remove_braces(pdftitle);
602         }
603         string pdfauthor = process_keyval_opt(options, "pdfauthor");
604         if (!pdfauthor.empty()) {
605                 h_pdf_author = remove_braces(pdfauthor);
606         }
607         string pdfsubject = process_keyval_opt(options, "pdfsubject");
608         if (!pdfsubject.empty())
609                 h_pdf_subject = remove_braces(pdfsubject);
610         string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
611         if (!pdfkeywords.empty())
612                 h_pdf_keywords = remove_braces(pdfkeywords);
613         if (!options.empty()) {
614                 if (!h_pdf_quoted_options.empty())
615                         h_pdf_quoted_options += ',';
616                 h_pdf_quoted_options += join(options, ",");
617                 options.clear();
618         }
619 }
620
621
622 void Preamble::handle_geometry(vector<string> & options)
623 {
624         h_use_geometry = "true";
625         vector<string>::iterator it;
626         // paper orientation
627         if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
628                 h_paperorientation = "landscape";
629                 options.erase(it);
630         }
631         // paper size
632         // keyval version: "paper=letter"
633         string paper = process_keyval_opt(options, "paper");
634         if (!paper.empty())
635                 h_papersize = paper + "paper";
636         // alternative version: "letterpaper"
637         handle_opt(options, known_paper_sizes, h_papersize);
638         delete_opt(options, known_paper_sizes);
639         // page margins
640         char const * const * margin = known_paper_margins;
641         for (; *margin; ++margin) {
642                 string value = process_keyval_opt(options, *margin);
643                 if (!value.empty()) {
644                         int k = margin - known_paper_margins;
645                         string name = known_coded_paper_margins[k];
646                         h_margins += '\\' + name + ' ' + value + '\n';
647                 }
648         }
649 }
650
651
652 void Preamble::handle_package(Parser &p, string const & name,
653                               string const & opts, bool in_lyx_preamble)
654 {
655         vector<string> options = split_options(opts);
656         add_package(name, options);
657         char const * const * where = 0;
658
659         if (is_known(name, known_xetex_packages)) {
660                 xetex = true;
661                 h_use_non_tex_fonts = "true";
662                 registerAutomaticallyLoadedPackage("fontspec");
663                 if (h_inputencoding == "auto")
664                         p.setEncoding("UTF-8");
665         }
666
667         // roman fonts
668         if (is_known(name, known_roman_fonts))
669                 h_font_roman = name;
670
671         if (name == "fourier") {
672                 h_font_roman = "utopia";
673                 // when font uses real small capitals
674                 if (opts == "expert")
675                         h_font_sc = "true";
676         }
677
678         if (name == "garamondx") {
679                 h_font_roman = "garamondx";
680                 if (opts == "osfI")
681                         h_font_osf = "true";
682         }
683
684         if (name == "libertine") {
685                 h_font_roman = "libertine";
686                 // this automatically invokes biolinum
687                 h_font_sans = "biolinum";
688                 if (opts == "osf")
689                         h_font_osf = "true";
690                 else if (opts == "lining")
691                         h_font_osf = "false";
692         }
693
694         if (name == "libertine-type1") {
695                 h_font_roman = "libertine";
696                 // NOTE: contrary to libertine.sty, libertine-type1
697                 // does not automatically invoke biolinum
698                 if (opts == "lining")
699                         h_font_osf = "false";
700                 else if (opts == "osf")
701                         h_font_osf = "true";
702         }
703         
704         if (name == "mathdesign") {
705                 if (opts.find("charter") != string::npos)
706                         h_font_roman = "md-charter";
707                 if (opts.find("garamond") != string::npos)
708                         h_font_roman = "md-garamond";
709                 if (opts.find("utopia") != string::npos)
710                         h_font_roman = "md-utopia";
711                 if (opts.find("expert") != string::npos) {
712                         h_font_sc = "true";
713                         h_font_osf = "true";
714                 }
715         }
716
717         else if (name == "mathpazo")
718                 h_font_roman = "palatino";
719
720         else if (name == "mathptmx")
721                 h_font_roman = "times";
722
723         // sansserif fonts
724         if (is_known(name, known_sans_fonts)) {
725                 h_font_sans = name;
726                 if (options.size() == 1) {
727                         if (scale_as_percentage(opts, h_font_sf_scale))
728                                 options.clear();
729                 }
730         }
731
732         if (name == "biolinum-type1")
733                 h_font_sans = "biolinum";
734
735         // typewriter fonts
736         if (is_known(name, known_typewriter_fonts)) {
737                 // fourier can be set as roman font _only_
738                 // fourier as typewriter is handled in handling of \ttdefault
739                 if (name != "fourier") {
740                         h_font_typewriter = name;
741                         if (options.size() == 1) {
742                                 if (scale_as_percentage(opts, h_font_tt_scale))
743                                         options.clear();
744                         }
745                 }
746         }
747
748         // font uses old-style figure
749         if (name == "eco")
750                 h_font_osf = "true";
751
752         // math fonts
753         if (is_known(name, known_math_fonts))
754                 h_font_math = name;
755
756         if (name == "newtxmath") {
757                 if (opts.empty())
758                         h_font_math = "newtxmath";
759                 else if (opts == "garamondx")
760                         h_font_math = "garamondx-ntxm";
761                 else if (opts == "libertine")
762                         h_font_math = "libertine-ntxm";
763                 else if (opts == "minion")
764                         h_font_math = "minion-ntxm";
765         }
766
767         // after the detection and handling of special cases, we can remove the
768         // fonts, otherwise they would appear in the preamble, see bug #7856
769         if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
770                 ||      is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
771                 ;
772
773         else if (name == "amsmath" || name == "amssymb" ||
774                  name == "esint" || name == "mhchem" || name == "mathdots" ||
775                  name == "mathtools" || name == "stackrel" ||
776                  name == "stmaryrd" || name == "undertilde")
777                 h_use_packages[name] = "2";
778
779         else if (name == "babel") {
780                 h_language_package = "default";
781                 // One might think we would have to do nothing if babel is loaded
782                 // without any options to prevent pollution of the preamble with this
783                 // babel call in every roundtrip.
784                 // But the user could have defined babel-specific things afterwards. So
785                 // we need to keep it in the preamble to prevent cases like bug #7861.
786                 if (!opts.empty()) {
787                         // check if more than one option was used - used later for inputenc
788                         if (options.begin() != options.end() - 1)
789                                 one_language = false;
790                         // babel takes the last language of the option of its \usepackage
791                         // call as document language. If there is no such language option, the
792                         // last language in the documentclass options is used.
793                         handle_opt(options, known_languages, h_language);
794                         // translate the babel name to a LyX name
795                         h_language = babel2lyx(h_language);
796                         if (h_language == "japanese") {
797                                 // For Japanese, the encoding isn't indicated in the source
798                                 // file, and there's really not much we can do. We could
799                                 // 1) offer a list of possible encodings to choose from, or
800                                 // 2) determine the encoding of the file by inspecting it.
801                                 // For the time being, we leave the encoding alone so that
802                                 // we don't get iconv errors when making a wrong guess, and
803                                 // we will output a note at the top of the document
804                                 // explaining what to do.
805                                 Encoding const * const enc = encodings.fromIconvName(
806                                         p.getEncoding(), Encoding::japanese, false);
807                                 if (enc)
808                                         h_inputencoding = enc->name();
809                                 is_nonCJKJapanese = true;
810                                 // in this case babel can be removed from the preamble
811                                 registerAutomaticallyLoadedPackage("babel");
812                         } else {
813                                 // If babel is called with options, LyX puts them by default into the
814                                 // document class options. This works for most languages, except
815                                 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
816                                 // perhaps in future others.
817                                 // Therefore keep the babel call as it is as the user might have
818                                 // reasons for it.
819                                 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
820                         }
821                         delete_opt(options, known_languages);
822                 } else {
823                         h_preamble << "\\usepackage{babel}\n";
824                         explicit_babel = true;
825                 }
826         }
827
828         else if (name == "polyglossia") {
829                 h_language_package = "default";
830                 h_default_output_format = "pdf4";
831                 h_use_non_tex_fonts = "true";
832                 xetex = true;
833                 registerAutomaticallyLoadedPackage("xunicode");
834                 if (h_inputencoding == "auto")
835                         p.setEncoding("UTF-8");
836         }
837
838         else if (name == "CJK") {
839                 // set the encoding to "auto" because it might be set to "default" by the babel handling
840                 // and this would not be correct for CJK
841                 if (h_inputencoding == "default")
842                         h_inputencoding = "auto";
843                 registerAutomaticallyLoadedPackage("CJK");
844         }
845
846         else if (name == "CJKutf8") {
847                 h_inputencoding = "utf8-cjk";
848                 p.setEncoding("UTF-8");
849                 registerAutomaticallyLoadedPackage("CJKutf8");
850         }
851
852         else if (name == "fontenc") {
853                 h_fontencoding = getStringFromVector(options, ",");
854                 /* We could do the following for better round trip support,
855                  * but this makes the document less portable, so I skip it:
856                 if (h_fontencoding == lyxrc.fontenc)
857                         h_fontencoding = "global";
858                  */
859                 options.clear();
860         }
861
862         else if (name == "inputenc" || name == "luainputenc") {
863                 // h_inputencoding is only set when there is not more than one
864                 // inputenc option because otherwise h_inputencoding must be
865                 // set to "auto" (the default encoding of the document language)
866                 // Therefore check that exactly one option is passed to inputenc.
867                 // It is also only set when there is not more than one babel
868                 // language option.
869                 if (!options.empty()) {
870                         string const encoding = options.back();
871                         Encoding const * const enc = encodings.fromLaTeXName(
872                                 encoding, Encoding::inputenc, true);
873                         if (!enc)
874                                 cerr << "Unknown encoding " << encoding << ". Ignoring." << std::endl;
875                         else {
876                                 if (!enc->unsafe() && options.size() == 1 && one_language == true)
877                                         h_inputencoding = enc->name();
878                                 p.setEncoding(enc->iconvName());
879                         }
880                         options.clear();
881                 }
882         }
883
884         else if (name == "srcltx") {
885                 h_output_sync = "1";
886                 if (!opts.empty()) {
887                         h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
888                         options.clear();
889                 } else
890                         h_output_sync_macro = "\\usepackage{srcltx}";
891         }
892
893         else if (is_known(name, known_old_language_packages)) {
894                 // known language packages from the times before babel
895                 // if they are found and not also babel, they will be used as
896                 // custom language package
897                 h_language_package = "\\usepackage{" + name + "}";
898         }
899
900         else if (name == "lyxskak") {
901                 // ignore this and its options
902                 const char * const o[] = {"ps", "mover", 0};
903                 delete_opt(options, o);
904         }
905
906         else if (is_known(name, known_lyx_packages) && options.empty()) {
907                 if (name == "splitidx")
908                         h_use_indices = "true";
909                 if (name == "refstyle")
910                         h_use_refstyle = true;
911                 else if (name == "prettyref")
912                         h_use_refstyle = false;
913                 if (!in_lyx_preamble) {
914                         h_preamble << package_beg_sep << name
915                                    << package_mid_sep << "\\usepackage{"
916                                    << name << '}';
917                         if (p.next_token().cat() == catNewline ||
918                             (p.next_token().cat() == catSpace &&
919                              p.next_next_token().cat() == catNewline))
920                                 h_preamble << '\n';
921                         h_preamble << package_end_sep;
922                 }
923         }
924
925         else if (name == "geometry")
926                 handle_geometry(options);
927
928         else if (name == "subfig")
929                 ; // ignore this FIXME: Use the package separator mechanism instead
930
931         else if ((where = is_known(name, known_languages)))
932                 h_language = known_coded_languages[where - known_languages];
933
934         else if (name == "natbib") {
935                 h_biblio_style = "plainnat";
936                 h_cite_engine = "natbib";
937                 h_cite_engine_type = "authoryear";
938                 vector<string>::iterator it =
939                         find(options.begin(), options.end(), "authoryear");
940                 if (it != options.end())
941                         options.erase(it);
942                 else {
943                         it = find(options.begin(), options.end(), "numbers");
944                         if (it != options.end()) {
945                                 h_cite_engine_type = "numerical";
946                                 options.erase(it);
947                         }
948                 }
949         }
950
951         else if (name == "jurabib") {
952                 h_biblio_style = "jurabib";
953                 h_cite_engine = "jurabib";
954                 h_cite_engine_type = "authoryear";
955         }
956
957         else if (name == "hyperref")
958                 handle_hyperref(options);
959
960         else if (!in_lyx_preamble) {
961                 if (options.empty())
962                         h_preamble << "\\usepackage{" << name << '}';
963                 else {
964                         h_preamble << "\\usepackage[" << opts << "]{"
965                                    << name << '}';
966                         options.clear();
967                 }
968                 if (p.next_token().cat() == catNewline ||
969                     (p.next_token().cat() == catSpace &&
970                      p.next_next_token().cat() == catNewline))
971                         h_preamble << '\n';
972         }
973
974         // We need to do something with the options...
975         if (!options.empty())
976                 cerr << "Ignoring options '" << join(options, ",")
977                      << "' of package " << name << '.' << endl;
978
979         // remove the whitespace
980         p.skip_spaces();
981 }
982
983
984 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
985 {
986         while (p.good()) {
987                 Token t = p.get_token();
988                 if (t.cat() == catEscape &&
989                     is_known(t.cs(), known_if_commands))
990                         handle_if(p, in_lyx_preamble);
991                 else {
992                         if (!in_lyx_preamble)
993                                 h_preamble << t.asInput();
994                         if (t.cat() == catEscape && t.cs() == "fi")
995                                 return;
996                 }
997         }
998 }
999
1000
1001 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
1002 {
1003         // set the quote language
1004         // LyX only knows the following quotes languages:
1005         // english, swedish, german, polish, french and danish
1006         // (quotes for "japanese" and "chinese-traditional" are missing because
1007         //  they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
1008         // conversion list taken from
1009         // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1010         // (quotes for kazakh and interlingua are unknown)
1011         // danish
1012         if (is_known(h_language, known_danish_quotes_languages))
1013                 h_quotes_language = "danish";
1014         // french
1015         else if (is_known(h_language, known_french_quotes_languages))
1016                 h_quotes_language = "french";
1017         // german
1018         else if (is_known(h_language, known_german_quotes_languages))
1019                 h_quotes_language = "german";
1020         // polish
1021         else if (is_known(h_language, known_polish_quotes_languages))
1022                 h_quotes_language = "polish";
1023         // swedish
1024         else if (is_known(h_language, known_swedish_quotes_languages))
1025                 h_quotes_language = "swedish";
1026         //english
1027         else if (is_known(h_language, known_english_quotes_languages))
1028                 h_quotes_language = "english";
1029
1030         if (contains(h_float_placement, "H"))
1031                 registerAutomaticallyLoadedPackage("float");
1032         if (h_spacing != "single" && h_spacing != "default")
1033                 registerAutomaticallyLoadedPackage("setspace");
1034         if (h_use_packages["amsmath"] == "2") {
1035                 // amsbsy and amstext are already provided by amsmath
1036                 registerAutomaticallyLoadedPackage("amsbsy");
1037                 registerAutomaticallyLoadedPackage("amstext");
1038         }
1039
1040         // output the LyX file settings
1041         os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
1042            << "\\lyxformat " << LYX_FORMAT << '\n'
1043            << "\\begin_document\n"
1044            << "\\begin_header\n"
1045            << "\\textclass " << h_textclass << "\n";
1046         string const raw = subdoc ? empty_string() : h_preamble.str();
1047         if (!raw.empty()) {
1048                 os << "\\begin_preamble\n";
1049                 for (string::size_type i = 0; i < raw.size(); ++i) {
1050                         if (raw[i] == package_beg_sep) {
1051                                 // Here follows some package loading code that
1052                                 // must be skipped if the package is loaded
1053                                 // automatically.
1054                                 string::size_type j = raw.find(package_mid_sep, i);
1055                                 if (j == string::npos)
1056                                         return false;
1057                                 string::size_type k = raw.find(package_end_sep, j);
1058                                 if (k == string::npos)
1059                                         return false;
1060                                 string const package = raw.substr(i + 1, j - i - 1);
1061                                 string const replacement = raw.substr(j + 1, k - j - 1);
1062                                 if (auto_packages.find(package) == auto_packages.end())
1063                                         os << replacement;
1064                                 i = k;
1065                         } else
1066                                 os.put(raw[i]);
1067                 }
1068                 os << "\n\\end_preamble\n";
1069         }
1070         if (!h_options.empty())
1071                 os << "\\options " << h_options << "\n";
1072         os << "\\use_default_options " << h_use_default_options << "\n";
1073         if (!used_modules.empty()) {
1074                 os << "\\begin_modules\n";
1075                 vector<string>::const_iterator const end = used_modules.end();
1076                 vector<string>::const_iterator it = used_modules.begin();
1077                 for (; it != end; ++it)
1078                         os << *it << '\n';
1079                 os << "\\end_modules\n";
1080         }
1081         os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1082            << "\\language " << h_language << "\n"
1083            << "\\language_package " << h_language_package << "\n"
1084            << "\\inputencoding " << h_inputencoding << "\n"
1085            << "\\fontencoding " << h_fontencoding << "\n"
1086            << "\\font_roman " << h_font_roman << "\n"
1087            << "\\font_sans " << h_font_sans << "\n"
1088            << "\\font_typewriter " << h_font_typewriter << "\n"
1089            << "\\font_math " << h_font_math << "\n"
1090            << "\\font_default_family " << h_font_default_family << "\n"
1091            << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
1092            << "\\font_sc " << h_font_sc << "\n"
1093            << "\\font_osf " << h_font_osf << "\n"
1094            << "\\font_sf_scale " << h_font_sf_scale << "\n"
1095            << "\\font_tt_scale " << h_font_tt_scale << '\n';
1096         if (!h_font_cjk.empty())
1097                 os << "\\font_cjk " << h_font_cjk << '\n';
1098         os << "\\graphics " << h_graphics << '\n'
1099            << "\\default_output_format " << h_default_output_format << "\n"
1100            << "\\output_sync " << h_output_sync << "\n";
1101         if (h_output_sync == "1")
1102                 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1103         os << "\\bibtex_command " << h_bibtex_command << "\n"
1104            << "\\index_command " << h_index_command << "\n";
1105         if (!h_float_placement.empty())
1106                 os << "\\float_placement " << h_float_placement << "\n";
1107         os << "\\paperfontsize " << h_paperfontsize << "\n"
1108            << "\\spacing " << h_spacing << "\n"
1109            << "\\use_hyperref " << h_use_hyperref << '\n';
1110         if (h_use_hyperref == "true") {
1111                 if (!h_pdf_title.empty())
1112                         os << "\\pdf_title \"" << h_pdf_title << "\"\n";
1113                 if (!h_pdf_author.empty())
1114                         os << "\\pdf_author \"" << h_pdf_author << "\"\n";
1115                 if (!h_pdf_subject.empty())
1116                         os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
1117                 if (!h_pdf_keywords.empty())
1118                         os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
1119                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1120                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1121                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1122                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1123                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1124                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1125                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1126                       "\\pdf_backref " << h_pdf_backref << "\n"
1127                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1128                 if (!h_pdf_pagemode.empty())
1129                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1130                 if (!h_pdf_quoted_options.empty())
1131                         os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
1132         }
1133         os << "\\papersize " << h_papersize << "\n"
1134            << "\\use_geometry " << h_use_geometry << '\n';
1135         for (map<string, string>::const_iterator it = h_use_packages.begin();
1136              it != h_use_packages.end(); ++it)
1137                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1138         os << "\\cite_engine " << h_cite_engine << '\n'
1139            << "\\cite_engine_type " << h_cite_engine_type << '\n'
1140            << "\\biblio_style " << h_biblio_style << "\n"
1141            << "\\use_bibtopic " << h_use_bibtopic << "\n"
1142            << "\\use_indices " << h_use_indices << "\n"
1143            << "\\paperorientation " << h_paperorientation << '\n'
1144            << "\\suppress_date " << h_suppress_date << '\n'
1145            << "\\justification " << h_justification << '\n'
1146            << "\\use_refstyle " << h_use_refstyle << '\n';
1147         if (!h_fontcolor.empty())
1148                 os << "\\fontcolor " << h_fontcolor << '\n';
1149         if (!h_notefontcolor.empty())
1150                 os << "\\notefontcolor " << h_notefontcolor << '\n';
1151         if (!h_backgroundcolor.empty())
1152                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1153         if (!h_boxbgcolor.empty())
1154                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1155         if (index_number != 0)
1156                 for (int i = 0; i < index_number; i++) {
1157                         os << "\\index " << h_index[i] << '\n'
1158                            << "\\shortcut " << h_shortcut[i] << '\n'
1159                            << "\\color " << h_color << '\n'
1160                            << "\\end_index\n";
1161                 }
1162         else {
1163                 os << "\\index " << h_index[0] << '\n'
1164                    << "\\shortcut " << h_shortcut[0] << '\n'
1165                    << "\\color " << h_color << '\n'
1166                    << "\\end_index\n";
1167         }
1168         os << h_margins
1169            << "\\secnumdepth " << h_secnumdepth << "\n"
1170            << "\\tocdepth " << h_tocdepth << "\n"
1171            << "\\paragraph_separation " << h_paragraph_separation << "\n";
1172         if (h_paragraph_separation == "skip")
1173                 os << "\\defskip " << h_defskip << "\n";
1174         else
1175                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1176         os << "\\quotes_language " << h_quotes_language << "\n"
1177            << "\\papercolumns " << h_papercolumns << "\n"
1178            << "\\papersides " << h_papersides << "\n"
1179            << "\\paperpagestyle " << h_paperpagestyle << "\n";
1180         if (!h_listings_params.empty())
1181                 os << "\\listings_params " << h_listings_params << "\n";
1182         os << "\\tracking_changes " << h_tracking_changes << "\n"
1183            << "\\output_changes " << h_output_changes << "\n"
1184            << "\\html_math_output " << h_html_math_output << "\n"
1185            << "\\html_css_as_file " << h_html_css_as_file << "\n"
1186            << "\\html_be_strict " << h_html_be_strict << "\n"
1187            << authors_
1188            << "\\end_header\n\n"
1189            << "\\begin_body\n";
1190         return true;
1191 }
1192
1193
1194 void Preamble::parse(Parser & p, string const & forceclass,
1195                      TeX2LyXDocClass & tc)
1196 {
1197         // initialize fixed types
1198         special_columns['D'] = 3;
1199         bool is_full_document = false;
1200         bool is_lyx_file = false;
1201         bool in_lyx_preamble = false;
1202
1203         // determine whether this is a full document or a fragment for inclusion
1204         while (p.good()) {
1205                 Token const & t = p.get_token();
1206
1207                 if (t.cat() == catEscape && t.cs() == "documentclass") {
1208                         is_full_document = true;
1209                         break;
1210                 }
1211         }
1212         p.reset();
1213
1214         while (is_full_document && p.good()) {
1215                 Token const & t = p.get_token();
1216
1217 #ifdef FILEDEBUG
1218                 cerr << "t: " << t << "\n";
1219 #endif
1220
1221                 //
1222                 // cat codes
1223                 //
1224                 if (!in_lyx_preamble &&
1225                     (t.cat() == catLetter ||
1226                      t.cat() == catSuper ||
1227                      t.cat() == catSub ||
1228                      t.cat() == catOther ||
1229                      t.cat() == catMath ||
1230                      t.cat() == catActive ||
1231                      t.cat() == catBegin ||
1232                      t.cat() == catEnd ||
1233                      t.cat() == catAlign ||
1234                      t.cat() == catParameter))
1235                         h_preamble << t.cs();
1236
1237                 else if (!in_lyx_preamble &&
1238                          (t.cat() == catSpace || t.cat() == catNewline))
1239                         h_preamble << t.asInput();
1240
1241                 else if (t.cat() == catComment) {
1242                         static regex const islyxfile("%% LyX .* created this file");
1243                         static regex const usercommands("User specified LaTeX commands");
1244
1245                         string const comment = t.asInput();
1246
1247                         // magically switch encoding default if it looks like XeLaTeX
1248                         static string const magicXeLaTeX =
1249                                 "% This document must be compiled with XeLaTeX ";
1250                         if (comment.size() > magicXeLaTeX.size()
1251                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1252                                   && h_inputencoding == "auto") {
1253                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
1254                                 h_inputencoding = "utf8";
1255                         }
1256                         smatch sub;
1257                         if (regex_search(comment, sub, islyxfile)) {
1258                                 is_lyx_file = true;
1259                                 in_lyx_preamble = true;
1260                         } else if (is_lyx_file
1261                                    && regex_search(comment, sub, usercommands))
1262                                 in_lyx_preamble = false;
1263                         else if (!in_lyx_preamble)
1264                                 h_preamble << t.asInput();
1265                 }
1266
1267                 else if (t.cs() == "pagestyle")
1268                         h_paperpagestyle = p.verbatim_item();
1269
1270                 else if (t.cs() == "setdefaultlanguage") {
1271                         xetex = true;
1272                         // We don't yet care about non-language variant options
1273                         // because LyX doesn't support this yet, see bug #8214
1274                         if (p.hasOpt()) {
1275                                 string langopts = p.getOpt();
1276                                 // check if the option contains a variant, if yes, extract it
1277                                 string::size_type pos_var = langopts.find("variant");
1278                                 string::size_type i = langopts.find(',', pos_var);
1279                                 string::size_type k = langopts.find('=', pos_var);
1280                                 if (pos_var != string::npos){
1281                                         string variant;
1282                                         if (i == string::npos)
1283                                                 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1284                                         else
1285                                                 variant = langopts.substr(k + 1, i - k - 1);
1286                                         h_language = variant;
1287                                 }
1288                                 p.verbatim_item();
1289                         } else
1290                                 h_language = p.verbatim_item();
1291                         //finally translate the poyglossia name to a LyX name
1292                         h_language = polyglossia2lyx(h_language);
1293                 }
1294
1295                 else if (t.cs() == "setotherlanguage") {
1296                         // We don't yet care about the option because LyX doesn't
1297                         // support this yet, see bug #8214
1298                         p.hasOpt() ? p.getOpt() : string();
1299                         p.verbatim_item();
1300                 }
1301
1302                 else if (t.cs() == "setmainfont") {
1303                         // we don't care about the option
1304                         p.hasOpt() ? p.getOpt() : string();
1305                         h_font_roman = p.getArg('{', '}');
1306                 }
1307
1308                 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1309                         // LyX currently only supports the scale option
1310                         string scale;
1311                         if (p.hasOpt()) {
1312                                 string fontopts = p.getArg('[', ']');
1313                                 // check if the option contains a scaling, if yes, extract it
1314                                 string::size_type pos = fontopts.find("Scale");
1315                                 if (pos != string::npos) {
1316                                         string::size_type i = fontopts.find(',', pos);
1317                                         if (i == string::npos)
1318                                                 scale_as_percentage(fontopts.substr(pos + 1), scale);
1319                                         else
1320                                                 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1321                                 }
1322                         }
1323                         if (t.cs() == "setsansfont") {
1324                                 if (!scale.empty())
1325                                         h_font_sf_scale = scale;
1326                                 h_font_sans = p.getArg('{', '}');
1327                         } else {
1328                                 if (!scale.empty())
1329                                         h_font_tt_scale = scale;
1330                                 h_font_typewriter = p.getArg('{', '}');
1331                         }
1332                 }
1333
1334                 else if (t.cs() == "date") {
1335                         string argument = p.getArg('{', '}');
1336                         if (argument.empty())
1337                                 h_suppress_date = "true";
1338                         else
1339                                 h_preamble << t.asInput() << '{' << argument << '}';
1340                 }
1341
1342                 else if (t.cs() == "color") {
1343                         string const space =
1344                                 (p.hasOpt() ? p.getOpt() : string());
1345                         string argument = p.getArg('{', '}');
1346                         // check the case that a standard color is used
1347                         if (space.empty() && is_known(argument, known_basic_colors)) {
1348                                 h_fontcolor = rgbcolor2code(argument);
1349                                 preamble.registerAutomaticallyLoadedPackage("color");
1350                         } else if (space.empty() && argument == "document_fontcolor")
1351                                 preamble.registerAutomaticallyLoadedPackage("color");
1352                         // check the case that LyX's document_fontcolor is defined
1353                         // but not used for \color
1354                         else {
1355                                 h_preamble << t.asInput();
1356                                 if (!space.empty())
1357                                         h_preamble << space;
1358                                 h_preamble << '{' << argument << '}';
1359                                 // the color might already be set because \definecolor
1360                                 // is parsed before this
1361                                 h_fontcolor = "";
1362                         }
1363                 }
1364
1365                 else if (t.cs() == "pagecolor") {
1366                         string argument = p.getArg('{', '}');
1367                         // check the case that a standard color is used
1368                         if (is_known(argument, known_basic_colors)) {
1369                                 h_backgroundcolor = rgbcolor2code(argument);
1370                         } else if (argument == "page_backgroundcolor")
1371                                 preamble.registerAutomaticallyLoadedPackage("color");
1372                         // check the case that LyX's page_backgroundcolor is defined
1373                         // but not used for \pagecolor
1374                         else {
1375                                 h_preamble << t.asInput() << '{' << argument << '}';
1376                                 // the color might already be set because \definecolor
1377                                 // is parsed before this
1378                                 h_backgroundcolor = "";
1379                         }
1380                 }
1381
1382                 else if (t.cs() == "makeatletter") {
1383                         // LyX takes care of this
1384                         p.setCatcode('@', catLetter);
1385                 }
1386
1387                 else if (t.cs() == "makeatother") {
1388                         // LyX takes care of this
1389                         p.setCatcode('@', catOther);
1390                 }
1391
1392                 else if (t.cs() == "makeindex") {
1393                         // LyX will re-add this if a print index command is found
1394                         p.skip_spaces();
1395                 }
1396
1397                 else if (t.cs() == "newindex") {
1398                         string const indexname = p.getArg('[', ']');
1399                         string const shortcut = p.verbatim_item();
1400                         if (!indexname.empty())
1401                                 h_index[index_number] = indexname;
1402                         else
1403                                 h_index[index_number] = shortcut;
1404                         h_shortcut[index_number] = shortcut;
1405                         index_number += 1;
1406                         p.skip_spaces();
1407                 }
1408
1409                 else if (t.cs() == "RS@ifundefined") {
1410                         string const name = p.verbatim_item();
1411                         string const body1 = p.verbatim_item();
1412                         string const body2 = p.verbatim_item();
1413                         // store the in_lyx_preamble setting
1414                         bool const was_in_lyx_preamble = in_lyx_preamble;
1415                         if (name == "subref"
1416                                 || name == "thmref"
1417                                 || name == "lemref") {
1418                                 p.skip_spaces();
1419                                 in_lyx_preamble = true;
1420                         }
1421                         // only non-lyxspecific stuff
1422                         if (!in_lyx_preamble) {
1423                                 ostringstream ss;
1424                                 ss << '\\' << t.cs();
1425                                 ss << '{' << name << '}'
1426                                    << '{' << body1 << '}'
1427                                    << '{' << body2 << '}';
1428                                 h_preamble << ss.str();
1429                         }
1430                         // restore the in_lyx_preamble setting
1431                         in_lyx_preamble = was_in_lyx_preamble;
1432                 }
1433                 
1434                 else if (t.cs() == "AtBeginDocument") {
1435                         string const name = p.verbatim_item();
1436                         // store the in_lyx_preamble setting
1437                         bool const was_in_lyx_preamble = in_lyx_preamble;
1438                         if (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1439                                 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1440                                 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1441                                 || name == "\\providecommand\\subref[1]{\\ref{sub:#1}}"
1442                                 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1443                                 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1444                                 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1445                                 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1446                                 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1447                                 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1448                                 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1449                                 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1450                                 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1451                                 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1452                                 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}") {
1453                                 p.skip_spaces();
1454                                 in_lyx_preamble = true;
1455                         }
1456                         // only non-lyxspecific stuff
1457                         if (!in_lyx_preamble) {
1458                                 ostringstream ss;
1459                                 ss << '\\' << t.cs();
1460                                 ss << '{' << name << '}';
1461                                 h_preamble << ss.str();
1462                         }
1463                         // restore the in_lyx_preamble setting
1464                         in_lyx_preamble = was_in_lyx_preamble;
1465                 }
1466
1467                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1468                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1469                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
1470                                 || t.cs() == "DeclareRobustCommand"
1471                       || t.cs() == "DeclareRobustCommandx"
1472                                 || t.cs() == "ProvideTextCommandDefault"
1473                                 || t.cs() == "DeclareMathAccent") {
1474                         bool star = false;
1475                         if (p.next_token().character() == '*') {
1476                                 p.get_token();
1477                                 star = true;
1478                         }
1479                         string const name = p.verbatim_item();
1480                         string const opt1 = p.getFullOpt();
1481                         string const opt2 = p.getFullOpt();
1482                         string const body = p.verbatim_item();
1483                         // store the in_lyx_preamble setting
1484                         bool const was_in_lyx_preamble = in_lyx_preamble;
1485                         // font settings
1486                         if (name == "\\rmdefault")
1487                                 if (is_known(body, known_roman_fonts)) {
1488                                         h_font_roman = body;
1489                                         p.skip_spaces();
1490                                         in_lyx_preamble = true;
1491                                 }
1492                         if (name == "\\sfdefault")
1493                                 if (is_known(body, known_sans_fonts)) {
1494                                         h_font_sans = body;
1495                                         p.skip_spaces();
1496                                         in_lyx_preamble = true;
1497                                 }
1498                         if (name == "\\ttdefault")
1499                                 if (is_known(body, known_typewriter_fonts)) {
1500                                         h_font_typewriter = body;
1501                                         p.skip_spaces();
1502                                         in_lyx_preamble = true;
1503                                 }
1504                         if (name == "\\familydefault") {
1505                                 string family = body;
1506                                 // remove leading "\"
1507                                 h_font_default_family = family.erase(0,1);
1508                                 p.skip_spaces();
1509                                 in_lyx_preamble = true;
1510                         }
1511                         if (name == "\\bfdefault")
1512                                 // LyX re-adds this if a kurier font is used
1513                                 if (is_known(h_font_sans, known_kurier_fonts) && body == "b") {
1514                                         p.skip_spaces();
1515                                         in_lyx_preamble = true;
1516                                 }
1517
1518                         // remove the lyxdot definition that is re-added by LyX
1519                         // if necessary
1520                         if (name == "\\lyxdot") {
1521                                 p.skip_spaces();
1522                                 in_lyx_preamble = true;
1523                         }
1524
1525                         // Add the command to the known commands
1526                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1527
1528                         // only non-lyxspecific stuff
1529                         if (!in_lyx_preamble) {
1530                                 ostringstream ss;
1531                                 ss << '\\' << t.cs();
1532                                 if (star)
1533                                         ss << '*';
1534                                 ss << '{' << name << '}' << opt1 << opt2
1535                                    << '{' << body << "}";
1536                                 h_preamble << ss.str();
1537 /*
1538                                 ostream & out = in_preamble ? h_preamble : os;
1539                                 out << "\\" << t.cs() << "{" << name << "}"
1540                                     << opts << "{" << body << "}";
1541 */
1542                         }
1543                         // restore the in_lyx_preamble setting
1544                         in_lyx_preamble = was_in_lyx_preamble;
1545                 }
1546
1547                 else if (t.cs() == "edef"){
1548                         // we only support this for kurier fonts
1549                         string const command = p.next_token().asInput();
1550                         p.get_token();
1551                         if (command == "\\sfdefault") {
1552                                 p.getArg('{', '}');
1553                                 if (h_font_sans == "kurier")
1554                                         h_font_sans = "kurier-condensed";
1555                                 if (h_font_sans == "kurierl")
1556                                         h_font_sans = "kurier-light-condensed";
1557                                 p.skip_spaces();
1558                         }
1559                         else
1560                                 h_preamble << "\\edef" << command << "{" << p.getArg('{', '}') << "}\n";
1561                 }
1562
1563                 else if (t.cs() == "documentclass") {
1564                         vector<string>::iterator it;
1565                         vector<string> opts = split_options(p.getArg('[', ']'));
1566                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1567                         delete_opt(opts, known_fontsizes);
1568                         // delete "pt" at the end
1569                         string::size_type i = h_paperfontsize.find("pt");
1570                         if (i != string::npos)
1571                                 h_paperfontsize.erase(i);
1572                         // The documentclass options are always parsed before the options
1573                         // of the babel call so that a language cannot overwrite the babel
1574                         // options.
1575                         handle_opt(opts, known_languages, h_language);
1576                         delete_opt(opts, known_languages);
1577
1578                         // paper orientation
1579                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1580                                 h_paperorientation = "landscape";
1581                                 opts.erase(it);
1582                         }
1583                         // paper sides
1584                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1585                                  != opts.end()) {
1586                                 h_papersides = "1";
1587                                 opts.erase(it);
1588                         }
1589                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1590                                  != opts.end()) {
1591                                 h_papersides = "2";
1592                                 opts.erase(it);
1593                         }
1594                         // paper columns
1595                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1596                                  != opts.end()) {
1597                                 h_papercolumns = "1";
1598                                 opts.erase(it);
1599                         }
1600                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1601                                  != opts.end()) {
1602                                 h_papercolumns = "2";
1603                                 opts.erase(it);
1604                         }
1605                         // paper sizes
1606                         // some size options are known to any document classes, other sizes
1607                         // are handled by the \geometry command of the geometry package
1608                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1609                         delete_opt(opts, known_class_paper_sizes);
1610                         // the remaining options
1611                         h_options = join(opts, ",");
1612                         // FIXME This does not work for classes that have a
1613                         //       different name in LyX than in LaTeX
1614                         h_textclass = p.getArg('{', '}');
1615                         p.skip_spaces();
1616                 }
1617
1618                 else if (t.cs() == "usepackage") {
1619                         string const options = p.getArg('[', ']');
1620                         string const name = p.getArg('{', '}');
1621                         vector<string> vecnames;
1622                         split(name, vecnames, ',');
1623                         vector<string>::const_iterator it  = vecnames.begin();
1624                         vector<string>::const_iterator end = vecnames.end();
1625                         for (; it != end; ++it)
1626                                 handle_package(p, trimSpaceAndEol(*it), options,
1627                                                in_lyx_preamble);
1628                 }
1629
1630                 else if (t.cs() == "inputencoding") {
1631                         string const encoding = p.getArg('{','}');
1632                         Encoding const * const enc = encodings.fromLaTeXName(
1633                                 encoding, Encoding::inputenc, true);
1634                         if (!enc)
1635                                 cerr << "Unknown encoding " << encoding << ". Ignoring." << std::endl;
1636                         else {
1637                                 if (!enc->unsafe())
1638                                         h_inputencoding = enc->name();
1639                                 p.setEncoding(enc->iconvName());
1640                         }
1641                 }
1642
1643                 else if (t.cs() == "newenvironment") {
1644                         string const name = p.getArg('{', '}');
1645                         string const opt1 = p.getFullOpt();
1646                         string const opt2 = p.getFullOpt();
1647                         string const beg = p.verbatim_item();
1648                         string const end = p.verbatim_item();
1649                         if (!in_lyx_preamble) {
1650                                 h_preamble << "\\newenvironment{" << name
1651                                            << '}' << opt1 << opt2 << '{'
1652                                            << beg << "}{" << end << '}';
1653                         }
1654                         add_known_environment(name, opt1, !opt2.empty(),
1655                                               from_utf8(beg), from_utf8(end));
1656
1657                 }
1658
1659                 else if (t.cs() == "newtheorem") {
1660                         string const name = p.getArg('{', '}');
1661                         string const opt1 = p.getFullOpt();
1662                         string const opt2 = p.getFullOpt();
1663                         string const body = p.verbatim_item();
1664                         string const opt3 = p.getFullOpt();
1665
1666                         add_known_theorem(name, opt1, !opt2.empty(),
1667                                 from_utf8("\\newtheorem{" + name + '}' +
1668                                           opt1 + opt2 + '{' + body + '}' + opt3));
1669
1670                         if (!in_lyx_preamble)
1671                                 h_preamble << "\\newtheorem{" << name << '}'
1672                                            << opt1 << opt2 << '{' << '}' << opt3;
1673                 }
1674
1675                 else if (t.cs() == "def") {
1676                         string name = p.get_token().cs();
1677                         // In fact, name may be more than the name:
1678                         // In the test case of bug 8116
1679                         // name == "csname SF@gobble@opt \endcsname".
1680                         // Therefore, we need to use asInput() instead of cs().
1681                         while (p.next_token().cat() != catBegin)
1682                                 name += p.get_token().asInput();
1683                         if (!in_lyx_preamble)
1684                                 h_preamble << "\\def\\" << name << '{'
1685                                            << p.verbatim_item() << "}";
1686                 }
1687
1688                 else if (t.cs() == "newcolumntype") {
1689                         string const name = p.getArg('{', '}');
1690                         trimSpaceAndEol(name);
1691                         int nargs = 0;
1692                         string opts = p.getOpt();
1693                         if (!opts.empty()) {
1694                                 istringstream is(string(opts, 1));
1695                                 is >> nargs;
1696                         }
1697                         special_columns[name[0]] = nargs;
1698                         h_preamble << "\\newcolumntype{" << name << "}";
1699                         if (nargs)
1700                                 h_preamble << "[" << nargs << "]";
1701                         h_preamble << "{" << p.verbatim_item() << "}";
1702                 }
1703
1704                 else if (t.cs() == "setcounter") {
1705                         string const name = p.getArg('{', '}');
1706                         string const content = p.getArg('{', '}');
1707                         if (name == "secnumdepth")
1708                                 h_secnumdepth = content;
1709                         else if (name == "tocdepth")
1710                                 h_tocdepth = content;
1711                         else
1712                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1713                 }
1714
1715                 else if (t.cs() == "setlength") {
1716                         string const name = p.verbatim_item();
1717                         string const content = p.verbatim_item();
1718                         // the paragraphs are only not indented when \parindent is set to zero
1719                         if (name == "\\parindent" && content != "") {
1720                                 if (content[0] == '0')
1721                                         h_paragraph_separation = "skip";
1722                                 else
1723                                         h_paragraph_indentation = translate_len(content);
1724                         } else if (name == "\\parskip") {
1725                                 if (content == "\\smallskipamount")
1726                                         h_defskip = "smallskip";
1727                                 else if (content == "\\medskipamount")
1728                                         h_defskip = "medskip";
1729                                 else if (content == "\\bigskipamount")
1730                                         h_defskip = "bigskip";
1731                                 else
1732                                         h_defskip = content;
1733                         } else
1734                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1735                 }
1736
1737                 else if (t.cs() == "onehalfspacing")
1738                         h_spacing = "onehalf";
1739
1740                 else if (t.cs() == "doublespacing")
1741                         h_spacing = "double";
1742
1743                 else if (t.cs() == "setstretch")
1744                         h_spacing = "other " + p.verbatim_item();
1745
1746                 else if (t.cs() == "synctex") {
1747                         // the scheme is \synctex=value
1748                         // where value can only be "1" or "-1"
1749                         h_output_sync = "1";
1750                         // there can be any character behind the value (e.g. a linebreak or a '\'
1751                         // therefore we extract it char by char
1752                         p.get_token();
1753                         string value = p.get_token().asInput();
1754                         if (value == "-")
1755                                 value += p.get_token().asInput();
1756                         h_output_sync_macro = "\\synctex=" + value;
1757                 }
1758
1759                 else if (t.cs() == "begin") {
1760                         string const name = p.getArg('{', '}');
1761                         if (name == "document")
1762                                 break;
1763                         h_preamble << "\\begin{" << name << "}";
1764                 }
1765
1766                 else if (t.cs() == "geometry") {
1767                         vector<string> opts = split_options(p.getArg('{', '}'));
1768                         handle_geometry(opts);
1769                 }
1770
1771                 else if (t.cs() == "definecolor") {
1772                         string const color = p.getArg('{', '}');
1773                         string const space = p.getArg('{', '}');
1774                         string const value = p.getArg('{', '}');
1775                         if (color == "document_fontcolor" && space == "rgb") {
1776                                 RGBColor c(RGBColorFromLaTeX(value));
1777                                 h_fontcolor = X11hexname(c);
1778                         } else if (color == "note_fontcolor" && space == "rgb") {
1779                                 RGBColor c(RGBColorFromLaTeX(value));
1780                                 h_notefontcolor = X11hexname(c);
1781                         } else if (color == "page_backgroundcolor" && space == "rgb") {
1782                                 RGBColor c(RGBColorFromLaTeX(value));
1783                                 h_backgroundcolor = X11hexname(c);
1784                         } else if (color == "shadecolor" && space == "rgb") {
1785                                 RGBColor c(RGBColorFromLaTeX(value));
1786                                 h_boxbgcolor = X11hexname(c);
1787                         } else {
1788                                 h_preamble << "\\definecolor{" << color
1789                                            << "}{" << space << "}{" << value
1790                                            << '}';
1791                         }
1792                 }
1793
1794                 else if (t.cs() == "bibliographystyle")
1795                         h_biblio_style = p.verbatim_item();
1796
1797                 else if (t.cs() == "jurabibsetup") {
1798                         // FIXME p.getArg('{', '}') is most probably wrong (it
1799                         //       does not handle nested braces).
1800                         //       Use p.verbatim_item() instead.
1801                         vector<string> jurabibsetup =
1802                                 split_options(p.getArg('{', '}'));
1803                         // add jurabibsetup to the jurabib package options
1804                         add_package("jurabib", jurabibsetup);
1805                         if (!jurabibsetup.empty()) {
1806                                 h_preamble << "\\jurabibsetup{"
1807                                            << join(jurabibsetup, ",") << '}';
1808                         }
1809                 }
1810
1811                 else if (t.cs() == "hypersetup") {
1812                         vector<string> hypersetup =
1813                                 split_options(p.verbatim_item());
1814                         // add hypersetup to the hyperref package options
1815                         handle_hyperref(hypersetup);
1816                         if (!hypersetup.empty()) {
1817                                 h_preamble << "\\hypersetup{"
1818                                            << join(hypersetup, ",") << '}';
1819                         }
1820                 }
1821
1822                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1823                         // prevent misparsing of \usepackage if it is used
1824                         // as an argument (see e.g. our own output of
1825                         // \@ifundefined above)
1826                         string const arg1 = p.verbatim_item();
1827                         string const arg2 = p.verbatim_item();
1828                         string const arg3 = p.verbatim_item();
1829                         // test case \@ifundefined{date}{}{\date{}}
1830                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
1831                             arg2.empty() && arg3 == "\\date{}") {
1832                                 h_suppress_date = "true";
1833                         // older tex2lyx versions did output
1834                         // \@ifundefined{definecolor}{\usepackage{color}}{}
1835                         } else if (t.cs() == "@ifundefined" &&
1836                                    arg1 == "definecolor" &&
1837                                    arg2 == "\\usepackage{color}" &&
1838                                    arg3.empty()) {
1839                                 if (!in_lyx_preamble)
1840                                         h_preamble << package_beg_sep
1841                                                    << "color"
1842                                                    << package_mid_sep
1843                                                    << "\\@ifundefined{definecolor}{color}{}"
1844                                                    << package_end_sep;
1845                         // test for case
1846                         //\@ifundefined{showcaptionsetup}{}{%
1847                         // \PassOptionsToPackage{caption=false}{subfig}}
1848                         // that LyX uses for subfloats
1849                         } else if (t.cs() == "@ifundefined" &&
1850                                    arg1 == "showcaptionsetup" && arg2.empty()
1851                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1852                                 ; // do nothing
1853                         } else if (!in_lyx_preamble) {
1854                                 h_preamble << t.asInput()
1855                                            << '{' << arg1 << '}'
1856                                            << '{' << arg2 << '}'
1857                                            << '{' << arg3 << '}';
1858                         }
1859                 }
1860
1861                 else if (is_known(t.cs(), known_if_commands)) {
1862                         // must not parse anything in conditional code, since
1863                         // LyX would output the parsed contents unconditionally
1864                         if (!in_lyx_preamble)
1865                                 h_preamble << t.asInput();
1866                         handle_if(p, in_lyx_preamble);
1867                 }
1868
1869                 else if (!t.cs().empty() && !in_lyx_preamble)
1870                         h_preamble << '\\' << t.cs();
1871         }
1872
1873         // remove the whitespace
1874         p.skip_spaces();
1875
1876         // Force textclass if the user wanted it
1877         if (!forceclass.empty())
1878                 h_textclass = forceclass;
1879         tc.setName(h_textclass);
1880         if (!tc.load()) {
1881                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1882                 exit(EXIT_FAILURE);
1883         }
1884         if (h_papersides.empty()) {
1885                 ostringstream ss;
1886                 ss << tc.sides();
1887                 h_papersides = ss.str();
1888         }
1889
1890         // If the CJK package is used we cannot set the document language from
1891         // the babel options. Instead, we guess which language is used most
1892         // and set this one.
1893         default_language = h_language;
1894         if (is_full_document &&
1895             (auto_packages.find("CJK") != auto_packages.end() ||
1896              auto_packages.find("CJKutf8") != auto_packages.end())) {
1897                 p.pushPosition();
1898                 h_language = guessLanguage(p, default_language);
1899                 p.popPosition();
1900                 if (explicit_babel && h_language != default_language) {
1901                         // We set the document language to a CJK language,
1902                         // but babel is explicitly called in the user preamble
1903                         // without options. LyX will not add the default
1904                         // language to the document options if it is either
1905                         // english, or no text is set as default language.
1906                         // Therefore we need to add a language option explicitly.
1907                         // FIXME: It would be better to remove all babel calls
1908                         //        from the user preamble, but this is difficult
1909                         //        without re-introducing bug 7861.
1910                         if (h_options.empty())
1911                                 h_options = lyx2babel(default_language);
1912                         else
1913                                 h_options += ',' + lyx2babel(default_language);
1914                 }
1915         }
1916 }
1917
1918
1919 string babel2lyx(string const & language)
1920 {
1921         char const * const * where = is_known(language, known_languages);
1922         if (where)
1923                 return known_coded_languages[where - known_languages];
1924         return language;
1925 }
1926
1927
1928 string lyx2babel(string const & language)
1929 {
1930         char const * const * where = is_known(language, known_coded_languages);
1931         if (where)
1932                 return known_languages[where - known_coded_languages];
1933         return language;
1934 }
1935
1936
1937 string Preamble::polyglossia2lyx(string const & language)
1938 {
1939         char const * const * where = is_known(language, polyglossia_languages);
1940         if (where)
1941                 return coded_polyglossia_languages[where - polyglossia_languages];
1942         return language;
1943 }
1944
1945
1946 string rgbcolor2code(string const & name)
1947 {
1948         char const * const * where = is_known(name, known_basic_colors);
1949         if (where) {
1950                 // "red", "green" etc
1951                 return known_basic_color_codes[where - known_basic_colors];
1952         }
1953         // "255,0,0", "0,255,0" etc
1954         RGBColor c(RGBColorFromLaTeX(name));
1955         return X11hexname(c);
1956 }
1957
1958 // }])
1959
1960
1961 } // namespace lyx