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