]> git.lyx.org Git - features.git/blob - src/tex2lyx/Preamble.cpp
Add \save_transient_properties parameter (#9841)
[features.git] / src / tex2lyx / Preamble.cpp
1 /**
2  * \file Preamble.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  * \author Uwe Stöhr
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 // {[(
13
14 #include <config.h>
15
16 #include "Preamble.h"
17 #include "tex2lyx.h"
18
19 #include "Encoding.h"
20 #include "LayoutFile.h"
21 #include "Layout.h"
22 #include "Lexer.h"
23 #include "TextClass.h"
24 #include "version.h"
25
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
30
31 #include "support/regex.h"
32
33 #include <algorithm>
34 #include <iostream>
35
36 using namespace std;
37 using namespace lyx::support;
38
39
40 namespace lyx {
41
42 Preamble preamble;
43
44 namespace {
45
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
47 // further down.
48 /**
49  * known babel language names (including synonyms)
50  * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51  * please keep this in sync with known_coded_languages line by line!
52  */
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
55 "bahasam", "basque", "belarusian", "brazil", "brazilian", "breton", "british",
56 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
57 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
58 "french", "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "polutonikogreek", "polish", "portuges", "portuguese", "romanian", "russian",
64 "russianb", "samin", "scottish", "serbian", "serbian-latin", "slovak",
65 "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
66 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
67 "vietnam", "welsh",
68 0};
69
70 /**
71  * the same as known_languages with .lyx names
72  * please keep this in sync with known_languages line by line!
73  */
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "brazilian", "brazilian", "breton", "british",
77 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
78 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
79 "french", "french", "french", "french", "galician", "german", "german",
80 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "polutonikogreek", "polish", "portuguese", "portuguese", "romanian", "russian",
85 "russian", "samin", "scottish", "serbian", "serbian-latin", "slovak",
86 "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
87 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
88 "vietnamese", "welsh",
89 0};
90
91 /// languages with danish quotes (.lyx names)
92 const char * const known_danish_quotes_languages[] = {"danish", 0};
93
94 /// languages with english quotes (.lyx names)
95 const char * const known_english_quotes_languages[] = {"american", "australian",
96 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
97 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
98 "thai", 0};
99
100 /// languages with french quotes (.lyx names)
101 const char * const known_french_quotes_languages[] = {"albanian",
102 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
103 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
104 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
105 "vietnamese", 0};
106
107 /// languages with german quotes (.lyx names)
108 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
109 "czech", "german", "georgian", "icelandic", "lithuanian", "lowersorbian", "naustrian",
110 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
111
112 /// languages with polish quotes (.lyx names)
113 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
114 "dutch", "estonian", "magyar", "polish", "romanian", 0};
115
116 /// languages with swedish quotes (.lyx names)
117 const char * const known_swedish_quotes_languages[] = {"finnish",
118 "swedish", 0};
119
120 /// known language packages from the times before babel
121 const char * const known_old_language_packages[] = {"french", "frenchle",
122 "frenchpro", "german", "ngerman", "pmfrench", 0};
123
124 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
125
126 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
127 "ccfonts", "chancery", "charter", "cmr", "fourier", "garamondx", "libertine",
128 "libertine-type1", "lmodern", "mathdesign", "mathpazo", "mathptmx", "newcent",
129 "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", 0};
130
131 const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum-type1",
132 "cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc", "kurier",
133 "kurierc", "kurierl", "kurierlc", "lmss", "tgadventor", "tgheros", 0};
134
135 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
136 "courier", "lmtt", "luximono", "fourier", "libertineMono-type1", "lmodern",
137 "mathpazo", "mathptmx", "newcent", "tgcursor", "txtt", 0};
138
139 const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0};
140
141 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
142 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
143 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
144 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
145 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
146
147 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
148 "executivepaper", "legalpaper", "letterpaper", 0};
149
150 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
151 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
152
153 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
154 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
155 "columnsep", 0};
156
157 /// commands that can start an \if...\else...\endif sequence
158 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
159 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
160 "ifsidecap", "ifupgreek", 0};
161
162 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
163         "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
164         "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
165
166 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
167         "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
168         "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
169
170 /// conditional commands with three arguments like \@ifundefined{}{}{}
171 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
172 0};
173
174 /// packages that work only in xetex
175 /// polyglossia is handled separately
176 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
177 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
178 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
179
180 /// packages that are automatically skipped if loaded by LyX
181 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
182 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
183 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
184 "makeidx", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle", "rotating",
185 "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa", "tipx",
186 "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xunicode", 0};
187
188 // used for the handling of \newindex
189 int index_number = 0;
190
191 // codes used to remove packages that are loaded automatically by LyX.
192 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
193 const char package_beg_sep = '\001';
194 const char package_mid_sep = '\002';
195 const char package_end_sep = '\003';
196
197
198 // returns true if at least one of the options in what has been found
199 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
200 {
201         if (opts.empty())
202                 return false;
203
204         bool found = false;
205         // the last language option is the document language (for babel and LyX)
206         // the last size option is the document font size
207         vector<string>::iterator it;
208         vector<string>::iterator position = opts.begin();
209         for (; *what; ++what) {
210                 it = find(opts.begin(), opts.end(), *what);
211                 if (it != opts.end()) {
212                         if (it >= position) {
213                                 found = true;
214                                 target = *what;
215                                 position = it;
216                         }
217                 }
218         }
219         return found;
220 }
221
222
223 void delete_opt(vector<string> & opts, char const * const * what)
224 {
225         if (opts.empty())
226                 return;
227
228         // remove found options from the list
229         // do this after handle_opt to avoid potential memory leaks
230         vector<string>::iterator it;
231         for (; *what; ++what) {
232                 it = find(opts.begin(), opts.end(), *what);
233                 if (it != opts.end())
234                         opts.erase(it);
235         }
236 }
237
238
239 /*!
240  * Split a package options string (keyval format) into a vector.
241  * Example input:
242  *   authorformat=smallcaps,
243  *   commabeforerest,
244  *   titleformat=colonsep,
245  *   bibformat={tabular,ibidem,numbered}
246  */
247 vector<string> split_options(string const & input)
248 {
249         vector<string> options;
250         string option;
251         Parser p(input);
252         while (p.good()) {
253                 Token const & t = p.get_token();
254                 if (t.asInput() == ",") {
255                         options.push_back(trimSpaceAndEol(option));
256                         option.erase();
257                 } else if (t.asInput() == "=") {
258                         option += '=';
259                         p.skip_spaces(true);
260                         if (p.next_token().asInput() == "{")
261                                 option += '{' + p.getArg('{', '}') + '}';
262                 } else if (t.cat() != catSpace)
263                         option += t.asInput();
264         }
265
266         if (!option.empty())
267                 options.push_back(trimSpaceAndEol(option));
268
269         return options;
270 }
271
272
273 /*!
274  * Retrieve a keyval option "name={value with=sign}" named \p name from
275  * \p options and return the value.
276  * The found option is also removed from \p options.
277  */
278 string process_keyval_opt(vector<string> & options, string name)
279 {
280         for (size_t i = 0; i < options.size(); ++i) {
281                 vector<string> option;
282                 split(options[i], option, '=');
283                 if (option.size() < 2)
284                         continue;
285                 if (option[0] == name) {
286                         options.erase(options.begin() + i);
287                         option.erase(option.begin());
288                         return join(option, "=");
289                 }
290         }
291         return "";
292 }
293
294 } // anonymous namespace
295
296
297 /**
298  * known polyglossia language names (including variants)
299  * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
300  */
301 const char * const Preamble::polyglossia_languages[] = {
302 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
303 "nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil",
304 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
305 "irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish",
306 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
307 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "tibetan", "urdu",
308 "brazil", "brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton",
309 "french", "lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar",
310 "slovak", "welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek",
311 "marathi", "spanish", "austrian",
312 "american", "ancient", "australian", "british", "monotonic", "newzealand",
313 "polytonic", 0};
314
315 /**
316  * the same as polyglossia_languages with .lyx names
317  * please keep this in sync with polyglossia_languages line by line!
318  */
319 const char * const Preamble::coded_polyglossia_languages[] = {
320 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
321 "nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil",
322 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
323 "irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish",
324 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
325 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "tibetan", "urdu",
326 "brazilian", "brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton",
327 "french", "lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar",
328 "slovak", "welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek",
329 "marathi", "spanish", "naustrian",
330 "american", "ancientgreek", "australian", "british", "greek", "newzealand",
331 "polutonikogreek", 0};
332
333
334 bool Preamble::usePolyglossia() const
335 {
336         return h_use_non_tex_fonts && h_language_package == "default";
337 }
338
339
340 bool Preamble::indentParagraphs() const
341 {
342         return h_paragraph_separation == "indent";
343 }
344
345
346 bool Preamble::isPackageUsed(string const & package) const
347 {
348         return used_packages.find(package) != used_packages.end();
349 }
350
351
352 vector<string> Preamble::getPackageOptions(string const & package) const
353 {
354         map<string, vector<string> >::const_iterator it = used_packages.find(package);
355         if (it != used_packages.end())
356                 return it->second;
357         return vector<string>();
358 }
359
360
361 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
362 {
363         auto_packages.insert(package);
364 }
365
366
367 void Preamble::addModule(string const & module)
368 {
369         used_modules.push_back(module);
370 }
371
372
373 void Preamble::suppressDate(bool suppress)
374 {
375         if (suppress)
376                 h_suppress_date = "true";
377         else
378                 h_suppress_date = "false";
379 }
380
381
382 void Preamble::registerAuthor(std::string const & name)
383 {
384         Author author(from_utf8(name), empty_docstring());
385         author.setUsed(true);
386         authors_.record(author);
387         h_tracking_changes = "true";
388         h_output_changes = "true";
389 }
390
391
392 Author const & Preamble::getAuthor(std::string const & name) const
393 {
394         Author author(from_utf8(name), empty_docstring());
395         for (AuthorList::Authors::const_iterator it = authors_.begin();
396              it != authors_.end(); ++it)
397                 if (*it == author)
398                         return *it;
399         static Author const dummy;
400         return dummy;
401 }
402
403
404 int Preamble::getSpecialTableColumnArguments(char c) const
405 {
406         map<char, int>::const_iterator it = special_columns_.find(c);
407         if (it == special_columns_.end())
408                 return -1;
409         return it->second;
410 }
411
412
413 void Preamble::add_package(string const & name, vector<string> & options)
414 {
415         // every package inherits the global options
416         if (used_packages.find(name) == used_packages.end())
417                 used_packages[name] = split_options(h_options);
418
419         vector<string> & v = used_packages[name];
420         v.insert(v.end(), options.begin(), options.end());
421         if (name == "jurabib") {
422                 // Don't output the order argument (see the cite command
423                 // handling code in text.cpp).
424                 vector<string>::iterator end =
425                         remove(options.begin(), options.end(), "natbiborder");
426                 end = remove(options.begin(), end, "jurabiborder");
427                 options.erase(end, options.end());
428         }
429 }
430
431
432 namespace {
433
434 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
435 bool scale_as_percentage(string const & scale, string & percentage)
436 {
437         string::size_type pos = scale.find('=');
438         if (pos != string::npos) {
439                 string value = scale.substr(pos + 1);
440                 if (isStrDbl(value)) {
441                         percentage = convert<string>(
442                                 static_cast<int>(100 * convert<double>(value)));
443                         return true;
444                 }
445         }
446         return false;
447 }
448
449
450 string remove_braces(string const & value)
451 {
452         if (value.empty())
453                 return value;
454         if (value[0] == '{' && value[value.length()-1] == '}')
455                 return value.substr(1, value.length()-2);
456         return value;
457 }
458
459 } // anonymous namespace
460
461
462 Preamble::Preamble() : one_language(true), explicit_babel(false),
463         title_layout_found(false), h_font_cjk_set(false)
464 {
465         //h_backgroundcolor;
466         //h_boxbgcolor;
467         h_biblio_style            = "plain";
468         h_bibtex_command          = "default";
469         h_cite_engine             = "basic";
470         h_cite_engine_type        = "default";
471         h_color                   = "#008000";
472         h_defskip                 = "medskip";
473         //h_float_placement;
474         //h_fontcolor;
475         h_fontencoding            = "default";
476         h_font_roman[0]           = "default";
477         h_font_roman[1]           = "default";
478         h_font_sans[0]            = "default";
479         h_font_sans[1]            = "default";
480         h_font_typewriter[0]      = "default";
481         h_font_typewriter[1]      = "default";
482         h_font_math[0]            = "auto";
483         h_font_math[1]            = "auto";
484         h_font_default_family     = "default";
485         h_use_non_tex_fonts       = false;
486         h_font_sc                 = "false";
487         h_font_osf                = "false";
488         h_font_sf_scale[0]        = "100";
489         h_font_sf_scale[1]        = "100";
490         h_font_tt_scale[0]        = "100";
491         h_font_tt_scale[1]        = "100";
492         //h_font_cjk
493         h_graphics                = "default";
494         h_default_output_format   = "default";
495         h_html_be_strict          = "false";
496         h_html_css_as_file        = "0";
497         h_html_math_output        = "0";
498         h_index[0]                = "Index";
499         h_index_command           = "default";
500         h_inputencoding           = "auto";
501         h_justification           = "true";
502         h_language                = "english";
503         h_language_package        = "none";
504         //h_listings_params;
505         h_maintain_unincluded_children = "false";
506         //h_margins;
507         //h_notefontcolor;
508         //h_options;
509         h_output_changes          = "false";
510         h_output_sync             = "0";
511         //h_output_sync_macro
512         h_papercolumns            = "1";
513         h_paperfontsize           = "default";
514         h_paperorientation        = "portrait";
515         h_paperpagestyle          = "default";
516         //h_papersides;
517         h_papersize               = "default";
518         h_paragraph_indentation   = "default";
519         h_paragraph_separation    = "indent";
520         //h_pdf_title;
521         //h_pdf_author;
522         //h_pdf_subject;
523         //h_pdf_keywords;
524         h_pdf_bookmarks           = "0";
525         h_pdf_bookmarksnumbered   = "0";
526         h_pdf_bookmarksopen       = "0";
527         h_pdf_bookmarksopenlevel  = "1";
528         h_pdf_breaklinks          = "0";
529         h_pdf_pdfborder           = "0";
530         h_pdf_colorlinks          = "0";
531         h_pdf_backref             = "section";
532         h_pdf_pdfusetitle         = "0";
533         //h_pdf_pagemode;
534         //h_pdf_quoted_options;
535         h_quotes_language         = "english";
536         h_secnumdepth             = "3";
537         h_shortcut[0]             = "idx";
538         h_spacing                 = "single";
539         h_save_transient_properties = "true";
540         h_suppress_date           = "false";
541         h_textclass               = "article";
542         h_tocdepth                = "3";
543         h_tracking_changes        = "false";
544         h_use_bibtopic            = "false";
545         h_use_indices             = "false";
546         h_use_geometry            = "false";
547         h_use_default_options     = "false";
548         h_use_hyperref            = "false";
549         h_use_refstyle            = false;
550         h_use_packages["amsmath"]    = "1";
551         h_use_packages["amssymb"]    = "0";
552         h_use_packages["cancel"]     = "0";
553         h_use_packages["esint"]      = "1";
554         h_use_packages["mhchem"]     = "0";
555         h_use_packages["mathdots"]   = "0";
556         h_use_packages["mathtools"]  = "0";
557         h_use_packages["stackrel"]   = "0";
558         h_use_packages["stmaryrd"]   = "0";
559         h_use_packages["undertilde"] = "0";
560 }
561
562
563 void Preamble::handle_hyperref(vector<string> & options)
564 {
565         // FIXME swallow inputencoding changes that might surround the
566         //       hyperref setup if it was written by LyX
567         h_use_hyperref = "true";
568         // swallow "unicode=true", since LyX does always write that
569         vector<string>::iterator it =
570                 find(options.begin(), options.end(), "unicode=true");
571         if (it != options.end())
572                 options.erase(it);
573         it = find(options.begin(), options.end(), "pdfusetitle");
574         if (it != options.end()) {
575                 h_pdf_pdfusetitle = "1";
576                 options.erase(it);
577         }
578         string bookmarks = process_keyval_opt(options, "bookmarks");
579         if (bookmarks == "true")
580                 h_pdf_bookmarks = "1";
581         else if (bookmarks == "false")
582                 h_pdf_bookmarks = "0";
583         if (h_pdf_bookmarks == "1") {
584                 string bookmarksnumbered =
585                         process_keyval_opt(options, "bookmarksnumbered");
586                 if (bookmarksnumbered == "true")
587                         h_pdf_bookmarksnumbered = "1";
588                 else if (bookmarksnumbered == "false")
589                         h_pdf_bookmarksnumbered = "0";
590                 string bookmarksopen =
591                         process_keyval_opt(options, "bookmarksopen");
592                 if (bookmarksopen == "true")
593                         h_pdf_bookmarksopen = "1";
594                 else if (bookmarksopen == "false")
595                         h_pdf_bookmarksopen = "0";
596                 if (h_pdf_bookmarksopen == "1") {
597                         string bookmarksopenlevel =
598                                 process_keyval_opt(options, "bookmarksopenlevel");
599                         if (!bookmarksopenlevel.empty())
600                                 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
601                 }
602         }
603         string breaklinks = process_keyval_opt(options, "breaklinks");
604         if (breaklinks == "true")
605                 h_pdf_breaklinks = "1";
606         else if (breaklinks == "false")
607                 h_pdf_breaklinks = "0";
608         string pdfborder = process_keyval_opt(options, "pdfborder");
609         if (pdfborder == "{0 0 0}")
610                 h_pdf_pdfborder = "1";
611         else if (pdfborder == "{0 0 1}")
612                 h_pdf_pdfborder = "0";
613         string backref = process_keyval_opt(options, "backref");
614         if (!backref.empty())
615                 h_pdf_backref = backref;
616         string colorlinks = process_keyval_opt(options, "colorlinks");
617         if (colorlinks == "true")
618                 h_pdf_colorlinks = "1";
619         else if (colorlinks == "false")
620                 h_pdf_colorlinks = "0";
621         string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
622         if (!pdfpagemode.empty())
623                 h_pdf_pagemode = pdfpagemode;
624         string pdftitle = process_keyval_opt(options, "pdftitle");
625         if (!pdftitle.empty()) {
626                 h_pdf_title = remove_braces(pdftitle);
627         }
628         string pdfauthor = process_keyval_opt(options, "pdfauthor");
629         if (!pdfauthor.empty()) {
630                 h_pdf_author = remove_braces(pdfauthor);
631         }
632         string pdfsubject = process_keyval_opt(options, "pdfsubject");
633         if (!pdfsubject.empty())
634                 h_pdf_subject = remove_braces(pdfsubject);
635         string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
636         if (!pdfkeywords.empty())
637                 h_pdf_keywords = remove_braces(pdfkeywords);
638         if (!options.empty()) {
639                 if (!h_pdf_quoted_options.empty())
640                         h_pdf_quoted_options += ',';
641                 h_pdf_quoted_options += join(options, ",");
642                 options.clear();
643         }
644 }
645
646
647 void Preamble::handle_geometry(vector<string> & options)
648 {
649         h_use_geometry = "true";
650         vector<string>::iterator it;
651         // paper orientation
652         if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
653                 h_paperorientation = "landscape";
654                 options.erase(it);
655         }
656         // paper size
657         // keyval version: "paper=letter"
658         string paper = process_keyval_opt(options, "paper");
659         if (!paper.empty())
660                 h_papersize = paper + "paper";
661         // alternative version: "letterpaper"
662         handle_opt(options, known_paper_sizes, h_papersize);
663         delete_opt(options, known_paper_sizes);
664         // page margins
665         char const * const * margin = known_paper_margins;
666         for (; *margin; ++margin) {
667                 string value = process_keyval_opt(options, *margin);
668                 if (!value.empty()) {
669                         int k = margin - known_paper_margins;
670                         string name = known_coded_paper_margins[k];
671                         h_margins += '\\' + name + ' ' + value + '\n';
672                 }
673         }
674 }
675
676
677 void Preamble::handle_package(Parser &p, string const & name,
678                               string const & opts, bool in_lyx_preamble)
679 {
680         vector<string> options = split_options(opts);
681         add_package(name, options);
682         char const * const * where = 0;
683
684         if (is_known(name, known_xetex_packages)) {
685                 xetex = true;
686                 h_use_non_tex_fonts = true;
687                 registerAutomaticallyLoadedPackage("fontspec");
688                 if (h_inputencoding == "auto")
689                         p.setEncoding("UTF-8");
690         }
691
692         // roman fonts
693         if (is_known(name, known_roman_fonts))
694                 h_font_roman[0] = name;
695
696         if (name == "fourier") {
697                 h_font_roman[0] = "utopia";
698                 // when font uses real small capitals
699                 if (opts == "expert")
700                         h_font_sc = "true";
701         }
702
703         if (name == "garamondx") {
704                 h_font_roman[0] = "garamondx";
705                 if (opts == "osfI")
706                         h_font_osf = "true";
707         }
708
709         if (name == "libertine") {
710                 h_font_roman[0] = "libertine";
711                 // this automatically invokes biolinum
712                 h_font_sans[0] = "biolinum";
713                 if (opts == "osf")
714                         h_font_osf = "true";
715                 else if (opts == "lining")
716                         h_font_osf = "false";
717         }
718
719         if (name == "libertine-type1") {
720                 h_font_roman[0] = "libertine";
721                 // NOTE: contrary to libertine.sty, libertine-type1
722                 // does not automatically invoke biolinum
723                 if (opts == "lining")
724                         h_font_osf = "false";
725                 else if (opts == "osf")
726                         h_font_osf = "true";
727         }
728
729         if (name == "mathdesign") {
730                 if (opts.find("charter") != string::npos)
731                         h_font_roman[0] = "md-charter";
732                 if (opts.find("garamond") != string::npos)
733                         h_font_roman[0] = "md-garamond";
734                 if (opts.find("utopia") != string::npos)
735                         h_font_roman[0] = "md-utopia";
736                 if (opts.find("expert") != string::npos) {
737                         h_font_sc = "true";
738                         h_font_osf = "true";
739                 }
740         }
741
742         else if (name == "mathpazo")
743                 h_font_roman[0] = "palatino";
744
745         else if (name == "mathptmx")
746                 h_font_roman[0] = "times";
747
748         // sansserif fonts
749         if (is_known(name, known_sans_fonts)) {
750                 h_font_sans[0] = name;
751                 if (options.size() >= 1) {
752                         if (scale_as_percentage(opts, h_font_sf_scale[0]))
753                                 options.clear();
754                 }
755         }
756
757         if (name == "biolinum-type1") {
758                 h_font_sans[0] = "biolinum";
759                 // biolinum can have several options, e.g. [osf,scaled=0.97]
760                 string::size_type pos = opts.find("osf");
761                 if (pos != string::npos)
762                         h_font_osf = "true";
763         }
764
765         // typewriter fonts
766         if (is_known(name, known_typewriter_fonts)) {
767                 // fourier can be set as roman font _only_
768                 // fourier as typewriter is handled in handling of \ttdefault
769                 if (name != "fourier") {
770                         h_font_typewriter[0] = name;
771                         if (options.size() >= 1) {
772                                 if (scale_as_percentage(opts, h_font_tt_scale[0]))
773                                         options.clear();
774                         }
775                 }
776         }
777
778         if (name == "libertineMono-type1") {
779                 h_font_typewriter[0] = "libertine-mono";
780         }
781
782         // font uses old-style figure
783         if (name == "eco")
784                 h_font_osf = "true";
785
786         // math fonts
787         if (is_known(name, known_math_fonts))
788                 h_font_math[0] = name;
789
790         if (name == "newtxmath") {
791                 if (opts.empty())
792                         h_font_math[0] = "newtxmath";
793                 else if (opts == "garamondx")
794                         h_font_math[0] = "garamondx-ntxm";
795                 else if (opts == "libertine")
796                         h_font_math[0] = "libertine-ntxm";
797                 else if (opts == "minion")
798                         h_font_math[0] = "minion-ntxm";
799         }
800
801         if (name == "iwona")
802                 if (opts == "math")
803                         h_font_math[0] = "iwona-math";
804
805         if (name == "kurier")
806                 if (opts == "math")
807                         h_font_math[0] = "kurier-math";
808
809         // after the detection and handling of special cases, we can remove the
810         // fonts, otherwise they would appear in the preamble, see bug #7856
811         if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
812                 ||      is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
813                 ;
814
815         else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
816                  name == "esint" || name == "mhchem" || name == "mathdots" ||
817                  name == "mathtools" || name == "stackrel" ||
818                  name == "stmaryrd" || name == "undertilde")
819                 h_use_packages[name] = "2";
820
821         else if (name == "babel") {
822                 h_language_package = "default";
823                 // One might think we would have to do nothing if babel is loaded
824                 // without any options to prevent pollution of the preamble with this
825                 // babel call in every roundtrip.
826                 // But the user could have defined babel-specific things afterwards. So
827                 // we need to keep it in the preamble to prevent cases like bug #7861.
828                 if (!opts.empty()) {
829                         // check if more than one option was used - used later for inputenc
830                         if (options.begin() != options.end() - 1)
831                                 one_language = false;
832                         // babel takes the last language of the option of its \usepackage
833                         // call as document language. If there is no such language option, the
834                         // last language in the documentclass options is used.
835                         handle_opt(options, known_languages, h_language);
836                         // translate the babel name to a LyX name
837                         h_language = babel2lyx(h_language);
838                         if (h_language == "japanese") {
839                                 // For Japanese, the encoding isn't indicated in the source
840                                 // file, and there's really not much we can do. We could
841                                 // 1) offer a list of possible encodings to choose from, or
842                                 // 2) determine the encoding of the file by inspecting it.
843                                 // For the time being, we leave the encoding alone so that
844                                 // we don't get iconv errors when making a wrong guess, and
845                                 // we will output a note at the top of the document
846                                 // explaining what to do.
847                                 Encoding const * const enc = encodings.fromIconvName(
848                                         p.getEncoding(), Encoding::japanese, false);
849                                 if (enc)
850                                         h_inputencoding = enc->name();
851                                 is_nonCJKJapanese = true;
852                                 // in this case babel can be removed from the preamble
853                                 registerAutomaticallyLoadedPackage("babel");
854                         } else {
855                                 // If babel is called with options, LyX puts them by default into the
856                                 // document class options. This works for most languages, except
857                                 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
858                                 // perhaps in future others.
859                                 // Therefore keep the babel call as it is as the user might have
860                                 // reasons for it.
861                                 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
862                         }
863                         delete_opt(options, known_languages);
864                 } else {
865                         h_preamble << "\\usepackage{babel}\n";
866                         explicit_babel = true;
867                 }
868         }
869
870         else if (name == "polyglossia") {
871                 h_language_package = "default";
872                 h_default_output_format = "pdf4";
873                 h_use_non_tex_fonts = true;
874                 xetex = true;
875                 registerAutomaticallyLoadedPackage("xunicode");
876                 if (h_inputencoding == "auto")
877                         p.setEncoding("UTF-8");
878         }
879
880         else if (name == "CJK") {
881                 // set the encoding to "auto" because it might be set to "default" by the babel handling
882                 // and this would not be correct for CJK
883                 if (h_inputencoding == "default")
884                         h_inputencoding = "auto";
885                 registerAutomaticallyLoadedPackage("CJK");
886         }
887
888         else if (name == "CJKutf8") {
889                 h_inputencoding = "utf8-cjk";
890                 p.setEncoding("UTF-8");
891                 registerAutomaticallyLoadedPackage("CJKutf8");
892         }
893
894         else if (name == "fontenc") {
895                 h_fontencoding = getStringFromVector(options, ",");
896                 /* We could do the following for better round trip support,
897                  * but this makes the document less portable, so I skip it:
898                 if (h_fontencoding == lyxrc.fontenc)
899                         h_fontencoding = "global";
900                  */
901                 options.clear();
902         }
903
904         else if (name == "inputenc" || name == "luainputenc") {
905                 // h_inputencoding is only set when there is not more than one
906                 // inputenc option because otherwise h_inputencoding must be
907                 // set to "auto" (the default encoding of the document language)
908                 // Therefore check that exactly one option is passed to inputenc.
909                 // It is also only set when there is not more than one babel
910                 // language option.
911                 if (!options.empty()) {
912                         string const encoding = options.back();
913                         Encoding const * const enc = encodings.fromLaTeXName(
914                                 encoding, Encoding::inputenc, true);
915                         if (!enc)
916                                 cerr << "Unknown encoding " << encoding << ". Ignoring." << std::endl;
917                         else {
918                                 if (!enc->unsafe() && options.size() == 1 && one_language == true)
919                                         h_inputencoding = enc->name();
920                                 p.setEncoding(enc->iconvName());
921                         }
922                         options.clear();
923                 }
924         }
925
926         else if (name == "srcltx") {
927                 h_output_sync = "1";
928                 if (!opts.empty()) {
929                         h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
930                         options.clear();
931                 } else
932                         h_output_sync_macro = "\\usepackage{srcltx}";
933         }
934
935         else if (is_known(name, known_old_language_packages)) {
936                 // known language packages from the times before babel
937                 // if they are found and not also babel, they will be used as
938                 // custom language package
939                 h_language_package = "\\usepackage{" + name + "}";
940         }
941
942         else if (name == "lyxskak") {
943                 // ignore this and its options
944                 const char * const o[] = {"ps", "mover", 0};
945                 delete_opt(options, o);
946         }
947
948         else if (is_known(name, known_lyx_packages) && options.empty()) {
949                 if (name == "splitidx")
950                         h_use_indices = "true";
951                 if (name == "refstyle")
952                         h_use_refstyle = true;
953                 else if (name == "prettyref")
954                         h_use_refstyle = false;
955                 if (!in_lyx_preamble) {
956                         h_preamble << package_beg_sep << name
957                                    << package_mid_sep << "\\usepackage{"
958                                    << name << '}';
959                         if (p.next_token().cat() == catNewline ||
960                             (p.next_token().cat() == catSpace &&
961                              p.next_next_token().cat() == catNewline))
962                                 h_preamble << '\n';
963                         h_preamble << package_end_sep;
964                 }
965         }
966
967         else if (name == "geometry")
968                 handle_geometry(options);
969
970         else if (name == "subfig")
971                 ; // ignore this FIXME: Use the package separator mechanism instead
972
973         else if ((where = is_known(name, known_languages)))
974                 h_language = known_coded_languages[where - known_languages];
975
976         else if (name == "natbib") {
977                 h_biblio_style = "plainnat";
978                 h_cite_engine = "natbib";
979                 h_cite_engine_type = "authoryear";
980                 vector<string>::iterator it =
981                         find(options.begin(), options.end(), "authoryear");
982                 if (it != options.end())
983                         options.erase(it);
984                 else {
985                         it = find(options.begin(), options.end(), "numbers");
986                         if (it != options.end()) {
987                                 h_cite_engine_type = "numerical";
988                                 options.erase(it);
989                         }
990                 }
991         }
992
993         else if (name == "jurabib") {
994                 h_biblio_style = "jurabib";
995                 h_cite_engine = "jurabib";
996                 h_cite_engine_type = "authoryear";
997         }
998
999         else if (name == "bibtopic")
1000                 h_use_bibtopic = "true";
1001
1002         else if (name == "hyperref")
1003                 handle_hyperref(options);
1004
1005         else if (name == "algorithm2e") {
1006                 // Load "algorithm2e" module
1007                 addModule("algorithm2e");
1008                 // Add the package options to the global document options
1009                 if (!options.empty()) {
1010                         if (h_options.empty())
1011                                 h_options = join(options, ",");
1012                         else
1013                                 h_options += ',' + join(options, ",");
1014                 }
1015         }
1016
1017         else if (!in_lyx_preamble) {
1018                 if (options.empty())
1019                         h_preamble << "\\usepackage{" << name << '}';
1020                 else {
1021                         h_preamble << "\\usepackage[" << opts << "]{"
1022                                    << name << '}';
1023                         options.clear();
1024                 }
1025                 if (p.next_token().cat() == catNewline ||
1026                     (p.next_token().cat() == catSpace &&
1027                      p.next_next_token().cat() == catNewline))
1028                         h_preamble << '\n';
1029         }
1030
1031         // We need to do something with the options...
1032         if (!options.empty())
1033                 cerr << "Ignoring options '" << join(options, ",")
1034                      << "' of package " << name << '.' << endl;
1035
1036         // remove the whitespace
1037         p.skip_spaces();
1038 }
1039
1040
1041 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1042 {
1043         while (p.good()) {
1044                 Token t = p.get_token();
1045                 if (t.cat() == catEscape &&
1046                     is_known(t.cs(), known_if_commands))
1047                         handle_if(p, in_lyx_preamble);
1048                 else {
1049                         if (!in_lyx_preamble)
1050                                 h_preamble << t.asInput();
1051                         if (t.cat() == catEscape && t.cs() == "fi")
1052                                 return;
1053                 }
1054         }
1055 }
1056
1057
1058 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1059 {
1060         // set the quote language
1061         // LyX only knows the following quotes languages:
1062         // english, swedish, german, polish, french and danish
1063         // (quotes for "japanese" and "chinese-traditional" are missing because
1064         //  they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
1065         // conversion list taken from
1066         // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1067         // (quotes for kazakh and interlingua are unknown)
1068         // danish
1069         if (is_known(h_language, known_danish_quotes_languages))
1070                 h_quotes_language = "danish";
1071         // french
1072         else if (is_known(h_language, known_french_quotes_languages))
1073                 h_quotes_language = "french";
1074         // german
1075         else if (is_known(h_language, known_german_quotes_languages))
1076                 h_quotes_language = "german";
1077         // polish
1078         else if (is_known(h_language, known_polish_quotes_languages))
1079                 h_quotes_language = "polish";
1080         // swedish
1081         else if (is_known(h_language, known_swedish_quotes_languages))
1082                 h_quotes_language = "swedish";
1083         //english
1084         else if (is_known(h_language, known_english_quotes_languages))
1085                 h_quotes_language = "english";
1086
1087         if (contains(h_float_placement, "H"))
1088                 registerAutomaticallyLoadedPackage("float");
1089         if (h_spacing != "single" && h_spacing != "default")
1090                 registerAutomaticallyLoadedPackage("setspace");
1091         if (h_use_packages["amsmath"] == "2") {
1092                 // amsbsy and amstext are already provided by amsmath
1093                 registerAutomaticallyLoadedPackage("amsbsy");
1094                 registerAutomaticallyLoadedPackage("amstext");
1095         }
1096
1097         // output the LyX file settings
1098         // Important: Keep the version formatting in sync with LyX and
1099         //            lyx2lyx (bug 7951)
1100         string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1101         os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1102            << lyx_version_minor << '\n'
1103            << "\\lyxformat " << LYX_FORMAT << '\n'
1104            << "\\begin_document\n"
1105            << "\\begin_header\n"
1106            << "\\save_transient_properties " << h_save_transient_properties << "\n"
1107            << "\\origin " << origin << "\n"
1108            << "\\textclass " << h_textclass << "\n";
1109         string const raw = subdoc ? empty_string() : h_preamble.str();
1110         if (!raw.empty()) {
1111                 os << "\\begin_preamble\n";
1112                 for (string::size_type i = 0; i < raw.size(); ++i) {
1113                         if (raw[i] == package_beg_sep) {
1114                                 // Here follows some package loading code that
1115                                 // must be skipped if the package is loaded
1116                                 // automatically.
1117                                 string::size_type j = raw.find(package_mid_sep, i);
1118                                 if (j == string::npos)
1119                                         return false;
1120                                 string::size_type k = raw.find(package_end_sep, j);
1121                                 if (k == string::npos)
1122                                         return false;
1123                                 string const package = raw.substr(i + 1, j - i - 1);
1124                                 string const replacement = raw.substr(j + 1, k - j - 1);
1125                                 if (auto_packages.find(package) == auto_packages.end())
1126                                         os << replacement;
1127                                 i = k;
1128                         } else
1129                                 os.put(raw[i]);
1130                 }
1131                 os << "\n\\end_preamble\n";
1132         }
1133         if (!h_options.empty())
1134                 os << "\\options " << h_options << "\n";
1135         os << "\\use_default_options " << h_use_default_options << "\n";
1136         if (!used_modules.empty()) {
1137                 os << "\\begin_modules\n";
1138                 vector<string>::const_iterator const end = used_modules.end();
1139                 vector<string>::const_iterator it = used_modules.begin();
1140                 for (; it != end; ++it)
1141                         os << *it << '\n';
1142                 os << "\\end_modules\n";
1143         }
1144         os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1145            << "\\language " << h_language << "\n"
1146            << "\\language_package " << h_language_package << "\n"
1147            << "\\inputencoding " << h_inputencoding << "\n"
1148            << "\\fontencoding " << h_fontencoding << "\n"
1149            << "\\font_roman \"" << h_font_roman[0]
1150            << "\" \"" << h_font_roman[1] << "\"\n"
1151            << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1152            << "\\font_typewriter \"" << h_font_typewriter[0]
1153            << "\" \"" << h_font_typewriter[1] << "\"\n"
1154            << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1155            << "\\font_default_family " << h_font_default_family << "\n"
1156            << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1157            << "\\font_sc " << h_font_sc << "\n"
1158            << "\\font_osf " << h_font_osf << "\n"
1159            << "\\font_sf_scale " << h_font_sf_scale[0]
1160            << ' ' << h_font_sf_scale[1] << '\n'
1161            << "\\font_tt_scale " << h_font_tt_scale[0]
1162            << ' ' << h_font_tt_scale[1] << '\n';
1163         if (!h_font_cjk.empty())
1164                 os << "\\font_cjk " << h_font_cjk << '\n';
1165         os << "\\graphics " << h_graphics << '\n'
1166            << "\\default_output_format " << h_default_output_format << "\n"
1167            << "\\output_sync " << h_output_sync << "\n";
1168         if (h_output_sync == "1")
1169                 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1170         os << "\\bibtex_command " << h_bibtex_command << "\n"
1171            << "\\index_command " << h_index_command << "\n";
1172         if (!h_float_placement.empty())
1173                 os << "\\float_placement " << h_float_placement << "\n";
1174         os << "\\paperfontsize " << h_paperfontsize << "\n"
1175            << "\\spacing " << h_spacing << "\n"
1176            << "\\use_hyperref " << h_use_hyperref << '\n';
1177         if (h_use_hyperref == "true") {
1178                 if (!h_pdf_title.empty())
1179                         os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1180                 if (!h_pdf_author.empty())
1181                         os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1182                 if (!h_pdf_subject.empty())
1183                         os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1184                 if (!h_pdf_keywords.empty())
1185                         os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1186                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1187                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1188                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1189                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1190                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1191                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1192                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1193                       "\\pdf_backref " << h_pdf_backref << "\n"
1194                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1195                 if (!h_pdf_pagemode.empty())
1196                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1197                 if (!h_pdf_quoted_options.empty())
1198                         os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1199         }
1200         os << "\\papersize " << h_papersize << "\n"
1201            << "\\use_geometry " << h_use_geometry << '\n';
1202         for (map<string, string>::const_iterator it = h_use_packages.begin();
1203              it != h_use_packages.end(); ++it)
1204                 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1205         os << "\\cite_engine " << h_cite_engine << '\n'
1206            << "\\cite_engine_type " << h_cite_engine_type << '\n'
1207            << "\\biblio_style " << h_biblio_style << "\n"
1208            << "\\use_bibtopic " << h_use_bibtopic << "\n"
1209            << "\\use_indices " << h_use_indices << "\n"
1210            << "\\paperorientation " << h_paperorientation << '\n'
1211            << "\\suppress_date " << h_suppress_date << '\n'
1212            << "\\justification " << h_justification << '\n'
1213            << "\\use_refstyle " << h_use_refstyle << '\n';
1214         if (!h_fontcolor.empty())
1215                 os << "\\fontcolor " << h_fontcolor << '\n';
1216         if (!h_notefontcolor.empty())
1217                 os << "\\notefontcolor " << h_notefontcolor << '\n';
1218         if (!h_backgroundcolor.empty())
1219                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1220         if (!h_boxbgcolor.empty())
1221                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1222         if (index_number != 0)
1223                 for (int i = 0; i < index_number; i++) {
1224                         os << "\\index " << h_index[i] << '\n'
1225                            << "\\shortcut " << h_shortcut[i] << '\n'
1226                            << "\\color " << h_color << '\n'
1227                            << "\\end_index\n";
1228                 }
1229         else {
1230                 os << "\\index " << h_index[0] << '\n'
1231                    << "\\shortcut " << h_shortcut[0] << '\n'
1232                    << "\\color " << h_color << '\n'
1233                    << "\\end_index\n";
1234         }
1235         os << h_margins
1236            << "\\secnumdepth " << h_secnumdepth << "\n"
1237            << "\\tocdepth " << h_tocdepth << "\n"
1238            << "\\paragraph_separation " << h_paragraph_separation << "\n";
1239         if (h_paragraph_separation == "skip")
1240                 os << "\\defskip " << h_defskip << "\n";
1241         else
1242                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1243         os << "\\quotes_language " << h_quotes_language << "\n"
1244            << "\\papercolumns " << h_papercolumns << "\n"
1245            << "\\papersides " << h_papersides << "\n"
1246            << "\\paperpagestyle " << h_paperpagestyle << "\n";
1247         if (!h_listings_params.empty())
1248                 os << "\\listings_params " << h_listings_params << "\n";
1249         os << "\\tracking_changes " << h_tracking_changes << "\n"
1250            << "\\output_changes " << h_output_changes << "\n"
1251            << "\\html_math_output " << h_html_math_output << "\n"
1252            << "\\html_css_as_file " << h_html_css_as_file << "\n"
1253            << "\\html_be_strict " << h_html_be_strict << "\n"
1254            << authors_
1255            << "\\end_header\n\n"
1256            << "\\begin_body\n";
1257         return true;
1258 }
1259
1260
1261 void Preamble::parse(Parser & p, string const & forceclass,
1262                      TeX2LyXDocClass & tc)
1263 {
1264         // initialize fixed types
1265         special_columns_['D'] = 3;
1266         bool is_full_document = false;
1267         bool is_lyx_file = false;
1268         bool in_lyx_preamble = false;
1269
1270         // determine whether this is a full document or a fragment for inclusion
1271         while (p.good()) {
1272                 Token const & t = p.get_token();
1273
1274                 if (t.cat() == catEscape && t.cs() == "documentclass") {
1275                         is_full_document = true;
1276                         break;
1277                 }
1278         }
1279         p.reset();
1280
1281         while (is_full_document && p.good()) {
1282                 Token const & t = p.get_token();
1283
1284 #ifdef FILEDEBUG
1285                 cerr << "t: " << t << "\n";
1286 #endif
1287
1288                 //
1289                 // cat codes
1290                 //
1291                 if (!in_lyx_preamble &&
1292                     (t.cat() == catLetter ||
1293                      t.cat() == catSuper ||
1294                      t.cat() == catSub ||
1295                      t.cat() == catOther ||
1296                      t.cat() == catMath ||
1297                      t.cat() == catActive ||
1298                      t.cat() == catBegin ||
1299                      t.cat() == catEnd ||
1300                      t.cat() == catAlign ||
1301                      t.cat() == catParameter))
1302                         h_preamble << t.cs();
1303
1304                 else if (!in_lyx_preamble &&
1305                          (t.cat() == catSpace || t.cat() == catNewline))
1306                         h_preamble << t.asInput();
1307
1308                 else if (t.cat() == catComment) {
1309                         static regex const islyxfile("%% LyX .* created this file");
1310                         static regex const usercommands("User specified LaTeX commands");
1311
1312                         string const comment = t.asInput();
1313
1314                         // magically switch encoding default if it looks like XeLaTeX
1315                         static string const magicXeLaTeX =
1316                                 "% This document must be compiled with XeLaTeX ";
1317                         if (comment.size() > magicXeLaTeX.size()
1318                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1319                                   && h_inputencoding == "auto") {
1320                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
1321                                 h_inputencoding = "utf8";
1322                         }
1323                         smatch sub;
1324                         if (regex_search(comment, sub, islyxfile)) {
1325                                 is_lyx_file = true;
1326                                 in_lyx_preamble = true;
1327                         } else if (is_lyx_file
1328                                    && regex_search(comment, sub, usercommands))
1329                                 in_lyx_preamble = false;
1330                         else if (!in_lyx_preamble)
1331                                 h_preamble << t.asInput();
1332                 }
1333
1334                 else if (t.cs() == "pagestyle")
1335                         h_paperpagestyle = p.verbatim_item();
1336
1337                 else if (t.cs() == "setdefaultlanguage") {
1338                         xetex = true;
1339                         // We don't yet care about non-language variant options
1340                         // because LyX doesn't support this yet, see bug #8214
1341                         if (p.hasOpt()) {
1342                                 string langopts = p.getOpt();
1343                                 // check if the option contains a variant, if yes, extract it
1344                                 string::size_type pos_var = langopts.find("variant");
1345                                 string::size_type i = langopts.find(',', pos_var);
1346                                 string::size_type k = langopts.find('=', pos_var);
1347                                 if (pos_var != string::npos){
1348                                         string variant;
1349                                         if (i == string::npos)
1350                                                 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1351                                         else
1352                                                 variant = langopts.substr(k + 1, i - k - 1);
1353                                         h_language = variant;
1354                                 }
1355                                 p.verbatim_item();
1356                         } else
1357                                 h_language = p.verbatim_item();
1358                         //finally translate the poyglossia name to a LyX name
1359                         h_language = polyglossia2lyx(h_language);
1360                 }
1361
1362                 else if (t.cs() == "setotherlanguage") {
1363                         // We don't yet care about the option because LyX doesn't
1364                         // support this yet, see bug #8214
1365                         p.hasOpt() ? p.getOpt() : string();
1366                         p.verbatim_item();
1367                 }
1368
1369                 else if (t.cs() == "setmainfont") {
1370                         // we don't care about the option
1371                         p.hasOpt() ? p.getOpt() : string();
1372                         h_font_roman[1] = p.getArg('{', '}');
1373                 }
1374
1375                 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1376                         // LyX currently only supports the scale option
1377                         string scale;
1378                         if (p.hasOpt()) {
1379                                 string fontopts = p.getArg('[', ']');
1380                                 // check if the option contains a scaling, if yes, extract it
1381                                 string::size_type pos = fontopts.find("Scale");
1382                                 if (pos != string::npos) {
1383                                         string::size_type i = fontopts.find(',', pos);
1384                                         if (i == string::npos)
1385                                                 scale_as_percentage(fontopts.substr(pos + 1), scale);
1386                                         else
1387                                                 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1388                                 }
1389                         }
1390                         if (t.cs() == "setsansfont") {
1391                                 if (!scale.empty())
1392                                         h_font_sf_scale[1] = scale;
1393                                 h_font_sans[1] = p.getArg('{', '}');
1394                         } else {
1395                                 if (!scale.empty())
1396                                         h_font_tt_scale[1] = scale;
1397                                 h_font_typewriter[1] = p.getArg('{', '}');
1398                         }
1399                 }
1400
1401                 else if (t.cs() == "date") {
1402                         string argument = p.getArg('{', '}');
1403                         if (argument.empty())
1404                                 h_suppress_date = "true";
1405                         else
1406                                 h_preamble << t.asInput() << '{' << argument << '}';
1407                 }
1408
1409                 else if (t.cs() == "color") {
1410                         string const space =
1411                                 (p.hasOpt() ? p.getOpt() : string());
1412                         string argument = p.getArg('{', '}');
1413                         // check the case that a standard color is used
1414                         if (space.empty() && is_known(argument, known_basic_colors)) {
1415                                 h_fontcolor = rgbcolor2code(argument);
1416                                 preamble.registerAutomaticallyLoadedPackage("color");
1417                         } else if (space.empty() && argument == "document_fontcolor")
1418                                 preamble.registerAutomaticallyLoadedPackage("color");
1419                         // check the case that LyX's document_fontcolor is defined
1420                         // but not used for \color
1421                         else {
1422                                 h_preamble << t.asInput();
1423                                 if (!space.empty())
1424                                         h_preamble << space;
1425                                 h_preamble << '{' << argument << '}';
1426                                 // the color might already be set because \definecolor
1427                                 // is parsed before this
1428                                 h_fontcolor = "";
1429                         }
1430                 }
1431
1432                 else if (t.cs() == "pagecolor") {
1433                         string argument = p.getArg('{', '}');
1434                         // check the case that a standard color is used
1435                         if (is_known(argument, known_basic_colors)) {
1436                                 h_backgroundcolor = rgbcolor2code(argument);
1437                         } else if (argument == "page_backgroundcolor")
1438                                 preamble.registerAutomaticallyLoadedPackage("color");
1439                         // check the case that LyX's page_backgroundcolor is defined
1440                         // but not used for \pagecolor
1441                         else {
1442                                 h_preamble << t.asInput() << '{' << argument << '}';
1443                                 // the color might already be set because \definecolor
1444                                 // is parsed before this
1445                                 h_backgroundcolor = "";
1446                         }
1447                 }
1448
1449                 else if (t.cs() == "makeatletter") {
1450                         // LyX takes care of this
1451                         p.setCatcode('@', catLetter);
1452                 }
1453
1454                 else if (t.cs() == "makeatother") {
1455                         // LyX takes care of this
1456                         p.setCatcode('@', catOther);
1457                 }
1458
1459                 else if (t.cs() == "makeindex") {
1460                         // LyX will re-add this if a print index command is found
1461                         p.skip_spaces();
1462                 }
1463
1464                 else if (t.cs() == "newindex") {
1465                         string const indexname = p.getArg('[', ']');
1466                         string const shortcut = p.verbatim_item();
1467                         if (!indexname.empty())
1468                                 h_index[index_number] = indexname;
1469                         else
1470                                 h_index[index_number] = shortcut;
1471                         h_shortcut[index_number] = shortcut;
1472                         index_number += 1;
1473                         p.skip_spaces();
1474                 }
1475
1476                 else if (t.cs() == "RS@ifundefined") {
1477                         string const name = p.verbatim_item();
1478                         string const body1 = p.verbatim_item();
1479                         string const body2 = p.verbatim_item();
1480                         // only non-lyxspecific stuff
1481                         if (in_lyx_preamble &&
1482                             (name == "subsecref" || name == "thmref" || name == "lemref"))
1483                                 p.skip_spaces();
1484                         else {
1485                                 ostringstream ss;
1486                                 ss << '\\' << t.cs();
1487                                 ss << '{' << name << '}'
1488                                    << '{' << body1 << '}'
1489                                    << '{' << body2 << '}';
1490                                 h_preamble << ss.str();
1491                         }
1492                 }
1493
1494                 else if (t.cs() == "AtBeginDocument") {
1495                         string const name = p.verbatim_item();
1496                         // only non-lyxspecific stuff
1497                         if (in_lyx_preamble &&
1498                             (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1499                                 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1500                                 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1501                                 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1502                                 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1503                                 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1504                                 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1505                                 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1506                                 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1507                                 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1508                                 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1509                                 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1510                                 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1511                                 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1512                                 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1513                                 p.skip_spaces();
1514                         else {
1515                                 ostringstream ss;
1516                                 ss << '\\' << t.cs();
1517                                 ss << '{' << name << '}';
1518                                 h_preamble << ss.str();
1519                         }
1520                 }
1521
1522                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1523                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1524                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
1525                                 || t.cs() == "DeclareRobustCommand"
1526                       || t.cs() == "DeclareRobustCommandx"
1527                                 || t.cs() == "ProvideTextCommandDefault"
1528                                 || t.cs() == "DeclareMathAccent") {
1529                         bool star = false;
1530                         if (p.next_token().character() == '*') {
1531                                 p.get_token();
1532                                 star = true;
1533                         }
1534                         string const name = p.verbatim_item();
1535                         string const opt1 = p.getFullOpt();
1536                         string const opt2 = p.getFullOpt();
1537                         string const body = p.verbatim_item();
1538                         // store the in_lyx_preamble setting
1539                         bool const was_in_lyx_preamble = in_lyx_preamble;
1540                         // font settings
1541                         if (name == "\\rmdefault")
1542                                 if (is_known(body, known_roman_fonts)) {
1543                                         h_font_roman[0] = body;
1544                                         p.skip_spaces();
1545                                         in_lyx_preamble = true;
1546                                 }
1547                         if (name == "\\sfdefault")
1548                                 if (is_known(body, known_sans_fonts)) {
1549                                         h_font_sans[0] = body;
1550                                         p.skip_spaces();
1551                                         in_lyx_preamble = true;
1552                                 }
1553                         if (name == "\\ttdefault")
1554                                 if (is_known(body, known_typewriter_fonts)) {
1555                                         h_font_typewriter[0] = body;
1556                                         p.skip_spaces();
1557                                         in_lyx_preamble = true;
1558                                 }
1559                         if (name == "\\familydefault") {
1560                                 string family = body;
1561                                 // remove leading "\"
1562                                 h_font_default_family = family.erase(0,1);
1563                                 p.skip_spaces();
1564                                 in_lyx_preamble = true;
1565                         }
1566
1567                         // remove LyX-specific definitions that are re-added by LyX
1568                         // if necessary
1569                         // \lyxline is an ancient command that is converted by tex2lyx into
1570                         // a \rule therefore remove its preamble code
1571                         if (name == "\\lyxdot" || name == "\\lyxarrow"
1572                             || name == "\\lyxline" || name == "\\LyX") {
1573                                 p.skip_spaces();
1574                                 in_lyx_preamble = true;
1575                         }
1576
1577                         // Add the command to the known commands
1578                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1579
1580                         // only non-lyxspecific stuff
1581                         if (!in_lyx_preamble) {
1582                                 ostringstream ss;
1583                                 ss << '\\' << t.cs();
1584                                 if (star)
1585                                         ss << '*';
1586                                 ss << '{' << name << '}' << opt1 << opt2
1587                                    << '{' << body << "}";
1588                                 h_preamble << ss.str();
1589 /*
1590                                 ostream & out = in_preamble ? h_preamble : os;
1591                                 out << "\\" << t.cs() << "{" << name << "}"
1592                                     << opts << "{" << body << "}";
1593 */
1594                         }
1595                         // restore the in_lyx_preamble setting
1596                         in_lyx_preamble = was_in_lyx_preamble;
1597                 }
1598
1599                 else if (t.cs() == "documentclass") {
1600                         vector<string>::iterator it;
1601                         vector<string> opts = split_options(p.getArg('[', ']'));
1602                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1603                         delete_opt(opts, known_fontsizes);
1604                         // delete "pt" at the end
1605                         string::size_type i = h_paperfontsize.find("pt");
1606                         if (i != string::npos)
1607                                 h_paperfontsize.erase(i);
1608                         // The documentclass options are always parsed before the options
1609                         // of the babel call so that a language cannot overwrite the babel
1610                         // options.
1611                         handle_opt(opts, known_languages, h_language);
1612                         delete_opt(opts, known_languages);
1613
1614                         // paper orientation
1615                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1616                                 h_paperorientation = "landscape";
1617                                 opts.erase(it);
1618                         }
1619                         // paper sides
1620                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1621                                  != opts.end()) {
1622                                 h_papersides = "1";
1623                                 opts.erase(it);
1624                         }
1625                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1626                                  != opts.end()) {
1627                                 h_papersides = "2";
1628                                 opts.erase(it);
1629                         }
1630                         // paper columns
1631                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1632                                  != opts.end()) {
1633                                 h_papercolumns = "1";
1634                                 opts.erase(it);
1635                         }
1636                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1637                                  != opts.end()) {
1638                                 h_papercolumns = "2";
1639                                 opts.erase(it);
1640                         }
1641                         // paper sizes
1642                         // some size options are known to any document classes, other sizes
1643                         // are handled by the \geometry command of the geometry package
1644                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1645                         delete_opt(opts, known_class_paper_sizes);
1646                         // the remaining options
1647                         h_options = join(opts, ",");
1648                         // FIXME This does not work for classes that have a
1649                         //       different name in LyX than in LaTeX
1650                         h_textclass = p.getArg('{', '}');
1651                         p.skip_spaces();
1652                 }
1653
1654                 else if (t.cs() == "usepackage") {
1655                         string const options = p.getArg('[', ']');
1656                         string const name = p.getArg('{', '}');
1657                         vector<string> vecnames;
1658                         split(name, vecnames, ',');
1659                         vector<string>::const_iterator it  = vecnames.begin();
1660                         vector<string>::const_iterator end = vecnames.end();
1661                         for (; it != end; ++it)
1662                                 handle_package(p, trimSpaceAndEol(*it), options,
1663                                                in_lyx_preamble);
1664                 }
1665
1666                 else if (t.cs() == "inputencoding") {
1667                         string const encoding = p.getArg('{','}');
1668                         Encoding const * const enc = encodings.fromLaTeXName(
1669                                 encoding, Encoding::inputenc, true);
1670                         if (!enc)
1671                                 cerr << "Unknown encoding " << encoding << ". Ignoring." << std::endl;
1672                         else {
1673                                 if (!enc->unsafe())
1674                                         h_inputencoding = enc->name();
1675                                 p.setEncoding(enc->iconvName());
1676                         }
1677                 }
1678
1679                 else if (t.cs() == "newenvironment") {
1680                         string const name = p.getArg('{', '}');
1681                         string const opt1 = p.getFullOpt();
1682                         string const opt2 = p.getFullOpt();
1683                         string const beg = p.verbatim_item();
1684                         string const end = p.verbatim_item();
1685                         if (!in_lyx_preamble) {
1686                                 h_preamble << "\\newenvironment{" << name
1687                                            << '}' << opt1 << opt2 << '{'
1688                                            << beg << "}{" << end << '}';
1689                         }
1690                         add_known_environment(name, opt1, !opt2.empty(),
1691                                               from_utf8(beg), from_utf8(end));
1692
1693                 }
1694
1695                 else if (t.cs() == "newtheorem") {
1696                         string const name = p.getArg('{', '}');
1697                         string const opt1 = p.getFullOpt();
1698                         string const opt2 = p.getFullOpt();
1699                         string const body = p.verbatim_item();
1700                         string const opt3 = p.getFullOpt();
1701
1702                         string const complete = "\\newtheorem{" + name + '}' +
1703                                           opt1 + opt2 + '{' + body + '}' + opt3;
1704
1705                         add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1706
1707                         if (!in_lyx_preamble)
1708                                 h_preamble << complete;
1709                 }
1710
1711                 else if (t.cs() == "def") {
1712                         string name = p.get_token().cs();
1713                         // In fact, name may be more than the name:
1714                         // In the test case of bug 8116
1715                         // name == "csname SF@gobble@opt \endcsname".
1716                         // Therefore, we need to use asInput() instead of cs().
1717                         while (p.next_token().cat() != catBegin)
1718                                 name += p.get_token().asInput();
1719                         if (!in_lyx_preamble)
1720                                 h_preamble << "\\def\\" << name << '{'
1721                                            << p.verbatim_item() << "}";
1722                 }
1723
1724                 else if (t.cs() == "newcolumntype") {
1725                         string const name = p.getArg('{', '}');
1726                         trimSpaceAndEol(name);
1727                         int nargs = 0;
1728                         string opts = p.getOpt();
1729                         if (!opts.empty()) {
1730                                 istringstream is(string(opts, 1));
1731                                 is >> nargs;
1732                         }
1733                         special_columns_[name[0]] = nargs;
1734                         h_preamble << "\\newcolumntype{" << name << "}";
1735                         if (nargs)
1736                                 h_preamble << "[" << nargs << "]";
1737                         h_preamble << "{" << p.verbatim_item() << "}";
1738                 }
1739
1740                 else if (t.cs() == "setcounter") {
1741                         string const name = p.getArg('{', '}');
1742                         string const content = p.getArg('{', '}');
1743                         if (name == "secnumdepth")
1744                                 h_secnumdepth = content;
1745                         else if (name == "tocdepth")
1746                                 h_tocdepth = content;
1747                         else
1748                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1749                 }
1750
1751                 else if (t.cs() == "setlength") {
1752                         string const name = p.verbatim_item();
1753                         string const content = p.verbatim_item();
1754                         // the paragraphs are only not indented when \parindent is set to zero
1755                         if (name == "\\parindent" && content != "") {
1756                                 if (content[0] == '0')
1757                                         h_paragraph_separation = "skip";
1758                                 else
1759                                         h_paragraph_indentation = translate_len(content);
1760                         } else if (name == "\\parskip") {
1761                                 if (content == "\\smallskipamount")
1762                                         h_defskip = "smallskip";
1763                                 else if (content == "\\medskipamount")
1764                                         h_defskip = "medskip";
1765                                 else if (content == "\\bigskipamount")
1766                                         h_defskip = "bigskip";
1767                                 else
1768                                         h_defskip = translate_len(content);
1769                         } else
1770                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1771                 }
1772
1773                 else if (t.cs() == "onehalfspacing")
1774                         h_spacing = "onehalf";
1775
1776                 else if (t.cs() == "doublespacing")
1777                         h_spacing = "double";
1778
1779                 else if (t.cs() == "setstretch")
1780                         h_spacing = "other " + p.verbatim_item();
1781
1782                 else if (t.cs() == "synctex") {
1783                         // the scheme is \synctex=value
1784                         // where value can only be "1" or "-1"
1785                         h_output_sync = "1";
1786                         // there can be any character behind the value (e.g. a linebreak or a '\'
1787                         // therefore we extract it char by char
1788                         p.get_token();
1789                         string value = p.get_token().asInput();
1790                         if (value == "-")
1791                                 value += p.get_token().asInput();
1792                         h_output_sync_macro = "\\synctex=" + value;
1793                 }
1794
1795                 else if (t.cs() == "begin") {
1796                         string const name = p.getArg('{', '}');
1797                         if (name == "document")
1798                                 break;
1799                         h_preamble << "\\begin{" << name << "}";
1800                 }
1801
1802                 else if (t.cs() == "geometry") {
1803                         vector<string> opts = split_options(p.getArg('{', '}'));
1804                         handle_geometry(opts);
1805                 }
1806
1807                 else if (t.cs() == "definecolor") {
1808                         string const color = p.getArg('{', '}');
1809                         string const space = p.getArg('{', '}');
1810                         string const value = p.getArg('{', '}');
1811                         if (color == "document_fontcolor" && space == "rgb") {
1812                                 RGBColor c(RGBColorFromLaTeX(value));
1813                                 h_fontcolor = X11hexname(c);
1814                         } else if (color == "note_fontcolor" && space == "rgb") {
1815                                 RGBColor c(RGBColorFromLaTeX(value));
1816                                 h_notefontcolor = X11hexname(c);
1817                         } else if (color == "page_backgroundcolor" && space == "rgb") {
1818                                 RGBColor c(RGBColorFromLaTeX(value));
1819                                 h_backgroundcolor = X11hexname(c);
1820                         } else if (color == "shadecolor" && space == "rgb") {
1821                                 RGBColor c(RGBColorFromLaTeX(value));
1822                                 h_boxbgcolor = X11hexname(c);
1823                         } else {
1824                                 h_preamble << "\\definecolor{" << color
1825                                            << "}{" << space << "}{" << value
1826                                            << '}';
1827                         }
1828                 }
1829
1830                 else if (t.cs() == "bibliographystyle")
1831                         h_biblio_style = p.verbatim_item();
1832
1833                 else if (t.cs() == "jurabibsetup") {
1834                         // FIXME p.getArg('{', '}') is most probably wrong (it
1835                         //       does not handle nested braces).
1836                         //       Use p.verbatim_item() instead.
1837                         vector<string> jurabibsetup =
1838                                 split_options(p.getArg('{', '}'));
1839                         // add jurabibsetup to the jurabib package options
1840                         add_package("jurabib", jurabibsetup);
1841                         if (!jurabibsetup.empty()) {
1842                                 h_preamble << "\\jurabibsetup{"
1843                                            << join(jurabibsetup, ",") << '}';
1844                         }
1845                 }
1846
1847                 else if (t.cs() == "hypersetup") {
1848                         vector<string> hypersetup =
1849                                 split_options(p.verbatim_item());
1850                         // add hypersetup to the hyperref package options
1851                         handle_hyperref(hypersetup);
1852                         if (!hypersetup.empty()) {
1853                                 h_preamble << "\\hypersetup{"
1854                                            << join(hypersetup, ",") << '}';
1855                         }
1856                 }
1857
1858                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1859                         // prevent misparsing of \usepackage if it is used
1860                         // as an argument (see e.g. our own output of
1861                         // \@ifundefined above)
1862                         string const arg1 = p.verbatim_item();
1863                         string const arg2 = p.verbatim_item();
1864                         string const arg3 = p.verbatim_item();
1865                         // test case \@ifundefined{date}{}{\date{}}
1866                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
1867                             arg2.empty() && arg3 == "\\date{}") {
1868                                 h_suppress_date = "true";
1869                         // older tex2lyx versions did output
1870                         // \@ifundefined{definecolor}{\usepackage{color}}{}
1871                         } else if (t.cs() == "@ifundefined" &&
1872                                    arg1 == "definecolor" &&
1873                                    arg2 == "\\usepackage{color}" &&
1874                                    arg3.empty()) {
1875                                 if (!in_lyx_preamble)
1876                                         h_preamble << package_beg_sep
1877                                                    << "color"
1878                                                    << package_mid_sep
1879                                                    << "\\@ifundefined{definecolor}{color}{}"
1880                                                    << package_end_sep;
1881                         // test for case
1882                         //\@ifundefined{showcaptionsetup}{}{%
1883                         // \PassOptionsToPackage{caption=false}{subfig}}
1884                         // that LyX uses for subfloats
1885                         } else if (t.cs() == "@ifundefined" &&
1886                                    arg1 == "showcaptionsetup" && arg2.empty()
1887                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1888                                 ; // do nothing
1889                         } else if (!in_lyx_preamble) {
1890                                 h_preamble << t.asInput()
1891                                            << '{' << arg1 << '}'
1892                                            << '{' << arg2 << '}'
1893                                            << '{' << arg3 << '}';
1894                         }
1895                 }
1896
1897                 else if (is_known(t.cs(), known_if_commands)) {
1898                         // must not parse anything in conditional code, since
1899                         // LyX would output the parsed contents unconditionally
1900                         if (!in_lyx_preamble)
1901                                 h_preamble << t.asInput();
1902                         handle_if(p, in_lyx_preamble);
1903                 }
1904
1905                 else if (!t.cs().empty() && !in_lyx_preamble)
1906                         h_preamble << '\\' << t.cs();
1907         }
1908
1909         // remove the whitespace
1910         p.skip_spaces();
1911
1912         // Force textclass if the user wanted it
1913         if (!forceclass.empty())
1914                 h_textclass = forceclass;
1915         tc.setName(h_textclass);
1916         if (!tc.load()) {
1917                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1918                 exit(EXIT_FAILURE);
1919         }
1920         if (h_papersides.empty()) {
1921                 ostringstream ss;
1922                 ss << tc.sides();
1923                 h_papersides = ss.str();
1924         }
1925
1926         // If the CJK package is used we cannot set the document language from
1927         // the babel options. Instead, we guess which language is used most
1928         // and set this one.
1929         default_language = h_language;
1930         if (is_full_document &&
1931             (auto_packages.find("CJK") != auto_packages.end() ||
1932              auto_packages.find("CJKutf8") != auto_packages.end())) {
1933                 p.pushPosition();
1934                 h_language = guessLanguage(p, default_language);
1935                 p.popPosition();
1936                 if (explicit_babel && h_language != default_language) {
1937                         // We set the document language to a CJK language,
1938                         // but babel is explicitly called in the user preamble
1939                         // without options. LyX will not add the default
1940                         // language to the document options if it is either
1941                         // english, or no text is set as default language.
1942                         // Therefore we need to add a language option explicitly.
1943                         // FIXME: It would be better to remove all babel calls
1944                         //        from the user preamble, but this is difficult
1945                         //        without re-introducing bug 7861.
1946                         if (h_options.empty())
1947                                 h_options = lyx2babel(default_language);
1948                         else
1949                                 h_options += ',' + lyx2babel(default_language);
1950                 }
1951         }
1952 }
1953
1954
1955 string babel2lyx(string const & language)
1956 {
1957         char const * const * where = is_known(language, known_languages);
1958         if (where)
1959                 return known_coded_languages[where - known_languages];
1960         return language;
1961 }
1962
1963
1964 string lyx2babel(string const & language)
1965 {
1966         char const * const * where = is_known(language, known_coded_languages);
1967         if (where)
1968                 return known_languages[where - known_coded_languages];
1969         return language;
1970 }
1971
1972
1973 string Preamble::polyglossia2lyx(string const & language)
1974 {
1975         char const * const * where = is_known(language, polyglossia_languages);
1976         if (where)
1977                 return coded_polyglossia_languages[where - polyglossia_languages];
1978         return language;
1979 }
1980
1981
1982 string rgbcolor2code(string const & name)
1983 {
1984         char const * const * where = is_known(name, known_basic_colors);
1985         if (where) {
1986                 // "red", "green" etc
1987                 return known_basic_color_codes[where - known_basic_colors];
1988         }
1989         // "255,0,0", "0,255,0" etc
1990         RGBColor c(RGBColorFromLaTeX(name));
1991         return X11hexname(c);
1992 }
1993
1994 // }])
1995
1996
1997 } // namespace lyx