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