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