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