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