]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
test-structure.tex: add checks for \verb
[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 himself.
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"
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                                 string::size_type k = langopts.find('=', pos_var);
1162                                 if (pos_var != string::npos){
1163                                         string variant;
1164                                         if (i == string::npos)
1165                                                 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1166                                         else
1167                                                 variant = langopts.substr(k + 1, i - k - 1);
1168                                         h_language = variant;
1169                                 }
1170                                 p.verbatim_item();
1171                         } else
1172                                 h_language = p.verbatim_item();
1173                         //finally translate the poyglossia name to a LyX name
1174                         h_language = polyglossia2lyx(h_language);
1175                 }
1176
1177                 else if (t.cs() == "setotherlanguage") {
1178                         // We don't yet care about the option because LyX doesn't
1179                         // support this yet, see bug #8214
1180                         p.hasOpt() ? p.getOpt() : string();
1181                         p.verbatim_item();
1182                 }
1183
1184                 else if (t.cs() == "setmainfont") {
1185                         // we don't care about the option
1186                         p.hasOpt() ? p.getOpt() : string();
1187                         h_font_roman = p.getArg('{', '}');
1188                 }
1189
1190                 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1191                         // LyX currently only supports the scale option
1192                         string scale;
1193                         if (p.hasOpt()) {
1194                                 string fontopts = p.getArg('[', ']');
1195                                 // check if the option contains a scaling, if yes, extract it
1196                                 string::size_type pos = fontopts.find("Scale");
1197                                 if (pos != string::npos) {
1198                                         string::size_type i = fontopts.find(',', pos);
1199                                         if (i == string::npos)
1200                                                 scale = scale_as_percentage(fontopts.substr(pos + 1));
1201                                         else
1202                                                 scale = scale_as_percentage(fontopts.substr(pos, i - pos));
1203                                 }
1204                         }
1205                         if (t.cs() == "setsansfont") {
1206                                 if (!scale.empty())
1207                                         h_font_sf_scale = scale;
1208                                 h_font_sans = p.getArg('{', '}');
1209                         } else {
1210                                 if (!scale.empty())
1211                                         h_font_tt_scale = scale;
1212                                 h_font_typewriter = p.getArg('{', '}');
1213                         }
1214                 }
1215
1216                 else if (t.cs() == "date") {
1217                         string argument = p.getArg('{', '}');
1218                         if (argument.empty())
1219                                 h_suppress_date = "true";
1220                         else
1221                                 h_preamble << t.asInput() << '{' << argument << '}';
1222                 }
1223
1224                 else if (t.cs() == "color") {
1225                         string const space =
1226                                 (p.hasOpt() ? p.getOpt() : string());
1227                         string argument = p.getArg('{', '}');
1228                         // check the case that a standard color is used
1229                         if (space.empty() && is_known(argument, known_basic_colors)) {
1230                                 h_fontcolor = rgbcolor2code(argument);
1231                                 preamble.registerAutomaticallyLoadedPackage("color");
1232                         } else if (space.empty() && argument == "document_fontcolor")
1233                                 preamble.registerAutomaticallyLoadedPackage("color");
1234                         // check the case that LyX's document_fontcolor is defined
1235                         // but not used for \color
1236                         else {
1237                                 h_preamble << t.asInput();
1238                                 if (!space.empty())
1239                                         h_preamble << space;
1240                                 h_preamble << '{' << argument << '}';
1241                                 // the color might already be set because \definecolor
1242                                 // is parsed before this
1243                                 h_fontcolor = "";
1244                         }
1245                 }
1246
1247                 else if (t.cs() == "pagecolor") {
1248                         string argument = p.getArg('{', '}');
1249                         // check the case that a standard color is used
1250                         if (is_known(argument, known_basic_colors)) {
1251                                 h_backgroundcolor = rgbcolor2code(argument);
1252                         } else if (argument == "page_backgroundcolor")
1253                                 preamble.registerAutomaticallyLoadedPackage("color");
1254                         // check the case that LyX's page_backgroundcolor is defined
1255                         // but not used for \pagecolor
1256                         else {
1257                                 h_preamble << t.asInput() << '{' << argument << '}';
1258                                 // the color might already be set because \definecolor
1259                                 // is parsed before this
1260                                 h_backgroundcolor = "";
1261                         }
1262                 }
1263
1264                 else if (t.cs() == "makeatletter") {
1265                         // LyX takes care of this
1266                         p.setCatCode('@', catLetter);
1267                 }
1268
1269                 else if (t.cs() == "makeatother") {
1270                         // LyX takes care of this
1271                         p.setCatCode('@', catOther);
1272                 }
1273
1274                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1275                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1276                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
1277                                 || t.cs() == "DeclareRobustCommand"
1278                       || t.cs() == "DeclareRobustCommandx"
1279                                 || t.cs() == "ProvideTextCommandDefault"
1280                                 || t.cs() == "DeclareMathAccent") {
1281                         bool star = false;
1282                         if (p.next_token().character() == '*') {
1283                                 p.get_token();
1284                                 star = true;
1285                         }
1286                         string const name = p.verbatim_item();
1287                         string const opt1 = p.getFullOpt();
1288                         string const opt2 = p.getFullOpt();
1289                         string const body = p.verbatim_item();
1290                         // font settings
1291                         if (name == "\\rmdefault")
1292                                 if (is_known(body, known_roman_fonts))
1293                                         h_font_roman = body;
1294                         if (name == "\\sfdefault")
1295                                 if (is_known(body, known_sans_fonts))
1296                                         h_font_sans = body;
1297                         if (name == "\\ttdefault")
1298                                 if (is_known(body, known_typewriter_fonts))
1299                                         h_font_typewriter = body;
1300                         if (name == "\\familydefault") {
1301                                 string family = body;
1302                                 // remove leading "\"
1303                                 h_font_default_family = family.erase(0,1);
1304                         }
1305
1306                         // remove the lyxdot definition that is re-added by LyX
1307                         // if necessary
1308                         if (name == "\\lyxdot")
1309                                 in_lyx_preamble = true;
1310
1311                         // Add the command to the known commands
1312                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1313
1314                         // only non-lyxspecific stuff
1315                         if (!in_lyx_preamble) {
1316                                 ostringstream ss;
1317                                 ss << '\\' << t.cs();
1318                                 if (star)
1319                                         ss << '*';
1320                                 ss << '{' << name << '}' << opt1 << opt2
1321                                    << '{' << body << "}";
1322                                 h_preamble << ss.str();
1323 /*
1324                                 ostream & out = in_preamble ? h_preamble : os;
1325                                 out << "\\" << t.cs() << "{" << name << "}"
1326                                     << opts << "{" << body << "}";
1327 */
1328                         }
1329                 }
1330
1331                 else if (t.cs() == "documentclass") {
1332                         vector<string>::iterator it;
1333                         vector<string> opts = split_options(p.getArg('[', ']'));
1334                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1335                         delete_opt(opts, known_fontsizes);
1336                         // delete "pt" at the end
1337                         string::size_type i = h_paperfontsize.find("pt");
1338                         if (i != string::npos)
1339                                 h_paperfontsize.erase(i);
1340                         // The documentclass options are always parsed before the options
1341                         // of the babel call so that a language cannot overwrite the babel
1342                         // options.
1343                         handle_opt(opts, known_languages, h_language);
1344                         delete_opt(opts, known_languages);
1345
1346                         // paper orientation
1347                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1348                                 h_paperorientation = "landscape";
1349                                 opts.erase(it);
1350                         }
1351                         // paper sides
1352                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1353                                  != opts.end()) {
1354                                 h_papersides = "1";
1355                                 opts.erase(it);
1356                         }
1357                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1358                                  != opts.end()) {
1359                                 h_papersides = "2";
1360                                 opts.erase(it);
1361                         }
1362                         // paper columns
1363                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1364                                  != opts.end()) {
1365                                 h_papercolumns = "1";
1366                                 opts.erase(it);
1367                         }
1368                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1369                                  != opts.end()) {
1370                                 h_papercolumns = "2";
1371                                 opts.erase(it);
1372                         }
1373                         // paper sizes
1374                         // some size options are known to any document classes, other sizes
1375                         // are handled by the \geometry command of the geometry package
1376                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1377                         delete_opt(opts, known_class_paper_sizes);
1378                         // the remaining options
1379                         h_options = join(opts, ",");
1380                         // FIXME This does not work for classes that have a
1381                         //       different name in LyX than in LaTeX
1382                         h_textclass = p.getArg('{', '}');
1383                 }
1384
1385                 else if (t.cs() == "usepackage") {
1386                         string const options = p.getArg('[', ']');
1387                         string const name = p.getArg('{', '}');
1388                         vector<string> vecnames;
1389                         split(name, vecnames, ',');
1390                         vector<string>::const_iterator it  = vecnames.begin();
1391                         vector<string>::const_iterator end = vecnames.end();
1392                         for (; it != end; ++it)
1393                                 handle_package(p, trimSpaceAndEol(*it), options,
1394                                                in_lyx_preamble);
1395                 }
1396
1397                 else if (t.cs() == "inputencoding") {
1398                         string const encoding = p.getArg('{','}');
1399                         h_inputencoding = encoding;
1400                         p.setEncoding(encoding);
1401                 }
1402
1403                 else if (t.cs() == "newenvironment") {
1404                         string const name = p.getArg('{', '}');
1405                         string const opt1 = p.getFullOpt();
1406                         string const opt2 = p.getFullOpt();
1407                         string const beg = p.verbatim_item();
1408                         string const end = p.verbatim_item();
1409                         if (!in_lyx_preamble) {
1410                                 h_preamble << "\\newenvironment{" << name
1411                                            << '}' << opt1 << opt2 << '{'
1412                                            << beg << "}{" << end << '}';
1413                         }
1414                         add_known_environment(name, opt1, !opt2.empty(),
1415                                               from_utf8(beg), from_utf8(end));
1416
1417                 }
1418
1419                 else if (t.cs() == "def") {
1420                         string name = p.get_token().cs();
1421                         // In fact, name may be more than the name:
1422                         // In the test case of bug 8116
1423                         // name == "csname SF@gobble@opt \endcsname".
1424                         // Therefore, we need to use asInput() instead of cs().
1425                         while (p.next_token().cat() != catBegin)
1426                                 name += p.get_token().asInput();
1427                         if (!in_lyx_preamble)
1428                                 h_preamble << "\\def\\" << name << '{'
1429                                            << p.verbatim_item() << "}";
1430                 }
1431
1432                 else if (t.cs() == "newcolumntype") {
1433                         string const name = p.getArg('{', '}');
1434                         trimSpaceAndEol(name);
1435                         int nargs = 0;
1436                         string opts = p.getOpt();
1437                         if (!opts.empty()) {
1438                                 istringstream is(string(opts, 1));
1439                                 is >> nargs;
1440                         }
1441                         special_columns[name[0]] = nargs;
1442                         h_preamble << "\\newcolumntype{" << name << "}";
1443                         if (nargs)
1444                                 h_preamble << "[" << nargs << "]";
1445                         h_preamble << "{" << p.verbatim_item() << "}";
1446                 }
1447
1448                 else if (t.cs() == "setcounter") {
1449                         string const name = p.getArg('{', '}');
1450                         string const content = p.getArg('{', '}');
1451                         if (name == "secnumdepth")
1452                                 h_secnumdepth = content;
1453                         else if (name == "tocdepth")
1454                                 h_tocdepth = content;
1455                         else
1456                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1457                 }
1458
1459                 else if (t.cs() == "setlength") {
1460                         string const name = p.verbatim_item();
1461                         string const content = p.verbatim_item();
1462                         // the paragraphs are only not indented when \parindent is set to zero
1463                         if (name == "\\parindent" && content != "") {
1464                                 if (content[0] == '0')
1465                                         h_paragraph_separation = "skip";
1466                                 else
1467                                         h_paragraph_indentation = translate_len(content);
1468                         } else if (name == "\\parskip") {
1469                                 if (content == "\\smallskipamount")
1470                                         h_defskip = "smallskip";
1471                                 else if (content == "\\medskipamount")
1472                                         h_defskip = "medskip";
1473                                 else if (content == "\\bigskipamount")
1474                                         h_defskip = "bigskip";
1475                                 else
1476                                         h_defskip = content;
1477                         } else
1478                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1479                 }
1480
1481                 else if (t.cs() == "onehalfspacing")
1482                         h_spacing = "onehalf";
1483
1484                 else if (t.cs() == "doublespacing")
1485                         h_spacing = "double";
1486
1487                 else if (t.cs() == "setstretch")
1488                         h_spacing = "other " + p.verbatim_item();
1489
1490                 else if (t.cs() == "synctex") {
1491                         // the scheme is \synctex=value
1492                         // where value can only be "1" or "-1"
1493                         h_output_sync = "1";
1494                         // there can be any character behind the value (e.g. a linebreak or a '\'
1495                         // therefore we extract it char by char
1496                         p.get_token();
1497                         string value = p.get_token().asInput();
1498                         if (value == "-")
1499                                 value += p.get_token().asInput();
1500                         h_output_sync_macro = "\\synctex=" + value;
1501                 }
1502
1503                 else if (t.cs() == "begin") {
1504                         string const name = p.getArg('{', '}');
1505                         if (name == "document")
1506                                 break;
1507                         h_preamble << "\\begin{" << name << "}";
1508                 }
1509
1510                 else if (t.cs() == "geometry") {
1511                         vector<string> opts = split_options(p.getArg('{', '}'));
1512                         handle_geometry(opts);
1513                 }
1514
1515                 else if (t.cs() == "definecolor") {
1516                         string const color = p.getArg('{', '}');
1517                         string const space = p.getArg('{', '}');
1518                         string const value = p.getArg('{', '}');
1519                         if (color == "document_fontcolor" && space == "rgb") {
1520                                 RGBColor c(RGBColorFromLaTeX(value));
1521                                 h_fontcolor = X11hexname(c);
1522                         } else if (color == "note_fontcolor" && space == "rgb") {
1523                                 RGBColor c(RGBColorFromLaTeX(value));
1524                                 h_notefontcolor = X11hexname(c);
1525                         } else if (color == "page_backgroundcolor" && space == "rgb") {
1526                                 RGBColor c(RGBColorFromLaTeX(value));
1527                                 h_backgroundcolor = X11hexname(c);
1528                         } else if (color == "shadecolor" && space == "rgb") {
1529                                 RGBColor c(RGBColorFromLaTeX(value));
1530                                 h_boxbgcolor = X11hexname(c);
1531                         } else {
1532                                 h_preamble << "\\definecolor{" << color
1533                                            << "}{" << space << "}{" << value
1534                                            << '}';
1535                         }
1536                 }
1537
1538                 else if (t.cs() == "bibliographystyle")
1539                         h_biblio_style = p.verbatim_item();
1540
1541                 else if (t.cs() == "jurabibsetup") {
1542                         // FIXME p.getArg('{', '}') is most probably wrong (it
1543                         //       does not handle nested braces).
1544                         //       Use p.verbatim_item() instead.
1545                         vector<string> jurabibsetup =
1546                                 split_options(p.getArg('{', '}'));
1547                         // add jurabibsetup to the jurabib package options
1548                         add_package("jurabib", jurabibsetup);
1549                         if (!jurabibsetup.empty()) {
1550                                 h_preamble << "\\jurabibsetup{"
1551                                            << join(jurabibsetup, ",") << '}';
1552                         }
1553                 }
1554
1555                 else if (t.cs() == "hypersetup") {
1556                         vector<string> hypersetup =
1557                                 split_options(p.verbatim_item());
1558                         // add hypersetup to the hyperref package options
1559                         handle_hyperref(hypersetup);
1560                         if (!hypersetup.empty()) {
1561                                 h_preamble << "\\hypersetup{"
1562                                            << join(hypersetup, ",") << '}';
1563                         }
1564                 }
1565
1566                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1567                         // prevent misparsing of \usepackage if it is used
1568                         // as an argument (see e.g. our own output of
1569                         // \@ifundefined above)
1570                         string const arg1 = p.verbatim_item();
1571                         string const arg2 = p.verbatim_item();
1572                         string const arg3 = p.verbatim_item();
1573                         // test case \@ifundefined{date}{}{\date{}}
1574                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
1575                             arg2.empty() && arg3 == "\\date{}") {
1576                                 h_suppress_date = "true";
1577                         // older tex2lyx versions did output
1578                         // \@ifundefined{definecolor}{\usepackage{color}}{}
1579                         } else if (t.cs() == "@ifundefined" &&
1580                                    arg1 == "definecolor" &&
1581                                    arg2 == "\\usepackage{color}" &&
1582                                    arg3.empty()) {
1583                                 if (!in_lyx_preamble)
1584                                         h_preamble << package_beg_sep
1585                                                    << "color"
1586                                                    << package_mid_sep
1587                                                    << "\\@ifundefined{definecolor}{color}{}"
1588                                                    << package_end_sep;
1589                         // test for case
1590                         //\@ifundefined{showcaptionsetup}{}{%
1591                         // \PassOptionsToPackage{caption=false}{subfig}}
1592                         // that LyX uses for subfloats
1593                         } else if (t.cs() == "@ifundefined" &&
1594                                    arg1 == "showcaptionsetup" && arg2.empty()
1595                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1596                                 ; // do nothing
1597                         } else if (!in_lyx_preamble) {
1598                                 h_preamble << t.asInput()
1599                                            << '{' << arg1 << '}'
1600                                            << '{' << arg2 << '}'
1601                                            << '{' << arg3 << '}';
1602                         }
1603                 }
1604
1605                 else if (is_known(t.cs(), known_if_commands)) {
1606                         // must not parse anything in conditional code, since
1607                         // LyX would output the parsed contents unconditionally
1608                         if (!in_lyx_preamble)
1609                                 h_preamble << t.asInput();
1610                         handle_if(p, in_lyx_preamble);
1611                 }
1612
1613                 else if (!t.cs().empty() && !in_lyx_preamble)
1614                         h_preamble << '\\' << t.cs();
1615         }
1616
1617         // remove the whitespace
1618         p.skip_spaces();
1619
1620         // Force textclass if the user wanted it
1621         if (!forceclass.empty())
1622                 h_textclass = forceclass;
1623         if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1624                 h_textclass.insert(0, "literate-");
1625         tc.setName(h_textclass);
1626         if (!tc.load()) {
1627                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1628                 exit(EXIT_FAILURE);
1629         }
1630         if (h_papersides.empty()) {
1631                 ostringstream ss;
1632                 ss << tc.sides();
1633                 h_papersides = ss.str();
1634         }
1635 }
1636
1637
1638 string babel2lyx(string const & language)
1639 {
1640         char const * const * where = is_known(language, known_languages);
1641         if (where)
1642                 return known_coded_languages[where - known_languages];
1643         return language;
1644 }
1645
1646
1647 string polyglossia2lyx(string const & language)
1648 {
1649         char const * const * where = is_known(language, polyglossia_languages);
1650         if (where)
1651                 return coded_polyglossia_languages[where - polyglossia_languages];
1652         return language;
1653 }
1654
1655
1656 string rgbcolor2code(string const & name)
1657 {
1658         char const * const * where = is_known(name, known_basic_colors);
1659         if (where) {
1660                 // "red", "green" etc
1661                 return known_basic_color_codes[where - known_basic_colors];
1662         }
1663         // "255,0,0", "0,255,0" etc
1664         RGBColor c(RGBColorFromLaTeX(name));
1665         return X11hexname(c);
1666 }
1667
1668 // }])
1669
1670
1671 } // namespace lyx