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