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