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