]> git.lyx.org Git - features.git/blob - src/tex2lyx/Preamble.cpp
tex2lyx: support for mathdesign
[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
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",
128 "mathdesign", "mathpazo", "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         if (name == "mathdesign") {
673                 // when font uses real small capitals
674                 if (opts.find("charter") != string::npos)
675                         h_font_roman = "md-charter";
676                 if (opts.find("garamond") != string::npos)
677                         h_font_roman = "md-garamond";
678                 if (opts.find("utopia") != string::npos)
679                         h_font_roman = "md-utopia";
680                 if (opts.find("expert") != string::npos) {
681                         h_font_sc = "true";
682                         h_font_osf = "true";
683                 }
684         }
685
686         else if (name == "mathpazo")
687                 h_font_roman = "palatino";
688
689         else if (name == "mathptmx")
690                 h_font_roman = "times";
691
692         // sansserif fonts
693         if (is_known(name, known_sans_fonts)) {
694                 h_font_sans = name;
695                 if (options.size() == 1) {
696                         if (scale_as_percentage(opts, h_font_sf_scale))
697                                 options.clear();
698                 }
699         }
700
701         // typewriter fonts
702         if (is_known(name, known_typewriter_fonts)) {
703                 // fourier can be set as roman font _only_
704                 // fourier as typewriter is handled in handling of \ttdefault
705                 if (name != "fourier") {
706                         h_font_typewriter = name;
707                         if (options.size() == 1) {
708                                 if (scale_as_percentage(opts, h_font_tt_scale))
709                                         options.clear();
710                         }
711                 }
712         }
713
714         // font uses old-style figure
715         if (name == "eco")
716                 h_font_osf = "true";
717
718         if (name == "refstyle")
719                 h_use_refstyle = "1";
720
721         // after the detection and handling of special cases, we can remove the
722         // fonts, otherwise they would appear in the preamble, see bug #7856
723         if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
724                 ||      is_known(name, known_typewriter_fonts))
725                 ;
726
727         else if (name == "amsmath" || name == "amssymb" ||
728                  name == "esint" || name == "mhchem" || name == "mathdots" ||
729                  name == "mathtools" || name == "stackrel" ||
730                  name == "stmaryrd" || name == "undertilde")
731                 h_use_packages[name] = "2";
732
733         else if (name == "babel") {
734                 h_language_package = "default";
735                 // One might think we would have to do nothing if babel is loaded
736                 // without any options to prevent pollution of the preamble with this
737                 // babel call in every roundtrip.
738                 // But the user could have defined babel-specific things afterwards. So
739                 // we need to keep it in the preamble to prevent cases like bug #7861.
740                 if (!opts.empty()) {
741                         // check if more than one option was used - used later for inputenc
742                         if (options.begin() != options.end() - 1)
743                                 one_language = false;
744                         // babel takes the last language of the option of its \usepackage
745                         // call as document language. If there is no such language option, the
746                         // last language in the documentclass options is used.
747                         handle_opt(options, known_languages, h_language);
748                         // translate the babel name to a LyX name
749                         h_language = babel2lyx(h_language);
750                         if (h_language == "japanese") {
751                                 // For Japanese, the encoding isn't indicated in the source
752                                 // file, and there's really not much we can do. We could
753                                 // 1) offer a list of possible encodings to choose from, or
754                                 // 2) determine the encoding of the file by inspecting it.
755                                 // For the time being, we leave the encoding alone so that
756                                 // we don't get iconv errors when making a wrong guess, and
757                                 // we will output a note at the top of the document
758                                 // explaining what to do.
759                                 Encoding const * const enc = encodings.fromIconvName(
760                                         p.getEncoding(), Encoding::japanese, false);
761                                 if (enc)
762                                         h_inputencoding = enc->name();
763                                 is_nonCJKJapanese = true;
764                                 // in this case babel can be removed from the preamble
765                                 registerAutomaticallyLoadedPackage("babel");
766                         } else {
767                                 // If babel is called with options, LyX puts them by default into the
768                                 // document class options. This works for most languages, except
769                                 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
770                                 // perhaps in future others.
771                                 // Therefore keep the babel call as it is as the user might have
772                                 // reasons for it.
773                                 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
774                         }
775                         delete_opt(options, known_languages);
776                 }
777                 else
778                         h_preamble << "\\usepackage{babel}\n";
779         }
780
781         else if (name == "polyglossia") {
782                 h_language_package = "default";
783                 h_default_output_format = "pdf4";
784                 h_use_non_tex_fonts = "true";
785                 xetex = true;
786                 registerAutomaticallyLoadedPackage("xunicode");
787                 if (h_inputencoding == "auto")
788                         p.setEncoding("UTF-8");
789         }
790
791         else if (name == "CJK") {
792                 // set the encoding to "auto" because it might be set to "default" by the babel handling
793                 // and this would not be correct for CJK
794                 if (h_inputencoding == "default")
795                         h_inputencoding = "auto";
796                 registerAutomaticallyLoadedPackage("CJK");
797         }
798
799         else if (name == "CJKutf8") {
800                 h_inputencoding = "UTF8";
801                 p.setEncoding("UTF-8");
802                 registerAutomaticallyLoadedPackage("CJKutf8");
803         }
804
805         else if (name == "fontenc") {
806                 h_fontencoding = getStringFromVector(options, ",");
807                 /* We could do the following for better round trip support,
808                  * but this makes the document less portable, so I skip it:
809                 if (h_fontencoding == lyxrc.fontenc)
810                         h_fontencoding = "global";
811                  */
812                 options.clear();
813         }
814
815         else if (name == "inputenc" || name == "luainputenc") {
816                 // h_inputencoding is only set when there is not more than one
817                 // inputenc option because otherwise h_inputencoding must be
818                 // set to "auto" (the default encoding of the document language)
819                 // Therefore check for the "," character.
820                 // It is also only set when there is not more than one babel
821                 // language option.
822                 if (!opts.empty()) {
823                         if (opts.find(",") == string::npos && one_language == true) {
824                                 h_inputencoding = opts;
825                                 // FIXME: if this line is used, tex2lyx swallows the next character
826                                 // in the file behind "{inputenc}"
827                                 //p.setEncoding(opts);
828                         } else {
829                                 h_preamble << "\\usepackage[" << opts << "}{" << name << "}\n";
830                                 // FIXME: enabling this introduces bug #8525
831                                 //p.setEncoding(options.back(), Encoding::inputenc);
832                         }
833                         options.clear();
834                 } else
835                         h_preamble << "\\usepackage{" << name << "}\n";         
836         }
837
838         else if (name == "srcltx") {
839                 h_output_sync = "1";
840                 if (!opts.empty()) {
841                         h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
842                         options.clear();
843                 } else
844                         h_output_sync_macro = "\\usepackage{srcltx}";
845         }
846
847         else if (is_known(name, known_old_language_packages)) {
848                 // known language packages from the times before babel
849                 // if they are found and not also babel, they will be used as
850                 // custom language package
851                 h_language_package = "\\usepackage{" + name + "}";
852         }
853
854         else if (name == "prettyref")
855                 ; // ignore this FIXME: Use the package separator mechanism instead
856
857         else if (name == "lyxskak") {
858                 // ignore this and its options
859                 const char * const o[] = {"ps", "mover", 0};
860                 delete_opt(options, o);
861         }
862
863         else if (is_known(name, known_lyx_packages) && options.empty()) {
864                 if (name == "splitidx")
865                         h_use_indices = "true";
866                 if (!in_lyx_preamble) {
867                         h_preamble << package_beg_sep << name
868                                    << package_mid_sep << "\\usepackage{"
869                                    << name << '}';
870                         if (p.next_token().cat() == catNewline ||
871                             (p.next_token().cat() == catSpace &&
872                              p.next_next_token().cat() == catNewline))
873                                 h_preamble << '\n';
874                         h_preamble << package_end_sep;
875                 }
876         }
877
878         else if (name == "geometry")
879                 handle_geometry(options);
880
881         else if (name == "subfig")
882                 ; // ignore this FIXME: Use the package separator mechanism instead
883
884         else if ((where = is_known(name, known_languages)))
885                 h_language = known_coded_languages[where - known_languages];
886
887         else if (name == "natbib") {
888                 h_biblio_style = "plainnat";
889                 h_cite_engine = "natbib";
890                 h_cite_engine_type = "authoryear";
891                 vector<string>::iterator it =
892                         find(options.begin(), options.end(), "authoryear");
893                 if (it != options.end())
894                         options.erase(it);
895                 else {
896                         it = find(options.begin(), options.end(), "numbers");
897                         if (it != options.end()) {
898                                 h_cite_engine_type = "numerical";
899                                 options.erase(it);
900                         }
901                 }
902         }
903
904         else if (name == "jurabib") {
905                 h_biblio_style = "jurabib";
906                 h_cite_engine = "jurabib";
907                 h_cite_engine_type = "authoryear";
908         }
909
910         else if (name == "hyperref")
911                 handle_hyperref(options);
912
913         else if (!in_lyx_preamble) {
914                 if (options.empty())
915                         h_preamble << "\\usepackage{" << name << '}';
916                 else {
917                         h_preamble << "\\usepackage[" << opts << "]{"
918                                    << name << '}';
919                         options.clear();
920                 }
921                 if (p.next_token().cat() == catNewline ||
922                     (p.next_token().cat() == catSpace &&
923                      p.next_next_token().cat() == catNewline))
924                         h_preamble << '\n';
925         }
926
927         // We need to do something with the options...
928         if (!options.empty())
929                 cerr << "Ignoring options '" << join(options, ",")
930                      << "' of package " << name << '.' << endl;
931
932         // remove the whitespace
933         p.skip_spaces();
934 }
935
936
937 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
938 {
939         while (p.good()) {
940                 Token t = p.get_token();
941                 if (t.cat() == catEscape &&
942                     is_known(t.cs(), known_if_commands))
943                         handle_if(p, in_lyx_preamble);
944                 else {
945                         if (!in_lyx_preamble)
946                                 h_preamble << t.asInput();
947                         if (t.cat() == catEscape && t.cs() == "fi")
948                                 return;
949                 }
950         }
951 }
952
953
954 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
955 {
956         // set the quote language
957         // LyX only knows the following quotes languages:
958         // english, swedish, german, polish, french and danish
959         // (quotes for "japanese" and "chinese-traditional" are missing because
960         //  they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
961         // conversion list taken from
962         // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
963         // (quotes for kazakh and interlingua are unknown)
964         // danish
965         if (is_known(h_language, known_danish_quotes_languages))
966                 h_quotes_language = "danish";
967         // french
968         else if (is_known(h_language, known_french_quotes_languages))
969                 h_quotes_language = "french";
970         // german
971         else if (is_known(h_language, known_german_quotes_languages))
972                 h_quotes_language = "german";
973         // polish
974         else if (is_known(h_language, known_polish_quotes_languages))
975                 h_quotes_language = "polish";
976         // swedish
977         else if (is_known(h_language, known_swedish_quotes_languages))
978                 h_quotes_language = "swedish";
979         //english
980         else if (is_known(h_language, known_english_quotes_languages))
981                 h_quotes_language = "english";
982
983         if (contains(h_float_placement, "H"))
984                 registerAutomaticallyLoadedPackage("float");
985         if (h_spacing != "single" && h_spacing != "default")
986                 registerAutomaticallyLoadedPackage("setspace");
987         if (h_use_packages["amsmath"] == "2") {
988                 // amsbsy and amstext are already provided by amsmath
989                 registerAutomaticallyLoadedPackage("amsbsy");
990                 registerAutomaticallyLoadedPackage("amstext");
991         }
992
993         // output the LyX file settings
994         os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
995            << "\\lyxformat " << LYX_FORMAT << '\n'
996            << "\\begin_document\n"
997            << "\\begin_header\n"
998            << "\\textclass " << h_textclass << "\n";
999         string const raw = subdoc ? empty_string() : h_preamble.str();
1000         if (!raw.empty()) {
1001                 os << "\\begin_preamble\n";
1002                 for (string::size_type i = 0; i < raw.size(); ++i) {
1003                         if (raw[i] == package_beg_sep) {
1004                                 // Here follows some package loading code that
1005                                 // must be skipped if the package is loaded
1006                                 // automatically.
1007                                 string::size_type j = raw.find(package_mid_sep, i);
1008                                 if (j == string::npos)
1009                                         return false;
1010                                 string::size_type k = raw.find(package_end_sep, j);
1011                                 if (k == string::npos)
1012                                         return false;
1013                                 string const package = raw.substr(i + 1, j - i - 1);
1014                                 string const replacement = raw.substr(j + 1, k - j - 1);
1015                                 if (auto_packages.find(package) == auto_packages.end())
1016                                         os << replacement;
1017                                 i = k;
1018                         } else
1019                                 os.put(raw[i]);
1020                 }
1021                 os << "\n\\end_preamble\n";
1022         }
1023         if (!h_options.empty())
1024                 os << "\\options " << h_options << "\n";
1025         os << "\\use_default_options " << h_use_default_options << "\n";
1026         if (!used_modules.empty()) {
1027                 os << "\\begin_modules\n";
1028                 vector<string>::const_iterator const end = used_modules.end();
1029                 vector<string>::const_iterator it = used_modules.begin();
1030                 for (; it != end; ++it)
1031                         os << *it << '\n';
1032                 os << "\\end_modules\n";
1033         }
1034         os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1035            << "\\language " << h_language << "\n"
1036            << "\\language_package " << h_language_package << "\n"
1037            << "\\inputencoding " << h_inputencoding << "\n"
1038            << "\\fontencoding " << h_fontencoding << "\n"
1039            << "\\font_roman " << h_font_roman << "\n"
1040            << "\\font_sans " << h_font_sans << "\n"
1041            << "\\font_typewriter " << h_font_typewriter << "\n"
1042            << "\\font_math " << h_font_math << "\n"
1043            << "\\font_default_family " << h_font_default_family << "\n"
1044            << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
1045            << "\\font_sc " << h_font_sc << "\n"
1046            << "\\font_osf " << h_font_osf << "\n"
1047            << "\\font_sf_scale " << h_font_sf_scale << "\n"
1048            << "\\font_tt_scale " << h_font_tt_scale << '\n';
1049         if (!h_font_cjk.empty())
1050                 os << "\\font_cjk " << h_font_cjk << '\n';
1051         os << "\\graphics " << h_graphics << '\n'
1052            << "\\default_output_format " << h_default_output_format << "\n"
1053            << "\\output_sync " << h_output_sync << "\n";
1054         if (h_output_sync == "1")
1055                 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1056         os << "\\bibtex_command " << h_bibtex_command << "\n"
1057            << "\\index_command " << h_index_command << "\n";
1058         if (!h_float_placement.empty())
1059                 os << "\\float_placement " << h_float_placement << "\n";
1060         os << "\\paperfontsize " << h_paperfontsize << "\n"
1061            << "\\spacing " << h_spacing << "\n"
1062            << "\\use_hyperref " << h_use_hyperref << '\n';
1063         if (h_use_hyperref == "true") {
1064                 if (!h_pdf_title.empty())
1065                         os << "\\pdf_title \"" << h_pdf_title << "\"\n";
1066                 if (!h_pdf_author.empty())
1067                         os << "\\pdf_author \"" << h_pdf_author << "\"\n";
1068                 if (!h_pdf_subject.empty())
1069                         os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
1070                 if (!h_pdf_keywords.empty())
1071                         os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
1072                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1073                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1074                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1075                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1076                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1077                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1078                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1079                       "\\pdf_backref " << h_pdf_backref << "\n"
1080                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1081                 if (!h_pdf_pagemode.empty())
1082                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1083                 if (!h_pdf_quoted_options.empty())
1084                         os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
1085         }
1086         os << "\\papersize " << h_papersize << "\n"
1087            << "\\use_geometry " << h_use_geometry << '\n';
1088         for (map<string, string>::const_iterator it = h_use_packages.begin();
1089              it != h_use_packages.end(); ++it)
1090                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1091         os << "\\cite_engine " << h_cite_engine << '\n'
1092            << "\\cite_engine_type " << h_cite_engine_type << '\n'
1093            << "\\biblio_style " << h_biblio_style << "\n"
1094            << "\\use_bibtopic " << h_use_bibtopic << "\n"
1095            << "\\use_indices " << h_use_indices << "\n"
1096            << "\\paperorientation " << h_paperorientation << '\n'
1097            << "\\suppress_date " << h_suppress_date << '\n'
1098            << "\\justification " << h_justification << '\n'
1099            << "\\use_refstyle " << h_use_refstyle << '\n';
1100         if (!h_fontcolor.empty())
1101                 os << "\\fontcolor " << h_fontcolor << '\n';
1102         if (!h_notefontcolor.empty())
1103                 os << "\\notefontcolor " << h_notefontcolor << '\n';
1104         if (!h_backgroundcolor.empty())
1105                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1106         if (!h_boxbgcolor.empty())
1107                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1108         os << "\\index " << h_index << '\n'
1109            << "\\shortcut " << h_shortcut << '\n'
1110            << "\\color " << h_color << '\n'
1111            << "\\end_index\n";
1112         os << h_margins
1113            << "\\secnumdepth " << h_secnumdepth << "\n"
1114            << "\\tocdepth " << h_tocdepth << "\n"
1115            << "\\paragraph_separation " << h_paragraph_separation << "\n";
1116         if (h_paragraph_separation == "skip")
1117                 os << "\\defskip " << h_defskip << "\n";
1118         else
1119                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1120         os << "\\quotes_language " << h_quotes_language << "\n"
1121            << "\\papercolumns " << h_papercolumns << "\n"
1122            << "\\papersides " << h_papersides << "\n"
1123            << "\\paperpagestyle " << h_paperpagestyle << "\n";
1124         if (!h_listings_params.empty())
1125                 os << "\\listings_params " << h_listings_params << "\n";
1126         os << "\\tracking_changes " << h_tracking_changes << "\n"
1127            << "\\output_changes " << h_output_changes << "\n"
1128            << "\\html_math_output " << h_html_math_output << "\n"
1129            << "\\html_css_as_file " << h_html_css_as_file << "\n"
1130            << "\\html_be_strict " << h_html_be_strict << "\n"
1131            << authors_
1132            << "\\end_header\n\n"
1133            << "\\begin_body\n";
1134         return true;
1135 }
1136
1137
1138 void Preamble::parse(Parser & p, string const & forceclass,
1139                      TeX2LyXDocClass & tc)
1140 {
1141         // initialize fixed types
1142         special_columns['D'] = 3;
1143         bool is_full_document = false;
1144         bool is_lyx_file = false;
1145         bool in_lyx_preamble = false;
1146
1147         // determine whether this is a full document or a fragment for inclusion
1148         while (p.good()) {
1149                 Token const & t = p.get_token();
1150
1151                 if (t.cat() == catEscape && t.cs() == "documentclass") {
1152                         is_full_document = true;
1153                         break;
1154                 }
1155         }
1156         p.reset();
1157
1158         while (is_full_document && p.good()) {
1159                 Token const & t = p.get_token();
1160
1161 #ifdef FILEDEBUG
1162                 cerr << "t: " << t << "\n";
1163 #endif
1164
1165                 //
1166                 // cat codes
1167                 //
1168                 if (!in_lyx_preamble &&
1169                     (t.cat() == catLetter ||
1170                      t.cat() == catSuper ||
1171                      t.cat() == catSub ||
1172                      t.cat() == catOther ||
1173                      t.cat() == catMath ||
1174                      t.cat() == catActive ||
1175                      t.cat() == catBegin ||
1176                      t.cat() == catEnd ||
1177                      t.cat() == catAlign ||
1178                      t.cat() == catParameter))
1179                         h_preamble << t.cs();
1180
1181                 else if (!in_lyx_preamble &&
1182                          (t.cat() == catSpace || t.cat() == catNewline))
1183                         h_preamble << t.asInput();
1184
1185                 else if (t.cat() == catComment) {
1186                         static regex const islyxfile("%% LyX .* created this file");
1187                         static regex const usercommands("User specified LaTeX commands");
1188
1189                         string const comment = t.asInput();
1190
1191                         // magically switch encoding default if it looks like XeLaTeX
1192                         static string const magicXeLaTeX =
1193                                 "% This document must be compiled with XeLaTeX ";
1194                         if (comment.size() > magicXeLaTeX.size()
1195                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1196                                   && h_inputencoding == "auto") {
1197                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
1198                                 h_inputencoding = "utf8";
1199                         }
1200                         smatch sub;
1201                         if (regex_search(comment, sub, islyxfile)) {
1202                                 is_lyx_file = true;
1203                                 in_lyx_preamble = true;
1204                         } else if (is_lyx_file
1205                                    && regex_search(comment, sub, usercommands))
1206                                 in_lyx_preamble = false;
1207                         else if (!in_lyx_preamble)
1208                                 h_preamble << t.asInput();
1209                 }
1210
1211                 else if (t.cs() == "pagestyle")
1212                         h_paperpagestyle = p.verbatim_item();
1213
1214                 else if (t.cs() == "setdefaultlanguage") {
1215                         xetex = true;
1216                         // We don't yet care about non-language variant options
1217                         // because LyX doesn't support this yet, see bug #8214
1218                         if (p.hasOpt()) {
1219                                 string langopts = p.getOpt();
1220                                 // check if the option contains a variant, if yes, extract it
1221                                 string::size_type pos_var = langopts.find("variant");
1222                                 string::size_type i = langopts.find(',', pos_var);
1223                                 string::size_type k = langopts.find('=', pos_var);
1224                                 if (pos_var != string::npos){
1225                                         string variant;
1226                                         if (i == string::npos)
1227                                                 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1228                                         else
1229                                                 variant = langopts.substr(k + 1, i - k - 1);
1230                                         h_language = variant;
1231                                 }
1232                                 p.verbatim_item();
1233                         } else
1234                                 h_language = p.verbatim_item();
1235                         //finally translate the poyglossia name to a LyX name
1236                         h_language = polyglossia2lyx(h_language);
1237                 }
1238
1239                 else if (t.cs() == "setotherlanguage") {
1240                         // We don't yet care about the option because LyX doesn't
1241                         // support this yet, see bug #8214
1242                         p.hasOpt() ? p.getOpt() : string();
1243                         p.verbatim_item();
1244                 }
1245
1246                 else if (t.cs() == "setmainfont") {
1247                         // we don't care about the option
1248                         p.hasOpt() ? p.getOpt() : string();
1249                         h_font_roman = p.getArg('{', '}');
1250                 }
1251
1252                 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1253                         // LyX currently only supports the scale option
1254                         string scale;
1255                         if (p.hasOpt()) {
1256                                 string fontopts = p.getArg('[', ']');
1257                                 // check if the option contains a scaling, if yes, extract it
1258                                 string::size_type pos = fontopts.find("Scale");
1259                                 if (pos != string::npos) {
1260                                         string::size_type i = fontopts.find(',', pos);
1261                                         if (i == string::npos)
1262                                                 scale_as_percentage(fontopts.substr(pos + 1), scale);
1263                                         else
1264                                                 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1265                                 }
1266                         }
1267                         if (t.cs() == "setsansfont") {
1268                                 if (!scale.empty())
1269                                         h_font_sf_scale = scale;
1270                                 h_font_sans = p.getArg('{', '}');
1271                         } else {
1272                                 if (!scale.empty())
1273                                         h_font_tt_scale = scale;
1274                                 h_font_typewriter = p.getArg('{', '}');
1275                         }
1276                 }
1277
1278                 else if (t.cs() == "date") {
1279                         string argument = p.getArg('{', '}');
1280                         if (argument.empty())
1281                                 h_suppress_date = "true";
1282                         else
1283                                 h_preamble << t.asInput() << '{' << argument << '}';
1284                 }
1285
1286                 else if (t.cs() == "color") {
1287                         string const space =
1288                                 (p.hasOpt() ? p.getOpt() : string());
1289                         string argument = p.getArg('{', '}');
1290                         // check the case that a standard color is used
1291                         if (space.empty() && is_known(argument, known_basic_colors)) {
1292                                 h_fontcolor = rgbcolor2code(argument);
1293                                 preamble.registerAutomaticallyLoadedPackage("color");
1294                         } else if (space.empty() && argument == "document_fontcolor")
1295                                 preamble.registerAutomaticallyLoadedPackage("color");
1296                         // check the case that LyX's document_fontcolor is defined
1297                         // but not used for \color
1298                         else {
1299                                 h_preamble << t.asInput();
1300                                 if (!space.empty())
1301                                         h_preamble << space;
1302                                 h_preamble << '{' << argument << '}';
1303                                 // the color might already be set because \definecolor
1304                                 // is parsed before this
1305                                 h_fontcolor = "";
1306                         }
1307                 }
1308
1309                 else if (t.cs() == "pagecolor") {
1310                         string argument = p.getArg('{', '}');
1311                         // check the case that a standard color is used
1312                         if (is_known(argument, known_basic_colors)) {
1313                                 h_backgroundcolor = rgbcolor2code(argument);
1314                         } else if (argument == "page_backgroundcolor")
1315                                 preamble.registerAutomaticallyLoadedPackage("color");
1316                         // check the case that LyX's page_backgroundcolor is defined
1317                         // but not used for \pagecolor
1318                         else {
1319                                 h_preamble << t.asInput() << '{' << argument << '}';
1320                                 // the color might already be set because \definecolor
1321                                 // is parsed before this
1322                                 h_backgroundcolor = "";
1323                         }
1324                 }
1325
1326                 else if (t.cs() == "makeatletter") {
1327                         // LyX takes care of this
1328                         p.setCatcode('@', catLetter);
1329                 }
1330
1331                 else if (t.cs() == "makeatother") {
1332                         // LyX takes care of this
1333                         p.setCatcode('@', catOther);
1334                 }
1335
1336                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1337                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1338                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
1339                                 || t.cs() == "DeclareRobustCommand"
1340                       || t.cs() == "DeclareRobustCommandx"
1341                                 || t.cs() == "ProvideTextCommandDefault"
1342                                 || t.cs() == "DeclareMathAccent") {
1343                         bool star = false;
1344                         if (p.next_token().character() == '*') {
1345                                 p.get_token();
1346                                 star = true;
1347                         }
1348                         string const name = p.verbatim_item();
1349                         string const opt1 = p.getFullOpt();
1350                         string const opt2 = p.getFullOpt();
1351                         string const body = p.verbatim_item();
1352                         // store the in_lyx_preamble setting
1353                         bool const was_in_lyx_preamble = in_lyx_preamble;
1354                         // font settings
1355                         if (name == "\\rmdefault")
1356                                 if (is_known(body, known_roman_fonts)) {
1357                                         h_font_roman = body;
1358                                         p.skip_spaces();
1359                                         in_lyx_preamble = true;
1360                                 }
1361                         if (name == "\\sfdefault")
1362                                 if (is_known(body, known_sans_fonts)) {
1363                                         h_font_sans = body;
1364                                         p.skip_spaces();
1365                                         in_lyx_preamble = true;
1366                                 }
1367                         if (name == "\\ttdefault")
1368                                 if (is_known(body, known_typewriter_fonts)) {
1369                                         h_font_typewriter = body;
1370                                         p.skip_spaces();
1371                                         in_lyx_preamble = true;
1372                                 }
1373                         if (name == "\\familydefault") {
1374                                 string family = body;
1375                                 // remove leading "\"
1376                                 h_font_default_family = family.erase(0,1);
1377                                 p.skip_spaces();
1378                                 in_lyx_preamble = true;
1379                         }
1380
1381                         if (name == "\\bfdefault")
1382                                 // LyX re-adds this if a kurier font is used
1383                                 if (is_known(h_font_sans, known_kurier_fonts) && body == "b") {
1384                                         p.skip_spaces();
1385                                         in_lyx_preamble = true;
1386                                 }
1387
1388                         // remove the lyxdot definition that is re-added by LyX
1389                         // if necessary
1390                         if (name == "\\lyxdot") {
1391                                 p.skip_spaces();
1392                                 in_lyx_preamble = true;
1393                         }
1394
1395                         // Add the command to the known commands
1396                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1397
1398                         // only non-lyxspecific stuff
1399                         if (!in_lyx_preamble) {
1400                                 ostringstream ss;
1401                                 ss << '\\' << t.cs();
1402                                 if (star)
1403                                         ss << '*';
1404                                 ss << '{' << name << '}' << opt1 << opt2
1405                                    << '{' << body << "}";
1406                                 h_preamble << ss.str();
1407 /*
1408                                 ostream & out = in_preamble ? h_preamble : os;
1409                                 out << "\\" << t.cs() << "{" << name << "}"
1410                                     << opts << "{" << body << "}";
1411 */
1412                         }
1413                         // restore the in_lyx_preamble setting
1414                         in_lyx_preamble = was_in_lyx_preamble;
1415                 }
1416
1417                 else if (t.cs() == "edef"){
1418                         // we only support this for kurier fonts
1419                         string const command = p.next_token().asInput();
1420                         p.get_token();
1421                         if (command == "\\sfdefault") {
1422                                 p.getArg('{', '}');
1423                                 if (h_font_sans == "kurier")
1424                                         h_font_sans = "kurier-condensed";
1425                                 if (h_font_sans == "kurierl")
1426                                         h_font_sans = "kurier-light-condensed";
1427                                 p.skip_spaces();
1428                         }
1429                         else
1430                                 h_preamble << "\\edef" << command << "{" << p.getArg('{', '}') << "}\n";
1431                 }
1432
1433                 else if (t.cs() == "documentclass") {
1434                         vector<string>::iterator it;
1435                         vector<string> opts = split_options(p.getArg('[', ']'));
1436                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1437                         delete_opt(opts, known_fontsizes);
1438                         // delete "pt" at the end
1439                         string::size_type i = h_paperfontsize.find("pt");
1440                         if (i != string::npos)
1441                                 h_paperfontsize.erase(i);
1442                         // The documentclass options are always parsed before the options
1443                         // of the babel call so that a language cannot overwrite the babel
1444                         // options.
1445                         handle_opt(opts, known_languages, h_language);
1446                         delete_opt(opts, known_languages);
1447
1448                         // paper orientation
1449                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1450                                 h_paperorientation = "landscape";
1451                                 opts.erase(it);
1452                         }
1453                         // paper sides
1454                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1455                                  != opts.end()) {
1456                                 h_papersides = "1";
1457                                 opts.erase(it);
1458                         }
1459                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1460                                  != opts.end()) {
1461                                 h_papersides = "2";
1462                                 opts.erase(it);
1463                         }
1464                         // paper columns
1465                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1466                                  != opts.end()) {
1467                                 h_papercolumns = "1";
1468                                 opts.erase(it);
1469                         }
1470                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1471                                  != opts.end()) {
1472                                 h_papercolumns = "2";
1473                                 opts.erase(it);
1474                         }
1475                         // paper sizes
1476                         // some size options are known to any document classes, other sizes
1477                         // are handled by the \geometry command of the geometry package
1478                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1479                         delete_opt(opts, known_class_paper_sizes);
1480                         // the remaining options
1481                         h_options = join(opts, ",");
1482                         // FIXME This does not work for classes that have a
1483                         //       different name in LyX than in LaTeX
1484                         h_textclass = p.getArg('{', '}');
1485                         p.skip_spaces();
1486                 }
1487
1488                 else if (t.cs() == "usepackage") {
1489                         string const options = p.getArg('[', ']');
1490                         string const name = p.getArg('{', '}');
1491                         vector<string> vecnames;
1492                         split(name, vecnames, ',');
1493                         vector<string>::const_iterator it  = vecnames.begin();
1494                         vector<string>::const_iterator end = vecnames.end();
1495                         for (; it != end; ++it)
1496                                 handle_package(p, trimSpaceAndEol(*it), options,
1497                                                in_lyx_preamble);
1498                 }
1499
1500                 else if (t.cs() == "inputencoding") {
1501                         string const encoding = p.getArg('{','}');
1502                         h_inputencoding = encoding;
1503                         p.setEncoding(encoding, Encoding::inputenc);
1504                 }
1505
1506                 else if (t.cs() == "newenvironment") {
1507                         string const name = p.getArg('{', '}');
1508                         string const opt1 = p.getFullOpt();
1509                         string const opt2 = p.getFullOpt();
1510                         string const beg = p.verbatim_item();
1511                         string const end = p.verbatim_item();
1512                         if (!in_lyx_preamble) {
1513                                 h_preamble << "\\newenvironment{" << name
1514                                            << '}' << opt1 << opt2 << '{'
1515                                            << beg << "}{" << end << '}';
1516                         }
1517                         add_known_environment(name, opt1, !opt2.empty(),
1518                                               from_utf8(beg), from_utf8(end));
1519
1520                 }
1521
1522                 else if (t.cs() == "newtheorem") {
1523                         string const name = p.getArg('{', '}');
1524                         string const opt1 = p.getFullOpt();
1525                         string const opt2 = p.getFullOpt();
1526                         string const body = p.verbatim_item();
1527                         string const opt3 = p.getFullOpt();
1528
1529                         add_known_theorem(name, opt1, !opt2.empty(),
1530                                 from_utf8("\\newtheorem{" + name + '}' +
1531                                           opt1 + opt2 + '{' + body + '}' + opt3));
1532
1533                         if (!in_lyx_preamble)
1534                                 h_preamble << "\\newtheorem{" << name << '}'
1535                                            << opt1 << opt2 << '{' << '}' << opt3;
1536                 }
1537
1538                 else if (t.cs() == "def") {
1539                         string name = p.get_token().cs();
1540                         // In fact, name may be more than the name:
1541                         // In the test case of bug 8116
1542                         // name == "csname SF@gobble@opt \endcsname".
1543                         // Therefore, we need to use asInput() instead of cs().
1544                         while (p.next_token().cat() != catBegin)
1545                                 name += p.get_token().asInput();
1546                         if (!in_lyx_preamble)
1547                                 h_preamble << "\\def\\" << name << '{'
1548                                            << p.verbatim_item() << "}";
1549                 }
1550
1551                 else if (t.cs() == "newcolumntype") {
1552                         string const name = p.getArg('{', '}');
1553                         trimSpaceAndEol(name);
1554                         int nargs = 0;
1555                         string opts = p.getOpt();
1556                         if (!opts.empty()) {
1557                                 istringstream is(string(opts, 1));
1558                                 is >> nargs;
1559                         }
1560                         special_columns[name[0]] = nargs;
1561                         h_preamble << "\\newcolumntype{" << name << "}";
1562                         if (nargs)
1563                                 h_preamble << "[" << nargs << "]";
1564                         h_preamble << "{" << p.verbatim_item() << "}";
1565                 }
1566
1567                 else if (t.cs() == "setcounter") {
1568                         string const name = p.getArg('{', '}');
1569                         string const content = p.getArg('{', '}');
1570                         if (name == "secnumdepth")
1571                                 h_secnumdepth = content;
1572                         else if (name == "tocdepth")
1573                                 h_tocdepth = content;
1574                         else
1575                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1576                 }
1577
1578                 else if (t.cs() == "setlength") {
1579                         string const name = p.verbatim_item();
1580                         string const content = p.verbatim_item();
1581                         // the paragraphs are only not indented when \parindent is set to zero
1582                         if (name == "\\parindent" && content != "") {
1583                                 if (content[0] == '0')
1584                                         h_paragraph_separation = "skip";
1585                                 else
1586                                         h_paragraph_indentation = translate_len(content);
1587                         } else if (name == "\\parskip") {
1588                                 if (content == "\\smallskipamount")
1589                                         h_defskip = "smallskip";
1590                                 else if (content == "\\medskipamount")
1591                                         h_defskip = "medskip";
1592                                 else if (content == "\\bigskipamount")
1593                                         h_defskip = "bigskip";
1594                                 else
1595                                         h_defskip = content;
1596                         } else
1597                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1598                 }
1599
1600                 else if (t.cs() == "onehalfspacing")
1601                         h_spacing = "onehalf";
1602
1603                 else if (t.cs() == "doublespacing")
1604                         h_spacing = "double";
1605
1606                 else if (t.cs() == "setstretch")
1607                         h_spacing = "other " + p.verbatim_item();
1608
1609                 else if (t.cs() == "synctex") {
1610                         // the scheme is \synctex=value
1611                         // where value can only be "1" or "-1"
1612                         h_output_sync = "1";
1613                         // there can be any character behind the value (e.g. a linebreak or a '\'
1614                         // therefore we extract it char by char
1615                         p.get_token();
1616                         string value = p.get_token().asInput();
1617                         if (value == "-")
1618                                 value += p.get_token().asInput();
1619                         h_output_sync_macro = "\\synctex=" + value;
1620                 }
1621
1622                 else if (t.cs() == "begin") {
1623                         string const name = p.getArg('{', '}');
1624                         if (name == "document")
1625                                 break;
1626                         h_preamble << "\\begin{" << name << "}";
1627                 }
1628
1629                 else if (t.cs() == "geometry") {
1630                         vector<string> opts = split_options(p.getArg('{', '}'));
1631                         handle_geometry(opts);
1632                 }
1633
1634                 else if (t.cs() == "definecolor") {
1635                         string const color = p.getArg('{', '}');
1636                         string const space = p.getArg('{', '}');
1637                         string const value = p.getArg('{', '}');
1638                         if (color == "document_fontcolor" && space == "rgb") {
1639                                 RGBColor c(RGBColorFromLaTeX(value));
1640                                 h_fontcolor = X11hexname(c);
1641                         } else if (color == "note_fontcolor" && space == "rgb") {
1642                                 RGBColor c(RGBColorFromLaTeX(value));
1643                                 h_notefontcolor = X11hexname(c);
1644                         } else if (color == "page_backgroundcolor" && space == "rgb") {
1645                                 RGBColor c(RGBColorFromLaTeX(value));
1646                                 h_backgroundcolor = X11hexname(c);
1647                         } else if (color == "shadecolor" && space == "rgb") {
1648                                 RGBColor c(RGBColorFromLaTeX(value));
1649                                 h_boxbgcolor = X11hexname(c);
1650                         } else {
1651                                 h_preamble << "\\definecolor{" << color
1652                                            << "}{" << space << "}{" << value
1653                                            << '}';
1654                         }
1655                 }
1656
1657                 else if (t.cs() == "bibliographystyle")
1658                         h_biblio_style = p.verbatim_item();
1659
1660                 else if (t.cs() == "jurabibsetup") {
1661                         // FIXME p.getArg('{', '}') is most probably wrong (it
1662                         //       does not handle nested braces).
1663                         //       Use p.verbatim_item() instead.
1664                         vector<string> jurabibsetup =
1665                                 split_options(p.getArg('{', '}'));
1666                         // add jurabibsetup to the jurabib package options
1667                         add_package("jurabib", jurabibsetup);
1668                         if (!jurabibsetup.empty()) {
1669                                 h_preamble << "\\jurabibsetup{"
1670                                            << join(jurabibsetup, ",") << '}';
1671                         }
1672                 }
1673
1674                 else if (t.cs() == "hypersetup") {
1675                         vector<string> hypersetup =
1676                                 split_options(p.verbatim_item());
1677                         // add hypersetup to the hyperref package options
1678                         handle_hyperref(hypersetup);
1679                         if (!hypersetup.empty()) {
1680                                 h_preamble << "\\hypersetup{"
1681                                            << join(hypersetup, ",") << '}';
1682                         }
1683                 }
1684
1685                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1686                         // prevent misparsing of \usepackage if it is used
1687                         // as an argument (see e.g. our own output of
1688                         // \@ifundefined above)
1689                         string const arg1 = p.verbatim_item();
1690                         string const arg2 = p.verbatim_item();
1691                         string const arg3 = p.verbatim_item();
1692                         // test case \@ifundefined{date}{}{\date{}}
1693                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
1694                             arg2.empty() && arg3 == "\\date{}") {
1695                                 h_suppress_date = "true";
1696                         // older tex2lyx versions did output
1697                         // \@ifundefined{definecolor}{\usepackage{color}}{}
1698                         } else if (t.cs() == "@ifundefined" &&
1699                                    arg1 == "definecolor" &&
1700                                    arg2 == "\\usepackage{color}" &&
1701                                    arg3.empty()) {
1702                                 if (!in_lyx_preamble)
1703                                         h_preamble << package_beg_sep
1704                                                    << "color"
1705                                                    << package_mid_sep
1706                                                    << "\\@ifundefined{definecolor}{color}{}"
1707                                                    << package_end_sep;
1708                         // test for case
1709                         //\@ifundefined{showcaptionsetup}{}{%
1710                         // \PassOptionsToPackage{caption=false}{subfig}}
1711                         // that LyX uses for subfloats
1712                         } else if (t.cs() == "@ifundefined" &&
1713                                    arg1 == "showcaptionsetup" && arg2.empty()
1714                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1715                                 ; // do nothing
1716                         } else if (!in_lyx_preamble) {
1717                                 h_preamble << t.asInput()
1718                                            << '{' << arg1 << '}'
1719                                            << '{' << arg2 << '}'
1720                                            << '{' << arg3 << '}';
1721                         }
1722                 }
1723
1724                 else if (is_known(t.cs(), known_if_commands)) {
1725                         // must not parse anything in conditional code, since
1726                         // LyX would output the parsed contents unconditionally
1727                         if (!in_lyx_preamble)
1728                                 h_preamble << t.asInput();
1729                         handle_if(p, in_lyx_preamble);
1730                 }
1731
1732                 else if (!t.cs().empty() && !in_lyx_preamble)
1733                         h_preamble << '\\' << t.cs();
1734         }
1735
1736         // remove the whitespace
1737         p.skip_spaces();
1738
1739         // Force textclass if the user wanted it
1740         if (!forceclass.empty())
1741                 h_textclass = forceclass;
1742         tc.setName(h_textclass);
1743         if (!tc.load()) {
1744                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1745                 exit(EXIT_FAILURE);
1746         }
1747         if (h_papersides.empty()) {
1748                 ostringstream ss;
1749                 ss << tc.sides();
1750                 h_papersides = ss.str();
1751         }
1752
1753         // If the CJK package is used we cannot set the document language from
1754         // the babel options. Instead, we guess which language is used most
1755         // and set this one.
1756         default_language = h_language;
1757         if (is_full_document &&
1758             (auto_packages.find("CJK") != auto_packages.end() ||
1759              auto_packages.find("CJKutf8") != auto_packages.end())) {
1760                 p.pushPosition();
1761                 h_language = guessLanguage(p, default_language);
1762                 p.popPosition();
1763         }
1764 }
1765
1766
1767 string babel2lyx(string const & language)
1768 {
1769         char const * const * where = is_known(language, known_languages);
1770         if (where)
1771                 return known_coded_languages[where - known_languages];
1772         return language;
1773 }
1774
1775
1776 string Preamble::polyglossia2lyx(string const & language)
1777 {
1778         char const * const * where = is_known(language, polyglossia_languages);
1779         if (where)
1780                 return coded_polyglossia_languages[where - polyglossia_languages];
1781         return language;
1782 }
1783
1784
1785 string rgbcolor2code(string const & name)
1786 {
1787         char const * const * where = is_known(name, known_basic_colors);
1788         if (where) {
1789                 // "red", "green" etc
1790                 return known_basic_color_codes[where - known_basic_colors];
1791         }
1792         // "255,0,0", "0,255,0" etc
1793         RGBColor c(RGBColorFromLaTeX(name));
1794         return X11hexname(c);
1795 }
1796
1797 // }])
1798
1799
1800 } // namespace lyx