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