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", "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", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "polutonikogreek", "polish", "portuges", "portuguese", "romanian", "russian",
64 "russianb", "samin", "scottish", "serbian", "serbian-latin", "slovak",
65 "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", "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 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "polutonikogreek", "polish", "portuguese", "portuguese", "romanian", "russian",
85 "russian", "samin", "scottish", "serbian", "serbian-latin", "slovak",
86 "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",
103 "galician", "greek", "italian", "norsk", "nynorsk", "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", "naustrian",
110 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
112 /// languages with polish quotes (.lyx names)
113 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
114 "dutch", "estonian", "magyar", "polish", "romanian", 0};
116 /// languages with swedish quotes (.lyx names)
117 const char * const known_swedish_quotes_languages[] = {"finnish",
120 /// known language packages from the times before babel
121 const char * const known_old_language_packages[] = {"french", "frenchle",
122 "frenchpro", "german", "ngerman", "pmfrench", 0};
124 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
126 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
127 "ccfonts", "chancery", "charter", "cmr", "fourier", "garamondx", "libertine",
128 "libertine-type1", "lmodern", "mathdesign", "mathpazo", "mathptmx", "newcent",
129 "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", 0};
131 const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum-type1",
132 "cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc", "kurier",
133 "kurierc", "kurierl", "kurierlc", "lmss", "tgadventor", "tgheros", 0};
135 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
136 "courier", "lmtt", "luximono", "fourier", "libertineMono-type1", "lmodern",
137 "mathpazo", "mathptmx", "newcent", "tgcursor", "txtt", 0};
139 const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0};
141 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
142 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
143 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
144 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
145 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
147 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
148 "executivepaper", "legalpaper", "letterpaper", 0};
150 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
151 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
153 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
154 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
157 /// commands that can start an \if...\else...\endif sequence
158 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
159 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
160 "ifsidecap", "ifupgreek", 0};
162 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
163 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
164 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
166 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
167 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
168 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
170 /// conditional commands with three arguments like \@ifundefined{}{}{}
171 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
174 /// packages that work only in xetex
175 /// polyglossia is handled separately
176 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
177 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
178 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
180 /// packages that are automatically skipped if loaded by LyX
181 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
182 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
183 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
184 "makeidx", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle", "rotating",
185 "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa", "tipx",
186 "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xunicode", 0};
188 // used for the handling of \newindex
189 int index_number = 0;
191 // codes used to remove packages that are loaded automatically by LyX.
192 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
193 const char package_beg_sep = '\001';
194 const char package_mid_sep = '\002';
195 const char package_end_sep = '\003';
198 // returns true if at least one of the options in what has been found
199 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
205 // the last language option is the document language (for babel and LyX)
206 // the last size option is the document font size
207 vector<string>::iterator it;
208 vector<string>::iterator position = opts.begin();
209 for (; *what; ++what) {
210 it = find(opts.begin(), opts.end(), *what);
211 if (it != opts.end()) {
212 if (it >= position) {
223 void delete_opt(vector<string> & opts, char const * const * what)
228 // remove found options from the list
229 // do this after handle_opt to avoid potential memory leaks
230 vector<string>::iterator it;
231 for (; *what; ++what) {
232 it = find(opts.begin(), opts.end(), *what);
233 if (it != opts.end())
240 * Split a package options string (keyval format) into a vector.
242 * authorformat=smallcaps,
244 * titleformat=colonsep,
245 * bibformat={tabular,ibidem,numbered}
247 vector<string> split_options(string const & input)
249 vector<string> options;
253 Token const & t = p.get_token();
254 if (t.asInput() == ",") {
255 options.push_back(trimSpaceAndEol(option));
257 } else if (t.asInput() == "=") {
260 if (p.next_token().asInput() == "{")
261 option += '{' + p.getArg('{', '}') + '}';
262 } else if (t.cat() != catSpace)
263 option += t.asInput();
267 options.push_back(trimSpaceAndEol(option));
274 * Retrieve a keyval option "name={value with=sign}" named \p name from
275 * \p options and return the value.
276 * The found option is also removed from \p options.
278 string process_keyval_opt(vector<string> & options, string name)
280 for (size_t i = 0; i < options.size(); ++i) {
281 vector<string> option;
282 split(options[i], option, '=');
283 if (option.size() < 2)
285 if (option[0] == name) {
286 options.erase(options.begin() + i);
287 option.erase(option.begin());
288 return join(option, "=");
294 } // anonymous namespace
298 * known polyglossia language names (including variants)
299 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
301 const char * const Preamble::polyglossia_languages[] = {
302 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
303 "nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil",
304 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
305 "irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish",
306 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
307 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "tibetan", "urdu",
308 "brazil", "brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton",
309 "french", "lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar",
310 "slovak", "welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek",
311 "marathi", "spanish", "austrian",
312 "american", "ancient", "australian", "british", "monotonic", "newzealand",
316 * the same as polyglossia_languages with .lyx names
317 * please keep this in sync with polyglossia_languages line by line!
319 const char * const Preamble::coded_polyglossia_languages[] = {
320 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
321 "nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil",
322 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
323 "irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish",
324 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
325 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "tibetan", "urdu",
326 "brazilian", "brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton",
327 "french", "lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar",
328 "slovak", "welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek",
329 "marathi", "spanish", "naustrian",
330 "american", "ancientgreek", "australian", "british", "greek", "newzealand",
331 "polutonikogreek", 0};
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), h_font_cjk_set(false)
467 h_biblio_style = "plain";
468 h_bibtex_command = "default";
469 h_cite_engine = "basic";
470 h_cite_engine_type = "default";
472 h_defskip = "medskip";
475 h_fontencoding = "default";
476 h_font_roman[0] = "default";
477 h_font_roman[1] = "default";
478 h_font_sans[0] = "default";
479 h_font_sans[1] = "default";
480 h_font_typewriter[0] = "default";
481 h_font_typewriter[1] = "default";
482 h_font_math[0] = "auto";
483 h_font_math[1] = "auto";
484 h_font_default_family = "default";
485 h_use_non_tex_fonts = false;
487 h_font_osf = "false";
488 h_font_sf_scale[0] = "100";
489 h_font_sf_scale[1] = "100";
490 h_font_tt_scale[0] = "100";
491 h_font_tt_scale[1] = "100";
493 h_graphics = "default";
494 h_default_output_format = "default";
495 h_html_be_strict = "false";
496 h_html_css_as_file = "0";
497 h_html_math_output = "0";
498 h_index[0] = "Index";
499 h_index_command = "default";
500 h_inputencoding = "auto";
501 h_justification = "true";
502 h_language = "english";
503 h_language_package = "none";
505 h_maintain_unincluded_children = "false";
509 h_output_changes = "false";
511 //h_output_sync_macro
512 h_papercolumns = "1";
513 h_paperfontsize = "default";
514 h_paperorientation = "portrait";
515 h_paperpagestyle = "default";
517 h_papersize = "default";
518 h_paragraph_indentation = "default";
519 h_paragraph_separation = "indent";
524 h_pdf_bookmarks = "0";
525 h_pdf_bookmarksnumbered = "0";
526 h_pdf_bookmarksopen = "0";
527 h_pdf_bookmarksopenlevel = "1";
528 h_pdf_breaklinks = "0";
529 h_pdf_pdfborder = "0";
530 h_pdf_colorlinks = "0";
531 h_pdf_backref = "section";
532 h_pdf_pdfusetitle = "0";
534 //h_pdf_quoted_options;
535 h_quotes_language = "english";
537 h_shortcut[0] = "idx";
538 h_spacing = "single";
539 h_suppress_date = "false";
540 h_textclass = "article";
542 h_tracking_changes = "false";
543 h_use_bibtopic = "false";
544 h_use_indices = "false";
545 h_use_geometry = "false";
546 h_use_default_options = "false";
547 h_use_hyperref = "false";
548 h_use_refstyle = false;
549 h_use_packages["amsmath"] = "1";
550 h_use_packages["amssymb"] = "0";
551 h_use_packages["cancel"] = "0";
552 h_use_packages["esint"] = "1";
553 h_use_packages["mhchem"] = "0";
554 h_use_packages["mathdots"] = "0";
555 h_use_packages["mathtools"] = "0";
556 h_use_packages["stackrel"] = "0";
557 h_use_packages["stmaryrd"] = "0";
558 h_use_packages["undertilde"] = "0";
562 void Preamble::handle_hyperref(vector<string> & options)
564 // FIXME swallow inputencoding changes that might surround the
565 // hyperref setup if it was written by LyX
566 h_use_hyperref = "true";
567 // swallow "unicode=true", since LyX does always write that
568 vector<string>::iterator it =
569 find(options.begin(), options.end(), "unicode=true");
570 if (it != options.end())
572 it = find(options.begin(), options.end(), "pdfusetitle");
573 if (it != options.end()) {
574 h_pdf_pdfusetitle = "1";
577 string bookmarks = process_keyval_opt(options, "bookmarks");
578 if (bookmarks == "true")
579 h_pdf_bookmarks = "1";
580 else if (bookmarks == "false")
581 h_pdf_bookmarks = "0";
582 if (h_pdf_bookmarks == "1") {
583 string bookmarksnumbered =
584 process_keyval_opt(options, "bookmarksnumbered");
585 if (bookmarksnumbered == "true")
586 h_pdf_bookmarksnumbered = "1";
587 else if (bookmarksnumbered == "false")
588 h_pdf_bookmarksnumbered = "0";
589 string bookmarksopen =
590 process_keyval_opt(options, "bookmarksopen");
591 if (bookmarksopen == "true")
592 h_pdf_bookmarksopen = "1";
593 else if (bookmarksopen == "false")
594 h_pdf_bookmarksopen = "0";
595 if (h_pdf_bookmarksopen == "1") {
596 string bookmarksopenlevel =
597 process_keyval_opt(options, "bookmarksopenlevel");
598 if (!bookmarksopenlevel.empty())
599 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
602 string breaklinks = process_keyval_opt(options, "breaklinks");
603 if (breaklinks == "true")
604 h_pdf_breaklinks = "1";
605 else if (breaklinks == "false")
606 h_pdf_breaklinks = "0";
607 string pdfborder = process_keyval_opt(options, "pdfborder");
608 if (pdfborder == "{0 0 0}")
609 h_pdf_pdfborder = "1";
610 else if (pdfborder == "{0 0 1}")
611 h_pdf_pdfborder = "0";
612 string backref = process_keyval_opt(options, "backref");
613 if (!backref.empty())
614 h_pdf_backref = backref;
615 string colorlinks = process_keyval_opt(options, "colorlinks");
616 if (colorlinks == "true")
617 h_pdf_colorlinks = "1";
618 else if (colorlinks == "false")
619 h_pdf_colorlinks = "0";
620 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
621 if (!pdfpagemode.empty())
622 h_pdf_pagemode = pdfpagemode;
623 string pdftitle = process_keyval_opt(options, "pdftitle");
624 if (!pdftitle.empty()) {
625 h_pdf_title = remove_braces(pdftitle);
627 string pdfauthor = process_keyval_opt(options, "pdfauthor");
628 if (!pdfauthor.empty()) {
629 h_pdf_author = remove_braces(pdfauthor);
631 string pdfsubject = process_keyval_opt(options, "pdfsubject");
632 if (!pdfsubject.empty())
633 h_pdf_subject = remove_braces(pdfsubject);
634 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
635 if (!pdfkeywords.empty())
636 h_pdf_keywords = remove_braces(pdfkeywords);
637 if (!options.empty()) {
638 if (!h_pdf_quoted_options.empty())
639 h_pdf_quoted_options += ',';
640 h_pdf_quoted_options += join(options, ",");
646 void Preamble::handle_geometry(vector<string> & options)
648 h_use_geometry = "true";
649 vector<string>::iterator it;
651 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
652 h_paperorientation = "landscape";
656 // keyval version: "paper=letter"
657 string paper = process_keyval_opt(options, "paper");
659 h_papersize = paper + "paper";
660 // alternative version: "letterpaper"
661 handle_opt(options, known_paper_sizes, h_papersize);
662 delete_opt(options, known_paper_sizes);
664 char const * const * margin = known_paper_margins;
665 for (; *margin; ++margin) {
666 string value = process_keyval_opt(options, *margin);
667 if (!value.empty()) {
668 int k = margin - known_paper_margins;
669 string name = known_coded_paper_margins[k];
670 h_margins += '\\' + name + ' ' + value + '\n';
676 void Preamble::handle_package(Parser &p, string const & name,
677 string const & opts, bool in_lyx_preamble)
679 vector<string> options = split_options(opts);
680 add_package(name, options);
681 char const * const * where = 0;
683 if (is_known(name, known_xetex_packages)) {
685 h_use_non_tex_fonts = true;
686 registerAutomaticallyLoadedPackage("fontspec");
687 if (h_inputencoding == "auto")
688 p.setEncoding("UTF-8");
692 if (is_known(name, known_roman_fonts))
693 h_font_roman[0] = name;
695 if (name == "fourier") {
696 h_font_roman[0] = "utopia";
697 // when font uses real small capitals
698 if (opts == "expert")
702 if (name == "garamondx") {
703 h_font_roman[0] = "garamondx";
708 if (name == "libertine") {
709 h_font_roman[0] = "libertine";
710 // this automatically invokes biolinum
711 h_font_sans[0] = "biolinum";
714 else if (opts == "lining")
715 h_font_osf = "false";
718 if (name == "libertine-type1") {
719 h_font_roman[0] = "libertine";
720 // NOTE: contrary to libertine.sty, libertine-type1
721 // does not automatically invoke biolinum
722 if (opts == "lining")
723 h_font_osf = "false";
724 else if (opts == "osf")
728 if (name == "mathdesign") {
729 if (opts.find("charter") != string::npos)
730 h_font_roman[0] = "md-charter";
731 if (opts.find("garamond") != string::npos)
732 h_font_roman[0] = "md-garamond";
733 if (opts.find("utopia") != string::npos)
734 h_font_roman[0] = "md-utopia";
735 if (opts.find("expert") != string::npos) {
741 else if (name == "mathpazo")
742 h_font_roman[0] = "palatino";
744 else if (name == "mathptmx")
745 h_font_roman[0] = "times";
748 if (is_known(name, known_sans_fonts)) {
749 h_font_sans[0] = name;
750 if (options.size() >= 1) {
751 if (scale_as_percentage(opts, h_font_sf_scale[0]))
756 if (name == "biolinum-type1") {
757 h_font_sans[0] = "biolinum";
758 // biolinum can have several options, e.g. [osf,scaled=0.97]
759 string::size_type pos = opts.find("osf");
760 if (pos != string::npos)
765 if (is_known(name, known_typewriter_fonts)) {
766 // fourier can be set as roman font _only_
767 // fourier as typewriter is handled in handling of \ttdefault
768 if (name != "fourier") {
769 h_font_typewriter[0] = name;
770 if (options.size() >= 1) {
771 if (scale_as_percentage(opts, h_font_tt_scale[0]))
777 if (name == "libertineMono-type1") {
778 h_font_typewriter[0] = "libertine-mono";
781 // font uses old-style figure
786 if (is_known(name, known_math_fonts))
787 h_font_math[0] = name;
789 if (name == "newtxmath") {
791 h_font_math[0] = "newtxmath";
792 else if (opts == "garamondx")
793 h_font_math[0] = "garamondx-ntxm";
794 else if (opts == "libertine")
795 h_font_math[0] = "libertine-ntxm";
796 else if (opts == "minion")
797 h_font_math[0] = "minion-ntxm";
802 h_font_math[0] = "iwona-math";
804 if (name == "kurier")
806 h_font_math[0] = "kurier-math";
808 // after the detection and handling of special cases, we can remove the
809 // fonts, otherwise they would appear in the preamble, see bug #7856
810 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
811 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
814 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
815 name == "esint" || name == "mhchem" || name == "mathdots" ||
816 name == "mathtools" || name == "stackrel" ||
817 name == "stmaryrd" || name == "undertilde")
818 h_use_packages[name] = "2";
820 else if (name == "babel") {
821 h_language_package = "default";
822 // One might think we would have to do nothing if babel is loaded
823 // without any options to prevent pollution of the preamble with this
824 // babel call in every roundtrip.
825 // But the user could have defined babel-specific things afterwards. So
826 // we need to keep it in the preamble to prevent cases like bug #7861.
828 // check if more than one option was used - used later for inputenc
829 if (options.begin() != options.end() - 1)
830 one_language = false;
831 // babel takes the last language of the option of its \usepackage
832 // call as document language. If there is no such language option, the
833 // last language in the documentclass options is used.
834 handle_opt(options, known_languages, h_language);
835 // translate the babel name to a LyX name
836 h_language = babel2lyx(h_language);
837 if (h_language == "japanese") {
838 // For Japanese, the encoding isn't indicated in the source
839 // file, and there's really not much we can do. We could
840 // 1) offer a list of possible encodings to choose from, or
841 // 2) determine the encoding of the file by inspecting it.
842 // For the time being, we leave the encoding alone so that
843 // we don't get iconv errors when making a wrong guess, and
844 // we will output a note at the top of the document
845 // explaining what to do.
846 Encoding const * const enc = encodings.fromIconvName(
847 p.getEncoding(), Encoding::japanese, false);
849 h_inputencoding = enc->name();
850 is_nonCJKJapanese = true;
851 // in this case babel can be removed from the preamble
852 registerAutomaticallyLoadedPackage("babel");
854 // If babel is called with options, LyX puts them by default into the
855 // document class options. This works for most languages, except
856 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
857 // perhaps in future others.
858 // Therefore keep the babel call as it is as the user might have
860 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
862 delete_opt(options, known_languages);
864 h_preamble << "\\usepackage{babel}\n";
865 explicit_babel = true;
869 else if (name == "polyglossia") {
870 h_language_package = "default";
871 h_default_output_format = "pdf4";
872 h_use_non_tex_fonts = true;
874 registerAutomaticallyLoadedPackage("xunicode");
875 if (h_inputencoding == "auto")
876 p.setEncoding("UTF-8");
879 else if (name == "CJK") {
880 // set the encoding to "auto" because it might be set to "default" by the babel handling
881 // and this would not be correct for CJK
882 if (h_inputencoding == "default")
883 h_inputencoding = "auto";
884 registerAutomaticallyLoadedPackage("CJK");
887 else if (name == "CJKutf8") {
888 h_inputencoding = "utf8-cjk";
889 p.setEncoding("UTF-8");
890 registerAutomaticallyLoadedPackage("CJKutf8");
893 else if (name == "fontenc") {
894 h_fontencoding = getStringFromVector(options, ",");
895 /* We could do the following for better round trip support,
896 * but this makes the document less portable, so I skip it:
897 if (h_fontencoding == lyxrc.fontenc)
898 h_fontencoding = "global";
903 else if (name == "inputenc" || name == "luainputenc") {
904 // h_inputencoding is only set when there is not more than one
905 // inputenc option because otherwise h_inputencoding must be
906 // set to "auto" (the default encoding of the document language)
907 // Therefore check that exactly one option is passed to inputenc.
908 // It is also only set when there is not more than one babel
910 if (!options.empty()) {
911 string const encoding = options.back();
912 Encoding const * const enc = encodings.fromLaTeXName(
913 encoding, Encoding::inputenc, true);
915 cerr << "Unknown encoding " << encoding << ". Ignoring." << std::endl;
917 if (!enc->unsafe() && options.size() == 1 && one_language == true)
918 h_inputencoding = enc->name();
919 p.setEncoding(enc->iconvName());
925 else if (name == "srcltx") {
928 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
931 h_output_sync_macro = "\\usepackage{srcltx}";
934 else if (is_known(name, known_old_language_packages)) {
935 // known language packages from the times before babel
936 // if they are found and not also babel, they will be used as
937 // custom language package
938 h_language_package = "\\usepackage{" + name + "}";
941 else if (name == "lyxskak") {
942 // ignore this and its options
943 const char * const o[] = {"ps", "mover", 0};
944 delete_opt(options, o);
947 else if (is_known(name, known_lyx_packages) && options.empty()) {
948 if (name == "splitidx")
949 h_use_indices = "true";
950 if (name == "refstyle")
951 h_use_refstyle = true;
952 else if (name == "prettyref")
953 h_use_refstyle = false;
954 if (!in_lyx_preamble) {
955 h_preamble << package_beg_sep << name
956 << package_mid_sep << "\\usepackage{"
958 if (p.next_token().cat() == catNewline ||
959 (p.next_token().cat() == catSpace &&
960 p.next_next_token().cat() == catNewline))
962 h_preamble << package_end_sep;
966 else if (name == "geometry")
967 handle_geometry(options);
969 else if (name == "subfig")
970 ; // ignore this FIXME: Use the package separator mechanism instead
972 else if ((where = is_known(name, known_languages)))
973 h_language = known_coded_languages[where - known_languages];
975 else if (name == "natbib") {
976 h_biblio_style = "plainnat";
977 h_cite_engine = "natbib";
978 h_cite_engine_type = "authoryear";
979 vector<string>::iterator it =
980 find(options.begin(), options.end(), "authoryear");
981 if (it != options.end())
984 it = find(options.begin(), options.end(), "numbers");
985 if (it != options.end()) {
986 h_cite_engine_type = "numerical";
992 else if (name == "jurabib") {
993 h_biblio_style = "jurabib";
994 h_cite_engine = "jurabib";
995 h_cite_engine_type = "authoryear";
998 else if (name == "bibtopic")
999 h_use_bibtopic = "true";
1001 else if (name == "hyperref")
1002 handle_hyperref(options);
1004 else if (name == "algorithm2e") {
1005 // Load "algorithm2e" module
1006 addModule("algorithm2e");
1007 // Add the package options to the global document options
1008 if (!options.empty()) {
1009 if (h_options.empty())
1010 h_options = join(options, ",");
1012 h_options += ',' + join(options, ",");
1016 else if (!in_lyx_preamble) {
1017 if (options.empty())
1018 h_preamble << "\\usepackage{" << name << '}';
1020 h_preamble << "\\usepackage[" << opts << "]{"
1024 if (p.next_token().cat() == catNewline ||
1025 (p.next_token().cat() == catSpace &&
1026 p.next_next_token().cat() == catNewline))
1030 // We need to do something with the options...
1031 if (!options.empty())
1032 cerr << "Ignoring options '" << join(options, ",")
1033 << "' of package " << name << '.' << endl;
1035 // remove the whitespace
1040 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1043 Token t = p.get_token();
1044 if (t.cat() == catEscape &&
1045 is_known(t.cs(), known_if_commands))
1046 handle_if(p, in_lyx_preamble);
1048 if (!in_lyx_preamble)
1049 h_preamble << t.asInput();
1050 if (t.cat() == catEscape && t.cs() == "fi")
1057 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1059 // set the quote language
1060 // LyX only knows the following quotes languages:
1061 // english, swedish, german, polish, french and danish
1062 // (quotes for "japanese" and "chinese-traditional" are missing because
1063 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
1064 // conversion list taken from
1065 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1066 // (quotes for kazakh and interlingua are unknown)
1068 if (is_known(h_language, known_danish_quotes_languages))
1069 h_quotes_language = "danish";
1071 else if (is_known(h_language, known_french_quotes_languages))
1072 h_quotes_language = "french";
1074 else if (is_known(h_language, known_german_quotes_languages))
1075 h_quotes_language = "german";
1077 else if (is_known(h_language, known_polish_quotes_languages))
1078 h_quotes_language = "polish";
1080 else if (is_known(h_language, known_swedish_quotes_languages))
1081 h_quotes_language = "swedish";
1083 else if (is_known(h_language, known_english_quotes_languages))
1084 h_quotes_language = "english";
1086 if (contains(h_float_placement, "H"))
1087 registerAutomaticallyLoadedPackage("float");
1088 if (h_spacing != "single" && h_spacing != "default")
1089 registerAutomaticallyLoadedPackage("setspace");
1090 if (h_use_packages["amsmath"] == "2") {
1091 // amsbsy and amstext are already provided by amsmath
1092 registerAutomaticallyLoadedPackage("amsbsy");
1093 registerAutomaticallyLoadedPackage("amstext");
1096 // output the LyX file settings
1097 // Important: Keep the version formatting in sync with LyX and
1098 // lyx2lyx (bug 7951)
1099 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1100 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1101 << lyx_version_minor << '\n'
1102 << "\\lyxformat " << LYX_FORMAT << '\n'
1103 << "\\begin_document\n"
1104 << "\\begin_header\n"
1105 << "\\origin " << origin << "\n"
1106 << "\\textclass " << h_textclass << "\n";
1107 string const raw = subdoc ? empty_string() : h_preamble.str();
1109 os << "\\begin_preamble\n";
1110 for (string::size_type i = 0; i < raw.size(); ++i) {
1111 if (raw[i] == package_beg_sep) {
1112 // Here follows some package loading code that
1113 // must be skipped if the package is loaded
1115 string::size_type j = raw.find(package_mid_sep, i);
1116 if (j == string::npos)
1118 string::size_type k = raw.find(package_end_sep, j);
1119 if (k == string::npos)
1121 string const package = raw.substr(i + 1, j - i - 1);
1122 string const replacement = raw.substr(j + 1, k - j - 1);
1123 if (auto_packages.find(package) == auto_packages.end())
1129 os << "\n\\end_preamble\n";
1131 if (!h_options.empty())
1132 os << "\\options " << h_options << "\n";
1133 os << "\\use_default_options " << h_use_default_options << "\n";
1134 if (!used_modules.empty()) {
1135 os << "\\begin_modules\n";
1136 vector<string>::const_iterator const end = used_modules.end();
1137 vector<string>::const_iterator it = used_modules.begin();
1138 for (; it != end; ++it)
1140 os << "\\end_modules\n";
1142 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1143 << "\\language " << h_language << "\n"
1144 << "\\language_package " << h_language_package << "\n"
1145 << "\\inputencoding " << h_inputencoding << "\n"
1146 << "\\fontencoding " << h_fontencoding << "\n"
1147 << "\\font_roman \"" << h_font_roman[0]
1148 << "\" \"" << h_font_roman[1] << "\"\n"
1149 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1150 << "\\font_typewriter \"" << h_font_typewriter[0]
1151 << "\" \"" << h_font_typewriter[1] << "\"\n"
1152 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1153 << "\\font_default_family " << h_font_default_family << "\n"
1154 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1155 << "\\font_sc " << h_font_sc << "\n"
1156 << "\\font_osf " << h_font_osf << "\n"
1157 << "\\font_sf_scale " << h_font_sf_scale[0]
1158 << ' ' << h_font_sf_scale[1] << '\n'
1159 << "\\font_tt_scale " << h_font_tt_scale[0]
1160 << ' ' << h_font_tt_scale[1] << '\n';
1161 if (!h_font_cjk.empty())
1162 os << "\\font_cjk " << h_font_cjk << '\n';
1163 os << "\\graphics " << h_graphics << '\n'
1164 << "\\default_output_format " << h_default_output_format << "\n"
1165 << "\\output_sync " << h_output_sync << "\n";
1166 if (h_output_sync == "1")
1167 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1168 os << "\\bibtex_command " << h_bibtex_command << "\n"
1169 << "\\index_command " << h_index_command << "\n";
1170 if (!h_float_placement.empty())
1171 os << "\\float_placement " << h_float_placement << "\n";
1172 os << "\\paperfontsize " << h_paperfontsize << "\n"
1173 << "\\spacing " << h_spacing << "\n"
1174 << "\\use_hyperref " << h_use_hyperref << '\n';
1175 if (h_use_hyperref == "true") {
1176 if (!h_pdf_title.empty())
1177 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1178 if (!h_pdf_author.empty())
1179 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1180 if (!h_pdf_subject.empty())
1181 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1182 if (!h_pdf_keywords.empty())
1183 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1184 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1185 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1186 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1187 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1188 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1189 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1190 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1191 "\\pdf_backref " << h_pdf_backref << "\n"
1192 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1193 if (!h_pdf_pagemode.empty())
1194 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1195 if (!h_pdf_quoted_options.empty())
1196 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1198 os << "\\papersize " << h_papersize << "\n"
1199 << "\\use_geometry " << h_use_geometry << '\n';
1200 for (map<string, string>::const_iterator it = h_use_packages.begin();
1201 it != h_use_packages.end(); ++it)
1202 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1203 os << "\\cite_engine " << h_cite_engine << '\n'
1204 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1205 << "\\biblio_style " << h_biblio_style << "\n"
1206 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1207 << "\\use_indices " << h_use_indices << "\n"
1208 << "\\paperorientation " << h_paperorientation << '\n'
1209 << "\\suppress_date " << h_suppress_date << '\n'
1210 << "\\justification " << h_justification << '\n'
1211 << "\\use_refstyle " << h_use_refstyle << '\n';
1212 if (!h_fontcolor.empty())
1213 os << "\\fontcolor " << h_fontcolor << '\n';
1214 if (!h_notefontcolor.empty())
1215 os << "\\notefontcolor " << h_notefontcolor << '\n';
1216 if (!h_backgroundcolor.empty())
1217 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1218 if (!h_boxbgcolor.empty())
1219 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1220 if (index_number != 0)
1221 for (int i = 0; i < index_number; i++) {
1222 os << "\\index " << h_index[i] << '\n'
1223 << "\\shortcut " << h_shortcut[i] << '\n'
1224 << "\\color " << h_color << '\n'
1228 os << "\\index " << h_index[0] << '\n'
1229 << "\\shortcut " << h_shortcut[0] << '\n'
1230 << "\\color " << h_color << '\n'
1234 << "\\secnumdepth " << h_secnumdepth << "\n"
1235 << "\\tocdepth " << h_tocdepth << "\n"
1236 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1237 if (h_paragraph_separation == "skip")
1238 os << "\\defskip " << h_defskip << "\n";
1240 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1241 os << "\\quotes_language " << h_quotes_language << "\n"
1242 << "\\papercolumns " << h_papercolumns << "\n"
1243 << "\\papersides " << h_papersides << "\n"
1244 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1245 if (!h_listings_params.empty())
1246 os << "\\listings_params " << h_listings_params << "\n";
1247 os << "\\tracking_changes " << h_tracking_changes << "\n"
1248 << "\\output_changes " << h_output_changes << "\n"
1249 << "\\html_math_output " << h_html_math_output << "\n"
1250 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1251 << "\\html_be_strict " << h_html_be_strict << "\n"
1253 << "\\end_header\n\n"
1254 << "\\begin_body\n";
1259 void Preamble::parse(Parser & p, string const & forceclass,
1260 TeX2LyXDocClass & tc)
1262 // initialize fixed types
1263 special_columns_['D'] = 3;
1264 bool is_full_document = false;
1265 bool is_lyx_file = false;
1266 bool in_lyx_preamble = false;
1268 // determine whether this is a full document or a fragment for inclusion
1270 Token const & t = p.get_token();
1272 if (t.cat() == catEscape && t.cs() == "documentclass") {
1273 is_full_document = true;
1279 while (is_full_document && p.good()) {
1280 Token const & t = p.get_token();
1283 cerr << "t: " << t << "\n";
1289 if (!in_lyx_preamble &&
1290 (t.cat() == catLetter ||
1291 t.cat() == catSuper ||
1292 t.cat() == catSub ||
1293 t.cat() == catOther ||
1294 t.cat() == catMath ||
1295 t.cat() == catActive ||
1296 t.cat() == catBegin ||
1297 t.cat() == catEnd ||
1298 t.cat() == catAlign ||
1299 t.cat() == catParameter))
1300 h_preamble << t.cs();
1302 else if (!in_lyx_preamble &&
1303 (t.cat() == catSpace || t.cat() == catNewline))
1304 h_preamble << t.asInput();
1306 else if (t.cat() == catComment) {
1307 static regex const islyxfile("%% LyX .* created this file");
1308 static regex const usercommands("User specified LaTeX commands");
1310 string const comment = t.asInput();
1312 // magically switch encoding default if it looks like XeLaTeX
1313 static string const magicXeLaTeX =
1314 "% This document must be compiled with XeLaTeX ";
1315 if (comment.size() > magicXeLaTeX.size()
1316 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1317 && h_inputencoding == "auto") {
1318 cerr << "XeLaTeX comment found, switching to UTF8\n";
1319 h_inputencoding = "utf8";
1322 if (regex_search(comment, sub, islyxfile)) {
1324 in_lyx_preamble = true;
1325 } else if (is_lyx_file
1326 && regex_search(comment, sub, usercommands))
1327 in_lyx_preamble = false;
1328 else if (!in_lyx_preamble)
1329 h_preamble << t.asInput();
1332 else if (t.cs() == "pagestyle")
1333 h_paperpagestyle = p.verbatim_item();
1335 else if (t.cs() == "setdefaultlanguage") {
1337 // We don't yet care about non-language variant options
1338 // because LyX doesn't support this yet, see bug #8214
1340 string langopts = p.getOpt();
1341 // check if the option contains a variant, if yes, extract it
1342 string::size_type pos_var = langopts.find("variant");
1343 string::size_type i = langopts.find(',', pos_var);
1344 string::size_type k = langopts.find('=', pos_var);
1345 if (pos_var != string::npos){
1347 if (i == string::npos)
1348 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1350 variant = langopts.substr(k + 1, i - k - 1);
1351 h_language = variant;
1355 h_language = p.verbatim_item();
1356 //finally translate the poyglossia name to a LyX name
1357 h_language = polyglossia2lyx(h_language);
1360 else if (t.cs() == "setotherlanguage") {
1361 // We don't yet care about the option because LyX doesn't
1362 // support this yet, see bug #8214
1363 p.hasOpt() ? p.getOpt() : string();
1367 else if (t.cs() == "setmainfont") {
1368 // we don't care about the option
1369 p.hasOpt() ? p.getOpt() : string();
1370 h_font_roman[1] = p.getArg('{', '}');
1373 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1374 // LyX currently only supports the scale option
1377 string fontopts = p.getArg('[', ']');
1378 // check if the option contains a scaling, if yes, extract it
1379 string::size_type pos = fontopts.find("Scale");
1380 if (pos != string::npos) {
1381 string::size_type i = fontopts.find(',', pos);
1382 if (i == string::npos)
1383 scale_as_percentage(fontopts.substr(pos + 1), scale);
1385 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1388 if (t.cs() == "setsansfont") {
1390 h_font_sf_scale[1] = scale;
1391 h_font_sans[1] = p.getArg('{', '}');
1394 h_font_tt_scale[1] = scale;
1395 h_font_typewriter[1] = p.getArg('{', '}');
1399 else if (t.cs() == "date") {
1400 string argument = p.getArg('{', '}');
1401 if (argument.empty())
1402 h_suppress_date = "true";
1404 h_preamble << t.asInput() << '{' << argument << '}';
1407 else if (t.cs() == "color") {
1408 string const space =
1409 (p.hasOpt() ? p.getOpt() : string());
1410 string argument = p.getArg('{', '}');
1411 // check the case that a standard color is used
1412 if (space.empty() && is_known(argument, known_basic_colors)) {
1413 h_fontcolor = rgbcolor2code(argument);
1414 preamble.registerAutomaticallyLoadedPackage("color");
1415 } else if (space.empty() && argument == "document_fontcolor")
1416 preamble.registerAutomaticallyLoadedPackage("color");
1417 // check the case that LyX's document_fontcolor is defined
1418 // but not used for \color
1420 h_preamble << t.asInput();
1422 h_preamble << space;
1423 h_preamble << '{' << argument << '}';
1424 // the color might already be set because \definecolor
1425 // is parsed before this
1430 else if (t.cs() == "pagecolor") {
1431 string argument = p.getArg('{', '}');
1432 // check the case that a standard color is used
1433 if (is_known(argument, known_basic_colors)) {
1434 h_backgroundcolor = rgbcolor2code(argument);
1435 } else if (argument == "page_backgroundcolor")
1436 preamble.registerAutomaticallyLoadedPackage("color");
1437 // check the case that LyX's page_backgroundcolor is defined
1438 // but not used for \pagecolor
1440 h_preamble << t.asInput() << '{' << argument << '}';
1441 // the color might already be set because \definecolor
1442 // is parsed before this
1443 h_backgroundcolor = "";
1447 else if (t.cs() == "makeatletter") {
1448 // LyX takes care of this
1449 p.setCatcode('@', catLetter);
1452 else if (t.cs() == "makeatother") {
1453 // LyX takes care of this
1454 p.setCatcode('@', catOther);
1457 else if (t.cs() == "makeindex") {
1458 // LyX will re-add this if a print index command is found
1462 else if (t.cs() == "newindex") {
1463 string const indexname = p.getArg('[', ']');
1464 string const shortcut = p.verbatim_item();
1465 if (!indexname.empty())
1466 h_index[index_number] = indexname;
1468 h_index[index_number] = shortcut;
1469 h_shortcut[index_number] = shortcut;
1474 else if (t.cs() == "RS@ifundefined") {
1475 string const name = p.verbatim_item();
1476 string const body1 = p.verbatim_item();
1477 string const body2 = p.verbatim_item();
1478 // only non-lyxspecific stuff
1479 if (in_lyx_preamble &&
1480 (name == "subsecref" || name == "thmref" || name == "lemref"))
1484 ss << '\\' << t.cs();
1485 ss << '{' << name << '}'
1486 << '{' << body1 << '}'
1487 << '{' << body2 << '}';
1488 h_preamble << ss.str();
1492 else if (t.cs() == "AtBeginDocument") {
1493 string const name = p.verbatim_item();
1494 // only non-lyxspecific stuff
1495 if (in_lyx_preamble &&
1496 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1497 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1498 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1499 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1500 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1501 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1502 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1503 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1504 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1505 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1506 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1507 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1508 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1509 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1510 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1514 ss << '\\' << t.cs();
1515 ss << '{' << name << '}';
1516 h_preamble << ss.str();
1520 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1521 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1522 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1523 || t.cs() == "DeclareRobustCommand"
1524 || t.cs() == "DeclareRobustCommandx"
1525 || t.cs() == "ProvideTextCommandDefault"
1526 || t.cs() == "DeclareMathAccent") {
1528 if (p.next_token().character() == '*') {
1532 string const name = p.verbatim_item();
1533 string const opt1 = p.getFullOpt();
1534 string const opt2 = p.getFullOpt();
1535 string const body = p.verbatim_item();
1536 // store the in_lyx_preamble setting
1537 bool const was_in_lyx_preamble = in_lyx_preamble;
1539 if (name == "\\rmdefault")
1540 if (is_known(body, known_roman_fonts)) {
1541 h_font_roman[0] = body;
1543 in_lyx_preamble = true;
1545 if (name == "\\sfdefault")
1546 if (is_known(body, known_sans_fonts)) {
1547 h_font_sans[0] = body;
1549 in_lyx_preamble = true;
1551 if (name == "\\ttdefault")
1552 if (is_known(body, known_typewriter_fonts)) {
1553 h_font_typewriter[0] = body;
1555 in_lyx_preamble = true;
1557 if (name == "\\familydefault") {
1558 string family = body;
1559 // remove leading "\"
1560 h_font_default_family = family.erase(0,1);
1562 in_lyx_preamble = true;
1565 // remove LyX-specific definitions that are re-added by LyX
1567 // \lyxline is an ancient command that is converted by tex2lyx into
1568 // a \rule therefore remove its preamble code
1569 if (name == "\\lyxdot" || name == "\\lyxarrow"
1570 || name == "\\lyxline" || name == "\\LyX") {
1572 in_lyx_preamble = true;
1575 // Add the command to the known commands
1576 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1578 // only non-lyxspecific stuff
1579 if (!in_lyx_preamble) {
1581 ss << '\\' << t.cs();
1584 ss << '{' << name << '}' << opt1 << opt2
1585 << '{' << body << "}";
1586 h_preamble << ss.str();
1588 ostream & out = in_preamble ? h_preamble : os;
1589 out << "\\" << t.cs() << "{" << name << "}"
1590 << opts << "{" << body << "}";
1593 // restore the in_lyx_preamble setting
1594 in_lyx_preamble = was_in_lyx_preamble;
1597 else if (t.cs() == "documentclass") {
1598 vector<string>::iterator it;
1599 vector<string> opts = split_options(p.getArg('[', ']'));
1600 handle_opt(opts, known_fontsizes, h_paperfontsize);
1601 delete_opt(opts, known_fontsizes);
1602 // delete "pt" at the end
1603 string::size_type i = h_paperfontsize.find("pt");
1604 if (i != string::npos)
1605 h_paperfontsize.erase(i);
1606 // The documentclass options are always parsed before the options
1607 // of the babel call so that a language cannot overwrite the babel
1609 handle_opt(opts, known_languages, h_language);
1610 delete_opt(opts, known_languages);
1612 // paper orientation
1613 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1614 h_paperorientation = "landscape";
1618 if ((it = find(opts.begin(), opts.end(), "oneside"))
1623 if ((it = find(opts.begin(), opts.end(), "twoside"))
1629 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1631 h_papercolumns = "1";
1634 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1636 h_papercolumns = "2";
1640 // some size options are known to any document classes, other sizes
1641 // are handled by the \geometry command of the geometry package
1642 handle_opt(opts, known_class_paper_sizes, h_papersize);
1643 delete_opt(opts, known_class_paper_sizes);
1644 // the remaining options
1645 h_options = join(opts, ",");
1646 // FIXME This does not work for classes that have a
1647 // different name in LyX than in LaTeX
1648 h_textclass = p.getArg('{', '}');
1652 else if (t.cs() == "usepackage") {
1653 string const options = p.getArg('[', ']');
1654 string const name = p.getArg('{', '}');
1655 vector<string> vecnames;
1656 split(name, vecnames, ',');
1657 vector<string>::const_iterator it = vecnames.begin();
1658 vector<string>::const_iterator end = vecnames.end();
1659 for (; it != end; ++it)
1660 handle_package(p, trimSpaceAndEol(*it), options,
1664 else if (t.cs() == "inputencoding") {
1665 string const encoding = p.getArg('{','}');
1666 Encoding const * const enc = encodings.fromLaTeXName(
1667 encoding, Encoding::inputenc, true);
1669 cerr << "Unknown encoding " << encoding << ". Ignoring." << std::endl;
1672 h_inputencoding = enc->name();
1673 p.setEncoding(enc->iconvName());
1677 else if (t.cs() == "newenvironment") {
1678 string const name = p.getArg('{', '}');
1679 string const opt1 = p.getFullOpt();
1680 string const opt2 = p.getFullOpt();
1681 string const beg = p.verbatim_item();
1682 string const end = p.verbatim_item();
1683 if (!in_lyx_preamble) {
1684 h_preamble << "\\newenvironment{" << name
1685 << '}' << opt1 << opt2 << '{'
1686 << beg << "}{" << end << '}';
1688 add_known_environment(name, opt1, !opt2.empty(),
1689 from_utf8(beg), from_utf8(end));
1693 else if (t.cs() == "newtheorem") {
1694 string const name = p.getArg('{', '}');
1695 string const opt1 = p.getFullOpt();
1696 string const opt2 = p.getFullOpt();
1697 string const body = p.verbatim_item();
1698 string const opt3 = p.getFullOpt();
1700 string const complete = "\\newtheorem{" + name + '}' +
1701 opt1 + opt2 + '{' + body + '}' + opt3;
1703 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1705 if (!in_lyx_preamble)
1706 h_preamble << complete;
1709 else if (t.cs() == "def") {
1710 string name = p.get_token().cs();
1711 // In fact, name may be more than the name:
1712 // In the test case of bug 8116
1713 // name == "csname SF@gobble@opt \endcsname".
1714 // Therefore, we need to use asInput() instead of cs().
1715 while (p.next_token().cat() != catBegin)
1716 name += p.get_token().asInput();
1717 if (!in_lyx_preamble)
1718 h_preamble << "\\def\\" << name << '{'
1719 << p.verbatim_item() << "}";
1722 else if (t.cs() == "newcolumntype") {
1723 string const name = p.getArg('{', '}');
1724 trimSpaceAndEol(name);
1726 string opts = p.getOpt();
1727 if (!opts.empty()) {
1728 istringstream is(string(opts, 1));
1731 special_columns_[name[0]] = nargs;
1732 h_preamble << "\\newcolumntype{" << name << "}";
1734 h_preamble << "[" << nargs << "]";
1735 h_preamble << "{" << p.verbatim_item() << "}";
1738 else if (t.cs() == "setcounter") {
1739 string const name = p.getArg('{', '}');
1740 string const content = p.getArg('{', '}');
1741 if (name == "secnumdepth")
1742 h_secnumdepth = content;
1743 else if (name == "tocdepth")
1744 h_tocdepth = content;
1746 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1749 else if (t.cs() == "setlength") {
1750 string const name = p.verbatim_item();
1751 string const content = p.verbatim_item();
1752 // the paragraphs are only not indented when \parindent is set to zero
1753 if (name == "\\parindent" && content != "") {
1754 if (content[0] == '0')
1755 h_paragraph_separation = "skip";
1757 h_paragraph_indentation = translate_len(content);
1758 } else if (name == "\\parskip") {
1759 if (content == "\\smallskipamount")
1760 h_defskip = "smallskip";
1761 else if (content == "\\medskipamount")
1762 h_defskip = "medskip";
1763 else if (content == "\\bigskipamount")
1764 h_defskip = "bigskip";
1766 h_defskip = translate_len(content);
1768 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1771 else if (t.cs() == "onehalfspacing")
1772 h_spacing = "onehalf";
1774 else if (t.cs() == "doublespacing")
1775 h_spacing = "double";
1777 else if (t.cs() == "setstretch")
1778 h_spacing = "other " + p.verbatim_item();
1780 else if (t.cs() == "synctex") {
1781 // the scheme is \synctex=value
1782 // where value can only be "1" or "-1"
1783 h_output_sync = "1";
1784 // there can be any character behind the value (e.g. a linebreak or a '\'
1785 // therefore we extract it char by char
1787 string value = p.get_token().asInput();
1789 value += p.get_token().asInput();
1790 h_output_sync_macro = "\\synctex=" + value;
1793 else if (t.cs() == "begin") {
1794 string const name = p.getArg('{', '}');
1795 if (name == "document")
1797 h_preamble << "\\begin{" << name << "}";
1800 else if (t.cs() == "geometry") {
1801 vector<string> opts = split_options(p.getArg('{', '}'));
1802 handle_geometry(opts);
1805 else if (t.cs() == "definecolor") {
1806 string const color = p.getArg('{', '}');
1807 string const space = p.getArg('{', '}');
1808 string const value = p.getArg('{', '}');
1809 if (color == "document_fontcolor" && space == "rgb") {
1810 RGBColor c(RGBColorFromLaTeX(value));
1811 h_fontcolor = X11hexname(c);
1812 } else if (color == "note_fontcolor" && space == "rgb") {
1813 RGBColor c(RGBColorFromLaTeX(value));
1814 h_notefontcolor = X11hexname(c);
1815 } else if (color == "page_backgroundcolor" && space == "rgb") {
1816 RGBColor c(RGBColorFromLaTeX(value));
1817 h_backgroundcolor = X11hexname(c);
1818 } else if (color == "shadecolor" && space == "rgb") {
1819 RGBColor c(RGBColorFromLaTeX(value));
1820 h_boxbgcolor = X11hexname(c);
1822 h_preamble << "\\definecolor{" << color
1823 << "}{" << space << "}{" << value
1828 else if (t.cs() == "bibliographystyle")
1829 h_biblio_style = p.verbatim_item();
1831 else if (t.cs() == "jurabibsetup") {
1832 // FIXME p.getArg('{', '}') is most probably wrong (it
1833 // does not handle nested braces).
1834 // Use p.verbatim_item() instead.
1835 vector<string> jurabibsetup =
1836 split_options(p.getArg('{', '}'));
1837 // add jurabibsetup to the jurabib package options
1838 add_package("jurabib", jurabibsetup);
1839 if (!jurabibsetup.empty()) {
1840 h_preamble << "\\jurabibsetup{"
1841 << join(jurabibsetup, ",") << '}';
1845 else if (t.cs() == "hypersetup") {
1846 vector<string> hypersetup =
1847 split_options(p.verbatim_item());
1848 // add hypersetup to the hyperref package options
1849 handle_hyperref(hypersetup);
1850 if (!hypersetup.empty()) {
1851 h_preamble << "\\hypersetup{"
1852 << join(hypersetup, ",") << '}';
1856 else if (is_known(t.cs(), known_if_3arg_commands)) {
1857 // prevent misparsing of \usepackage if it is used
1858 // as an argument (see e.g. our own output of
1859 // \@ifundefined above)
1860 string const arg1 = p.verbatim_item();
1861 string const arg2 = p.verbatim_item();
1862 string const arg3 = p.verbatim_item();
1863 // test case \@ifundefined{date}{}{\date{}}
1864 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1865 arg2.empty() && arg3 == "\\date{}") {
1866 h_suppress_date = "true";
1867 // older tex2lyx versions did output
1868 // \@ifundefined{definecolor}{\usepackage{color}}{}
1869 } else if (t.cs() == "@ifundefined" &&
1870 arg1 == "definecolor" &&
1871 arg2 == "\\usepackage{color}" &&
1873 if (!in_lyx_preamble)
1874 h_preamble << package_beg_sep
1877 << "\\@ifundefined{definecolor}{color}{}"
1880 //\@ifundefined{showcaptionsetup}{}{%
1881 // \PassOptionsToPackage{caption=false}{subfig}}
1882 // that LyX uses for subfloats
1883 } else if (t.cs() == "@ifundefined" &&
1884 arg1 == "showcaptionsetup" && arg2.empty()
1885 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1887 } else if (!in_lyx_preamble) {
1888 h_preamble << t.asInput()
1889 << '{' << arg1 << '}'
1890 << '{' << arg2 << '}'
1891 << '{' << arg3 << '}';
1895 else if (is_known(t.cs(), known_if_commands)) {
1896 // must not parse anything in conditional code, since
1897 // LyX would output the parsed contents unconditionally
1898 if (!in_lyx_preamble)
1899 h_preamble << t.asInput();
1900 handle_if(p, in_lyx_preamble);
1903 else if (!t.cs().empty() && !in_lyx_preamble)
1904 h_preamble << '\\' << t.cs();
1907 // remove the whitespace
1910 // Force textclass if the user wanted it
1911 if (!forceclass.empty())
1912 h_textclass = forceclass;
1913 tc.setName(h_textclass);
1915 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1918 if (h_papersides.empty()) {
1921 h_papersides = ss.str();
1924 // If the CJK package is used we cannot set the document language from
1925 // the babel options. Instead, we guess which language is used most
1926 // and set this one.
1927 default_language = h_language;
1928 if (is_full_document &&
1929 (auto_packages.find("CJK") != auto_packages.end() ||
1930 auto_packages.find("CJKutf8") != auto_packages.end())) {
1932 h_language = guessLanguage(p, default_language);
1934 if (explicit_babel && h_language != default_language) {
1935 // We set the document language to a CJK language,
1936 // but babel is explicitly called in the user preamble
1937 // without options. LyX will not add the default
1938 // language to the document options if it is either
1939 // english, or no text is set as default language.
1940 // Therefore we need to add a language option explicitly.
1941 // FIXME: It would be better to remove all babel calls
1942 // from the user preamble, but this is difficult
1943 // without re-introducing bug 7861.
1944 if (h_options.empty())
1945 h_options = lyx2babel(default_language);
1947 h_options += ',' + lyx2babel(default_language);
1953 string babel2lyx(string const & language)
1955 char const * const * where = is_known(language, known_languages);
1957 return known_coded_languages[where - known_languages];
1962 string lyx2babel(string const & language)
1964 char const * const * where = is_known(language, known_coded_languages);
1966 return known_languages[where - known_coded_languages];
1971 string Preamble::polyglossia2lyx(string const & language)
1973 char const * const * where = is_known(language, polyglossia_languages);
1975 return coded_polyglossia_languages[where - polyglossia_languages];
1980 string rgbcolor2code(string const & name)
1982 char const * const * where = is_known(name, known_basic_colors);
1984 // "red", "green" etc
1985 return known_basic_color_codes[where - known_basic_colors];
1987 // "255,0,0", "0,255,0" etc
1988 RGBColor c(RGBColorFromLaTeX(name));
1989 return X11hexname(c);