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