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[] = {"blue", "black", "cyan", "green",
163 "magenta", "red", "white", "yellow", 0};
165 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff",
166 "#00ff00", "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
168 /// conditional commands with three arguments like \@ifundefined{}{}{}
169 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
172 /// packages that work only in xetex
173 /// polyglossia is handled separately
174 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
175 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
176 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
178 /// packages that are automatically skipped if loaded by LyX
179 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
180 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
181 "float", "fontspec", "graphicx", "hhline", "ifthen", "longtable", "makeidx",
182 "multirow", "nomencl", "pdfpages", "prettyref", "refstyle", "rotating",
183 "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa", "tipx",
184 "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xunicode", 0};
186 // used for the handling of \newindex
187 int index_number = 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", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
301 "nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil",
302 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
303 "irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish",
304 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
305 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "tibetan", "urdu",
306 "brazil", "brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton",
307 "french", "lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar",
308 "slovak", "welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek",
309 "marathi", "spanish", "austrian",
310 "american", "ancient", "australian", "british", "monotonic", "newzealand",
314 * the same as polyglossia_languages with .lyx names
315 * please keep this in sync with polyglossia_languages line by line!
317 const char * const Preamble::coded_polyglossia_languages[] = {
318 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
319 "nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil",
320 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
321 "irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish",
322 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
323 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "tibetan", "urdu",
324 "brazilian", "brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton",
325 "french", "lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar",
326 "slovak", "welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek",
327 "marathi", "spanish", "naustrian",
328 "american", "ancientgreek", "australian", "british", "greek", "newzealand",
329 "polutonikogreek", 0};
332 bool Preamble::usePolyglossia() const
334 return h_use_non_tex_fonts && h_language_package == "default";
338 bool Preamble::indentParagraphs() const
340 return h_paragraph_separation == "indent";
344 bool Preamble::isPackageUsed(string const & package) const
346 return used_packages.find(package) != used_packages.end();
350 vector<string> Preamble::getPackageOptions(string const & package) const
352 map<string, vector<string> >::const_iterator it = used_packages.find(package);
353 if (it != used_packages.end())
355 return vector<string>();
359 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
361 auto_packages.insert(package);
365 void Preamble::addModule(string const & module)
367 used_modules.push_back(module);
371 void Preamble::suppressDate(bool suppress)
374 h_suppress_date = "true";
376 h_suppress_date = "false";
380 void Preamble::registerAuthor(std::string const & name)
382 Author author(from_utf8(name), empty_docstring());
383 author.setUsed(true);
384 authors_.record(author);
385 h_tracking_changes = "true";
386 h_output_changes = "true";
390 Author const & Preamble::getAuthor(std::string const & name) const
392 Author author(from_utf8(name), empty_docstring());
393 for (AuthorList::Authors::const_iterator it = authors_.begin();
394 it != authors_.end(); ++it)
397 static Author const dummy;
402 int Preamble::getSpecialTableColumnArguments(char c) const
404 map<char, int>::const_iterator it = special_columns_.find(c);
405 if (it == special_columns_.end())
411 void Preamble::add_package(string const & name, vector<string> & options)
413 // every package inherits the global options
414 if (used_packages.find(name) == used_packages.end())
415 used_packages[name] = split_options(h_options);
417 vector<string> & v = used_packages[name];
418 v.insert(v.end(), options.begin(), options.end());
419 if (name == "jurabib") {
420 // Don't output the order argument (see the cite command
421 // handling code in text.cpp).
422 vector<string>::iterator end =
423 remove(options.begin(), options.end(), "natbiborder");
424 end = remove(options.begin(), end, "jurabiborder");
425 options.erase(end, options.end());
432 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
433 bool scale_as_percentage(string const & scale, string & percentage)
435 string::size_type pos = scale.find('=');
436 if (pos != string::npos) {
437 string value = scale.substr(pos + 1);
438 if (isStrDbl(value)) {
439 percentage = convert<string>(
440 static_cast<int>(100 * convert<double>(value)));
448 string remove_braces(string const & value)
452 if (value[0] == '{' && value[value.length()-1] == '}')
453 return value.substr(1, value.length()-2);
457 } // anonymous namespace
460 Preamble::Preamble() : one_language(true), explicit_babel(false),
461 title_layout_found(false), h_font_cjk_set(false)
465 h_biblio_style = "plain";
466 h_bibtex_command = "default";
467 h_cite_engine = "basic";
468 h_cite_engine_type = "default";
470 h_defskip = "medskip";
473 h_fontencoding = "default";
474 h_font_roman = "default";
475 h_font_sans = "default";
476 h_font_typewriter = "default";
477 h_font_math = "auto";
478 h_font_default_family = "default";
479 h_use_non_tex_fonts = false;
481 h_font_osf = "false";
482 h_font_sf_scale = "100";
483 h_font_tt_scale = "100";
485 h_graphics = "default";
486 h_default_output_format = "default";
487 h_html_be_strict = "false";
488 h_html_css_as_file = "0";
489 h_html_math_output = "0";
490 h_index[0] = "Index";
491 h_index_command = "default";
492 h_inputencoding = "auto";
493 h_justification = "true";
494 h_language = "english";
495 h_language_package = "none";
497 h_maintain_unincluded_children = "false";
501 h_output_changes = "false";
503 //h_output_sync_macro
504 h_papercolumns = "1";
505 h_paperfontsize = "default";
506 h_paperorientation = "portrait";
507 h_paperpagestyle = "default";
509 h_papersize = "default";
510 h_paragraph_indentation = "default";
511 h_paragraph_separation = "indent";
516 h_pdf_bookmarks = "0";
517 h_pdf_bookmarksnumbered = "0";
518 h_pdf_bookmarksopen = "0";
519 h_pdf_bookmarksopenlevel = "1";
520 h_pdf_breaklinks = "0";
521 h_pdf_pdfborder = "0";
522 h_pdf_colorlinks = "0";
523 h_pdf_backref = "section";
524 h_pdf_pdfusetitle = "0";
526 //h_pdf_quoted_options;
527 h_quotes_language = "english";
529 h_shortcut[0] = "idx";
530 h_spacing = "single";
531 h_suppress_date = "false";
532 h_textclass = "article";
534 h_tracking_changes = "false";
535 h_use_bibtopic = "false";
536 h_use_indices = "false";
537 h_use_geometry = "false";
538 h_use_default_options = "false";
539 h_use_hyperref = "false";
540 h_use_refstyle = false;
541 h_use_packages["amsmath"] = "1";
542 h_use_packages["amssymb"] = "0";
543 h_use_packages["cancel"] = "0";
544 h_use_packages["esint"] = "1";
545 h_use_packages["mhchem"] = "0";
546 h_use_packages["mathdots"] = "0";
547 h_use_packages["mathtools"] = "0";
548 h_use_packages["stackrel"] = "0";
549 h_use_packages["stmaryrd"] = "0";
550 h_use_packages["undertilde"] = "0";
554 void Preamble::handle_hyperref(vector<string> & options)
556 // FIXME swallow inputencoding changes that might surround the
557 // hyperref setup if it was written by LyX
558 h_use_hyperref = "true";
559 // swallow "unicode=true", since LyX does always write that
560 vector<string>::iterator it =
561 find(options.begin(), options.end(), "unicode=true");
562 if (it != options.end())
564 it = find(options.begin(), options.end(), "pdfusetitle");
565 if (it != options.end()) {
566 h_pdf_pdfusetitle = "1";
569 string bookmarks = process_keyval_opt(options, "bookmarks");
570 if (bookmarks == "true")
571 h_pdf_bookmarks = "1";
572 else if (bookmarks == "false")
573 h_pdf_bookmarks = "0";
574 if (h_pdf_bookmarks == "1") {
575 string bookmarksnumbered =
576 process_keyval_opt(options, "bookmarksnumbered");
577 if (bookmarksnumbered == "true")
578 h_pdf_bookmarksnumbered = "1";
579 else if (bookmarksnumbered == "false")
580 h_pdf_bookmarksnumbered = "0";
581 string bookmarksopen =
582 process_keyval_opt(options, "bookmarksopen");
583 if (bookmarksopen == "true")
584 h_pdf_bookmarksopen = "1";
585 else if (bookmarksopen == "false")
586 h_pdf_bookmarksopen = "0";
587 if (h_pdf_bookmarksopen == "1") {
588 string bookmarksopenlevel =
589 process_keyval_opt(options, "bookmarksopenlevel");
590 if (!bookmarksopenlevel.empty())
591 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
594 string breaklinks = process_keyval_opt(options, "breaklinks");
595 if (breaklinks == "true")
596 h_pdf_breaklinks = "1";
597 else if (breaklinks == "false")
598 h_pdf_breaklinks = "0";
599 string pdfborder = process_keyval_opt(options, "pdfborder");
600 if (pdfborder == "{0 0 0}")
601 h_pdf_pdfborder = "1";
602 else if (pdfborder == "{0 0 1}")
603 h_pdf_pdfborder = "0";
604 string backref = process_keyval_opt(options, "backref");
605 if (!backref.empty())
606 h_pdf_backref = backref;
607 string colorlinks = process_keyval_opt(options, "colorlinks");
608 if (colorlinks == "true")
609 h_pdf_colorlinks = "1";
610 else if (colorlinks == "false")
611 h_pdf_colorlinks = "0";
612 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
613 if (!pdfpagemode.empty())
614 h_pdf_pagemode = pdfpagemode;
615 string pdftitle = process_keyval_opt(options, "pdftitle");
616 if (!pdftitle.empty()) {
617 h_pdf_title = remove_braces(pdftitle);
619 string pdfauthor = process_keyval_opt(options, "pdfauthor");
620 if (!pdfauthor.empty()) {
621 h_pdf_author = remove_braces(pdfauthor);
623 string pdfsubject = process_keyval_opt(options, "pdfsubject");
624 if (!pdfsubject.empty())
625 h_pdf_subject = remove_braces(pdfsubject);
626 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
627 if (!pdfkeywords.empty())
628 h_pdf_keywords = remove_braces(pdfkeywords);
629 if (!options.empty()) {
630 if (!h_pdf_quoted_options.empty())
631 h_pdf_quoted_options += ',';
632 h_pdf_quoted_options += join(options, ",");
638 void Preamble::handle_geometry(vector<string> & options)
640 h_use_geometry = "true";
641 vector<string>::iterator it;
643 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
644 h_paperorientation = "landscape";
648 // keyval version: "paper=letter"
649 string paper = process_keyval_opt(options, "paper");
651 h_papersize = paper + "paper";
652 // alternative version: "letterpaper"
653 handle_opt(options, known_paper_sizes, h_papersize);
654 delete_opt(options, known_paper_sizes);
656 char const * const * margin = known_paper_margins;
657 for (; *margin; ++margin) {
658 string value = process_keyval_opt(options, *margin);
659 if (!value.empty()) {
660 int k = margin - known_paper_margins;
661 string name = known_coded_paper_margins[k];
662 h_margins += '\\' + name + ' ' + value + '\n';
668 void Preamble::handle_package(Parser &p, string const & name,
669 string const & opts, bool in_lyx_preamble)
671 vector<string> options = split_options(opts);
672 add_package(name, options);
673 char const * const * where = 0;
675 if (is_known(name, known_xetex_packages)) {
677 h_use_non_tex_fonts = true;
678 registerAutomaticallyLoadedPackage("fontspec");
679 if (h_inputencoding == "auto")
680 p.setEncoding("UTF-8");
684 if (is_known(name, known_roman_fonts))
687 if (name == "fourier") {
688 h_font_roman = "utopia";
689 // when font uses real small capitals
690 if (opts == "expert")
694 if (name == "garamondx") {
695 h_font_roman = "garamondx";
700 if (name == "libertine") {
701 h_font_roman = "libertine";
702 // this automatically invokes biolinum
703 h_font_sans = "biolinum";
706 else if (opts == "lining")
707 h_font_osf = "false";
710 if (name == "libertine-type1") {
711 h_font_roman = "libertine";
712 // NOTE: contrary to libertine.sty, libertine-type1
713 // does not automatically invoke biolinum
714 if (opts == "lining")
715 h_font_osf = "false";
716 else if (opts == "osf")
720 if (name == "mathdesign") {
721 if (opts.find("charter") != string::npos)
722 h_font_roman = "md-charter";
723 if (opts.find("garamond") != string::npos)
724 h_font_roman = "md-garamond";
725 if (opts.find("utopia") != string::npos)
726 h_font_roman = "md-utopia";
727 if (opts.find("expert") != string::npos) {
733 else if (name == "mathpazo")
734 h_font_roman = "palatino";
736 else if (name == "mathptmx")
737 h_font_roman = "times";
740 if (is_known(name, known_sans_fonts)) {
742 if (options.size() >= 1) {
743 if (scale_as_percentage(opts, h_font_sf_scale))
748 if (name == "biolinum-type1") {
749 h_font_sans = "biolinum";
750 // biolinum can have several options, e.g. [osf,scaled=0.97]
751 string::size_type pos = opts.find("osf");
752 if (pos != string::npos)
757 if (is_known(name, known_typewriter_fonts)) {
758 // fourier can be set as roman font _only_
759 // fourier as typewriter is handled in handling of \ttdefault
760 if (name != "fourier") {
761 h_font_typewriter = name;
762 if (options.size() >= 1) {
763 if (scale_as_percentage(opts, h_font_tt_scale))
769 if (name == "libertineMono-type1") {
770 h_font_typewriter = "libertine-mono";
773 // font uses old-style figure
778 if (is_known(name, known_math_fonts))
781 if (name == "newtxmath") {
783 h_font_math = "newtxmath";
784 else if (opts == "garamondx")
785 h_font_math = "garamondx-ntxm";
786 else if (opts == "libertine")
787 h_font_math = "libertine-ntxm";
788 else if (opts == "minion")
789 h_font_math = "minion-ntxm";
794 h_font_math = "iwona-math";
796 if (name == "kurier")
798 h_font_math = "kurier-math";
800 // after the detection and handling of special cases, we can remove the
801 // fonts, otherwise they would appear in the preamble, see bug #7856
802 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
803 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
806 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
807 name == "esint" || name == "mhchem" || name == "mathdots" ||
808 name == "mathtools" || name == "stackrel" ||
809 name == "stmaryrd" || name == "undertilde")
810 h_use_packages[name] = "2";
812 else if (name == "babel") {
813 h_language_package = "default";
814 // One might think we would have to do nothing if babel is loaded
815 // without any options to prevent pollution of the preamble with this
816 // babel call in every roundtrip.
817 // But the user could have defined babel-specific things afterwards. So
818 // we need to keep it in the preamble to prevent cases like bug #7861.
820 // check if more than one option was used - used later for inputenc
821 if (options.begin() != options.end() - 1)
822 one_language = false;
823 // babel takes the last language of the option of its \usepackage
824 // call as document language. If there is no such language option, the
825 // last language in the documentclass options is used.
826 handle_opt(options, known_languages, h_language);
827 // translate the babel name to a LyX name
828 h_language = babel2lyx(h_language);
829 if (h_language == "japanese") {
830 // For Japanese, the encoding isn't indicated in the source
831 // file, and there's really not much we can do. We could
832 // 1) offer a list of possible encodings to choose from, or
833 // 2) determine the encoding of the file by inspecting it.
834 // For the time being, we leave the encoding alone so that
835 // we don't get iconv errors when making a wrong guess, and
836 // we will output a note at the top of the document
837 // explaining what to do.
838 Encoding const * const enc = encodings.fromIconvName(
839 p.getEncoding(), Encoding::japanese, false);
841 h_inputencoding = enc->name();
842 is_nonCJKJapanese = true;
843 // in this case babel can be removed from the preamble
844 registerAutomaticallyLoadedPackage("babel");
846 // If babel is called with options, LyX puts them by default into the
847 // document class options. This works for most languages, except
848 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
849 // perhaps in future others.
850 // Therefore keep the babel call as it is as the user might have
852 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
854 delete_opt(options, known_languages);
856 h_preamble << "\\usepackage{babel}\n";
857 explicit_babel = true;
861 else if (name == "polyglossia") {
862 h_language_package = "default";
863 h_default_output_format = "pdf4";
864 h_use_non_tex_fonts = true;
866 registerAutomaticallyLoadedPackage("xunicode");
867 if (h_inputencoding == "auto")
868 p.setEncoding("UTF-8");
871 else if (name == "CJK") {
872 // set the encoding to "auto" because it might be set to "default" by the babel handling
873 // and this would not be correct for CJK
874 if (h_inputencoding == "default")
875 h_inputencoding = "auto";
876 registerAutomaticallyLoadedPackage("CJK");
879 else if (name == "CJKutf8") {
880 h_inputencoding = "utf8-cjk";
881 p.setEncoding("UTF-8");
882 registerAutomaticallyLoadedPackage("CJKutf8");
885 else if (name == "fontenc") {
886 h_fontencoding = getStringFromVector(options, ",");
887 /* We could do the following for better round trip support,
888 * but this makes the document less portable, so I skip it:
889 if (h_fontencoding == lyxrc.fontenc)
890 h_fontencoding = "global";
895 else if (name == "inputenc" || name == "luainputenc") {
896 // h_inputencoding is only set when there is not more than one
897 // inputenc option because otherwise h_inputencoding must be
898 // set to "auto" (the default encoding of the document language)
899 // Therefore check that exactly one option is passed to inputenc.
900 // It is also only set when there is not more than one babel
902 if (!options.empty()) {
903 string const encoding = options.back();
904 Encoding const * const enc = encodings.fromLaTeXName(
905 encoding, Encoding::inputenc, true);
907 cerr << "Unknown encoding " << encoding << ". Ignoring." << std::endl;
909 if (!enc->unsafe() && options.size() == 1 && one_language == true)
910 h_inputencoding = enc->name();
911 p.setEncoding(enc->iconvName());
917 else if (name == "srcltx") {
920 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
923 h_output_sync_macro = "\\usepackage{srcltx}";
926 else if (is_known(name, known_old_language_packages)) {
927 // known language packages from the times before babel
928 // if they are found and not also babel, they will be used as
929 // custom language package
930 h_language_package = "\\usepackage{" + name + "}";
933 else if (name == "lyxskak") {
934 // ignore this and its options
935 const char * const o[] = {"ps", "mover", 0};
936 delete_opt(options, o);
939 else if (is_known(name, known_lyx_packages) && options.empty()) {
940 if (name == "splitidx")
941 h_use_indices = "true";
942 if (name == "refstyle")
943 h_use_refstyle = true;
944 else if (name == "prettyref")
945 h_use_refstyle = false;
946 if (!in_lyx_preamble) {
947 h_preamble << package_beg_sep << name
948 << package_mid_sep << "\\usepackage{"
950 if (p.next_token().cat() == catNewline ||
951 (p.next_token().cat() == catSpace &&
952 p.next_next_token().cat() == catNewline))
954 h_preamble << package_end_sep;
958 else if (name == "geometry")
959 handle_geometry(options);
961 else if (name == "subfig")
962 ; // ignore this FIXME: Use the package separator mechanism instead
964 else if ((where = is_known(name, known_languages)))
965 h_language = known_coded_languages[where - known_languages];
967 else if (name == "natbib") {
968 h_biblio_style = "plainnat";
969 h_cite_engine = "natbib";
970 h_cite_engine_type = "authoryear";
971 vector<string>::iterator it =
972 find(options.begin(), options.end(), "authoryear");
973 if (it != options.end())
976 it = find(options.begin(), options.end(), "numbers");
977 if (it != options.end()) {
978 h_cite_engine_type = "numerical";
984 else if (name == "jurabib") {
985 h_biblio_style = "jurabib";
986 h_cite_engine = "jurabib";
987 h_cite_engine_type = "authoryear";
990 else if (name == "bibtopic")
991 h_use_bibtopic = "true";
993 else if (name == "hyperref")
994 handle_hyperref(options);
996 else if (name == "algorithm2e") {
997 // Load "algorithm2e" module
998 addModule("algorithm2e");
999 // Add the package options to the global document options
1000 if (!options.empty()) {
1001 if (h_options.empty())
1002 h_options = join(options, ",");
1004 h_options += ',' + join(options, ",");
1008 else if (!in_lyx_preamble) {
1009 if (options.empty())
1010 h_preamble << "\\usepackage{" << name << '}';
1012 h_preamble << "\\usepackage[" << opts << "]{"
1016 if (p.next_token().cat() == catNewline ||
1017 (p.next_token().cat() == catSpace &&
1018 p.next_next_token().cat() == catNewline))
1022 // We need to do something with the options...
1023 if (!options.empty())
1024 cerr << "Ignoring options '" << join(options, ",")
1025 << "' of package " << name << '.' << endl;
1027 // remove the whitespace
1032 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1035 Token t = p.get_token();
1036 if (t.cat() == catEscape &&
1037 is_known(t.cs(), known_if_commands))
1038 handle_if(p, in_lyx_preamble);
1040 if (!in_lyx_preamble)
1041 h_preamble << t.asInput();
1042 if (t.cat() == catEscape && t.cs() == "fi")
1049 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
1051 // set the quote language
1052 // LyX only knows the following quotes languages:
1053 // english, swedish, german, polish, french and danish
1054 // (quotes for "japanese" and "chinese-traditional" are missing because
1055 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
1056 // conversion list taken from
1057 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1058 // (quotes for kazakh and interlingua are unknown)
1060 if (is_known(h_language, known_danish_quotes_languages))
1061 h_quotes_language = "danish";
1063 else if (is_known(h_language, known_french_quotes_languages))
1064 h_quotes_language = "french";
1066 else if (is_known(h_language, known_german_quotes_languages))
1067 h_quotes_language = "german";
1069 else if (is_known(h_language, known_polish_quotes_languages))
1070 h_quotes_language = "polish";
1072 else if (is_known(h_language, known_swedish_quotes_languages))
1073 h_quotes_language = "swedish";
1075 else if (is_known(h_language, known_english_quotes_languages))
1076 h_quotes_language = "english";
1078 if (contains(h_float_placement, "H"))
1079 registerAutomaticallyLoadedPackage("float");
1080 if (h_spacing != "single" && h_spacing != "default")
1081 registerAutomaticallyLoadedPackage("setspace");
1082 if (h_use_packages["amsmath"] == "2") {
1083 // amsbsy and amstext are already provided by amsmath
1084 registerAutomaticallyLoadedPackage("amsbsy");
1085 registerAutomaticallyLoadedPackage("amstext");
1088 // output the LyX file settings
1089 // Important: Keep the version formatting in sync with LyX and
1090 // lyx2lyx (bug 7951)
1091 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1092 << lyx_version_minor << '\n'
1093 << "\\lyxformat " << LYX_FORMAT << '\n'
1094 << "\\begin_document\n"
1095 << "\\begin_header\n"
1096 << "\\textclass " << h_textclass << "\n";
1097 string const raw = subdoc ? empty_string() : h_preamble.str();
1099 os << "\\begin_preamble\n";
1100 for (string::size_type i = 0; i < raw.size(); ++i) {
1101 if (raw[i] == package_beg_sep) {
1102 // Here follows some package loading code that
1103 // must be skipped if the package is loaded
1105 string::size_type j = raw.find(package_mid_sep, i);
1106 if (j == string::npos)
1108 string::size_type k = raw.find(package_end_sep, j);
1109 if (k == string::npos)
1111 string const package = raw.substr(i + 1, j - i - 1);
1112 string const replacement = raw.substr(j + 1, k - j - 1);
1113 if (auto_packages.find(package) == auto_packages.end())
1119 os << "\n\\end_preamble\n";
1121 if (!h_options.empty())
1122 os << "\\options " << h_options << "\n";
1123 os << "\\use_default_options " << h_use_default_options << "\n";
1124 if (!used_modules.empty()) {
1125 os << "\\begin_modules\n";
1126 vector<string>::const_iterator const end = used_modules.end();
1127 vector<string>::const_iterator it = used_modules.begin();
1128 for (; it != end; ++it)
1130 os << "\\end_modules\n";
1132 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1133 << "\\language " << h_language << "\n"
1134 << "\\language_package " << h_language_package << "\n"
1135 << "\\inputencoding " << h_inputencoding << "\n"
1136 << "\\fontencoding " << h_fontencoding << "\n"
1137 << "\\font_roman " << h_font_roman << "\n"
1138 << "\\font_sans " << h_font_sans << "\n"
1139 << "\\font_typewriter " << h_font_typewriter << "\n"
1140 << "\\font_math " << h_font_math << "\n"
1141 << "\\font_default_family " << h_font_default_family << "\n"
1142 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1143 << "\\font_sc " << h_font_sc << "\n"
1144 << "\\font_osf " << h_font_osf << "\n"
1145 << "\\font_sf_scale " << h_font_sf_scale << "\n"
1146 << "\\font_tt_scale " << h_font_tt_scale << '\n';
1147 if (!h_font_cjk.empty())
1148 os << "\\font_cjk " << h_font_cjk << '\n';
1149 os << "\\graphics " << h_graphics << '\n'
1150 << "\\default_output_format " << h_default_output_format << "\n"
1151 << "\\output_sync " << h_output_sync << "\n";
1152 if (h_output_sync == "1")
1153 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1154 os << "\\bibtex_command " << h_bibtex_command << "\n"
1155 << "\\index_command " << h_index_command << "\n";
1156 if (!h_float_placement.empty())
1157 os << "\\float_placement " << h_float_placement << "\n";
1158 os << "\\paperfontsize " << h_paperfontsize << "\n"
1159 << "\\spacing " << h_spacing << "\n"
1160 << "\\use_hyperref " << h_use_hyperref << '\n';
1161 if (h_use_hyperref == "true") {
1162 if (!h_pdf_title.empty())
1163 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
1164 if (!h_pdf_author.empty())
1165 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
1166 if (!h_pdf_subject.empty())
1167 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
1168 if (!h_pdf_keywords.empty())
1169 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
1170 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1171 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1172 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1173 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1174 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1175 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1176 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1177 "\\pdf_backref " << h_pdf_backref << "\n"
1178 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1179 if (!h_pdf_pagemode.empty())
1180 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1181 if (!h_pdf_quoted_options.empty())
1182 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
1184 os << "\\papersize " << h_papersize << "\n"
1185 << "\\use_geometry " << h_use_geometry << '\n';
1186 for (map<string, string>::const_iterator it = h_use_packages.begin();
1187 it != h_use_packages.end(); ++it)
1188 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1189 os << "\\cite_engine " << h_cite_engine << '\n'
1190 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1191 << "\\biblio_style " << h_biblio_style << "\n"
1192 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1193 << "\\use_indices " << h_use_indices << "\n"
1194 << "\\paperorientation " << h_paperorientation << '\n'
1195 << "\\suppress_date " << h_suppress_date << '\n'
1196 << "\\justification " << h_justification << '\n'
1197 << "\\use_refstyle " << h_use_refstyle << '\n';
1198 if (!h_fontcolor.empty())
1199 os << "\\fontcolor " << h_fontcolor << '\n';
1200 if (!h_notefontcolor.empty())
1201 os << "\\notefontcolor " << h_notefontcolor << '\n';
1202 if (!h_backgroundcolor.empty())
1203 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1204 if (!h_boxbgcolor.empty())
1205 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1206 if (index_number != 0)
1207 for (int i = 0; i < index_number; i++) {
1208 os << "\\index " << h_index[i] << '\n'
1209 << "\\shortcut " << h_shortcut[i] << '\n'
1210 << "\\color " << h_color << '\n'
1214 os << "\\index " << h_index[0] << '\n'
1215 << "\\shortcut " << h_shortcut[0] << '\n'
1216 << "\\color " << h_color << '\n'
1220 << "\\secnumdepth " << h_secnumdepth << "\n"
1221 << "\\tocdepth " << h_tocdepth << "\n"
1222 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1223 if (h_paragraph_separation == "skip")
1224 os << "\\defskip " << h_defskip << "\n";
1226 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1227 os << "\\quotes_language " << h_quotes_language << "\n"
1228 << "\\papercolumns " << h_papercolumns << "\n"
1229 << "\\papersides " << h_papersides << "\n"
1230 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1231 if (!h_listings_params.empty())
1232 os << "\\listings_params " << h_listings_params << "\n";
1233 os << "\\tracking_changes " << h_tracking_changes << "\n"
1234 << "\\output_changes " << h_output_changes << "\n"
1235 << "\\html_math_output " << h_html_math_output << "\n"
1236 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1237 << "\\html_be_strict " << h_html_be_strict << "\n"
1239 << "\\end_header\n\n"
1240 << "\\begin_body\n";
1245 void Preamble::parse(Parser & p, string const & forceclass,
1246 TeX2LyXDocClass & tc)
1248 // initialize fixed types
1249 special_columns_['D'] = 3;
1250 bool is_full_document = false;
1251 bool is_lyx_file = false;
1252 bool in_lyx_preamble = false;
1254 // determine whether this is a full document or a fragment for inclusion
1256 Token const & t = p.get_token();
1258 if (t.cat() == catEscape && t.cs() == "documentclass") {
1259 is_full_document = true;
1265 while (is_full_document && p.good()) {
1266 Token const & t = p.get_token();
1269 cerr << "t: " << t << "\n";
1275 if (!in_lyx_preamble &&
1276 (t.cat() == catLetter ||
1277 t.cat() == catSuper ||
1278 t.cat() == catSub ||
1279 t.cat() == catOther ||
1280 t.cat() == catMath ||
1281 t.cat() == catActive ||
1282 t.cat() == catBegin ||
1283 t.cat() == catEnd ||
1284 t.cat() == catAlign ||
1285 t.cat() == catParameter))
1286 h_preamble << t.cs();
1288 else if (!in_lyx_preamble &&
1289 (t.cat() == catSpace || t.cat() == catNewline))
1290 h_preamble << t.asInput();
1292 else if (t.cat() == catComment) {
1293 static regex const islyxfile("%% LyX .* created this file");
1294 static regex const usercommands("User specified LaTeX commands");
1296 string const comment = t.asInput();
1298 // magically switch encoding default if it looks like XeLaTeX
1299 static string const magicXeLaTeX =
1300 "% This document must be compiled with XeLaTeX ";
1301 if (comment.size() > magicXeLaTeX.size()
1302 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1303 && h_inputencoding == "auto") {
1304 cerr << "XeLaTeX comment found, switching to UTF8\n";
1305 h_inputencoding = "utf8";
1308 if (regex_search(comment, sub, islyxfile)) {
1310 in_lyx_preamble = true;
1311 } else if (is_lyx_file
1312 && regex_search(comment, sub, usercommands))
1313 in_lyx_preamble = false;
1314 else if (!in_lyx_preamble)
1315 h_preamble << t.asInput();
1318 else if (t.cs() == "pagestyle")
1319 h_paperpagestyle = p.verbatim_item();
1321 else if (t.cs() == "setdefaultlanguage") {
1323 // We don't yet care about non-language variant options
1324 // because LyX doesn't support this yet, see bug #8214
1326 string langopts = p.getOpt();
1327 // check if the option contains a variant, if yes, extract it
1328 string::size_type pos_var = langopts.find("variant");
1329 string::size_type i = langopts.find(',', pos_var);
1330 string::size_type k = langopts.find('=', pos_var);
1331 if (pos_var != string::npos){
1333 if (i == string::npos)
1334 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1336 variant = langopts.substr(k + 1, i - k - 1);
1337 h_language = variant;
1341 h_language = p.verbatim_item();
1342 //finally translate the poyglossia name to a LyX name
1343 h_language = polyglossia2lyx(h_language);
1346 else if (t.cs() == "setotherlanguage") {
1347 // We don't yet care about the option because LyX doesn't
1348 // support this yet, see bug #8214
1349 p.hasOpt() ? p.getOpt() : string();
1353 else if (t.cs() == "setmainfont") {
1354 // we don't care about the option
1355 p.hasOpt() ? p.getOpt() : string();
1356 h_font_roman = p.getArg('{', '}');
1359 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1360 // LyX currently only supports the scale option
1363 string fontopts = p.getArg('[', ']');
1364 // check if the option contains a scaling, if yes, extract it
1365 string::size_type pos = fontopts.find("Scale");
1366 if (pos != string::npos) {
1367 string::size_type i = fontopts.find(',', pos);
1368 if (i == string::npos)
1369 scale_as_percentage(fontopts.substr(pos + 1), scale);
1371 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1374 if (t.cs() == "setsansfont") {
1376 h_font_sf_scale = scale;
1377 h_font_sans = p.getArg('{', '}');
1380 h_font_tt_scale = scale;
1381 h_font_typewriter = p.getArg('{', '}');
1385 else if (t.cs() == "date") {
1386 string argument = p.getArg('{', '}');
1387 if (argument.empty())
1388 h_suppress_date = "true";
1390 h_preamble << t.asInput() << '{' << argument << '}';
1393 else if (t.cs() == "color") {
1394 string const space =
1395 (p.hasOpt() ? p.getOpt() : string());
1396 string argument = p.getArg('{', '}');
1397 // check the case that a standard color is used
1398 if (space.empty() && is_known(argument, known_basic_colors)) {
1399 h_fontcolor = rgbcolor2code(argument);
1400 preamble.registerAutomaticallyLoadedPackage("color");
1401 } else if (space.empty() && argument == "document_fontcolor")
1402 preamble.registerAutomaticallyLoadedPackage("color");
1403 // check the case that LyX's document_fontcolor is defined
1404 // but not used for \color
1406 h_preamble << t.asInput();
1408 h_preamble << space;
1409 h_preamble << '{' << argument << '}';
1410 // the color might already be set because \definecolor
1411 // is parsed before this
1416 else if (t.cs() == "pagecolor") {
1417 string argument = p.getArg('{', '}');
1418 // check the case that a standard color is used
1419 if (is_known(argument, known_basic_colors)) {
1420 h_backgroundcolor = rgbcolor2code(argument);
1421 } else if (argument == "page_backgroundcolor")
1422 preamble.registerAutomaticallyLoadedPackage("color");
1423 // check the case that LyX's page_backgroundcolor is defined
1424 // but not used for \pagecolor
1426 h_preamble << t.asInput() << '{' << argument << '}';
1427 // the color might already be set because \definecolor
1428 // is parsed before this
1429 h_backgroundcolor = "";
1433 else if (t.cs() == "makeatletter") {
1434 // LyX takes care of this
1435 p.setCatcode('@', catLetter);
1438 else if (t.cs() == "makeatother") {
1439 // LyX takes care of this
1440 p.setCatcode('@', catOther);
1443 else if (t.cs() == "makeindex") {
1444 // LyX will re-add this if a print index command is found
1448 else if (t.cs() == "newindex") {
1449 string const indexname = p.getArg('[', ']');
1450 string const shortcut = p.verbatim_item();
1451 if (!indexname.empty())
1452 h_index[index_number] = indexname;
1454 h_index[index_number] = shortcut;
1455 h_shortcut[index_number] = shortcut;
1460 else if (t.cs() == "RS@ifundefined") {
1461 string const name = p.verbatim_item();
1462 string const body1 = p.verbatim_item();
1463 string const body2 = p.verbatim_item();
1464 // only non-lyxspecific stuff
1465 if (in_lyx_preamble &&
1466 (name == "subref" || name == "thmref" || name == "lemref"))
1470 ss << '\\' << t.cs();
1471 ss << '{' << name << '}'
1472 << '{' << body1 << '}'
1473 << '{' << body2 << '}';
1474 h_preamble << ss.str();
1478 else if (t.cs() == "AtBeginDocument") {
1479 string const name = p.verbatim_item();
1480 // only non-lyxspecific stuff
1481 if (in_lyx_preamble &&
1482 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1483 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1484 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1485 || name == "\\providecommand\\subref[1]{\\ref{sub:#1}}"
1486 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1487 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1488 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1489 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1490 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1491 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1492 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1493 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1494 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1495 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1496 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1500 ss << '\\' << t.cs();
1501 ss << '{' << name << '}';
1502 h_preamble << ss.str();
1506 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1507 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1508 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1509 || t.cs() == "DeclareRobustCommand"
1510 || t.cs() == "DeclareRobustCommandx"
1511 || t.cs() == "ProvideTextCommandDefault"
1512 || t.cs() == "DeclareMathAccent") {
1514 if (p.next_token().character() == '*') {
1518 string const name = p.verbatim_item();
1519 string const opt1 = p.getFullOpt();
1520 string const opt2 = p.getFullOpt();
1521 string const body = p.verbatim_item();
1522 // store the in_lyx_preamble setting
1523 bool const was_in_lyx_preamble = in_lyx_preamble;
1525 if (name == "\\rmdefault")
1526 if (is_known(body, known_roman_fonts)) {
1527 h_font_roman = body;
1529 in_lyx_preamble = true;
1531 if (name == "\\sfdefault")
1532 if (is_known(body, known_sans_fonts)) {
1535 in_lyx_preamble = true;
1537 if (name == "\\ttdefault")
1538 if (is_known(body, known_typewriter_fonts)) {
1539 h_font_typewriter = body;
1541 in_lyx_preamble = true;
1543 if (name == "\\familydefault") {
1544 string family = body;
1545 // remove leading "\"
1546 h_font_default_family = family.erase(0,1);
1548 in_lyx_preamble = true;
1551 // remove the lyxdot definition that is re-added by LyX
1553 if (name == "\\lyxdot") {
1555 in_lyx_preamble = true;
1558 // Add the command to the known commands
1559 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1561 // only non-lyxspecific stuff
1562 if (!in_lyx_preamble) {
1564 ss << '\\' << t.cs();
1567 ss << '{' << name << '}' << opt1 << opt2
1568 << '{' << body << "}";
1569 h_preamble << ss.str();
1571 ostream & out = in_preamble ? h_preamble : os;
1572 out << "\\" << t.cs() << "{" << name << "}"
1573 << opts << "{" << body << "}";
1576 // restore the in_lyx_preamble setting
1577 in_lyx_preamble = was_in_lyx_preamble;
1580 else if (t.cs() == "documentclass") {
1581 vector<string>::iterator it;
1582 vector<string> opts = split_options(p.getArg('[', ']'));
1583 handle_opt(opts, known_fontsizes, h_paperfontsize);
1584 delete_opt(opts, known_fontsizes);
1585 // delete "pt" at the end
1586 string::size_type i = h_paperfontsize.find("pt");
1587 if (i != string::npos)
1588 h_paperfontsize.erase(i);
1589 // The documentclass options are always parsed before the options
1590 // of the babel call so that a language cannot overwrite the babel
1592 handle_opt(opts, known_languages, h_language);
1593 delete_opt(opts, known_languages);
1595 // paper orientation
1596 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1597 h_paperorientation = "landscape";
1601 if ((it = find(opts.begin(), opts.end(), "oneside"))
1606 if ((it = find(opts.begin(), opts.end(), "twoside"))
1612 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1614 h_papercolumns = "1";
1617 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1619 h_papercolumns = "2";
1623 // some size options are known to any document classes, other sizes
1624 // are handled by the \geometry command of the geometry package
1625 handle_opt(opts, known_class_paper_sizes, h_papersize);
1626 delete_opt(opts, known_class_paper_sizes);
1627 // the remaining options
1628 h_options = join(opts, ",");
1629 // FIXME This does not work for classes that have a
1630 // different name in LyX than in LaTeX
1631 h_textclass = p.getArg('{', '}');
1635 else if (t.cs() == "usepackage") {
1636 string const options = p.getArg('[', ']');
1637 string const name = p.getArg('{', '}');
1638 vector<string> vecnames;
1639 split(name, vecnames, ',');
1640 vector<string>::const_iterator it = vecnames.begin();
1641 vector<string>::const_iterator end = vecnames.end();
1642 for (; it != end; ++it)
1643 handle_package(p, trimSpaceAndEol(*it), options,
1647 else if (t.cs() == "inputencoding") {
1648 string const encoding = p.getArg('{','}');
1649 Encoding const * const enc = encodings.fromLaTeXName(
1650 encoding, Encoding::inputenc, true);
1652 cerr << "Unknown encoding " << encoding << ". Ignoring." << std::endl;
1655 h_inputencoding = enc->name();
1656 p.setEncoding(enc->iconvName());
1660 else if (t.cs() == "newenvironment") {
1661 string const name = p.getArg('{', '}');
1662 string const opt1 = p.getFullOpt();
1663 string const opt2 = p.getFullOpt();
1664 string const beg = p.verbatim_item();
1665 string const end = p.verbatim_item();
1666 if (!in_lyx_preamble) {
1667 h_preamble << "\\newenvironment{" << name
1668 << '}' << opt1 << opt2 << '{'
1669 << beg << "}{" << end << '}';
1671 add_known_environment(name, opt1, !opt2.empty(),
1672 from_utf8(beg), from_utf8(end));
1676 else if (t.cs() == "newtheorem") {
1677 string const name = p.getArg('{', '}');
1678 string const opt1 = p.getFullOpt();
1679 string const opt2 = p.getFullOpt();
1680 string const body = p.verbatim_item();
1681 string const opt3 = p.getFullOpt();
1683 add_known_theorem(name, opt1, !opt2.empty(),
1684 from_utf8("\\newtheorem{" + name + '}' +
1685 opt1 + opt2 + '{' + body + '}' + opt3));
1687 if (!in_lyx_preamble)
1688 h_preamble << "\\newtheorem{" << name << '}'
1689 << opt1 << opt2 << '{' << '}' << opt3;
1692 else if (t.cs() == "def") {
1693 string name = p.get_token().cs();
1694 // In fact, name may be more than the name:
1695 // In the test case of bug 8116
1696 // name == "csname SF@gobble@opt \endcsname".
1697 // Therefore, we need to use asInput() instead of cs().
1698 while (p.next_token().cat() != catBegin)
1699 name += p.get_token().asInput();
1700 if (!in_lyx_preamble)
1701 h_preamble << "\\def\\" << name << '{'
1702 << p.verbatim_item() << "}";
1705 else if (t.cs() == "newcolumntype") {
1706 string const name = p.getArg('{', '}');
1707 trimSpaceAndEol(name);
1709 string opts = p.getOpt();
1710 if (!opts.empty()) {
1711 istringstream is(string(opts, 1));
1714 special_columns_[name[0]] = nargs;
1715 h_preamble << "\\newcolumntype{" << name << "}";
1717 h_preamble << "[" << nargs << "]";
1718 h_preamble << "{" << p.verbatim_item() << "}";
1721 else if (t.cs() == "setcounter") {
1722 string const name = p.getArg('{', '}');
1723 string const content = p.getArg('{', '}');
1724 if (name == "secnumdepth")
1725 h_secnumdepth = content;
1726 else if (name == "tocdepth")
1727 h_tocdepth = content;
1729 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1732 else if (t.cs() == "setlength") {
1733 string const name = p.verbatim_item();
1734 string const content = p.verbatim_item();
1735 // the paragraphs are only not indented when \parindent is set to zero
1736 if (name == "\\parindent" && content != "") {
1737 if (content[0] == '0')
1738 h_paragraph_separation = "skip";
1740 h_paragraph_indentation = translate_len(content);
1741 } else if (name == "\\parskip") {
1742 if (content == "\\smallskipamount")
1743 h_defskip = "smallskip";
1744 else if (content == "\\medskipamount")
1745 h_defskip = "medskip";
1746 else if (content == "\\bigskipamount")
1747 h_defskip = "bigskip";
1749 h_defskip = translate_len(content);
1751 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1754 else if (t.cs() == "onehalfspacing")
1755 h_spacing = "onehalf";
1757 else if (t.cs() == "doublespacing")
1758 h_spacing = "double";
1760 else if (t.cs() == "setstretch")
1761 h_spacing = "other " + p.verbatim_item();
1763 else if (t.cs() == "synctex") {
1764 // the scheme is \synctex=value
1765 // where value can only be "1" or "-1"
1766 h_output_sync = "1";
1767 // there can be any character behind the value (e.g. a linebreak or a '\'
1768 // therefore we extract it char by char
1770 string value = p.get_token().asInput();
1772 value += p.get_token().asInput();
1773 h_output_sync_macro = "\\synctex=" + value;
1776 else if (t.cs() == "begin") {
1777 string const name = p.getArg('{', '}');
1778 if (name == "document")
1780 h_preamble << "\\begin{" << name << "}";
1783 else if (t.cs() == "geometry") {
1784 vector<string> opts = split_options(p.getArg('{', '}'));
1785 handle_geometry(opts);
1788 else if (t.cs() == "definecolor") {
1789 string const color = p.getArg('{', '}');
1790 string const space = p.getArg('{', '}');
1791 string const value = p.getArg('{', '}');
1792 if (color == "document_fontcolor" && space == "rgb") {
1793 RGBColor c(RGBColorFromLaTeX(value));
1794 h_fontcolor = X11hexname(c);
1795 } else if (color == "note_fontcolor" && space == "rgb") {
1796 RGBColor c(RGBColorFromLaTeX(value));
1797 h_notefontcolor = X11hexname(c);
1798 } else if (color == "page_backgroundcolor" && space == "rgb") {
1799 RGBColor c(RGBColorFromLaTeX(value));
1800 h_backgroundcolor = X11hexname(c);
1801 } else if (color == "shadecolor" && space == "rgb") {
1802 RGBColor c(RGBColorFromLaTeX(value));
1803 h_boxbgcolor = X11hexname(c);
1805 h_preamble << "\\definecolor{" << color
1806 << "}{" << space << "}{" << value
1811 else if (t.cs() == "bibliographystyle")
1812 h_biblio_style = p.verbatim_item();
1814 else if (t.cs() == "jurabibsetup") {
1815 // FIXME p.getArg('{', '}') is most probably wrong (it
1816 // does not handle nested braces).
1817 // Use p.verbatim_item() instead.
1818 vector<string> jurabibsetup =
1819 split_options(p.getArg('{', '}'));
1820 // add jurabibsetup to the jurabib package options
1821 add_package("jurabib", jurabibsetup);
1822 if (!jurabibsetup.empty()) {
1823 h_preamble << "\\jurabibsetup{"
1824 << join(jurabibsetup, ",") << '}';
1828 else if (t.cs() == "hypersetup") {
1829 vector<string> hypersetup =
1830 split_options(p.verbatim_item());
1831 // add hypersetup to the hyperref package options
1832 handle_hyperref(hypersetup);
1833 if (!hypersetup.empty()) {
1834 h_preamble << "\\hypersetup{"
1835 << join(hypersetup, ",") << '}';
1839 else if (is_known(t.cs(), known_if_3arg_commands)) {
1840 // prevent misparsing of \usepackage if it is used
1841 // as an argument (see e.g. our own output of
1842 // \@ifundefined above)
1843 string const arg1 = p.verbatim_item();
1844 string const arg2 = p.verbatim_item();
1845 string const arg3 = p.verbatim_item();
1846 // test case \@ifundefined{date}{}{\date{}}
1847 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1848 arg2.empty() && arg3 == "\\date{}") {
1849 h_suppress_date = "true";
1850 // older tex2lyx versions did output
1851 // \@ifundefined{definecolor}{\usepackage{color}}{}
1852 } else if (t.cs() == "@ifundefined" &&
1853 arg1 == "definecolor" &&
1854 arg2 == "\\usepackage{color}" &&
1856 if (!in_lyx_preamble)
1857 h_preamble << package_beg_sep
1860 << "\\@ifundefined{definecolor}{color}{}"
1863 //\@ifundefined{showcaptionsetup}{}{%
1864 // \PassOptionsToPackage{caption=false}{subfig}}
1865 // that LyX uses for subfloats
1866 } else if (t.cs() == "@ifundefined" &&
1867 arg1 == "showcaptionsetup" && arg2.empty()
1868 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1870 } else if (!in_lyx_preamble) {
1871 h_preamble << t.asInput()
1872 << '{' << arg1 << '}'
1873 << '{' << arg2 << '}'
1874 << '{' << arg3 << '}';
1878 else if (is_known(t.cs(), known_if_commands)) {
1879 // must not parse anything in conditional code, since
1880 // LyX would output the parsed contents unconditionally
1881 if (!in_lyx_preamble)
1882 h_preamble << t.asInput();
1883 handle_if(p, in_lyx_preamble);
1886 else if (!t.cs().empty() && !in_lyx_preamble)
1887 h_preamble << '\\' << t.cs();
1890 // remove the whitespace
1893 // Force textclass if the user wanted it
1894 if (!forceclass.empty())
1895 h_textclass = forceclass;
1896 tc.setName(h_textclass);
1898 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1901 if (h_papersides.empty()) {
1904 h_papersides = ss.str();
1907 // If the CJK package is used we cannot set the document language from
1908 // the babel options. Instead, we guess which language is used most
1909 // and set this one.
1910 default_language = h_language;
1911 if (is_full_document &&
1912 (auto_packages.find("CJK") != auto_packages.end() ||
1913 auto_packages.find("CJKutf8") != auto_packages.end())) {
1915 h_language = guessLanguage(p, default_language);
1917 if (explicit_babel && h_language != default_language) {
1918 // We set the document language to a CJK language,
1919 // but babel is explicitly called in the user preamble
1920 // without options. LyX will not add the default
1921 // language to the document options if it is either
1922 // english, or no text is set as default language.
1923 // Therefore we need to add a language option explicitly.
1924 // FIXME: It would be better to remove all babel calls
1925 // from the user preamble, but this is difficult
1926 // without re-introducing bug 7861.
1927 if (h_options.empty())
1928 h_options = lyx2babel(default_language);
1930 h_options += ',' + lyx2babel(default_language);
1936 string babel2lyx(string const & language)
1938 char const * const * where = is_known(language, known_languages);
1940 return known_coded_languages[where - known_languages];
1945 string lyx2babel(string const & language)
1947 char const * const * where = is_known(language, known_coded_languages);
1949 return known_languages[where - known_coded_languages];
1954 string Preamble::polyglossia2lyx(string const & language)
1956 char const * const * where = is_known(language, polyglossia_languages);
1958 return coded_polyglossia_languages[where - polyglossia_languages];
1963 string rgbcolor2code(string const & name)
1965 char const * const * where = is_known(name, known_basic_colors);
1967 // "red", "green" etc
1968 return known_basic_color_codes[where - known_basic_colors];
1970 // "255,0,0", "0,255,0" etc
1971 RGBColor c(RGBColorFromLaTeX(name));
1972 return X11hexname(c);