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