]> git.lyx.org Git - lyx.git/blob - src/tex2lyx/Preamble.cpp
ad472f89ec75839d315bd721e9fafa252828540a
[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 == "subscript")
675                 ; // ignore this
676
677         else if (name == "color") {
678                 if (!in_lyx_preamble)
679                         h_preamble << package_beg_sep << name
680                                    << package_mid_sep << "\\usepackage{"
681                                    << name << '}' << package_end_sep;
682         }
683
684         else if (name == "graphicx")
685                 ; // ignore this
686
687         else if (name == "setspace")
688                 ; // ignore this
689
690 #if 0
691         // do not ignore as long as we don't support all commands (e.g. \xout is missing)
692         // and as long as we don't support change tracking
693         else if (name == "ulem")
694                 ; // ignore this
695 #endif
696
697         else if (name == "geometry")
698                 ; // Ignore this, the geometry settings are made by the \geometry
699                   // command. This command is handled below.
700
701         else if (name == "rotfloat")
702                 ; // ignore this
703
704         else if (name == "wrapfig")
705                 ; // ignore this
706
707         else if (name == "subfig")
708                 ; // ignore this
709
710         else if (is_known(name, known_languages))
711                 h_language = name;
712
713         else if (name == "natbib") {
714                 h_cite_engine = "natbib_authoryear";
715                 vector<string>::iterator it =
716                         find(options.begin(), options.end(), "authoryear");
717                 if (it != options.end())
718                         options.erase(it);
719                 else {
720                         it = find(options.begin(), options.end(), "numbers");
721                         if (it != options.end()) {
722                                 h_cite_engine = "natbib_numerical";
723                                 options.erase(it);
724                         }
725                 }
726         }
727
728         else if (name == "jurabib")
729                 h_cite_engine = "jurabib";
730
731         else if (name == "hyperref")
732                 handle_hyperref(options);
733
734         else if (!in_lyx_preamble) {
735                 if (options.empty())
736                         h_preamble << "\\usepackage{" << name << "}";
737                 else {
738                         h_preamble << "\\usepackage[" << opts << "]{" 
739                                    << name << "}";
740                         options.clear();
741                 }
742         }
743
744         // We need to do something with the options...
745         if (!options.empty())
746                 cerr << "Ignoring options '" << join(options, ",")
747                      << "' of package " << name << '.' << endl;
748
749         // remove the whitespace
750         p.skip_spaces();
751 }
752
753
754 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
755 {
756         while (p.good()) {
757                 Token t = p.get_token();
758                 if (t.cat() == catEscape &&
759                     is_known(t.cs(), known_if_commands))
760                         handle_if(p, in_lyx_preamble);
761                 else {
762                         if (!in_lyx_preamble)
763                                 h_preamble << t.asInput();
764                         if (t.cat() == catEscape && t.cs() == "fi")
765                                 return;
766                 }
767         }
768 }
769
770
771 bool Preamble::writeLyXHeader(ostream & os)
772 {
773         // translate from babel to LyX names
774         h_language = babel2lyx(h_language);
775
776         // set the quote language
777         // LyX only knows the following quotes languages:
778         // english, swedish, german, polish, french and danish
779         // (quotes for "japanese" and "chinese-traditional" are missing because
780         //  they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
781         // conversion list taken from
782         // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
783         // (quotes for kazakh and interlingua are unknown)
784         // danish
785         if (h_language == "danish")
786                 h_quotes_language = "danish";
787         // french
788         else if (is_known(h_language, known_french_quotes_languages))
789                 h_quotes_language = "french";
790         // german
791         else if (is_known(h_language, known_german_quotes_languages))
792                 h_quotes_language = "german";
793         // polish
794         else if (is_known(h_language, known_polish_quotes_languages))
795                 h_quotes_language = "polish";
796         // swedish
797         else if (is_known(h_language, known_swedish_quotes_languages))
798                 h_quotes_language = "swedish";
799         //english
800         else if (is_known(h_language, known_english_quotes_languages))
801                 h_quotes_language = "english";
802
803         // output the LyX file settings
804         os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
805            << "\\lyxformat " << LYX_FORMAT << '\n'
806            << "\\begin_document\n"
807            << "\\begin_header\n"
808            << "\\textclass " << h_textclass << "\n";
809         string const raw = h_preamble.str();
810         if (!raw.empty()) {
811                 os << "\\begin_preamble\n";
812                 for (string::size_type i = 0; i < raw.size(); ++i) {
813                         if (raw[i] == package_beg_sep) {
814                                 // Here follows some package loading code that
815                                 // must be skipped if the package is loaded
816                                 // automatically.
817                                 string::size_type j = raw.find(package_mid_sep, i);
818                                 if (j == string::npos)
819                                         return false;
820                                 string::size_type k = raw.find(package_end_sep, j);
821                                 if (k == string::npos)
822                                         return false;
823                                 string const package = raw.substr(i + 1, j - i - 1);
824                                 string const replacement = raw.substr(j + 1, k - j - 1);
825                                 if (auto_packages.find(package) == auto_packages.end())
826                                         os << replacement;
827                                 i = k;
828                         } else
829                                 os.put(raw[i]);
830                 }
831                 os << "\n\\end_preamble\n";
832         }
833         if (!h_options.empty())
834                 os << "\\options " << h_options << "\n";
835         os << "\\use_default_options " << h_use_default_options << "\n";
836         if (!used_modules.empty()) {
837                 os << "\\begin_modules\n";
838                 vector<string>::const_iterator const end = used_modules.end();
839                 vector<string>::const_iterator it = used_modules.begin();
840                 for (; it != end; it++)
841                         os << *it << '\n';
842                 os << "\\end_modules\n";
843         }
844         os << "\\language " << h_language << "\n"
845            << "\\language_package " << h_language_package << "\n"
846            << "\\inputencoding " << h_inputencoding << "\n"
847            << "\\fontencoding " << h_fontencoding << "\n"
848            << "\\font_roman " << h_font_roman << "\n"
849            << "\\font_sans " << h_font_sans << "\n"
850            << "\\font_typewriter " << h_font_typewriter << "\n"
851            << "\\font_default_family " << h_font_default_family << "\n"
852            << "\\font_sc " << h_font_sc << "\n"
853            << "\\font_osf " << h_font_osf << "\n"
854            << "\\font_sf_scale " << h_font_sf_scale << "\n"
855            << "\\font_tt_scale " << h_font_tt_scale << "\n"
856            << "\\graphics " << h_graphics << "\n";
857         if (!h_float_placement.empty())
858                 os << "\\float_placement " << h_float_placement << "\n";
859         os << "\\paperfontsize " << h_paperfontsize << "\n"
860            << "\\spacing " << h_spacing << "\n"
861            << "\\use_hyperref " << h_use_hyperref << '\n';
862         if (h_use_hyperref == "1") {
863                 if (!h_pdf_title.empty())
864                         os << "\\pdf_title \"" << h_pdf_title << "\"\n";
865                 if (!h_pdf_author.empty())
866                         os << "\\pdf_author \"" << h_pdf_author << "\"\n";
867                 if (!h_pdf_subject.empty())
868                         os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
869                 if (!h_pdf_keywords.empty())
870                         os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
871                 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
872                       "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
873                       "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
874                       "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
875                       "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
876                       "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
877                       "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
878                       "\\pdf_backref " << h_pdf_backref << "\n"
879                       "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
880                 if (!h_pdf_pagemode.empty())
881                         os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
882                 if (!h_pdf_quoted_options.empty())
883                         os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
884         }
885         os << "\\papersize " << h_papersize << "\n"
886            << "\\use_geometry " << h_use_geometry << "\n"
887            << "\\use_amsmath " << h_use_amsmath << "\n"
888            << "\\use_esint " << h_use_esint << "\n"
889            << "\\use_mhchem " << h_use_mhchem << "\n"
890            << "\\use_mathdots " << h_use_mathdots << "\n"
891            << "\\use_undertilde " << h_use_undertilde << "\n"
892            << "\\cite_engine " << h_cite_engine << "\n"
893            << "\\use_bibtopic " << h_use_bibtopic << "\n"
894            << "\\paperorientation " << h_paperorientation << '\n'
895            << "\\suppress_date " << h_suppress_date << '\n'
896            << "\\use_refstyle " << h_use_refstyle << '\n';
897         if (!h_fontcolor.empty())
898                 os << "\\fontcolor " << h_fontcolor << '\n';
899         if (!h_notefontcolor.empty())
900                 os << "\\notefontcolor " << h_notefontcolor << '\n';
901         if (!h_backgroundcolor.empty())
902                 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
903         if (!h_boxbgcolor.empty())
904                 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
905         os << h_margins
906            << "\\secnumdepth " << h_secnumdepth << "\n"
907            << "\\tocdepth " << h_tocdepth << "\n"
908            << "\\paragraph_separation " << h_paragraph_separation << "\n";
909         if (h_paragraph_separation == "skip")
910                 os << "\\defskip " << h_defskip << "\n";
911         else
912                 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
913         os << "\\quotes_language " << h_quotes_language << "\n"
914            << "\\papercolumns " << h_papercolumns << "\n"
915            << "\\papersides " << h_papersides << "\n"
916            << "\\paperpagestyle " << h_paperpagestyle << "\n";
917         if (!h_listings_params.empty())
918                 os << "\\listings_params " << h_listings_params << "\n";
919         os << "\\tracking_changes " << h_tracking_changes << "\n"
920            << "\\output_changes " << h_output_changes << "\n"
921            << "\\html_math_output " << h_html_math_output << "\n"
922            << "\\html_css_as_file " << h_html_css_as_file << "\n"
923            << "\\html_be_strict " << h_html_be_strict << "\n"
924            << "\\end_header\n\n"
925            << "\\begin_body\n";
926         // clear preamble for subdocuments
927         h_preamble.str("");
928         return true;
929 }
930
931
932 void Preamble::parse(Parser & p, string const & forceclass,
933                      TeX2LyXDocClass & tc)
934 {
935         // initialize fixed types
936         special_columns['D'] = 3;
937         bool is_full_document = false;
938         bool is_lyx_file = false;
939         bool in_lyx_preamble = false;
940
941         // determine whether this is a full document or a fragment for inclusion
942         while (p.good()) {
943                 Token const & t = p.get_token();
944
945                 if (t.cat() == catEscape && t.cs() == "documentclass") {
946                         is_full_document = true;
947                         break;
948                 }
949         }
950         p.reset();
951
952         while (is_full_document && p.good()) {
953                 Token const & t = p.get_token();
954
955 #ifdef FILEDEBUG
956                 cerr << "t: " << t << "\n";
957 #endif
958
959                 //
960                 // cat codes
961                 //
962                 if (!in_lyx_preamble &&
963                     (t.cat() == catLetter ||
964                      t.cat() == catSuper ||
965                      t.cat() == catSub ||
966                      t.cat() == catOther ||
967                      t.cat() == catMath ||
968                      t.cat() == catActive ||
969                      t.cat() == catBegin ||
970                      t.cat() == catEnd ||
971                      t.cat() == catAlign ||
972                      t.cat() == catParameter))
973                         h_preamble << t.cs();
974
975                 else if (!in_lyx_preamble && 
976                          (t.cat() == catSpace || t.cat() == catNewline))
977                         h_preamble << t.asInput();
978
979                 else if (t.cat() == catComment) {
980                         static regex const islyxfile("%% LyX .* created this file");
981                         static regex const usercommands("User specified LaTeX commands");
982
983                         string const comment = t.asInput();
984
985                         // magically switch encoding default if it looks like XeLaTeX
986                         static string const magicXeLaTeX =
987                                 "% This document must be compiled with XeLaTeX ";
988                         if (comment.size() > magicXeLaTeX.size() 
989                                   && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
990                                   && h_inputencoding == "auto") {
991                                 cerr << "XeLaTeX comment found, switching to UTF8\n";
992                                 h_inputencoding = "utf8";
993                         }
994                         smatch sub;
995                         if (regex_search(comment, sub, islyxfile)) {
996                                 is_lyx_file = true;
997                                 in_lyx_preamble = true;
998                         } else if (is_lyx_file
999                                    && regex_search(comment, sub, usercommands))
1000                                 in_lyx_preamble = false;
1001                         else if (!in_lyx_preamble)
1002                                 h_preamble << t.asInput();
1003                 }
1004
1005                 else if (t.cs() == "pagestyle")
1006                         h_paperpagestyle = p.verbatim_item();
1007
1008                 else if (t.cs() == "date") {
1009                         string argument = p.getArg('{', '}');
1010                         if (argument.empty())
1011                                 h_suppress_date = "true";
1012                         else
1013                                 h_preamble << t.asInput() << '{' << argument << '}';
1014                 }
1015
1016                 else if (t.cs() == "color") {
1017                         string argument = p.getArg('{', '}');
1018                         // check the case that a standard color is used
1019                         if (is_known(argument, known_basic_colors)) {
1020                                 h_fontcolor = rgbcolor2code(argument);
1021                                 preamble.registerAutomaticallyLoadedPackage("color");
1022                         } else if (argument == "document_fontcolor")
1023                                 preamble.registerAutomaticallyLoadedPackage("color");
1024                         // check the case that LyX's document_fontcolor is defined
1025                         // but not used for \color
1026                         else {
1027                                 h_preamble << t.asInput() << '{' << argument << '}';
1028                                 // the color might already be set because \definecolor
1029                                 // is parsed before this
1030                                 h_fontcolor = "";
1031                         }
1032                 }
1033
1034                 else if (t.cs() == "pagecolor") {
1035                         string argument = p.getArg('{', '}');
1036                         // check the case that a standard color is used
1037                         if (is_known(argument, known_basic_colors)) {
1038                                 h_backgroundcolor = rgbcolor2code(argument);
1039                         } else if (argument == "page_backgroundcolor")
1040                                 preamble.registerAutomaticallyLoadedPackage("color");
1041                         // check the case that LyX's page_backgroundcolor is defined
1042                         // but not used for \pagecolor
1043                         else {
1044                                 h_preamble << t.asInput() << '{' << argument << '}';
1045                                 // the color might already be set because \definecolor
1046                                 // is parsed before this
1047                                 h_backgroundcolor = "";
1048                         }
1049                 }
1050
1051                 else if (t.cs() == "makeatletter") {
1052                         // LyX takes care of this
1053                         p.setCatCode('@', catLetter);
1054                 }
1055
1056                 else if (t.cs() == "makeatother") {
1057                         // LyX takes care of this
1058                         p.setCatCode('@', catOther);
1059                 }
1060
1061                 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1062                       || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1063                       || t.cs() == "providecommand" || t.cs() == "providecommandx"
1064                                 || t.cs() == "DeclareRobustCommand"
1065                       || t.cs() == "DeclareRobustCommandx"
1066                                 || t.cs() == "ProvideTextCommandDefault"
1067                                 || t.cs() == "DeclareMathAccent") {
1068                         bool star = false;
1069                         if (p.next_token().character() == '*') {
1070                                 p.get_token();
1071                                 star = true;
1072                         }
1073                         string const name = p.verbatim_item();
1074                         string const opt1 = p.getFullOpt();
1075                         string const opt2 = p.getFullOpt();
1076                         string const body = p.verbatim_item();
1077                         // font settings
1078                         if (name == "\\rmdefault")
1079                                 if (is_known(body, known_roman_fonts))
1080                                         h_font_roman = body;
1081                         if (name == "\\sfdefault")
1082                                 if (is_known(body, known_sans_fonts))
1083                                         h_font_sans = body;
1084                         if (name == "\\ttdefault")
1085                                 if (is_known(body, known_typewriter_fonts))
1086                                         h_font_typewriter = body;
1087                         if (name == "\\familydefault") {
1088                                 string family = body;
1089                                 // remove leading "\"
1090                                 h_font_default_family = family.erase(0,1);
1091                         }
1092
1093                         // Add the command to the known commands
1094                         add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1095
1096                         // only non-lyxspecific stuff
1097                         if (!in_lyx_preamble) {
1098                                 ostringstream ss;
1099                                 ss << '\\' << t.cs();
1100                                 if (star)
1101                                         ss << '*';
1102                                 ss << '{' << name << '}' << opt1 << opt2
1103                                    << '{' << body << "}";
1104                                 h_preamble << ss.str();
1105 /*
1106                                 ostream & out = in_preamble ? h_preamble : os;
1107                                 out << "\\" << t.cs() << "{" << name << "}"
1108                                     << opts << "{" << body << "}";
1109 */
1110                         }
1111                 }
1112
1113                 else if (t.cs() == "documentclass") {
1114                         vector<string>::iterator it;
1115                         vector<string> opts = split_options(p.getArg('[', ']'));
1116                         handle_opt(opts, known_fontsizes, h_paperfontsize);
1117                         delete_opt(opts, known_fontsizes);
1118                         // delete "pt" at the end
1119                         string::size_type i = h_paperfontsize.find("pt");
1120                         if (i != string::npos)
1121                                 h_paperfontsize.erase(i);
1122                         // The documentclass options are always parsed before the options
1123                         // of the babel call so that a language cannot overwrite the babel
1124                         // options.
1125                         handle_opt(opts, known_languages, h_language);
1126                         delete_opt(opts, known_languages);
1127
1128                         // paper orientation
1129                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1130                                 h_paperorientation = "landscape";
1131                                 opts.erase(it);
1132                         }
1133                         // paper sides
1134                         if ((it = find(opts.begin(), opts.end(), "oneside"))
1135                                  != opts.end()) {
1136                                 h_papersides = "1";
1137                                 opts.erase(it);
1138                         }
1139                         if ((it = find(opts.begin(), opts.end(), "twoside"))
1140                                  != opts.end()) {
1141                                 h_papersides = "2";
1142                                 opts.erase(it);
1143                         }
1144                         // paper columns
1145                         if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1146                                  != opts.end()) {
1147                                 h_papercolumns = "1";
1148                                 opts.erase(it);
1149                         }
1150                         if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1151                                  != opts.end()) {
1152                                 h_papercolumns = "2";
1153                                 opts.erase(it);
1154                         }
1155                         // paper sizes
1156                         // some size options are know to any document classes, other sizes
1157                         // are handled by the \geometry command of the geometry package
1158                         handle_opt(opts, known_class_paper_sizes, h_papersize);
1159                         delete_opt(opts, known_class_paper_sizes);
1160                         // the remaining options
1161                         h_options = join(opts, ",");
1162                         // FIXME This does not work for classes that have a
1163                         //       different name in LyX than in LaTeX
1164                         h_textclass = p.getArg('{', '}');
1165                 }
1166
1167                 else if (t.cs() == "usepackage") {
1168                         string const options = p.getArg('[', ']');
1169                         string const name = p.getArg('{', '}');
1170                         vector<string> vecnames;
1171                         split(name, vecnames, ',');
1172                         vector<string>::const_iterator it  = vecnames.begin();
1173                         vector<string>::const_iterator end = vecnames.end();
1174                         for (; it != end; ++it)
1175                                 handle_package(p, trimSpaceAndEol(*it), options, 
1176                                                in_lyx_preamble);
1177                 }
1178
1179                 else if (t.cs() == "inputencoding") {
1180                         string const encoding = p.getArg('{','}');
1181                         h_inputencoding = encoding;
1182                         p.setEncoding(encoding);
1183                 }
1184
1185                 else if (t.cs() == "newenvironment") {
1186                         string const name = p.getArg('{', '}');
1187                         string const opt1 = p.getFullOpt();
1188                         string const opt2 = p.getFullOpt();
1189                         string const beg = p.verbatim_item();
1190                         string const end = p.verbatim_item();
1191                         if (!in_lyx_preamble) {
1192                                 h_preamble << "\\newenvironment{" << name
1193                                            << '}' << opt1 << opt2 << '{'
1194                                            << beg << "}{" << end << '}';
1195                         }
1196                         add_known_environment(name, opt1, !opt2.empty(),
1197                                               from_utf8(beg), from_utf8(end));
1198
1199                 }
1200
1201                 else if (t.cs() == "def") {
1202                         string name = p.get_token().cs();
1203                         while (p.next_token().cat() != catBegin)
1204                                 name += p.get_token().cs();
1205                         if (!in_lyx_preamble)
1206                                 h_preamble << "\\def\\" << name << '{'
1207                                            << p.verbatim_item() << "}";
1208                 }
1209
1210                 else if (t.cs() == "newcolumntype") {
1211                         string const name = p.getArg('{', '}');
1212                         trimSpaceAndEol(name);
1213                         int nargs = 0;
1214                         string opts = p.getOpt();
1215                         if (!opts.empty()) {
1216                                 istringstream is(string(opts, 1));
1217                                 is >> nargs;
1218                         }
1219                         special_columns[name[0]] = nargs;
1220                         h_preamble << "\\newcolumntype{" << name << "}";
1221                         if (nargs)
1222                                 h_preamble << "[" << nargs << "]";
1223                         h_preamble << "{" << p.verbatim_item() << "}";
1224                 }
1225
1226                 else if (t.cs() == "setcounter") {
1227                         string const name = p.getArg('{', '}');
1228                         string const content = p.getArg('{', '}');
1229                         if (name == "secnumdepth")
1230                                 h_secnumdepth = content;
1231                         else if (name == "tocdepth")
1232                                 h_tocdepth = content;
1233                         else
1234                                 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1235                 }
1236
1237                 else if (t.cs() == "setlength") {
1238                         string const name = p.verbatim_item();
1239                         string const content = p.verbatim_item();
1240                         // the paragraphs are only not indented when \parindent is set to zero
1241                         if (name == "\\parindent" && content != "") {
1242                                 if (content[0] == '0')
1243                                         h_paragraph_separation = "skip";
1244                                 else
1245                                         h_paragraph_indentation = translate_len(content);
1246                         } else if (name == "\\parskip") {
1247                                 if (content == "\\smallskipamount")
1248                                         h_defskip = "smallskip";
1249                                 else if (content == "\\medskipamount")
1250                                         h_defskip = "medskip";
1251                                 else if (content == "\\bigskipamount")
1252                                         h_defskip = "bigskip";
1253                                 else
1254                                         h_defskip = content;
1255                         } else
1256                                 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1257                 }
1258
1259                 else if (t.cs() == "onehalfspacing")
1260                         h_spacing = "onehalf";
1261
1262                 else if (t.cs() == "doublespacing")
1263                         h_spacing = "double";
1264
1265                 else if (t.cs() == "setstretch")
1266                         h_spacing = "other " + p.verbatim_item();
1267
1268                 else if (t.cs() == "begin") {
1269                         string const name = p.getArg('{', '}');
1270                         if (name == "document")
1271                                 break;
1272                         h_preamble << "\\begin{" << name << "}";
1273                 }
1274
1275                 else if (t.cs() == "geometry") {
1276                         h_use_geometry = "true";
1277                         vector<string> opts = split_options(p.getArg('{', '}'));
1278                         vector<string>::iterator it;
1279                         // paper orientation
1280                         if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1281                                 h_paperorientation = "landscape";
1282                                 opts.erase(it);
1283                         }
1284                         // paper size
1285                         handle_opt(opts, known_paper_sizes, h_papersize);
1286                         delete_opt(opts, known_paper_sizes);
1287                         // page margins
1288                         char const * const * margin = known_paper_margins;
1289                         int k = -1;
1290                         for (; *margin; ++margin) {
1291                                 k += 1;
1292                                 // search for the "=" in e.g. "lmargin=2cm" to get the value
1293                                 for(size_t i = 0; i != opts.size(); i++) {
1294                                         if (opts.at(i).find(*margin) != string::npos) {
1295                                                 string::size_type pos = opts.at(i).find("=");
1296                                                 string value = opts.at(i).substr(pos + 1);
1297                                                 string name = known_coded_paper_margins[k];
1298                                                 h_margins += "\\" + name + " " + value + "\n";
1299                                         }
1300                                 }
1301                         }
1302                 }
1303
1304                 else if (t.cs() == "definecolor") {
1305                         string const color = p.getArg('{', '}');
1306                         string const space = p.getArg('{', '}');
1307                         string const value = p.getArg('{', '}');
1308                         if (color == "document_fontcolor" && space == "rgb") {
1309                                 RGBColor c(RGBColorFromLaTeX(value));
1310                                 h_fontcolor = X11hexname(c);
1311                         } else if (color == "note_fontcolor" && space == "rgb") {
1312                                 RGBColor c(RGBColorFromLaTeX(value));
1313                                 h_notefontcolor = X11hexname(c);
1314                         } else if (color == "page_backgroundcolor" && space == "rgb") {
1315                                 RGBColor c(RGBColorFromLaTeX(value));
1316                                 h_backgroundcolor = X11hexname(c);
1317                         } else if (color == "shadecolor" && space == "rgb") {
1318                                 RGBColor c(RGBColorFromLaTeX(value));
1319                                 h_boxbgcolor = X11hexname(c);
1320                         } else {
1321                                 h_preamble << "\\definecolor{" << color
1322                                            << "}{" << space << "}{" << value
1323                                            << '}';
1324                         }
1325                 }
1326
1327                 else if (t.cs() == "jurabibsetup") {
1328                         // FIXME p.getArg('{', '}') is most probably wrong (it
1329                         //       does not handle nested braces).
1330                         //       Use p.verbatim_item() instead.
1331                         vector<string> jurabibsetup =
1332                                 split_options(p.getArg('{', '}'));
1333                         // add jurabibsetup to the jurabib package options
1334                         add_package("jurabib", jurabibsetup);
1335                         if (!jurabibsetup.empty()) {
1336                                 h_preamble << "\\jurabibsetup{"
1337                                            << join(jurabibsetup, ",") << '}';
1338                         }
1339                 }
1340
1341                 else if (t.cs() == "hypersetup") {
1342                         vector<string> hypersetup =
1343                                 split_options(p.verbatim_item());
1344                         // add hypersetup to the hyperref package options
1345                         handle_hyperref(hypersetup);
1346                         if (!hypersetup.empty()) {
1347                                 h_preamble << "\\hypersetup{"
1348                                            << join(hypersetup, ",") << '}';
1349                         }
1350                 }
1351
1352                 else if (is_known(t.cs(), known_if_3arg_commands)) {
1353                         // prevent misparsing of \usepackage if it is used
1354                         // as an argument (see e.g. our own output of
1355                         // \@ifundefined above)
1356                         string const arg1 = p.verbatim_item();
1357                         string const arg2 = p.verbatim_item();
1358                         string const arg3 = p.verbatim_item();
1359                         // test case \@ifundefined{date}{}{\date{}}
1360                         if (t.cs() == "@ifundefined" && arg1 == "date" &&
1361                             arg2.empty() && arg3 == "\\date{}") {
1362                                 h_suppress_date = "true";
1363                         // older tex2lyx versions did output
1364                         // \@ifundefined{definecolor}{\usepackage{color}}{}
1365                         } else if (t.cs() == "@ifundefined" &&
1366                                    arg1 == "definecolor" &&
1367                                    arg2 == "\\usepackage{color}" &&
1368                                    arg3.empty()) {
1369                                 if (!in_lyx_preamble)
1370                                         h_preamble << package_beg_sep
1371                                                    << "color"
1372                                                    << package_mid_sep
1373                                                    << "\\@ifundefined{definecolor}{color}{}"
1374                                                    << package_end_sep;
1375                         // test for case
1376                         //\@ifundefined{showcaptionsetup}{}{%
1377                         // \PassOptionsToPackage{caption=false}{subfig}}
1378                         // that LyX uses for subfloats
1379                         } else if (t.cs() == "@ifundefined" &&
1380                                    arg1 == "showcaptionsetup" && arg2.empty()
1381                                 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1382                                 ; // do nothing
1383                         } else if (!in_lyx_preamble) {
1384                                 h_preamble << t.asInput()
1385                                            << '{' << arg1 << '}'
1386                                            << '{' << arg2 << '}'
1387                                            << '{' << arg3 << '}';
1388                         }
1389                 }
1390
1391                 else if (is_known(t.cs(), known_if_commands)) {
1392                         // must not parse anything in conditional code, since
1393                         // LyX would output the parsed contents unconditionally
1394                         if (!in_lyx_preamble)
1395                                 h_preamble << t.asInput();
1396                         handle_if(p, in_lyx_preamble);
1397                 }
1398
1399                 else if (!t.cs().empty() && !in_lyx_preamble)
1400                         h_preamble << '\\' << t.cs();
1401         }
1402
1403         // remove the whitespace
1404         p.skip_spaces();
1405
1406         // Force textclass if the user wanted it
1407         if (!forceclass.empty())
1408                 h_textclass = forceclass;
1409         if (noweb_mode && !prefixIs(h_textclass, "literate-"))
1410                 h_textclass.insert(0, "literate-");
1411         tc.setName(h_textclass);
1412         if (!tc.load()) {
1413                 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1414                 exit(EXIT_FAILURE);
1415         }
1416         if (h_papersides.empty()) {
1417                 ostringstream ss;
1418                 ss << tc.sides();
1419                 h_papersides = ss.str();
1420         }
1421 }
1422
1423
1424 string babel2lyx(string const & language)
1425 {
1426         char const * const * where = is_known(language, known_languages);
1427         if (where)
1428                 return known_coded_languages[where - known_languages];
1429         return language;
1430 }
1431
1432
1433 string rgbcolor2code(string const & name)
1434 {
1435         char const * const * where = is_known(name, known_basic_colors);
1436         if (where) {
1437                 // "red", "green" etc
1438                 return known_basic_color_codes[where - known_basic_colors];
1439         }
1440         // "255,0,0", "0,255,0" etc
1441         RGBColor c(RGBColorFromLaTeX(name));
1442         return X11hexname(c);
1443 }
1444
1445 // }])
1446
1447
1448 } // namespace lyx