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