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"
25 #include "support/convert.h"
26 #include "support/FileName.h"
27 #include "support/filetools.h"
28 #include "support/lstrings.h"
30 #include "support/regex.h"
36 using namespace lyx::support;
41 // special columntypes
42 extern map<char, int> special_columns;
48 // CJK languages are handled in text.cpp, polyglossia languages are listed
51 * known babel language names (including synonyms)
52 * not in standard babel: arabic, arabtex, armenian, belarusian, serbian-latin, thai
53 * please keep this in sync with known_coded_languages line by line!
55 const char * const known_languages[] = {"acadian", "afrikaans", "albanian",
56 "american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai",
57 "bahasam", "basque", "belarusian", "brazil", "brazilian", "breton", "british",
58 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
59 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais",
60 "french", "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb",
61 "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua",
62 "irish", "italian", "japanese", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
63 "lowersorbian", "lsorbian", "magyar", "malay", "meyalu", "mongolian", "naustrian",
64 "newzealand", "ngerman", "ngermanb", "norsk", "nynorsk", "polutonikogreek", "polish",
65 "portuges", "portuguese", "romanian", "russian", "russianb", "samin",
66 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
67 "swedish", "thai", "turkish", "turkmen", "ukraineb", "ukrainian",
68 "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh",
72 * the same as known_languages with .lyx names
73 * please keep this in sync with known_languages line by line!
75 const char * const known_coded_languages[] = {"french", "afrikaans", "albanian",
76 "american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa",
77 "bahasam", "basque", "belarusian", "brazilian", "brazilian", "breton", "british",
78 "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish",
79 "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french",
80 "french", "french", "french", "french", "galician", "german", "german",
81 "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua",
82 "irish", "italian", "japanese", "kazakh", "kurmanji", "latin", "latvian", "lithuanian",
83 "lowersorbian", "lowersorbian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian",
84 "newzealand", "ngerman", "ngerman", "norsk", "nynorsk", "polutonikogreek", "polish",
85 "portuguese", "portuguese", "romanian", "russian", "russian", "samin",
86 "scottish", "serbian", "serbian-latin", "slovak", "slovene", "spanish",
87 "swedish", "thai", "turkish", "turkmen", "ukrainian", "ukrainian",
88 "uppersorbian", "uppersorbian", "english", "english", "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", "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-type1",
128 "lmodern", "mathdesign", "mathpazo", "mathptmx", "newcent", "tgbonum",
129 "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", 0};
131 const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum-type1",
132 "cmbr", "cmss", "helvet", "kurier", "kurierl", "lmss", "tgadventor", "tgheros", 0};
134 const char * const known_kurier_fonts[] = { "kurier", "kurierl", "kurier-condensed",
135 "kurier-light-condensed", 0};
137 const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt",
138 "courier", "lmtt", "luximono", "fourier", "lmodern", "mathpazo", "mathptmx",
139 "newcent", "tgcursor", 0};
141 const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper",
142 "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper",
143 "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper",
144 "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper",
145 "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0};
147 const char * const known_class_paper_sizes[] = { "a4paper", "a5paper",
148 "executivepaper", "legalpaper", "letterpaper", 0};
150 const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin",
151 "bmargin", "headheight", "headsep", "footskip", "columnsep", 0};
153 const char * const known_coded_paper_margins[] = { "leftmargin", "topmargin",
154 "rightmargin", "bottommargin", "headheight", "headsep", "footskip",
157 /// commands that can start an \if...\else...\endif sequence
158 const char * const known_if_commands[] = {"if", "ifarydshln", "ifbraket",
159 "ifcancel", "ifcolortbl", "ifeurosym", "ifmarginnote", "ifmmode", "ifpdf",
160 "ifsidecap", "ifupgreek", 0};
162 const char * const known_basic_colors[] = {"blue", "black", "cyan", "green",
163 "magenta", "red", "white", "yellow", 0};
165 const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff", "#00ff00",
166 "#ff00ff", "#ff0000", "#ffffff", "#ffff00", 0};
168 /// conditional commands with three arguments like \@ifundefined{}{}{}
169 const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
172 /// packages that work only in xetex
173 /// polyglossia is handled separately
174 const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
175 "fontbook", "fontwrap", "mathspec", "philokalia", "unisugar",
176 "xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
178 /// packages that are automatically skipped if loaded by LyX
179 const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb",
180 "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color", "float",
181 "fontspec", "graphicx", "hhline", "ifthen", "longtable", "makeidx", "multirow",
182 "nomencl", "pdfpages", "rotating", "rotfloat", "splitidx", "setspace",
183 "subscript", "textcomp", "ulem", "url", "varioref", "verbatim", "wrapfig",
186 // codes used to remove packages that are loaded automatically by LyX.
187 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
188 const char package_beg_sep = '\001';
189 const char package_mid_sep = '\002';
190 const char package_end_sep = '\003';
193 // returns true if at least one of the options in what has been found
194 bool handle_opt(vector<string> & opts, char const * const * what, string & target)
200 // the last language option is the document language (for babel and LyX)
201 // the last size option is the document font size
202 vector<string>::iterator it;
203 vector<string>::iterator position = opts.begin();
204 for (; *what; ++what) {
205 it = find(opts.begin(), opts.end(), *what);
206 if (it != opts.end()) {
207 if (it >= position) {
218 void delete_opt(vector<string> & opts, char const * const * what)
223 // remove found options from the list
224 // do this after handle_opt to avoid potential memory leaks
225 vector<string>::iterator it;
226 for (; *what; ++what) {
227 it = find(opts.begin(), opts.end(), *what);
228 if (it != opts.end())
235 * Split a package options string (keyval format) into a vector.
237 * authorformat=smallcaps,
239 * titleformat=colonsep,
240 * bibformat={tabular,ibidem,numbered}
242 vector<string> split_options(string const & input)
244 vector<string> options;
248 Token const & t = p.get_token();
249 if (t.asInput() == ",") {
250 options.push_back(trimSpaceAndEol(option));
252 } else if (t.asInput() == "=") {
255 if (p.next_token().asInput() == "{")
256 option += '{' + p.getArg('{', '}') + '}';
257 } else if (t.cat() != catSpace)
258 option += t.asInput();
262 options.push_back(trimSpaceAndEol(option));
269 * Retrieve a keyval option "name={value with=sign}" named \p name from
270 * \p options and return the value.
271 * The found option is also removed from \p options.
273 string process_keyval_opt(vector<string> & options, string name)
275 for (size_t i = 0; i < options.size(); ++i) {
276 vector<string> option;
277 split(options[i], option, '=');
278 if (option.size() < 2)
280 if (option[0] == name) {
281 options.erase(options.begin() + i);
282 option.erase(option.begin());
283 return join(option, "=");
289 } // anonymous namespace
293 * known polyglossia language names (including variants)
295 const char * const Preamble::polyglossia_languages[] = {
296 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
297 "nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil",
298 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
299 "irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish",
300 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
301 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazil",
302 "brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton", "french",
303 "lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
304 "welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek",
305 "marathi", "spanish",
306 "american", "ancient", "australian", "british", "monotonic", "newzealand",
310 * the same as polyglossia_languages with .lyx names
311 * please keep this in sync with polyglossia_languages line by line!
313 const char * const Preamble::coded_polyglossia_languages[] = {
314 "albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi",
315 "nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil",
316 "armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch",
317 "irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish",
318 "bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin",
319 "samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "urdu", "brazilian",
320 "brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton", "french",
321 "lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", "slovak",
322 "welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek",
323 "marathi", "spanish",
324 "american", "ancientgreek", "australian", "british", "greek", "newzealand",
325 "polutonikogreek", 0};
328 bool Preamble::indentParagraphs() const
330 return h_paragraph_separation == "indent";
334 bool Preamble::isPackageUsed(string const & package) const
336 return used_packages.find(package) != used_packages.end();
340 vector<string> Preamble::getPackageOptions(string const & package) const
342 map<string, vector<string> >::const_iterator it = used_packages.find(package);
343 if (it != used_packages.end())
345 return vector<string>();
349 void Preamble::registerAutomaticallyLoadedPackage(std::string const & package)
351 auto_packages.insert(package);
355 void Preamble::addModule(string const & module)
357 used_modules.push_back(module);
361 void Preamble::suppressDate(bool suppress)
364 h_suppress_date = "true";
366 h_suppress_date = "false";
370 void Preamble::registerAuthor(std::string const & name)
372 Author author(from_utf8(name), empty_docstring());
373 author.setUsed(true);
374 authors_.record(author);
375 h_tracking_changes = "true";
376 h_output_changes = "true";
380 Author const & Preamble::getAuthor(std::string const & name) const
382 Author author(from_utf8(name), empty_docstring());
383 for (AuthorList::Authors::const_iterator it = authors_.begin();
384 it != authors_.end(); ++it)
387 static Author const dummy;
392 void Preamble::add_package(string const & name, vector<string> & options)
394 // every package inherits the global options
395 if (used_packages.find(name) == used_packages.end())
396 used_packages[name] = split_options(h_options);
398 vector<string> & v = used_packages[name];
399 v.insert(v.end(), options.begin(), options.end());
400 if (name == "jurabib") {
401 // Don't output the order argument (see the cite command
402 // handling code in text.cpp).
403 vector<string>::iterator end =
404 remove(options.begin(), options.end(), "natbiborder");
405 end = remove(options.begin(), end, "jurabiborder");
406 options.erase(end, options.end());
413 // Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100
414 bool scale_as_percentage(string const & scale, string & percentage)
416 string::size_type pos = scale.find('=');
417 if (pos != string::npos) {
418 string value = scale.substr(pos + 1);
419 if (isStrDbl(value)) {
420 percentage = convert<string>(100 * convert<double>(value));
428 string remove_braces(string const & value)
432 if (value[0] == '{' && value[value.length()-1] == '}')
433 return value.substr(1, value.length()-2);
437 } // anonymous namespace
440 Preamble::Preamble() : one_language(true), title_layout_found(false),
441 h_font_cjk_set(false)
445 h_biblio_style = "plain";
446 h_bibtex_command = "default";
447 h_cite_engine = "basic";
448 h_cite_engine_type = "numerical";
450 h_defskip = "medskip";
453 h_fontencoding = "default";
454 h_font_roman = "default";
455 h_font_sans = "default";
456 h_font_typewriter = "default";
457 h_font_math = "auto";
458 h_font_default_family = "default";
459 h_use_non_tex_fonts = "false";
461 h_font_osf = "false";
462 h_font_sf_scale = "100";
463 h_font_tt_scale = "100";
465 h_graphics = "default";
466 h_default_output_format = "default";
467 h_html_be_strict = "false";
468 h_html_css_as_file = "0";
469 h_html_math_output = "0";
471 h_index_command = "default";
472 h_inputencoding = "auto";
473 h_justification = "true";
474 h_language = "english";
475 h_language_package = "none";
477 h_maintain_unincluded_children = "false";
481 h_output_changes = "false";
483 //h_output_sync_macro
484 h_papercolumns = "1";
485 h_paperfontsize = "default";
486 h_paperorientation = "portrait";
487 h_paperpagestyle = "default";
489 h_papersize = "default";
490 h_paragraph_indentation = "default";
491 h_paragraph_separation = "indent";
496 h_pdf_bookmarks = "1";
497 h_pdf_bookmarksnumbered = "0";
498 h_pdf_bookmarksopen = "0";
499 h_pdf_bookmarksopenlevel = "1";
500 h_pdf_breaklinks = "0";
501 h_pdf_pdfborder = "0";
502 h_pdf_colorlinks = "0";
503 h_pdf_backref = "section";
504 h_pdf_pdfusetitle = "1";
506 //h_pdf_quoted_options;
507 h_quotes_language = "english";
510 h_spacing = "single";
511 h_suppress_date = "false";
512 h_textclass = "article";
514 h_tracking_changes = "false";
515 h_use_bibtopic = "false";
516 h_use_indices = "false";
517 h_use_geometry = "false";
518 h_use_default_options = "false";
519 h_use_hyperref = "false";
520 h_use_refstyle = "0";
521 h_use_packages["amsmath"] = "1";
522 h_use_packages["amssymb"] = "0";
523 h_use_packages["esint"] = "1";
524 h_use_packages["mhchem"] = "0";
525 h_use_packages["mathdots"] = "0";
526 h_use_packages["mathtools"] = "0";
527 h_use_packages["stackrel"] = "0";
528 h_use_packages["stmaryrd"] = "0";
529 h_use_packages["undertilde"] = "0";
533 void Preamble::handle_hyperref(vector<string> & options)
535 // FIXME swallow inputencoding changes that might surround the
536 // hyperref setup if it was written by LyX
537 h_use_hyperref = "true";
538 // swallow "unicode=true", since LyX does always write that
539 vector<string>::iterator it =
540 find(options.begin(), options.end(), "unicode=true");
541 if (it != options.end())
543 it = find(options.begin(), options.end(), "pdfusetitle");
544 if (it != options.end()) {
545 h_pdf_pdfusetitle = "1";
548 string bookmarks = process_keyval_opt(options, "bookmarks");
549 if (bookmarks == "true")
550 h_pdf_bookmarks = "1";
551 else if (bookmarks == "false")
552 h_pdf_bookmarks = "0";
553 if (h_pdf_bookmarks == "1") {
554 string bookmarksnumbered =
555 process_keyval_opt(options, "bookmarksnumbered");
556 if (bookmarksnumbered == "true")
557 h_pdf_bookmarksnumbered = "1";
558 else if (bookmarksnumbered == "false")
559 h_pdf_bookmarksnumbered = "0";
560 string bookmarksopen =
561 process_keyval_opt(options, "bookmarksopen");
562 if (bookmarksopen == "true")
563 h_pdf_bookmarksopen = "1";
564 else if (bookmarksopen == "false")
565 h_pdf_bookmarksopen = "0";
566 if (h_pdf_bookmarksopen == "1") {
567 string bookmarksopenlevel =
568 process_keyval_opt(options, "bookmarksopenlevel");
569 if (!bookmarksopenlevel.empty())
570 h_pdf_bookmarksopenlevel = bookmarksopenlevel;
573 string breaklinks = process_keyval_opt(options, "breaklinks");
574 if (breaklinks == "true")
575 h_pdf_breaklinks = "1";
576 else if (breaklinks == "false")
577 h_pdf_breaklinks = "0";
578 string pdfborder = process_keyval_opt(options, "pdfborder");
579 if (pdfborder == "{0 0 0}")
580 h_pdf_pdfborder = "1";
581 else if (pdfborder == "{0 0 1}")
582 h_pdf_pdfborder = "0";
583 string backref = process_keyval_opt(options, "backref");
584 if (!backref.empty())
585 h_pdf_backref = backref;
586 string colorlinks = process_keyval_opt(options, "colorlinks");
587 if (colorlinks == "true")
588 h_pdf_colorlinks = "1";
589 else if (colorlinks == "false")
590 h_pdf_colorlinks = "0";
591 string pdfpagemode = process_keyval_opt(options, "pdfpagemode");
592 if (!pdfpagemode.empty())
593 h_pdf_pagemode = pdfpagemode;
594 string pdftitle = process_keyval_opt(options, "pdftitle");
595 if (!pdftitle.empty()) {
596 h_pdf_title = remove_braces(pdftitle);
598 string pdfauthor = process_keyval_opt(options, "pdfauthor");
599 if (!pdfauthor.empty()) {
600 h_pdf_author = remove_braces(pdfauthor);
602 string pdfsubject = process_keyval_opt(options, "pdfsubject");
603 if (!pdfsubject.empty())
604 h_pdf_subject = remove_braces(pdfsubject);
605 string pdfkeywords = process_keyval_opt(options, "pdfkeywords");
606 if (!pdfkeywords.empty())
607 h_pdf_keywords = remove_braces(pdfkeywords);
608 if (!options.empty()) {
609 if (!h_pdf_quoted_options.empty())
610 h_pdf_quoted_options += ',';
611 h_pdf_quoted_options += join(options, ",");
617 void Preamble::handle_geometry(vector<string> & options)
619 h_use_geometry = "true";
620 vector<string>::iterator it;
622 if ((it = find(options.begin(), options.end(), "landscape")) != options.end()) {
623 h_paperorientation = "landscape";
627 // keyval version: "paper=letter"
628 string paper = process_keyval_opt(options, "paper");
630 h_papersize = paper + "paper";
631 // alternative version: "letterpaper"
632 handle_opt(options, known_paper_sizes, h_papersize);
633 delete_opt(options, known_paper_sizes);
635 char const * const * margin = known_paper_margins;
636 for (; *margin; ++margin) {
637 string value = process_keyval_opt(options, *margin);
638 if (!value.empty()) {
639 int k = margin - known_paper_margins;
640 string name = known_coded_paper_margins[k];
641 h_margins += '\\' + name + ' ' + value + '\n';
647 void Preamble::handle_package(Parser &p, string const & name,
648 string const & opts, bool in_lyx_preamble)
650 vector<string> options = split_options(opts);
651 add_package(name, options);
652 char const * const * where = 0;
654 if (is_known(name, known_xetex_packages)) {
656 h_use_non_tex_fonts = "true";
657 registerAutomaticallyLoadedPackage("fontspec");
658 if (h_inputencoding == "auto")
659 p.setEncoding("UTF-8");
663 if (is_known(name, known_roman_fonts))
666 if (name == "fourier") {
667 h_font_roman = "utopia";
668 // when font uses real small capitals
669 if (opts == "expert")
673 if (name == "garamondx") {
674 h_font_roman = "garamondx";
679 if (name == "libertine-type1") {
680 h_font_roman = "libertine";
681 if (opts == "lining")
682 h_font_osf = "false";
687 if (name == "mathdesign") {
688 if (opts.find("charter") != string::npos)
689 h_font_roman = "md-charter";
690 if (opts.find("garamond") != string::npos)
691 h_font_roman = "md-garamond";
692 if (opts.find("utopia") != string::npos)
693 h_font_roman = "md-utopia";
694 if (opts.find("expert") != string::npos) {
700 else if (name == "mathpazo")
701 h_font_roman = "palatino";
703 else if (name == "mathptmx")
704 h_font_roman = "times";
707 if (is_known(name, known_sans_fonts)) {
709 if (options.size() == 1) {
710 if (scale_as_percentage(opts, h_font_sf_scale))
715 if (name == "biolinum-type1")
716 h_font_sans = "biolinum";
719 if (is_known(name, known_typewriter_fonts)) {
720 // fourier can be set as roman font _only_
721 // fourier as typewriter is handled in handling of \ttdefault
722 if (name != "fourier") {
723 h_font_typewriter = name;
724 if (options.size() == 1) {
725 if (scale_as_percentage(opts, h_font_tt_scale))
731 // font uses old-style figure
735 if (name == "refstyle")
736 h_use_refstyle = "1";
738 // after the detection and handling of special cases, we can remove the
739 // fonts, otherwise they would appear in the preamble, see bug #7856
740 if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts)
741 || is_known(name, known_typewriter_fonts))
744 else if (name == "amsmath" || name == "amssymb" ||
745 name == "esint" || name == "mhchem" || name == "mathdots" ||
746 name == "mathtools" || name == "stackrel" ||
747 name == "stmaryrd" || name == "undertilde")
748 h_use_packages[name] = "2";
750 else if (name == "babel") {
751 h_language_package = "default";
752 // One might think we would have to do nothing if babel is loaded
753 // without any options to prevent pollution of the preamble with this
754 // babel call in every roundtrip.
755 // But the user could have defined babel-specific things afterwards. So
756 // we need to keep it in the preamble to prevent cases like bug #7861.
758 // check if more than one option was used - used later for inputenc
759 if (options.begin() != options.end() - 1)
760 one_language = false;
761 // babel takes the last language of the option of its \usepackage
762 // call as document language. If there is no such language option, the
763 // last language in the documentclass options is used.
764 handle_opt(options, known_languages, h_language);
765 // translate the babel name to a LyX name
766 h_language = babel2lyx(h_language);
767 if (h_language == "japanese") {
768 // For Japanese, the encoding isn't indicated in the source
769 // file, and there's really not much we can do. We could
770 // 1) offer a list of possible encodings to choose from, or
771 // 2) determine the encoding of the file by inspecting it.
772 // For the time being, we leave the encoding alone so that
773 // we don't get iconv errors when making a wrong guess, and
774 // we will output a note at the top of the document
775 // explaining what to do.
776 Encoding const * const enc = encodings.fromIconvName(
777 p.getEncoding(), Encoding::japanese, false);
779 h_inputencoding = enc->name();
780 is_nonCJKJapanese = true;
781 // in this case babel can be removed from the preamble
782 registerAutomaticallyLoadedPackage("babel");
784 // If babel is called with options, LyX puts them by default into the
785 // document class options. This works for most languages, except
786 // for Latvian, Lithuanian, Mongolian, Turkmen and Vietnamese and
787 // perhaps in future others.
788 // Therefore keep the babel call as it is as the user might have
790 h_preamble << "\\usepackage[" << opts << "]{babel}\n";
792 delete_opt(options, known_languages);
795 h_preamble << "\\usepackage{babel}\n";
798 else if (name == "polyglossia") {
799 h_language_package = "default";
800 h_default_output_format = "pdf4";
801 h_use_non_tex_fonts = "true";
803 registerAutomaticallyLoadedPackage("xunicode");
804 if (h_inputencoding == "auto")
805 p.setEncoding("UTF-8");
808 else if (name == "CJK") {
809 // set the encoding to "auto" because it might be set to "default" by the babel handling
810 // and this would not be correct for CJK
811 if (h_inputencoding == "default")
812 h_inputencoding = "auto";
813 registerAutomaticallyLoadedPackage("CJK");
816 else if (name == "CJKutf8") {
817 h_inputencoding = "UTF8";
818 p.setEncoding("UTF-8");
819 registerAutomaticallyLoadedPackage("CJKutf8");
822 else if (name == "fontenc") {
823 h_fontencoding = getStringFromVector(options, ",");
824 /* We could do the following for better round trip support,
825 * but this makes the document less portable, so I skip it:
826 if (h_fontencoding == lyxrc.fontenc)
827 h_fontencoding = "global";
832 else if (name == "inputenc" || name == "luainputenc") {
833 // h_inputencoding is only set when there is not more than one
834 // inputenc option because otherwise h_inputencoding must be
835 // set to "auto" (the default encoding of the document language)
836 // Therefore check for the "," character.
837 // It is also only set when there is not more than one babel
840 if (opts.find(",") == string::npos && one_language == true) {
841 h_inputencoding = opts;
842 // FIXME: if this line is used, tex2lyx swallows the next character
843 // in the file behind "{inputenc}"
844 //p.setEncoding(opts);
846 h_preamble << "\\usepackage[" << opts << "}{" << name << "}\n";
847 // FIXME: enabling this introduces bug #8525
848 //p.setEncoding(options.back(), Encoding::inputenc);
852 h_preamble << "\\usepackage{" << name << "}\n";
855 else if (name == "srcltx") {
858 h_output_sync_macro = "\\usepackage[" + opts + "]{srcltx}";
861 h_output_sync_macro = "\\usepackage{srcltx}";
864 else if (is_known(name, known_old_language_packages)) {
865 // known language packages from the times before babel
866 // if they are found and not also babel, they will be used as
867 // custom language package
868 h_language_package = "\\usepackage{" + name + "}";
871 else if (name == "prettyref")
872 ; // ignore this FIXME: Use the package separator mechanism instead
874 else if (name == "lyxskak") {
875 // ignore this and its options
876 const char * const o[] = {"ps", "mover", 0};
877 delete_opt(options, o);
880 else if (is_known(name, known_lyx_packages) && options.empty()) {
881 if (name == "splitidx")
882 h_use_indices = "true";
883 if (!in_lyx_preamble) {
884 h_preamble << package_beg_sep << name
885 << package_mid_sep << "\\usepackage{"
887 if (p.next_token().cat() == catNewline ||
888 (p.next_token().cat() == catSpace &&
889 p.next_next_token().cat() == catNewline))
891 h_preamble << package_end_sep;
895 else if (name == "geometry")
896 handle_geometry(options);
898 else if (name == "subfig")
899 ; // ignore this FIXME: Use the package separator mechanism instead
901 else if ((where = is_known(name, known_languages)))
902 h_language = known_coded_languages[where - known_languages];
904 else if (name == "natbib") {
905 h_biblio_style = "plainnat";
906 h_cite_engine = "natbib";
907 h_cite_engine_type = "authoryear";
908 vector<string>::iterator it =
909 find(options.begin(), options.end(), "authoryear");
910 if (it != options.end())
913 it = find(options.begin(), options.end(), "numbers");
914 if (it != options.end()) {
915 h_cite_engine_type = "numerical";
921 else if (name == "jurabib") {
922 h_biblio_style = "jurabib";
923 h_cite_engine = "jurabib";
924 h_cite_engine_type = "authoryear";
927 else if (name == "hyperref")
928 handle_hyperref(options);
930 else if (!in_lyx_preamble) {
932 h_preamble << "\\usepackage{" << name << '}';
934 h_preamble << "\\usepackage[" << opts << "]{"
938 if (p.next_token().cat() == catNewline ||
939 (p.next_token().cat() == catSpace &&
940 p.next_next_token().cat() == catNewline))
944 // We need to do something with the options...
945 if (!options.empty())
946 cerr << "Ignoring options '" << join(options, ",")
947 << "' of package " << name << '.' << endl;
949 // remove the whitespace
954 void Preamble::handle_if(Parser & p, bool in_lyx_preamble)
957 Token t = p.get_token();
958 if (t.cat() == catEscape &&
959 is_known(t.cs(), known_if_commands))
960 handle_if(p, in_lyx_preamble);
962 if (!in_lyx_preamble)
963 h_preamble << t.asInput();
964 if (t.cat() == catEscape && t.cs() == "fi")
971 bool Preamble::writeLyXHeader(ostream & os, bool subdoc)
973 // set the quote language
974 // LyX only knows the following quotes languages:
975 // english, swedish, german, polish, french and danish
976 // (quotes for "japanese" and "chinese-traditional" are missing because
977 // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383)
978 // conversion list taken from
979 // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage
980 // (quotes for kazakh and interlingua are unknown)
982 if (is_known(h_language, known_danish_quotes_languages))
983 h_quotes_language = "danish";
985 else if (is_known(h_language, known_french_quotes_languages))
986 h_quotes_language = "french";
988 else if (is_known(h_language, known_german_quotes_languages))
989 h_quotes_language = "german";
991 else if (is_known(h_language, known_polish_quotes_languages))
992 h_quotes_language = "polish";
994 else if (is_known(h_language, known_swedish_quotes_languages))
995 h_quotes_language = "swedish";
997 else if (is_known(h_language, known_english_quotes_languages))
998 h_quotes_language = "english";
1000 if (contains(h_float_placement, "H"))
1001 registerAutomaticallyLoadedPackage("float");
1002 if (h_spacing != "single" && h_spacing != "default")
1003 registerAutomaticallyLoadedPackage("setspace");
1004 if (h_use_packages["amsmath"] == "2") {
1005 // amsbsy and amstext are already provided by amsmath
1006 registerAutomaticallyLoadedPackage("amsbsy");
1007 registerAutomaticallyLoadedPackage("amstext");
1010 // output the LyX file settings
1011 os << "#LyX file created by tex2lyx " << PACKAGE_VERSION << "\n"
1012 << "\\lyxformat " << LYX_FORMAT << '\n'
1013 << "\\begin_document\n"
1014 << "\\begin_header\n"
1015 << "\\textclass " << h_textclass << "\n";
1016 string const raw = subdoc ? empty_string() : h_preamble.str();
1018 os << "\\begin_preamble\n";
1019 for (string::size_type i = 0; i < raw.size(); ++i) {
1020 if (raw[i] == package_beg_sep) {
1021 // Here follows some package loading code that
1022 // must be skipped if the package is loaded
1024 string::size_type j = raw.find(package_mid_sep, i);
1025 if (j == string::npos)
1027 string::size_type k = raw.find(package_end_sep, j);
1028 if (k == string::npos)
1030 string const package = raw.substr(i + 1, j - i - 1);
1031 string const replacement = raw.substr(j + 1, k - j - 1);
1032 if (auto_packages.find(package) == auto_packages.end())
1038 os << "\n\\end_preamble\n";
1040 if (!h_options.empty())
1041 os << "\\options " << h_options << "\n";
1042 os << "\\use_default_options " << h_use_default_options << "\n";
1043 if (!used_modules.empty()) {
1044 os << "\\begin_modules\n";
1045 vector<string>::const_iterator const end = used_modules.end();
1046 vector<string>::const_iterator it = used_modules.begin();
1047 for (; it != end; ++it)
1049 os << "\\end_modules\n";
1051 os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n"
1052 << "\\language " << h_language << "\n"
1053 << "\\language_package " << h_language_package << "\n"
1054 << "\\inputencoding " << h_inputencoding << "\n"
1055 << "\\fontencoding " << h_fontencoding << "\n"
1056 << "\\font_roman " << h_font_roman << "\n"
1057 << "\\font_sans " << h_font_sans << "\n"
1058 << "\\font_typewriter " << h_font_typewriter << "\n"
1059 << "\\font_math " << h_font_math << "\n"
1060 << "\\font_default_family " << h_font_default_family << "\n"
1061 << "\\use_non_tex_fonts " << h_use_non_tex_fonts << "\n"
1062 << "\\font_sc " << h_font_sc << "\n"
1063 << "\\font_osf " << h_font_osf << "\n"
1064 << "\\font_sf_scale " << h_font_sf_scale << "\n"
1065 << "\\font_tt_scale " << h_font_tt_scale << '\n';
1066 if (!h_font_cjk.empty())
1067 os << "\\font_cjk " << h_font_cjk << '\n';
1068 os << "\\graphics " << h_graphics << '\n'
1069 << "\\default_output_format " << h_default_output_format << "\n"
1070 << "\\output_sync " << h_output_sync << "\n";
1071 if (h_output_sync == "1")
1072 os << "\\output_sync_macro \"" << h_output_sync_macro << "\"\n";
1073 os << "\\bibtex_command " << h_bibtex_command << "\n"
1074 << "\\index_command " << h_index_command << "\n";
1075 if (!h_float_placement.empty())
1076 os << "\\float_placement " << h_float_placement << "\n";
1077 os << "\\paperfontsize " << h_paperfontsize << "\n"
1078 << "\\spacing " << h_spacing << "\n"
1079 << "\\use_hyperref " << h_use_hyperref << '\n';
1080 if (h_use_hyperref == "true") {
1081 if (!h_pdf_title.empty())
1082 os << "\\pdf_title \"" << h_pdf_title << "\"\n";
1083 if (!h_pdf_author.empty())
1084 os << "\\pdf_author \"" << h_pdf_author << "\"\n";
1085 if (!h_pdf_subject.empty())
1086 os << "\\pdf_subject \"" << h_pdf_subject << "\"\n";
1087 if (!h_pdf_keywords.empty())
1088 os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n";
1089 os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n"
1090 "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n"
1091 "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n"
1092 "\\pdf_bookmarksopenlevel " << h_pdf_bookmarksopenlevel << "\n"
1093 "\\pdf_breaklinks " << h_pdf_breaklinks << "\n"
1094 "\\pdf_pdfborder " << h_pdf_pdfborder << "\n"
1095 "\\pdf_colorlinks " << h_pdf_colorlinks << "\n"
1096 "\\pdf_backref " << h_pdf_backref << "\n"
1097 "\\pdf_pdfusetitle " << h_pdf_pdfusetitle << '\n';
1098 if (!h_pdf_pagemode.empty())
1099 os << "\\pdf_pagemode " << h_pdf_pagemode << '\n';
1100 if (!h_pdf_quoted_options.empty())
1101 os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n";
1103 os << "\\papersize " << h_papersize << "\n"
1104 << "\\use_geometry " << h_use_geometry << '\n';
1105 for (map<string, string>::const_iterator it = h_use_packages.begin();
1106 it != h_use_packages.end(); ++it)
1107 os << "\\use_package " << it->first << ' ' << it->second << '\n';
1108 os << "\\cite_engine " << h_cite_engine << '\n'
1109 << "\\cite_engine_type " << h_cite_engine_type << '\n'
1110 << "\\biblio_style " << h_biblio_style << "\n"
1111 << "\\use_bibtopic " << h_use_bibtopic << "\n"
1112 << "\\use_indices " << h_use_indices << "\n"
1113 << "\\paperorientation " << h_paperorientation << '\n'
1114 << "\\suppress_date " << h_suppress_date << '\n'
1115 << "\\justification " << h_justification << '\n'
1116 << "\\use_refstyle " << h_use_refstyle << '\n';
1117 if (!h_fontcolor.empty())
1118 os << "\\fontcolor " << h_fontcolor << '\n';
1119 if (!h_notefontcolor.empty())
1120 os << "\\notefontcolor " << h_notefontcolor << '\n';
1121 if (!h_backgroundcolor.empty())
1122 os << "\\backgroundcolor " << h_backgroundcolor << '\n';
1123 if (!h_boxbgcolor.empty())
1124 os << "\\boxbgcolor " << h_boxbgcolor << '\n';
1125 os << "\\index " << h_index << '\n'
1126 << "\\shortcut " << h_shortcut << '\n'
1127 << "\\color " << h_color << '\n'
1130 << "\\secnumdepth " << h_secnumdepth << "\n"
1131 << "\\tocdepth " << h_tocdepth << "\n"
1132 << "\\paragraph_separation " << h_paragraph_separation << "\n";
1133 if (h_paragraph_separation == "skip")
1134 os << "\\defskip " << h_defskip << "\n";
1136 os << "\\paragraph_indentation " << h_paragraph_indentation << "\n";
1137 os << "\\quotes_language " << h_quotes_language << "\n"
1138 << "\\papercolumns " << h_papercolumns << "\n"
1139 << "\\papersides " << h_papersides << "\n"
1140 << "\\paperpagestyle " << h_paperpagestyle << "\n";
1141 if (!h_listings_params.empty())
1142 os << "\\listings_params " << h_listings_params << "\n";
1143 os << "\\tracking_changes " << h_tracking_changes << "\n"
1144 << "\\output_changes " << h_output_changes << "\n"
1145 << "\\html_math_output " << h_html_math_output << "\n"
1146 << "\\html_css_as_file " << h_html_css_as_file << "\n"
1147 << "\\html_be_strict " << h_html_be_strict << "\n"
1149 << "\\end_header\n\n"
1150 << "\\begin_body\n";
1155 void Preamble::parse(Parser & p, string const & forceclass,
1156 TeX2LyXDocClass & tc)
1158 // initialize fixed types
1159 special_columns['D'] = 3;
1160 bool is_full_document = false;
1161 bool is_lyx_file = false;
1162 bool in_lyx_preamble = false;
1164 // determine whether this is a full document or a fragment for inclusion
1166 Token const & t = p.get_token();
1168 if (t.cat() == catEscape && t.cs() == "documentclass") {
1169 is_full_document = true;
1175 while (is_full_document && p.good()) {
1176 Token const & t = p.get_token();
1179 cerr << "t: " << t << "\n";
1185 if (!in_lyx_preamble &&
1186 (t.cat() == catLetter ||
1187 t.cat() == catSuper ||
1188 t.cat() == catSub ||
1189 t.cat() == catOther ||
1190 t.cat() == catMath ||
1191 t.cat() == catActive ||
1192 t.cat() == catBegin ||
1193 t.cat() == catEnd ||
1194 t.cat() == catAlign ||
1195 t.cat() == catParameter))
1196 h_preamble << t.cs();
1198 else if (!in_lyx_preamble &&
1199 (t.cat() == catSpace || t.cat() == catNewline))
1200 h_preamble << t.asInput();
1202 else if (t.cat() == catComment) {
1203 static regex const islyxfile("%% LyX .* created this file");
1204 static regex const usercommands("User specified LaTeX commands");
1206 string const comment = t.asInput();
1208 // magically switch encoding default if it looks like XeLaTeX
1209 static string const magicXeLaTeX =
1210 "% This document must be compiled with XeLaTeX ";
1211 if (comment.size() > magicXeLaTeX.size()
1212 && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX
1213 && h_inputencoding == "auto") {
1214 cerr << "XeLaTeX comment found, switching to UTF8\n";
1215 h_inputencoding = "utf8";
1218 if (regex_search(comment, sub, islyxfile)) {
1220 in_lyx_preamble = true;
1221 } else if (is_lyx_file
1222 && regex_search(comment, sub, usercommands))
1223 in_lyx_preamble = false;
1224 else if (!in_lyx_preamble)
1225 h_preamble << t.asInput();
1228 else if (t.cs() == "pagestyle")
1229 h_paperpagestyle = p.verbatim_item();
1231 else if (t.cs() == "setdefaultlanguage") {
1233 // We don't yet care about non-language variant options
1234 // because LyX doesn't support this yet, see bug #8214
1236 string langopts = p.getOpt();
1237 // check if the option contains a variant, if yes, extract it
1238 string::size_type pos_var = langopts.find("variant");
1239 string::size_type i = langopts.find(',', pos_var);
1240 string::size_type k = langopts.find('=', pos_var);
1241 if (pos_var != string::npos){
1243 if (i == string::npos)
1244 variant = langopts.substr(k + 1, langopts.length() - k - 2);
1246 variant = langopts.substr(k + 1, i - k - 1);
1247 h_language = variant;
1251 h_language = p.verbatim_item();
1252 //finally translate the poyglossia name to a LyX name
1253 h_language = polyglossia2lyx(h_language);
1256 else if (t.cs() == "setotherlanguage") {
1257 // We don't yet care about the option because LyX doesn't
1258 // support this yet, see bug #8214
1259 p.hasOpt() ? p.getOpt() : string();
1263 else if (t.cs() == "setmainfont") {
1264 // we don't care about the option
1265 p.hasOpt() ? p.getOpt() : string();
1266 h_font_roman = p.getArg('{', '}');
1269 else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") {
1270 // LyX currently only supports the scale option
1273 string fontopts = p.getArg('[', ']');
1274 // check if the option contains a scaling, if yes, extract it
1275 string::size_type pos = fontopts.find("Scale");
1276 if (pos != string::npos) {
1277 string::size_type i = fontopts.find(',', pos);
1278 if (i == string::npos)
1279 scale_as_percentage(fontopts.substr(pos + 1), scale);
1281 scale_as_percentage(fontopts.substr(pos, i - pos), scale);
1284 if (t.cs() == "setsansfont") {
1286 h_font_sf_scale = scale;
1287 h_font_sans = p.getArg('{', '}');
1290 h_font_tt_scale = scale;
1291 h_font_typewriter = p.getArg('{', '}');
1295 else if (t.cs() == "date") {
1296 string argument = p.getArg('{', '}');
1297 if (argument.empty())
1298 h_suppress_date = "true";
1300 h_preamble << t.asInput() << '{' << argument << '}';
1303 else if (t.cs() == "color") {
1304 string const space =
1305 (p.hasOpt() ? p.getOpt() : string());
1306 string argument = p.getArg('{', '}');
1307 // check the case that a standard color is used
1308 if (space.empty() && is_known(argument, known_basic_colors)) {
1309 h_fontcolor = rgbcolor2code(argument);
1310 preamble.registerAutomaticallyLoadedPackage("color");
1311 } else if (space.empty() && argument == "document_fontcolor")
1312 preamble.registerAutomaticallyLoadedPackage("color");
1313 // check the case that LyX's document_fontcolor is defined
1314 // but not used for \color
1316 h_preamble << t.asInput();
1318 h_preamble << space;
1319 h_preamble << '{' << argument << '}';
1320 // the color might already be set because \definecolor
1321 // is parsed before this
1326 else if (t.cs() == "pagecolor") {
1327 string argument = p.getArg('{', '}');
1328 // check the case that a standard color is used
1329 if (is_known(argument, known_basic_colors)) {
1330 h_backgroundcolor = rgbcolor2code(argument);
1331 } else if (argument == "page_backgroundcolor")
1332 preamble.registerAutomaticallyLoadedPackage("color");
1333 // check the case that LyX's page_backgroundcolor is defined
1334 // but not used for \pagecolor
1336 h_preamble << t.asInput() << '{' << argument << '}';
1337 // the color might already be set because \definecolor
1338 // is parsed before this
1339 h_backgroundcolor = "";
1343 else if (t.cs() == "makeatletter") {
1344 // LyX takes care of this
1345 p.setCatcode('@', catLetter);
1348 else if (t.cs() == "makeatother") {
1349 // LyX takes care of this
1350 p.setCatcode('@', catOther);
1353 else if (t.cs() == "newcommand" || t.cs() == "newcommandx"
1354 || t.cs() == "renewcommand" || t.cs() == "renewcommandx"
1355 || t.cs() == "providecommand" || t.cs() == "providecommandx"
1356 || t.cs() == "DeclareRobustCommand"
1357 || t.cs() == "DeclareRobustCommandx"
1358 || t.cs() == "ProvideTextCommandDefault"
1359 || t.cs() == "DeclareMathAccent") {
1361 if (p.next_token().character() == '*') {
1365 string const name = p.verbatim_item();
1366 string const opt1 = p.getFullOpt();
1367 string const opt2 = p.getFullOpt();
1368 string const body = p.verbatim_item();
1369 // store the in_lyx_preamble setting
1370 bool const was_in_lyx_preamble = in_lyx_preamble;
1372 if (name == "\\rmdefault")
1373 if (is_known(body, known_roman_fonts)) {
1374 h_font_roman = body;
1376 in_lyx_preamble = true;
1378 if (name == "\\sfdefault")
1379 if (is_known(body, known_sans_fonts)) {
1382 in_lyx_preamble = true;
1384 if (name == "\\ttdefault")
1385 if (is_known(body, known_typewriter_fonts)) {
1386 h_font_typewriter = body;
1388 in_lyx_preamble = true;
1390 if (name == "\\familydefault") {
1391 string family = body;
1392 // remove leading "\"
1393 h_font_default_family = family.erase(0,1);
1395 in_lyx_preamble = true;
1398 if (name == "\\bfdefault")
1399 // LyX re-adds this if a kurier font is used
1400 if (is_known(h_font_sans, known_kurier_fonts) && body == "b") {
1402 in_lyx_preamble = true;
1405 // remove the lyxdot definition that is re-added by LyX
1407 if (name == "\\lyxdot") {
1409 in_lyx_preamble = true;
1412 // Add the command to the known commands
1413 add_known_command(name, opt1, !opt2.empty(), from_utf8(body));
1415 // only non-lyxspecific stuff
1416 if (!in_lyx_preamble) {
1418 ss << '\\' << t.cs();
1421 ss << '{' << name << '}' << opt1 << opt2
1422 << '{' << body << "}";
1423 h_preamble << ss.str();
1425 ostream & out = in_preamble ? h_preamble : os;
1426 out << "\\" << t.cs() << "{" << name << "}"
1427 << opts << "{" << body << "}";
1430 // restore the in_lyx_preamble setting
1431 in_lyx_preamble = was_in_lyx_preamble;
1434 else if (t.cs() == "edef"){
1435 // we only support this for kurier fonts
1436 string const command = p.next_token().asInput();
1438 if (command == "\\sfdefault") {
1440 if (h_font_sans == "kurier")
1441 h_font_sans = "kurier-condensed";
1442 if (h_font_sans == "kurierl")
1443 h_font_sans = "kurier-light-condensed";
1447 h_preamble << "\\edef" << command << "{" << p.getArg('{', '}') << "}\n";
1450 else if (t.cs() == "documentclass") {
1451 vector<string>::iterator it;
1452 vector<string> opts = split_options(p.getArg('[', ']'));
1453 handle_opt(opts, known_fontsizes, h_paperfontsize);
1454 delete_opt(opts, known_fontsizes);
1455 // delete "pt" at the end
1456 string::size_type i = h_paperfontsize.find("pt");
1457 if (i != string::npos)
1458 h_paperfontsize.erase(i);
1459 // The documentclass options are always parsed before the options
1460 // of the babel call so that a language cannot overwrite the babel
1462 handle_opt(opts, known_languages, h_language);
1463 delete_opt(opts, known_languages);
1465 // paper orientation
1466 if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) {
1467 h_paperorientation = "landscape";
1471 if ((it = find(opts.begin(), opts.end(), "oneside"))
1476 if ((it = find(opts.begin(), opts.end(), "twoside"))
1482 if ((it = find(opts.begin(), opts.end(), "onecolumn"))
1484 h_papercolumns = "1";
1487 if ((it = find(opts.begin(), opts.end(), "twocolumn"))
1489 h_papercolumns = "2";
1493 // some size options are known to any document classes, other sizes
1494 // are handled by the \geometry command of the geometry package
1495 handle_opt(opts, known_class_paper_sizes, h_papersize);
1496 delete_opt(opts, known_class_paper_sizes);
1497 // the remaining options
1498 h_options = join(opts, ",");
1499 // FIXME This does not work for classes that have a
1500 // different name in LyX than in LaTeX
1501 h_textclass = p.getArg('{', '}');
1505 else if (t.cs() == "usepackage") {
1506 string const options = p.getArg('[', ']');
1507 string const name = p.getArg('{', '}');
1508 vector<string> vecnames;
1509 split(name, vecnames, ',');
1510 vector<string>::const_iterator it = vecnames.begin();
1511 vector<string>::const_iterator end = vecnames.end();
1512 for (; it != end; ++it)
1513 handle_package(p, trimSpaceAndEol(*it), options,
1517 else if (t.cs() == "inputencoding") {
1518 string const encoding = p.getArg('{','}');
1519 h_inputencoding = encoding;
1520 p.setEncoding(encoding, Encoding::inputenc);
1523 else if (t.cs() == "newenvironment") {
1524 string const name = p.getArg('{', '}');
1525 string const opt1 = p.getFullOpt();
1526 string const opt2 = p.getFullOpt();
1527 string const beg = p.verbatim_item();
1528 string const end = p.verbatim_item();
1529 if (!in_lyx_preamble) {
1530 h_preamble << "\\newenvironment{" << name
1531 << '}' << opt1 << opt2 << '{'
1532 << beg << "}{" << end << '}';
1534 add_known_environment(name, opt1, !opt2.empty(),
1535 from_utf8(beg), from_utf8(end));
1539 else if (t.cs() == "newtheorem") {
1540 string const name = p.getArg('{', '}');
1541 string const opt1 = p.getFullOpt();
1542 string const opt2 = p.getFullOpt();
1543 string const body = p.verbatim_item();
1544 string const opt3 = p.getFullOpt();
1546 add_known_theorem(name, opt1, !opt2.empty(),
1547 from_utf8("\\newtheorem{" + name + '}' +
1548 opt1 + opt2 + '{' + body + '}' + opt3));
1550 if (!in_lyx_preamble)
1551 h_preamble << "\\newtheorem{" << name << '}'
1552 << opt1 << opt2 << '{' << '}' << opt3;
1555 else if (t.cs() == "def") {
1556 string name = p.get_token().cs();
1557 // In fact, name may be more than the name:
1558 // In the test case of bug 8116
1559 // name == "csname SF@gobble@opt \endcsname".
1560 // Therefore, we need to use asInput() instead of cs().
1561 while (p.next_token().cat() != catBegin)
1562 name += p.get_token().asInput();
1563 if (!in_lyx_preamble)
1564 h_preamble << "\\def\\" << name << '{'
1565 << p.verbatim_item() << "}";
1568 else if (t.cs() == "newcolumntype") {
1569 string const name = p.getArg('{', '}');
1570 trimSpaceAndEol(name);
1572 string opts = p.getOpt();
1573 if (!opts.empty()) {
1574 istringstream is(string(opts, 1));
1577 special_columns[name[0]] = nargs;
1578 h_preamble << "\\newcolumntype{" << name << "}";
1580 h_preamble << "[" << nargs << "]";
1581 h_preamble << "{" << p.verbatim_item() << "}";
1584 else if (t.cs() == "setcounter") {
1585 string const name = p.getArg('{', '}');
1586 string const content = p.getArg('{', '}');
1587 if (name == "secnumdepth")
1588 h_secnumdepth = content;
1589 else if (name == "tocdepth")
1590 h_tocdepth = content;
1592 h_preamble << "\\setcounter{" << name << "}{" << content << "}";
1595 else if (t.cs() == "setlength") {
1596 string const name = p.verbatim_item();
1597 string const content = p.verbatim_item();
1598 // the paragraphs are only not indented when \parindent is set to zero
1599 if (name == "\\parindent" && content != "") {
1600 if (content[0] == '0')
1601 h_paragraph_separation = "skip";
1603 h_paragraph_indentation = translate_len(content);
1604 } else if (name == "\\parskip") {
1605 if (content == "\\smallskipamount")
1606 h_defskip = "smallskip";
1607 else if (content == "\\medskipamount")
1608 h_defskip = "medskip";
1609 else if (content == "\\bigskipamount")
1610 h_defskip = "bigskip";
1612 h_defskip = content;
1614 h_preamble << "\\setlength{" << name << "}{" << content << "}";
1617 else if (t.cs() == "onehalfspacing")
1618 h_spacing = "onehalf";
1620 else if (t.cs() == "doublespacing")
1621 h_spacing = "double";
1623 else if (t.cs() == "setstretch")
1624 h_spacing = "other " + p.verbatim_item();
1626 else if (t.cs() == "synctex") {
1627 // the scheme is \synctex=value
1628 // where value can only be "1" or "-1"
1629 h_output_sync = "1";
1630 // there can be any character behind the value (e.g. a linebreak or a '\'
1631 // therefore we extract it char by char
1633 string value = p.get_token().asInput();
1635 value += p.get_token().asInput();
1636 h_output_sync_macro = "\\synctex=" + value;
1639 else if (t.cs() == "begin") {
1640 string const name = p.getArg('{', '}');
1641 if (name == "document")
1643 h_preamble << "\\begin{" << name << "}";
1646 else if (t.cs() == "geometry") {
1647 vector<string> opts = split_options(p.getArg('{', '}'));
1648 handle_geometry(opts);
1651 else if (t.cs() == "definecolor") {
1652 string const color = p.getArg('{', '}');
1653 string const space = p.getArg('{', '}');
1654 string const value = p.getArg('{', '}');
1655 if (color == "document_fontcolor" && space == "rgb") {
1656 RGBColor c(RGBColorFromLaTeX(value));
1657 h_fontcolor = X11hexname(c);
1658 } else if (color == "note_fontcolor" && space == "rgb") {
1659 RGBColor c(RGBColorFromLaTeX(value));
1660 h_notefontcolor = X11hexname(c);
1661 } else if (color == "page_backgroundcolor" && space == "rgb") {
1662 RGBColor c(RGBColorFromLaTeX(value));
1663 h_backgroundcolor = X11hexname(c);
1664 } else if (color == "shadecolor" && space == "rgb") {
1665 RGBColor c(RGBColorFromLaTeX(value));
1666 h_boxbgcolor = X11hexname(c);
1668 h_preamble << "\\definecolor{" << color
1669 << "}{" << space << "}{" << value
1674 else if (t.cs() == "bibliographystyle")
1675 h_biblio_style = p.verbatim_item();
1677 else if (t.cs() == "jurabibsetup") {
1678 // FIXME p.getArg('{', '}') is most probably wrong (it
1679 // does not handle nested braces).
1680 // Use p.verbatim_item() instead.
1681 vector<string> jurabibsetup =
1682 split_options(p.getArg('{', '}'));
1683 // add jurabibsetup to the jurabib package options
1684 add_package("jurabib", jurabibsetup);
1685 if (!jurabibsetup.empty()) {
1686 h_preamble << "\\jurabibsetup{"
1687 << join(jurabibsetup, ",") << '}';
1691 else if (t.cs() == "hypersetup") {
1692 vector<string> hypersetup =
1693 split_options(p.verbatim_item());
1694 // add hypersetup to the hyperref package options
1695 handle_hyperref(hypersetup);
1696 if (!hypersetup.empty()) {
1697 h_preamble << "\\hypersetup{"
1698 << join(hypersetup, ",") << '}';
1702 else if (is_known(t.cs(), known_if_3arg_commands)) {
1703 // prevent misparsing of \usepackage if it is used
1704 // as an argument (see e.g. our own output of
1705 // \@ifundefined above)
1706 string const arg1 = p.verbatim_item();
1707 string const arg2 = p.verbatim_item();
1708 string const arg3 = p.verbatim_item();
1709 // test case \@ifundefined{date}{}{\date{}}
1710 if (t.cs() == "@ifundefined" && arg1 == "date" &&
1711 arg2.empty() && arg3 == "\\date{}") {
1712 h_suppress_date = "true";
1713 // older tex2lyx versions did output
1714 // \@ifundefined{definecolor}{\usepackage{color}}{}
1715 } else if (t.cs() == "@ifundefined" &&
1716 arg1 == "definecolor" &&
1717 arg2 == "\\usepackage{color}" &&
1719 if (!in_lyx_preamble)
1720 h_preamble << package_beg_sep
1723 << "\\@ifundefined{definecolor}{color}{}"
1726 //\@ifundefined{showcaptionsetup}{}{%
1727 // \PassOptionsToPackage{caption=false}{subfig}}
1728 // that LyX uses for subfloats
1729 } else if (t.cs() == "@ifundefined" &&
1730 arg1 == "showcaptionsetup" && arg2.empty()
1731 && arg3 == "%\n \\PassOptionsToPackage{caption=false}{subfig}") {
1733 } else if (!in_lyx_preamble) {
1734 h_preamble << t.asInput()
1735 << '{' << arg1 << '}'
1736 << '{' << arg2 << '}'
1737 << '{' << arg3 << '}';
1741 else if (is_known(t.cs(), known_if_commands)) {
1742 // must not parse anything in conditional code, since
1743 // LyX would output the parsed contents unconditionally
1744 if (!in_lyx_preamble)
1745 h_preamble << t.asInput();
1746 handle_if(p, in_lyx_preamble);
1749 else if (!t.cs().empty() && !in_lyx_preamble)
1750 h_preamble << '\\' << t.cs();
1753 // remove the whitespace
1756 // Force textclass if the user wanted it
1757 if (!forceclass.empty())
1758 h_textclass = forceclass;
1759 tc.setName(h_textclass);
1761 cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl;
1764 if (h_papersides.empty()) {
1767 h_papersides = ss.str();
1770 // If the CJK package is used we cannot set the document language from
1771 // the babel options. Instead, we guess which language is used most
1772 // and set this one.
1773 default_language = h_language;
1774 if (is_full_document &&
1775 (auto_packages.find("CJK") != auto_packages.end() ||
1776 auto_packages.find("CJKutf8") != auto_packages.end())) {
1778 h_language = guessLanguage(p, default_language);
1784 string babel2lyx(string const & language)
1786 char const * const * where = is_known(language, known_languages);
1788 return known_coded_languages[where - known_languages];
1793 string Preamble::polyglossia2lyx(string const & language)
1795 char const * const * where = is_known(language, polyglossia_languages);
1797 return coded_polyglossia_languages[where - polyglossia_languages];
1802 string rgbcolor2code(string const & name)
1804 char const * const * where = is_known(name, known_basic_colors);
1806 // "red", "green" etc
1807 return known_basic_color_codes[where - known_basic_colors];
1809 // "255,0,0", "0,255,0" etc
1810 RGBColor c(RGBColorFromLaTeX(name));
1811 return X11hexname(c);