3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "LayoutFile.h"
23 #include "TextClass.h"
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
31 #include "support/regex.h"
37 using namespace lyx::support;
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
49 * known babel language names (including synonyms)
50 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51 * please keep this in sync with known_coded_languages line by line!
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
55 "bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british",
56 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
57 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
58 "french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese",
64 "romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin",
65 "slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
66 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
71 * the same as known_languages with .lyx names
72 * please keep this in sync with known_languages line by line!
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british",
77 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
78 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
79 "french", "french", "french", "french", "friulan", "galician", "german", "german",
80 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese",
85 "romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin",
86 "slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
87 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
88 "vietnamese", "welsh",
91 /// languages with danish quotes (.lyx names)
92 const char * const known_danish_quotes_languages[] = {"danish", 0};
94 /// languages with english quotes (.lyx names)
95 const char * const known_english_quotes_languages[] = {"american", "australian",
96 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
97 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
100 /// languages with french quotes (.lyx names)
101 const char * const known_french_quotes_languages[] = {"albanian",
102 "arabic_arabi", "arabic_arabtex", "asturian", "basque", "canadien", "catalan",
103 "french", "friulan", "galician", "greek", "italian", "norsk", "nynorsk",
104 "piedmontese", "polutonikogreek", "russian", "spanish", "spanish-mexico",
105 "turkish", "turkmen", "ukrainian", "vietnamese", 0};
107 /// languages with german quotes (.lyx names)
108 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
109 "czech", "german", "georgian", "icelandic", "lithuanian", "lowersorbian", "macedonian",
110 "naustrian", "ngerman", "romansh", "serbian", "serbian-latin", "slovak", "slovene",
113 /// languages with polish quotes (.lyx names)
114 const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian",
115 "dutch", "estonian", "magyar", "polish", "romanian", 0};
117 /// languages with swedish quotes (.lyx names)
118 const char * const known_swedish_quotes_languages[] = {"finnish",
121 /// known language packages from the times before babel
122 const char * const known_old_language_packages[] = {"french", "frenchle",
123 "frenchpro", "german", "ngerman", "pmfrench", 0};
125 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
127 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
128 "ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "fourier",
129 "garamondx", "libertine", "libertine-type1", "lmodern", "mathdesign", "mathpazo",
130 "mathptmx", "newcent", "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes",
133 const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum-type1",
134 "cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc", "kurier",
135 "kurierc", "kurierl", "kurierlc", "lmss", "tgadventor", "tgheros", 0};
137 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
138 "courier", "lmtt", "luximono", "fourier", "libertineMono-type1", "lmodern",
139 "mathpazo", "mathptmx", "newcent", "tgcursor", "txtt", 0};
141 const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0};
143 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
144 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
145 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
146 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
147 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
149 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
150 "executivepaper", "legalpaper", "letterpaper", 0};
152 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
153 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
155 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
156 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
159 /// commands that can start an \if...\else...\endif sequence
160 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
161 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
162 "ifsidecap", "ifupgreek", 0};
164 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
165 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
166 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
168 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
169 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
170 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
172 /// conditional commands with three arguments like \@ifundefined{}{}{}
173 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
176 /// packages that work only in xetex
177 /// polyglossia is handled separately
178 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
179 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
180 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
182 /// packages that are automatically skipped if loaded by LyX
183 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
184 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
185 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
186 "makeidx", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle", "rotating",
187 "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa", "tipx",
188 "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xunicode", 0};
190 // codes used to remove packages that are loaded automatically by LyX.
191 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
192 const char package_beg_sep = '\001';
193 const char package_mid_sep = '\002';
194 const char package_end_sep = '\003';
197 // returns true if at least one of the options in what has been found
198 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
204 // the last language option is the document language (for babel and LyX)
205 // the last size option is the document font size
206 vector<string>::iterator it;
207 vector<string>::iterator position = opts.begin();
208 for (; *what; ++what) {
209 it = find(opts.begin(), opts.end(), *what);
210 if (it != opts.end()) {
211 if (it >= position) {
222 void delete_opt(vector<string> & opts, char const * const * what)
227 // remove found options from the list
228 // do this after handle_opt to avoid potential memory leaks
229 vector<string>::iterator it;
230 for (; *what; ++what) {
231 it = find(opts.begin(), opts.end(), *what);
232 if (it != opts.end())
239 * Split a package options string (keyval format) into a vector.
241 * authorformat=smallcaps,
243 * titleformat=colonsep,
244 * bibformat={tabular,ibidem,numbered}
246 vector<string> split_options(string const & input)
248 vector<string> options;
252 Token const & t = p.get_token();
253 if (t.asInput() == ",") {
254 options.push_back(trimSpaceAndEol(option));
256 } else if (t.asInput() == "=") {
259 if (p.next_token().asInput() == "{")
260 option += '{' + p.getArg('{', '}') + '}';
261 } else if (t.cat() != catSpace)
262 option += t.asInput();
266 options.push_back(trimSpaceAndEol(option));
273 * Retrieve a keyval option "name={value with=sign}" named \p name from
274 * \p options and return the value.
275 * The found option is also removed from \p options.
277 string process_keyval_opt(vector<string> & options, string name)
279 for (size_t i = 0; i < options.size(); ++i) {
280 vector<string> option;
281 split(options[i], option, '=');
282 if (option.size() < 2)
284 if (option[0] == name) {
285 options.erase(options.begin() + i);
286 option.erase(option.begin());
287 return join(option, "=");
293 } // anonymous namespace
297 * known polyglossia language names (including variants)
298 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
300 const char * const Preamble::polyglossia_languages[] = {
301 "albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian",
302 "bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian",
303 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
304 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
305 "galician", "greek", "monotonic", "hebrew", "hindi",
306 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
307 "lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi",
308 "austrian", "newzealand", "german", "norsk", "nynorsk", "occitan",
309 "piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian",
310 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac",
311 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
312 "ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0};
313 // not yet supported by LyX: "korean", "nko"
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", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian",
321 "bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian",
322 "catalan", "coptic", "croatian", "czech", "danish", "divehi", "dutch",
323 "english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan",
324 "galician", "greek", "greek", "hebrew", "hindi",
325 "icelandic", "interlingua", "irish", "italian", "kannada", "khmer",
326 "lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi",
327 "naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan",
328 "piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian",
329 "samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac",
330 "tamil", "telugu", "thai", "tibetan", "turkish", "turkmen",
331 "ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0};
332 // not yet supported by LyX: "korean-polyglossia", "nko"
335 bool Preamble::usePolyglossia() const
337 return h_use_non_tex_fonts && h_language_package == "default";
341 bool Preamble::indentParagraphs() const
343 return h_paragraph_separation == "indent";
347 bool Preamble::isPackageUsed(string const & package) const
349 return used_packages.find(package) != used_packages.end();
353 vector<string> Preamble::getPackageOptions(string const & package) const
355 map<string, vector<string> >::const_iterator it = used_packages.find(package);
356 if (it != used_packages.end())
358 return vector<string>();
362 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
364 auto_packages.insert(package);
368 void Preamble::addModule(string const & module)
370 used_modules.push_back(module);
374 void Preamble::suppressDate(bool suppress)
377 h_suppress_date = "true";
379 h_suppress_date = "false";
383 void Preamble::registerAuthor(std::string const & name)
385 Author author(from_utf8(name), empty_docstring());
386 author.setUsed(true);
387 authors_.record(author);
388 h_tracking_changes = "true";
389 h_output_changes = "true";
393 Author const & Preamble::getAuthor(std::string const & name) const
395 Author author(from_utf8(name), empty_docstring());
396 for (AuthorList::Authors::const_iterator it = authors_.begin();
397 it != authors_.end(); ++it)
400 static Author const dummy;
405 int Preamble::getSpecialTableColumnArguments(char c) const
407 map<char, int>::const_iterator it = special_columns_.find(c);
408 if (it == special_columns_.end())
414 void Preamble::add_package(string const & name, vector<string> & options)
416 // every package inherits the global options
417 if (used_packages.find(name) == used_packages.end())
418 used_packages[name] = split_options(h_options);
420 vector<string> & v = used_packages[name];
421 v.insert(v.end(), options.begin(), options.end());
422 if (name == "jurabib") {
423 // Don't output the order argument (see the cite command
424 // handling code in text.cpp).
425 vector<string>::iterator end =
426 remove(options.begin(), options.end(), "natbiborder");
427 end = remove(options.begin(), end, "jurabiborder");
428 options.erase(end, options.end());
435 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
436 bool scale_as_percentage(string const & scale, string & percentage)
438 string::size_type pos = scale.find('=');
439 if (pos != string::npos) {
440 string value = scale.substr(pos + 1);
441 if (isStrDbl(value)) {
442 percentage = convert<string>(
443 static_cast<int>(100 * convert<double>(value)));
451 string remove_braces(string const & value)
455 if (value[0] == '{' && value[value.length()-1] == '}')
456 return value.substr(1, value.length()-2);
460 } // anonymous namespace
463 Preamble::Preamble() : one_language(true), explicit_babel(false),
464 title_layout_found(false), index_number(0), h_font_cjk_set(false),
465 h_use_microtype(false)
469 h_biblio_style = "plain";
470 h_bibtex_command = "default";
471 h_cite_engine = "basic";
472 h_cite_engine_type = "default";
474 h_defskip = "medskip";
477 h_fontencoding = "default";
478 h_font_roman[0] = "default";
479 h_font_roman[1] = "default";
480 h_font_sans[0] = "default";
481 h_font_sans[1] = "default";
482 h_font_typewriter[0] = "default";
483 h_font_typewriter[1] = "default";
484 h_font_math[0] = "auto";
485 h_font_math[1] = "auto";
486 h_font_default_family = "default";
487 h_use_non_tex_fonts = false;
489 h_font_osf = "false";
490 h_font_sf_scale[0] = "100";
491 h_font_sf_scale[1] = "100";
492 h_font_tt_scale[0] = "100";
493 h_font_tt_scale[1] = "100";
495 h_graphics = "default";
496 h_default_output_format = "default";
497 h_html_be_strict = "false";
498 h_html_css_as_file = "0";
499 h_html_math_output = "0";
500 h_index[0] = "Index";
501 h_index_command = "default";
502 h_inputencoding = "auto";
503 h_justification = "true";
504 h_language = "english";
505 h_language_package = "none";
507 h_maintain_unincluded_children = "false";
511 h_output_changes = "false";
513 //h_output_sync_macro
514 h_papercolumns = "1";
515 h_paperfontsize = "default";
516 h_paperorientation = "portrait";
517 h_paperpagestyle = "default";
519 h_papersize = "default";
520 h_paragraph_indentation = "default";
521 h_paragraph_separation = "indent";
526 h_pdf_bookmarks = "0";
527 h_pdf_bookmarksnumbered = "0";
528 h_pdf_bookmarksopen = "0";
529 h_pdf_bookmarksopenlevel = "1";
530 h_pdf_breaklinks = "0";
531 h_pdf_pdfborder = "0";
532 h_pdf_colorlinks = "0";
533 h_pdf_backref = "section";
534 h_pdf_pdfusetitle = "0";
536 //h_pdf_quoted_options;
537 h_quotes_style = "english";
539 h_shortcut[0] = "idx";
540 h_spacing = "single";
541 h_save_transient_properties = "true";
542 h_suppress_date = "false";
543 h_textclass = "article";
545 h_tracking_changes = "false";
546 h_use_bibtopic = "false";
547 h_use_indices = "false";
548 h_use_geometry = "false";
549 h_use_default_options = "false";
550 h_use_hyperref = "false";
551 h_use_microtype = false;
552 h_use_refstyle = false;
553 h_use_packages["amsmath"] = "1";
554 h_use_packages["amssymb"] = "0";
555 h_use_packages["cancel"] = "0";
556 h_use_packages["esint"] = "1";
557 h_use_packages["mhchem"] = "0";
558 h_use_packages["mathdots"] = "0";
559 h_use_packages["mathtools"] = "0";
560 h_use_packages["stackrel"] = "0";
561 h_use_packages["stmaryrd"] = "0";
562 h_use_packages["undertilde"] = "0";
566 void Preamble::handle_hyperref(vector<string> & options)
568 // FIXME swallow inputencoding changes that might surround the
569 // hyperref setup if it was written by LyX
570 h_use_hyperref = "true";
571 // swallow "unicode=true", since LyX does always write that
572 vector<string>::iterator it =
573 find(options.begin(), options.end(), "unicode=true");
574 if (it != options.end())
576 it = find(options.begin(), options.end(), "pdfusetitle");
577 if (it != options.end()) {
578 h_pdf_pdfusetitle = "1";
581 string bookmarks = process_keyval_opt(options, "bookmarks");
582 if (bookmarks == "true")
583 h_pdf_bookmarks = "1";
584 else if (bookmarks == "false")
585 h_pdf_bookmarks = "0";
586 if (h_pdf_bookmarks == "1") {
587 string bookmarksnumbered =
588 process_keyval_opt(options, "bookmarksnumbered");
589 if (bookmarksnumbered == "true")
590 h_pdf_bookmarksnumbered = "1";
591 else if (bookmarksnumbered == "false")
592 h_pdf_bookmarksnumbered = "0";
593 string bookmarksopen =
594 process_keyval_opt(options, "bookmarksopen");
595 if (bookmarksopen == "true")
596 h_pdf_bookmarksopen = "1";
597 else if (bookmarksopen == "false")
598 h_pdf_bookmarksopen = "0";
599 if (h_pdf_bookmarksopen == "1") {
600 string bookmarksopenlevel =
601 process_keyval_opt(options, "bookmarksopenlevel");
602 if (!bookmarksopenlevel.empty())
603 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
606 string breaklinks = process_keyval_opt(options, "breaklinks");
607 if (breaklinks == "true")
608 h_pdf_breaklinks = "1";
609 else if (breaklinks == "false")
610 h_pdf_breaklinks = "0";
611 string pdfborder = process_keyval_opt(options, "pdfborder");
612 if (pdfborder == "{0 0 0}")
613 h_pdf_pdfborder = "1";
614 else if (pdfborder == "{0 0 1}")
615 h_pdf_pdfborder = "0";
616 string backref = process_keyval_opt(options, "backref");
617 if (!backref.empty())
618 h_pdf_backref = backref;
619 string colorlinks = process_keyval_opt(options, "colorlinks");
620 if (colorlinks == "true")
621 h_pdf_colorlinks = "1";
622 else if (colorlinks == "false")
623 h_pdf_colorlinks = "0";
624 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
625 if (!pdfpagemode.empty())
626 h_pdf_pagemode = pdfpagemode;
627 string pdftitle = process_keyval_opt(options, "pdftitle");
628 if (!pdftitle.empty()) {
629 h_pdf_title = remove_braces(pdftitle);
631 string pdfauthor = process_keyval_opt(options, "pdfauthor");
632 if (!pdfauthor.empty()) {
633 h_pdf_author = remove_braces(pdfauthor);
635 string pdfsubject = process_keyval_opt(options, "pdfsubject");
636 if (!pdfsubject.empty())
637 h_pdf_subject = remove_braces(pdfsubject);
638 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
639 if (!pdfkeywords.empty())
640 h_pdf_keywords = remove_braces(pdfkeywords);
641 if (!options.empty()) {
642 if (!h_pdf_quoted_options.empty())
643 h_pdf_quoted_options += ',';
644 h_pdf_quoted_options += join(options, ",");
650 void Preamble::handle_geometry(vector<string> & options)
652 h_use_geometry = "true";
653 vector<string>::iterator it;
655 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
656 h_paperorientation = "landscape";
660 // keyval version: "paper=letter"
661 string paper = process_keyval_opt(options, "paper");
663 h_papersize = paper + "paper";
664 // alternative version: "letterpaper"
665 handle_opt(options, known_paper_sizes, h_papersize);
666 delete_opt(options, known_paper_sizes);
668 char const * const * margin = known_paper_margins;
669 for (; *margin; ++margin) {
670 string value = process_keyval_opt(options, *margin);
671 if (!value.empty()) {
672 int k = margin - known_paper_margins;
673 string name = known_coded_paper_margins[k];
674 h_margins += '\\' + name + ' ' + value + '\n';
680 void Preamble::handle_package(Parser &p, string const & name,
681 string const & opts, bool in_lyx_preamble,
684 vector<string> options = split_options(opts);
685 add_package(name, options);
686 char const * const * where = 0;
688 if (is_known(name, known_xetex_packages)) {
690 h_use_non_tex_fonts = true;
691 registerAutomaticallyLoadedPackage("fontspec");
692 if (h_inputencoding == "auto")
693 p.setEncoding("UTF-8");
697 if (is_known(name, known_roman_fonts))
698 h_font_roman[0] = name;
700 if (name == "fourier") {
701 h_font_roman[0] = "utopia";
702 // when font uses real small capitals
703 if (opts == "expert")
707 if (name == "garamondx") {
708 h_font_roman[0] = "garamondx";
713 if (name == "libertine") {
714 h_font_roman[0] = "libertine";
715 // this automatically invokes biolinum
716 h_font_sans[0] = "biolinum";
719 else if (opts == "lining")
720 h_font_osf = "false";
723 if (name == "libertine-type1") {
724 h_font_roman[0] = "libertine";
725 // NOTE: contrary to libertine.sty, libertine-type1
726 // does not automatically invoke biolinum
727 if (opts == "lining")
728 h_font_osf = "false";
729 else if (opts == "osf")
733 if (name == "mathdesign") {
734 if (opts.find("charter") != string::npos)
735 h_font_roman[0] = "md-charter";
736 if (opts.find("garamond") != string::npos)
737 h_font_roman[0] = "md-garamond";
738 if (opts.find("utopia") != string::npos)
739 h_font_roman[0] = "md-utopia";
740 if (opts.find("expert") != string::npos) {
746 else if (name == "mathpazo")
747 h_font_roman[0] = "palatino";
749 else if (name == "mathptmx")
750 h_font_roman[0] = "times";
752 if (name == "crimson")
753 h_font_roman[0] = "cochineal";
755 if (name == "cochineal") {
756 h_font_roman[0] = "cochineal";
757 // cochineal can have several options, e.g. [proportional,osf]
758 string::size_type pos = opts.find("osf");
759 if (pos != string::npos)
764 if (is_known(name, known_sans_fonts)) {
765 h_font_sans[0] = name;
766 if (options.size() >= 1) {
767 if (scale_as_percentage(opts, h_font_sf_scale[0]))
772 if (name == "biolinum-type1") {
773 h_font_sans[0] = "biolinum";
774 // biolinum can have several options, e.g. [osf,scaled=0.97]
775 string::size_type pos = opts.find("osf");
776 if (pos != string::npos)
781 if (is_known(name, known_typewriter_fonts)) {
782 // fourier can be set as roman font _only_
783 // fourier as typewriter is handled in handling of \ttdefault
784 if (name != "fourier") {
785 h_font_typewriter[0] = name;
786 if (options.size() >= 1) {
787 if (scale_as_percentage(opts, h_font_tt_scale[0]))
793 if (name == "libertineMono-type1") {
794 h_font_typewriter[0] = "libertine-mono";
797 // font uses old-style figure
802 if (is_known(name, known_math_fonts))
803 h_font_math[0] = name;
805 if (name == "newtxmath") {
807 h_font_math[0] = "newtxmath";
808 else if (opts == "garamondx")
809 h_font_math[0] = "garamondx-ntxm";
810 else if (opts == "libertine")
811 h_font_math[0] = "libertine-ntxm";
812 else if (opts == "minion")
813 h_font_math[0] = "minion-ntxm";
814 else if (opts == "cochineal")
815 h_font_math[0] = "cochineal-ntxm";
820 h_font_math[0] = "iwona-math";
822 if (name == "kurier")
824 h_font_math[0] = "kurier-math";
826 // after the detection and handling of special cases, we can remove the
827 // fonts, otherwise they would appear in the preamble, see bug #7856
828 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
829 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
831 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
832 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
833 name == "esint" || name == "mhchem" || name == "mathdots" ||
834 name == "mathtools" || name == "stackrel" ||
835 name == "stmaryrd" || name == "undertilde")
836 h_use_packages[name] = "2";
838 else if (name == "babel") {
839 h_language_package = "default";
840 // One might think we would have to do nothing if babel is loaded
841 // without any options to prevent pollution of the preamble with this
842 // babel call in every roundtrip.
843 // But the user could have defined babel-specific things afterwards. So
844 // we need to keep it in the preamble to prevent cases like bug #7861.
846 // check if more than one option was used - used later for inputenc
847 if (options.begin() != options.end() - 1)
848 one_language = false;
849 // babel takes the last language of the option of its \usepackage
850 // call as document language. If there is no such language option, the
851 // last language in the documentclass options is used.
852 handle_opt(options, known_languages, h_language);
853 // translate the babel name to a LyX name
854 h_language = babel2lyx(h_language);
855 if (h_language == "japanese") {
856 // For Japanese, the encoding isn't indicated in the source
857 // file, and there's really not much we can do. We could
858 // 1) offer a list of possible encodings to choose from, or
859 // 2) determine the encoding of the file by inspecting it.
860 // For the time being, we leave the encoding alone so that
861 // we don't get iconv errors when making a wrong guess, and
862 // we will output a note at the top of the document
863 // explaining what to do.
864 Encoding const * const enc = encodings.fromIconvName(
865 p.getEncoding(), Encoding::japanese, false);
867 h_inputencoding = enc->name();
868 is_nonCJKJapanese = true;
869 // in this case babel can be removed from the preamble
870 registerAutomaticallyLoadedPackage("babel");
872 // If babel is called with options, LyX puts them by default into the
873 // document class options. This works for most languages, except
874 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
875 // perhaps in future others.
876 // Therefore keep the babel call as it is as the user might have
878 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
880 delete_opt(options, known_languages);
882 h_preamble << "\\usepackage{babel}\n";
883 explicit_babel = true;
887 else if (name == "polyglossia") {
888 h_language_package = "default";
889 h_default_output_format = "pdf4";
890 h_use_non_tex_fonts = true;
892 registerAutomaticallyLoadedPackage("xunicode");
893 if (h_inputencoding == "auto")
894 p.setEncoding("UTF-8");
897 else if (name == "CJK") {
898 // set the encoding to "auto" because it might be set to "default" by the babel handling
899 // and this would not be correct for CJK
900 if (h_inputencoding == "default")
901 h_inputencoding = "auto";
902 registerAutomaticallyLoadedPackage("CJK");
905 else if (name == "CJKutf8") {
906 h_inputencoding = "utf8-cjk";
907 p.setEncoding("UTF-8");
908 registerAutomaticallyLoadedPackage("CJKutf8");
911 else if (name == "fontenc") {
912 h_fontencoding = getStringFromVector(options, ",");
913 /* We could do the following for better round trip support,
914 * but this makes the document less portable, so I skip it:
915 if (h_fontencoding == lyxrc.fontenc)
916 h_fontencoding = "global";
921 else if (name == "inputenc" || name == "luainputenc") {
922 // h_inputencoding is only set when there is not more than one
923 // inputenc option because otherwise h_inputencoding must be
924 // set to "auto" (the default encoding of the document language)
925 // Therefore check that exactly one option is passed to inputenc.
926 // It is also only set when there is not more than one babel
928 if (!options.empty()) {
929 string const encoding = options.back();
930 Encoding const * const enc = encodings.fromLaTeXName(
931 encoding, Encoding::inputenc, true);
934 cerr << "Unknown encoding " << encoding
935 << ". Ignoring." << std::endl;
937 if (!enc->unsafe() && options.size() == 1 && one_language == true)
938 h_inputencoding = enc->name();
939 p.setEncoding(enc->iconvName());
945 else if (name == "srcltx") {
948 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
951 h_output_sync_macro = "\\usepackage{srcltx}";
954 else if (is_known(name, known_old_language_packages)) {
955 // known language packages from the times before babel
956 // if they are found and not also babel, they will be used as
957 // custom language package
958 h_language_package = "\\usepackage{" + name + "}";
961 else if (name == "lyxskak") {
962 // ignore this and its options
963 const char * const o[] = {"ps", "mover", 0};
964 delete_opt(options, o);
967 else if (is_known(name, known_lyx_packages) && options.empty()) {
968 if (name == "splitidx")
969 h_use_indices = "true";
970 if (name == "refstyle")
971 h_use_refstyle = true;
972 else if (name == "prettyref")
973 h_use_refstyle = false;
974 if (!in_lyx_preamble) {
975 h_preamble << package_beg_sep << name
976 << package_mid_sep << "\\usepackage{"
978 if (p.next_token().cat() == catNewline ||
979 (p.next_token().cat() == catSpace &&
980 p.next_next_token().cat() == catNewline))
982 h_preamble << package_end_sep;
986 else if (name == "geometry")
987 handle_geometry(options);
989 else if (name == "subfig")
990 ; // ignore this FIXME: Use the package separator mechanism instead
992 else if ((where = is_known(name, known_languages)))
993 h_language = known_coded_languages[where - known_languages];
995 else if (name == "natbib") {
996 h_biblio_style = "plainnat";
997 h_cite_engine = "natbib";
998 h_cite_engine_type = "authoryear";
999 vector<string>::iterator it =
1000 find(options.begin(), options.end(), "authoryear");
1001 if (it != options.end())
1004 it = find(options.begin(), options.end(), "numbers");
1005 if (it != options.end()) {
1006 h_cite_engine_type = "numerical";
1012 else if (name == "jurabib") {
1013 h_biblio_style = "jurabib";
1014 h_cite_engine = "jurabib";
1015 h_cite_engine_type = "authoryear";
1018 else if (name == "bibtopic")
1019 h_use_bibtopic = "true";
1021 else if (name == "hyperref")
1022 handle_hyperref(options);
1024 else if (name == "algorithm2e") {
1025 // Load "algorithm2e" module
1026 addModule("algorithm2e");
1027 // Add the package options to the global document options
1028 if (!options.empty()) {
1029 if (h_options.empty())
1030 h_options = join(options, ",");
1032 h_options += ',' + join(options, ",");
1035 else if (name == "microtype") {
1036 //we internally support only microtype without params
1037 if (options.empty())
1038 h_use_microtype = true;
1040 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1043 else if (!in_lyx_preamble) {
1044 if (options.empty())
1045 h_preamble << "\\usepackage{" << name << '}';
1047 h_preamble << "\\usepackage[" << opts << "]{"
1051 if (p.next_token().cat() == catNewline ||
1052 (p.next_token().cat() == catSpace &&
1053 p.next_next_token().cat() == catNewline))
1057 // We need to do something with the options...
1058 if (!options.empty() && !detectEncoding)
1059 cerr << "Ignoring options '" << join(options, ",")
1060 << "' of package " << name << '.' << endl;
1062 // remove the whitespace
1067 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1070 Token t = p.get_token();
1071 if (t.cat() == catEscape &&
1072 is_known(t.cs(), known_if_commands))
1073 handle_if(p, in_lyx_preamble);
1075 if (!in_lyx_preamble)
1076 h_preamble << t.asInput();
1077 if (t.cat() == catEscape && t.cs() == "fi")
1084 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1086 // set the quote language
1087 // LyX only knows the following quotes languages:
1088 // english, swedish, german, polish, french and danish
1089 // (quotes for "japanese" and "chinese-traditional" are missing because
1090 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
1091 // conversion list taken from
1092 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1093 // (quotes for kazakh and interlingua are unknown)
1095 if (is_known(h_language, known_danish_quotes_languages))
1096 h_quotes_style = "danish";
1098 else if (is_known(h_language, known_french_quotes_languages))
1099 h_quotes_style = "french";
1101 else if (is_known(h_language, known_german_quotes_languages))
1102 h_quotes_style = "german";
1104 else if (is_known(h_language, known_polish_quotes_languages))
1105 h_quotes_style = "polish";
1107 else if (is_known(h_language, known_swedish_quotes_languages))
1108 h_quotes_style = "swedish";
1110 else if (is_known(h_language, known_english_quotes_languages))
1111 h_quotes_style = "english";
1113 if (contains(h_float_placement, "H"))
1114 registerAutomaticallyLoadedPackage("float");
1115 if (h_spacing != "single" && h_spacing != "default")
1116 registerAutomaticallyLoadedPackage("setspace");
1117 if (h_use_packages["amsmath"] == "2") {
1118 // amsbsy and amstext are already provided by amsmath
1119 registerAutomaticallyLoadedPackage("amsbsy");
1120 registerAutomaticallyLoadedPackage("amstext");
1123 // output the LyX file settings
1124 // Important: Keep the version formatting in sync with LyX and
1125 // lyx2lyx (bug 7951)
1126 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1127 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1128 << lyx_version_minor << '\n'
1129 << "\\lyxformat " << LYX_FORMAT << '\n'
1130 << "\\begin_document\n"
1131 << "\\begin_header\n"
1132 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1133 << "\\origin " << origin << "\n"
1134 << "\\textclass " << h_textclass << "\n";
1135 string const raw = subdoc ? empty_string() : h_preamble.str();
1137 os << "\\begin_preamble\n";
1138 for (string::size_type i = 0; i < raw.size(); ++i) {
1139 if (raw[i] == package_beg_sep) {
1140 // Here follows some package loading code that
1141 // must be skipped if the package is loaded
1143 string::size_type j = raw.find(package_mid_sep, i);
1144 if (j == string::npos)
1146 string::size_type k = raw.find(package_end_sep, j);
1147 if (k == string::npos)
1149 string const package = raw.substr(i + 1, j - i - 1);
1150 string const replacement = raw.substr(j + 1, k - j - 1);
1151 if (auto_packages.find(package) == auto_packages.end())
1157 os << "\n\\end_preamble\n";
1159 if (!h_options.empty())
1160 os << "\\options " << h_options << "\n";
1161 os << "\\use_default_options " << h_use_default_options << "\n";
1162 if (!used_modules.empty()) {
1163 os << "\\begin_modules\n";
1164 vector<string>::const_iterator const end = used_modules.end();
1165 vector<string>::const_iterator it = used_modules.begin();
1166 for (; it != end; ++it)
1168 os << "\\end_modules\n";
1170 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1171 << "\\language " << h_language << "\n"
1172 << "\\language_package " << h_language_package << "\n"
1173 << "\\inputencoding " << h_inputencoding << "\n"
1174 << "\\fontencoding " << h_fontencoding << "\n"
1175 << "\\font_roman \"" << h_font_roman[0]
1176 << "\" \"" << h_font_roman[1] << "\"\n"
1177 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1178 << "\\font_typewriter \"" << h_font_typewriter[0]
1179 << "\" \"" << h_font_typewriter[1] << "\"\n"
1180 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1181 << "\\font_default_family " << h_font_default_family << "\n"
1182 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1183 << "\\font_sc " << h_font_sc << "\n"
1184 << "\\font_osf " << h_font_osf << "\n"
1185 << "\\font_sf_scale " << h_font_sf_scale[0]
1186 << ' ' << h_font_sf_scale[1] << '\n'
1187 << "\\font_tt_scale " << h_font_tt_scale[0]
1188 << ' ' << h_font_tt_scale[1] << '\n';
1189 if (!h_font_cjk.empty())
1190 os << "\\font_cjk " << h_font_cjk << '\n';
1191 os << "\\use_microtype " << h_use_microtype << '\n'
1192 << "\\graphics " << h_graphics << '\n'
1193 << "\\default_output_format " << h_default_output_format << "\n"
1194 << "\\output_sync " << h_output_sync << "\n";
1195 if (h_output_sync == "1")
1196 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1197 os << "\\bibtex_command " << h_bibtex_command << "\n"
1198 << "\\index_command " << h_index_command << "\n";
1199 if (!h_float_placement.empty())
1200 os << "\\float_placement " << h_float_placement << "\n";
1201 os << "\\paperfontsize " << h_paperfontsize << "\n"
1202 << "\\spacing " << h_spacing << "\n"
1203 << "\\use_hyperref " << h_use_hyperref << '\n';
1204 if (h_use_hyperref == "true") {
1205 if (!h_pdf_title.empty())
1206 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1207 if (!h_pdf_author.empty())
1208 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1209 if (!h_pdf_subject.empty())
1210 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1211 if (!h_pdf_keywords.empty())
1212 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1213 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1214 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1215 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1216 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1217 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1218 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1219 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1220 "\\pdf_backref " << h_pdf_backref << "\n"
1221 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1222 if (!h_pdf_pagemode.empty())
1223 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1224 if (!h_pdf_quoted_options.empty())
1225 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1227 os << "\\papersize " << h_papersize << "\n"
1228 << "\\use_geometry " << h_use_geometry << '\n';
1229 for (map<string, string>::const_iterator it = h_use_packages.begin();
1230 it != h_use_packages.end(); ++it)
1231 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1232 os << "\\cite_engine " << h_cite_engine << '\n'
1233 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1234 << "\\biblio_style " << h_biblio_style << "\n"
1235 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1236 << "\\use_indices " << h_use_indices << "\n"
1237 << "\\paperorientation " << h_paperorientation << '\n'
1238 << "\\suppress_date " << h_suppress_date << '\n'
1239 << "\\justification " << h_justification << '\n'
1240 << "\\use_refstyle " << h_use_refstyle << '\n';
1241 if (!h_fontcolor.empty())
1242 os << "\\fontcolor " << h_fontcolor << '\n';
1243 if (!h_notefontcolor.empty())
1244 os << "\\notefontcolor " << h_notefontcolor << '\n';
1245 if (!h_backgroundcolor.empty())
1246 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1247 if (!h_boxbgcolor.empty())
1248 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1249 if (index_number != 0)
1250 for (int i = 0; i < index_number; i++) {
1251 os << "\\index " << h_index[i] << '\n'
1252 << "\\shortcut " << h_shortcut[i] << '\n'
1253 << "\\color " << h_color << '\n'
1257 os << "\\index " << h_index[0] << '\n'
1258 << "\\shortcut " << h_shortcut[0] << '\n'
1259 << "\\color " << h_color << '\n'
1263 << "\\secnumdepth " << h_secnumdepth << "\n"
1264 << "\\tocdepth " << h_tocdepth << "\n"
1265 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1266 if (h_paragraph_separation == "skip")
1267 os << "\\defskip " << h_defskip << "\n";
1269 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1270 os << "\\quotes_style " << h_quotes_style << "\n"
1271 << "\\papercolumns " << h_papercolumns << "\n"
1272 << "\\papersides " << h_papersides << "\n"
1273 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1274 if (!h_listings_params.empty())
1275 os << "\\listings_params " << h_listings_params << "\n";
1276 os << "\\tracking_changes " << h_tracking_changes << "\n"
1277 << "\\output_changes " << h_output_changes << "\n"
1278 << "\\html_math_output " << h_html_math_output << "\n"
1279 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1280 << "\\html_be_strict " << h_html_be_strict << "\n"
1282 << "\\end_header\n\n"
1283 << "\\begin_body\n";
1288 void Preamble::parse(Parser & p, string const & forceclass,
1289 TeX2LyXDocClass & tc)
1291 // initialize fixed types
1292 special_columns_['D'] = 3;
1293 parse(p, forceclass, false, tc);
1297 void Preamble::parse(Parser & p, string const & forceclass,
1298 bool detectEncoding, TeX2LyXDocClass & tc)
1300 bool is_full_document = false;
1301 bool is_lyx_file = false;
1302 bool in_lyx_preamble = false;
1304 // determine whether this is a full document or a fragment for inclusion
1306 Token const & t = p.get_token();
1308 if (t.cat() == catEscape && t.cs() == "documentclass") {
1309 is_full_document = true;
1315 if (detectEncoding && !is_full_document)
1318 while (is_full_document && p.good()) {
1319 if (detectEncoding && h_inputencoding != "auto" &&
1320 h_inputencoding != "default")
1323 Token const & t = p.get_token();
1326 if (!detectEncoding)
1327 cerr << "t: " << t << '\n';
1333 if (!in_lyx_preamble &&
1334 (t.cat() == catLetter ||
1335 t.cat() == catSuper ||
1336 t.cat() == catSub ||
1337 t.cat() == catOther ||
1338 t.cat() == catMath ||
1339 t.cat() == catActive ||
1340 t.cat() == catBegin ||
1341 t.cat() == catEnd ||
1342 t.cat() == catAlign ||
1343 t.cat() == catParameter))
1344 h_preamble << t.cs();
1346 else if (!in_lyx_preamble &&
1347 (t.cat() == catSpace || t.cat() == catNewline))
1348 h_preamble << t.asInput();
1350 else if (t.cat() == catComment) {
1351 static regex const islyxfile("%% LyX .* created this file");
1352 static regex const usercommands("User specified LaTeX commands");
1354 string const comment = t.asInput();
1356 // magically switch encoding default if it looks like XeLaTeX
1357 static string const magicXeLaTeX =
1358 "% This document must be compiled with XeLaTeX ";
1359 if (comment.size() > magicXeLaTeX.size()
1360 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1361 && h_inputencoding == "auto") {
1362 if (!detectEncoding)
1363 cerr << "XeLaTeX comment found, switching to UTF8\n";
1364 h_inputencoding = "utf8";
1367 if (regex_search(comment, sub, islyxfile)) {
1369 in_lyx_preamble = true;
1370 } else if (is_lyx_file
1371 && regex_search(comment, sub, usercommands))
1372 in_lyx_preamble = false;
1373 else if (!in_lyx_preamble)
1374 h_preamble << t.asInput();
1377 else if (t.cs() == "pagestyle")
1378 h_paperpagestyle = p.verbatim_item();
1380 else if (t.cs() == "setdefaultlanguage") {
1382 // We don't yet care about non-language variant options
1383 // because LyX doesn't support this yet, see bug #8214
1385 string langopts = p.getOpt();
1386 // check if the option contains a variant, if yes, extract it
1387 string::size_type pos_var = langopts.find("variant");
1388 string::size_type i = langopts.find(',', pos_var);
1389 string::size_type k = langopts.find('=', pos_var);
1390 if (pos_var != string::npos){
1392 if (i == string::npos)
1393 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1395 variant = langopts.substr(k + 1, i - k - 1);
1396 h_language = variant;
1400 h_language = p.verbatim_item();
1401 //finally translate the poyglossia name to a LyX name
1402 h_language = polyglossia2lyx(h_language);
1405 else if (t.cs() == "setotherlanguage") {
1406 // We don't yet care about the option because LyX doesn't
1407 // support this yet, see bug #8214
1408 p.hasOpt() ? p.getOpt() : string();
1412 else if (t.cs() == "setmainfont") {
1413 // we don't care about the option
1414 p.hasOpt() ? p.getOpt() : string();
1415 h_font_roman[1] = p.getArg('{', '}');
1418 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1419 // LyX currently only supports the scale option
1422 string fontopts = p.getArg('[', ']');
1423 // check if the option contains a scaling, if yes, extract it
1424 string::size_type pos = fontopts.find("Scale");
1425 if (pos != string::npos) {
1426 string::size_type i = fontopts.find(',', pos);
1427 if (i == string::npos)
1428 scale_as_percentage(fontopts.substr(pos + 1), scale);
1430 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1433 if (t.cs() == "setsansfont") {
1435 h_font_sf_scale[1] = scale;
1436 h_font_sans[1] = p.getArg('{', '}');
1439 h_font_tt_scale[1] = scale;
1440 h_font_typewriter[1] = p.getArg('{', '}');
1444 else if (t.cs() == "date") {
1445 string argument = p.getArg('{', '}');
1446 if (argument.empty())
1447 h_suppress_date = "true";
1449 h_preamble << t.asInput() << '{' << argument << '}';
1452 else if (t.cs() == "color") {
1453 string const space =
1454 (p.hasOpt() ? p.getOpt() : string());
1455 string argument = p.getArg('{', '}');
1456 // check the case that a standard color is used
1457 if (space.empty() && is_known(argument, known_basic_colors)) {
1458 h_fontcolor = rgbcolor2code(argument);
1459 registerAutomaticallyLoadedPackage("color");
1460 } else if (space.empty() && argument == "document_fontcolor")
1461 registerAutomaticallyLoadedPackage("color");
1462 // check the case that LyX's document_fontcolor is defined
1463 // but not used for \color
1465 h_preamble << t.asInput();
1467 h_preamble << space;
1468 h_preamble << '{' << argument << '}';
1469 // the color might already be set because \definecolor
1470 // is parsed before this
1475 else if (t.cs() == "pagecolor") {
1476 string argument = p.getArg('{', '}');
1477 // check the case that a standard color is used
1478 if (is_known(argument, known_basic_colors)) {
1479 h_backgroundcolor = rgbcolor2code(argument);
1480 } else if (argument == "page_backgroundcolor")
1481 registerAutomaticallyLoadedPackage("color");
1482 // check the case that LyX's page_backgroundcolor is defined
1483 // but not used for \pagecolor
1485 h_preamble << t.asInput() << '{' << argument << '}';
1486 // the color might already be set because \definecolor
1487 // is parsed before this
1488 h_backgroundcolor = "";
1492 else if (t.cs() == "makeatletter") {
1493 // LyX takes care of this
1494 p.setCatcode('@', catLetter);
1497 else if (t.cs() == "makeatother") {
1498 // LyX takes care of this
1499 p.setCatcode('@', catOther);
1502 else if (t.cs() == "makeindex") {
1503 // LyX will re-add this if a print index command is found
1507 else if (t.cs() == "newindex") {
1508 string const indexname = p.getArg('[', ']');
1509 string const shortcut = p.verbatim_item();
1510 if (!indexname.empty())
1511 h_index[index_number] = indexname;
1513 h_index[index_number] = shortcut;
1514 h_shortcut[index_number] = shortcut;
1519 else if (t.cs() == "RS@ifundefined") {
1520 string const name = p.verbatim_item();
1521 string const body1 = p.verbatim_item();
1522 string const body2 = p.verbatim_item();
1523 // only non-lyxspecific stuff
1524 if (in_lyx_preamble &&
1525 (name == "subsecref" || name == "thmref" || name == "lemref"))
1529 ss << '\\' << t.cs();
1530 ss << '{' << name << '}'
1531 << '{' << body1 << '}'
1532 << '{' << body2 << '}';
1533 h_preamble << ss.str();
1537 else if (t.cs() == "AtBeginDocument") {
1538 string const name = p.verbatim_item();
1539 // only non-lyxspecific stuff
1540 if (in_lyx_preamble &&
1541 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1542 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1543 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1544 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1545 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1546 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1547 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1548 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1549 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1550 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1551 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1552 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1553 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1554 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1555 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1559 ss << '\\' << t.cs();
1560 ss << '{' << name << '}';
1561 h_preamble << ss.str();
1565 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1566 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1567 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1568 || t.cs() == "DeclareRobustCommand"
1569 || t.cs() == "DeclareRobustCommandx"
1570 || t.cs() == "ProvideTextCommandDefault"
1571 || t.cs() == "DeclareMathAccent") {
1573 if (p.next_token().character() == '*') {
1577 string const name = p.verbatim_item();
1578 string const opt1 = p.getFullOpt();
1579 string const opt2 = p.getFullOpt();
1580 string const body = p.verbatim_item();
1581 // store the in_lyx_preamble setting
1582 bool const was_in_lyx_preamble = in_lyx_preamble;
1584 if (name == "\\rmdefault")
1585 if (is_known(body, known_roman_fonts)) {
1586 h_font_roman[0] = body;
1588 in_lyx_preamble = true;
1590 if (name == "\\sfdefault")
1591 if (is_known(body, known_sans_fonts)) {
1592 h_font_sans[0] = body;
1594 in_lyx_preamble = true;
1596 if (name == "\\ttdefault")
1597 if (is_known(body, known_typewriter_fonts)) {
1598 h_font_typewriter[0] = body;
1600 in_lyx_preamble = true;
1602 if (name == "\\familydefault") {
1603 string family = body;
1604 // remove leading "\"
1605 h_font_default_family = family.erase(0,1);
1607 in_lyx_preamble = true;
1610 // remove LyX-specific definitions that are re-added by LyX
1612 // \lyxline is an ancient command that is converted by tex2lyx into
1613 // a \rule therefore remove its preamble code
1614 if (name == "\\lyxdot" || name == "\\lyxarrow"
1615 || name == "\\lyxline" || name == "\\LyX") {
1617 in_lyx_preamble = true;
1620 // Add the command to the known commands
1621 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1623 // only non-lyxspecific stuff
1624 if (!in_lyx_preamble) {
1626 ss << '\\' << t.cs();
1629 ss << '{' << name << '}' << opt1 << opt2
1630 << '{' << body << "}";
1631 h_preamble << ss.str();
1633 ostream & out = in_preamble ? h_preamble : os;
1634 out << "\\" << t.cs() << "{" << name << "}"
1635 << opts << "{" << body << "}";
1638 // restore the in_lyx_preamble setting
1639 in_lyx_preamble = was_in_lyx_preamble;
1642 else if (t.cs() == "documentclass") {
1643 vector<string>::iterator it;
1644 vector<string> opts = split_options(p.getArg('[', ']'));
1645 handle_opt(opts, known_fontsizes, h_paperfontsize);
1646 delete_opt(opts, known_fontsizes);
1647 // delete "pt" at the end
1648 string::size_type i = h_paperfontsize.find("pt");
1649 if (i != string::npos)
1650 h_paperfontsize.erase(i);
1651 // The documentclass options are always parsed before the options
1652 // of the babel call so that a language cannot overwrite the babel
1654 handle_opt(opts, known_languages, h_language);
1655 delete_opt(opts, known_languages);
1657 // paper orientation
1658 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1659 h_paperorientation = "landscape";
1663 if ((it = find(opts.begin(), opts.end(), "oneside"))
1668 if ((it = find(opts.begin(), opts.end(), "twoside"))
1674 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1676 h_papercolumns = "1";
1679 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1681 h_papercolumns = "2";
1685 // some size options are known to any document classes, other sizes
1686 // are handled by the \geometry command of the geometry package
1687 handle_opt(opts, known_class_paper_sizes, h_papersize);
1688 delete_opt(opts, known_class_paper_sizes);
1689 // the remaining options
1690 h_options = join(opts, ",");
1691 // FIXME This does not work for classes that have a
1692 // different name in LyX than in LaTeX
1693 h_textclass = p.getArg('{', '}');
1697 else if (t.cs() == "usepackage") {
1698 string const options = p.getArg('[', ']');
1699 string const name = p.getArg('{', '}');
1700 vector<string> vecnames;
1701 split(name, vecnames, ',');
1702 vector<string>::const_iterator it = vecnames.begin();
1703 vector<string>::const_iterator end = vecnames.end();
1704 for (; it != end; ++it)
1705 handle_package(p, trimSpaceAndEol(*it), options,
1706 in_lyx_preamble, detectEncoding);
1709 else if (t.cs() == "inputencoding") {
1710 string const encoding = p.getArg('{','}');
1711 Encoding const * const enc = encodings.fromLaTeXName(
1712 encoding, Encoding::inputenc, true);
1714 if (!detectEncoding)
1715 cerr << "Unknown encoding " << encoding
1716 << ". Ignoring." << std::endl;
1719 h_inputencoding = enc->name();
1720 p.setEncoding(enc->iconvName());
1724 else if (t.cs() == "newenvironment") {
1725 string const name = p.getArg('{', '}');
1726 string const opt1 = p.getFullOpt();
1727 string const opt2 = p.getFullOpt();
1728 string const beg = p.verbatim_item();
1729 string const end = p.verbatim_item();
1730 if (!in_lyx_preamble) {
1731 h_preamble << "\\newenvironment{" << name
1732 << '}' << opt1 << opt2 << '{'
1733 << beg << "}{" << end << '}';
1735 add_known_environment(name, opt1, !opt2.empty(),
1736 from_utf8(beg), from_utf8(end));
1740 else if (t.cs() == "newtheorem") {
1741 string const name = p.getArg('{', '}');
1742 string const opt1 = p.getFullOpt();
1743 string const opt2 = p.getFullOpt();
1744 string const body = p.verbatim_item();
1745 string const opt3 = p.getFullOpt();
1747 string const complete = "\\newtheorem{" + name + '}' +
1748 opt1 + opt2 + '{' + body + '}' + opt3;
1750 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1752 if (!in_lyx_preamble)
1753 h_preamble << complete;
1756 else if (t.cs() == "def") {
1757 string name = p.get_token().cs();
1758 // In fact, name may be more than the name:
1759 // In the test case of bug 8116
1760 // name == "csname SF@gobble@opt \endcsname".
1761 // Therefore, we need to use asInput() instead of cs().
1762 while (p.next_token().cat() != catBegin)
1763 name += p.get_token().asInput();
1764 if (!in_lyx_preamble)
1765 h_preamble << "\\def\\" << name << '{'
1766 << p.verbatim_item() << "}";
1769 else if (t.cs() == "newcolumntype") {
1770 string const name = p.getArg('{', '}');
1771 trimSpaceAndEol(name);
1773 string opts = p.getOpt();
1774 if (!opts.empty()) {
1775 istringstream is(string(opts, 1));
1778 special_columns_[name[0]] = nargs;
1779 h_preamble << "\\newcolumntype{" << name << "}";
1781 h_preamble << "[" << nargs << "]";
1782 h_preamble << "{" << p.verbatim_item() << "}";
1785 else if (t.cs() == "setcounter") {
1786 string const name = p.getArg('{', '}');
1787 string const content = p.getArg('{', '}');
1788 if (name == "secnumdepth")
1789 h_secnumdepth = content;
1790 else if (name == "tocdepth")
1791 h_tocdepth = content;
1793 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1796 else if (t.cs() == "setlength") {
1797 string const name = p.verbatim_item();
1798 string const content = p.verbatim_item();
1799 // the paragraphs are only not indented when \parindent is set to zero
1800 if (name == "\\parindent" && content != "") {
1801 if (content[0] == '0')
1802 h_paragraph_separation = "skip";
1804 h_paragraph_indentation = translate_len(content);
1805 } else if (name == "\\parskip") {
1806 if (content == "\\smallskipamount")
1807 h_defskip = "smallskip";
1808 else if (content == "\\medskipamount")
1809 h_defskip = "medskip";
1810 else if (content == "\\bigskipamount")
1811 h_defskip = "bigskip";
1813 h_defskip = translate_len(content);
1815 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1818 else if (t.cs() == "onehalfspacing")
1819 h_spacing = "onehalf";
1821 else if (t.cs() == "doublespacing")
1822 h_spacing = "double";
1824 else if (t.cs() == "setstretch")
1825 h_spacing = "other " + p.verbatim_item();
1827 else if (t.cs() == "synctex") {
1828 // the scheme is \synctex=value
1829 // where value can only be "1" or "-1"
1830 h_output_sync = "1";
1831 // there can be any character behind the value (e.g. a linebreak or a '\'
1832 // therefore we extract it char by char
1834 string value = p.get_token().asInput();
1836 value += p.get_token().asInput();
1837 h_output_sync_macro = "\\synctex=" + value;
1840 else if (t.cs() == "begin") {
1841 string const name = p.getArg('{', '}');
1842 if (name == "document")
1844 h_preamble << "\\begin{" << name << "}";
1847 else if (t.cs() == "geometry") {
1848 vector<string> opts = split_options(p.getArg('{', '}'));
1849 handle_geometry(opts);
1852 else if (t.cs() == "definecolor") {
1853 string const color = p.getArg('{', '}');
1854 string const space = p.getArg('{', '}');
1855 string const value = p.getArg('{', '}');
1856 if (color == "document_fontcolor" && space == "rgb") {
1857 RGBColor c(RGBColorFromLaTeX(value));
1858 h_fontcolor = X11hexname(c);
1859 } else if (color == "note_fontcolor" && space == "rgb") {
1860 RGBColor c(RGBColorFromLaTeX(value));
1861 h_notefontcolor = X11hexname(c);
1862 } else if (color == "page_backgroundcolor" && space == "rgb") {
1863 RGBColor c(RGBColorFromLaTeX(value));
1864 h_backgroundcolor = X11hexname(c);
1865 } else if (color == "shadecolor" && space == "rgb") {
1866 RGBColor c(RGBColorFromLaTeX(value));
1867 h_boxbgcolor = X11hexname(c);
1869 h_preamble << "\\definecolor{" << color
1870 << "}{" << space << "}{" << value
1875 else if (t.cs() == "bibliographystyle")
1876 h_biblio_style = p.verbatim_item();
1878 else if (t.cs() == "jurabibsetup") {
1879 // FIXME p.getArg('{', '}') is most probably wrong (it
1880 // does not handle nested braces).
1881 // Use p.verbatim_item() instead.
1882 vector<string> jurabibsetup =
1883 split_options(p.getArg('{', '}'));
1884 // add jurabibsetup to the jurabib package options
1885 add_package("jurabib", jurabibsetup);
1886 if (!jurabibsetup.empty()) {
1887 h_preamble << "\\jurabibsetup{"
1888 << join(jurabibsetup, ",") << '}';
1892 else if (t.cs() == "hypersetup") {
1893 vector<string> hypersetup =
1894 split_options(p.verbatim_item());
1895 // add hypersetup to the hyperref package options
1896 handle_hyperref(hypersetup);
1897 if (!hypersetup.empty()) {
1898 h_preamble << "\\hypersetup{"
1899 << join(hypersetup, ",") << '}';
1903 else if (is_known(t.cs(), known_if_3arg_commands)) {
1904 // prevent misparsing of \usepackage if it is used
1905 // as an argument (see e.g. our own output of
1906 // \@ifundefined above)
1907 string const arg1 = p.verbatim_item();
1908 string const arg2 = p.verbatim_item();
1909 string const arg3 = p.verbatim_item();
1910 // test case \@ifundefined{date}{}{\date{}}
1911 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1912 arg2.empty() && arg3 == "\\date{}") {
1913 h_suppress_date = "true";
1914 // older tex2lyx versions did output
1915 // \@ifundefined{definecolor}{\usepackage{color}}{}
1916 } else if (t.cs() == "@ifundefined" &&
1917 arg1 == "definecolor" &&
1918 arg2 == "\\usepackage{color}" &&
1920 if (!in_lyx_preamble)
1921 h_preamble << package_beg_sep
1924 << "\\@ifundefined{definecolor}{color}{}"
1927 //\@ifundefined{showcaptionsetup}{}{%
1928 // \PassOptionsToPackage{caption=false}{subfig}}
1929 // that LyX uses for subfloats
1930 } else if (t.cs() == "@ifundefined" &&
1931 arg1 == "showcaptionsetup" && arg2.empty()
1932 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1934 } else if (!in_lyx_preamble) {
1935 h_preamble << t.asInput()
1936 << '{' << arg1 << '}'
1937 << '{' << arg2 << '}'
1938 << '{' << arg3 << '}';
1942 else if (is_known(t.cs(), known_if_commands)) {
1943 // must not parse anything in conditional code, since
1944 // LyX would output the parsed contents unconditionally
1945 if (!in_lyx_preamble)
1946 h_preamble << t.asInput();
1947 handle_if(p, in_lyx_preamble);
1950 else if (!t.cs().empty() && !in_lyx_preamble)
1951 h_preamble << '\\' << t.cs();
1954 // remove the whitespace
1957 // Force textclass if the user wanted it
1958 if (!forceclass.empty())
1959 h_textclass = forceclass;
1960 tc.setName(h_textclass);
1962 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1965 if (h_papersides.empty()) {
1968 h_papersides = ss.str();
1971 // If the CJK package is used we cannot set the document language from
1972 // the babel options. Instead, we guess which language is used most
1973 // and set this one.
1974 default_language = h_language;
1975 if (is_full_document &&
1976 (auto_packages.find("CJK") != auto_packages.end() ||
1977 auto_packages.find("CJKutf8") != auto_packages.end())) {
1979 h_language = guessLanguage(p, default_language);
1981 if (explicit_babel && h_language != default_language) {
1982 // We set the document language to a CJK language,
1983 // but babel is explicitly called in the user preamble
1984 // without options. LyX will not add the default
1985 // language to the document options if it is either
1986 // english, or no text is set as default language.
1987 // Therefore we need to add a language option explicitly.
1988 // FIXME: It would be better to remove all babel calls
1989 // from the user preamble, but this is difficult
1990 // without re-introducing bug 7861.
1991 if (h_options.empty())
1992 h_options = lyx2babel(default_language);
1994 h_options += ',' + lyx2babel(default_language);
2000 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2002 TeX2LyXDocClass dummy;
2003 parse(p, forceclass, true, dummy);
2004 if (h_inputencoding != "auto" && h_inputencoding != "default")
2005 return h_inputencoding;
2010 string babel2lyx(string const & language)
2012 char const * const * where = is_known(language, known_languages);
2014 return known_coded_languages[where - known_languages];
2019 string lyx2babel(string const & language)
2021 char const * const * where = is_known(language, known_coded_languages);
2023 return known_languages[where - known_coded_languages];
2028 string Preamble::polyglossia2lyx(string const & language)
2030 char const * const * where = is_known(language, polyglossia_languages);
2032 return coded_polyglossia_languages[where - polyglossia_languages];
2037 string rgbcolor2code(string const & name)
2039 char const * const * where = is_known(name, known_basic_colors);
2041 // "red", "green" etc
2042 return known_basic_color_codes[where - known_basic_colors];
2044 // "255,0,0", "0,255,0" etc
2045 RGBColor c(RGBColorFromLaTeX(name));
2046 return X11hexname(c);