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