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;
42 // special columntypes
43 extern map<char, int> special_columns;
49 // CJK languages are handled in text.cpp, polyglossia languages are listed
52 * known babel language names (including synonyms)
53 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
54 * please keep this in sync with known_coded_languages line by line!
56 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
57 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
58 "bahasam", "basque", "belarusian", "brazil", "brazilian", "breton", "british",
59 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
60 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
61 "french", "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb",
62 "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
63 "irish", "italian", "japanese", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
64 "lowersorbian", "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian",
65 "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman", "nynorsk",
66 "polutonikogreek", "polish", "portuges", "portuguese", "romanian", "russian",
67 "russianb", "samin", "scottish", "serbian", "serbian-latin", "slovak",
68 "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen",
69 "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian",
74 * the same as known_languages with .lyx names
75 * please keep this in sync with known_languages line by line!
77 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
78 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
79 "bahasam", "basque", "belarusian", "brazilian", "brazilian", "breton", "british",
80 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
81 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
82 "french", "french", "french", "french", "galician", "german", "german",
83 "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
84 "irish", "italian", "japanese", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
85 "lowersorbian", "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian",
86 "newzealand", "ngerman", "ngerman", "norsk", "german-ch", "nynorsk",
87 "polutonikogreek", "polish", "portuguese", "portuguese", "romanian", "russian",
88 "russian", "samin", "scottish", "serbian", "serbian-latin", "slovak",
89 "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen",
90 "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian",
91 "vietnamese", "welsh",
94 /// languages with danish quotes (.lyx names)
95 const char * const known_danish_quotes_languages[] = {"danish", 0};
97 /// languages with english quotes (.lyx names)
98 const char * const known_english_quotes_languages[] = {"american", "australian",
99 "bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english",
100 "esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish",
103 /// languages with french quotes (.lyx names)
104 const char * const known_french_quotes_languages[] = {"albanian",
105 "arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french",
106 "galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek",
107 "russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian",
110 /// languages with german quotes (.lyx names)
111 const char * const known_german_quotes_languages[] = {"austrian", "bulgarian",
112 "czech", "german", "icelandic", "lithuanian", "lowersorbian", "naustrian",
113 "ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0};
115 /// languages with polish quotes (.lyx names)
116 const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian",
117 "dutch", "estonian", "magyar", "polish", "romanian", 0};
119 /// languages with swedish quotes (.lyx names)
120 const char * const known_swedish_quotes_languages[] = {"finnish",
123 /// known language packages from the times before babel
124 const char * const known_old_language_packages[] = {"french", "frenchle",
125 "frenchpro", "german", "ngerman", "pmfrench", 0};
127 char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 };
129 const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman",
130 "ccfonts", "chancery", "charter", "cmr", "fourier", "garamondx", "libertine",
131 "libertine-type1", "lmodern", "mathdesign", "mathpazo", "mathptmx", "newcent",
132 "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", 0};
134 const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum-type1",
135 "cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc", "kurier",
136 "kurierc", "kurierl", "kurierlc", "lmss", "tgadventor", "tgheros", 0};
138 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
139 "courier", "lmtt", "luximono", "fourier", "libertineMono-type1", "lmodern",
140 "mathpazo", "mathptmx", "newcent", "tgcursor", "txtt", 0};
142 const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0};
144 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
145 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
146 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
147 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
148 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
150 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
151 "executivepaper", "legalpaper", "letterpaper", 0};
153 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
154 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
156 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
157 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
160 /// commands that can start an \if...\else...\endif sequence
161 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
162 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
163 "ifsidecap", "ifupgreek", 0};
165 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
166 "magenta", "red", "white", "yellow", 0};
168 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff",
169 "#00ff00", "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
171 /// conditional commands with three arguments like \@ifundefined{}{}{}
172 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
175 /// packages that work only in xetex
176 /// polyglossia is handled separately
177 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
178 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
179 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
181 /// packages that are automatically skipped if loaded by LyX
182 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
183 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color",
184 "float", "fontspec", "graphicx", "hhline", "ifthen", "longtable", "makeidx",
185 "multirow", "nomencl", "pdfpages", "prettyref", "refstyle", "rotating",
186 "rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa", "tipx",
187 "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xunicode", 0};
189 // used for the handling of \newindex
190 int index_number = 0;
192 // codes used to remove packages that are loaded automatically by LyX.
193 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
194 const char package_beg_sep = '\001';
195 const char package_mid_sep = '\002';
196 const char package_end_sep = '\003';
199 // returns true if at least one of the options in what has been found
200 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
206 // the last language option is the document language (for babel and LyX)
207 // the last size option is the document font size
208 vector<string>::iterator it;
209 vector<string>::iterator position = opts.begin();
210 for (; *what; ++what) {
211 it = find(opts.begin(), opts.end(), *what);
212 if (it != opts.end()) {
213 if (it >= position) {
224 void delete_opt(vector<string> & opts, char const * const * what)
229 // remove found options from the list
230 // do this after handle_opt to avoid potential memory leaks
231 vector<string>::iterator it;
232 for (; *what; ++what) {
233 it = find(opts.begin(), opts.end(), *what);
234 if (it != opts.end())
241 * Split a package options string (keyval format) into a vector.
243 * authorformat=smallcaps,
245 * titleformat=colonsep,
246 * bibformat={tabular,ibidem,numbered}
248 vector<string> split_options(string const & input)
250 vector<string> options;
254 Token const & t = p.get_token();
255 if (t.asInput() == ",") {
256 options.push_back(trimSpaceAndEol(option));
258 } else if (t.asInput() == "=") {
261 if (p.next_token().asInput() == "{")
262 option += '{' + p.getArg('{', '}') + '}';
263 } else if (t.cat() != catSpace)
264 option += t.asInput();
268 options.push_back(trimSpaceAndEol(option));
275 * Retrieve a keyval option "name={value with=sign}" named \p name from
276 * \p options and return the value.
277 * The found option is also removed from \p options.
279 string process_keyval_opt(vector<string> & options, string name)
281 for (size_t i = 0; i < options.size(); ++i) {
282 vector<string> option;
283 split(options[i], option, '=');
284 if (option.size() < 2)
286 if (option[0] == name) {
287 options.erase(options.begin() + i);
288 option.erase(option.begin());
289 return join(option, "=");
295 } // anonymous namespace
299 * known polyglossia language names (including variants)
300 * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc)
302 const char * const Preamble::polyglossia_languages[] = {
303 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
304 "nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil",
305 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
306 "irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish",
307 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
308 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "tibetan", "urdu",
309 "brazil", "brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton",
310 "french", "lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar",
311 "slovak", "welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek",
312 "marathi", "spanish", "austrian",
313 "american", "ancient", "australian", "british", "monotonic", "newzealand",
317 * the same as polyglossia_languages with .lyx names
318 * please keep this in sync with polyglossia_languages line by line!
320 const char * const Preamble::coded_polyglossia_languages[] = {
321 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
322 "nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil",
323 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
324 "irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish",
325 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
326 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "tibetan", "urdu",
327 "brazilian", "brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton",
328 "french", "lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar",
329 "slovak", "welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek",
330 "marathi", "spanish", "naustrian",
331 "american", "ancientgreek", "australian", "british", "greek", "newzealand",
332 "polutonikogreek", 0};
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 void Preamble::add_package(string const & name, vector<string> & options)
407 // every package inherits the global options
408 if (used_packages.find(name) == used_packages.end())
409 used_packages[name] = split_options(h_options);
411 vector<string> & v = used_packages[name];
412 v.insert(v.end(), options.begin(), options.end());
413 if (name == "jurabib") {
414 // Don't output the order argument (see the cite command
415 // handling code in text.cpp).
416 vector<string>::iterator end =
417 remove(options.begin(), options.end(), "natbiborder");
418 end = remove(options.begin(), end, "jurabiborder");
419 options.erase(end, options.end());
426 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
427 bool scale_as_percentage(string const & scale, string & percentage)
429 string::size_type pos = scale.find('=');
430 if (pos != string::npos) {
431 string value = scale.substr(pos + 1);
432 if (isStrDbl(value)) {
433 percentage = convert<string>(
434 static_cast<int>(100 * convert<double>(value)));
442 string remove_braces(string const & value)
446 if (value[0] == '{' && value[value.length()-1] == '}')
447 return value.substr(1, value.length()-2);
451 } // anonymous namespace
454 Preamble::Preamble() : one_language(true), explicit_babel(false),
455 title_layout_found(false), h_font_cjk_set(false)
459 h_biblio_style = "plain";
460 h_bibtex_command = "default";
461 h_cite_engine = "basic";
462 h_cite_engine_type = "default";
464 h_defskip = "medskip";
467 h_fontencoding = "default";
468 h_font_roman = "default";
469 h_font_sans = "default";
470 h_font_typewriter = "default";
471 h_font_math = "auto";
472 h_font_default_family = "default";
473 h_use_non_tex_fonts = false;
475 h_font_osf = "false";
476 h_font_sf_scale = "100";
477 h_font_tt_scale = "100";
479 h_graphics = "default";
480 h_default_output_format = "default";
481 h_html_be_strict = "false";
482 h_html_css_as_file = "0";
483 h_html_math_output = "0";
484 h_index[0] = "Index";
485 h_index_command = "default";
486 h_inputencoding = "auto";
487 h_justification = "true";
488 h_language = "english";
489 h_language_package = "none";
491 h_maintain_unincluded_children = "false";
495 h_output_changes = "false";
497 //h_output_sync_macro
498 h_papercolumns = "1";
499 h_paperfontsize = "default";
500 h_paperorientation = "portrait";
501 h_paperpagestyle = "default";
503 h_papersize = "default";
504 h_paragraph_indentation = "default";
505 h_paragraph_separation = "indent";
510 h_pdf_bookmarks = "0";
511 h_pdf_bookmarksnumbered = "0";
512 h_pdf_bookmarksopen = "0";
513 h_pdf_bookmarksopenlevel = "1";
514 h_pdf_breaklinks = "0";
515 h_pdf_pdfborder = "0";
516 h_pdf_colorlinks = "0";
517 h_pdf_backref = "section";
518 h_pdf_pdfusetitle = "0";
520 //h_pdf_quoted_options;
521 h_quotes_language = "english";
523 h_shortcut[0] = "idx";
524 h_spacing = "single";
525 h_suppress_date = "false";
526 h_textclass = "article";
528 h_tracking_changes = "false";
529 h_use_bibtopic = "false";
530 h_use_indices = "false";
531 h_use_geometry = "false";
532 h_use_default_options = "false";
533 h_use_hyperref = "false";
534 h_use_refstyle = false;
535 h_use_packages["amsmath"] = "1";
536 h_use_packages["amssymb"] = "0";
537 h_use_packages["cancel"] = "0";
538 h_use_packages["esint"] = "1";
539 h_use_packages["mhchem"] = "0";
540 h_use_packages["mathdots"] = "0";
541 h_use_packages["mathtools"] = "0";
542 h_use_packages["stackrel"] = "0";
543 h_use_packages["stmaryrd"] = "0";
544 h_use_packages["undertilde"] = "0";
548 void Preamble::handle_hyperref(vector<string> & options)
550 // FIXME swallow inputencoding changes that might surround the
551 // hyperref setup if it was written by LyX
552 h_use_hyperref = "true";
553 // swallow "unicode=true", since LyX does always write that
554 vector<string>::iterator it =
555 find(options.begin(), options.end(), "unicode=true");
556 if (it != options.end())
558 it = find(options.begin(), options.end(), "pdfusetitle");
559 if (it != options.end()) {
560 h_pdf_pdfusetitle = "1";
563 string bookmarks = process_keyval_opt(options, "bookmarks");
564 if (bookmarks == "true")
565 h_pdf_bookmarks = "1";
566 else if (bookmarks == "false")
567 h_pdf_bookmarks = "0";
568 if (h_pdf_bookmarks == "1") {
569 string bookmarksnumbered =
570 process_keyval_opt(options, "bookmarksnumbered");
571 if (bookmarksnumbered == "true")
572 h_pdf_bookmarksnumbered = "1";
573 else if (bookmarksnumbered == "false")
574 h_pdf_bookmarksnumbered = "0";
575 string bookmarksopen =
576 process_keyval_opt(options, "bookmarksopen");
577 if (bookmarksopen == "true")
578 h_pdf_bookmarksopen = "1";
579 else if (bookmarksopen == "false")
580 h_pdf_bookmarksopen = "0";
581 if (h_pdf_bookmarksopen == "1") {
582 string bookmarksopenlevel =
583 process_keyval_opt(options, "bookmarksopenlevel");
584 if (!bookmarksopenlevel.empty())
585 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
588 string breaklinks = process_keyval_opt(options, "breaklinks");
589 if (breaklinks == "true")
590 h_pdf_breaklinks = "1";
591 else if (breaklinks == "false")
592 h_pdf_breaklinks = "0";
593 string pdfborder = process_keyval_opt(options, "pdfborder");
594 if (pdfborder == "{0 0 0}")
595 h_pdf_pdfborder = "1";
596 else if (pdfborder == "{0 0 1}")
597 h_pdf_pdfborder = "0";
598 string backref = process_keyval_opt(options, "backref");
599 if (!backref.empty())
600 h_pdf_backref = backref;
601 string colorlinks = process_keyval_opt(options, "colorlinks");
602 if (colorlinks == "true")
603 h_pdf_colorlinks = "1";
604 else if (colorlinks == "false")
605 h_pdf_colorlinks = "0";
606 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
607 if (!pdfpagemode.empty())
608 h_pdf_pagemode = pdfpagemode;
609 string pdftitle = process_keyval_opt(options, "pdftitle");
610 if (!pdftitle.empty()) {
611 h_pdf_title = remove_braces(pdftitle);
613 string pdfauthor = process_keyval_opt(options, "pdfauthor");
614 if (!pdfauthor.empty()) {
615 h_pdf_author = remove_braces(pdfauthor);
617 string pdfsubject = process_keyval_opt(options, "pdfsubject");
618 if (!pdfsubject.empty())
619 h_pdf_subject = remove_braces(pdfsubject);
620 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
621 if (!pdfkeywords.empty())
622 h_pdf_keywords = remove_braces(pdfkeywords);
623 if (!options.empty()) {
624 if (!h_pdf_quoted_options.empty())
625 h_pdf_quoted_options += ',';
626 h_pdf_quoted_options += join(options, ",");
632 void Preamble::handle_geometry(vector<string> & options)
634 h_use_geometry = "true";
635 vector<string>::iterator it;
637 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
638 h_paperorientation = "landscape";
642 // keyval version: "paper=letter"
643 string paper = process_keyval_opt(options, "paper");
645 h_papersize = paper + "paper";
646 // alternative version: "letterpaper"
647 handle_opt(options, known_paper_sizes, h_papersize);
648 delete_opt(options, known_paper_sizes);
650 char const * const * margin = known_paper_margins;
651 for (; *margin; ++margin) {
652 string value = process_keyval_opt(options, *margin);
653 if (!value.empty()) {
654 int k = margin - known_paper_margins;
655 string name = known_coded_paper_margins[k];
656 h_margins += '\\' + name + ' ' + value + '\n';
662 void Preamble::handle_package(Parser &p, string const & name,
663 string const & opts, bool in_lyx_preamble)
665 vector<string> options = split_options(opts);
666 add_package(name, options);
667 char const * const * where = 0;
669 if (is_known(name, known_xetex_packages)) {
671 h_use_non_tex_fonts = true;
672 registerAutomaticallyLoadedPackage("fontspec");
673 if (h_inputencoding == "auto")
674 p.setEncoding("UTF-8");
678 if (is_known(name, known_roman_fonts))
681 if (name == "fourier") {
682 h_font_roman = "utopia";
683 // when font uses real small capitals
684 if (opts == "expert")
688 if (name == "garamondx") {
689 h_font_roman = "garamondx";
694 if (name == "libertine") {
695 h_font_roman = "libertine";
696 // this automatically invokes biolinum
697 h_font_sans = "biolinum";
700 else if (opts == "lining")
701 h_font_osf = "false";
704 if (name == "libertine-type1") {
705 h_font_roman = "libertine";
706 // NOTE: contrary to libertine.sty, libertine-type1
707 // does not automatically invoke biolinum
708 if (opts == "lining")
709 h_font_osf = "false";
710 else if (opts == "osf")
714 if (name == "mathdesign") {
715 if (opts.find("charter") != string::npos)
716 h_font_roman = "md-charter";
717 if (opts.find("garamond") != string::npos)
718 h_font_roman = "md-garamond";
719 if (opts.find("utopia") != string::npos)
720 h_font_roman = "md-utopia";
721 if (opts.find("expert") != string::npos) {
727 else if (name == "mathpazo")
728 h_font_roman = "palatino";
730 else if (name == "mathptmx")
731 h_font_roman = "times";
734 if (is_known(name, known_sans_fonts)) {
736 if (options.size() >= 1) {
737 if (scale_as_percentage(opts, h_font_sf_scale))
742 if (name == "biolinum-type1") {
743 h_font_sans = "biolinum";
744 // biolinum can have several options, e.g. [osf,scaled=0.97]
745 string::size_type pos = opts.find("osf");
746 if (pos != string::npos)
751 if (is_known(name, known_typewriter_fonts)) {
752 // fourier can be set as roman font _only_
753 // fourier as typewriter is handled in handling of \ttdefault
754 if (name != "fourier") {
755 h_font_typewriter = name;
756 if (options.size() >= 1) {
757 if (scale_as_percentage(opts, h_font_tt_scale))
763 if (name == "libertineMono-type1") {
764 h_font_typewriter = "libertine-mono";
767 // font uses old-style figure
772 if (is_known(name, known_math_fonts))
775 if (name == "newtxmath") {
777 h_font_math = "newtxmath";
778 else if (opts == "garamondx")
779 h_font_math = "garamondx-ntxm";
780 else if (opts == "libertine")
781 h_font_math = "libertine-ntxm";
782 else if (opts == "minion")
783 h_font_math = "minion-ntxm";
788 h_font_math = "iwona-math";
790 if (name == "kurier")
792 h_font_math = "kurier-math";
794 // after the detection and handling of special cases, we can remove the
795 // fonts, otherwise they would appear in the preamble, see bug #7856
796 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
797 || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts))
800 else if (name == "amsmath" || name == "amssymb" || name == "cancel" ||
801 name == "esint" || name == "mhchem" || name == "mathdots" ||
802 name == "mathtools" || name == "stackrel" ||
803 name == "stmaryrd" || name == "undertilde")
804 h_use_packages[name] = "2";
806 else if (name == "babel") {
807 h_language_package = "default";
808 // One might think we would have to do nothing if babel is loaded
809 // without any options to prevent pollution of the preamble with this
810 // babel call in every roundtrip.
811 // But the user could have defined babel-specific things afterwards. So
812 // we need to keep it in the preamble to prevent cases like bug #7861.
814 // check if more than one option was used - used later for inputenc
815 if (options.begin() != options.end() - 1)
816 one_language = false;
817 // babel takes the last language of the option of its \usepackage
818 // call as document language. If there is no such language option, the
819 // last language in the documentclass options is used.
820 handle_opt(options, known_languages, h_language);
821 // translate the babel name to a LyX name
822 h_language = babel2lyx(h_language);
823 if (h_language == "japanese") {
824 // For Japanese, the encoding isn't indicated in the source
825 // file, and there's really not much we can do. We could
826 // 1) offer a list of possible encodings to choose from, or
827 // 2) determine the encoding of the file by inspecting it.
828 // For the time being, we leave the encoding alone so that
829 // we don't get iconv errors when making a wrong guess, and
830 // we will output a note at the top of the document
831 // explaining what to do.
832 Encoding const * const enc = encodings.fromIconvName(
833 p.getEncoding(), Encoding::japanese, false);
835 h_inputencoding = enc->name();
836 is_nonCJKJapanese = true;
837 // in this case babel can be removed from the preamble
838 registerAutomaticallyLoadedPackage("babel");
840 // If babel is called with options, LyX puts them by default into the
841 // document class options. This works for most languages, except
842 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
843 // perhaps in future others.
844 // Therefore keep the babel call as it is as the user might have
846 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
848 delete_opt(options, known_languages);
850 h_preamble << "\\usepackage{babel}\n";
851 explicit_babel = true;
855 else if (name == "polyglossia") {
856 h_language_package = "default";
857 h_default_output_format = "pdf4";
858 h_use_non_tex_fonts = true;
860 registerAutomaticallyLoadedPackage("xunicode");
861 if (h_inputencoding == "auto")
862 p.setEncoding("UTF-8");
865 else if (name == "CJK") {
866 // set the encoding to "auto" because it might be set to "default" by the babel handling
867 // and this would not be correct for CJK
868 if (h_inputencoding == "default")
869 h_inputencoding = "auto";
870 registerAutomaticallyLoadedPackage("CJK");
873 else if (name == "CJKutf8") {
874 h_inputencoding = "utf8-cjk";
875 p.setEncoding("UTF-8");
876 registerAutomaticallyLoadedPackage("CJKutf8");
879 else if (name == "fontenc") {
880 h_fontencoding = getStringFromVector(options, ",");
881 /* We could do the following for better round trip support,
882 * but this makes the document less portable, so I skip it:
883 if (h_fontencoding == lyxrc.fontenc)
884 h_fontencoding = "global";
889 else if (name == "inputenc" || name == "luainputenc") {
890 // h_inputencoding is only set when there is not more than one
891 // inputenc option because otherwise h_inputencoding must be
892 // set to "auto" (the default encoding of the document language)
893 // Therefore check that exactly one option is passed to inputenc.
894 // It is also only set when there is not more than one babel
896 if (!options.empty()) {
897 string const encoding = options.back();
898 Encoding const * const enc = encodings.fromLaTeXName(
899 encoding, Encoding::inputenc, true);
901 cerr << "Unknown encoding " << encoding << ". Ignoring." << std::endl;
903 if (!enc->unsafe() && options.size() == 1 && one_language == true)
904 h_inputencoding = enc->name();
905 p.setEncoding(enc->iconvName());
911 else if (name == "srcltx") {
914 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
917 h_output_sync_macro = "\\usepackage{srcltx}";
920 else if (is_known(name, known_old_language_packages)) {
921 // known language packages from the times before babel
922 // if they are found and not also babel, they will be used as
923 // custom language package
924 h_language_package = "\\usepackage{" + name + "}";
927 else if (name == "lyxskak") {
928 // ignore this and its options
929 const char * const o[] = {"ps", "mover", 0};
930 delete_opt(options, o);
933 else if (is_known(name, known_lyx_packages) && options.empty()) {
934 if (name == "splitidx")
935 h_use_indices = "true";
936 if (name == "refstyle")
937 h_use_refstyle = true;
938 else if (name == "prettyref")
939 h_use_refstyle = false;
940 if (!in_lyx_preamble) {
941 h_preamble << package_beg_sep << name
942 << package_mid_sep << "\\usepackage{"
944 if (p.next_token().cat() == catNewline ||
945 (p.next_token().cat() == catSpace &&
946 p.next_next_token().cat() == catNewline))
948 h_preamble << package_end_sep;
952 else if (name == "geometry")
953 handle_geometry(options);
955 else if (name == "subfig")
956 ; // ignore this FIXME: Use the package separator mechanism instead
958 else if ((where = is_known(name, known_languages)))
959 h_language = known_coded_languages[where - known_languages];
961 else if (name == "natbib") {
962 h_biblio_style = "plainnat";
963 h_cite_engine = "natbib";
964 h_cite_engine_type = "authoryear";
965 vector<string>::iterator it =
966 find(options.begin(), options.end(), "authoryear");
967 if (it != options.end())
970 it = find(options.begin(), options.end(), "numbers");
971 if (it != options.end()) {
972 h_cite_engine_type = "numerical";
978 else if (name == "jurabib") {
979 h_biblio_style = "jurabib";
980 h_cite_engine = "jurabib";
981 h_cite_engine_type = "authoryear";
984 else if (name == "bibtopic")
985 h_use_bibtopic = "true";
987 else if (name == "hyperref")
988 handle_hyperref(options);
990 else if (name == "algorithm2e") {
991 // Load "algorithm2e" module
992 addModule("algorithm2e");
993 // Add the package options to the global document options
994 if (!options.empty()) {
995 if (h_options.empty())
996 h_options = join(options, ",");
998 h_options += ',' + join(options, ",");
1002 else if (!in_lyx_preamble) {
1003 if (options.empty())
1004 h_preamble << "\\usepackage{" << name << '}';
1006 h_preamble << "\\usepackage[" << opts << "]{"
1010 if (p.next_token().cat() == catNewline ||
1011 (p.next_token().cat() == catSpace &&
1012 p.next_next_token().cat() == catNewline))
1016 // We need to do something with the options...
1017 if (!options.empty())
1018 cerr << "Ignoring options '" << join(options, ",")
1019 << "' of package " << name << '.' << endl;
1021 // remove the whitespace
1026 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
1029 Token t = p.get_token();
1030 if (t.cat() == catEscape &&
1031 is_known(t.cs(), known_if_commands))
1032 handle_if(p, in_lyx_preamble);
1034 if (!in_lyx_preamble)
1035 h_preamble << t.asInput();
1036 if (t.cat() == catEscape && t.cs() == "fi")
1043 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
1045 // set the quote language
1046 // LyX only knows the following quotes languages:
1047 // english, swedish, german, polish, french and danish
1048 // (quotes for "japanese" and "chinese-traditional" are missing because
1049 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
1050 // conversion list taken from
1051 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
1052 // (quotes for kazakh and interlingua are unknown)
1054 if (is_known(h_language, known_danish_quotes_languages))
1055 h_quotes_language = "danish";
1057 else if (is_known(h_language, known_french_quotes_languages))
1058 h_quotes_language = "french";
1060 else if (is_known(h_language, known_german_quotes_languages))
1061 h_quotes_language = "german";
1063 else if (is_known(h_language, known_polish_quotes_languages))
1064 h_quotes_language = "polish";
1066 else if (is_known(h_language, known_swedish_quotes_languages))
1067 h_quotes_language = "swedish";
1069 else if (is_known(h_language, known_english_quotes_languages))
1070 h_quotes_language = "english";
1072 if (contains(h_float_placement, "H"))
1073 registerAutomaticallyLoadedPackage("float");
1074 if (h_spacing != "single" && h_spacing != "default")
1075 registerAutomaticallyLoadedPackage("setspace");
1076 if (h_use_packages["amsmath"] == "2") {
1077 // amsbsy and amstext are already provided by amsmath
1078 registerAutomaticallyLoadedPackage("amsbsy");
1079 registerAutomaticallyLoadedPackage("amstext");
1082 // output the LyX file settings
1083 // Important: Keep the version formatting in sync with LyX and
1084 // lyx2lyx (bug 7951)
1085 os << "#LyX file created by tex2lyx " << lyx_version_major << '.'
1086 << lyx_version_minor << '\n'
1087 << "\\lyxformat " << LYX_FORMAT << '\n'
1088 << "\\begin_document\n"
1089 << "\\begin_header\n"
1090 << "\\textclass " << h_textclass << "\n";
1091 string const raw = subdoc ? empty_string() : h_preamble.str();
1093 os << "\\begin_preamble\n";
1094 for (string::size_type i = 0; i < raw.size(); ++i) {
1095 if (raw[i] == package_beg_sep) {
1096 // Here follows some package loading code that
1097 // must be skipped if the package is loaded
1099 string::size_type j = raw.find(package_mid_sep, i);
1100 if (j == string::npos)
1102 string::size_type k = raw.find(package_end_sep, j);
1103 if (k == string::npos)
1105 string const package = raw.substr(i + 1, j - i - 1);
1106 string const replacement = raw.substr(j + 1, k - j - 1);
1107 if (auto_packages.find(package) == auto_packages.end())
1113 os << "\n\\end_preamble\n";
1115 if (!h_options.empty())
1116 os << "\\options " << h_options << "\n";
1117 os << "\\use_default_options " << h_use_default_options << "\n";
1118 if (!used_modules.empty()) {
1119 os << "\\begin_modules\n";
1120 vector<string>::const_iterator const end = used_modules.end();
1121 vector<string>::const_iterator it = used_modules.begin();
1122 for (; it != end; ++it)
1124 os << "\\end_modules\n";
1126 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1127 << "\\language " << h_language << "\n"
1128 << "\\language_package " << h_language_package << "\n"
1129 << "\\inputencoding " << h_inputencoding << "\n"
1130 << "\\fontencoding " << h_fontencoding << "\n"
1131 << "\\font_roman " << h_font_roman << "\n"
1132 << "\\font_sans " << h_font_sans << "\n"
1133 << "\\font_typewriter " << h_font_typewriter << "\n"
1134 << "\\font_math " << h_font_math << "\n"
1135 << "\\font_default_family " << h_font_default_family << "\n"
1136 << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n'
1137 << "\\font_sc " << h_font_sc << "\n"
1138 << "\\font_osf " << h_font_osf << "\n"
1139 << "\\font_sf_scale " << h_font_sf_scale << "\n"
1140 << "\\font_tt_scale " << h_font_tt_scale << '\n';
1141 if (!h_font_cjk.empty())
1142 os << "\\font_cjk " << h_font_cjk << '\n';
1143 os << "\\graphics " << h_graphics << '\n'
1144 << "\\default_output_format " << h_default_output_format << "\n"
1145 << "\\output_sync " << h_output_sync << "\n";
1146 if (h_output_sync == "1")
1147 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1148 os << "\\bibtex_command " << h_bibtex_command << "\n"
1149 << "\\index_command " << h_index_command << "\n";
1150 if (!h_float_placement.empty())
1151 os << "\\float_placement " << h_float_placement << "\n";
1152 os << "\\paperfontsize " << h_paperfontsize << "\n"
1153 << "\\spacing " << h_spacing << "\n"
1154 << "\\use_hyperref " << h_use_hyperref << '\n';
1155 if (h_use_hyperref == "true") {
1156 if (!h_pdf_title.empty())
1157 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
1158 if (!h_pdf_author.empty())
1159 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
1160 if (!h_pdf_subject.empty())
1161 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
1162 if (!h_pdf_keywords.empty())
1163 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
1164 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1165 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1166 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1167 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1168 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1169 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1170 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1171 "\\pdf_backref " << h_pdf_backref << "\n"
1172 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1173 if (!h_pdf_pagemode.empty())
1174 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1175 if (!h_pdf_quoted_options.empty())
1176 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
1178 os << "\\papersize " << h_papersize << "\n"
1179 << "\\use_geometry " << h_use_geometry << '\n';
1180 for (map<string, string>::const_iterator it = h_use_packages.begin();
1181 it != h_use_packages.end(); ++it)
1182 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1183 os << "\\cite_engine " << h_cite_engine << '\n'
1184 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1185 << "\\biblio_style " << h_biblio_style << "\n"
1186 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1187 << "\\use_indices " << h_use_indices << "\n"
1188 << "\\paperorientation " << h_paperorientation << '\n'
1189 << "\\suppress_date " << h_suppress_date << '\n'
1190 << "\\justification " << h_justification << '\n'
1191 << "\\use_refstyle " << h_use_refstyle << '\n';
1192 if (!h_fontcolor.empty())
1193 os << "\\fontcolor " << h_fontcolor << '\n';
1194 if (!h_notefontcolor.empty())
1195 os << "\\notefontcolor " << h_notefontcolor << '\n';
1196 if (!h_backgroundcolor.empty())
1197 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1198 if (!h_boxbgcolor.empty())
1199 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1200 if (index_number != 0)
1201 for (int i = 0; i < index_number; i++) {
1202 os << "\\index " << h_index[i] << '\n'
1203 << "\\shortcut " << h_shortcut[i] << '\n'
1204 << "\\color " << h_color << '\n'
1208 os << "\\index " << h_index[0] << '\n'
1209 << "\\shortcut " << h_shortcut[0] << '\n'
1210 << "\\color " << h_color << '\n'
1214 << "\\secnumdepth " << h_secnumdepth << "\n"
1215 << "\\tocdepth " << h_tocdepth << "\n"
1216 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1217 if (h_paragraph_separation == "skip")
1218 os << "\\defskip " << h_defskip << "\n";
1220 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1221 os << "\\quotes_language " << h_quotes_language << "\n"
1222 << "\\papercolumns " << h_papercolumns << "\n"
1223 << "\\papersides " << h_papersides << "\n"
1224 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1225 if (!h_listings_params.empty())
1226 os << "\\listings_params " << h_listings_params << "\n";
1227 os << "\\tracking_changes " << h_tracking_changes << "\n"
1228 << "\\output_changes " << h_output_changes << "\n"
1229 << "\\html_math_output " << h_html_math_output << "\n"
1230 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1231 << "\\html_be_strict " << h_html_be_strict << "\n"
1233 << "\\end_header\n\n"
1234 << "\\begin_body\n";
1239 void Preamble::parse(Parser & p, string const & forceclass,
1240 TeX2LyXDocClass & tc)
1242 // initialize fixed types
1243 special_columns['D'] = 3;
1244 bool is_full_document = false;
1245 bool is_lyx_file = false;
1246 bool in_lyx_preamble = false;
1248 // determine whether this is a full document or a fragment for inclusion
1250 Token const & t = p.get_token();
1252 if (t.cat() == catEscape && t.cs() == "documentclass") {
1253 is_full_document = true;
1259 while (is_full_document && p.good()) {
1260 Token const & t = p.get_token();
1263 cerr << "t: " << t << "\n";
1269 if (!in_lyx_preamble &&
1270 (t.cat() == catLetter ||
1271 t.cat() == catSuper ||
1272 t.cat() == catSub ||
1273 t.cat() == catOther ||
1274 t.cat() == catMath ||
1275 t.cat() == catActive ||
1276 t.cat() == catBegin ||
1277 t.cat() == catEnd ||
1278 t.cat() == catAlign ||
1279 t.cat() == catParameter))
1280 h_preamble << t.cs();
1282 else if (!in_lyx_preamble &&
1283 (t.cat() == catSpace || t.cat() == catNewline))
1284 h_preamble << t.asInput();
1286 else if (t.cat() == catComment) {
1287 static regex const islyxfile("%% LyX .* created this file");
1288 static regex const usercommands("User specified LaTeX commands");
1290 string const comment = t.asInput();
1292 // magically switch encoding default if it looks like XeLaTeX
1293 static string const magicXeLaTeX =
1294 "% This document must be compiled with XeLaTeX ";
1295 if (comment.size() > magicXeLaTeX.size()
1296 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1297 && h_inputencoding == "auto") {
1298 cerr << "XeLaTeX comment found, switching to UTF8\n";
1299 h_inputencoding = "utf8";
1302 if (regex_search(comment, sub, islyxfile)) {
1304 in_lyx_preamble = true;
1305 } else if (is_lyx_file
1306 && regex_search(comment, sub, usercommands))
1307 in_lyx_preamble = false;
1308 else if (!in_lyx_preamble)
1309 h_preamble << t.asInput();
1312 else if (t.cs() == "pagestyle")
1313 h_paperpagestyle = p.verbatim_item();
1315 else if (t.cs() == "setdefaultlanguage") {
1317 // We don't yet care about non-language variant options
1318 // because LyX doesn't support this yet, see bug #8214
1320 string langopts = p.getOpt();
1321 // check if the option contains a variant, if yes, extract it
1322 string::size_type pos_var = langopts.find("variant");
1323 string::size_type i = langopts.find(',', pos_var);
1324 string::size_type k = langopts.find('=', pos_var);
1325 if (pos_var != string::npos){
1327 if (i == string::npos)
1328 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1330 variant = langopts.substr(k + 1, i - k - 1);
1331 h_language = variant;
1335 h_language = p.verbatim_item();
1336 //finally translate the poyglossia name to a LyX name
1337 h_language = polyglossia2lyx(h_language);
1340 else if (t.cs() == "setotherlanguage") {
1341 // We don't yet care about the option because LyX doesn't
1342 // support this yet, see bug #8214
1343 p.hasOpt() ? p.getOpt() : string();
1347 else if (t.cs() == "setmainfont") {
1348 // we don't care about the option
1349 p.hasOpt() ? p.getOpt() : string();
1350 h_font_roman = p.getArg('{', '}');
1353 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1354 // LyX currently only supports the scale option
1357 string fontopts = p.getArg('[', ']');
1358 // check if the option contains a scaling, if yes, extract it
1359 string::size_type pos = fontopts.find("Scale");
1360 if (pos != string::npos) {
1361 string::size_type i = fontopts.find(',', pos);
1362 if (i == string::npos)
1363 scale_as_percentage(fontopts.substr(pos + 1), scale);
1365 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1368 if (t.cs() == "setsansfont") {
1370 h_font_sf_scale = scale;
1371 h_font_sans = p.getArg('{', '}');
1374 h_font_tt_scale = scale;
1375 h_font_typewriter = p.getArg('{', '}');
1379 else if (t.cs() == "date") {
1380 string argument = p.getArg('{', '}');
1381 if (argument.empty())
1382 h_suppress_date = "true";
1384 h_preamble << t.asInput() << '{' << argument << '}';
1387 else if (t.cs() == "color") {
1388 string const space =
1389 (p.hasOpt() ? p.getOpt() : string());
1390 string argument = p.getArg('{', '}');
1391 // check the case that a standard color is used
1392 if (space.empty() && is_known(argument, known_basic_colors)) {
1393 h_fontcolor = rgbcolor2code(argument);
1394 preamble.registerAutomaticallyLoadedPackage("color");
1395 } else if (space.empty() && argument == "document_fontcolor")
1396 preamble.registerAutomaticallyLoadedPackage("color");
1397 // check the case that LyX's document_fontcolor is defined
1398 // but not used for \color
1400 h_preamble << t.asInput();
1402 h_preamble << space;
1403 h_preamble << '{' << argument << '}';
1404 // the color might already be set because \definecolor
1405 // is parsed before this
1410 else if (t.cs() == "pagecolor") {
1411 string argument = p.getArg('{', '}');
1412 // check the case that a standard color is used
1413 if (is_known(argument, known_basic_colors)) {
1414 h_backgroundcolor = rgbcolor2code(argument);
1415 } else if (argument == "page_backgroundcolor")
1416 preamble.registerAutomaticallyLoadedPackage("color");
1417 // check the case that LyX's page_backgroundcolor is defined
1418 // but not used for \pagecolor
1420 h_preamble << t.asInput() << '{' << argument << '}';
1421 // the color might already be set because \definecolor
1422 // is parsed before this
1423 h_backgroundcolor = "";
1427 else if (t.cs() == "makeatletter") {
1428 // LyX takes care of this
1429 p.setCatcode('@', catLetter);
1432 else if (t.cs() == "makeatother") {
1433 // LyX takes care of this
1434 p.setCatcode('@', catOther);
1437 else if (t.cs() == "makeindex") {
1438 // LyX will re-add this if a print index command is found
1442 else if (t.cs() == "newindex") {
1443 string const indexname = p.getArg('[', ']');
1444 string const shortcut = p.verbatim_item();
1445 if (!indexname.empty())
1446 h_index[index_number] = indexname;
1448 h_index[index_number] = shortcut;
1449 h_shortcut[index_number] = shortcut;
1454 else if (t.cs() == "RS@ifundefined") {
1455 string const name = p.verbatim_item();
1456 string const body1 = p.verbatim_item();
1457 string const body2 = p.verbatim_item();
1458 // only non-lyxspecific stuff
1459 if (in_lyx_preamble &&
1460 (name == "subref" || name == "thmref" || name == "lemref"))
1464 ss << '\\' << t.cs();
1465 ss << '{' << name << '}'
1466 << '{' << body1 << '}'
1467 << '{' << body2 << '}';
1468 h_preamble << ss.str();
1472 else if (t.cs() == "AtBeginDocument") {
1473 string const name = p.verbatim_item();
1474 // only non-lyxspecific stuff
1475 if (in_lyx_preamble &&
1476 (name == "\\providecommand\\partref[1]{\\ref{part:#1}}"
1477 || name == "\\providecommand\\chapref[1]{\\ref{chap:#1}}"
1478 || name == "\\providecommand\\secref[1]{\\ref{sec:#1}}"
1479 || name == "\\providecommand\\subref[1]{\\ref{sub:#1}}"
1480 || name == "\\providecommand\\parref[1]{\\ref{par:#1}}"
1481 || name == "\\providecommand\\figref[1]{\\ref{fig:#1}}"
1482 || name == "\\providecommand\\tabref[1]{\\ref{tab:#1}}"
1483 || name == "\\providecommand\\algref[1]{\\ref{alg:#1}}"
1484 || name == "\\providecommand\\fnref[1]{\\ref{fn:#1}}"
1485 || name == "\\providecommand\\enuref[1]{\\ref{enu:#1}}"
1486 || name == "\\providecommand\\eqref[1]{\\ref{eq:#1}}"
1487 || name == "\\providecommand\\lemref[1]{\\ref{lem:#1}}"
1488 || name == "\\providecommand\\thmref[1]{\\ref{thm:#1}}"
1489 || name == "\\providecommand\\corref[1]{\\ref{cor:#1}}"
1490 || name == "\\providecommand\\propref[1]{\\ref{prop:#1}}"))
1494 ss << '\\' << t.cs();
1495 ss << '{' << name << '}';
1496 h_preamble << ss.str();
1500 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1501 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1502 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1503 || t.cs() == "DeclareRobustCommand"
1504 || t.cs() == "DeclareRobustCommandx"
1505 || t.cs() == "ProvideTextCommandDefault"
1506 || t.cs() == "DeclareMathAccent") {
1508 if (p.next_token().character() == '*') {
1512 string const name = p.verbatim_item();
1513 string const opt1 = p.getFullOpt();
1514 string const opt2 = p.getFullOpt();
1515 string const body = p.verbatim_item();
1516 // store the in_lyx_preamble setting
1517 bool const was_in_lyx_preamble = in_lyx_preamble;
1519 if (name == "\\rmdefault")
1520 if (is_known(body, known_roman_fonts)) {
1521 h_font_roman = body;
1523 in_lyx_preamble = true;
1525 if (name == "\\sfdefault")
1526 if (is_known(body, known_sans_fonts)) {
1529 in_lyx_preamble = true;
1531 if (name == "\\ttdefault")
1532 if (is_known(body, known_typewriter_fonts)) {
1533 h_font_typewriter = body;
1535 in_lyx_preamble = true;
1537 if (name == "\\familydefault") {
1538 string family = body;
1539 // remove leading "\"
1540 h_font_default_family = family.erase(0,1);
1542 in_lyx_preamble = true;
1545 // remove the lyxdot definition that is re-added by LyX
1547 if (name == "\\lyxdot") {
1549 in_lyx_preamble = true;
1552 // Add the command to the known commands
1553 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1555 // only non-lyxspecific stuff
1556 if (!in_lyx_preamble) {
1558 ss << '\\' << t.cs();
1561 ss << '{' << name << '}' << opt1 << opt2
1562 << '{' << body << "}";
1563 h_preamble << ss.str();
1565 ostream & out = in_preamble ? h_preamble : os;
1566 out << "\\" << t.cs() << "{" << name << "}"
1567 << opts << "{" << body << "}";
1570 // restore the in_lyx_preamble setting
1571 in_lyx_preamble = was_in_lyx_preamble;
1574 else if (t.cs() == "documentclass") {
1575 vector<string>::iterator it;
1576 vector<string> opts = split_options(p.getArg('[', ']'));
1577 handle_opt(opts, known_fontsizes, h_paperfontsize);
1578 delete_opt(opts, known_fontsizes);
1579 // delete "pt" at the end
1580 string::size_type i = h_paperfontsize.find("pt");
1581 if (i != string::npos)
1582 h_paperfontsize.erase(i);
1583 // The documentclass options are always parsed before the options
1584 // of the babel call so that a language cannot overwrite the babel
1586 handle_opt(opts, known_languages, h_language);
1587 delete_opt(opts, known_languages);
1589 // paper orientation
1590 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1591 h_paperorientation = "landscape";
1595 if ((it = find(opts.begin(), opts.end(), "oneside"))
1600 if ((it = find(opts.begin(), opts.end(), "twoside"))
1606 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1608 h_papercolumns = "1";
1611 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1613 h_papercolumns = "2";
1617 // some size options are known to any document classes, other sizes
1618 // are handled by the \geometry command of the geometry package
1619 handle_opt(opts, known_class_paper_sizes, h_papersize);
1620 delete_opt(opts, known_class_paper_sizes);
1621 // the remaining options
1622 h_options = join(opts, ",");
1623 // FIXME This does not work for classes that have a
1624 // different name in LyX than in LaTeX
1625 h_textclass = p.getArg('{', '}');
1629 else if (t.cs() == "usepackage") {
1630 string const options = p.getArg('[', ']');
1631 string const name = p.getArg('{', '}');
1632 vector<string> vecnames;
1633 split(name, vecnames, ',');
1634 vector<string>::const_iterator it = vecnames.begin();
1635 vector<string>::const_iterator end = vecnames.end();
1636 for (; it != end; ++it)
1637 handle_package(p, trimSpaceAndEol(*it), options,
1641 else if (t.cs() == "inputencoding") {
1642 string const encoding = p.getArg('{','}');
1643 Encoding const * const enc = encodings.fromLaTeXName(
1644 encoding, Encoding::inputenc, true);
1646 cerr << "Unknown encoding " << encoding << ". Ignoring." << std::endl;
1649 h_inputencoding = enc->name();
1650 p.setEncoding(enc->iconvName());
1654 else if (t.cs() == "newenvironment") {
1655 string const name = p.getArg('{', '}');
1656 string const opt1 = p.getFullOpt();
1657 string const opt2 = p.getFullOpt();
1658 string const beg = p.verbatim_item();
1659 string const end = p.verbatim_item();
1660 if (!in_lyx_preamble) {
1661 h_preamble << "\\newenvironment{" << name
1662 << '}' << opt1 << opt2 << '{'
1663 << beg << "}{" << end << '}';
1665 add_known_environment(name, opt1, !opt2.empty(),
1666 from_utf8(beg), from_utf8(end));
1670 else if (t.cs() == "newtheorem") {
1671 string const name = p.getArg('{', '}');
1672 string const opt1 = p.getFullOpt();
1673 string const opt2 = p.getFullOpt();
1674 string const body = p.verbatim_item();
1675 string const opt3 = p.getFullOpt();
1677 add_known_theorem(name, opt1, !opt2.empty(),
1678 from_utf8("\\newtheorem{" + name + '}' +
1679 opt1 + opt2 + '{' + body + '}' + opt3));
1681 if (!in_lyx_preamble)
1682 h_preamble << "\\newtheorem{" << name << '}'
1683 << opt1 << opt2 << '{' << '}' << opt3;
1686 else if (t.cs() == "def") {
1687 string name = p.get_token().cs();
1688 // In fact, name may be more than the name:
1689 // In the test case of bug 8116
1690 // name == "csname SF@gobble@opt \endcsname".
1691 // Therefore, we need to use asInput() instead of cs().
1692 while (p.next_token().cat() != catBegin)
1693 name += p.get_token().asInput();
1694 if (!in_lyx_preamble)
1695 h_preamble << "\\def\\" << name << '{'
1696 << p.verbatim_item() << "}";
1699 else if (t.cs() == "newcolumntype") {
1700 string const name = p.getArg('{', '}');
1701 trimSpaceAndEol(name);
1703 string opts = p.getOpt();
1704 if (!opts.empty()) {
1705 istringstream is(string(opts, 1));
1708 special_columns[name[0]] = nargs;
1709 h_preamble << "\\newcolumntype{" << name << "}";
1711 h_preamble << "[" << nargs << "]";
1712 h_preamble << "{" << p.verbatim_item() << "}";
1715 else if (t.cs() == "setcounter") {
1716 string const name = p.getArg('{', '}');
1717 string const content = p.getArg('{', '}');
1718 if (name == "secnumdepth")
1719 h_secnumdepth = content;
1720 else if (name == "tocdepth")
1721 h_tocdepth = content;
1723 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1726 else if (t.cs() == "setlength") {
1727 string const name = p.verbatim_item();
1728 string const content = p.verbatim_item();
1729 // the paragraphs are only not indented when \parindent is set to zero
1730 if (name == "\\parindent" && content != "") {
1731 if (content[0] == '0')
1732 h_paragraph_separation = "skip";
1734 h_paragraph_indentation = translate_len(content);
1735 } else if (name == "\\parskip") {
1736 if (content == "\\smallskipamount")
1737 h_defskip = "smallskip";
1738 else if (content == "\\medskipamount")
1739 h_defskip = "medskip";
1740 else if (content == "\\bigskipamount")
1741 h_defskip = "bigskip";
1743 h_defskip = translate_len(content);
1745 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1748 else if (t.cs() == "onehalfspacing")
1749 h_spacing = "onehalf";
1751 else if (t.cs() == "doublespacing")
1752 h_spacing = "double";
1754 else if (t.cs() == "setstretch")
1755 h_spacing = "other " + p.verbatim_item();
1757 else if (t.cs() == "synctex") {
1758 // the scheme is \synctex=value
1759 // where value can only be "1" or "-1"
1760 h_output_sync = "1";
1761 // there can be any character behind the value (e.g. a linebreak or a '\'
1762 // therefore we extract it char by char
1764 string value = p.get_token().asInput();
1766 value += p.get_token().asInput();
1767 h_output_sync_macro = "\\synctex=" + value;
1770 else if (t.cs() == "begin") {
1771 string const name = p.getArg('{', '}');
1772 if (name == "document")
1774 h_preamble << "\\begin{" << name << "}";
1777 else if (t.cs() == "geometry") {
1778 vector<string> opts = split_options(p.getArg('{', '}'));
1779 handle_geometry(opts);
1782 else if (t.cs() == "definecolor") {
1783 string const color = p.getArg('{', '}');
1784 string const space = p.getArg('{', '}');
1785 string const value = p.getArg('{', '}');
1786 if (color == "document_fontcolor" && space == "rgb") {
1787 RGBColor c(RGBColorFromLaTeX(value));
1788 h_fontcolor = X11hexname(c);
1789 } else if (color == "note_fontcolor" && space == "rgb") {
1790 RGBColor c(RGBColorFromLaTeX(value));
1791 h_notefontcolor = X11hexname(c);
1792 } else if (color == "page_backgroundcolor" && space == "rgb") {
1793 RGBColor c(RGBColorFromLaTeX(value));
1794 h_backgroundcolor = X11hexname(c);
1795 } else if (color == "shadecolor" && space == "rgb") {
1796 RGBColor c(RGBColorFromLaTeX(value));
1797 h_boxbgcolor = X11hexname(c);
1799 h_preamble << "\\definecolor{" << color
1800 << "}{" << space << "}{" << value
1805 else if (t.cs() == "bibliographystyle")
1806 h_biblio_style = p.verbatim_item();
1808 else if (t.cs() == "jurabibsetup") {
1809 // FIXME p.getArg('{', '}') is most probably wrong (it
1810 // does not handle nested braces).
1811 // Use p.verbatim_item() instead.
1812 vector<string> jurabibsetup =
1813 split_options(p.getArg('{', '}'));
1814 // add jurabibsetup to the jurabib package options
1815 add_package("jurabib", jurabibsetup);
1816 if (!jurabibsetup.empty()) {
1817 h_preamble << "\\jurabibsetup{"
1818 << join(jurabibsetup, ",") << '}';
1822 else if (t.cs() == "hypersetup") {
1823 vector<string> hypersetup =
1824 split_options(p.verbatim_item());
1825 // add hypersetup to the hyperref package options
1826 handle_hyperref(hypersetup);
1827 if (!hypersetup.empty()) {
1828 h_preamble << "\\hypersetup{"
1829 << join(hypersetup, ",") << '}';
1833 else if (is_known(t.cs(), known_if_3arg_commands)) {
1834 // prevent misparsing of \usepackage if it is used
1835 // as an argument (see e.g. our own output of
1836 // \@ifundefined above)
1837 string const arg1 = p.verbatim_item();
1838 string const arg2 = p.verbatim_item();
1839 string const arg3 = p.verbatim_item();
1840 // test case \@ifundefined{date}{}{\date{}}
1841 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1842 arg2.empty() && arg3 == "\\date{}") {
1843 h_suppress_date = "true";
1844 // older tex2lyx versions did output
1845 // \@ifundefined{definecolor}{\usepackage{color}}{}
1846 } else if (t.cs() == "@ifundefined" &&
1847 arg1 == "definecolor" &&
1848 arg2 == "\\usepackage{color}" &&
1850 if (!in_lyx_preamble)
1851 h_preamble << package_beg_sep
1854 << "\\@ifundefined{definecolor}{color}{}"
1857 //\@ifundefined{showcaptionsetup}{}{%
1858 // \PassOptionsToPackage{caption=false}{subfig}}
1859 // that LyX uses for subfloats
1860 } else if (t.cs() == "@ifundefined" &&
1861 arg1 == "showcaptionsetup" && arg2.empty()
1862 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1864 } else if (!in_lyx_preamble) {
1865 h_preamble << t.asInput()
1866 << '{' << arg1 << '}'
1867 << '{' << arg2 << '}'
1868 << '{' << arg3 << '}';
1872 else if (is_known(t.cs(), known_if_commands)) {
1873 // must not parse anything in conditional code, since
1874 // LyX would output the parsed contents unconditionally
1875 if (!in_lyx_preamble)
1876 h_preamble << t.asInput();
1877 handle_if(p, in_lyx_preamble);
1880 else if (!t.cs().empty() && !in_lyx_preamble)
1881 h_preamble << '\\' << t.cs();
1884 // remove the whitespace
1887 // Force textclass if the user wanted it
1888 if (!forceclass.empty())
1889 h_textclass = forceclass;
1890 tc.setName(h_textclass);
1892 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1895 if (h_papersides.empty()) {
1898 h_papersides = ss.str();
1901 // If the CJK package is used we cannot set the document language from
1902 // the babel options. Instead, we guess which language is used most
1903 // and set this one.
1904 default_language = h_language;
1905 if (is_full_document &&
1906 (auto_packages.find("CJK") != auto_packages.end() ||
1907 auto_packages.find("CJKutf8") != auto_packages.end())) {
1909 h_language = guessLanguage(p, default_language);
1911 if (explicit_babel && h_language != default_language) {
1912 // We set the document language to a CJK language,
1913 // but babel is explicitly called in the user preamble
1914 // without options. LyX will not add the default
1915 // language to the document options if it is either
1916 // english, or no text is set as default language.
1917 // Therefore we need to add a language option explicitly.
1918 // FIXME: It would be better to remove all babel calls
1919 // from the user preamble, but this is difficult
1920 // without re-introducing bug 7861.
1921 if (h_options.empty())
1922 h_options = lyx2babel(default_language);
1924 h_options += ',' + lyx2babel(default_language);
1930 string babel2lyx(string const & language)
1932 char const * const * where = is_known(language, known_languages);
1934 return known_coded_languages[where - known_languages];
1939 string lyx2babel(string const & language)
1941 char const * const * where = is_known(language, known_coded_languages);
1943 return known_languages[where - known_coded_languages];
1948 string Preamble::polyglossia2lyx(string const & language)
1950 char const * const * where = is_known(language, polyglossia_languages);
1952 return coded_polyglossia_languages[where - polyglossia_languages];
1957 string rgbcolor2code(string const & name)
1959 char const * const * where = is_known(name, known_basic_colors);
1961 // "red", "green" etc
1962 return known_basic_color_codes[where - known_basic_colors];
1964 // "255,0,0", "0,255,0" etc
1965 RGBColor c(RGBColorFromLaTeX(name));
1966 return X11hexname(c);