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