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