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