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