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