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