3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
20 #include "LayoutFile.h"
23 #include "TextClass.h"
26 #include "support/convert.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/lstrings.h"
31 #include "support/regex.h"
37 using namespace lyx::support;
46 // CJK languages are handled in text.cpp, polyglossia languages are listed
49 * known babel language names (including synonyms)
50 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
51 * please keep this in sync with known_coded_languages line by line!
53 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
54 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
55 "bahasam", "basque", "belarusian", "brazil", "brazilian", "breton", "british",
56 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
57 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
58 "french", "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb",
59 "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian",
60 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
61 "latvian", "lithuanian", "lowersorbian", "lsorbian", "magyar", "malay", "meyalu",
62 "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman",
63 "nynorsk", "polutonikogreek", "polish", "portuges", "portuguese", "romanian", "russian",
64 "russianb", "samin", "scottish", "serbian", "serbian-latin", "slovak",
65 "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
66 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
71 * the same as known_languages with .lyx names
72 * please keep this in sync with known_languages line by line!
74 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
75 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
76 "bahasam", "basque", "belarusian", "brazilian", "brazilian", "breton", "british",
77 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
78 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
79 "french", "french", "french", "french", "galician", "german", "german",
80 "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa",
81 "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin",
82 "latvian", "lithuanian", "lowersorbian", "lowersorbian", "magyar", "bahasam", "bahasam",
83 "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch",
84 "nynorsk", "polutonikogreek", "polish", "portuguese", "portuguese", "romanian", "russian",
85 "russian", "samin", "scottish", "serbian", "serbian-latin", "slovak",
86 "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
87 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
88 "vietnamese", "welsh",
91 /// languages with danish quotes (.lyx names)
92 const char * const known_danish_quotes_languages[] = {"danish", 0};
94 /// languages with english quotes (.lyx names)
95 const char * const known_english_quotes_languages[] = {"american", "australian",
96 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
97 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
100 /// languages with french quotes (.lyx names)
101 const char * const known_french_quotes_languages[] = {"albanian",
102 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
103 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
104 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
107 /// languages with german quotes (.lyx names)
108 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
109 "czech", "german", "georgian", "icelandic", "lithuanian", "lowersorbian", "naustrian",
110 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
112 /// languages with polish quotes (.lyx names)
113 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
114 "dutch", "estonian", "magyar", "polish", "romanian", 0};
116 /// languages with swedish quotes (.lyx names)
117 const char * const known_swedish_quotes_languages[] = {"finnish",
120 /// known language packages from the times before babel
121 const char * const known_old_language_packages[] = {"french", "frenchle",
122 "frenchpro", "german", "ngerman", "pmfrench", 0};
124 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
126 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
127 "ccfonts", "chancery", "charter", "cmr", "fourier", "garamondx", "libertine",
128 "libertine-type1", "lmodern", "mathdesign", "mathpazo", "mathptmx", "newcent",
129 "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", 0};
131 const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum-type1",
132 "cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc", "kurier",
133 "kurierc", "kurierl", "kurierlc", "lmss", "tgadventor", "tgheros", 0};
135 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
136 "courier", "lmtt", "luximono", "fourier", "libertineMono-type1", "lmodern",
137 "mathpazo", "mathptmx", "newcent", "tgcursor", "txtt", 0};
139 const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0};
141 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
142 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
143 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
144 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
145 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
147 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
148 "executivepaper", "legalpaper", "letterpaper", 0};
150 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
151 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
153 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
154 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
157 /// commands that can start an \if...\else...\endif sequence
158 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
159 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
160 "ifsidecap", "ifupgreek", 0};
162 const char * const known_basic_colors[] = {"black", "blue", "brown", "cyan",
163 "darkgray", "gray", "green", "lightgray", "lime", "magenta", "orange", "olive",
164 "pink", "purple", "red", "teal", "violet", "white", "yellow", 0};
166 const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", "#00ffff",
167 "#a9a9a9", "#808080", "#00ff00", "#d3d3d3", "#bfff00", "#ff00ff", "#ff7f00", "#808000",
168 "#ffc0cb", "#800080", "#ff0000", "#008080", "#8f00ff", "#ffffff", "#ffff00", 0};
170 /// conditional commands with three arguments like \@ifundefined{}{}{}
171 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
174 /// packages that work only in xetex
175 /// polyglossia is handled separately
176 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
177 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
178 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
180 /// packages that are automatically skipped if loaded by LyX
181 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
182 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
183 "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable",
184 "makeidx", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle", "rotating",
185 "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa", "tipx",
186 "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xunicode", 0};
188 // codes used to remove packages that are loaded automatically by LyX.
189 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
190 const char package_beg_sep = '\001';
191 const char package_mid_sep = '\002';
192 const char package_end_sep = '\003';
195 // returns true if at least one of the options in what has been found
196 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
202 // the last language option is the document language (for babel and LyX)
203 // the last size option is the document font size
204 vector<string>::iterator it;
205 vector<string>::iterator position = opts.begin();
206 for (; *what; ++what) {
207 it = find(opts.begin(), opts.end(), *what);
208 if (it != opts.end()) {
209 if (it >= position) {
220 void delete_opt(vector<string> & opts, char const * const * what)
225 // remove found options from the list
226 // do this after handle_opt to avoid potential memory leaks
227 vector<string>::iterator it;
228 for (; *what; ++what) {
229 it = find(opts.begin(), opts.end(), *what);
230 if (it != opts.end())
237 * Split a package options string (keyval format) into a vector.
239 * authorformat=smallcaps,
241 * titleformat=colonsep,
242 * bibformat={tabular,ibidem,numbered}
244 vector<string> split_options(string const & input)
246 vector<string> options;
250 Token const & t = p.get_token();
251 if (t.asInput() == ",") {
252 options.push_back(trimSpaceAndEol(option));
254 } else if (t.asInput() == "=") {
257 if (p.next_token().asInput() == "{")
258 option += '{' + p.getArg('{', '}') + '}';
259 } else if (t.cat() != catSpace)
260 option += t.asInput();
264 options.push_back(trimSpaceAndEol(option));
271 * Retrieve a keyval option "name={value with=sign}" named \p name from
272 * \p options and return the value.
273 * The found option is also removed from \p options.
275 string process_keyval_opt(vector<string> & options, string name)
277 for (size_t i = 0; i < options.size(); ++i) {
278 vector<string> option;
279 split(options[i], option, '=');
280 if (option.size() < 2)
282 if (option[0] == name) {
283 options.erase(options.begin() + i);
284 option.erase(option.begin());
285 return join(option, "=");
291 } // anonymous namespace
295 * known polyglossia language names (including variants)
296 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
298 const char * const Preamble::polyglossia_languages[] = {
299 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
300 "nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil",
301 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
302 "irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish",
303 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
304 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "tibetan", "urdu",
305 "brazil", "brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton",
306 "french", "lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar",
307 "slovak", "welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek",
308 "marathi", "spanish", "austrian",
309 "american", "ancient", "australian", "british", "monotonic", "newzealand",
313 * the same as polyglossia_languages with .lyx names
314 * please keep this in sync with polyglossia_languages line by line!
316 const char * const Preamble::coded_polyglossia_languages[] = {
317 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
318 "nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil",
319 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
320 "irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish",
321 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
322 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "tibetan", "urdu",
323 "brazilian", "brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton",
324 "french", "lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar",
325 "slovak", "welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek",
326 "marathi", "spanish", "naustrian",
327 "american", "ancientgreek", "australian", "british", "greek", "newzealand",
328 "polutonikogreek", 0};
331 bool Preamble::usePolyglossia() const
333 return h_use_non_tex_fonts && h_language_package == "default";
337 bool Preamble::indentParagraphs() const
339 return h_paragraph_separation == "indent";
343 bool Preamble::isPackageUsed(string const & package) const
345 return used_packages.find(package) != used_packages.end();
349 vector<string> Preamble::getPackageOptions(string const & package) const
351 map<string, vector<string> >::const_iterator it = used_packages.find(package);
352 if (it != used_packages.end())
354 return vector<string>();
358 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
360 auto_packages.insert(package);
364 void Preamble::addModule(string const & module)
366 used_modules.push_back(module);
370 void Preamble::suppressDate(bool suppress)
373 h_suppress_date = "true";
375 h_suppress_date = "false";
379 void Preamble::registerAuthor(std::string const & name)
381 Author author(from_utf8(name), empty_docstring());
382 author.setUsed(true);
383 authors_.record(author);
384 h_tracking_changes = "true";
385 h_output_changes = "true";
389 Author const & Preamble::getAuthor(std::string const & name) const
391 Author author(from_utf8(name), empty_docstring());
392 for (AuthorList::Authors::const_iterator it = authors_.begin();
393 it != authors_.end(); ++it)
396 static Author const dummy;
401 int Preamble::getSpecialTableColumnArguments(char c) const
403 map<char, int>::const_iterator it = special_columns_.find(c);
404 if (it == special_columns_.end())
410 void Preamble::add_package(string const & name, vector<string> & options)
412 // every package inherits the global options
413 if (used_packages.find(name) == used_packages.end())
414 used_packages[name] = split_options(h_options);
416 vector<string> & v = used_packages[name];
417 v.insert(v.end(), options.begin(), options.end());
418 if (name == "jurabib") {
419 // Don't output the order argument (see the cite command
420 // handling code in text.cpp).
421 vector<string>::iterator end =
422 remove(options.begin(), options.end(), "natbiborder");
423 end = remove(options.begin(), end, "jurabiborder");
424 options.erase(end, options.end());
431 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
432 bool scale_as_percentage(string const & scale, string & percentage)
434 string::size_type pos = scale.find('=');
435 if (pos != string::npos) {
436 string value = scale.substr(pos + 1);
437 if (isStrDbl(value)) {
438 percentage = convert<string>(
439 static_cast<int>(100 * convert<double>(value)));
447 string remove_braces(string const & value)
451 if (value[0] == '{' && value[value.length()-1] == '}')
452 return value.substr(1, value.length()-2);
456 } // anonymous namespace
459 Preamble::Preamble() : one_language(true), explicit_babel(false),
460 title_layout_found(false), index_number(0), h_font_cjk_set(false),
461 h_use_microtype(false)
465 h_biblio_style = "plain";
466 h_bibtex_command = "default";
467 h_cite_engine = "basic";
468 h_cite_engine_type = "default";
470 h_defskip = "medskip";
473 h_fontencoding = "default";
474 h_font_roman[0] = "default";
475 h_font_roman[1] = "default";
476 h_font_sans[0] = "default";
477 h_font_sans[1] = "default";
478 h_font_typewriter[0] = "default";
479 h_font_typewriter[1] = "default";
480 h_font_math[0] = "auto";
481 h_font_math[1] = "auto";
482 h_font_default_family = "default";
483 h_use_non_tex_fonts = false;
485 h_font_osf = "false";
486 h_font_sf_scale[0] = "100";
487 h_font_sf_scale[1] = "100";
488 h_font_tt_scale[0] = "100";
489 h_font_tt_scale[1] = "100";
491 h_graphics = "default";
492 h_default_output_format = "default";
493 h_html_be_strict = "false";
494 h_html_css_as_file = "0";
495 h_html_math_output = "0";
496 h_index[0] = "Index";
497 h_index_command = "default";
498 h_inputencoding = "auto";
499 h_justification = "true";
500 h_language = "english";
501 h_language_package = "none";
503 h_maintain_unincluded_children = "false";
507 h_output_changes = "false";
509 //h_output_sync_macro
510 h_papercolumns = "1";
511 h_paperfontsize = "default";
512 h_paperorientation = "portrait";
513 h_paperpagestyle = "default";
515 h_papersize = "default";
516 h_paragraph_indentation = "default";
517 h_paragraph_separation = "indent";
522 h_pdf_bookmarks = "0";
523 h_pdf_bookmarksnumbered = "0";
524 h_pdf_bookmarksopen = "0";
525 h_pdf_bookmarksopenlevel = "1";
526 h_pdf_breaklinks = "0";
527 h_pdf_pdfborder = "0";
528 h_pdf_colorlinks = "0";
529 h_pdf_backref = "section";
530 h_pdf_pdfusetitle = "0";
532 //h_pdf_quoted_options;
533 h_quotes_language = "english";
535 h_shortcut[0] = "idx";
536 h_spacing = "single";
537 h_save_transient_properties = "true";
538 h_suppress_date = "false";
539 h_textclass = "article";
541 h_tracking_changes = "false";
542 h_use_bibtopic = "false";
543 h_use_indices = "false";
544 h_use_geometry = "false";
545 h_use_default_options = "false";
546 h_use_hyperref = "false";
547 h_use_microtype = false;
548 h_use_refstyle = false;
549 h_use_packages["amsmath"] = "1";
550 h_use_packages["amssymb"] = "0";
551 h_use_packages["cancel"] = "0";
552 h_use_packages["esint"] = "1";
553 h_use_packages["mhchem"] = "0";
554 h_use_packages["mathdots"] = "0";
555 h_use_packages["mathtools"] = "0";
556 h_use_packages["stackrel"] = "0";
557 h_use_packages["stmaryrd"] = "0";
558 h_use_packages["undertilde"] = "0";
562 void Preamble::handle_hyperref(vector<string> & options)
564 // FIXME swallow inputencoding changes that might surround the
565 // hyperref setup if it was written by LyX
566 h_use_hyperref = "true";
567 // swallow "unicode=true", since LyX does always write that
568 vector<string>::iterator it =
569 find(options.begin(), options.end(), "unicode=true");
570 if (it != options.end())
572 it = find(options.begin(), options.end(), "pdfusetitle");
573 if (it != options.end()) {
574 h_pdf_pdfusetitle = "1";
577 string bookmarks = process_keyval_opt(options, "bookmarks");
578 if (bookmarks == "true")
579 h_pdf_bookmarks = "1";
580 else if (bookmarks == "false")
581 h_pdf_bookmarks = "0";
582 if (h_pdf_bookmarks == "1") {
583 string bookmarksnumbered =
584 process_keyval_opt(options, "bookmarksnumbered");
585 if (bookmarksnumbered == "true")
586 h_pdf_bookmarksnumbered = "1";
587 else if (bookmarksnumbered == "false")
588 h_pdf_bookmarksnumbered = "0";
589 string bookmarksopen =
590 process_keyval_opt(options, "bookmarksopen");
591 if (bookmarksopen == "true")
592 h_pdf_bookmarksopen = "1";
593 else if (bookmarksopen == "false")
594 h_pdf_bookmarksopen = "0";
595 if (h_pdf_bookmarksopen == "1") {
596 string bookmarksopenlevel =
597 process_keyval_opt(options, "bookmarksopenlevel");
598 if (!bookmarksopenlevel.empty())
599 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
602 string breaklinks = process_keyval_opt(options, "breaklinks");
603 if (breaklinks == "true")
604 h_pdf_breaklinks = "1";
605 else if (breaklinks == "false")
606 h_pdf_breaklinks = "0";
607 string pdfborder = process_keyval_opt(options, "pdfborder");
608 if (pdfborder == "{0 0 0}")
609 h_pdf_pdfborder = "1";
610 else if (pdfborder == "{0 0 1}")
611 h_pdf_pdfborder = "0";
612 string backref = process_keyval_opt(options, "backref");
613 if (!backref.empty())
614 h_pdf_backref = backref;
615 string colorlinks = process_keyval_opt(options, "colorlinks");
616 if (colorlinks == "true")
617 h_pdf_colorlinks = "1";
618 else if (colorlinks == "false")
619 h_pdf_colorlinks = "0";
620 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
621 if (!pdfpagemode.empty())
622 h_pdf_pagemode = pdfpagemode;
623 string pdftitle = process_keyval_opt(options, "pdftitle");
624 if (!pdftitle.empty()) {
625 h_pdf_title = remove_braces(pdftitle);
627 string pdfauthor = process_keyval_opt(options, "pdfauthor");
628 if (!pdfauthor.empty()) {
629 h_pdf_author = remove_braces(pdfauthor);
631 string pdfsubject = process_keyval_opt(options, "pdfsubject");
632 if (!pdfsubject.empty())
633 h_pdf_subject = remove_braces(pdfsubject);
634 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
635 if (!pdfkeywords.empty())
636 h_pdf_keywords = remove_braces(pdfkeywords);
637 if (!options.empty()) {
638 if (!h_pdf_quoted_options.empty())
639 h_pdf_quoted_options += ',';
640 h_pdf_quoted_options += join(options, ",");
646 void Preamble::handle_geometry(vector<string> & options)
648 h_use_geometry = "true";
649 vector<string>::iterator it;
651 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
652 h_paperorientation = "landscape";
656 // keyval version: "paper=letter"
657 string paper = process_keyval_opt(options, "paper");
659 h_papersize = paper + "paper";
660 // alternative version: "letterpaper"
661 handle_opt(options, known_paper_sizes, h_papersize);
662 delete_opt(options, known_paper_sizes);
664 char const * const * margin = known_paper_margins;
665 for (; *margin; ++margin) {
666 string value = process_keyval_opt(options, *margin);
667 if (!value.empty()) {
668 int k = margin - known_paper_margins;
669 string name = known_coded_paper_margins[k];
670 h_margins += '\\' + name + ' ' + value + '\n';
676 void Preamble::handle_package(Parser &p, string const & name,
677 string const & opts, bool in_lyx_preamble,
680 vector<string> options = split_options(opts);
681 add_package(name, options);
682 char const * const * where = 0;
684 if (is_known(name, known_xetex_packages)) {
686 h_use_non_tex_fonts = true;
687 registerAutomaticallyLoadedPackage("fontspec");
688 if (h_inputencoding == "auto")
689 p.setEncoding("UTF-8");
693 if (is_known(name, known_roman_fonts))
694 h_font_roman[0] = name;
696 if (name == "fourier") {
697 h_font_roman[0] = "utopia";
698 // when font uses real small capitals
699 if (opts == "expert")
703 if (name == "garamondx") {
704 h_font_roman[0] = "garamondx";
709 if (name == "libertine") {
710 h_font_roman[0] = "libertine";
711 // this automatically invokes biolinum
712 h_font_sans[0] = "biolinum";
715 else if (opts == "lining")
716 h_font_osf = "false";
719 if (name == "libertine-type1") {
720 h_font_roman[0] = "libertine";
721 // NOTE: contrary to libertine.sty, libertine-type1
722 // does not automatically invoke biolinum
723 if (opts == "lining")
724 h_font_osf = "false";
725 else if (opts == "osf")
729 if (name == "mathdesign") {
730 if (opts.find("charter") != string::npos)
731 h_font_roman[0] = "md-charter";
732 if (opts.find("garamond") != string::npos)
733 h_font_roman[0] = "md-garamond";
734 if (opts.find("utopia") != string::npos)
735 h_font_roman[0] = "md-utopia";
736 if (opts.find("expert") != string::npos) {
742 else if (name == "mathpazo")
743 h_font_roman[0] = "palatino";
745 else if (name == "mathptmx")
746 h_font_roman[0] = "times";
749 if (is_known(name, known_sans_fonts)) {
750 h_font_sans[0] = name;
751 if (options.size() >= 1) {
752 if (scale_as_percentage(opts, h_font_sf_scale[0]))
757 if (name == "biolinum-type1") {
758 h_font_sans[0] = "biolinum";
759 // biolinum can have several options, e.g. [osf,scaled=0.97]
760 string::size_type pos = opts.find("osf");
761 if (pos != string::npos)
766 if (is_known(name, known_typewriter_fonts)) {
767 // fourier can be set as roman font _only_
768 // fourier as typewriter is handled in handling of \ttdefault
769 if (name != "fourier") {
770 h_font_typewriter[0] = name;
771 if (options.size() >= 1) {
772 if (scale_as_percentage(opts, h_font_tt_scale[0]))
778 if (name == "libertineMono-type1") {
779 h_font_typewriter[0] = "libertine-mono";
782 // font uses old-style figure
787 if (is_known(name, known_math_fonts))
788 h_font_math[0] = name;
790 if (name == "newtxmath") {
792 h_font_math[0] = "newtxmath";
793 else if (opts == "garamondx")
794 h_font_math[0] = "garamondx-ntxm";
795 else if (opts == "libertine")
796 h_font_math[0] = "libertine-ntxm";
797 else if (opts == "minion")
798 h_font_math[0] = "minion-ntxm";
803 h_font_math[0] = "iwona-math";
805 if (name == "kurier")
807 h_font_math[0] = "kurier-math";
809 // after the detection and handling of special cases, we can remove the
810 // fonts, otherwise they would appear in the preamble, see bug #7856
811 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
812 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
814 //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42"
815 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
816 name == "esint" || name == "mhchem" || name == "mathdots" ||
817 name == "mathtools" || name == "stackrel" ||
818 name == "stmaryrd" || name == "undertilde")
819 h_use_packages[name] = "2";
821 else if (name == "babel") {
822 h_language_package = "default";
823 // One might think we would have to do nothing if babel is loaded
824 // without any options to prevent pollution of the preamble with this
825 // babel call in every roundtrip.
826 // But the user could have defined babel-specific things afterwards. So
827 // we need to keep it in the preamble to prevent cases like bug #7861.
829 // check if more than one option was used - used later for inputenc
830 if (options.begin() != options.end() - 1)
831 one_language = false;
832 // babel takes the last language of the option of its \usepackage
833 // call as document language. If there is no such language option, the
834 // last language in the documentclass options is used.
835 handle_opt(options, known_languages, h_language);
836 // translate the babel name to a LyX name
837 h_language = babel2lyx(h_language);
838 if (h_language == "japanese") {
839 // For Japanese, the encoding isn't indicated in the source
840 // file, and there's really not much we can do. We could
841 // 1) offer a list of possible encodings to choose from, or
842 // 2) determine the encoding of the file by inspecting it.
843 // For the time being, we leave the encoding alone so that
844 // we don't get iconv errors when making a wrong guess, and
845 // we will output a note at the top of the document
846 // explaining what to do.
847 Encoding const * const enc = encodings.fromIconvName(
848 p.getEncoding(), Encoding::japanese, false);
850 h_inputencoding = enc->name();
851 is_nonCJKJapanese = true;
852 // in this case babel can be removed from the preamble
853 registerAutomaticallyLoadedPackage("babel");
855 // If babel is called with options, LyX puts them by default into the
856 // document class options. This works for most languages, except
857 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
858 // perhaps in future others.
859 // Therefore keep the babel call as it is as the user might have
861 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
863 delete_opt(options, known_languages);
865 h_preamble << "\\usepackage{babel}\n";
866 explicit_babel = true;
870 else if (name == "polyglossia") {
871 h_language_package = "default";
872 h_default_output_format = "pdf4";
873 h_use_non_tex_fonts = true;
875 registerAutomaticallyLoadedPackage("xunicode");
876 if (h_inputencoding == "auto")
877 p.setEncoding("UTF-8");
880 else if (name == "CJK") {
881 // set the encoding to "auto" because it might be set to "default" by the babel handling
882 // and this would not be correct for CJK
883 if (h_inputencoding == "default")
884 h_inputencoding = "auto";
885 registerAutomaticallyLoadedPackage("CJK");
888 else if (name == "CJKutf8") {
889 h_inputencoding = "utf8-cjk";
890 p.setEncoding("UTF-8");
891 registerAutomaticallyLoadedPackage("CJKutf8");
894 else if (name == "fontenc") {
895 h_fontencoding = getStringFromVector(options, ",");
896 /* We could do the following for better round trip support,
897 * but this makes the document less portable, so I skip it:
898 if (h_fontencoding == lyxrc.fontenc)
899 h_fontencoding = "global";
904 else if (name == "inputenc" || name == "luainputenc") {
905 // h_inputencoding is only set when there is not more than one
906 // inputenc option because otherwise h_inputencoding must be
907 // set to "auto" (the default encoding of the document language)
908 // Therefore check that exactly one option is passed to inputenc.
909 // It is also only set when there is not more than one babel
911 if (!options.empty()) {
912 string const encoding = options.back();
913 Encoding const * const enc = encodings.fromLaTeXName(
914 encoding, Encoding::inputenc, true);
917 cerr << "Unknown encoding " << encoding
918 << ". Ignoring." << std::endl;
920 if (!enc->unsafe() && options.size() == 1 && one_language == true)
921 h_inputencoding = enc->name();
922 p.setEncoding(enc->iconvName());
928 else if (name == "srcltx") {
931 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
934 h_output_sync_macro = "\\usepackage{srcltx}";
937 else if (is_known(name, known_old_language_packages)) {
938 // known language packages from the times before babel
939 // if they are found and not also babel, they will be used as
940 // custom language package
941 h_language_package = "\\usepackage{" + name + "}";
944 else if (name == "lyxskak") {
945 // ignore this and its options
946 const char * const o[] = {"ps", "mover", 0};
947 delete_opt(options, o);
950 else if (is_known(name, known_lyx_packages) && options.empty()) {
951 if (name == "splitidx")
952 h_use_indices = "true";
953 if (name == "refstyle")
954 h_use_refstyle = true;
955 else if (name == "prettyref")
956 h_use_refstyle = false;
957 if (!in_lyx_preamble) {
958 h_preamble << package_beg_sep << name
959 << package_mid_sep << "\\usepackage{"
961 if (p.next_token().cat() == catNewline ||
962 (p.next_token().cat() == catSpace &&
963 p.next_next_token().cat() == catNewline))
965 h_preamble << package_end_sep;
969 else if (name == "geometry")
970 handle_geometry(options);
972 else if (name == "subfig")
973 ; // ignore this FIXME: Use the package separator mechanism instead
975 else if ((where = is_known(name, known_languages)))
976 h_language = known_coded_languages[where - known_languages];
978 else if (name == "natbib") {
979 h_biblio_style = "plainnat";
980 h_cite_engine = "natbib";
981 h_cite_engine_type = "authoryear";
982 vector<string>::iterator it =
983 find(options.begin(), options.end(), "authoryear");
984 if (it != options.end())
987 it = find(options.begin(), options.end(), "numbers");
988 if (it != options.end()) {
989 h_cite_engine_type = "numerical";
995 else if (name == "jurabib") {
996 h_biblio_style = "jurabib";
997 h_cite_engine = "jurabib";
998 h_cite_engine_type = "authoryear";
1001 else if (name == "bibtopic")
1002 h_use_bibtopic = "true";
1004 else if (name == "hyperref")
1005 handle_hyperref(options);
1007 else if (name == "algorithm2e") {
1008 // Load "algorithm2e" module
1009 addModule("algorithm2e");
1010 // Add the package options to the global document options
1011 if (!options.empty()) {
1012 if (h_options.empty())
1013 h_options = join(options, ",");
1015 h_options += ',' + join(options, ",");
1018 else if (name == "microtype") {
1019 //we internally support only microtype without params
1020 if (options.empty())
1021 h_use_microtype = true;
1023 h_preamble << "\\usepackage[" << opts << "]{microtype}";
1026 else if (!in_lyx_preamble) {
1027 if (options.empty())
1028 h_preamble << "\\usepackage{" << name << '}';
1030 h_preamble << "\\usepackage[" << opts << "]{"
1034 if (p.next_token().cat() == catNewline ||
1035 (p.next_token().cat() == catSpace &&
1036 p.next_next_token().cat() == catNewline))
1040 // We need to do something with the options...
1041 if (!options.empty() && !detectEncoding)
1042 cerr << "Ignoring options '" << join(options, ",")
1043 << "' of package " << name << '.' << endl;
1045 // remove the whitespace
1050 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1053 Token t = p.get_token();
1054 if (t.cat() == catEscape &&
1055 is_known(t.cs(), known_if_commands))
1056 handle_if(p, in_lyx_preamble);
1058 if (!in_lyx_preamble)
1059 h_preamble << t.asInput();
1060 if (t.cat() == catEscape && t.cs() == "fi")
1067 bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir)
1069 // set the quote language
1070 // LyX only knows the following quotes languages:
1071 // english, swedish, german, polish, french and danish
1072 // (quotes for "japanese" and "chinese-traditional" are missing because
1073 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
1074 // conversion list taken from
1075 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1076 // (quotes for kazakh and interlingua are unknown)
1078 if (is_known(h_language, known_danish_quotes_languages))
1079 h_quotes_language = "danish";
1081 else if (is_known(h_language, known_french_quotes_languages))
1082 h_quotes_language = "french";
1084 else if (is_known(h_language, known_german_quotes_languages))
1085 h_quotes_language = "german";
1087 else if (is_known(h_language, known_polish_quotes_languages))
1088 h_quotes_language = "polish";
1090 else if (is_known(h_language, known_swedish_quotes_languages))
1091 h_quotes_language = "swedish";
1093 else if (is_known(h_language, known_english_quotes_languages))
1094 h_quotes_language = "english";
1096 if (contains(h_float_placement, "H"))
1097 registerAutomaticallyLoadedPackage("float");
1098 if (h_spacing != "single" && h_spacing != "default")
1099 registerAutomaticallyLoadedPackage("setspace");
1100 if (h_use_packages["amsmath"] == "2") {
1101 // amsbsy and amstext are already provided by amsmath
1102 registerAutomaticallyLoadedPackage("amsbsy");
1103 registerAutomaticallyLoadedPackage("amstext");
1106 // output the LyX file settings
1107 // Important: Keep the version formatting in sync with LyX and
1108 // lyx2lyx (bug 7951)
1109 string const origin = roundtripMode() ? "roundtrip" : outfiledir;
1110 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1111 << lyx_version_minor << '\n'
1112 << "\\lyxformat " << LYX_FORMAT << '\n'
1113 << "\\begin_document\n"
1114 << "\\begin_header\n"
1115 << "\\save_transient_properties " << h_save_transient_properties << "\n"
1116 << "\\origin " << origin << "\n"
1117 << "\\textclass " << h_textclass << "\n";
1118 string const raw = subdoc ? empty_string() : h_preamble.str();
1120 os << "\\begin_preamble\n";
1121 for (string::size_type i = 0; i < raw.size(); ++i) {
1122 if (raw[i] == package_beg_sep) {
1123 // Here follows some package loading code that
1124 // must be skipped if the package is loaded
1126 string::size_type j = raw.find(package_mid_sep, i);
1127 if (j == string::npos)
1129 string::size_type k = raw.find(package_end_sep, j);
1130 if (k == string::npos)
1132 string const package = raw.substr(i + 1, j - i - 1);
1133 string const replacement = raw.substr(j + 1, k - j - 1);
1134 if (auto_packages.find(package) == auto_packages.end())
1140 os << "\n\\end_preamble\n";
1142 if (!h_options.empty())
1143 os << "\\options " << h_options << "\n";
1144 os << "\\use_default_options " << h_use_default_options << "\n";
1145 if (!used_modules.empty()) {
1146 os << "\\begin_modules\n";
1147 vector<string>::const_iterator const end = used_modules.end();
1148 vector<string>::const_iterator it = used_modules.begin();
1149 for (; it != end; ++it)
1151 os << "\\end_modules\n";
1153 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1154 << "\\language " << h_language << "\n"
1155 << "\\language_package " << h_language_package << "\n"
1156 << "\\inputencoding " << h_inputencoding << "\n"
1157 << "\\fontencoding " << h_fontencoding << "\n"
1158 << "\\font_roman \"" << h_font_roman[0]
1159 << "\" \"" << h_font_roman[1] << "\"\n"
1160 << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n"
1161 << "\\font_typewriter \"" << h_font_typewriter[0]
1162 << "\" \"" << h_font_typewriter[1] << "\"\n"
1163 << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n"
1164 << "\\font_default_family " << h_font_default_family << "\n"
1165 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1166 << "\\font_sc " << h_font_sc << "\n"
1167 << "\\font_osf " << h_font_osf << "\n"
1168 << "\\font_sf_scale " << h_font_sf_scale[0]
1169 << ' ' << h_font_sf_scale[1] << '\n'
1170 << "\\font_tt_scale " << h_font_tt_scale[0]
1171 << ' ' << h_font_tt_scale[1] << '\n';
1172 if (!h_font_cjk.empty())
1173 os << "\\font_cjk " << h_font_cjk << '\n';
1174 os << "\\use_microtype " << h_use_microtype << '\n'
1175 << "\\graphics " << h_graphics << '\n'
1176 << "\\default_output_format " << h_default_output_format << "\n"
1177 << "\\output_sync " << h_output_sync << "\n";
1178 if (h_output_sync == "1")
1179 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1180 os << "\\bibtex_command " << h_bibtex_command << "\n"
1181 << "\\index_command " << h_index_command << "\n";
1182 if (!h_float_placement.empty())
1183 os << "\\float_placement " << h_float_placement << "\n";
1184 os << "\\paperfontsize " << h_paperfontsize << "\n"
1185 << "\\spacing " << h_spacing << "\n"
1186 << "\\use_hyperref " << h_use_hyperref << '\n';
1187 if (h_use_hyperref == "true") {
1188 if (!h_pdf_title.empty())
1189 os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n';
1190 if (!h_pdf_author.empty())
1191 os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n';
1192 if (!h_pdf_subject.empty())
1193 os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n';
1194 if (!h_pdf_keywords.empty())
1195 os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n';
1196 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1197 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1198 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1199 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1200 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1201 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1202 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1203 "\\pdf_backref " << h_pdf_backref << "\n"
1204 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1205 if (!h_pdf_pagemode.empty())
1206 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1207 if (!h_pdf_quoted_options.empty())
1208 os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n';
1210 os << "\\papersize " << h_papersize << "\n"
1211 << "\\use_geometry " << h_use_geometry << '\n';
1212 for (map<string, string>::const_iterator it = h_use_packages.begin();
1213 it != h_use_packages.end(); ++it)
1214 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1215 os << "\\cite_engine " << h_cite_engine << '\n'
1216 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1217 << "\\biblio_style " << h_biblio_style << "\n"
1218 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1219 << "\\use_indices " << h_use_indices << "\n"
1220 << "\\paperorientation " << h_paperorientation << '\n'
1221 << "\\suppress_date " << h_suppress_date << '\n'
1222 << "\\justification " << h_justification << '\n'
1223 << "\\use_refstyle " << h_use_refstyle << '\n';
1224 if (!h_fontcolor.empty())
1225 os << "\\fontcolor " << h_fontcolor << '\n';
1226 if (!h_notefontcolor.empty())
1227 os << "\\notefontcolor " << h_notefontcolor << '\n';
1228 if (!h_backgroundcolor.empty())
1229 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1230 if (!h_boxbgcolor.empty())
1231 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1232 if (index_number != 0)
1233 for (int i = 0; i < index_number; i++) {
1234 os << "\\index " << h_index[i] << '\n'
1235 << "\\shortcut " << h_shortcut[i] << '\n'
1236 << "\\color " << h_color << '\n'
1240 os << "\\index " << h_index[0] << '\n'
1241 << "\\shortcut " << h_shortcut[0] << '\n'
1242 << "\\color " << h_color << '\n'
1246 << "\\secnumdepth " << h_secnumdepth << "\n"
1247 << "\\tocdepth " << h_tocdepth << "\n"
1248 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1249 if (h_paragraph_separation == "skip")
1250 os << "\\defskip " << h_defskip << "\n";
1252 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1253 os << "\\quotes_language " << h_quotes_language << "\n"
1254 << "\\papercolumns " << h_papercolumns << "\n"
1255 << "\\papersides " << h_papersides << "\n"
1256 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1257 if (!h_listings_params.empty())
1258 os << "\\listings_params " << h_listings_params << "\n";
1259 os << "\\tracking_changes " << h_tracking_changes << "\n"
1260 << "\\output_changes " << h_output_changes << "\n"
1261 << "\\html_math_output " << h_html_math_output << "\n"
1262 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1263 << "\\html_be_strict " << h_html_be_strict << "\n"
1265 << "\\end_header\n\n"
1266 << "\\begin_body\n";
1271 void Preamble::parse(Parser & p, string const & forceclass,
1272 TeX2LyXDocClass & tc)
1274 // initialize fixed types
1275 special_columns_['D'] = 3;
1276 parse(p, forceclass, false, tc);
1280 void Preamble::parse(Parser & p, string const & forceclass,
1281 bool detectEncoding, TeX2LyXDocClass & tc)
1283 bool is_full_document = false;
1284 bool is_lyx_file = false;
1285 bool in_lyx_preamble = false;
1287 // determine whether this is a full document or a fragment for inclusion
1289 Token const & t = p.get_token();
1291 if (t.cat() == catEscape && t.cs() == "documentclass") {
1292 is_full_document = true;
1298 if (detectEncoding && !is_full_document)
1301 while (is_full_document && p.good()) {
1302 if (detectEncoding && h_inputencoding != "auto" &&
1303 h_inputencoding != "default")
1306 Token const & t = p.get_token();
1309 if (!detectEncoding)
1310 cerr << "t: " << t << '\n';
1316 if (!in_lyx_preamble &&
1317 (t.cat() == catLetter ||
1318 t.cat() == catSuper ||
1319 t.cat() == catSub ||
1320 t.cat() == catOther ||
1321 t.cat() == catMath ||
1322 t.cat() == catActive ||
1323 t.cat() == catBegin ||
1324 t.cat() == catEnd ||
1325 t.cat() == catAlign ||
1326 t.cat() == catParameter))
1327 h_preamble << t.cs();
1329 else if (!in_lyx_preamble &&
1330 (t.cat() == catSpace || t.cat() == catNewline))
1331 h_preamble << t.asInput();
1333 else if (t.cat() == catComment) {
1334 static regex const islyxfile("%% LyX .* created this file");
1335 static regex const usercommands("User specified LaTeX commands");
1337 string const comment = t.asInput();
1339 // magically switch encoding default if it looks like XeLaTeX
1340 static string const magicXeLaTeX =
1341 "% This document must be compiled with XeLaTeX ";
1342 if (comment.size() > magicXeLaTeX.size()
1343 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1344 && h_inputencoding == "auto") {
1345 if (!detectEncoding)
1346 cerr << "XeLaTeX comment found, switching to UTF8\n";
1347 h_inputencoding = "utf8";
1350 if (regex_search(comment, sub, islyxfile)) {
1352 in_lyx_preamble = true;
1353 } else if (is_lyx_file
1354 && regex_search(comment, sub, usercommands))
1355 in_lyx_preamble = false;
1356 else if (!in_lyx_preamble)
1357 h_preamble << t.asInput();
1360 else if (t.cs() == "pagestyle")
1361 h_paperpagestyle = p.verbatim_item();
1363 else if (t.cs() == "setdefaultlanguage") {
1365 // We don't yet care about non-language variant options
1366 // because LyX doesn't support this yet, see bug #8214
1368 string langopts = p.getOpt();
1369 // check if the option contains a variant, if yes, extract it
1370 string::size_type pos_var = langopts.find("variant");
1371 string::size_type i = langopts.find(',', pos_var);
1372 string::size_type k = langopts.find('=', pos_var);
1373 if (pos_var != string::npos){
1375 if (i == string::npos)
1376 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1378 variant = langopts.substr(k + 1, i - k - 1);
1379 h_language = variant;
1383 h_language = p.verbatim_item();
1384 //finally translate the poyglossia name to a LyX name
1385 h_language = polyglossia2lyx(h_language);
1388 else if (t.cs() == "setotherlanguage") {
1389 // We don't yet care about the option because LyX doesn't
1390 // support this yet, see bug #8214
1391 p.hasOpt() ? p.getOpt() : string();
1395 else if (t.cs() == "setmainfont") {
1396 // we don't care about the option
1397 p.hasOpt() ? p.getOpt() : string();
1398 h_font_roman[1] = p.getArg('{', '}');
1401 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1402 // LyX currently only supports the scale option
1405 string fontopts = p.getArg('[', ']');
1406 // check if the option contains a scaling, if yes, extract it
1407 string::size_type pos = fontopts.find("Scale");
1408 if (pos != string::npos) {
1409 string::size_type i = fontopts.find(',', pos);
1410 if (i == string::npos)
1411 scale_as_percentage(fontopts.substr(pos + 1), scale);
1413 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1416 if (t.cs() == "setsansfont") {
1418 h_font_sf_scale[1] = scale;
1419 h_font_sans[1] = p.getArg('{', '}');
1422 h_font_tt_scale[1] = scale;
1423 h_font_typewriter[1] = p.getArg('{', '}');
1427 else if (t.cs() == "date") {
1428 string argument = p.getArg('{', '}');
1429 if (argument.empty())
1430 h_suppress_date = "true";
1432 h_preamble << t.asInput() << '{' << argument << '}';
1435 else if (t.cs() == "color") {
1436 string const space =
1437 (p.hasOpt() ? p.getOpt() : string());
1438 string argument = p.getArg('{', '}');
1439 // check the case that a standard color is used
1440 if (space.empty() && is_known(argument, known_basic_colors)) {
1441 h_fontcolor = rgbcolor2code(argument);
1442 registerAutomaticallyLoadedPackage("color");
1443 } else if (space.empty() && argument == "document_fontcolor")
1444 registerAutomaticallyLoadedPackage("color");
1445 // check the case that LyX's document_fontcolor is defined
1446 // but not used for \color
1448 h_preamble << t.asInput();
1450 h_preamble << space;
1451 h_preamble << '{' << argument << '}';
1452 // the color might already be set because \definecolor
1453 // is parsed before this
1458 else if (t.cs() == "pagecolor") {
1459 string argument = p.getArg('{', '}');
1460 // check the case that a standard color is used
1461 if (is_known(argument, known_basic_colors)) {
1462 h_backgroundcolor = rgbcolor2code(argument);
1463 } else if (argument == "page_backgroundcolor")
1464 registerAutomaticallyLoadedPackage("color");
1465 // check the case that LyX's page_backgroundcolor is defined
1466 // but not used for \pagecolor
1468 h_preamble << t.asInput() << '{' << argument << '}';
1469 // the color might already be set because \definecolor
1470 // is parsed before this
1471 h_backgroundcolor = "";
1475 else if (t.cs() == "makeatletter") {
1476 // LyX takes care of this
1477 p.setCatcode('@', catLetter);
1480 else if (t.cs() == "makeatother") {
1481 // LyX takes care of this
1482 p.setCatcode('@', catOther);
1485 else if (t.cs() == "makeindex") {
1486 // LyX will re-add this if a print index command is found
1490 else if (t.cs() == "newindex") {
1491 string const indexname = p.getArg('[', ']');
1492 string const shortcut = p.verbatim_item();
1493 if (!indexname.empty())
1494 h_index[index_number] = indexname;
1496 h_index[index_number] = shortcut;
1497 h_shortcut[index_number] = shortcut;
1502 else if (t.cs() == "RS@ifundefined") {
1503 string const name = p.verbatim_item();
1504 string const body1 = p.verbatim_item();
1505 string const body2 = p.verbatim_item();
1506 // only non-lyxspecific stuff
1507 if (in_lyx_preamble &&
1508 (name == "subsecref" || name == "thmref" || name == "lemref"))
1512 ss << '\\' << t.cs();
1513 ss << '{' << name << '}'
1514 << '{' << body1 << '}'
1515 << '{' << body2 << '}';
1516 h_preamble << ss.str();
1520 else if (t.cs() == "AtBeginDocument") {
1521 string const name = p.verbatim_item();
1522 // only non-lyxspecific stuff
1523 if (in_lyx_preamble &&
1524 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1525 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1526 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1527 || name == "\\providecommand\\subsecref[1]{\\ref{subsec:#1}}"
1528 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1529 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1530 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1531 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1532 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1533 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1534 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1535 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1536 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1537 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1538 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1542 ss << '\\' << t.cs();
1543 ss << '{' << name << '}';
1544 h_preamble << ss.str();
1548 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1549 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1550 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1551 || t.cs() == "DeclareRobustCommand"
1552 || t.cs() == "DeclareRobustCommandx"
1553 || t.cs() == "ProvideTextCommandDefault"
1554 || t.cs() == "DeclareMathAccent") {
1556 if (p.next_token().character() == '*') {
1560 string const name = p.verbatim_item();
1561 string const opt1 = p.getFullOpt();
1562 string const opt2 = p.getFullOpt();
1563 string const body = p.verbatim_item();
1564 // store the in_lyx_preamble setting
1565 bool const was_in_lyx_preamble = in_lyx_preamble;
1567 if (name == "\\rmdefault")
1568 if (is_known(body, known_roman_fonts)) {
1569 h_font_roman[0] = body;
1571 in_lyx_preamble = true;
1573 if (name == "\\sfdefault")
1574 if (is_known(body, known_sans_fonts)) {
1575 h_font_sans[0] = body;
1577 in_lyx_preamble = true;
1579 if (name == "\\ttdefault")
1580 if (is_known(body, known_typewriter_fonts)) {
1581 h_font_typewriter[0] = body;
1583 in_lyx_preamble = true;
1585 if (name == "\\familydefault") {
1586 string family = body;
1587 // remove leading "\"
1588 h_font_default_family = family.erase(0,1);
1590 in_lyx_preamble = true;
1593 // remove LyX-specific definitions that are re-added by LyX
1595 // \lyxline is an ancient command that is converted by tex2lyx into
1596 // a \rule therefore remove its preamble code
1597 if (name == "\\lyxdot" || name == "\\lyxarrow"
1598 || name == "\\lyxline" || name == "\\LyX") {
1600 in_lyx_preamble = true;
1603 // Add the command to the known commands
1604 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1606 // only non-lyxspecific stuff
1607 if (!in_lyx_preamble) {
1609 ss << '\\' << t.cs();
1612 ss << '{' << name << '}' << opt1 << opt2
1613 << '{' << body << "}";
1614 h_preamble << ss.str();
1616 ostream & out = in_preamble ? h_preamble : os;
1617 out << "\\" << t.cs() << "{" << name << "}"
1618 << opts << "{" << body << "}";
1621 // restore the in_lyx_preamble setting
1622 in_lyx_preamble = was_in_lyx_preamble;
1625 else if (t.cs() == "documentclass") {
1626 vector<string>::iterator it;
1627 vector<string> opts = split_options(p.getArg('[', ']'));
1628 handle_opt(opts, known_fontsizes, h_paperfontsize);
1629 delete_opt(opts, known_fontsizes);
1630 // delete "pt" at the end
1631 string::size_type i = h_paperfontsize.find("pt");
1632 if (i != string::npos)
1633 h_paperfontsize.erase(i);
1634 // The documentclass options are always parsed before the options
1635 // of the babel call so that a language cannot overwrite the babel
1637 handle_opt(opts, known_languages, h_language);
1638 delete_opt(opts, known_languages);
1640 // paper orientation
1641 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1642 h_paperorientation = "landscape";
1646 if ((it = find(opts.begin(), opts.end(), "oneside"))
1651 if ((it = find(opts.begin(), opts.end(), "twoside"))
1657 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1659 h_papercolumns = "1";
1662 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1664 h_papercolumns = "2";
1668 // some size options are known to any document classes, other sizes
1669 // are handled by the \geometry command of the geometry package
1670 handle_opt(opts, known_class_paper_sizes, h_papersize);
1671 delete_opt(opts, known_class_paper_sizes);
1672 // the remaining options
1673 h_options = join(opts, ",");
1674 // FIXME This does not work for classes that have a
1675 // different name in LyX than in LaTeX
1676 h_textclass = p.getArg('{', '}');
1680 else if (t.cs() == "usepackage") {
1681 string const options = p.getArg('[', ']');
1682 string const name = p.getArg('{', '}');
1683 vector<string> vecnames;
1684 split(name, vecnames, ',');
1685 vector<string>::const_iterator it = vecnames.begin();
1686 vector<string>::const_iterator end = vecnames.end();
1687 for (; it != end; ++it)
1688 handle_package(p, trimSpaceAndEol(*it), options,
1689 in_lyx_preamble, detectEncoding);
1692 else if (t.cs() == "inputencoding") {
1693 string const encoding = p.getArg('{','}');
1694 Encoding const * const enc = encodings.fromLaTeXName(
1695 encoding, Encoding::inputenc, true);
1697 if (!detectEncoding)
1698 cerr << "Unknown encoding " << encoding
1699 << ". Ignoring." << std::endl;
1702 h_inputencoding = enc->name();
1703 p.setEncoding(enc->iconvName());
1707 else if (t.cs() == "newenvironment") {
1708 string const name = p.getArg('{', '}');
1709 string const opt1 = p.getFullOpt();
1710 string const opt2 = p.getFullOpt();
1711 string const beg = p.verbatim_item();
1712 string const end = p.verbatim_item();
1713 if (!in_lyx_preamble) {
1714 h_preamble << "\\newenvironment{" << name
1715 << '}' << opt1 << opt2 << '{'
1716 << beg << "}{" << end << '}';
1718 add_known_environment(name, opt1, !opt2.empty(),
1719 from_utf8(beg), from_utf8(end));
1723 else if (t.cs() == "newtheorem") {
1724 string const name = p.getArg('{', '}');
1725 string const opt1 = p.getFullOpt();
1726 string const opt2 = p.getFullOpt();
1727 string const body = p.verbatim_item();
1728 string const opt3 = p.getFullOpt();
1730 string const complete = "\\newtheorem{" + name + '}' +
1731 opt1 + opt2 + '{' + body + '}' + opt3;
1733 add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete));
1735 if (!in_lyx_preamble)
1736 h_preamble << complete;
1739 else if (t.cs() == "def") {
1740 string name = p.get_token().cs();
1741 // In fact, name may be more than the name:
1742 // In the test case of bug 8116
1743 // name == "csname SF@gobble@opt \endcsname".
1744 // Therefore, we need to use asInput() instead of cs().
1745 while (p.next_token().cat() != catBegin)
1746 name += p.get_token().asInput();
1747 if (!in_lyx_preamble)
1748 h_preamble << "\\def\\" << name << '{'
1749 << p.verbatim_item() << "}";
1752 else if (t.cs() == "newcolumntype") {
1753 string const name = p.getArg('{', '}');
1754 trimSpaceAndEol(name);
1756 string opts = p.getOpt();
1757 if (!opts.empty()) {
1758 istringstream is(string(opts, 1));
1761 special_columns_[name[0]] = nargs;
1762 h_preamble << "\\newcolumntype{" << name << "}";
1764 h_preamble << "[" << nargs << "]";
1765 h_preamble << "{" << p.verbatim_item() << "}";
1768 else if (t.cs() == "setcounter") {
1769 string const name = p.getArg('{', '}');
1770 string const content = p.getArg('{', '}');
1771 if (name == "secnumdepth")
1772 h_secnumdepth = content;
1773 else if (name == "tocdepth")
1774 h_tocdepth = content;
1776 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1779 else if (t.cs() == "setlength") {
1780 string const name = p.verbatim_item();
1781 string const content = p.verbatim_item();
1782 // the paragraphs are only not indented when \parindent is set to zero
1783 if (name == "\\parindent" && content != "") {
1784 if (content[0] == '0')
1785 h_paragraph_separation = "skip";
1787 h_paragraph_indentation = translate_len(content);
1788 } else if (name == "\\parskip") {
1789 if (content == "\\smallskipamount")
1790 h_defskip = "smallskip";
1791 else if (content == "\\medskipamount")
1792 h_defskip = "medskip";
1793 else if (content == "\\bigskipamount")
1794 h_defskip = "bigskip";
1796 h_defskip = translate_len(content);
1798 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1801 else if (t.cs() == "onehalfspacing")
1802 h_spacing = "onehalf";
1804 else if (t.cs() == "doublespacing")
1805 h_spacing = "double";
1807 else if (t.cs() == "setstretch")
1808 h_spacing = "other " + p.verbatim_item();
1810 else if (t.cs() == "synctex") {
1811 // the scheme is \synctex=value
1812 // where value can only be "1" or "-1"
1813 h_output_sync = "1";
1814 // there can be any character behind the value (e.g. a linebreak or a '\'
1815 // therefore we extract it char by char
1817 string value = p.get_token().asInput();
1819 value += p.get_token().asInput();
1820 h_output_sync_macro = "\\synctex=" + value;
1823 else if (t.cs() == "begin") {
1824 string const name = p.getArg('{', '}');
1825 if (name == "document")
1827 h_preamble << "\\begin{" << name << "}";
1830 else if (t.cs() == "geometry") {
1831 vector<string> opts = split_options(p.getArg('{', '}'));
1832 handle_geometry(opts);
1835 else if (t.cs() == "definecolor") {
1836 string const color = p.getArg('{', '}');
1837 string const space = p.getArg('{', '}');
1838 string const value = p.getArg('{', '}');
1839 if (color == "document_fontcolor" && space == "rgb") {
1840 RGBColor c(RGBColorFromLaTeX(value));
1841 h_fontcolor = X11hexname(c);
1842 } else if (color == "note_fontcolor" && space == "rgb") {
1843 RGBColor c(RGBColorFromLaTeX(value));
1844 h_notefontcolor = X11hexname(c);
1845 } else if (color == "page_backgroundcolor" && space == "rgb") {
1846 RGBColor c(RGBColorFromLaTeX(value));
1847 h_backgroundcolor = X11hexname(c);
1848 } else if (color == "shadecolor" && space == "rgb") {
1849 RGBColor c(RGBColorFromLaTeX(value));
1850 h_boxbgcolor = X11hexname(c);
1852 h_preamble << "\\definecolor{" << color
1853 << "}{" << space << "}{" << value
1858 else if (t.cs() == "bibliographystyle")
1859 h_biblio_style = p.verbatim_item();
1861 else if (t.cs() == "jurabibsetup") {
1862 // FIXME p.getArg('{', '}') is most probably wrong (it
1863 // does not handle nested braces).
1864 // Use p.verbatim_item() instead.
1865 vector<string> jurabibsetup =
1866 split_options(p.getArg('{', '}'));
1867 // add jurabibsetup to the jurabib package options
1868 add_package("jurabib", jurabibsetup);
1869 if (!jurabibsetup.empty()) {
1870 h_preamble << "\\jurabibsetup{"
1871 << join(jurabibsetup, ",") << '}';
1875 else if (t.cs() == "hypersetup") {
1876 vector<string> hypersetup =
1877 split_options(p.verbatim_item());
1878 // add hypersetup to the hyperref package options
1879 handle_hyperref(hypersetup);
1880 if (!hypersetup.empty()) {
1881 h_preamble << "\\hypersetup{"
1882 << join(hypersetup, ",") << '}';
1886 else if (is_known(t.cs(), known_if_3arg_commands)) {
1887 // prevent misparsing of \usepackage if it is used
1888 // as an argument (see e.g. our own output of
1889 // \@ifundefined above)
1890 string const arg1 = p.verbatim_item();
1891 string const arg2 = p.verbatim_item();
1892 string const arg3 = p.verbatim_item();
1893 // test case \@ifundefined{date}{}{\date{}}
1894 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1895 arg2.empty() && arg3 == "\\date{}") {
1896 h_suppress_date = "true";
1897 // older tex2lyx versions did output
1898 // \@ifundefined{definecolor}{\usepackage{color}}{}
1899 } else if (t.cs() == "@ifundefined" &&
1900 arg1 == "definecolor" &&
1901 arg2 == "\\usepackage{color}" &&
1903 if (!in_lyx_preamble)
1904 h_preamble << package_beg_sep
1907 << "\\@ifundefined{definecolor}{color}{}"
1910 //\@ifundefined{showcaptionsetup}{}{%
1911 // \PassOptionsToPackage{caption=false}{subfig}}
1912 // that LyX uses for subfloats
1913 } else if (t.cs() == "@ifundefined" &&
1914 arg1 == "showcaptionsetup" && arg2.empty()
1915 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1917 } else if (!in_lyx_preamble) {
1918 h_preamble << t.asInput()
1919 << '{' << arg1 << '}'
1920 << '{' << arg2 << '}'
1921 << '{' << arg3 << '}';
1925 else if (is_known(t.cs(), known_if_commands)) {
1926 // must not parse anything in conditional code, since
1927 // LyX would output the parsed contents unconditionally
1928 if (!in_lyx_preamble)
1929 h_preamble << t.asInput();
1930 handle_if(p, in_lyx_preamble);
1933 else if (!t.cs().empty() && !in_lyx_preamble)
1934 h_preamble << '\\' << t.cs();
1937 // remove the whitespace
1940 // Force textclass if the user wanted it
1941 if (!forceclass.empty())
1942 h_textclass = forceclass;
1943 tc.setName(h_textclass);
1945 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1948 if (h_papersides.empty()) {
1951 h_papersides = ss.str();
1954 // If the CJK package is used we cannot set the document language from
1955 // the babel options. Instead, we guess which language is used most
1956 // and set this one.
1957 default_language = h_language;
1958 if (is_full_document &&
1959 (auto_packages.find("CJK") != auto_packages.end() ||
1960 auto_packages.find("CJKutf8") != auto_packages.end())) {
1962 h_language = guessLanguage(p, default_language);
1964 if (explicit_babel && h_language != default_language) {
1965 // We set the document language to a CJK language,
1966 // but babel is explicitly called in the user preamble
1967 // without options. LyX will not add the default
1968 // language to the document options if it is either
1969 // english, or no text is set as default language.
1970 // Therefore we need to add a language option explicitly.
1971 // FIXME: It would be better to remove all babel calls
1972 // from the user preamble, but this is difficult
1973 // without re-introducing bug 7861.
1974 if (h_options.empty())
1975 h_options = lyx2babel(default_language);
1977 h_options += ',' + lyx2babel(default_language);
1983 string Preamble::parseEncoding(Parser & p, string const & forceclass)
1985 TeX2LyXDocClass dummy;
1986 parse(p, forceclass, true, dummy);
1987 if (h_inputencoding != "auto" && h_inputencoding != "default")
1988 return h_inputencoding;
1993 string babel2lyx(string const & language)
1995 char const * const * where = is_known(language, known_languages);
1997 return known_coded_languages[where - known_languages];
2002 string lyx2babel(string const & language)
2004 char const * const * where = is_known(language, known_coded_languages);
2006 return known_languages[where - known_coded_languages];
2011 string Preamble::polyglossia2lyx(string const & language)
2013 char const * const * where = is_known(language, polyglossia_languages);
2015 return coded_polyglossia_languages[where - polyglossia_languages];
2020 string rgbcolor2code(string const & name)
2022 char const * const * where = is_known(name, known_basic_colors);
2024 // "red", "green" etc
2025 return known_basic_color_codes[where - known_basic_colors];
2027 // "255,0,0", "0,255,0" etc
2028 RGBColor c(RGBColorFromLaTeX(name));
2029 return X11hexname(c);