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