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