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