3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "LayoutFile.h"
23 #include "TextClass.h"
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
31 #include "support/regex.h"
37 using namespace lyx::support;
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
49 * known babel language names (including synonyms)
50 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51 * please keep this in sync with known_coded_languages line by line!
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
55 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
56 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
57 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
58 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
64 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
65 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
66 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
71 * the same as known_languages with .lyx names
72 * please keep this in sync with known_languages line by line!
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "bosnian", "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", "friulan", "galician", "german", "german",
80 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
85 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
86 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
87 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
88 "vietnamese", "welsh",
91 /// languages with danish quotes (.lyx names)
92 const char * const known_danish_quotes_languages[] = {"danish", 0};
94 /// languages with english quotes (.lyx names)
95 const char * const known_english_quotes_languages[] = {"american", "australian",
96 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
97 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
100 /// languages with french quotes (.lyx names)
101 const char * const known_french_quotes_languages[] = {"albanian",
102 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french", "friulan",
103 "galician", "greek", "italian", "norsk", "nynorsk", "piedmontese", "polutonikogreek",
104 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
107 /// languages with german quotes (.lyx names)
108 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
109 "czech", "german", "georgian", "icelandic", "lithuanian", "lowersorbian", "macedonian",
110 "naustrian", "ngerman", "romansh", "serbian", "serbian-latin", "slovak", "slovene",
113 /// languages with polish quotes (.lyx names)
114 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
115 "dutch", "estonian", "magyar", "polish", "romanian", 0};
117 /// languages with swedish quotes (.lyx names)
118 const char * const known_swedish_quotes_languages[] = {"finnish",
121 /// known language packages from the times before babel
122 const char * const known_old_language_packages[] = {"french", "frenchle",
123 "frenchpro", "german", "ngerman", "pmfrench", 0};
125 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
127 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
128 "ccfonts", "chancery", "charter", "cmr", "fourier", "garamondx", "libertine",
129 "libertine-type1", "lmodern", "mathdesign", "mathpazo", "mathptmx", "newcent",
130 "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", 0};
132 const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum-type1",
133 "cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc", "kurier",
134 "kurierc", "kurierl", "kurierlc", "lmss", "tgadventor", "tgheros", 0};
136 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
137 "courier", "lmtt", "luximono", "fourier", "libertineMono-type1", "lmodern",
138 "mathpazo", "mathptmx", "newcent", "tgcursor", "txtt", 0};
140 const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0};
142 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
143 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
144 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
145 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
146 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
148 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
149 "executivepaper", "legalpaper", "letterpaper", 0};
151 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
152 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
154 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
155 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
158 /// commands that can start an \if...\else...\endif sequence
159 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
160 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
161 "ifsidecap", "ifupgreek", 0};
163 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
164 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
165 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
167 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
168 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
169 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
171 /// conditional commands with three arguments like \@ifundefined{}{}{}
172 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
175 /// packages that work only in xetex
176 /// polyglossia is handled separately
177 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
178 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
179 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
181 /// packages that are automatically skipped if loaded by LyX
182 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
183 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
184 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
185 "makeidx", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle", "rotating",
186 "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa", "tipx",
187 "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xunicode", 0};
189 // codes used to remove packages that are loaded automatically by LyX.
190 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
191 const char package_beg_sep = '\001';
192 const char package_mid_sep = '\002';
193 const char package_end_sep = '\003';
196 // returns true if at least one of the options in what has been found
197 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
203 // the last language option is the document language (for babel and LyX)
204 // the last size option is the document font size
205 vector<string>::iterator it;
206 vector<string>::iterator position = opts.begin();
207 for (; *what; ++what) {
208 it = find(opts.begin(), opts.end(), *what);
209 if (it != opts.end()) {
210 if (it >= position) {
221 void delete_opt(vector<string> & opts, char const * const * what)
226 // remove found options from the list
227 // do this after handle_opt to avoid potential memory leaks
228 vector<string>::iterator it;
229 for (; *what; ++what) {
230 it = find(opts.begin(), opts.end(), *what);
231 if (it != opts.end())
238 * Split a package options string (keyval format) into a vector.
240 * authorformat=smallcaps,
242 * titleformat=colonsep,
243 * bibformat={tabular,ibidem,numbered}
245 vector<string> split_options(string const & input)
247 vector<string> options;
251 Token const & t = p.get_token();
252 if (t.asInput() == ",") {
253 options.push_back(trimSpaceAndEol(option));
255 } else if (t.asInput() == "=") {
258 if (p.next_token().asInput() == "{")
259 option += '{' + p.getArg('{', '}') + '}';
260 } else if (t.cat() != catSpace)
261 option += t.asInput();
265 options.push_back(trimSpaceAndEol(option));
272 * Retrieve a keyval option "name={value with=sign}" named \p name from
273 * \p options and return the value.
274 * The found option is also removed from \p options.
276 string process_keyval_opt(vector<string> & options, string name)
278 for (size_t i = 0; i < options.size(); ++i) {
279 vector<string> option;
280 split(options[i], option, '=');
281 if (option.size() < 2)
283 if (option[0] == name) {
284 options.erase(options.begin() + i);
285 option.erase(option.begin());
286 return join(option, "=");
292 } // anonymous namespace
296 * known polyglossia language names (including variants)
297 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
299 const char * const Preamble::polyglossia_languages[] = {
300 "albanian", "american", "ancient", "arabic", "armenian", "australian",
301 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
302 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
303 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
304 "galician", "greek", "monotonic", "hebrew", "hindi",
305 "icelandic", "interlingua", "irish", "italian",
306 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
307 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan",
308 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
309 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish",
310 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
311 "ukrainian", "usorbian", "vietnamese", "welsh", 0};
312 // not yet supported by LyX: "amharic", "asturian", "syriac", "urdu"
315 * the same as polyglossia_languages with .lyx names
316 * please keep this in sync with polyglossia_languages line by line!
318 const char * const Preamble::coded_polyglossia_languages[] = {
319 "albanian", "american", "ancientgreek", "arabic_arabi", "armenian", "australian",
320 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
321 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
322 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
323 "galician", "greek", "greek", "hebrew", "hindi",
324 "icelandic", "interlingua", "irish", "italian",
325 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
326 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan",
327 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
328 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish",
329 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
330 "ukrainian", "uppersorbian", "vietnamese", "welsh", 0};
331 // not yet supported by LyX: "amharic", "asturian", "syriac", "urdu"
334 bool Preamble::usePolyglossia() const
336 return h_use_non_tex_fonts && h_language_package == "default";
340 bool Preamble::indentParagraphs() const
342 return h_paragraph_separation == "indent";
346 bool Preamble::isPackageUsed(string const & package) const
348 return used_packages.find(package) != used_packages.end();
352 vector<string> Preamble::getPackageOptions(string const & package) const
354 map<string, vector<string> >::const_iterator it = used_packages.find(package);
355 if (it != used_packages.end())
357 return vector<string>();
361 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
363 auto_packages.insert(package);
367 void Preamble::addModule(string const & module)
369 used_modules.push_back(module);
373 void Preamble::suppressDate(bool suppress)
376 h_suppress_date = "true";
378 h_suppress_date = "false";
382 void Preamble::registerAuthor(std::string const & name)
384 Author author(from_utf8(name), empty_docstring());
385 author.setUsed(true);
386 authors_.record(author);
387 h_tracking_changes = "true";
388 h_output_changes = "true";
392 Author const & Preamble::getAuthor(std::string const & name) const
394 Author author(from_utf8(name), empty_docstring());
395 for (AuthorList::Authors::const_iterator it = authors_.begin();
396 it != authors_.end(); ++it)
399 static Author const dummy;
404 int Preamble::getSpecialTableColumnArguments(char c) const
406 map<char, int>::const_iterator it = special_columns_.find(c);
407 if (it == special_columns_.end())
413 void Preamble::add_package(string const & name, vector<string> & options)
415 // every package inherits the global options
416 if (used_packages.find(name) == used_packages.end())
417 used_packages[name] = split_options(h_options);
419 vector<string> & v = used_packages[name];
420 v.insert(v.end(), options.begin(), options.end());
421 if (name == "jurabib") {
422 // Don't output the order argument (see the cite command
423 // handling code in text.cpp).
424 vector<string>::iterator end =
425 remove(options.begin(), options.end(), "natbiborder");
426 end = remove(options.begin(), end, "jurabiborder");
427 options.erase(end, options.end());
434 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
435 bool scale_as_percentage(string const & scale, string & percentage)
437 string::size_type pos = scale.find('=');
438 if (pos != string::npos) {
439 string value = scale.substr(pos + 1);
440 if (isStrDbl(value)) {
441 percentage = convert<string>(
442 static_cast<int>(100 * convert<double>(value)));
450 string remove_braces(string const & value)
454 if (value[0] == '{' && value[value.length()-1] == '}')
455 return value.substr(1, value.length()-2);
459 } // anonymous namespace
462 Preamble::Preamble() : one_language(true), explicit_babel(false),
463 title_layout_found(false), index_number(0), h_font_cjk_set(false),
464 h_use_microtype(false)
468 h_biblio_style = "plain";
469 h_bibtex_command = "default";
470 h_cite_engine = "basic";
471 h_cite_engine_type = "default";
473 h_defskip = "medskip";
476 h_fontencoding = "default";
477 h_font_roman[0] = "default";
478 h_font_roman[1] = "default";
479 h_font_sans[0] = "default";
480 h_font_sans[1] = "default";
481 h_font_typewriter[0] = "default";
482 h_font_typewriter[1] = "default";
483 h_font_math[0] = "auto";
484 h_font_math[1] = "auto";
485 h_font_default_family = "default";
486 h_use_non_tex_fonts = false;
488 h_font_osf = "false";
489 h_font_sf_scale[0] = "100";
490 h_font_sf_scale[1] = "100";
491 h_font_tt_scale[0] = "100";
492 h_font_tt_scale[1] = "100";
494 h_graphics = "default";
495 h_default_output_format = "default";
496 h_html_be_strict = "false";
497 h_html_css_as_file = "0";
498 h_html_math_output = "0";
499 h_index[0] = "Index";
500 h_index_command = "default";
501 h_inputencoding = "auto";
502 h_justification = "true";
503 h_language = "english";
504 h_language_package = "none";
506 h_maintain_unincluded_children = "false";
510 h_output_changes = "false";
512 //h_output_sync_macro
513 h_papercolumns = "1";
514 h_paperfontsize = "default";
515 h_paperorientation = "portrait";
516 h_paperpagestyle = "default";
518 h_papersize = "default";
519 h_paragraph_indentation = "default";
520 h_paragraph_separation = "indent";
525 h_pdf_bookmarks = "0";
526 h_pdf_bookmarksnumbered = "0";
527 h_pdf_bookmarksopen = "0";
528 h_pdf_bookmarksopenlevel = "1";
529 h_pdf_breaklinks = "0";
530 h_pdf_pdfborder = "0";
531 h_pdf_colorlinks = "0";
532 h_pdf_backref = "section";
533 h_pdf_pdfusetitle = "0";
535 //h_pdf_quoted_options;
536 h_quotes_language = "english";
538 h_shortcut[0] = "idx";
539 h_spacing = "single";
540 h_save_transient_properties = "true";
541 h_suppress_date = "false";
542 h_textclass = "article";
544 h_tracking_changes = "false";
545 h_use_bibtopic = "false";
546 h_use_indices = "false";
547 h_use_geometry = "false";
548 h_use_default_options = "false";
549 h_use_hyperref = "false";
550 h_use_microtype = false;
551 h_use_refstyle = false;
552 h_use_packages["amsmath"] = "1";
553 h_use_packages["amssymb"] = "0";
554 h_use_packages["cancel"] = "0";
555 h_use_packages["esint"] = "1";
556 h_use_packages["mhchem"] = "0";
557 h_use_packages["mathdots"] = "0";
558 h_use_packages["mathtools"] = "0";
559 h_use_packages["stackrel"] = "0";
560 h_use_packages["stmaryrd"] = "0";
561 h_use_packages["undertilde"] = "0";
565 void Preamble::handle_hyperref(vector<string> & options)
567 // FIXME swallow inputencoding changes that might surround the
568 // hyperref setup if it was written by LyX
569 h_use_hyperref = "true";
570 // swallow "unicode=true", since LyX does always write that
571 vector<string>::iterator it =
572 find(options.begin(), options.end(), "unicode=true");
573 if (it != options.end())
575 it = find(options.begin(), options.end(), "pdfusetitle");
576 if (it != options.end()) {
577 h_pdf_pdfusetitle = "1";
580 string bookmarks = process_keyval_opt(options, "bookmarks");
581 if (bookmarks == "true")
582 h_pdf_bookmarks = "1";
583 else if (bookmarks == "false")
584 h_pdf_bookmarks = "0";
585 if (h_pdf_bookmarks == "1") {
586 string bookmarksnumbered =
587 process_keyval_opt(options, "bookmarksnumbered");
588 if (bookmarksnumbered == "true")
589 h_pdf_bookmarksnumbered = "1";
590 else if (bookmarksnumbered == "false")
591 h_pdf_bookmarksnumbered = "0";
592 string bookmarksopen =
593 process_keyval_opt(options, "bookmarksopen");
594 if (bookmarksopen == "true")
595 h_pdf_bookmarksopen = "1";
596 else if (bookmarksopen == "false")
597 h_pdf_bookmarksopen = "0";
598 if (h_pdf_bookmarksopen == "1") {
599 string bookmarksopenlevel =
600 process_keyval_opt(options, "bookmarksopenlevel");
601 if (!bookmarksopenlevel.empty())
602 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
605 string breaklinks = process_keyval_opt(options, "breaklinks");
606 if (breaklinks == "true")
607 h_pdf_breaklinks = "1";
608 else if (breaklinks == "false")
609 h_pdf_breaklinks = "0";
610 string pdfborder = process_keyval_opt(options, "pdfborder");
611 if (pdfborder == "{0 0 0}")
612 h_pdf_pdfborder = "1";
613 else if (pdfborder == "{0 0 1}")
614 h_pdf_pdfborder = "0";
615 string backref = process_keyval_opt(options, "backref");
616 if (!backref.empty())
617 h_pdf_backref = backref;
618 string colorlinks = process_keyval_opt(options, "colorlinks");
619 if (colorlinks == "true")
620 h_pdf_colorlinks = "1";
621 else if (colorlinks == "false")
622 h_pdf_colorlinks = "0";
623 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
624 if (!pdfpagemode.empty())
625 h_pdf_pagemode = pdfpagemode;
626 string pdftitle = process_keyval_opt(options, "pdftitle");
627 if (!pdftitle.empty()) {
628 h_pdf_title = remove_braces(pdftitle);
630 string pdfauthor = process_keyval_opt(options, "pdfauthor");
631 if (!pdfauthor.empty()) {
632 h_pdf_author = remove_braces(pdfauthor);
634 string pdfsubject = process_keyval_opt(options, "pdfsubject");
635 if (!pdfsubject.empty())
636 h_pdf_subject = remove_braces(pdfsubject);
637 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
638 if (!pdfkeywords.empty())
639 h_pdf_keywords = remove_braces(pdfkeywords);
640 if (!options.empty()) {
641 if (!h_pdf_quoted_options.empty())
642 h_pdf_quoted_options += ',';
643 h_pdf_quoted_options += join(options, ",");
649 void Preamble::handle_geometry(vector<string> & options)
651 h_use_geometry = "true";
652 vector<string>::iterator it;
654 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
655 h_paperorientation = "landscape";
659 // keyval version: "paper=letter"
660 string paper = process_keyval_opt(options, "paper");
662 h_papersize = paper + "paper";
663 // alternative version: "letterpaper"
664 handle_opt(options, known_paper_sizes, h_papersize);
665 delete_opt(options, known_paper_sizes);
667 char const * const * margin = known_paper_margins;
668 for (; *margin; ++margin) {
669 string value = process_keyval_opt(options, *margin);
670 if (!value.empty()) {
671 int k = margin - known_paper_margins;
672 string name = known_coded_paper_margins[k];
673 h_margins += '\\' + name + ' ' + value + '\n';
679 void Preamble::handle_package(Parser &p, string const & name,
680 string const & opts, bool in_lyx_preamble,
683 vector<string> options = split_options(opts);
684 add_package(name, options);
685 char const * const * where = 0;
687 if (is_known(name, known_xetex_packages)) {
689 h_use_non_tex_fonts = true;
690 registerAutomaticallyLoadedPackage("fontspec");
691 if (h_inputencoding == "auto")
692 p.setEncoding("UTF-8");
696 if (is_known(name, known_roman_fonts))
697 h_font_roman[0] = name;
699 if (name == "fourier") {
700 h_font_roman[0] = "utopia";
701 // when font uses real small capitals
702 if (opts == "expert")
706 if (name == "garamondx") {
707 h_font_roman[0] = "garamondx";
712 if (name == "libertine") {
713 h_font_roman[0] = "libertine";
714 // this automatically invokes biolinum
715 h_font_sans[0] = "biolinum";
718 else if (opts == "lining")
719 h_font_osf = "false";
722 if (name == "libertine-type1") {
723 h_font_roman[0] = "libertine";
724 // NOTE: contrary to libertine.sty, libertine-type1
725 // does not automatically invoke biolinum
726 if (opts == "lining")
727 h_font_osf = "false";
728 else if (opts == "osf")
732 if (name == "mathdesign") {
733 if (opts.find("charter") != string::npos)
734 h_font_roman[0] = "md-charter";
735 if (opts.find("garamond") != string::npos)
736 h_font_roman[0] = "md-garamond";
737 if (opts.find("utopia") != string::npos)
738 h_font_roman[0] = "md-utopia";
739 if (opts.find("expert") != string::npos) {
745 else if (name == "mathpazo")
746 h_font_roman[0] = "palatino";
748 else if (name == "mathptmx")
749 h_font_roman[0] = "times";
752 if (is_known(name, known_sans_fonts)) {
753 h_font_sans[0] = name;
754 if (options.size() >= 1) {
755 if (scale_as_percentage(opts, h_font_sf_scale[0]))
760 if (name == "biolinum-type1") {
761 h_font_sans[0] = "biolinum";
762 // biolinum can have several options, e.g. [osf,scaled=0.97]
763 string::size_type pos = opts.find("osf");
764 if (pos != string::npos)
769 if (is_known(name, known_typewriter_fonts)) {
770 // fourier can be set as roman font _only_
771 // fourier as typewriter is handled in handling of \ttdefault
772 if (name != "fourier") {
773 h_font_typewriter[0] = name;
774 if (options.size() >= 1) {
775 if (scale_as_percentage(opts, h_font_tt_scale[0]))
781 if (name == "libertineMono-type1") {
782 h_font_typewriter[0] = "libertine-mono";
785 // font uses old-style figure
790 if (is_known(name, known_math_fonts))
791 h_font_math[0] = name;
793 if (name == "newtxmath") {
795 h_font_math[0] = "newtxmath";
796 else if (opts == "garamondx")
797 h_font_math[0] = "garamondx-ntxm";
798 else if (opts == "libertine")
799 h_font_math[0] = "libertine-ntxm";
800 else if (opts == "minion")
801 h_font_math[0] = "minion-ntxm";
806 h_font_math[0] = "iwona-math";
808 if (name == "kurier")
810 h_font_math[0] = "kurier-math";
812 // after the detection and handling of special cases, we can remove the
813 // fonts, otherwise they would appear in the preamble, see bug #7856
814 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
815 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
817 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
818 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
819 name == "esint" || name == "mhchem" || name == "mathdots" ||
820 name == "mathtools" || name == "stackrel" ||
821 name == "stmaryrd" || name == "undertilde")
822 h_use_packages[name] = "2";
824 else if (name == "babel") {
825 h_language_package = "default";
826 // One might think we would have to do nothing if babel is loaded
827 // without any options to prevent pollution of the preamble with this
828 // babel call in every roundtrip.
829 // But the user could have defined babel-specific things afterwards. So
830 // we need to keep it in the preamble to prevent cases like bug #7861.
832 // check if more than one option was used - used later for inputenc
833 if (options.begin() != options.end() - 1)
834 one_language = false;
835 // babel takes the last language of the option of its \usepackage
836 // call as document language. If there is no such language option, the
837 // last language in the documentclass options is used.
838 handle_opt(options, known_languages, h_language);
839 // translate the babel name to a LyX name
840 h_language = babel2lyx(h_language);
841 if (h_language == "japanese") {
842 // For Japanese, the encoding isn't indicated in the source
843 // file, and there's really not much we can do. We could
844 // 1) offer a list of possible encodings to choose from, or
845 // 2) determine the encoding of the file by inspecting it.
846 // For the time being, we leave the encoding alone so that
847 // we don't get iconv errors when making a wrong guess, and
848 // we will output a note at the top of the document
849 // explaining what to do.
850 Encoding const * const enc = encodings.fromIconvName(
851 p.getEncoding(), Encoding::japanese, false);
853 h_inputencoding = enc->name();
854 is_nonCJKJapanese = true;
855 // in this case babel can be removed from the preamble
856 registerAutomaticallyLoadedPackage("babel");
858 // If babel is called with options, LyX puts them by default into the
859 // document class options. This works for most languages, except
860 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
861 // perhaps in future others.
862 // Therefore keep the babel call as it is as the user might have
864 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
866 delete_opt(options, known_languages);
868 h_preamble << "\\usepackage{babel}\n";
869 explicit_babel = true;
873 else if (name == "polyglossia") {
874 h_language_package = "default";
875 h_default_output_format = "pdf4";
876 h_use_non_tex_fonts = true;
878 registerAutomaticallyLoadedPackage("xunicode");
879 if (h_inputencoding == "auto")
880 p.setEncoding("UTF-8");
883 else if (name == "CJK") {
884 // set the encoding to "auto" because it might be set to "default" by the babel handling
885 // and this would not be correct for CJK
886 if (h_inputencoding == "default")
887 h_inputencoding = "auto";
888 registerAutomaticallyLoadedPackage("CJK");
891 else if (name == "CJKutf8") {
892 h_inputencoding = "utf8-cjk";
893 p.setEncoding("UTF-8");
894 registerAutomaticallyLoadedPackage("CJKutf8");
897 else if (name == "fontenc") {
898 h_fontencoding = getStringFromVector(options, ",");
899 /* We could do the following for better round trip support,
900 * but this makes the document less portable, so I skip it:
901 if (h_fontencoding == lyxrc.fontenc)
902 h_fontencoding = "global";
907 else if (name == "inputenc" || name == "luainputenc") {
908 // h_inputencoding is only set when there is not more than one
909 // inputenc option because otherwise h_inputencoding must be
910 // set to "auto" (the default encoding of the document language)
911 // Therefore check that exactly one option is passed to inputenc.
912 // It is also only set when there is not more than one babel
914 if (!options.empty()) {
915 string const encoding = options.back();
916 Encoding const * const enc = encodings.fromLaTeXName(
917 encoding, Encoding::inputenc, true);
920 cerr << "Unknown encoding " << encoding
921 << ". Ignoring." << std::endl;
923 if (!enc->unsafe() && options.size() == 1 && one_language == true)
924 h_inputencoding = enc->name();
925 p.setEncoding(enc->iconvName());
931 else if (name == "srcltx") {
934 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
937 h_output_sync_macro = "\\usepackage{srcltx}";
940 else if (is_known(name, known_old_language_packages)) {
941 // known language packages from the times before babel
942 // if they are found and not also babel, they will be used as
943 // custom language package
944 h_language_package = "\\usepackage{" + name + "}";
947 else if (name == "lyxskak") {
948 // ignore this and its options
949 const char * const o[] = {"ps", "mover", 0};
950 delete_opt(options, o);
953 else if (is_known(name, known_lyx_packages) && options.empty()) {
954 if (name == "splitidx")
955 h_use_indices = "true";
956 if (name == "refstyle")
957 h_use_refstyle = true;
958 else if (name == "prettyref")
959 h_use_refstyle = false;
960 if (!in_lyx_preamble) {
961 h_preamble << package_beg_sep << name
962 << package_mid_sep << "\\usepackage{"
964 if (p.next_token().cat() == catNewline ||
965 (p.next_token().cat() == catSpace &&
966 p.next_next_token().cat() == catNewline))
968 h_preamble << package_end_sep;
972 else if (name == "geometry")
973 handle_geometry(options);
975 else if (name == "subfig")
976 ; // ignore this FIXME: Use the package separator mechanism instead
978 else if ((where = is_known(name, known_languages)))
979 h_language = known_coded_languages[where - known_languages];
981 else if (name == "natbib") {
982 h_biblio_style = "plainnat";
983 h_cite_engine = "natbib";
984 h_cite_engine_type = "authoryear";
985 vector<string>::iterator it =
986 find(options.begin(), options.end(), "authoryear");
987 if (it != options.end())
990 it = find(options.begin(), options.end(), "numbers");
991 if (it != options.end()) {
992 h_cite_engine_type = "numerical";
998 else if (name == "jurabib") {
999 h_biblio_style = "jurabib";
1000 h_cite_engine = "jurabib";
1001 h_cite_engine_type = "authoryear";
1004 else if (name == "bibtopic")
1005 h_use_bibtopic = "true";
1007 else if (name == "hyperref")
1008 handle_hyperref(options);
1010 else if (name == "algorithm2e") {
1011 // Load "algorithm2e" module
1012 addModule("algorithm2e");
1013 // Add the package options to the global document options
1014 if (!options.empty()) {
1015 if (h_options.empty())
1016 h_options = join(options, ",");
1018 h_options += ',' + join(options, ",");
1021 else if (name == "microtype") {
1022 //we internally support only microtype without params
1023 if (options.empty())
1024 h_use_microtype = true;
1026 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1029 else if (!in_lyx_preamble) {
1030 if (options.empty())
1031 h_preamble << "\\usepackage{" << name << '}';
1033 h_preamble << "\\usepackage[" << opts << "]{"
1037 if (p.next_token().cat() == catNewline ||
1038 (p.next_token().cat() == catSpace &&
1039 p.next_next_token().cat() == catNewline))
1043 // We need to do something with the options...
1044 if (!options.empty() && !detectEncoding)
1045 cerr << "Ignoring options '" << join(options, ",")
1046 << "' of package " << name << '.' << endl;
1048 // remove the whitespace
1053 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1056 Token t = p.get_token();
1057 if (t.cat() == catEscape &&
1058 is_known(t.cs(), known_if_commands))
1059 handle_if(p, in_lyx_preamble);
1061 if (!in_lyx_preamble)
1062 h_preamble << t.asInput();
1063 if (t.cat() == catEscape && t.cs() == "fi")
1070 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1072 // set the quote language
1073 // LyX only knows the following quotes languages:
1074 // english, swedish, german, polish, french and danish
1075 // (quotes for "japanese" and "chinese-traditional" are missing because
1076 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
1077 // conversion list taken from
1078 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1079 // (quotes for kazakh and interlingua are unknown)
1081 if (is_known(h_language, known_danish_quotes_languages))
1082 h_quotes_language = "danish";
1084 else if (is_known(h_language, known_french_quotes_languages))
1085 h_quotes_language = "french";
1087 else if (is_known(h_language, known_german_quotes_languages))
1088 h_quotes_language = "german";
1090 else if (is_known(h_language, known_polish_quotes_languages))
1091 h_quotes_language = "polish";
1093 else if (is_known(h_language, known_swedish_quotes_languages))
1094 h_quotes_language = "swedish";
1096 else if (is_known(h_language, known_english_quotes_languages))
1097 h_quotes_language = "english";
1099 if (contains(h_float_placement, "H"))
1100 registerAutomaticallyLoadedPackage("float");
1101 if (h_spacing != "single" && h_spacing != "default")
1102 registerAutomaticallyLoadedPackage("setspace");
1103 if (h_use_packages["amsmath"] == "2") {
1104 // amsbsy and amstext are already provided by amsmath
1105 registerAutomaticallyLoadedPackage("amsbsy");
1106 registerAutomaticallyLoadedPackage("amstext");
1109 // output the LyX file settings
1110 // Important: Keep the version formatting in sync with LyX and
1111 // lyx2lyx (bug 7951)
1112 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1113 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1114 << lyx_version_minor << '\n'
1115 << "\\lyxformat " << LYX_FORMAT << '\n'
1116 << "\\begin_document\n"
1117 << "\\begin_header\n"
1118 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1119 << "\\origin " << origin << "\n"
1120 << "\\textclass " << h_textclass << "\n";
1121 string const raw = subdoc ? empty_string() : h_preamble.str();
1123 os << "\\begin_preamble\n";
1124 for (string::size_type i = 0; i < raw.size(); ++i) {
1125 if (raw[i] == package_beg_sep) {
1126 // Here follows some package loading code that
1127 // must be skipped if the package is loaded
1129 string::size_type j = raw.find(package_mid_sep, i);
1130 if (j == string::npos)
1132 string::size_type k = raw.find(package_end_sep, j);
1133 if (k == string::npos)
1135 string const package = raw.substr(i + 1, j - i - 1);
1136 string const replacement = raw.substr(j + 1, k - j - 1);
1137 if (auto_packages.find(package) == auto_packages.end())
1143 os << "\n\\end_preamble\n";
1145 if (!h_options.empty())
1146 os << "\\options " << h_options << "\n";
1147 os << "\\use_default_options " << h_use_default_options << "\n";
1148 if (!used_modules.empty()) {
1149 os << "\\begin_modules\n";
1150 vector<string>::const_iterator const end = used_modules.end();
1151 vector<string>::const_iterator it = used_modules.begin();
1152 for (; it != end; ++it)
1154 os << "\\end_modules\n";
1156 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1157 << "\\language " << h_language << "\n"
1158 << "\\language_package " << h_language_package << "\n"
1159 << "\\inputencoding " << h_inputencoding << "\n"
1160 << "\\fontencoding " << h_fontencoding << "\n"
1161 << "\\font_roman \"" << h_font_roman[0]
1162 << "\" \"" << h_font_roman[1] << "\"\n"
1163 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1164 << "\\font_typewriter \"" << h_font_typewriter[0]
1165 << "\" \"" << h_font_typewriter[1] << "\"\n"
1166 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1167 << "\\font_default_family " << h_font_default_family << "\n"
1168 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1169 << "\\font_sc " << h_font_sc << "\n"
1170 << "\\font_osf " << h_font_osf << "\n"
1171 << "\\font_sf_scale " << h_font_sf_scale[0]
1172 << ' ' << h_font_sf_scale[1] << '\n'
1173 << "\\font_tt_scale " << h_font_tt_scale[0]
1174 << ' ' << h_font_tt_scale[1] << '\n';
1175 if (!h_font_cjk.empty())
1176 os << "\\font_cjk " << h_font_cjk << '\n';
1177 os << "\\use_microtype " << h_use_microtype << '\n'
1178 << "\\graphics " << h_graphics << '\n'
1179 << "\\default_output_format " << h_default_output_format << "\n"
1180 << "\\output_sync " << h_output_sync << "\n";
1181 if (h_output_sync == "1")
1182 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1183 os << "\\bibtex_command " << h_bibtex_command << "\n"
1184 << "\\index_command " << h_index_command << "\n";
1185 if (!h_float_placement.empty())
1186 os << "\\float_placement " << h_float_placement << "\n";
1187 os << "\\paperfontsize " << h_paperfontsize << "\n"
1188 << "\\spacing " << h_spacing << "\n"
1189 << "\\use_hyperref " << h_use_hyperref << '\n';
1190 if (h_use_hyperref == "true") {
1191 if (!h_pdf_title.empty())
1192 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1193 if (!h_pdf_author.empty())
1194 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1195 if (!h_pdf_subject.empty())
1196 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1197 if (!h_pdf_keywords.empty())
1198 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1199 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1200 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1201 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1202 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1203 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1204 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1205 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1206 "\\pdf_backref " << h_pdf_backref << "\n"
1207 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1208 if (!h_pdf_pagemode.empty())
1209 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1210 if (!h_pdf_quoted_options.empty())
1211 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1213 os << "\\papersize " << h_papersize << "\n"
1214 << "\\use_geometry " << h_use_geometry << '\n';
1215 for (map<string, string>::const_iterator it = h_use_packages.begin();
1216 it != h_use_packages.end(); ++it)
1217 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1218 os << "\\cite_engine " << h_cite_engine << '\n'
1219 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1220 << "\\biblio_style " << h_biblio_style << "\n"
1221 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1222 << "\\use_indices " << h_use_indices << "\n"
1223 << "\\paperorientation " << h_paperorientation << '\n'
1224 << "\\suppress_date " << h_suppress_date << '\n'
1225 << "\\justification " << h_justification << '\n'
1226 << "\\use_refstyle " << h_use_refstyle << '\n';
1227 if (!h_fontcolor.empty())
1228 os << "\\fontcolor " << h_fontcolor << '\n';
1229 if (!h_notefontcolor.empty())
1230 os << "\\notefontcolor " << h_notefontcolor << '\n';
1231 if (!h_backgroundcolor.empty())
1232 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1233 if (!h_boxbgcolor.empty())
1234 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1235 if (index_number != 0)
1236 for (int i = 0; i < index_number; i++) {
1237 os << "\\index " << h_index[i] << '\n'
1238 << "\\shortcut " << h_shortcut[i] << '\n'
1239 << "\\color " << h_color << '\n'
1243 os << "\\index " << h_index[0] << '\n'
1244 << "\\shortcut " << h_shortcut[0] << '\n'
1245 << "\\color " << h_color << '\n'
1249 << "\\secnumdepth " << h_secnumdepth << "\n"
1250 << "\\tocdepth " << h_tocdepth << "\n"
1251 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1252 if (h_paragraph_separation == "skip")
1253 os << "\\defskip " << h_defskip << "\n";
1255 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1256 os << "\\quotes_language " << h_quotes_language << "\n"
1257 << "\\papercolumns " << h_papercolumns << "\n"
1258 << "\\papersides " << h_papersides << "\n"
1259 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1260 if (!h_listings_params.empty())
1261 os << "\\listings_params " << h_listings_params << "\n";
1262 os << "\\tracking_changes " << h_tracking_changes << "\n"
1263 << "\\output_changes " << h_output_changes << "\n"
1264 << "\\html_math_output " << h_html_math_output << "\n"
1265 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1266 << "\\html_be_strict " << h_html_be_strict << "\n"
1268 << "\\end_header\n\n"
1269 << "\\begin_body\n";
1274 void Preamble::parse(Parser & p, string const & forceclass,
1275 TeX2LyXDocClass & tc)
1277 // initialize fixed types
1278 special_columns_['D'] = 3;
1279 parse(p, forceclass, false, tc);
1283 void Preamble::parse(Parser & p, string const & forceclass,
1284 bool detectEncoding, TeX2LyXDocClass & tc)
1286 bool is_full_document = false;
1287 bool is_lyx_file = false;
1288 bool in_lyx_preamble = false;
1290 // determine whether this is a full document or a fragment for inclusion
1292 Token const & t = p.get_token();
1294 if (t.cat() == catEscape && t.cs() == "documentclass") {
1295 is_full_document = true;
1301 if (detectEncoding && !is_full_document)
1304 while (is_full_document && p.good()) {
1305 if (detectEncoding && h_inputencoding != "auto" &&
1306 h_inputencoding != "default")
1309 Token const & t = p.get_token();
1312 if (!detectEncoding)
1313 cerr << "t: " << t << '\n';
1319 if (!in_lyx_preamble &&
1320 (t.cat() == catLetter ||
1321 t.cat() == catSuper ||
1322 t.cat() == catSub ||
1323 t.cat() == catOther ||
1324 t.cat() == catMath ||
1325 t.cat() == catActive ||
1326 t.cat() == catBegin ||
1327 t.cat() == catEnd ||
1328 t.cat() == catAlign ||
1329 t.cat() == catParameter))
1330 h_preamble << t.cs();
1332 else if (!in_lyx_preamble &&
1333 (t.cat() == catSpace || t.cat() == catNewline))
1334 h_preamble << t.asInput();
1336 else if (t.cat() == catComment) {
1337 static regex const islyxfile("%% LyX .* created this file");
1338 static regex const usercommands("User specified LaTeX commands");
1340 string const comment = t.asInput();
1342 // magically switch encoding default if it looks like XeLaTeX
1343 static string const magicXeLaTeX =
1344 "% This document must be compiled with XeLaTeX ";
1345 if (comment.size() > magicXeLaTeX.size()
1346 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1347 && h_inputencoding == "auto") {
1348 if (!detectEncoding)
1349 cerr << "XeLaTeX comment found, switching to UTF8\n";
1350 h_inputencoding = "utf8";
1353 if (regex_search(comment, sub, islyxfile)) {
1355 in_lyx_preamble = true;
1356 } else if (is_lyx_file
1357 && regex_search(comment, sub, usercommands))
1358 in_lyx_preamble = false;
1359 else if (!in_lyx_preamble)
1360 h_preamble << t.asInput();
1363 else if (t.cs() == "pagestyle")
1364 h_paperpagestyle = p.verbatim_item();
1366 else if (t.cs() == "setdefaultlanguage") {
1368 // We don't yet care about non-language variant options
1369 // because LyX doesn't support this yet, see bug #8214
1371 string langopts = p.getOpt();
1372 // check if the option contains a variant, if yes, extract it
1373 string::size_type pos_var = langopts.find("variant");
1374 string::size_type i = langopts.find(',', pos_var);
1375 string::size_type k = langopts.find('=', pos_var);
1376 if (pos_var != string::npos){
1378 if (i == string::npos)
1379 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1381 variant = langopts.substr(k + 1, i - k - 1);
1382 h_language = variant;
1386 h_language = p.verbatim_item();
1387 //finally translate the poyglossia name to a LyX name
1388 h_language = polyglossia2lyx(h_language);
1391 else if (t.cs() == "setotherlanguage") {
1392 // We don't yet care about the option because LyX doesn't
1393 // support this yet, see bug #8214
1394 p.hasOpt() ? p.getOpt() : string();
1398 else if (t.cs() == "setmainfont") {
1399 // we don't care about the option
1400 p.hasOpt() ? p.getOpt() : string();
1401 h_font_roman[1] = p.getArg('{', '}');
1404 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1405 // LyX currently only supports the scale option
1408 string fontopts = p.getArg('[', ']');
1409 // check if the option contains a scaling, if yes, extract it
1410 string::size_type pos = fontopts.find("Scale");
1411 if (pos != string::npos) {
1412 string::size_type i = fontopts.find(',', pos);
1413 if (i == string::npos)
1414 scale_as_percentage(fontopts.substr(pos + 1), scale);
1416 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1419 if (t.cs() == "setsansfont") {
1421 h_font_sf_scale[1] = scale;
1422 h_font_sans[1] = p.getArg('{', '}');
1425 h_font_tt_scale[1] = scale;
1426 h_font_typewriter[1] = p.getArg('{', '}');
1430 else if (t.cs() == "date") {
1431 string argument = p.getArg('{', '}');
1432 if (argument.empty())
1433 h_suppress_date = "true";
1435 h_preamble << t.asInput() << '{' << argument << '}';
1438 else if (t.cs() == "color") {
1439 string const space =
1440 (p.hasOpt() ? p.getOpt() : string());
1441 string argument = p.getArg('{', '}');
1442 // check the case that a standard color is used
1443 if (space.empty() && is_known(argument, known_basic_colors)) {
1444 h_fontcolor = rgbcolor2code(argument);
1445 registerAutomaticallyLoadedPackage("color");
1446 } else if (space.empty() && argument == "document_fontcolor")
1447 registerAutomaticallyLoadedPackage("color");
1448 // check the case that LyX's document_fontcolor is defined
1449 // but not used for \color
1451 h_preamble << t.asInput();
1453 h_preamble << space;
1454 h_preamble << '{' << argument << '}';
1455 // the color might already be set because \definecolor
1456 // is parsed before this
1461 else if (t.cs() == "pagecolor") {
1462 string argument = p.getArg('{', '}');
1463 // check the case that a standard color is used
1464 if (is_known(argument, known_basic_colors)) {
1465 h_backgroundcolor = rgbcolor2code(argument);
1466 } else if (argument == "page_backgroundcolor")
1467 registerAutomaticallyLoadedPackage("color");
1468 // check the case that LyX's page_backgroundcolor is defined
1469 // but not used for \pagecolor
1471 h_preamble << t.asInput() << '{' << argument << '}';
1472 // the color might already be set because \definecolor
1473 // is parsed before this
1474 h_backgroundcolor = "";
1478 else if (t.cs() == "makeatletter") {
1479 // LyX takes care of this
1480 p.setCatcode('@', catLetter);
1483 else if (t.cs() == "makeatother") {
1484 // LyX takes care of this
1485 p.setCatcode('@', catOther);
1488 else if (t.cs() == "makeindex") {
1489 // LyX will re-add this if a print index command is found
1493 else if (t.cs() == "newindex") {
1494 string const indexname = p.getArg('[', ']');
1495 string const shortcut = p.verbatim_item();
1496 if (!indexname.empty())
1497 h_index[index_number] = indexname;
1499 h_index[index_number] = shortcut;
1500 h_shortcut[index_number] = shortcut;
1505 else if (t.cs() == "RS@ifundefined") {
1506 string const name = p.verbatim_item();
1507 string const body1 = p.verbatim_item();
1508 string const body2 = p.verbatim_item();
1509 // only non-lyxspecific stuff
1510 if (in_lyx_preamble &&
1511 (name == "subsecref" || name == "thmref" || name == "lemref"))
1515 ss << '\\' << t.cs();
1516 ss << '{' << name << '}'
1517 << '{' << body1 << '}'
1518 << '{' << body2 << '}';
1519 h_preamble << ss.str();
1523 else if (t.cs() == "AtBeginDocument") {
1524 string const name = p.verbatim_item();
1525 // only non-lyxspecific stuff
1526 if (in_lyx_preamble &&
1527 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1528 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1529 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1530 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1531 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1532 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1533 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1534 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1535 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1536 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1537 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1538 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1539 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1540 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1541 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1545 ss << '\\' << t.cs();
1546 ss << '{' << name << '}';
1547 h_preamble << ss.str();
1551 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1552 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1553 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1554 || t.cs() == "DeclareRobustCommand"
1555 || t.cs() == "DeclareRobustCommandx"
1556 || t.cs() == "ProvideTextCommandDefault"
1557 || t.cs() == "DeclareMathAccent") {
1559 if (p.next_token().character() == '*') {
1563 string const name = p.verbatim_item();
1564 string const opt1 = p.getFullOpt();
1565 string const opt2 = p.getFullOpt();
1566 string const body = p.verbatim_item();
1567 // store the in_lyx_preamble setting
1568 bool const was_in_lyx_preamble = in_lyx_preamble;
1570 if (name == "\\rmdefault")
1571 if (is_known(body, known_roman_fonts)) {
1572 h_font_roman[0] = body;
1574 in_lyx_preamble = true;
1576 if (name == "\\sfdefault")
1577 if (is_known(body, known_sans_fonts)) {
1578 h_font_sans[0] = body;
1580 in_lyx_preamble = true;
1582 if (name == "\\ttdefault")
1583 if (is_known(body, known_typewriter_fonts)) {
1584 h_font_typewriter[0] = body;
1586 in_lyx_preamble = true;
1588 if (name == "\\familydefault") {
1589 string family = body;
1590 // remove leading "\"
1591 h_font_default_family = family.erase(0,1);
1593 in_lyx_preamble = true;
1596 // remove LyX-specific definitions that are re-added by LyX
1598 // \lyxline is an ancient command that is converted by tex2lyx into
1599 // a \rule therefore remove its preamble code
1600 if (name == "\\lyxdot" || name == "\\lyxarrow"
1601 || name == "\\lyxline" || name == "\\LyX") {
1603 in_lyx_preamble = true;
1606 // Add the command to the known commands
1607 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1609 // only non-lyxspecific stuff
1610 if (!in_lyx_preamble) {
1612 ss << '\\' << t.cs();
1615 ss << '{' << name << '}' << opt1 << opt2
1616 << '{' << body << "}";
1617 h_preamble << ss.str();
1619 ostream & out = in_preamble ? h_preamble : os;
1620 out << "\\" << t.cs() << "{" << name << "}"
1621 << opts << "{" << body << "}";
1624 // restore the in_lyx_preamble setting
1625 in_lyx_preamble = was_in_lyx_preamble;
1628 else if (t.cs() == "documentclass") {
1629 vector<string>::iterator it;
1630 vector<string> opts = split_options(p.getArg('[', ']'));
1631 handle_opt(opts, known_fontsizes, h_paperfontsize);
1632 delete_opt(opts, known_fontsizes);
1633 // delete "pt" at the end
1634 string::size_type i = h_paperfontsize.find("pt");
1635 if (i != string::npos)
1636 h_paperfontsize.erase(i);
1637 // The documentclass options are always parsed before the options
1638 // of the babel call so that a language cannot overwrite the babel
1640 handle_opt(opts, known_languages, h_language);
1641 delete_opt(opts, known_languages);
1643 // paper orientation
1644 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1645 h_paperorientation = "landscape";
1649 if ((it = find(opts.begin(), opts.end(), "oneside"))
1654 if ((it = find(opts.begin(), opts.end(), "twoside"))
1660 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1662 h_papercolumns = "1";
1665 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1667 h_papercolumns = "2";
1671 // some size options are known to any document classes, other sizes
1672 // are handled by the \geometry command of the geometry package
1673 handle_opt(opts, known_class_paper_sizes, h_papersize);
1674 delete_opt(opts, known_class_paper_sizes);
1675 // the remaining options
1676 h_options = join(opts, ",");
1677 // FIXME This does not work for classes that have a
1678 // different name in LyX than in LaTeX
1679 h_textclass = p.getArg('{', '}');
1683 else if (t.cs() == "usepackage") {
1684 string const options = p.getArg('[', ']');
1685 string const name = p.getArg('{', '}');
1686 vector<string> vecnames;
1687 split(name, vecnames, ',');
1688 vector<string>::const_iterator it = vecnames.begin();
1689 vector<string>::const_iterator end = vecnames.end();
1690 for (; it != end; ++it)
1691 handle_package(p, trimSpaceAndEol(*it), options,
1692 in_lyx_preamble, detectEncoding);
1695 else if (t.cs() == "inputencoding") {
1696 string const encoding = p.getArg('{','}');
1697 Encoding const * const enc = encodings.fromLaTeXName(
1698 encoding, Encoding::inputenc, true);
1700 if (!detectEncoding)
1701 cerr << "Unknown encoding " << encoding
1702 << ". Ignoring." << std::endl;
1705 h_inputencoding = enc->name();
1706 p.setEncoding(enc->iconvName());
1710 else if (t.cs() == "newenvironment") {
1711 string const name = p.getArg('{', '}');
1712 string const opt1 = p.getFullOpt();
1713 string const opt2 = p.getFullOpt();
1714 string const beg = p.verbatim_item();
1715 string const end = p.verbatim_item();
1716 if (!in_lyx_preamble) {
1717 h_preamble << "\\newenvironment{" << name
1718 << '}' << opt1 << opt2 << '{'
1719 << beg << "}{" << end << '}';
1721 add_known_environment(name, opt1, !opt2.empty(),
1722 from_utf8(beg), from_utf8(end));
1726 else if (t.cs() == "newtheorem") {
1727 string const name = p.getArg('{', '}');
1728 string const opt1 = p.getFullOpt();
1729 string const opt2 = p.getFullOpt();
1730 string const body = p.verbatim_item();
1731 string const opt3 = p.getFullOpt();
1733 string const complete = "\\newtheorem{" + name + '}' +
1734 opt1 + opt2 + '{' + body + '}' + opt3;
1736 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1738 if (!in_lyx_preamble)
1739 h_preamble << complete;
1742 else if (t.cs() == "def") {
1743 string name = p.get_token().cs();
1744 // In fact, name may be more than the name:
1745 // In the test case of bug 8116
1746 // name == "csname SF@gobble@opt \endcsname".
1747 // Therefore, we need to use asInput() instead of cs().
1748 while (p.next_token().cat() != catBegin)
1749 name += p.get_token().asInput();
1750 if (!in_lyx_preamble)
1751 h_preamble << "\\def\\" << name << '{'
1752 << p.verbatim_item() << "}";
1755 else if (t.cs() == "newcolumntype") {
1756 string const name = p.getArg('{', '}');
1757 trimSpaceAndEol(name);
1759 string opts = p.getOpt();
1760 if (!opts.empty()) {
1761 istringstream is(string(opts, 1));
1764 special_columns_[name[0]] = nargs;
1765 h_preamble << "\\newcolumntype{" << name << "}";
1767 h_preamble << "[" << nargs << "]";
1768 h_preamble << "{" << p.verbatim_item() << "}";
1771 else if (t.cs() == "setcounter") {
1772 string const name = p.getArg('{', '}');
1773 string const content = p.getArg('{', '}');
1774 if (name == "secnumdepth")
1775 h_secnumdepth = content;
1776 else if (name == "tocdepth")
1777 h_tocdepth = content;
1779 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1782 else if (t.cs() == "setlength") {
1783 string const name = p.verbatim_item();
1784 string const content = p.verbatim_item();
1785 // the paragraphs are only not indented when \parindent is set to zero
1786 if (name == "\\parindent" && content != "") {
1787 if (content[0] == '0')
1788 h_paragraph_separation = "skip";
1790 h_paragraph_indentation = translate_len(content);
1791 } else if (name == "\\parskip") {
1792 if (content == "\\smallskipamount")
1793 h_defskip = "smallskip";
1794 else if (content == "\\medskipamount")
1795 h_defskip = "medskip";
1796 else if (content == "\\bigskipamount")
1797 h_defskip = "bigskip";
1799 h_defskip = translate_len(content);
1801 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1804 else if (t.cs() == "onehalfspacing")
1805 h_spacing = "onehalf";
1807 else if (t.cs() == "doublespacing")
1808 h_spacing = "double";
1810 else if (t.cs() == "setstretch")
1811 h_spacing = "other " + p.verbatim_item();
1813 else if (t.cs() == "synctex") {
1814 // the scheme is \synctex=value
1815 // where value can only be "1" or "-1"
1816 h_output_sync = "1";
1817 // there can be any character behind the value (e.g. a linebreak or a '\'
1818 // therefore we extract it char by char
1820 string value = p.get_token().asInput();
1822 value += p.get_token().asInput();
1823 h_output_sync_macro = "\\synctex=" + value;
1826 else if (t.cs() == "begin") {
1827 string const name = p.getArg('{', '}');
1828 if (name == "document")
1830 h_preamble << "\\begin{" << name << "}";
1833 else if (t.cs() == "geometry") {
1834 vector<string> opts = split_options(p.getArg('{', '}'));
1835 handle_geometry(opts);
1838 else if (t.cs() == "definecolor") {
1839 string const color = p.getArg('{', '}');
1840 string const space = p.getArg('{', '}');
1841 string const value = p.getArg('{', '}');
1842 if (color == "document_fontcolor" && space == "rgb") {
1843 RGBColor c(RGBColorFromLaTeX(value));
1844 h_fontcolor = X11hexname(c);
1845 } else if (color == "note_fontcolor" && space == "rgb") {
1846 RGBColor c(RGBColorFromLaTeX(value));
1847 h_notefontcolor = X11hexname(c);
1848 } else if (color == "page_backgroundcolor" && space == "rgb") {
1849 RGBColor c(RGBColorFromLaTeX(value));
1850 h_backgroundcolor = X11hexname(c);
1851 } else if (color == "shadecolor" && space == "rgb") {
1852 RGBColor c(RGBColorFromLaTeX(value));
1853 h_boxbgcolor = X11hexname(c);
1855 h_preamble << "\\definecolor{" << color
1856 << "}{" << space << "}{" << value
1861 else if (t.cs() == "bibliographystyle")
1862 h_biblio_style = p.verbatim_item();
1864 else if (t.cs() == "jurabibsetup") {
1865 // FIXME p.getArg('{', '}') is most probably wrong (it
1866 // does not handle nested braces).
1867 // Use p.verbatim_item() instead.
1868 vector<string> jurabibsetup =
1869 split_options(p.getArg('{', '}'));
1870 // add jurabibsetup to the jurabib package options
1871 add_package("jurabib", jurabibsetup);
1872 if (!jurabibsetup.empty()) {
1873 h_preamble << "\\jurabibsetup{"
1874 << join(jurabibsetup, ",") << '}';
1878 else if (t.cs() == "hypersetup") {
1879 vector<string> hypersetup =
1880 split_options(p.verbatim_item());
1881 // add hypersetup to the hyperref package options
1882 handle_hyperref(hypersetup);
1883 if (!hypersetup.empty()) {
1884 h_preamble << "\\hypersetup{"
1885 << join(hypersetup, ",") << '}';
1889 else if (is_known(t.cs(), known_if_3arg_commands)) {
1890 // prevent misparsing of \usepackage if it is used
1891 // as an argument (see e.g. our own output of
1892 // \@ifundefined above)
1893 string const arg1 = p.verbatim_item();
1894 string const arg2 = p.verbatim_item();
1895 string const arg3 = p.verbatim_item();
1896 // test case \@ifundefined{date}{}{\date{}}
1897 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1898 arg2.empty() && arg3 == "\\date{}") {
1899 h_suppress_date = "true";
1900 // older tex2lyx versions did output
1901 // \@ifundefined{definecolor}{\usepackage{color}}{}
1902 } else if (t.cs() == "@ifundefined" &&
1903 arg1 == "definecolor" &&
1904 arg2 == "\\usepackage{color}" &&
1906 if (!in_lyx_preamble)
1907 h_preamble << package_beg_sep
1910 << "\\@ifundefined{definecolor}{color}{}"
1913 //\@ifundefined{showcaptionsetup}{}{%
1914 // \PassOptionsToPackage{caption=false}{subfig}}
1915 // that LyX uses for subfloats
1916 } else if (t.cs() == "@ifundefined" &&
1917 arg1 == "showcaptionsetup" && arg2.empty()
1918 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1920 } else if (!in_lyx_preamble) {
1921 h_preamble << t.asInput()
1922 << '{' << arg1 << '}'
1923 << '{' << arg2 << '}'
1924 << '{' << arg3 << '}';
1928 else if (is_known(t.cs(), known_if_commands)) {
1929 // must not parse anything in conditional code, since
1930 // LyX would output the parsed contents unconditionally
1931 if (!in_lyx_preamble)
1932 h_preamble << t.asInput();
1933 handle_if(p, in_lyx_preamble);
1936 else if (!t.cs().empty() && !in_lyx_preamble)
1937 h_preamble << '\\' << t.cs();
1940 // remove the whitespace
1943 // Force textclass if the user wanted it
1944 if (!forceclass.empty())
1945 h_textclass = forceclass;
1946 tc.setName(h_textclass);
1948 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1951 if (h_papersides.empty()) {
1954 h_papersides = ss.str();
1957 // If the CJK package is used we cannot set the document language from
1958 // the babel options. Instead, we guess which language is used most
1959 // and set this one.
1960 default_language = h_language;
1961 if (is_full_document &&
1962 (auto_packages.find("CJK") != auto_packages.end() ||
1963 auto_packages.find("CJKutf8") != auto_packages.end())) {
1965 h_language = guessLanguage(p, default_language);
1967 if (explicit_babel && h_language != default_language) {
1968 // We set the document language to a CJK language,
1969 // but babel is explicitly called in the user preamble
1970 // without options. LyX will not add the default
1971 // language to the document options if it is either
1972 // english, or no text is set as default language.
1973 // Therefore we need to add a language option explicitly.
1974 // FIXME: It would be better to remove all babel calls
1975 // from the user preamble, but this is difficult
1976 // without re-introducing bug 7861.
1977 if (h_options.empty())
1978 h_options = lyx2babel(default_language);
1980 h_options += ',' + lyx2babel(default_language);
1986 string Preamble::parseEncoding(Parser & p, string const & forceclass)
1988 TeX2LyXDocClass dummy;
1989 parse(p, forceclass, true, dummy);
1990 if (h_inputencoding != "auto" && h_inputencoding != "default")
1991 return h_inputencoding;
1996 string babel2lyx(string const & language)
1998 char const * const * where = is_known(language, known_languages);
2000 return known_coded_languages[where - known_languages];
2005 string lyx2babel(string const & language)
2007 char const * const * where = is_known(language, known_coded_languages);
2009 return known_languages[where - known_coded_languages];
2014 string Preamble::polyglossia2lyx(string const & language)
2016 char const * const * where = is_known(language, polyglossia_languages);
2018 return coded_polyglossia_languages[where - polyglossia_languages];
2023 string rgbcolor2code(string const & name)
2025 char const * const * where = is_known(name, known_basic_colors);
2027 // "red", "green" etc
2028 return known_basic_color_codes[where - known_basic_colors];
2030 // "255,0,0", "0,255,0" etc
2031 RGBColor c(RGBColorFromLaTeX(name));
2032 return X11hexname(c);