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