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", "NotoSerif-TLF", "tgbonum", "tgchorus", "tgpagella", "tgschola",
131 "tgtermes", "utopia", 0};
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", "NotoSans-TLF", "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", "NotoMono-TLF", "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";
475 h_dynamic_quotes = false;
478 h_fontencoding = "default";
479 h_font_roman[0] = "default";
480 h_font_roman[1] = "default";
481 h_font_sans[0] = "default";
482 h_font_sans[1] = "default";
483 h_font_typewriter[0] = "default";
484 h_font_typewriter[1] = "default";
485 h_font_math[0] = "auto";
486 h_font_math[1] = "auto";
487 h_font_default_family = "default";
488 h_use_non_tex_fonts = false;
490 h_font_osf = "false";
491 h_font_sf_scale[0] = "100";
492 h_font_sf_scale[1] = "100";
493 h_font_tt_scale[0] = "100";
494 h_font_tt_scale[1] = "100";
496 h_is_mathindent = "0";
497 h_math_numbering_side = "default";
498 h_graphics = "default";
499 h_default_output_format = "default";
500 h_html_be_strict = "false";
501 h_html_css_as_file = "0";
502 h_html_math_output = "0";
503 h_index[0] = "Index";
504 h_index_command = "default";
505 h_inputencoding = "auto";
506 h_justification = "true";
507 h_language = "english";
508 h_language_package = "none";
510 h_maintain_unincluded_children = "false";
514 h_output_changes = "false";
516 //h_output_sync_macro
517 h_papercolumns = "1";
518 h_paperfontsize = "default";
519 h_paperorientation = "portrait";
520 h_paperpagestyle = "default";
522 h_papersize = "default";
523 h_paragraph_indentation = "default";
524 h_paragraph_separation = "indent";
529 h_pdf_bookmarks = "0";
530 h_pdf_bookmarksnumbered = "0";
531 h_pdf_bookmarksopen = "0";
532 h_pdf_bookmarksopenlevel = "1";
533 h_pdf_breaklinks = "0";
534 h_pdf_pdfborder = "0";
535 h_pdf_colorlinks = "0";
536 h_pdf_backref = "section";
537 h_pdf_pdfusetitle = "0";
539 //h_pdf_quoted_options;
540 h_quotes_style = "english";
542 h_shortcut[0] = "idx";
543 h_spacing = "single";
544 h_save_transient_properties = "true";
545 h_suppress_date = "false";
546 h_textclass = "article";
548 h_tracking_changes = "false";
549 h_use_bibtopic = "false";
550 h_use_dash_ligatures = "true";
551 h_use_indices = "false";
552 h_use_geometry = "false";
553 h_use_default_options = "false";
554 h_use_hyperref = "false";
555 h_use_microtype = "false";
556 h_use_refstyle = false;
557 h_use_packages["amsmath"] = "1";
558 h_use_packages["amssymb"] = "0";
559 h_use_packages["cancel"] = "0";
560 h_use_packages["esint"] = "1";
561 h_use_packages["mhchem"] = "0";
562 h_use_packages["mathdots"] = "0";
563 h_use_packages["mathtools"] = "0";
564 h_use_packages["stackrel"] = "0";
565 h_use_packages["stmaryrd"] = "0";
566 h_use_packages["undertilde"] = "0";
570 void Preamble::handle_hyperref(vector<string> & options)
572 // FIXME swallow inputencoding changes that might surround the
573 // hyperref setup if it was written by LyX
574 h_use_hyperref = "true";
575 // swallow "unicode=true", since LyX does always write that
576 vector<string>::iterator it =
577 find(options.begin(), options.end(), "unicode=true");
578 if (it != options.end())
580 it = find(options.begin(), options.end(), "pdfusetitle");
581 if (it != options.end()) {
582 h_pdf_pdfusetitle = "1";
585 string bookmarks = process_keyval_opt(options, "bookmarks");
586 if (bookmarks == "true")
587 h_pdf_bookmarks = "1";
588 else if (bookmarks == "false")
589 h_pdf_bookmarks = "0";
590 if (h_pdf_bookmarks == "1") {
591 string bookmarksnumbered =
592 process_keyval_opt(options, "bookmarksnumbered");
593 if (bookmarksnumbered == "true")
594 h_pdf_bookmarksnumbered = "1";
595 else if (bookmarksnumbered == "false")
596 h_pdf_bookmarksnumbered = "0";
597 string bookmarksopen =
598 process_keyval_opt(options, "bookmarksopen");
599 if (bookmarksopen == "true")
600 h_pdf_bookmarksopen = "1";
601 else if (bookmarksopen == "false")
602 h_pdf_bookmarksopen = "0";
603 if (h_pdf_bookmarksopen == "1") {
604 string bookmarksopenlevel =
605 process_keyval_opt(options, "bookmarksopenlevel");
606 if (!bookmarksopenlevel.empty())
607 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
610 string breaklinks = process_keyval_opt(options, "breaklinks");
611 if (breaklinks == "true")
612 h_pdf_breaklinks = "1";
613 else if (breaklinks == "false")
614 h_pdf_breaklinks = "0";
615 string pdfborder = process_keyval_opt(options, "pdfborder");
616 if (pdfborder == "{0 0 0}")
617 h_pdf_pdfborder = "1";
618 else if (pdfborder == "{0 0 1}")
619 h_pdf_pdfborder = "0";
620 string backref = process_keyval_opt(options, "backref");
621 if (!backref.empty())
622 h_pdf_backref = backref;
623 string colorlinks = process_keyval_opt(options, "colorlinks");
624 if (colorlinks == "true")
625 h_pdf_colorlinks = "1";
626 else if (colorlinks == "false")
627 h_pdf_colorlinks = "0";
628 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
629 if (!pdfpagemode.empty())
630 h_pdf_pagemode = pdfpagemode;
631 string pdftitle = process_keyval_opt(options, "pdftitle");
632 if (!pdftitle.empty()) {
633 h_pdf_title = remove_braces(pdftitle);
635 string pdfauthor = process_keyval_opt(options, "pdfauthor");
636 if (!pdfauthor.empty()) {
637 h_pdf_author = remove_braces(pdfauthor);
639 string pdfsubject = process_keyval_opt(options, "pdfsubject");
640 if (!pdfsubject.empty())
641 h_pdf_subject = remove_braces(pdfsubject);
642 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
643 if (!pdfkeywords.empty())
644 h_pdf_keywords = remove_braces(pdfkeywords);
645 if (!options.empty()) {
646 if (!h_pdf_quoted_options.empty())
647 h_pdf_quoted_options += ',';
648 h_pdf_quoted_options += join(options, ",");
654 void Preamble::handle_geometry(vector<string> & options)
656 h_use_geometry = "true";
657 vector<string>::iterator it;
659 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
660 h_paperorientation = "landscape";
664 // keyval version: "paper=letter"
665 string paper = process_keyval_opt(options, "paper");
667 h_papersize = paper + "paper";
668 // alternative version: "letterpaper"
669 handle_opt(options, known_paper_sizes, h_papersize);
670 delete_opt(options, known_paper_sizes);
672 char const * const * margin = known_paper_margins;
673 for (; *margin; ++margin) {
674 string value = process_keyval_opt(options, *margin);
675 if (!value.empty()) {
676 int k = margin - known_paper_margins;
677 string name = known_coded_paper_margins[k];
678 h_margins += '\\' + name + ' ' + value + '\n';
684 void Preamble::handle_package(Parser &p, string const & name,
685 string const & opts, bool in_lyx_preamble,
688 vector<string> options = split_options(opts);
689 add_package(name, options);
690 char const * const * where = 0;
692 if (is_known(name, known_xetex_packages)) {
694 h_use_non_tex_fonts = true;
695 registerAutomaticallyLoadedPackage("fontspec");
696 if (h_inputencoding == "auto")
697 p.setEncoding("UTF-8");
701 if (is_known(name, known_roman_fonts))
702 h_font_roman[0] = name;
704 if (name == "fourier") {
705 h_font_roman[0] = "utopia";
706 // when font uses real small capitals
707 if (opts == "expert")
711 if (name == "garamondx") {
712 h_font_roman[0] = "garamondx";
717 if (name == "libertine") {
718 h_font_roman[0] = "libertine";
719 // this automatically invokes biolinum
720 h_font_sans[0] = "biolinum";
723 else if (opts == "lining")
724 h_font_osf = "false";
727 if (name == "libertine-type1") {
728 h_font_roman[0] = "libertine";
729 // NOTE: contrary to libertine.sty, libertine-type1
730 // does not automatically invoke biolinum
731 if (opts == "lining")
732 h_font_osf = "false";
733 else if (opts == "osf")
737 if (name == "mathdesign") {
738 if (opts.find("charter") != string::npos)
739 h_font_roman[0] = "md-charter";
740 if (opts.find("garamond") != string::npos)
741 h_font_roman[0] = "md-garamond";
742 if (opts.find("utopia") != string::npos)
743 h_font_roman[0] = "md-utopia";
744 if (opts.find("expert") != string::npos) {
750 else if (name == "mathpazo")
751 h_font_roman[0] = "palatino";
753 else if (name == "mathptmx")
754 h_font_roman[0] = "times";
756 if (name == "crimson")
757 h_font_roman[0] = "cochineal";
759 if (name == "cochineal") {
760 h_font_roman[0] = "cochineal";
761 // cochineal can have several options, e.g. [proportional,osf]
762 string::size_type pos = opts.find("osf");
763 if (pos != string::npos)
767 if (name == "noto") {
768 // noto can have several options
770 h_font_roman[0] = "NotoSerif-TLF";
771 string::size_type pos = opts.find("rm");
772 if (pos != string::npos)
773 h_font_roman[0] = "NotoSerif-TLF";
774 pos = opts.find("sf");
775 if (pos != string::npos)
776 h_font_sans[0] = "NotoSans-TLF";
777 pos = opts.find("nott");
778 if (pos != string::npos) {
779 h_font_roman[0] = "NotoSerif-TLF";
780 h_font_sans[0] = "NotoSans-TLF";
782 // noto as typewriter is handled in handling of \ttdefault
783 // special cases are handled in handling of \rmdefault and \sfdefault
787 if (is_known(name, known_sans_fonts)) {
788 h_font_sans[0] = name;
789 if (options.size() >= 1) {
790 if (scale_as_percentage(opts, h_font_sf_scale[0]))
795 if (name == "biolinum-type1") {
796 h_font_sans[0] = "biolinum";
797 // biolinum can have several options, e.g. [osf,scaled=0.97]
798 string::size_type pos = opts.find("osf");
799 if (pos != string::npos)
804 if (is_known(name, known_typewriter_fonts)) {
805 // fourier can be set as roman font _only_
806 // fourier as typewriter is handled in handling of \ttdefault
807 if (name != "fourier") {
808 h_font_typewriter[0] = name;
809 if (options.size() >= 1) {
810 if (scale_as_percentage(opts, h_font_tt_scale[0]))
816 if (name == "libertineMono-type1") {
817 h_font_typewriter[0] = "libertine-mono";
820 // font uses old-style figure
825 if (is_known(name, known_math_fonts))
826 h_font_math[0] = name;
828 if (name == "newtxmath") {
830 h_font_math[0] = "newtxmath";
831 else if (opts == "garamondx")
832 h_font_math[0] = "garamondx-ntxm";
833 else if (opts == "libertine")
834 h_font_math[0] = "libertine-ntxm";
835 else if (opts == "minion")
836 h_font_math[0] = "minion-ntxm";
837 else if (opts == "cochineal")
838 h_font_math[0] = "cochineal-ntxm";
843 h_font_math[0] = "iwona-math";
845 if (name == "kurier")
847 h_font_math[0] = "kurier-math";
849 // after the detection and handling of special cases, we can remove the
850 // fonts, otherwise they would appear in the preamble, see bug #7856
851 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
852 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
854 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
855 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
856 name == "esint" || name == "mhchem" || name == "mathdots" ||
857 name == "mathtools" || name == "stackrel" ||
858 name == "stmaryrd" || name == "undertilde")
859 h_use_packages[name] = "2";
861 else if (name == "babel") {
862 h_language_package = "default";
863 // One might think we would have to do nothing if babel is loaded
864 // without any options to prevent pollution of the preamble with this
865 // babel call in every roundtrip.
866 // But the user could have defined babel-specific things afterwards. So
867 // we need to keep it in the preamble to prevent cases like bug #7861.
869 // check if more than one option was used - used later for inputenc
870 if (options.begin() != options.end() - 1)
871 one_language = false;
872 // babel takes the last language of the option of its \usepackage
873 // call as document language. If there is no such language option, the
874 // last language in the documentclass options is used.
875 handle_opt(options, known_languages, h_language);
876 // translate the babel name to a LyX name
877 h_language = babel2lyx(h_language);
878 if (h_language == "japanese") {
879 // For Japanese, the encoding isn't indicated in the source
880 // file, and there's really not much we can do. We could
881 // 1) offer a list of possible encodings to choose from, or
882 // 2) determine the encoding of the file by inspecting it.
883 // For the time being, we leave the encoding alone so that
884 // we don't get iconv errors when making a wrong guess, and
885 // we will output a note at the top of the document
886 // explaining what to do.
887 Encoding const * const enc = encodings.fromIconvName(
888 p.getEncoding(), Encoding::japanese, false);
890 h_inputencoding = enc->name();
891 is_nonCJKJapanese = true;
892 // in this case babel can be removed from the preamble
893 registerAutomaticallyLoadedPackage("babel");
895 // If babel is called with options, LyX puts them by default into the
896 // document class options. This works for most languages, except
897 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
898 // perhaps in future others.
899 // Therefore keep the babel call as it is as the user might have
901 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
903 delete_opt(options, known_languages);
905 h_preamble << "\\usepackage{babel}\n";
906 explicit_babel = true;
910 else if (name == "polyglossia") {
911 h_language_package = "default";
912 h_default_output_format = "pdf4";
913 h_use_non_tex_fonts = true;
915 registerAutomaticallyLoadedPackage("xunicode");
916 if (h_inputencoding == "auto")
917 p.setEncoding("UTF-8");
920 else if (name == "CJK") {
921 // set the encoding to "auto" because it might be set to "default" by the babel handling
922 // and this would not be correct for CJK
923 if (h_inputencoding == "default")
924 h_inputencoding = "auto";
925 registerAutomaticallyLoadedPackage("CJK");
928 else if (name == "CJKutf8") {
929 h_inputencoding = "utf8-cjk";
930 p.setEncoding("UTF-8");
931 registerAutomaticallyLoadedPackage("CJKutf8");
934 else if (name == "fontenc") {
935 h_fontencoding = getStringFromVector(options, ",");
936 /* We could do the following for better round trip support,
937 * but this makes the document less portable, so I skip it:
938 if (h_fontencoding == lyxrc.fontenc)
939 h_fontencoding = "global";
944 else if (name == "inputenc" || name == "luainputenc") {
945 // h_inputencoding is only set when there is not more than one
946 // inputenc option because otherwise h_inputencoding must be
947 // set to "auto" (the default encoding of the document language)
948 // Therefore check that exactly one option is passed to inputenc.
949 // It is also only set when there is not more than one babel
951 if (!options.empty()) {
952 string const encoding = options.back();
953 Encoding const * const enc = encodings.fromLaTeXName(
954 encoding, Encoding::inputenc, true);
957 cerr << "Unknown encoding " << encoding
958 << ". Ignoring." << std::endl;
960 if (!enc->unsafe() && options.size() == 1 && one_language == true)
961 h_inputencoding = enc->name();
962 p.setEncoding(enc->iconvName());
968 else if (name == "srcltx") {
971 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
974 h_output_sync_macro = "\\usepackage{srcltx}";
977 else if (is_known(name, known_old_language_packages)) {
978 // known language packages from the times before babel
979 // if they are found and not also babel, they will be used as
980 // custom language package
981 h_language_package = "\\usepackage{" + name + "}";
984 else if (name == "lyxskak") {
985 // ignore this and its options
986 const char * const o[] = {"ps", "mover", 0};
987 delete_opt(options, o);
990 else if (is_known(name, known_lyx_packages) && options.empty()) {
991 if (name == "splitidx")
992 h_use_indices = "true";
993 if (name == "refstyle")
994 h_use_refstyle = true;
995 else if (name == "prettyref")
996 h_use_refstyle = false;
997 if (!in_lyx_preamble) {
998 h_preamble << package_beg_sep << name
999 << package_mid_sep << "\\usepackage{"
1001 if (p.next_token().cat() == catNewline ||
1002 (p.next_token().cat() == catSpace &&
1003 p.next_next_token().cat() == catNewline))
1005 h_preamble << package_end_sep;
1009 else if (name == "geometry")
1010 handle_geometry(options);
1012 else if (name == "subfig")
1013 ; // ignore this FIXME: Use the package separator mechanism instead
1015 else if ((where = is_known(name, known_languages)))
1016 h_language = known_coded_languages[where - known_languages];
1018 else if (name == "natbib") {
1019 h_biblio_style = "plainnat";
1020 h_cite_engine = "natbib";
1021 h_cite_engine_type = "authoryear";
1022 vector<string>::iterator it =
1023 find(options.begin(), options.end(), "authoryear");
1024 if (it != options.end())
1027 it = find(options.begin(), options.end(), "numbers");
1028 if (it != options.end()) {
1029 h_cite_engine_type = "numerical";
1035 else if (name == "jurabib") {
1036 h_biblio_style = "jurabib";
1037 h_cite_engine = "jurabib";
1038 h_cite_engine_type = "authoryear";
1041 else if (name == "bibtopic")
1042 h_use_bibtopic = "true";
1044 else if (name == "hyperref")
1045 handle_hyperref(options);
1047 else if (name == "algorithm2e") {
1048 // Load "algorithm2e" module
1049 addModule("algorithm2e");
1050 // Add the package options to the global document options
1051 if (!options.empty()) {
1052 if (h_options.empty())
1053 h_options = join(options, ",");
1055 h_options += ',' + join(options, ",");
1058 else if (name == "microtype") {
1059 //we internally support only microtype without params
1060 if (options.empty())
1061 h_use_microtype = "true";
1063 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1066 else if (!in_lyx_preamble) {
1067 if (options.empty())
1068 h_preamble << "\\usepackage{" << name << '}';
1070 h_preamble << "\\usepackage[" << opts << "]{"
1074 if (p.next_token().cat() == catNewline ||
1075 (p.next_token().cat() == catSpace &&
1076 p.next_next_token().cat() == catNewline))
1080 // We need to do something with the options...
1081 if (!options.empty() && !detectEncoding)
1082 cerr << "Ignoring options '" << join(options, ",")
1083 << "' of package " << name << '.' << endl;
1085 // remove the whitespace
1090 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1093 Token t = p.get_token();
1094 if (t.cat() == catEscape &&
1095 is_known(t.cs(), known_if_commands))
1096 handle_if(p, in_lyx_preamble);
1098 if (!in_lyx_preamble)
1099 h_preamble << t.asInput();
1100 if (t.cat() == catEscape && t.cs() == "fi")
1107 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1109 // set the quote language
1110 // LyX only knows the following quotes languages:
1111 // english, swedish, german, polish, french and danish
1112 // (quotes for "japanese" and "chinese-traditional" are missing because
1113 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
1114 // conversion list taken from
1115 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1116 // (quotes for kazakh and interlingua are unknown)
1118 if (is_known(h_language, known_danish_quotes_languages))
1119 h_quotes_style = "danish";
1121 else if (is_known(h_language, known_french_quotes_languages))
1122 h_quotes_style = "french";
1124 else if (is_known(h_language, known_german_quotes_languages))
1125 h_quotes_style = "german";
1127 else if (is_known(h_language, known_polish_quotes_languages))
1128 h_quotes_style = "polish";
1130 else if (is_known(h_language, known_swedish_quotes_languages))
1131 h_quotes_style = "swedish";
1133 else if (is_known(h_language, known_english_quotes_languages))
1134 h_quotes_style = "english";
1136 if (contains(h_float_placement, "H"))
1137 registerAutomaticallyLoadedPackage("float");
1138 if (h_spacing != "single" && h_spacing != "default")
1139 registerAutomaticallyLoadedPackage("setspace");
1140 if (h_use_packages["amsmath"] == "2") {
1141 // amsbsy and amstext are already provided by amsmath
1142 registerAutomaticallyLoadedPackage("amsbsy");
1143 registerAutomaticallyLoadedPackage("amstext");
1146 // output the LyX file settings
1147 // Important: Keep the version formatting in sync with LyX and
1148 // lyx2lyx (bug 7951)
1149 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1150 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1151 << lyx_version_minor << '\n'
1152 << "\\lyxformat " << LYX_FORMAT << '\n'
1153 << "\\begin_document\n"
1154 << "\\begin_header\n"
1155 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1156 << "\\origin " << origin << "\n"
1157 << "\\textclass " << h_textclass << "\n";
1158 string const raw = subdoc ? empty_string() : h_preamble.str();
1160 os << "\\begin_preamble\n";
1161 for (string::size_type i = 0; i < raw.size(); ++i) {
1162 if (raw[i] == package_beg_sep) {
1163 // Here follows some package loading code that
1164 // must be skipped if the package is loaded
1166 string::size_type j = raw.find(package_mid_sep, i);
1167 if (j == string::npos)
1169 string::size_type k = raw.find(package_end_sep, j);
1170 if (k == string::npos)
1172 string const package = raw.substr(i + 1, j - i - 1);
1173 string const replacement = raw.substr(j + 1, k - j - 1);
1174 if (auto_packages.find(package) == auto_packages.end())
1180 os << "\n\\end_preamble\n";
1182 if (!h_options.empty())
1183 os << "\\options " << h_options << "\n";
1184 os << "\\use_default_options " << h_use_default_options << "\n";
1185 if (!used_modules.empty()) {
1186 os << "\\begin_modules\n";
1187 vector<string>::const_iterator const end = used_modules.end();
1188 vector<string>::const_iterator it = used_modules.begin();
1189 for (; it != end; ++it)
1191 os << "\\end_modules\n";
1193 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1194 << "\\language " << h_language << "\n"
1195 << "\\language_package " << h_language_package << "\n"
1196 << "\\inputencoding " << h_inputencoding << "\n"
1197 << "\\fontencoding " << h_fontencoding << "\n"
1198 << "\\font_roman \"" << h_font_roman[0]
1199 << "\" \"" << h_font_roman[1] << "\"\n"
1200 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1201 << "\\font_typewriter \"" << h_font_typewriter[0]
1202 << "\" \"" << h_font_typewriter[1] << "\"\n"
1203 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1204 << "\\font_default_family " << h_font_default_family << "\n"
1205 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1206 << "\\font_sc " << h_font_sc << "\n"
1207 << "\\font_osf " << h_font_osf << "\n"
1208 << "\\font_sf_scale " << h_font_sf_scale[0]
1209 << ' ' << h_font_sf_scale[1] << '\n'
1210 << "\\font_tt_scale " << h_font_tt_scale[0]
1211 << ' ' << h_font_tt_scale[1] << '\n';
1212 if (!h_font_cjk.empty())
1213 os << "\\font_cjk " << h_font_cjk << '\n';
1214 os << "\\use_microtype " << h_use_microtype << '\n'
1215 << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n'
1216 << "\\graphics " << h_graphics << '\n'
1217 << "\\default_output_format " << h_default_output_format << "\n"
1218 << "\\output_sync " << h_output_sync << "\n";
1219 if (h_output_sync == "1")
1220 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1221 os << "\\bibtex_command " << h_bibtex_command << "\n"
1222 << "\\index_command " << h_index_command << "\n";
1223 if (!h_float_placement.empty())
1224 os << "\\float_placement " << h_float_placement << "\n";
1225 os << "\\paperfontsize " << h_paperfontsize << "\n"
1226 << "\\spacing " << h_spacing << "\n"
1227 << "\\use_hyperref " << h_use_hyperref << '\n';
1228 if (h_use_hyperref == "true") {
1229 if (!h_pdf_title.empty())
1230 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1231 if (!h_pdf_author.empty())
1232 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1233 if (!h_pdf_subject.empty())
1234 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1235 if (!h_pdf_keywords.empty())
1236 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1237 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1238 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1239 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1240 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1241 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1242 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1243 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1244 "\\pdf_backref " << h_pdf_backref << "\n"
1245 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1246 if (!h_pdf_pagemode.empty())
1247 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1248 if (!h_pdf_quoted_options.empty())
1249 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1251 os << "\\papersize " << h_papersize << "\n"
1252 << "\\use_geometry " << h_use_geometry << '\n';
1253 for (map<string, string>::const_iterator it = h_use_packages.begin();
1254 it != h_use_packages.end(); ++it)
1255 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1256 os << "\\cite_engine " << h_cite_engine << '\n'
1257 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1258 << "\\biblio_style " << h_biblio_style << "\n"
1259 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1260 << "\\use_indices " << h_use_indices << "\n"
1261 << "\\paperorientation " << h_paperorientation << '\n'
1262 << "\\suppress_date " << h_suppress_date << '\n'
1263 << "\\justification " << h_justification << '\n'
1264 << "\\use_refstyle " << h_use_refstyle << '\n';
1265 if (!h_fontcolor.empty())
1266 os << "\\fontcolor " << h_fontcolor << '\n';
1267 if (!h_notefontcolor.empty())
1268 os << "\\notefontcolor " << h_notefontcolor << '\n';
1269 if (!h_backgroundcolor.empty())
1270 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1271 if (!h_boxbgcolor.empty())
1272 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1273 if (index_number != 0)
1274 for (int i = 0; i < index_number; i++) {
1275 os << "\\index " << h_index[i] << '\n'
1276 << "\\shortcut " << h_shortcut[i] << '\n'
1277 << "\\color " << h_color << '\n'
1281 os << "\\index " << h_index[0] << '\n'
1282 << "\\shortcut " << h_shortcut[0] << '\n'
1283 << "\\color " << h_color << '\n'
1287 << "\\secnumdepth " << h_secnumdepth << "\n"
1288 << "\\tocdepth " << h_tocdepth << "\n"
1289 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1290 if (h_paragraph_separation == "skip")
1291 os << "\\defskip " << h_defskip << "\n";
1293 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1294 os << "\\is_math_indent " << h_is_mathindent << "\n";
1295 if (!h_mathindentation.empty())
1296 os << "\\math_indentation " << h_mathindentation << "\n";
1297 os << "\\math_numbering_side " << h_math_numbering_side << "\n";
1298 os << "\\quotes_style " << h_quotes_style << "\n"
1299 << "\\dynamic_quotes " << h_dynamic_quotes << "\n"
1300 << "\\papercolumns " << h_papercolumns << "\n"
1301 << "\\papersides " << h_papersides << "\n"
1302 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1303 if (!h_listings_params.empty())
1304 os << "\\listings_params " << h_listings_params << "\n";
1305 os << "\\tracking_changes " << h_tracking_changes << "\n"
1306 << "\\output_changes " << h_output_changes << "\n"
1307 << "\\html_math_output " << h_html_math_output << "\n"
1308 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1309 << "\\html_be_strict " << h_html_be_strict << "\n"
1311 << "\\end_header\n\n"
1312 << "\\begin_body\n";
1317 void Preamble::parse(Parser & p, string const & forceclass,
1318 TeX2LyXDocClass & tc)
1320 // initialize fixed types
1321 special_columns_['D'] = 3;
1322 parse(p, forceclass, false, tc);
1326 void Preamble::parse(Parser & p, string const & forceclass,
1327 bool detectEncoding, TeX2LyXDocClass & tc)
1329 bool is_full_document = false;
1330 bool is_lyx_file = false;
1331 bool in_lyx_preamble = false;
1333 // determine whether this is a full document or a fragment for inclusion
1335 Token const & t = p.get_token();
1337 if (t.cat() == catEscape && t.cs() == "documentclass") {
1338 is_full_document = true;
1344 if (detectEncoding && !is_full_document)
1347 while (is_full_document && p.good()) {
1348 if (detectEncoding && h_inputencoding != "auto" &&
1349 h_inputencoding != "default")
1352 Token const & t = p.get_token();
1355 if (!detectEncoding)
1356 cerr << "t: " << t << '\n';
1362 if (!in_lyx_preamble &&
1363 (t.cat() == catLetter ||
1364 t.cat() == catSuper ||
1365 t.cat() == catSub ||
1366 t.cat() == catOther ||
1367 t.cat() == catMath ||
1368 t.cat() == catActive ||
1369 t.cat() == catBegin ||
1370 t.cat() == catEnd ||
1371 t.cat() == catAlign ||
1372 t.cat() == catParameter))
1373 h_preamble << t.cs();
1375 else if (!in_lyx_preamble &&
1376 (t.cat() == catSpace || t.cat() == catNewline))
1377 h_preamble << t.asInput();
1379 else if (t.cat() == catComment) {
1380 static regex const islyxfile("%% LyX .* created this file");
1381 static regex const usercommands("User specified LaTeX commands");
1383 string const comment = t.asInput();
1385 // magically switch encoding default if it looks like XeLaTeX
1386 static string const magicXeLaTeX =
1387 "% This document must be compiled with XeLaTeX ";
1388 if (comment.size() > magicXeLaTeX.size()
1389 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1390 && h_inputencoding == "auto") {
1391 if (!detectEncoding)
1392 cerr << "XeLaTeX comment found, switching to UTF8\n";
1393 h_inputencoding = "utf8";
1396 if (regex_search(comment, sub, islyxfile)) {
1398 in_lyx_preamble = true;
1399 } else if (is_lyx_file
1400 && regex_search(comment, sub, usercommands))
1401 in_lyx_preamble = false;
1402 else if (!in_lyx_preamble)
1403 h_preamble << t.asInput();
1406 else if (t.cs() == "pagestyle")
1407 h_paperpagestyle = p.verbatim_item();
1409 else if (t.cs() == "setdefaultlanguage") {
1411 // We don't yet care about non-language variant options
1412 // because LyX doesn't support this yet, see bug #8214
1414 string langopts = p.getOpt();
1415 // check if the option contains a variant, if yes, extract it
1416 string::size_type pos_var = langopts.find("variant");
1417 string::size_type i = langopts.find(',', pos_var);
1418 string::size_type k = langopts.find('=', pos_var);
1419 if (pos_var != string::npos){
1421 if (i == string::npos)
1422 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1424 variant = langopts.substr(k + 1, i - k - 1);
1425 h_language = variant;
1429 h_language = p.verbatim_item();
1430 //finally translate the poyglossia name to a LyX name
1431 h_language = polyglossia2lyx(h_language);
1434 else if (t.cs() == "setotherlanguage") {
1435 // We don't yet care about the option because LyX doesn't
1436 // support this yet, see bug #8214
1437 p.hasOpt() ? p.getOpt() : string();
1441 else if (t.cs() == "setmainfont") {
1442 // we don't care about the option
1443 p.hasOpt() ? p.getOpt() : string();
1444 h_font_roman[1] = p.getArg('{', '}');
1447 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1448 // LyX currently only supports the scale option
1451 string fontopts = p.getArg('[', ']');
1452 // check if the option contains a scaling, if yes, extract it
1453 string::size_type pos = fontopts.find("Scale");
1454 if (pos != string::npos) {
1455 string::size_type i = fontopts.find(',', pos);
1456 if (i == string::npos)
1457 scale_as_percentage(fontopts.substr(pos + 1), scale);
1459 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1462 if (t.cs() == "setsansfont") {
1464 h_font_sf_scale[1] = scale;
1465 h_font_sans[1] = p.getArg('{', '}');
1468 h_font_tt_scale[1] = scale;
1469 h_font_typewriter[1] = p.getArg('{', '}');
1473 else if (t.cs() == "date") {
1474 string argument = p.getArg('{', '}');
1475 if (argument.empty())
1476 h_suppress_date = "true";
1478 h_preamble << t.asInput() << '{' << argument << '}';
1481 else if (t.cs() == "color") {
1482 string const space =
1483 (p.hasOpt() ? p.getOpt() : string());
1484 string argument = p.getArg('{', '}');
1485 // check the case that a standard color is used
1486 if (space.empty() && is_known(argument, known_basic_colors)) {
1487 h_fontcolor = rgbcolor2code(argument);
1488 registerAutomaticallyLoadedPackage("color");
1489 } else if (space.empty() && argument == "document_fontcolor")
1490 registerAutomaticallyLoadedPackage("color");
1491 // check the case that LyX's document_fontcolor is defined
1492 // but not used for \color
1494 h_preamble << t.asInput();
1496 h_preamble << space;
1497 h_preamble << '{' << argument << '}';
1498 // the color might already be set because \definecolor
1499 // is parsed before this
1504 else if (t.cs() == "pagecolor") {
1505 string argument = p.getArg('{', '}');
1506 // check the case that a standard color is used
1507 if (is_known(argument, known_basic_colors)) {
1508 h_backgroundcolor = rgbcolor2code(argument);
1509 } else if (argument == "page_backgroundcolor")
1510 registerAutomaticallyLoadedPackage("color");
1511 // check the case that LyX's page_backgroundcolor is defined
1512 // but not used for \pagecolor
1514 h_preamble << t.asInput() << '{' << argument << '}';
1515 // the color might already be set because \definecolor
1516 // is parsed before this
1517 h_backgroundcolor = "";
1521 else if (t.cs() == "makeatletter") {
1522 // LyX takes care of this
1523 p.setCatcode('@', catLetter);
1526 else if (t.cs() == "makeatother") {
1527 // LyX takes care of this
1528 p.setCatcode('@', catOther);
1531 else if (t.cs() == "makeindex") {
1532 // LyX will re-add this if a print index command is found
1536 else if (t.cs() == "newindex") {
1537 string const indexname = p.getArg('[', ']');
1538 string const shortcut = p.verbatim_item();
1539 if (!indexname.empty())
1540 h_index[index_number] = indexname;
1542 h_index[index_number] = shortcut;
1543 h_shortcut[index_number] = shortcut;
1548 else if (t.cs() == "RS@ifundefined") {
1549 string const name = p.verbatim_item();
1550 string const body1 = p.verbatim_item();
1551 string const body2 = p.verbatim_item();
1552 // only non-lyxspecific stuff
1553 if (in_lyx_preamble &&
1554 (name == "subsecref" || name == "thmref" || name == "lemref"))
1558 ss << '\\' << t.cs();
1559 ss << '{' << name << '}'
1560 << '{' << body1 << '}'
1561 << '{' << body2 << '}';
1562 h_preamble << ss.str();
1566 else if (t.cs() == "AtBeginDocument") {
1567 string const name = p.verbatim_item();
1568 // only non-lyxspecific stuff
1569 if (in_lyx_preamble &&
1570 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1571 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1572 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1573 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1574 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1575 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1576 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1577 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1578 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1579 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1580 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1581 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1582 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1583 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1584 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1588 ss << '\\' << t.cs();
1589 ss << '{' << name << '}';
1590 h_preamble << ss.str();
1594 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1595 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1596 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1597 || t.cs() == "DeclareRobustCommand"
1598 || t.cs() == "DeclareRobustCommandx"
1599 || t.cs() == "ProvideTextCommandDefault"
1600 || t.cs() == "DeclareMathAccent") {
1602 if (p.next_token().character() == '*') {
1606 string const name = p.verbatim_item();
1607 string const opt1 = p.getFullOpt();
1608 string const opt2 = p.getFullOpt();
1609 string const body = p.verbatim_item();
1610 // store the in_lyx_preamble setting
1611 bool const was_in_lyx_preamble = in_lyx_preamble;
1613 if (name == "\\rmdefault")
1614 if (is_known(body, known_roman_fonts)) {
1615 h_font_roman[0] = body;
1617 in_lyx_preamble = true;
1619 if (name == "\\sfdefault")
1620 if (is_known(body, known_sans_fonts)) {
1621 h_font_sans[0] = body;
1623 in_lyx_preamble = true;
1625 if (name == "\\ttdefault")
1626 if (is_known(body, known_typewriter_fonts)) {
1627 h_font_typewriter[0] = body;
1629 in_lyx_preamble = true;
1631 if (name == "\\familydefault") {
1632 string family = body;
1633 // remove leading "\"
1634 h_font_default_family = family.erase(0,1);
1636 in_lyx_preamble = true;
1639 // remove LyX-specific definitions that are re-added by LyX
1641 // \lyxline is an ancient command that is converted by tex2lyx into
1642 // a \rule therefore remove its preamble code
1643 if (name == "\\lyxdot" || name == "\\lyxarrow"
1644 || name == "\\lyxline" || name == "\\LyX") {
1646 in_lyx_preamble = true;
1649 // Add the command to the known commands
1650 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1652 // only non-lyxspecific stuff
1653 if (!in_lyx_preamble) {
1655 ss << '\\' << t.cs();
1658 ss << '{' << name << '}' << opt1 << opt2
1659 << '{' << body << "}";
1660 h_preamble << ss.str();
1662 ostream & out = in_preamble ? h_preamble : os;
1663 out << "\\" << t.cs() << "{" << name << "}"
1664 << opts << "{" << body << "}";
1667 // restore the in_lyx_preamble setting
1668 in_lyx_preamble = was_in_lyx_preamble;
1671 else if (t.cs() == "documentclass") {
1672 vector<string>::iterator it;
1673 vector<string> opts = split_options(p.getArg('[', ']'));
1674 handle_opt(opts, known_fontsizes, h_paperfontsize);
1675 delete_opt(opts, known_fontsizes);
1676 // delete "pt" at the end
1677 string::size_type i = h_paperfontsize.find("pt");
1678 if (i != string::npos)
1679 h_paperfontsize.erase(i);
1680 // The documentclass options are always parsed before the options
1681 // of the babel call so that a language cannot overwrite the babel
1683 handle_opt(opts, known_languages, h_language);
1684 delete_opt(opts, known_languages);
1687 if ((it = find(opts.begin(), opts.end(), "fleqn"))
1689 h_is_mathindent = "1";
1692 // formula numbering side
1693 if ((it = find(opts.begin(), opts.end(), "leqno"))
1695 h_math_numbering_side = "left";
1698 else if ((it = find(opts.begin(), opts.end(), "reqno"))
1700 h_math_numbering_side = "right";
1704 // paper orientation
1705 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1706 h_paperorientation = "landscape";
1710 if ((it = find(opts.begin(), opts.end(), "oneside"))
1715 if ((it = find(opts.begin(), opts.end(), "twoside"))
1721 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1723 h_papercolumns = "1";
1726 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1728 h_papercolumns = "2";
1732 // some size options are known to any document classes, other sizes
1733 // are handled by the \geometry command of the geometry package
1734 handle_opt(opts, known_class_paper_sizes, h_papersize);
1735 delete_opt(opts, known_class_paper_sizes);
1736 // the remaining options
1737 h_options = join(opts, ",");
1738 // FIXME This does not work for classes that have a
1739 // different name in LyX than in LaTeX
1740 h_textclass = p.getArg('{', '}');
1744 else if (t.cs() == "usepackage") {
1745 string const options = p.getArg('[', ']');
1746 string const name = p.getArg('{', '}');
1747 vector<string> vecnames;
1748 split(name, vecnames, ',');
1749 vector<string>::const_iterator it = vecnames.begin();
1750 vector<string>::const_iterator end = vecnames.end();
1751 for (; it != end; ++it)
1752 handle_package(p, trimSpaceAndEol(*it), options,
1753 in_lyx_preamble, detectEncoding);
1756 else if (t.cs() == "inputencoding") {
1757 string const encoding = p.getArg('{','}');
1758 Encoding const * const enc = encodings.fromLaTeXName(
1759 encoding, Encoding::inputenc, true);
1761 if (!detectEncoding)
1762 cerr << "Unknown encoding " << encoding
1763 << ". Ignoring." << std::endl;
1766 h_inputencoding = enc->name();
1767 p.setEncoding(enc->iconvName());
1771 else if (t.cs() == "newenvironment") {
1772 string const name = p.getArg('{', '}');
1773 string const opt1 = p.getFullOpt();
1774 string const opt2 = p.getFullOpt();
1775 string const beg = p.verbatim_item();
1776 string const end = p.verbatim_item();
1777 if (!in_lyx_preamble) {
1778 h_preamble << "\\newenvironment{" << name
1779 << '}' << opt1 << opt2 << '{'
1780 << beg << "}{" << end << '}';
1782 add_known_environment(name, opt1, !opt2.empty(),
1783 from_utf8(beg), from_utf8(end));
1787 else if (t.cs() == "newtheorem") {
1788 string const name = p.getArg('{', '}');
1789 string const opt1 = p.getFullOpt();
1790 string const opt2 = p.getFullOpt();
1791 string const body = p.verbatim_item();
1792 string const opt3 = p.getFullOpt();
1794 string const complete = "\\newtheorem{" + name + '}' +
1795 opt1 + opt2 + '{' + body + '}' + opt3;
1797 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1799 if (!in_lyx_preamble)
1800 h_preamble << complete;
1803 else if (t.cs() == "def") {
1804 string name = p.get_token().cs();
1805 // In fact, name may be more than the name:
1806 // In the test case of bug 8116
1807 // name == "csname SF@gobble@opt \endcsname".
1808 // Therefore, we need to use asInput() instead of cs().
1809 while (p.next_token().cat() != catBegin)
1810 name += p.get_token().asInput();
1811 if (!in_lyx_preamble)
1812 h_preamble << "\\def\\" << name << '{'
1813 << p.verbatim_item() << "}";
1816 else if (t.cs() == "newcolumntype") {
1817 string const name = p.getArg('{', '}');
1818 trimSpaceAndEol(name);
1820 string opts = p.getOpt();
1821 if (!opts.empty()) {
1822 istringstream is(string(opts, 1));
1825 special_columns_[name[0]] = nargs;
1826 h_preamble << "\\newcolumntype{" << name << "}";
1828 h_preamble << "[" << nargs << "]";
1829 h_preamble << "{" << p.verbatim_item() << "}";
1832 else if (t.cs() == "setcounter") {
1833 string const name = p.getArg('{', '}');
1834 string const content = p.getArg('{', '}');
1835 if (name == "secnumdepth")
1836 h_secnumdepth = content;
1837 else if (name == "tocdepth")
1838 h_tocdepth = content;
1840 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1843 else if (t.cs() == "setlength") {
1844 string const name = p.verbatim_item();
1845 string const content = p.verbatim_item();
1846 // the paragraphs are only not indented when \parindent is set to zero
1847 if (name == "\\parindent" && content != "") {
1848 if (content[0] == '0')
1849 h_paragraph_separation = "skip";
1851 h_paragraph_indentation = translate_len(content);
1852 } else if (name == "\\parskip") {
1853 if (content == "\\smallskipamount")
1854 h_defskip = "smallskip";
1855 else if (content == "\\medskipamount")
1856 h_defskip = "medskip";
1857 else if (content == "\\bigskipamount")
1858 h_defskip = "bigskip";
1860 h_defskip = translate_len(content);
1861 } else if (name == "\\mathindent") {
1862 h_mathindentation = translate_len(content);
1864 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1867 else if (t.cs() == "onehalfspacing")
1868 h_spacing = "onehalf";
1870 else if (t.cs() == "doublespacing")
1871 h_spacing = "double";
1873 else if (t.cs() == "setstretch")
1874 h_spacing = "other " + p.verbatim_item();
1876 else if (t.cs() == "synctex") {
1877 // the scheme is \synctex=value
1878 // where value can only be "1" or "-1"
1879 h_output_sync = "1";
1880 // there can be any character behind the value (e.g. a linebreak or a '\'
1881 // therefore we extract it char by char
1883 string value = p.get_token().asInput();
1885 value += p.get_token().asInput();
1886 h_output_sync_macro = "\\synctex=" + value;
1889 else if (t.cs() == "begin") {
1890 string const name = p.getArg('{', '}');
1891 if (name == "document")
1893 h_preamble << "\\begin{" << name << "}";
1896 else if (t.cs() == "geometry") {
1897 vector<string> opts = split_options(p.getArg('{', '}'));
1898 handle_geometry(opts);
1901 else if (t.cs() == "definecolor") {
1902 string const color = p.getArg('{', '}');
1903 string const space = p.getArg('{', '}');
1904 string const value = p.getArg('{', '}');
1905 if (color == "document_fontcolor" && space == "rgb") {
1906 RGBColor c(RGBColorFromLaTeX(value));
1907 h_fontcolor = X11hexname(c);
1908 } else if (color == "note_fontcolor" && space == "rgb") {
1909 RGBColor c(RGBColorFromLaTeX(value));
1910 h_notefontcolor = X11hexname(c);
1911 } else if (color == "page_backgroundcolor" && space == "rgb") {
1912 RGBColor c(RGBColorFromLaTeX(value));
1913 h_backgroundcolor = X11hexname(c);
1914 } else if (color == "shadecolor" && space == "rgb") {
1915 RGBColor c(RGBColorFromLaTeX(value));
1916 h_boxbgcolor = X11hexname(c);
1918 h_preamble << "\\definecolor{" << color
1919 << "}{" << space << "}{" << value
1924 else if (t.cs() == "bibliographystyle")
1925 h_biblio_style = p.verbatim_item();
1927 else if (t.cs() == "jurabibsetup") {
1928 // FIXME p.getArg('{', '}') is most probably wrong (it
1929 // does not handle nested braces).
1930 // Use p.verbatim_item() instead.
1931 vector<string> jurabibsetup =
1932 split_options(p.getArg('{', '}'));
1933 // add jurabibsetup to the jurabib package options
1934 add_package("jurabib", jurabibsetup);
1935 if (!jurabibsetup.empty()) {
1936 h_preamble << "\\jurabibsetup{"
1937 << join(jurabibsetup, ",") << '}';
1941 else if (t.cs() == "hypersetup") {
1942 vector<string> hypersetup =
1943 split_options(p.verbatim_item());
1944 // add hypersetup to the hyperref package options
1945 handle_hyperref(hypersetup);
1946 if (!hypersetup.empty()) {
1947 h_preamble << "\\hypersetup{"
1948 << join(hypersetup, ",") << '}';
1952 else if (is_known(t.cs(), known_if_3arg_commands)) {
1953 // prevent misparsing of \usepackage if it is used
1954 // as an argument (see e.g. our own output of
1955 // \@ifundefined above)
1956 string const arg1 = p.verbatim_item();
1957 string const arg2 = p.verbatim_item();
1958 string const arg3 = p.verbatim_item();
1959 // test case \@ifundefined{date}{}{\date{}}
1960 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1961 arg2.empty() && arg3 == "\\date{}") {
1962 h_suppress_date = "true";
1963 // older tex2lyx versions did output
1964 // \@ifundefined{definecolor}{\usepackage{color}}{}
1965 } else if (t.cs() == "@ifundefined" &&
1966 arg1 == "definecolor" &&
1967 arg2 == "\\usepackage{color}" &&
1969 if (!in_lyx_preamble)
1970 h_preamble << package_beg_sep
1973 << "\\@ifundefined{definecolor}{color}{}"
1976 //\@ifundefined{showcaptionsetup}{}{%
1977 // \PassOptionsToPackage{caption=false}{subfig}}
1978 // that LyX uses for subfloats
1979 } else if (t.cs() == "@ifundefined" &&
1980 arg1 == "showcaptionsetup" && arg2.empty()
1981 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1983 } else if (!in_lyx_preamble) {
1984 h_preamble << t.asInput()
1985 << '{' << arg1 << '}'
1986 << '{' << arg2 << '}'
1987 << '{' << arg3 << '}';
1991 else if (is_known(t.cs(), known_if_commands)) {
1992 // must not parse anything in conditional code, since
1993 // LyX would output the parsed contents unconditionally
1994 if (!in_lyx_preamble)
1995 h_preamble << t.asInput();
1996 handle_if(p, in_lyx_preamble);
1999 else if (!t.cs().empty() && !in_lyx_preamble)
2000 h_preamble << '\\' << t.cs();
2003 // remove the whitespace
2006 // Force textclass if the user wanted it
2007 if (!forceclass.empty())
2008 h_textclass = forceclass;
2009 tc.setName(h_textclass);
2011 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
2014 if (h_papersides.empty()) {
2017 h_papersides = ss.str();
2020 // If the CJK package is used we cannot set the document language from
2021 // the babel options. Instead, we guess which language is used most
2022 // and set this one.
2023 default_language = h_language;
2024 if (is_full_document &&
2025 (auto_packages.find("CJK") != auto_packages.end() ||
2026 auto_packages.find("CJKutf8") != auto_packages.end())) {
2028 h_language = guessLanguage(p, default_language);
2030 if (explicit_babel && h_language != default_language) {
2031 // We set the document language to a CJK language,
2032 // but babel is explicitly called in the user preamble
2033 // without options. LyX will not add the default
2034 // language to the document options if it is either
2035 // english, or no text is set as default language.
2036 // Therefore we need to add a language option explicitly.
2037 // FIXME: It would be better to remove all babel calls
2038 // from the user preamble, but this is difficult
2039 // without re-introducing bug 7861.
2040 if (h_options.empty())
2041 h_options = lyx2babel(default_language);
2043 h_options += ',' + lyx2babel(default_language);
2049 string Preamble::parseEncoding(Parser & p, string const & forceclass)
2051 TeX2LyXDocClass dummy;
2052 parse(p, forceclass, true, dummy);
2053 if (h_inputencoding != "auto" && h_inputencoding != "default")
2054 return h_inputencoding;
2059 string babel2lyx(string const & language)
2061 char const * const * where = is_known(language, known_languages);
2063 return known_coded_languages[where - known_languages];
2068 string lyx2babel(string const & language)
2070 char const * const * where = is_known(language, known_coded_languages);
2072 return known_languages[where - known_coded_languages];
2077 string Preamble::polyglossia2lyx(string const & language)
2079 char const * const * where = is_known(language, polyglossia_languages);
2081 return coded_polyglossia_languages[where - polyglossia_languages];
2086 string rgbcolor2code(string const & name)
2088 char const * const * where = is_known(name, known_basic_colors);
2090 // "red", "green" etc
2091 return known_basic_color_codes[where - known_basic_colors];
2093 // "255,0,0", "0,255,0" etc
2094 RGBColor c(RGBColorFromLaTeX(name));
2095 return X11hexname(c);