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