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