]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
8873d3ff97a08781f1efb5b8ee3d19f0c6533cf1
[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 /**
51  * known babel language names (including synonyms)
52  * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
53  * please keep this in sync with known_coded_languages line by line!
54  */
55 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
56 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
57 "bahasam", "basque", "belarusian", "brazil", "brazilian", "breton", "british",
58 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
59 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
60 "french", "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb",
61 "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
62 "irish", "italian", "japanese", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
63 "lowersorbian", "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian",
64 "newzealand", "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
65 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
66 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
67 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
68 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
69 0};
70
71 /**
72  * the same as known_languages with .lyx names
73  * please keep this in sync with known_languages line by line!
74  */
75 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
76 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
77 "bahasam", "basque", "belarusian", "brazilian", "brazilian", "breton", "british",
78 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
79 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
80 "french", "french", "french", "french", "galician", "german", "german",
81 "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
82 "irish", "italian", "japanese", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
83 "lowersorbian", "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian",
84 "newzealand", "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
85 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
86 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
87 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
88 "uppersorbian", "uppersorbian", "english", "english", "vietnamese", "welsh",
89 0};
90
91 /**
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", "babel", "booktabs", "calc", "CJK", "color", "float",
209 "fontspec", "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_bibtex_command          = "default";
438         h_cite_engine             = "basic";
439         h_cite_engine_type        = "numerical";
440         h_color                   = "#008000";
441         h_defskip                 = "medskip";
442         //h_float_placement;
443         //h_fontcolor;
444         h_fontencoding            = "default";
445         h_font_roman              = "default";
446         h_font_sans               = "default";
447         h_font_typewriter         = "default";
448         h_font_default_family     = "default";
449         h_use_non_tex_fonts       = "false";
450         h_font_sc                 = "false";
451         h_font_osf                = "false";
452         h_font_sf_scale           = "100";
453         h_font_tt_scale           = "100";
454         h_graphics                = "default";
455         h_default_output_format   = "default";
456         h_html_be_strict          = "false";
457         h_html_css_as_file        = "0";
458         h_html_math_output        = "0";
459         h_index                   = "Index";
460         h_index_command           = "default";
461         h_inputencoding           = "auto";
462         h_justification           = "true";
463         h_language                = "english";
464         h_language_package        = "none";
465         //h_listings_params;
466         h_maintain_unincluded_children = "false";
467         //h_margins;
468         //h_notefontcolor;
469         //h_options;
470         h_output_changes          = "false";
471         h_output_sync             = "0";
472         //h_output_sync_macro
473         h_papercolumns            = "1";
474         h_paperfontsize           = "default";
475         h_paperorientation        = "portrait";
476         h_paperpagestyle          = "default";
477         //h_papersides;
478         h_papersize               = "default";
479         h_paragraph_indentation   = "default";
480         h_paragraph_separation    = "indent";
481         //h_pdf_title;
482         //h_pdf_author;
483         //h_pdf_subject;
484         //h_pdf_keywords;
485         h_pdf_bookmarks           = "1";
486         h_pdf_bookmarksnumbered   = "0";
487         h_pdf_bookmarksopen       = "0";
488         h_pdf_bookmarksopenlevel  = "1";
489         h_pdf_breaklinks          = "0";
490         h_pdf_pdfborder           = "0";
491         h_pdf_colorlinks          = "0";
492         h_pdf_backref             = "section";
493         h_pdf_pdfusetitle         = "1";
494         //h_pdf_pagemode;
495         //h_pdf_quoted_options;
496         h_quotes_language         = "english";
497         h_secnumdepth             = "3";
498         h_shortcut                = "idx";
499         h_spacing                 = "single";
500         h_suppress_date           = "false";
501         h_textclass               = "article";
502         h_tocdepth                = "3";
503         h_tracking_changes        = "false";
504         h_use_bibtopic            = "false";
505         h_use_indices             = "false";
506         h_use_geometry            = "false";
507         h_use_default_options     = "false";
508         h_use_hyperref            = "0";
509         h_use_refstyle            = "1";
510         h_use_packages["amsmath"]    = "1";
511         h_use_packages["amssymb"]    = "1";
512         h_use_packages["esint"]      = "1";
513         h_use_packages["mhchem"]     = "1";
514         h_use_packages["mathdots"]   = "1";
515         h_use_packages["mathtools"]  = "1";
516         h_use_packages["undertilde"] = "1";
517 }
518
519
520 void Preamble::handle_hyperref(vector<string> & options)
521 {
522         // FIXME swallow inputencoding changes that might surround the
523         //       hyperref setup if it was written by LyX
524         h_use_hyperref = "1";
525         // swallow "unicode=true", since LyX does always write that
526         vector<string>::iterator it =
527                 find(options.begin(), options.end(), "unicode=true");
528         if (it != options.end())
529                 options.erase(it);
530         it = find(options.begin(), options.end(), "pdfusetitle");
531         if (it != options.end()) {
532                 h_pdf_pdfusetitle = "1";
533                 options.erase(it);
534         }
535         string bookmarks = process_keyval_opt(options, "bookmarks");
536         if (bookmarks == "true")
537                 h_pdf_bookmarks = "1";
538         else if (bookmarks == "false")
539                 h_pdf_bookmarks = "0";
540         if (h_pdf_bookmarks == "1") {
541                 string bookmarksnumbered =
542                         process_keyval_opt(options, "bookmarksnumbered");
543                 if (bookmarksnumbered == "true")
544                         h_pdf_bookmarksnumbered = "1";
545                 else if (bookmarksnumbered == "false")
546                         h_pdf_bookmarksnumbered = "0";
547                 string bookmarksopen =
548                         process_keyval_opt(options, "bookmarksopen");
549                 if (bookmarksopen == "true")
550                         h_pdf_bookmarksopen = "1";
551                 else if (bookmarksopen == "false")
552                         h_pdf_bookmarksopen = "0";
553                 if (h_pdf_bookmarksopen == "1") {
554                         string bookmarksopenlevel =
555                                 process_keyval_opt(options, "bookmarksopenlevel");
556                         if (!bookmarksopenlevel.empty())
557                                 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
558                 }
559         }
560         string breaklinks = process_keyval_opt(options, "breaklinks");
561         if (breaklinks == "true")
562                 h_pdf_breaklinks = "1";
563         else if (breaklinks == "false")
564                 h_pdf_breaklinks = "0";
565         string pdfborder = process_keyval_opt(options, "pdfborder");
566         if (pdfborder == "{0 0 0}")
567                 h_pdf_pdfborder = "1";
568         else if (pdfborder == "{0 0 1}")
569                 h_pdf_pdfborder = "0";
570         string backref = process_keyval_opt(options, "backref");
571         if (!backref.empty())
572                 h_pdf_backref = backref;
573         string colorlinks = process_keyval_opt(options, "colorlinks");
574         if (colorlinks == "true")
575                 h_pdf_colorlinks = "1";
576         else if (colorlinks == "false")
577                 h_pdf_colorlinks = "0";
578         string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
579         if (!pdfpagemode.empty())
580                 h_pdf_pagemode = pdfpagemode;
581         string pdftitle = process_keyval_opt(options, "pdftitle");
582         if (!pdftitle.empty()) {
583                 h_pdf_title = remove_braces(pdftitle);
584         }
585         string pdfauthor = process_keyval_opt(options, "pdfauthor");
586         if (!pdfauthor.empty()) {
587                 h_pdf_author = remove_braces(pdfauthor);
588         }
589         string pdfsubject = process_keyval_opt(options, "pdfsubject");
590         if (!pdfsubject.empty())
591                 h_pdf_subject = remove_braces(pdfsubject);
592         string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
593         if (!pdfkeywords.empty())
594                 h_pdf_keywords = remove_braces(pdfkeywords);
595         if (!options.empty()) {
596                 if (!h_pdf_quoted_options.empty())
597                         h_pdf_quoted_options += ',';
598                 h_pdf_quoted_options += join(options, ",");
599                 options.clear();
600         }
601 }
602
603
604 void Preamble::handle_geometry(vector<string> & options)
605 {
606         h_use_geometry = "true";
607         vector<string>::iterator it;
608         // paper orientation
609         if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
610                 h_paperorientation = "landscape";
611                 options.erase(it);
612         }
613         // paper size
614         // keyval version: "paper=letter"
615         string paper = process_keyval_opt(options, "paper");
616         if (!paper.empty())
617                 h_papersize = paper + "paper";
618         // alternative version: "letterpaper"
619         handle_opt(options, known_paper_sizes, h_papersize);
620         delete_opt(options, known_paper_sizes);
621         // page margins
622         char const * const * margin = known_paper_margins;
623         for (; *margin; ++margin) {
624                 string value = process_keyval_opt(options, *margin);
625                 if (!value.empty()) {
626                         int k = margin - known_paper_margins;
627                         string name = known_coded_paper_margins[k];
628                         h_margins += '\\' + name + ' ' + value + '\n';
629                 }
630         }
631 }
632
633
634 void Preamble::handle_package(Parser &p, string const & name,
635                               string const & opts, bool in_lyx_preamble)
636 {
637         vector<string> options = split_options(opts);
638         add_package(name, options);
639         string scale;
640
641         if (is_known(name, known_xetex_packages)) {
642                 xetex = true;
643                 h_use_non_tex_fonts = "true";
644                 registerAutomaticallyLoadedPackage("fontspec");
645                 if (h_inputencoding == "auto")
646                         p.setEncoding("utf8");
647         }
648
649         // roman fonts
650         if (is_known(name, known_roman_fonts)) {
651                 h_font_roman = name;
652                 p.skip_spaces();
653         }
654
655         if (name == "fourier") {
656                 h_font_roman = "utopia";
657                 // when font uses real small capitals
658                 if (opts == "expert")
659                         h_font_sc = "true";
660         }
661
662         else if (name == "mathpazo")
663                 h_font_roman = "palatino";
664
665         else if (name == "mathptmx")
666                 h_font_roman = "times";
667
668         // sansserif fonts
669         if (is_known(name, known_sans_fonts)) {
670                 h_font_sans = name;
671                 if (!opts.empty()) {
672                         scale = opts;
673                         h_font_sf_scale = scale_as_percentage(scale);
674                 }
675         }
676
677         // typewriter fonts
678         if (is_known(name, known_typewriter_fonts)) {
679                 // fourier can be set as roman font _only_
680                 // fourier as typewriter is handled in handling of \ttdefault
681                 if (name != "fourier") {
682                         h_font_typewriter = name;
683                         if (!opts.empty()) {
684                                 scale = opts;
685                                 h_font_tt_scale = scale_as_percentage(scale);
686                         }
687                 }
688         }
689
690         // font uses old-style figure
691         if (name == "eco")
692                 h_font_osf = "true";
693
694         // after the detection and handling of special cases, we can remove the
695         // fonts, otherwise they would appear in the preamble, see bug #7856
696         if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
697                 ||      is_known(name, known_typewriter_fonts))
698                 ;
699
700         else if (name == "amsmath" || name == "amssymb" ||
701                  name == "esint" || name == "mhchem" || name == "mathdots" ||
702                  name == "mathtools" || name == "undertilde")
703                 h_use_packages[name] = "2";
704
705         else if (name == "babel") {
706                 h_language_package = "default";
707                 // One might think we would have to do nothing if babel is loaded
708                 // without any options to prevent pollution of the preamble with this
709                 // babel call in every roundtrip.
710                 // But the user could have defined babel-specific things afterwards. So
711                 // we need to keep it in the preamble to prevent cases like bug #7861.
712                 if (!opts.empty()) {
713                         // check if more than one option was used - used later for inputenc
714                         if (options.begin() != options.end() - 1)
715                                 one_language = false;
716                         // babel takes the last language of the option of its \usepackage
717                         // call as document language. If there is no such language option, the
718                         // last language in the documentclass options is used.
719                         handle_opt(options, known_languages, h_language);
720                         // translate the babel name to a LyX name
721                         h_language = babel2lyx(h_language);
722                         // for Japanese we assume EUC-JP as encoding
723                         // but we cannot determine the exact encoding and thus output also a note
724                         if (h_language == "japanese") {
725                                 h_inputencoding = "euc";
726                                 p.setEncoding("EUC-JP");
727                                 is_nonCJKJapanese = true;
728                                 // in this case babel can be removed from the preamble
729                                 registerAutomaticallyLoadedPackage("babel");
730                         } else {
731                                 // If babel is called with options, LyX puts them by default into the
732                                 // document class options. This works for most languages, except
733                                 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
734                                 // perhaps in future others.
735                                 // Therefore keep the babel call as it is as the user might have
736                                 // reasons for it.
737                                 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
738                         }
739                         delete_opt(options, known_languages);
740                 }
741                 else
742                         h_preamble << "\\usepackage{babel}\n";
743         }
744
745         else if (name == "polyglossia") {
746                 h_language_package = "default";
747                 h_default_output_format = "pdf4";
748                 h_use_non_tex_fonts = "true";
749                 xetex = true;
750                 registerAutomaticallyLoadedPackage("xunicode");
751                 if (h_inputencoding == "auto")
752                         p.setEncoding("utf8");
753         }
754
755         else if (name == "CJK") {
756                 // It is impossible to determine the document language if CJK is used.
757                 // All we can do is to notify the user that he has to set this by hisself.
758                 have_CJK = true;
759                 // set the encoding to "auto" because it might be set to "default" by the babel handling
760                 // and this would not be correct for CJK
761                 h_inputencoding = "auto";
762                 registerAutomaticallyLoadedPackage("CJK");
763         }
764
765         else if (name == "fontenc") {
766                 h_fontencoding = getStringFromVector(options, ",");
767                 /* We could do the following for better round trip support,
768                  * but this makes the document less portable, so I skip it:
769                 if (h_fontencoding == lyxrc.fontenc)
770                         h_fontencoding = "global";
771                  */
772                 options.clear();
773         }
774
775         else if (name == "inputenc" || name == "luainputenc") {
776                 // h_inputencoding is only set when there is not more than one
777                 // inputenc option because otherwise h_inputencoding must be
778                 // set to "auto" (the default encoding of the document language)
779                 // Therefore check for the "," character.
780                 // It is also only set when there is not more than one babel
781                 // language option.
782                 if (opts.find(",") == string::npos && one_language == true)
783                         h_inputencoding = opts;
784                 if (!options.empty())
785                         p.setEncoding(options.back());
786                 options.clear();
787         }
788
789         else if (name == "srcltx") {
790                 h_output_sync = "1";
791                 if (!opts.empty()) {
792                         h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
793                         options.clear();
794                 } else
795                         h_output_sync_macro = "\\usepackage{srcltx}";
796         }
797
798         else if (is_known(name, known_old_language_packages)) {
799                 // known language packages from the times before babel
800                 // if they are found and not also babel, they will be used as
801                 // custom language package
802                 h_language_package = "\\usepackage{" + name + "}";
803         }
804
805         else if (name == "prettyref")
806                 ; // ignore this FIXME: Use the package separator mechanism instead
807
808         else if (name == "lyxskak") {
809                 // ignore this and its options
810                 const char * const o[] = {"ps", "mover", 0};
811                 delete_opt(options, o);
812         }
813
814         else if (is_known(name, known_lyx_packages) && options.empty()) {
815                 if (name == "splitidx")
816                         h_use_indices = "true";
817                 if (!in_lyx_preamble)
818                         h_preamble << package_beg_sep << name
819                                    << package_mid_sep << "\\usepackage{"
820                                    << name << "}\n" << package_end_sep;
821         }
822
823         else if (name == "geometry")
824                 handle_geometry(options);
825
826         else if (name == "subfig")
827                 ; // ignore this FIXME: Use the package separator mechanism instead
828
829         else if (is_known(name, known_languages))
830                 h_language = name;
831
832         else if (name == "natbib") {
833                 h_biblio_style = "plainnat";
834                 h_cite_engine = "natbib";
835                 h_cite_engine_type = "authoryear";
836                 vector<string>::iterator it =
837                         find(options.begin(), options.end(), "authoryear");
838                 if (it != options.end())
839                         options.erase(it);
840                 else {
841                         it = find(options.begin(), options.end(), "numbers");
842                         if (it != options.end()) {
843                                 h_cite_engine_type = "numerical";
844                                 options.erase(it);
845                         }
846                 }
847         }
848
849         else if (name == "jurabib") {
850                 h_biblio_style = "jurabib";
851                 h_cite_engine = "jurabib";
852                 h_cite_engine_type = "authoryear";
853         }
854
855         else if (name == "hyperref")
856                 handle_hyperref(options);
857
858         else if (!in_lyx_preamble) {
859                 if (options.empty())
860                         h_preamble << "\\usepackage{" << name << "}\n";
861                 else {
862                         h_preamble << "\\usepackage[" << opts << "]{"
863                                    << name << "}\n";
864                         options.clear();
865                 }
866         }
867
868         // We need to do something with the options...
869         if (!options.empty())
870                 cerr << "Ignoring options '" << join(options, ",")
871                      << "' of package " << name << '.' << endl;
872
873         // remove the whitespace
874         p.skip_spaces();
875 }
876
877
878 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
879 {
880         while (p.good()) {
881                 Token t = p.get_token();
882                 if (t.cat() == catEscape &&
883                     is_known(t.cs(), known_if_commands))
884                         handle_if(p, in_lyx_preamble);
885                 else {
886                         if (!in_lyx_preamble)
887                                 h_preamble << t.asInput();
888                         if (t.cat() == catEscape && t.cs() == "fi")
889                                 return;
890                 }
891         }
892 }
893
894
895 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
896 {
897         // set the quote language
898         // LyX only knows the following quotes languages:
899         // english, swedish, german, polish, french and danish
900         // (quotes for "japanese" and "chinese-traditional" are missing because
901         //  they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
902         // conversion list taken from
903         // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
904         // (quotes for kazakh and interlingua are unknown)
905         // danish
906         if (h_language == "danish")
907                 h_quotes_language = "danish";
908         // french
909         else if (is_known(h_language, known_french_quotes_languages))
910                 h_quotes_language = "french";
911         // german
912         else if (is_known(h_language, known_german_quotes_languages))
913                 h_quotes_language = "german";
914         // polish
915         else if (is_known(h_language, known_polish_quotes_languages))
916                 h_quotes_language = "polish";
917         // swedish
918         else if (is_known(h_language, known_swedish_quotes_languages))
919                 h_quotes_language = "swedish";
920         //english
921         else if (is_known(h_language, known_english_quotes_languages))
922                 h_quotes_language = "english";
923
924         if (contains(h_float_placement, "H"))
925                 registerAutomaticallyLoadedPackage("float");
926         if (h_spacing != "single" && h_spacing != "default")
927                 registerAutomaticallyLoadedPackage("setspace");
928         if (h_use_packages["amsmath"] == "2") {
929                 // amsbsy and amstext are already provided by amsmath
930                 registerAutomaticallyLoadedPackage("amsbsy");
931                 registerAutomaticallyLoadedPackage("amstext");
932         }
933
934         // output the LyX file settings
935         os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
936            << "\\lyxformat " << LYX_FORMAT << '\n'
937            << "\\begin_document\n"
938            << "\\begin_header\n"
939            << "\\textclass " << h_textclass << "\n";
940         string const raw = subdoc ? empty_string() : h_preamble.str();
941         if (!raw.empty()) {
942                 os << "\\begin_preamble\n";
943                 for (string::size_type i = 0; i < raw.size(); ++i) {
944                         if (raw[i] == package_beg_sep) {
945                                 // Here follows some package loading code that
946                                 // must be skipped if the package is loaded
947                                 // automatically.
948                                 string::size_type j = raw.find(package_mid_sep, i);
949                                 if (j == string::npos)
950                                         return false;
951                                 string::size_type k = raw.find(package_end_sep, j);
952                                 if (k == string::npos)
953                                         return false;
954                                 string const package = raw.substr(i + 1, j - i - 1);
955                                 string const replacement = raw.substr(j + 1, k - j - 1);
956                                 if (auto_packages.find(package) == auto_packages.end())
957                                         os << replacement;
958                                 i = k;
959                         } else
960                                 os.put(raw[i]);
961                 }
962                 os << "\n\\end_preamble\n";
963         }
964         if (!h_options.empty())
965                 os << "\\options " << h_options << "\n";
966         os << "\\use_default_options " << h_use_default_options << "\n";
967         if (!used_modules.empty()) {
968                 os << "\\begin_modules\n";
969                 vector<string>::const_iterator const end = used_modules.end();
970                 vector<string>::const_iterator it = used_modules.begin();
971                 for (; it != end; ++it)
972                         os << *it << '\n';
973                 os << "\\end_modules\n";
974         }
975         os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
976            << "\\language " << h_language << "\n"
977            << "\\language_package " << h_language_package << "\n"
978            << "\\inputencoding " << h_inputencoding << "\n"
979            << "\\fontencoding " << h_fontencoding << "\n"
980            << "\\font_roman " << h_font_roman << "\n"
981            << "\\font_sans " << h_font_sans << "\n"
982            << "\\font_typewriter " << h_font_typewriter << "\n"
983            << "\\font_default_family " << h_font_default_family << "\n"
984            << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
985            << "\\font_sc " << h_font_sc << "\n"
986            << "\\font_osf " << h_font_osf << "\n"
987            << "\\font_sf_scale " << h_font_sf_scale << "\n"
988            << "\\font_tt_scale " << h_font_tt_scale << "\n\n"
989            << "\\graphics " << h_graphics << "\n"
990            << "\\default_output_format " << h_default_output_format << "\n"
991            << "\\output_sync " << h_output_sync << "\n";
992         if (h_output_sync == "1")
993                 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
994         os << "\\bibtex_command " << h_bibtex_command << "\n"
995            << "\\index_command " << h_index_command << "\n";
996         if (!h_float_placement.empty())
997                 os << "\\float_placement " << h_float_placement << "\n";
998         os << "\\paperfontsize " << h_paperfontsize << "\n"
999            << "\\spacing " << h_spacing << "\n"
1000            << "\\use_hyperref " << h_use_hyperref << '\n';
1001         if (h_use_hyperref == "1") {
1002                 if (!h_pdf_title.empty())
1003                         os << "\\pdf_title \"" << h_pdf_title << "\"\n";
1004                 if (!h_pdf_author.empty())
1005                         os << "\\pdf_author \"" << h_pdf_author << "\"\n";
1006                 if (!h_pdf_subject.empty())
1007                         os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
1008                 if (!h_pdf_keywords.empty())
1009                         os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
1010                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1011                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1012                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1013                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1014                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1015                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1016                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1017                       "\\pdf_backref " << h_pdf_backref << "\n"
1018                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1019                 if (!h_pdf_pagemode.empty())
1020                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1021                 if (!h_pdf_quoted_options.empty())
1022                         os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
1023         }
1024         os << "\\papersize " << h_papersize << "\n"
1025            << "\\use_geometry " << h_use_geometry << '\n';
1026         for (map<string, string>::const_iterator it = h_use_packages.begin();
1027              it != h_use_packages.end(); ++it)
1028                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1029         os << "\\cite_engine " << h_cite_engine << '\n'
1030            << "\\cite_engine_type " << h_cite_engine_type << '\n'
1031            << "\\biblio_style " << h_biblio_style << "\n"
1032            << "\\use_bibtopic " << h_use_bibtopic << "\n"
1033            << "\\use_indices " << h_use_indices << "\n"
1034            << "\\paperorientation " << h_paperorientation << '\n'
1035            << "\\suppress_date " << h_suppress_date << '\n'
1036            << "\\justification " << h_justification << '\n'
1037            << "\\use_refstyle " << h_use_refstyle << '\n';
1038         if (!h_fontcolor.empty())
1039                 os << "\\fontcolor " << h_fontcolor << '\n';
1040         if (!h_notefontcolor.empty())
1041                 os << "\\notefontcolor " << h_notefontcolor << '\n';
1042         if (!h_backgroundcolor.empty())
1043                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1044         if (!h_boxbgcolor.empty())
1045                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1046         os << "\\index " << h_index << '\n'
1047            << "\\shortcut " << h_shortcut << '\n'
1048            << "\\color " << h_color << '\n'
1049            << "\\end_index\n";
1050         os << h_margins
1051            << "\\secnumdepth " << h_secnumdepth << "\n"
1052            << "\\tocdepth " << h_tocdepth << "\n"
1053            << "\\paragraph_separation " << h_paragraph_separation << "\n";
1054         if (h_paragraph_separation == "skip")
1055                 os << "\\defskip " << h_defskip << "\n";
1056         else
1057                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1058         os << "\\quotes_language " << h_quotes_language << "\n"
1059            << "\\papercolumns " << h_papercolumns << "\n"
1060            << "\\papersides " << h_papersides << "\n"
1061            << "\\paperpagestyle " << h_paperpagestyle << "\n";
1062         if (!h_listings_params.empty())
1063                 os << "\\listings_params " << h_listings_params << "\n";
1064         os << "\\tracking_changes " << h_tracking_changes << "\n"
1065            << "\\output_changes " << h_output_changes << "\n"
1066            << "\\html_math_output " << h_html_math_output << "\n"
1067            << "\\html_css_as_file " << h_html_css_as_file << "\n"
1068            << "\\html_be_strict " << h_html_be_strict << "\n"
1069            << authors_
1070            << "\\end_header\n\n"
1071            << "\\begin_body\n";
1072         return true;
1073 }
1074
1075
1076 void Preamble::parse(Parser & p, string const & forceclass,
1077                      TeX2LyXDocClass & tc)
1078 {
1079         // initialize fixed types
1080         special_columns['D'] = 3;
1081         bool is_full_document = false;
1082         bool is_lyx_file = false;
1083         bool in_lyx_preamble = false;
1084
1085         // determine whether this is a full document or a fragment for inclusion
1086         while (p.good()) {
1087                 Token const & t = p.get_token();
1088
1089                 if (t.cat() == catEscape && t.cs() == "documentclass") {
1090                         is_full_document = true;
1091                         break;
1092                 }
1093         }
1094         p.reset();
1095
1096         while (is_full_document && p.good()) {
1097                 Token const & t = p.get_token();
1098
1099 #ifdef FILEDEBUG
1100                 cerr << "t: " << t << "\n";
1101 #endif
1102
1103                 //
1104                 // cat codes
1105                 //
1106                 if (!in_lyx_preamble &&
1107                     (t.cat() == catLetter ||
1108                      t.cat() == catSuper ||
1109                      t.cat() == catSub ||
1110                      t.cat() == catOther ||
1111                      t.cat() == catMath ||
1112                      t.cat() == catActive ||
1113                      t.cat() == catBegin ||
1114                      t.cat() == catEnd ||
1115                      t.cat() == catAlign ||
1116                      t.cat() == catParameter))
1117                         h_preamble << t.cs();
1118
1119                 else if (!in_lyx_preamble &&
1120                          (t.cat() == catSpace || t.cat() == catNewline))
1121                         h_preamble << t.asInput();
1122
1123                 else if (t.cat() == catComment) {
1124                         static regex const islyxfile("%% LyX .* created this file");
1125                         static regex const usercommands("User specified LaTeX commands");
1126
1127                         string const comment = t.asInput();
1128
1129                         // magically switch encoding default if it looks like XeLaTeX
1130                         static string const magicXeLaTeX =
1131                                 "% This document must be compiled with XeLaTeX ";
1132                         if (comment.size() > magicXeLaTeX.size()
1133                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1134                                   && h_inputencoding == "auto") {
1135                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
1136                                 h_inputencoding = "utf8";
1137                         }
1138                         smatch sub;
1139                         if (regex_search(comment, sub, islyxfile)) {
1140                                 is_lyx_file = true;
1141                                 in_lyx_preamble = true;
1142                         } else if (is_lyx_file
1143                                    && regex_search(comment, sub, usercommands))
1144                                 in_lyx_preamble = false;
1145                         else if (!in_lyx_preamble)
1146                                 h_preamble << t.asInput();
1147                 }
1148
1149                 else if (t.cs() == "pagestyle")
1150                         h_paperpagestyle = p.verbatim_item();
1151
1152                 else if (t.cs() == "setdefaultlanguage") {
1153                         xetex = true;
1154                         // We don't yet care about non-language variant options
1155                         // because LyX doesn't support this yet, see bug #8214
1156                         if (p.hasOpt()) {
1157                                 string langopts = p.getOpt();
1158                                 // check if the option contains a variant, if yes, extract it
1159                                 string::size_type pos_var = langopts.find("variant");
1160                                 string::size_type i = langopts.find(',', pos_var);
1161                                 if (pos_var != string::npos){
1162                                         string variant;
1163                                         if (i == string::npos)
1164                                                 variant = langopts.substr(pos_var + 8, langopts.length() - pos_var - 9);
1165                                         else
1166                                                 variant = langopts.substr(pos_var + 8, i - pos_var - 8);
1167                                         h_language = variant;
1168                                 }
1169                                 p.verbatim_item();
1170                         } else
1171                                 h_language = p.verbatim_item();
1172                         //finally translate the poyglossia name to a LyX name
1173                         h_language = polyglossia2lyx(h_language);
1174                 }
1175
1176                 else if (t.cs() == "setotherlanguage") {
1177                         // We don't yet care about the option because LyX doesn't
1178                         // support this yet, see bug #8214
1179                         p.hasOpt() ? p.getOpt() : string();
1180                         p.verbatim_item();
1181                 }
1182
1183                 else if (t.cs() == "setmainfont") {
1184                         // we don't care about the option
1185                         p.hasOpt() ? p.getOpt() : string();
1186                         h_font_roman = p.getArg('{', '}');
1187                 }
1188
1189                 else if (t.cs() == "setsansfont") {
1190                         // we don't care about the option
1191                         p.hasOpt() ? p.getOpt() : string();
1192                         h_font_sans = p.getArg('{', '}');
1193                 }
1194
1195                 else if (t.cs() == "setmonofont") {
1196                         // we don't care about the option
1197                         p.hasOpt() ? p.getOpt() : string();
1198                         h_font_typewriter = p.getArg('{', '}');
1199                 }
1200
1201                 else if (t.cs() == "date") {
1202                         string argument = p.getArg('{', '}');
1203                         if (argument.empty())
1204                                 h_suppress_date = "true";
1205                         else
1206                                 h_preamble << t.asInput() << '{' << argument << '}';
1207                 }
1208
1209                 else if (t.cs() == "color") {
1210                         string const space =
1211                                 (p.hasOpt() ? p.getOpt() : string());
1212                         string argument = p.getArg('{', '}');
1213                         // check the case that a standard color is used
1214                         if (space.empty() && is_known(argument, known_basic_colors)) {
1215                                 h_fontcolor = rgbcolor2code(argument);
1216                                 preamble.registerAutomaticallyLoadedPackage("color");
1217                         } else if (space.empty() && argument == "document_fontcolor")
1218                                 preamble.registerAutomaticallyLoadedPackage("color");
1219                         // check the case that LyX's document_fontcolor is defined
1220                         // but not used for \color
1221                         else {
1222                                 h_preamble << t.asInput();
1223                                 if (!space.empty())
1224                                         h_preamble << space;
1225                                 h_preamble << '{' << argument << '}';
1226                                 // the color might already be set because \definecolor
1227                                 // is parsed before this
1228                                 h_fontcolor = "";
1229                         }
1230                 }
1231
1232                 else if (t.cs() == "pagecolor") {
1233                         string argument = p.getArg('{', '}');
1234                         // check the case that a standard color is used
1235                         if (is_known(argument, known_basic_colors)) {
1236                                 h_backgroundcolor = rgbcolor2code(argument);
1237                         } else if (argument == "page_backgroundcolor")
1238                                 preamble.registerAutomaticallyLoadedPackage("color");
1239                         // check the case that LyX's page_backgroundcolor is defined
1240                         // but not used for \pagecolor
1241                         else {
1242                                 h_preamble << t.asInput() << '{' << argument << '}';
1243                                 // the color might already be set because \definecolor
1244                                 // is parsed before this
1245                                 h_backgroundcolor = "";
1246                         }
1247                 }
1248
1249                 else if (t.cs() == "makeatletter") {
1250                         // LyX takes care of this
1251                         p.setCatCode('@', catLetter);
1252                 }
1253
1254                 else if (t.cs() == "makeatother") {
1255                         // LyX takes care of this
1256                         p.setCatCode('@', catOther);
1257                 }
1258
1259                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1260                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1261                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
1262                                 || t.cs() == "DeclareRobustCommand"
1263                       || t.cs() == "DeclareRobustCommandx"
1264                                 || t.cs() == "ProvideTextCommandDefault"
1265                                 || t.cs() == "DeclareMathAccent") {
1266                         bool star = false;
1267                         if (p.next_token().character() == '*') {
1268                                 p.get_token();
1269                                 star = true;
1270                         }
1271                         string const name = p.verbatim_item();
1272                         string const opt1 = p.getFullOpt();
1273                         string const opt2 = p.getFullOpt();
1274                         string const body = p.verbatim_item();
1275                         // font settings
1276                         if (name == "\\rmdefault")
1277                                 if (is_known(body, known_roman_fonts))
1278                                         h_font_roman = body;
1279                         if (name == "\\sfdefault")
1280                                 if (is_known(body, known_sans_fonts))
1281                                         h_font_sans = body;
1282                         if (name == "\\ttdefault")
1283                                 if (is_known(body, known_typewriter_fonts))
1284                                         h_font_typewriter = body;
1285                         if (name == "\\familydefault") {
1286                                 string family = body;
1287                                 // remove leading "\"
1288                                 h_font_default_family = family.erase(0,1);
1289                         }
1290
1291                         // remove the lyxdot definition that is re-added by LyX
1292                         // if necessary
1293                         if (name == "\\lyxdot")
1294                                 in_lyx_preamble = true;
1295
1296                         // Add the command to the known commands
1297                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1298
1299                         // only non-lyxspecific stuff
1300                         if (!in_lyx_preamble) {
1301                                 ostringstream ss;
1302                                 ss << '\\' << t.cs();
1303                                 if (star)
1304                                         ss << '*';
1305                                 ss << '{' << name << '}' << opt1 << opt2
1306                                    << '{' << body << "}";
1307                                 h_preamble << ss.str();
1308 /*
1309                                 ostream & out = in_preamble ? h_preamble : os;
1310                                 out << "\\" << t.cs() << "{" << name << "}"
1311                                     << opts << "{" << body << "}";
1312 */
1313                         }
1314                 }
1315
1316                 else if (t.cs() == "documentclass") {
1317                         vector<string>::iterator it;
1318                         vector<string> opts = split_options(p.getArg('[', ']'));
1319                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1320                         delete_opt(opts, known_fontsizes);
1321                         // delete "pt" at the end
1322                         string::size_type i = h_paperfontsize.find("pt");
1323                         if (i != string::npos)
1324                                 h_paperfontsize.erase(i);
1325                         // The documentclass options are always parsed before the options
1326                         // of the babel call so that a language cannot overwrite the babel
1327                         // options.
1328                         handle_opt(opts, known_languages, h_language);
1329                         delete_opt(opts, known_languages);
1330
1331                         // paper orientation
1332                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1333                                 h_paperorientation = "landscape";
1334                                 opts.erase(it);
1335                         }
1336                         // paper sides
1337                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1338                                  != opts.end()) {
1339                                 h_papersides = "1";
1340                                 opts.erase(it);
1341                         }
1342                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1343                                  != opts.end()) {
1344                                 h_papersides = "2";
1345                                 opts.erase(it);
1346                         }
1347                         // paper columns
1348                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1349                                  != opts.end()) {
1350                                 h_papercolumns = "1";
1351                                 opts.erase(it);
1352                         }
1353                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1354                                  != opts.end()) {
1355                                 h_papercolumns = "2";
1356                                 opts.erase(it);
1357                         }
1358                         // paper sizes
1359                         // some size options are known to any document classes, other sizes
1360                         // are handled by the \geometry command of the geometry package
1361                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1362                         delete_opt(opts, known_class_paper_sizes);
1363                         // the remaining options
1364                         h_options = join(opts, ",");
1365                         // FIXME This does not work for classes that have a
1366                         //       different name in LyX than in LaTeX
1367                         h_textclass = p.getArg('{', '}');
1368                 }
1369
1370                 else if (t.cs() == "usepackage") {
1371                         string const options = p.getArg('[', ']');
1372                         string const name = p.getArg('{', '}');
1373                         vector<string> vecnames;
1374                         split(name, vecnames, ',');
1375                         vector<string>::const_iterator it  = vecnames.begin();
1376                         vector<string>::const_iterator end = vecnames.end();
1377                         for (; it != end; ++it)
1378                                 handle_package(p, trimSpaceAndEol(*it), options,
1379                                                in_lyx_preamble);
1380                 }
1381
1382                 else if (t.cs() == "inputencoding") {
1383                         string const encoding = p.getArg('{','}');
1384                         h_inputencoding = encoding;
1385                         p.setEncoding(encoding);
1386                 }
1387
1388                 else if (t.cs() == "newenvironment") {
1389                         string const name = p.getArg('{', '}');
1390                         string const opt1 = p.getFullOpt();
1391                         string const opt2 = p.getFullOpt();
1392                         string const beg = p.verbatim_item();
1393                         string const end = p.verbatim_item();
1394                         if (!in_lyx_preamble) {
1395                                 h_preamble << "\\newenvironment{" << name
1396                                            << '}' << opt1 << opt2 << '{'
1397                                            << beg << "}{" << end << '}';
1398                         }
1399                         add_known_environment(name, opt1, !opt2.empty(),
1400                                               from_utf8(beg), from_utf8(end));
1401
1402                 }
1403
1404                 else if (t.cs() == "def") {
1405                         string name = p.get_token().cs();
1406                         // In fact, name may be more than the name:
1407                         // In the test case of bug 8116
1408                         // name == "csname SF@gobble@opt \endcsname".
1409                         // Therefore, we need to use asInput() instead of cs().
1410                         while (p.next_token().cat() != catBegin)
1411                                 name += p.get_token().asInput();
1412                         if (!in_lyx_preamble)
1413                                 h_preamble << "\\def\\" << name << '{'
1414                                            << p.verbatim_item() << "}";
1415                 }
1416
1417                 else if (t.cs() == "newcolumntype") {
1418                         string const name = p.getArg('{', '}');
1419                         trimSpaceAndEol(name);
1420                         int nargs = 0;
1421                         string opts = p.getOpt();
1422                         if (!opts.empty()) {
1423                                 istringstream is(string(opts, 1));
1424                                 is >> nargs;
1425                         }
1426                         special_columns[name[0]] = nargs;
1427                         h_preamble << "\\newcolumntype{" << name << "}";
1428                         if (nargs)
1429                                 h_preamble << "[" << nargs << "]";
1430                         h_preamble << "{" << p.verbatim_item() << "}";
1431                 }
1432
1433                 else if (t.cs() == "setcounter") {
1434                         string const name = p.getArg('{', '}');
1435                         string const content = p.getArg('{', '}');
1436                         if (name == "secnumdepth")
1437                                 h_secnumdepth = content;
1438                         else if (name == "tocdepth")
1439                                 h_tocdepth = content;
1440                         else
1441                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1442                 }
1443
1444                 else if (t.cs() == "setlength") {
1445                         string const name = p.verbatim_item();
1446                         string const content = p.verbatim_item();
1447                         // the paragraphs are only not indented when \parindent is set to zero
1448                         if (name == "\\parindent" && content != "") {
1449                                 if (content[0] == '0')
1450                                         h_paragraph_separation = "skip";
1451                                 else
1452                                         h_paragraph_indentation = translate_len(content);
1453                         } else if (name == "\\parskip") {
1454                                 if (content == "\\smallskipamount")
1455                                         h_defskip = "smallskip";
1456                                 else if (content == "\\medskipamount")
1457                                         h_defskip = "medskip";
1458                                 else if (content == "\\bigskipamount")
1459                                         h_defskip = "bigskip";
1460                                 else
1461                                         h_defskip = content;
1462                         } else
1463                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1464                 }
1465
1466                 else if (t.cs() == "onehalfspacing")
1467                         h_spacing = "onehalf";
1468
1469                 else if (t.cs() == "doublespacing")
1470                         h_spacing = "double";
1471
1472                 else if (t.cs() == "setstretch")
1473                         h_spacing = "other " + p.verbatim_item();
1474
1475                 else if (t.cs() == "synctex") {
1476                         // the scheme is \synctex=value
1477                         // where value can only be "1" or "-1"
1478                         h_output_sync = "1";
1479                         // there can be any character behind the value (e.g. a linebreak or a '\'
1480                         // therefore we extract it char by char
1481                         p.get_token();
1482                         string value = p.get_token().asInput();
1483                         if (value == "-")
1484                                 value += p.get_token().asInput();
1485                         h_output_sync_macro = "\\synctex=" + value;
1486                 }
1487
1488                 else if (t.cs() == "begin") {
1489                         string const name = p.getArg('{', '}');
1490                         if (name == "document")
1491                                 break;
1492                         h_preamble << "\\begin{" << name << "}";
1493                 }
1494
1495                 else if (t.cs() == "geometry") {
1496                         vector<string> opts = split_options(p.getArg('{', '}'));
1497                         handle_geometry(opts);
1498                 }
1499
1500                 else if (t.cs() == "definecolor") {
1501                         string const color = p.getArg('{', '}');
1502                         string const space = p.getArg('{', '}');
1503                         string const value = p.getArg('{', '}');
1504                         if (color == "document_fontcolor" && space == "rgb") {
1505                                 RGBColor c(RGBColorFromLaTeX(value));
1506                                 h_fontcolor = X11hexname(c);
1507                         } else if (color == "note_fontcolor" && space == "rgb") {
1508                                 RGBColor c(RGBColorFromLaTeX(value));
1509                                 h_notefontcolor = X11hexname(c);
1510                         } else if (color == "page_backgroundcolor" && space == "rgb") {
1511                                 RGBColor c(RGBColorFromLaTeX(value));
1512                                 h_backgroundcolor = X11hexname(c);
1513                         } else if (color == "shadecolor" && space == "rgb") {
1514                                 RGBColor c(RGBColorFromLaTeX(value));
1515                                 h_boxbgcolor = X11hexname(c);
1516                         } else {
1517                                 h_preamble << "\\definecolor{" << color
1518                                            << "}{" << space << "}{" << value
1519                                            << '}';
1520                         }
1521                 }
1522
1523                 else if (t.cs() == "bibliographystyle")
1524                         h_biblio_style = p.verbatim_item();
1525
1526                 else if (t.cs() == "jurabibsetup") {
1527                         // FIXME p.getArg('{', '}') is most probably wrong (it
1528                         //       does not handle nested braces).
1529                         //       Use p.verbatim_item() instead.
1530                         vector<string> jurabibsetup =
1531                                 split_options(p.getArg('{', '}'));
1532                         // add jurabibsetup to the jurabib package options
1533                         add_package("jurabib", jurabibsetup);
1534                         if (!jurabibsetup.empty()) {
1535                                 h_preamble << "\\jurabibsetup{"
1536                                            << join(jurabibsetup, ",") << '}';
1537                         }
1538                 }
1539
1540                 else if (t.cs() == "hypersetup") {
1541                         vector<string> hypersetup =
1542                                 split_options(p.verbatim_item());
1543                         // add hypersetup to the hyperref package options
1544                         handle_hyperref(hypersetup);
1545                         if (!hypersetup.empty()) {
1546                                 h_preamble << "\\hypersetup{"
1547                                            << join(hypersetup, ",") << '}';
1548                         }
1549                 }
1550
1551                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1552                         // prevent misparsing of \usepackage if it is used
1553                         // as an argument (see e.g. our own output of
1554                         // \@ifundefined above)
1555                         string const arg1 = p.verbatim_item();
1556                         string const arg2 = p.verbatim_item();
1557                         string const arg3 = p.verbatim_item();
1558                         // test case \@ifundefined{date}{}{\date{}}
1559                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
1560                             arg2.empty() && arg3 == "\\date{}") {
1561                                 h_suppress_date = "true";
1562                         // older tex2lyx versions did output
1563                         // \@ifundefined{definecolor}{\usepackage{color}}{}
1564                         } else if (t.cs() == "@ifundefined" &&
1565                                    arg1 == "definecolor" &&
1566                                    arg2 == "\\usepackage{color}" &&
1567                                    arg3.empty()) {
1568                                 if (!in_lyx_preamble)
1569                                         h_preamble << package_beg_sep
1570                                                    << "color"
1571                                                    << package_mid_sep
1572                                                    << "\\@ifundefined{definecolor}{color}{}"
1573                                                    << package_end_sep;
1574                         // test for case
1575                         //\@ifundefined{showcaptionsetup}{}{%
1576                         // \PassOptionsToPackage{caption=false}{subfig}}
1577                         // that LyX uses for subfloats
1578                         } else if (t.cs() == "@ifundefined" &&
1579                                    arg1 == "showcaptionsetup" && arg2.empty()
1580                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1581                                 ; // do nothing
1582                         } else if (!in_lyx_preamble) {
1583                                 h_preamble << t.asInput()
1584                                            << '{' << arg1 << '}'
1585                                            << '{' << arg2 << '}'
1586                                            << '{' << arg3 << '}';
1587                         }
1588                 }
1589
1590                 else if (is_known(t.cs(), known_if_commands)) {
1591                         // must not parse anything in conditional code, since
1592                         // LyX would output the parsed contents unconditionally
1593                         if (!in_lyx_preamble)
1594                                 h_preamble << t.asInput();
1595                         handle_if(p, in_lyx_preamble);
1596                 }
1597
1598                 else if (!t.cs().empty() && !in_lyx_preamble)
1599                         h_preamble << '\\' << t.cs();
1600         }
1601
1602         // remove the whitespace
1603         p.skip_spaces();
1604
1605         // Force textclass if the user wanted it
1606         if (!forceclass.empty())
1607                 h_textclass = forceclass;
1608         if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1609                 h_textclass.insert(0, "literate-");
1610         tc.setName(h_textclass);
1611         if (!tc.load()) {
1612                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1613                 exit(EXIT_FAILURE);
1614         }
1615         if (h_papersides.empty()) {
1616                 ostringstream ss;
1617                 ss << tc.sides();
1618                 h_papersides = ss.str();
1619         }
1620 }
1621
1622
1623 string babel2lyx(string const & language)
1624 {
1625         char const * const * where = is_known(language, known_languages);
1626         if (where)
1627                 return known_coded_languages[where - known_languages];
1628         return language;
1629 }
1630
1631
1632 string polyglossia2lyx(string const & language)
1633 {
1634         char const * const * where = is_known(language, polyglossia_languages);
1635         if (where)
1636                 return coded_polyglossia_languages[where - polyglossia_languages];
1637         return language;
1638 }
1639
1640
1641 string rgbcolor2code(string const & name)
1642 {
1643         char const * const * where = is_known(name, known_basic_colors);
1644         if (where) {
1645                 // "red", "green" etc
1646                 return known_basic_color_codes[where - known_basic_colors];
1647         }
1648         // "255,0,0", "0,255,0" etc
1649         RGBColor c(RGBColorFromLaTeX(name));
1650         return X11hexname(c);
1651 }
1652
1653 // }])
1654
1655
1656 } // namespace lyx