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