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