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