X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ftex2lyx%2FPreamble.cpp;h=8b327b100d0a04622e872e0578b5df52ead4b5bd;hb=62af7ee772f16f154225d2d0b65d77f4376b6001;hp=613cbbb8c1e89c384462fd012c88f1257e0436f0;hpb=ee41b0f1930604e73f0525864cf3846e7459a449;p=lyx.git diff --git a/src/tex2lyx/Preamble.cpp b/src/tex2lyx/Preamble.cpp index 613cbbb8c1..8b327b100d 100644 --- a/src/tex2lyx/Preamble.cpp +++ b/src/tex2lyx/Preamble.cpp @@ -51,18 +51,18 @@ namespace { * please keep this in sync with known_coded_languages line by line! */ const char * const known_languages[] = {"acadian", "afrikaans", "albanian", -"american", "arabic", "arabtex", "australian", "austrian", "bahasa", "bahasai", -"bahasam", "basque", "belarusian", "brazil", "brazilian", "breton", "british", +"american", "arabic", "arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasai", +"bahasam", "basque", "belarusian", "bosnian", "brazil", "brazilian", "breton", "british", "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "francais", -"french", "frenchb", "frenchle", "frenchpro", "galician", "german", "germanb", +"french", "frenchb", "frenchle", "frenchpro", "friulan", "galician", "german", "germanb", "georgian", "greek", "hebrew", "hungarian", "icelandic", "indon", "indonesian", "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin", -"latvian", "lithuanian", "lowersorbian", "lsorbian", "magyar", "malay", "meyalu", +"latvian", "lithuanian", "lowersorbian", "lsorbian", "macedonian", "magyar", "malay", "meyalu", "mongolian", "naustrian", "newzealand", "ngerman", "ngermanb", "norsk", "nswissgerman", -"nynorsk", "polutonikogreek", "polish", "portuges", "portuguese", "romanian", "russian", -"russianb", "samin", "scottish", "serbian", "serbian-latin", "slovak", -"slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen", +"nynorsk", "piedmontese", "polutonikogreek", "polish", "portuges", "portuguese", +"romanian", "romansh", "russian", "russianb", "samin", "scottish", "serbian", "serbian-latin", +"slovak", "slovene", "spanish", "swedish", "swissgerman", "thai", "turkish", "turkmen", "ukraineb", "ukrainian", "uppersorbian", "UKenglish", "USenglish", "usorbian", "vietnam", "welsh", 0}; @@ -72,50 +72,68 @@ const char * const known_languages[] = {"acadian", "afrikaans", "albanian", * please keep this in sync with known_languages line by line! */ const char * const known_coded_languages[] = {"french", "afrikaans", "albanian", -"american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "bahasa", "bahasa", -"bahasam", "basque", "belarusian", "brazilian", "brazilian", "breton", "british", +"american", "arabic_arabi", "arabic_arabtex", "australian", "austrian", "azerbaijani", "bahasa", "bahasa", +"bahasam", "basque", "belarusian", "bosnian", "brazilian", "brazilian", "breton", "british", "bulgarian", "canadian", "canadien", "catalan", "croatian", "czech", "danish", "dutch", "english", "esperanto", "estonian", "farsi", "finnish", "french", -"french", "french", "french", "french", "galician", "german", "german", +"french", "french", "french", "french", "friulan", "galician", "german", "german", "georgian", "greek", "hebrew", "magyar", "icelandic", "bahasa", "bahasa", "interlingua", "irish", "italian", "japanese", "kazakh", "kurmanji", "latin", -"latvian", "lithuanian", "lowersorbian", "lowersorbian", "magyar", "bahasam", "bahasam", +"latvian", "lithuanian", "lowersorbian", "lowersorbian", "macedonian", "magyar", "bahasam", "bahasam", "mongolian", "naustrian", "newzealand", "ngerman", "ngerman", "norsk", "german-ch", -"nynorsk", "polutonikogreek", "polish", "portuguese", "portuguese", "romanian", "russian", -"russian", "samin", "scottish", "serbian", "serbian-latin", "slovak", -"slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen", +"nynorsk", "piedmontese", "polutonikogreek", "polish", "portuguese", "portuguese", +"romanian", "romansh", "russian", "russian", "samin", "scottish", "serbian", "serbian-latin", +"slovak", "slovene", "spanish", "swedish", "german-ch-old", "thai", "turkish", "turkmen", "ukrainian", "ukrainian", "uppersorbian", "english", "english", "uppersorbian", "vietnamese", "welsh", 0}; +/// languages with british quotes (.lyx names) +const char * const known_british_quotes_languages[] = {"british", "welsh", 0}; + +/// languages with cjk quotes (.lyx names) +const char * const known_cjk_quotes_languages[] = {"chinese-traditional", +"japanese", "japanese-cjk", 0}; + +/// languages with cjk-angle quotes (.lyx names) +const char * const known_cjkangle_quotes_languages[] = {"korean", 0}; + /// languages with danish quotes (.lyx names) const char * const known_danish_quotes_languages[] = {"danish", 0}; /// languages with english quotes (.lyx names) const char * const known_english_quotes_languages[] = {"american", "australian", -"bahasa", "bahasam", "brazilian", "canadian", "chinese-simplified", "english", -"esperanto", "hebrew", "irish", "korean", "newzealand", "portuguese", "scottish", -"thai", 0}; +"bahasa", "bahasam", "bengali", "brazilian", "canadian", "chinese-simplified", "english", +"esperanto", "farsi", "interlingua", "irish", "newzealand", "scottish", +"thai", "turkish", "vietnamese", 0}; /// languages with french quotes (.lyx names) -const char * const known_french_quotes_languages[] = {"albanian", -"arabic_arabi", "arabic_arabtex", "basque", "canadien", "catalan", "french", -"galician", "greek", "italian", "norsk", "nynorsk", "polutonikogreek", -"russian", "spanish", "spanish-mexico", "turkish", "turkmen", "ukrainian", -"vietnamese", 0}; +const char * const known_french_quotes_languages[] = {"ancientgreek", +"arabic_arabi", "arabic_arabtex", "asturian", "belarusian", "breton", +"canadien", "catalan", "french", "friulan", "galician", "italian", "occitan", +"piedmontese", "portuguese", "spanish", "spanish-mexico", 0}; /// languages with german quotes (.lyx names) const char * const known_german_quotes_languages[] = {"austrian", "bulgarian", -"czech", "german", "georgian", "icelandic", "lithuanian", "lowersorbian", "naustrian", -"ngerman", "serbian", "serbian-latin", "slovak", "slovene", "uppersorbian", 0}; +"czech", "estonian", "georgian", "german", "icelandic", "latvian", "lithuanian", +"lowersorbian", "macedonian", "naustrian", "ngerman", "romansh", "slovak", "slovene", +"uppersorbian", 0}; /// languages with polish quotes (.lyx names) -const char * const known_polish_quotes_languages[] = {"afrikaans", "croatian", -"dutch", "estonian", "magyar", "polish", "romanian", 0}; +const char * const known_polish_quotes_languages[] = {"afrikaans", "bosnian", "croatian", +"dutch", "magyar", "polish", "romanian", "serbian", "serbian-latin", 0}; + +/// languages with russian quotes (.lyx names) +const char * const known_russian_quotes_languages[] = {"azerbaijani", "oldrussian", +"russian", "ukrainian", 0}; /// languages with swedish quotes (.lyx names) -const char * const known_swedish_quotes_languages[] = {"finnish", -"swedish", 0}; +const char * const known_swedish_quotes_languages[] = {"finnish", "swedish", 0}; + +/// languages with swiss quotes (.lyx names) +const char * const known_swiss_quotes_languages[] = {"albanian", +"armenian", "basque", "churchslavonic", "german-ch", "german-ch-old", +"norsk", "nynorsk", "turkmen", "ukrainian", "vietnamese", 0}; /// known language packages from the times before babel const char * const known_old_language_packages[] = {"french", "frenchle", @@ -123,30 +141,29 @@ const char * const known_old_language_packages[] = {"french", "frenchle", char const * const known_fontsizes[] = { "10pt", "11pt", "12pt", 0 }; -const char * const known_roman_fonts[] = { "ae", "beraserif", "bookman", -"ccfonts", "chancery", "charter", "cmr", "fourier", "garamondx", "libertine", -"libertine-type1", "lmodern", "mathdesign", "mathpazo", "mathptmx", "newcent", -"tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", 0}; +const char * const known_roman_font_packages[] = { "ae", "beraserif", "bookman", +"ccfonts", "chancery", "charter", "cmr", "cochineal", "crimson", "CrimsonPro", "DejaVuSerif", +"DejaVuSerifCondensed", "fourier", "garamondx", "libertine", "libertineRoman", "libertine-type1", +"lmodern", "mathdesign", "mathpazo", "mathptmx", "MinionPro", "newcent", "noto", "noto-serif", +"PTSerif", "tgbonum", "tgchorus", "tgpagella", "tgschola", "tgtermes", "utopia", "xcharter", 0 }; -const char * const known_sans_fonts[] = { "avant", "berasans", "biolinum-type1", -"cmbr", "cmss", "helvet", "iwona", "iwonac", "iwonal", "iwonalc", "kurier", -"kurierc", "kurierl", "kurierlc", "lmss", "tgadventor", "tgheros", 0}; +const char * const known_sans_font_packages[] = { "avant", "berasans", "biolinum", +"biolinum-type1", "cantarell", "Chivo", "cmbr", "cmss", "DejaVuSans", "DejaVuSansCondensed", "FiraSans", "helvet", "iwona", +"iwonac", "iwonal", "iwonalc", "kurier", "kurierc", "kurierl", "kurierlc", "lmss", "noto-sans", "PTSans", +"tgadventor", "tgheros", "uop", 0 }; -const char * const known_typewriter_fonts[] = { "beramono", "cmtl", "cmtt", -"courier", "lmtt", "luximono", "fourier", "libertineMono-type1", "lmodern", -"mathpazo", "mathptmx", "newcent", "tgcursor", "txtt", 0}; +const char * const known_typewriter_font_packages[] = { "beramono", "cmtl", "cmtt", "courier", "DejaVuSansMono", +"FiraMono", "lmtt", "luximono", "libertineMono", "libertineMono-type1", "lmodern", +"mathpazo", "mathptmx", "newcent", "noto-mono", "PTMono", "tgcursor", "txtt", 0 }; -const char * const known_math_fonts[] = { "eulervm", "newtxmath", 0}; +const char * const known_math_font_packages[] = { "eulervm", "newtxmath", 0}; -const char * const known_paper_sizes[] = { "a0paper", "b0paper", "c0paper", +const char * const known_latex_paper_sizes[] = { "a0paper", "b0paper", "c0paper", "a1paper", "b1paper", "c1paper", "a2paper", "b2paper", "c2paper", "a3paper", "b3paper", "c3paper", "a4paper", "b4paper", "c4paper", "a5paper", "b5paper", "c5paper", "a6paper", "b6paper", "c6paper", "executivepaper", "legalpaper", "letterpaper", "b0j", "b1j", "b2j", "b3j", "b4j", "b5j", "b6j", 0}; -const char * const known_class_paper_sizes[] = { "a4paper", "a5paper", -"executivepaper", "legalpaper", "letterpaper", 0}; - const char * const known_paper_margins[] = { "lmargin", "tmargin", "rmargin", "bmargin", "headheight", "headsep", "footskip", "columnsep", 0}; @@ -171,6 +188,11 @@ const char * const known_basic_color_codes[] = {"#000000", "#0000ff", "#964B00", const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists", 0}; +/*! + * Known file extensions for TeX files as used by \\includeonly + */ +char const * const known_tex_extensions[] = {"tex", 0}; + /// packages that work only in xetex /// polyglossia is handled separately const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian", @@ -181,12 +203,10 @@ const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian", const char * const known_lyx_packages[] = {"amsbsy", "amsmath", "amssymb", "amstext", "amsthm", "array", "babel", "booktabs", "calc", "CJK", "color", "float", "fontspec", "framed", "graphicx", "hhline", "ifthen", "longtable", -"makeidx", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle", "rotating", -"rotfloat", "splitidx", "setspace", "subscript", "textcomp", "tipa", "tipx", -"tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xunicode", 0}; - -// used for the handling of \newindex -int index_number = 0; +"makeidx", "minted", "multirow", "nomencl", "pdfpages", "prettyref", "refstyle", +"rotating", "rotfloat", "splitidx", "setspace", "subscript", "tabularx","textcomp", "tipa", +"tipx", "tone", "ulem", "url", "varioref", "verbatim", "wrapfig", "xcolor", "xltabular", +"xunicode", 0}; // codes used to remove packages that are loaded automatically by LyX. // Syntax: package_beg_seppackage_mid_seppackage_end_sep @@ -259,7 +279,7 @@ vector split_options(string const & input) p.skip_spaces(true); if (p.next_token().asInput() == "{") option += '{' + p.getArg('{', '}') + '}'; - } else if (t.cat() != catSpace) + } else if (t.cat() != catSpace && t.cat() != catComment) option += t.asInput(); } @@ -299,36 +319,38 @@ string process_keyval_opt(vector & options, string name) * FIXME: support spelling=old for german variants (german vs. ngerman LyX names etc) */ const char * const Preamble::polyglossia_languages[] = { -"albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi", -"nynorsk", "syriac", "arabic", "danish", "icelandic", "occitan", "tamil", -"armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch", -"irish", "portuges", "thai", "bahasai", "english", "italian", "romanian", "turkish", -"bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin", -"samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "tibetan", "urdu", -"brazil", "brazilian", "finnish", "lithuanian", "scottish", "usorbian", "breton", -"french", "lsorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", -"slovak", "welsh", "catalan", "german", "malayalam", "slovenian", "coptic", "greek", -"marathi", "spanish", "austrian", -"american", "ancient", "australian", "british", "monotonic", "newzealand", -"polytonic", 0}; +"albanian", "american", "amharic", "ancient", "arabic", "armenian", "asturian", "australian", +"bahasai", "bahasam", "basque", "bengali", "brazil", "brazilian", "breton", "british", "bulgarian", +"catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch", +"english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan", +"galician", "greek", "monotonic", "hebrew", "hindi", +"icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean", +"lao", "latin", "latvian", "lithuanian", "lsorbian", "magyar", "malayalam", "marathi", +"austrian", "newzealand", "german", "norsk", "nynorsk", "occitan", "oldrussian", +"piedmontese", "polish", "polytonic", "portuges", "romanian", "romansh", "russian", +"samin", "sanskrit", "scottish", "serbian", "slovak", "slovenian", "spanish", "swedish", "syriac", +"tamil", "telugu", "thai", "tibetan", "turkish", "turkmen", +"ukrainian", "urdu", "usorbian", "vietnamese", "welsh", 0}; +// not yet supported by LyX: "korean", "nko" /** * the same as polyglossia_languages with .lyx names * please keep this in sync with polyglossia_languages line by line! */ const char * const Preamble::coded_polyglossia_languages[] = { -"albanian", "croatian", "hebrew", "norsk", "swedish", "amharic", "czech", "hindi", -"nynorsk", "syriac", "arabic_arabi", "danish", "icelandic", "occitan", "tamil", -"armenian", "divehi", "interlingua", "polish", "telugu", "asturian", "dutch", -"irish", "portuges", "thai", "bahasa", "english", "italian", "romanian", "turkish", -"bahasam", "esperanto", "lao", "russian", "turkmen", "basque", "estonian", "latin", -"samin", "ukrainian", "bengali", "farsi", "latvian", "sanskrit", "tibetan", "urdu", -"brazilian", "brazilian", "finnish", "lithuanian", "scottish", "uppersorbian", "breton", -"french", "lowersorbian", "serbian", "vietnamese", "bulgarian", "galician", "magyar", -"slovak", "welsh", "catalan", "ngerman", "malayalam", "slovene", "coptic", "greek", -"marathi", "spanish", "naustrian", -"american", "ancientgreek", "australian", "british", "greek", "newzealand", -"polutonikogreek", 0}; +"albanian", "american", "amharic", "ancientgreek", "arabic_arabi", "armenian", "asturian", "australian", +"bahasa", "bahasam", "basque", "bengali", "brazilian", "brazilian", "breton", "british", "bulgarian", +"catalan", "churchslavonic", "coptic", "croatian", "czech", "danish", "divehi", "dutch", +"english", "esperanto", "estonian", "farsi", "finnish", "french", "friulan", +"galician", "greek", "greek", "hebrew", "hindi", +"icelandic", "interlingua", "irish", "italian", "kannada", "khmer", "korean", +"lao", "latin", "latvian", "lithuanian", "lowersorbian", "magyar", "malayalam", "marathi", +"naustrian","newzealand", "ngerman", "norsk", "nynorsk", "occitan", "oldrussian", +"piedmontese", "polish", "polutonikogreek", "portuges", "romanian", "romansh", "russian", +"samin", "sanskrit", "scottish", "serbian", "slovak", "slovene", "spanish", "swedish", "syriac", +"tamil", "telugu", "thai", "tibetan", "turkish", "turkmen", +"ukrainian", "urdu", "uppersorbian", "vietnamese", "welsh", 0}; +// not yet supported by LyX: "korean-polyglossia", "nko" bool Preamble::usePolyglossia() const @@ -349,6 +371,12 @@ bool Preamble::isPackageUsed(string const & package) const } +bool Preamble::isPackageAutoLoaded(string const & package) const +{ + return auto_packages.find(package) != auto_packages.end(); +} + + vector Preamble::getPackageOptions(string const & package) const { map >::const_iterator it = used_packages.find(package); @@ -366,6 +394,10 @@ void Preamble::registerAutomaticallyLoadedPackage(std::string const & package) void Preamble::addModule(string const & module) { + for (auto const & m : used_modules) { + if (m == module) + return; + } used_modules.push_back(module); } @@ -379,9 +411,9 @@ void Preamble::suppressDate(bool suppress) } -void Preamble::registerAuthor(std::string const & name) +void Preamble::registerAuthor(std::string const & name, string const & initials) { - Author author(from_utf8(name), empty_docstring()); + Author author(from_utf8(name), empty_docstring(), from_utf8(initials)); author.setUsed(true); authors_.record(author); h_tracking_changes = "true"; @@ -391,7 +423,7 @@ void Preamble::registerAuthor(std::string const & name) Author const & Preamble::getAuthor(std::string const & name) const { - Author author(from_utf8(name), empty_docstring()); + Author author(from_utf8(name), empty_docstring(), empty_docstring()); for (AuthorList::Authors::const_iterator it = authors_.begin(); it != authors_.end(); ++it) if (*it == author) @@ -416,6 +448,15 @@ void Preamble::add_package(string const & name, vector & options) if (used_packages.find(name) == used_packages.end()) used_packages[name] = split_options(h_options); + // Insert options passed via PassOptionsToPackage + for (auto const & p : extra_package_options_) { + if (p.first == name) { + vector eo = getVectorFromString(p.second); + for (auto const & eoi : eo) + options.push_back(eoi); + } + + } vector & v = used_packages[name]; v.insert(v.end(), options.begin(), options.end()); if (name == "jurabib") { @@ -431,12 +472,11 @@ void Preamble::add_package(string const & name, vector & options) namespace { -// Given is a string like "scaled=0.9" or "Scale=0.9", return 0.9 * 100 +// Given is a string like "scaled=0.9" or "scale=0.9", return 0.9 * 100 bool scale_as_percentage(string const & scale, string & percentage) { - string::size_type pos = scale.find('='); - if (pos != string::npos) { - string value = scale.substr(pos + 1); + if (contains(scale, '=')) { + string const value = support::split(scale, '='); if (isStrDbl(value)) { percentage = convert( static_cast(100 * convert(value))); @@ -460,7 +500,7 @@ string remove_braces(string const & value) Preamble::Preamble() : one_language(true), explicit_babel(false), - title_layout_found(false), h_font_cjk_set(false) + title_layout_found(false), index_number(0), h_font_cjk_set(false) { //h_backgroundcolor; //h_boxbgcolor; @@ -470,20 +510,34 @@ Preamble::Preamble() : one_language(true), explicit_babel(false), h_cite_engine_type = "default"; h_color = "#008000"; h_defskip = "medskip"; + h_dynamic_quotes = false; //h_float_placement; //h_fontcolor; h_fontencoding = "default"; - h_font_roman = "default"; - h_font_sans = "default"; - h_font_typewriter = "default"; - h_font_math = "auto"; + h_font_roman[0] = "default"; + h_font_roman[1] = "default"; + h_font_sans[0] = "default"; + h_font_sans[1] = "default"; + h_font_typewriter[0] = "default"; + h_font_typewriter[1] = "default"; + h_font_math[0] = "auto"; + h_font_math[1] = "auto"; h_font_default_family = "default"; h_use_non_tex_fonts = false; h_font_sc = "false"; - h_font_osf = "false"; - h_font_sf_scale = "100"; - h_font_tt_scale = "100"; + h_font_roman_osf = "false"; + h_font_sans_osf = "false"; + h_font_typewriter_osf = "false"; + h_font_sf_scale[0] = "100"; + h_font_sf_scale[1] = "100"; + h_font_tt_scale[0] = "100"; + h_font_tt_scale[1] = "100"; + // h_font_roman_opts; + // h_font_sans_opts; + // h_font_typewriter_opts; //h_font_cjk + h_is_mathindent = "0"; + h_math_numbering_side = "default"; h_graphics = "default"; h_default_output_format = "default"; h_html_be_strict = "false"; @@ -491,7 +545,7 @@ Preamble::Preamble() : one_language(true), explicit_babel(false), h_html_math_output = "0"; h_index[0] = "Index"; h_index_command = "default"; - h_inputencoding = "auto"; + h_inputencoding = "auto-legacy"; h_justification = "true"; h_language = "english"; h_language_package = "none"; @@ -501,6 +555,7 @@ Preamble::Preamble() : one_language(true), explicit_babel(false), //h_notefontcolor; //h_options; h_output_changes = "false"; + h_change_bars = "false"; h_output_sync = "0"; //h_output_sync_macro h_papercolumns = "1"; @@ -526,20 +581,25 @@ Preamble::Preamble() : one_language(true), explicit_babel(false), h_pdf_pdfusetitle = "0"; //h_pdf_pagemode; //h_pdf_quoted_options; - h_quotes_language = "english"; + h_quotes_style = "english"; h_secnumdepth = "3"; h_shortcut[0] = "idx"; h_spacing = "single"; + h_save_transient_properties = "true"; h_suppress_date = "false"; h_textclass = "article"; h_tocdepth = "3"; h_tracking_changes = "false"; h_use_bibtopic = "false"; + h_use_dash_ligatures = "true"; h_use_indices = "false"; h_use_geometry = "false"; h_use_default_options = "false"; h_use_hyperref = "false"; + h_use_microtype = "false"; + h_use_lineno = "false"; h_use_refstyle = false; + h_use_minted = false; h_use_packages["amsmath"] = "1"; h_use_packages["amssymb"] = "0"; h_use_packages["cancel"] = "0"; @@ -647,13 +707,18 @@ void Preamble::handle_geometry(vector & options) options.erase(it); } // paper size - // keyval version: "paper=letter" + // keyval version: "paper=letter" or "paper=letterpaper" string paper = process_keyval_opt(options, "paper"); if (!paper.empty()) - h_papersize = paper + "paper"; + if (suffixIs(paper, "paper")) + paper = subst(paper, "paper", ""); // alternative version: "letterpaper" - handle_opt(options, known_paper_sizes, h_papersize); - delete_opt(options, known_paper_sizes); + handle_opt(options, known_latex_paper_sizes, paper); + if (suffixIs(paper, "paper")) + paper = subst(paper, "paper", ""); + delete_opt(options, known_latex_paper_sizes); + if (!paper.empty()) + h_papersize = paper; // page margins char const * const * margin = known_paper_margins; for (; *margin; ++margin) { @@ -668,148 +733,789 @@ void Preamble::handle_geometry(vector & options) void Preamble::handle_package(Parser &p, string const & name, - string const & opts, bool in_lyx_preamble) + string const & opts, bool in_lyx_preamble, + bool detectEncoding) { vector options = split_options(opts); add_package(name, options); - char const * const * where = 0; if (is_known(name, known_xetex_packages)) { xetex = true; h_use_non_tex_fonts = true; registerAutomaticallyLoadedPackage("fontspec"); - if (h_inputencoding == "auto") + if (h_inputencoding == "auto-legacy") p.setEncoding("UTF-8"); } + // vector of all options for easier parsing and + // skipping + vector allopts = getVectorFromString(opts); + // this stores the potential extra options + string xopts; + + // // roman fonts - if (is_known(name, known_roman_fonts)) - h_font_roman = name; + // + + // By default, we use the package name as LyX font name, + // so this only needs to be reset if these names differ + if (is_known(name, known_roman_font_packages)) + h_font_roman[0] = name; + + if (name == "ccfonts") { + for (auto const & opt : allopts) { + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_roman_opts = xopts; + options.clear(); + } + + if (name == "lmodern") { + for (auto const & opt : allopts) { + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_roman_opts = xopts; + options.clear(); + } if (name == "fourier") { - h_font_roman = "utopia"; - // when font uses real small capitals - if (opts == "expert") - h_font_sc = "true"; + h_font_roman[0] = "utopia"; + for (auto const & opt : allopts) { + if (opt == "osf") { + h_font_roman_osf = "true"; + continue; + } + if (opt == "expert") { + h_font_sc = "true"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_roman_opts = xopts; + options.clear(); } if (name == "garamondx") { - h_font_roman = "garamondx"; - if (opts == "osfI") - h_font_osf = "true"; + for (auto const & opt : allopts) { + if (opt == "osfI") { + h_font_roman_osf = "true"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_roman_opts = xopts; + options.clear(); } if (name == "libertine") { - h_font_roman = "libertine"; // this automatically invokes biolinum - h_font_sans = "biolinum"; - if (opts == "osf") - h_font_osf = "true"; - else if (opts == "lining") - h_font_osf = "false"; + h_font_sans[0] = "biolinum"; + // as well as libertineMono + h_font_typewriter[0] = "libertine-mono"; + for (auto const & opt : allopts) { + if (opt == "osf") { + h_font_roman_osf = "true"; + continue; + } + if (opt == "lining") { + h_font_roman_osf = "false"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_roman_opts = xopts; + options.clear(); } - if (name == "libertine-type1") { - h_font_roman = "libertine"; - // NOTE: contrary to libertine.sty, libertine-type1 - // does not automatically invoke biolinum + if (name == "libertineRoman" || name == "libertine-type1") { + h_font_roman[0] = "libertine"; + // NOTE: contrary to libertine.sty, libertineRoman + // and libertine-type1 do not automatically invoke + // biolinum and libertineMono if (opts == "lining") - h_font_osf = "false"; + h_font_roman_osf = "false"; else if (opts == "osf") - h_font_osf = "true"; + h_font_roman_osf = "true"; + } + + if (name == "MinionPro") { + h_font_roman[0] = "minionpro"; + h_font_roman_osf = "true"; + h_font_math[0] = "auto"; + for (auto const & opt : allopts) { + if (opt == "lf") { + h_font_roman_osf = "false"; + continue; + } + if (opt == "onlytext") { + h_font_math[0] = "default"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_roman_opts = xopts; + options.clear(); } if (name == "mathdesign") { - if (opts.find("charter") != string::npos) - h_font_roman = "md-charter"; - if (opts.find("garamond") != string::npos) - h_font_roman = "md-garamond"; - if (opts.find("utopia") != string::npos) - h_font_roman = "md-utopia"; - if (opts.find("expert") != string::npos) { - h_font_sc = "true"; - h_font_osf = "true"; + for (auto const & opt : allopts) { + if (opt == "charter") { + h_font_roman[0] = "md-charter"; + continue; + } + if (opt == "garamond") { + h_font_roman[0] = "md-garamond"; + continue; + } + if (opt == "utopia") { + h_font_roman[0] = "md-utopia"; + continue; + } + if (opt == "expert") { + h_font_sc = "true"; + h_font_roman_osf = "true"; + continue; + } + } + } + + else if (name == "mathpazo") { + h_font_roman[0] = "palatino"; + for (auto const & opt : allopts) { + if (opt == "osf") { + h_font_roman_osf = "true"; + continue; + } + if (opt == "sc") { + h_font_sc = "true"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_roman_opts = xopts; + options.clear(); + } + + else if (name == "mathptmx") { + h_font_roman[0] = "times"; + for (auto const & opt : allopts) { + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_roman_opts = xopts; + options.clear(); + } + + if (name == "crimson") + h_font_roman[0] = "cochineal"; + + if (name == "cochineal") { + for (auto const & opt : allopts) { + if (opt == "osf" || opt == "oldstyle") { + h_font_roman_osf = "true"; + continue; + } + if (opt == "proportional" || opt == "p") + continue; + if (!xopts.empty()) + xopts += ", "; + xopts += opt; } + if (!xopts.empty()) + h_font_roman_opts = xopts; + options.clear(); } - else if (name == "mathpazo") - h_font_roman = "palatino"; + if (name == "CrimsonPro") { + h_font_roman_osf = "true"; + for (auto const & opt : allopts) { + if (opt == "lf" || opt == "lining") { + h_font_roman_osf = "false"; + continue; + } + if (opt == "proportional" || opt == "p") + continue; + if (opt == "medium") { + h_font_roman[0] = "CrimsonProMedium"; + continue; + } + if (opt == "extralight") { + h_font_roman[0] = "CrimsonProExtraLight"; + continue; + } + if (opt == "light") { + h_font_roman[0] = "CrimsonProLight"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_roman_opts = xopts; + options.clear(); + } + + + if (name == "eco") + // font uses old-style figure + h_font_roman_osf = "true"; + + if (name == "paratype") { + // in this case all fonts are ParaType + h_font_roman[0] = "PTSerif-TLF"; + h_font_sans[0] = "default"; + h_font_typewriter[0] = "default"; + } + + if (name == "PTSerif") + h_font_roman[0] = "PTSerif-TLF"; + + if (name == "XCharter") { + h_font_roman[0] = "xcharter"; + for (auto const & opt : allopts) { + if (opt == "osf") { + h_font_roman_osf = "true"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_roman_opts = xopts; + options.clear(); + } + + if (name == "plex-serif") { + h_font_roman[0] = "IBMPlexSerif"; + for (auto const & opt : allopts) { + if (opt == "thin") { + h_font_roman[0] = "IBMPlexSerifThin"; + continue; + } + if (opt == "extralight") { + h_font_roman[0] = "IBMPlexSerifExtraLight"; + continue; + } + if (opt == "light") { + h_font_roman[0] = "IBMPlexSerifLight"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_roman_opts = xopts; + options.clear(); + } + + if (name == "noto-serif" || name == "noto") { + bool rm = false; + bool rmx = false; + bool sf = false; + bool sfx = false; + bool tt = false; + bool thin = false; + bool extralight = false; + bool light = false; + bool medium = false; + bool osf = false; + string scl; + if (name == "noto") { + rm = true; + sf = true; + tt = true; + } + // Since the options might apply to different shapes, + // we need to parse all options first and then handle them. + for (auto const & opt : allopts) { + if (opt == "regular") + // skip + continue; + if (opt == "rm") { + rm = true; + rmx = true; + sf = sfx; + tt = false; + continue; + } + if (opt == "thin") { + thin = true; + continue; + } + if (opt == "extralight") { + extralight = true; + continue; + } + if (opt == "light") { + light = true; + continue; + } + if (opt == "medium") { + medium = true; + continue; + } + if (opt == "sf") { + sfx = true; + sf = true; + rm = rmx; + tt = false; + continue; + } + if (opt == "nott") { + tt = false; + continue; + } + if (opt == "osf") { + osf = true; + continue; + } + if (prefixIs(opt, "scaled=")) { + scl = opt; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + options.clear(); + // handle options that might affect different shapes + if (name == "noto-serif" || rm) { + if (thin) + h_font_roman[0] = "NotoSerifThin"; + else if (extralight) + h_font_roman[0] = "NotoSerifExtralight"; + else if (light) + h_font_roman[0] = "NotoSerifLight"; + else if (medium) + h_font_roman[0] = "NotoSerifMedium"; + else + h_font_roman[0] = "NotoSerifRegular"; + if (osf) + h_font_roman_osf = "true"; + if (!xopts.empty()) + h_font_roman_opts = xopts; + } + if (name == "noto" && sf) { + if (thin) + h_font_sans[0] = "NotoSansThin"; + else if (extralight) + h_font_sans[0] = "NotoSansExtralight"; + else if (light) + h_font_sans[0] = "NotoSansLight"; + else if (medium) + h_font_sans[0] = "NotoSansMedium"; + else + h_font_sans[0] = "NotoSansRegular"; + if (osf) + h_font_sans_osf = "true"; + if (!scl.empty()) + scale_as_percentage(scl, h_font_sf_scale[0]); + if (!xopts.empty()) + h_font_sans_opts = xopts; + } + if (name == "noto" && tt) { + h_font_typewriter[0] = "NotoMonoRegular"; + if (osf) + h_font_typewriter_osf = "true"; + if (!scl.empty()) + scale_as_percentage(scl, h_font_tt_scale[0]); + if (!xopts.empty()) + h_font_typewriter_opts = xopts; + } + } - else if (name == "mathptmx") - h_font_roman = "times"; + if (name == "sourceserifpro") { + h_font_roman[0] = "ADOBESourceSerifPro"; + for (auto const & opt : allopts) { + if (opt == "osf") { + h_font_roman_osf = "true"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_roman_opts = xopts; + options.clear(); + } + // // sansserif fonts - if (is_known(name, known_sans_fonts)) { - h_font_sans = name; - if (options.size() >= 1) { - if (scale_as_percentage(opts, h_font_sf_scale)) - options.clear(); + // + + // By default, we use the package name as LyX font name, + // so this only needs to be reset if these names differ. + // Also, we handle the scaling option here generally. + if (is_known(name, known_sans_font_packages)) { + h_font_sans[0] = name; + if (contains(opts, "scale")) { + vector::iterator it = allopts.begin(); + for (; it != allopts.end() ; ++it) { + string const opt = *it; + if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) { + if (scale_as_percentage(opt, h_font_sf_scale[0])) { + allopts.erase(it); + break; + } + } + } + } + } + + if (name == "biolinum" || name == "biolinum-type1") { + h_font_sans[0] = "biolinum"; + for (auto const & opt : allopts) { + if (prefixIs(opt, "osf")) { + h_font_sans_osf = "true"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_sans_opts = xopts; + options.clear(); + } + + if (name == "cantarell") { + for (auto const & opt : allopts) { + if (opt == "defaultsans") + continue; + if (prefixIs(opt, "oldstyle")) { + h_font_sans_osf = "true"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_sans_opts = xopts; + options.clear(); + } + + if (name == "Chivo") { + for (auto const & opt : allopts) { + if (opt == "thin") { + h_font_roman[0] = "ChivoThin"; + continue; + } + if (opt == "light") { + h_font_roman[0] = "ChivoLight"; + continue; + } + if (opt == "regular") { + h_font_roman[0] = "Chivo"; + continue; + } + if (opt == "medium") { + h_font_roman[0] = "ChivoMedium"; + continue; + } + if (prefixIs(opt, "oldstyle")) { + h_font_sans_osf = "true"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_sans_opts = xopts; + options.clear(); + } + + if (name == "PTSans") { + h_font_sans[0] = "PTSans-TLF"; + } + + if (name == "FiraSans") { + h_font_sans_osf = "true"; + for (auto const & opt : allopts) { + if (opt == "book") { + h_font_sans[0] = "FiraSansBook"; + continue; + } + if (opt == "thin") { + continue; + } + if (opt == "extralight") { + h_font_sans[0] = "FiraSansExtralight"; + continue; + } + if (opt == "light") { + h_font_sans[0] = "FiraSansLight"; + continue; + } + if (opt == "ultralight") { + h_font_sans[0] = "FiraSansUltralight"; + continue; + } + if (opt == "thin") { + h_font_sans[0] = "FiraSansThin"; + continue; + } + if (opt == "lf" || opt == "lining") { + h_font_sans_osf = "false"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_sans_opts = xopts; + options.clear(); + } + + if (name == "plex-sans") { + h_font_sans[0] = "IBMPlexSans"; + for (auto const & opt : allopts) { + if (opt == "condensed") { + h_font_sans[0] = "IBMPlexSansCondensed"; + continue; + } + if (opt == "thin") { + h_font_sans[0] = "IBMPlexSansThin"; + continue; + } + if (opt == "extralight") { + h_font_sans[0] = "IBMPlexSansExtraLight"; + continue; + } + if (opt == "light") { + h_font_sans[0] = "IBMPlexSansLight"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_sans_opts = xopts; + options.clear(); + } + + if (name == "noto-sans") { + h_font_sans[0] = "NotoSansRegular"; + for (auto const & opt : allopts) { + if (opt == "regular") + continue; + if (opt == "medium") { + h_font_sans[0] = "NotoSansMedium"; + continue; + } + if (opt == "thin") { + h_font_sans[0] = "NotoSansThin"; + continue; + } + if (opt == "extralight") { + h_font_sans[0] = "NotoSansExtralight"; + continue; + } + if (opt == "light") { + h_font_sans[0] = "NotoSansLight"; + continue; + } + if (opt == "osf") { + h_font_sans_osf = "true"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; } + if (!xopts.empty()) + h_font_sans_opts = xopts; + options.clear(); } - if (name == "biolinum-type1") { - h_font_sans = "biolinum"; - // biolinum can have several options, e.g. [osf,scaled=0.97] - string::size_type pos = opts.find("osf"); - if (pos != string::npos) - h_font_osf = "true"; + if (name == "sourcesanspro") { + h_font_sans[0] = "ADOBESourceSansPro"; + for (auto const & opt : allopts) { + if (opt == "osf") { + h_font_sans_osf = "true"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_sans_opts = xopts; + options.clear(); } + // // typewriter fonts - if (is_known(name, known_typewriter_fonts)) { - // fourier can be set as roman font _only_ - // fourier as typewriter is handled in handling of \ttdefault - if (name != "fourier") { - h_font_typewriter = name; - if (options.size() >= 1) { - if (scale_as_percentage(opts, h_font_tt_scale)) - options.clear(); + // + + // By default, we use the package name as LyX font name, + // so this only needs to be reset if these names differ. + // Also, we handle the scaling option here generally. + if (is_known(name, known_typewriter_font_packages)) { + h_font_typewriter[0] = name; + if (contains(opts, "scale")) { + vector::iterator it = allopts.begin(); + for (; it != allopts.end() ; ++it) { + string const opt = *it; + if (prefixIs(opt, "scaled=") || prefixIs(opt, "scale=")) { + if (scale_as_percentage(opt, h_font_tt_scale[0])) { + allopts.erase(it); + break; + } + } } } } - if (name == "libertineMono-type1") { - h_font_typewriter = "libertine-mono"; + if (name == "libertineMono" || name == "libertineMono-type1") + h_font_typewriter[0] = "libertine-mono"; + + if (name == "FiraMono") { + h_font_typewriter_osf = "true"; + for (auto const & opt : allopts) { + if (opt == "lf" || opt == "lining") { + h_font_typewriter_osf = "false"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_typewriter_opts = xopts; + options.clear(); } - // font uses old-style figure - if (name == "eco") - h_font_osf = "true"; + if (name == "PTMono") + h_font_typewriter[0] = "PTMono-TLF"; + if (name == "plex-mono") { + h_font_typewriter[0] = "IBMPlexMono"; + for (auto const & opt : allopts) { + if (opt == "thin") { + h_font_typewriter[0] = "IBMPlexMonoThin"; + continue; + } + if (opt == "extralight") { + h_font_typewriter[0] = "IBMPlexMonoExtraLight"; + continue; + } + if (opt == "light") { + h_font_typewriter[0] = "IBMPlexMonoLight"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_typewriter_opts = xopts; + options.clear(); + } + + if (name == "noto-mono") { + h_font_typewriter[0] = "NotoMonoRegular"; + for (auto const & opt : allopts) { + if (opt == "regular") + continue; + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_typewriter_opts = xopts; + options.clear(); + } + + if (name == "sourcecodepro") { + h_font_typewriter[0] = "ADOBESourceCodePro"; + for (auto const & opt : allopts) { + if (opt == "osf") { + h_font_typewriter_osf = "true"; + continue; + } + if (!xopts.empty()) + xopts += ", "; + xopts += opt; + } + if (!xopts.empty()) + h_font_typewriter_opts = xopts; + options.clear(); + } + + // // math fonts - if (is_known(name, known_math_fonts)) - h_font_math = name; + // + + // By default, we use the package name as LyX font name, + // so this only needs to be reset if these names differ. + if (is_known(name, known_math_font_packages)) + h_font_math[0] = name; if (name == "newtxmath") { if (opts.empty()) - h_font_math = "newtxmath"; + h_font_math[0] = "newtxmath"; else if (opts == "garamondx") - h_font_math = "garamondx-ntxm"; + h_font_math[0] = "garamondx-ntxm"; else if (opts == "libertine") - h_font_math = "libertine-ntxm"; + h_font_math[0] = "libertine-ntxm"; else if (opts == "minion") - h_font_math = "minion-ntxm"; + h_font_math[0] = "minion-ntxm"; + else if (opts == "cochineal") + h_font_math[0] = "cochineal-ntxm"; } if (name == "iwona") if (opts == "math") - h_font_math = "iwona-math"; + h_font_math[0] = "iwona-math"; if (name == "kurier") if (opts == "math") - h_font_math = "kurier-math"; + h_font_math[0] = "kurier-math"; // after the detection and handling of special cases, we can remove the // fonts, otherwise they would appear in the preamble, see bug #7856 - if (is_known(name, known_roman_fonts) || is_known(name, known_sans_fonts) - || is_known(name, known_typewriter_fonts) || is_known(name, known_math_fonts)) + if (is_known(name, known_roman_font_packages) || is_known(name, known_sans_font_packages) + || is_known(name, known_typewriter_font_packages) || is_known(name, known_math_font_packages)) ; - + //"On". See the enum Package in BufferParams.h if you thought that "2" should have been "42" else if (name == "amsmath" || name == "amssymb" || name == "cancel" || name == "esint" || name == "mhchem" || name == "mathdots" || name == "mathtools" || name == "stackrel" || - name == "stmaryrd" || name == "undertilde") + name == "stmaryrd" || name == "undertilde") { h_use_packages[name] = "2"; + registerAutomaticallyLoadedPackage(name); + } else if (name == "babel") { h_language_package = "default"; @@ -851,11 +1557,14 @@ void Preamble::handle_package(Parser &p, string const & name, // perhaps in future others. // Therefore keep the babel call as it is as the user might have // reasons for it. - h_preamble << "\\usepackage[" << opts << "]{babel}\n"; + string const babelcall = "\\usepackage[" + opts + "]{babel}\n"; + if (!contains(h_preamble.str(), babelcall)) + h_preamble << babelcall; } delete_opt(options, known_languages); } else { - h_preamble << "\\usepackage{babel}\n"; + if (!contains(h_preamble.str(), "\\usepackage{babel}\n")) + h_preamble << "\\usepackage{babel}\n"; explicit_babel = true; } } @@ -866,15 +1575,15 @@ void Preamble::handle_package(Parser &p, string const & name, h_use_non_tex_fonts = true; xetex = true; registerAutomaticallyLoadedPackage("xunicode"); - if (h_inputencoding == "auto") + if (h_inputencoding == "auto-legacy") p.setEncoding("UTF-8"); } else if (name == "CJK") { - // set the encoding to "auto" because it might be set to "default" by the babel handling + // set the encoding to "auto-legacy" because it might be set to "auto-legacy-plain" by the babel handling // and this would not be correct for CJK - if (h_inputencoding == "default") - h_inputencoding = "auto"; + if (h_inputencoding == "auto-legacy-plain") + h_inputencoding = "auto-legacy"; registerAutomaticallyLoadedPackage("CJK"); } @@ -886,18 +1595,13 @@ void Preamble::handle_package(Parser &p, string const & name, else if (name == "fontenc") { h_fontencoding = getStringFromVector(options, ","); - /* We could do the following for better round trip support, - * but this makes the document less portable, so I skip it: - if (h_fontencoding == lyxrc.fontenc) - h_fontencoding = "global"; - */ options.clear(); } else if (name == "inputenc" || name == "luainputenc") { // h_inputencoding is only set when there is not more than one // inputenc option because otherwise h_inputencoding must be - // set to "auto" (the default encoding of the document language) + // set to "auto-legacy" (the default encodings of the document's languages) // Therefore check that exactly one option is passed to inputenc. // It is also only set when there is not more than one babel // language option. @@ -905,9 +1609,11 @@ void Preamble::handle_package(Parser &p, string const & name, string const encoding = options.back(); Encoding const * const enc = encodings.fromLaTeXName( encoding, Encoding::inputenc, true); - if (!enc) - cerr << "Unknown encoding " << encoding << ". Ignoring." << std::endl; - else { + if (!enc) { + if (!detectEncoding) + cerr << "Unknown encoding " << encoding + << ". Ignoring." << std::endl; + } else { if (!enc->unsafe() && options.size() == 1 && one_language == true) h_inputencoding = enc->name(); p.setEncoding(enc->iconvName()); @@ -941,7 +1647,9 @@ void Preamble::handle_package(Parser &p, string const & name, else if (is_known(name, known_lyx_packages) && options.empty()) { if (name == "splitidx") h_use_indices = "true"; - if (name == "refstyle") + else if (name == "minted") + h_use_minted = true; + else if (name == "refstyle") h_use_refstyle = true; else if (name == "prettyref") h_use_refstyle = false; @@ -963,7 +1671,7 @@ void Preamble::handle_package(Parser &p, string const & name, else if (name == "subfig") ; // ignore this FIXME: Use the package separator mechanism instead - else if ((where = is_known(name, known_languages))) + else if (char const * const * where = is_known(name, known_languages)) h_language = known_coded_languages[where - known_languages]; else if (name == "natbib") { @@ -981,17 +1689,70 @@ void Preamble::handle_package(Parser &p, string const & name, options.erase(it); } } + if (!options.empty()) + h_biblio_options = join(options, ","); + } + + else if (name == "biblatex") { + h_biblio_style = "plainnat"; + h_cite_engine = "biblatex"; + h_cite_engine_type = "authoryear"; + string opt; + vector::iterator it = + find(options.begin(), options.end(), "natbib"); + if (it != options.end()) { + options.erase(it); + h_cite_engine = "biblatex-natbib"; + } else { + opt = process_keyval_opt(options, "natbib"); + if (opt == "true") + h_cite_engine = "biblatex-natbib"; + } + opt = process_keyval_opt(options, "style"); + if (!opt.empty()) { + h_biblatex_citestyle = opt; + h_biblatex_bibstyle = opt; + } else { + opt = process_keyval_opt(options, "citestyle"); + if (!opt.empty()) + h_biblatex_citestyle = opt; + opt = process_keyval_opt(options, "bibstyle"); + if (!opt.empty()) + h_biblatex_bibstyle = opt; + } + opt = process_keyval_opt(options, "refsection"); + if (!opt.empty()) { + if (opt == "none" || opt == "part" + || opt == "chapter" || opt == "section" + || opt == "subsection") + h_multibib = opt; + else + cerr << "Ignoring unkown refesection value '" + << opt << "'."; + } + opt = process_keyval_opt(options, "bibencoding"); + if (!opt.empty()) + bibencoding = opt; + if (!options.empty()) { + h_biblio_options = join(options, ","); + options.clear(); + } } else if (name == "jurabib") { h_biblio_style = "jurabib"; h_cite_engine = "jurabib"; h_cite_engine_type = "authoryear"; + if (!options.empty()) + h_biblio_options = join(options, ","); } else if (name == "bibtopic") h_use_bibtopic = "true"; + else if (name == "chapterbib") + h_multibib = "child"; + else if (name == "hyperref") handle_hyperref(options); @@ -1006,6 +1767,24 @@ void Preamble::handle_package(Parser &p, string const & name, h_options += ',' + join(options, ","); } } + else if (name == "microtype") { + //we internally support only microtype without params + if (options.empty()) + h_use_microtype = "true"; + else + h_preamble << "\\usepackage[" << opts << "]{microtype}"; + } + + else if (name == "lineno") { + h_use_lineno = "true"; + if (!options.empty()) { + h_lineno_options = join(options, ","); + options.clear(); + } + } + + else if (name == "changebar") + h_output_changes = "true"; else if (!in_lyx_preamble) { if (options.empty()) @@ -1022,7 +1801,7 @@ void Preamble::handle_package(Parser &p, string const & name, } // We need to do something with the options... - if (!options.empty()) + if (!options.empty() && !detectEncoding) cerr << "Ignoring options '" << join(options, ",") << "' of package " << name << '.' << endl; @@ -1050,33 +1829,6 @@ void Preamble::handle_if(Parser & p, bool in_lyx_preamble) bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiledir) { - // set the quote language - // LyX only knows the following quotes languages: - // english, swedish, german, polish, french and danish - // (quotes for "japanese" and "chinese-traditional" are missing because - // they wouldn't be useful: http://www.lyx.org/trac/ticket/6383) - // conversion list taken from - // http://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage - // (quotes for kazakh and interlingua are unknown) - // danish - if (is_known(h_language, known_danish_quotes_languages)) - h_quotes_language = "danish"; - // french - else if (is_known(h_language, known_french_quotes_languages)) - h_quotes_language = "french"; - // german - else if (is_known(h_language, known_german_quotes_languages)) - h_quotes_language = "german"; - // polish - else if (is_known(h_language, known_polish_quotes_languages)) - h_quotes_language = "polish"; - // swedish - else if (is_known(h_language, known_swedish_quotes_languages)) - h_quotes_language = "swedish"; - //english - else if (is_known(h_language, known_english_quotes_languages)) - h_quotes_language = "english"; - if (contains(h_float_placement, "H")) registerAutomaticallyLoadedPackage("float"); if (h_spacing != "single" && h_spacing != "default") @@ -1096,6 +1848,7 @@ bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiled << "\\lyxformat " << LYX_FORMAT << '\n' << "\\begin_document\n" << "\\begin_header\n" + << "\\save_transient_properties " << h_save_transient_properties << "\n" << "\\origin " << origin << "\n" << "\\textclass " << h_textclass << "\n"; string const raw = subdoc ? empty_string() : h_preamble.str(); @@ -1133,24 +1886,44 @@ bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiled os << *it << '\n'; os << "\\end_modules\n"; } + if (!h_includeonlys.empty()) { + os << "\\begin_includeonly\n"; + for (auto const & iofile : h_includeonlys) + os << iofile << '\n'; + os << "\\end_includeonly\n"; + } os << "\\maintain_unincluded_children " << h_maintain_unincluded_children << "\n" << "\\language " << h_language << "\n" << "\\language_package " << h_language_package << "\n" << "\\inputencoding " << h_inputencoding << "\n" << "\\fontencoding " << h_fontencoding << "\n" - << "\\font_roman " << h_font_roman << "\n" - << "\\font_sans " << h_font_sans << "\n" - << "\\font_typewriter " << h_font_typewriter << "\n" - << "\\font_math " << h_font_math << "\n" + << "\\font_roman \"" << h_font_roman[0] + << "\" \"" << h_font_roman[1] << "\"\n" + << "\\font_sans \"" << h_font_sans[0] << "\" \"" << h_font_sans[1] << "\"\n" + << "\\font_typewriter \"" << h_font_typewriter[0] + << "\" \"" << h_font_typewriter[1] << "\"\n" + << "\\font_math \"" << h_font_math[0] << "\" \"" << h_font_math[1] << "\"\n" << "\\font_default_family " << h_font_default_family << "\n" << "\\use_non_tex_fonts " << (h_use_non_tex_fonts ? "true" : "false") << '\n' << "\\font_sc " << h_font_sc << "\n" - << "\\font_osf " << h_font_osf << "\n" - << "\\font_sf_scale " << h_font_sf_scale << "\n" - << "\\font_tt_scale " << h_font_tt_scale << '\n'; + << "\\font_roman_osf " << h_font_roman_osf << "\n" + << "\\font_sans_osf " << h_font_sans_osf << "\n" + << "\\font_typewriter_osf " << h_font_typewriter_osf << "\n"; + if (!h_font_roman_opts.empty()) + os << "\\font_roman_opts \"" << h_font_roman_opts << "\"" << '\n'; + os << "\\font_sf_scale " << h_font_sf_scale[0] + << ' ' << h_font_sf_scale[1] << '\n'; + if (!h_font_sans_opts.empty()) + os << "\\font_sans_opts \"" << h_font_sans_opts << "\"" << '\n'; + os << "\\font_tt_scale " << h_font_tt_scale[0] + << ' ' << h_font_tt_scale[1] << '\n'; if (!h_font_cjk.empty()) os << "\\font_cjk " << h_font_cjk << '\n'; - os << "\\graphics " << h_graphics << '\n' + if (!h_font_typewriter_opts.empty()) + os << "\\font_typewriter_opts \"" << h_font_typewriter_opts << "\"" << '\n'; + os << "\\use_microtype " << h_use_microtype << '\n' + << "\\use_dash_ligatures " << h_use_dash_ligatures << '\n' + << "\\graphics " << h_graphics << '\n' << "\\default_output_format " << h_default_output_format << "\n" << "\\output_sync " << h_output_sync << "\n"; if (h_output_sync == "1") @@ -1164,13 +1937,13 @@ bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiled << "\\use_hyperref " << h_use_hyperref << '\n'; if (h_use_hyperref == "true") { if (!h_pdf_title.empty()) - os << "\\pdf_title \"" << h_pdf_title << "\"\n"; + os << "\\pdf_title " << Lexer::quoteString(h_pdf_title) << '\n'; if (!h_pdf_author.empty()) - os << "\\pdf_author \"" << h_pdf_author << "\"\n"; + os << "\\pdf_author " << Lexer::quoteString(h_pdf_author) << '\n'; if (!h_pdf_subject.empty()) - os << "\\pdf_subject \"" << h_pdf_subject << "\"\n"; + os << "\\pdf_subject " << Lexer::quoteString(h_pdf_subject) << '\n'; if (!h_pdf_keywords.empty()) - os << "\\pdf_keywords \"" << h_pdf_keywords << "\"\n"; + os << "\\pdf_keywords " << Lexer::quoteString(h_pdf_keywords) << '\n'; os << "\\pdf_bookmarks " << h_pdf_bookmarks << "\n" "\\pdf_bookmarksnumbered " << h_pdf_bookmarksnumbered << "\n" "\\pdf_bookmarksopen " << h_pdf_bookmarksopen << "\n" @@ -1183,7 +1956,7 @@ bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiled if (!h_pdf_pagemode.empty()) os << "\\pdf_pagemode " << h_pdf_pagemode << '\n'; if (!h_pdf_quoted_options.empty()) - os << "\\pdf_quoted_options \"" << h_pdf_quoted_options << "\"\n"; + os << "\\pdf_quoted_options " << Lexer::quoteString(h_pdf_quoted_options) << '\n'; } os << "\\papersize " << h_papersize << "\n" << "\\use_geometry " << h_use_geometry << '\n'; @@ -1193,12 +1966,24 @@ bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiled os << "\\cite_engine " << h_cite_engine << '\n' << "\\cite_engine_type " << h_cite_engine_type << '\n' << "\\biblio_style " << h_biblio_style << "\n" - << "\\use_bibtopic " << h_use_bibtopic << "\n" - << "\\use_indices " << h_use_indices << "\n" + << "\\use_bibtopic " << h_use_bibtopic << "\n"; + if (!h_biblio_options.empty()) + os << "\\biblio_options " << h_biblio_options << "\n"; + if (!h_biblatex_bibstyle.empty()) + os << "\\biblatex_bibstyle " << h_biblatex_bibstyle << "\n"; + if (!h_biblatex_citestyle.empty()) + os << "\\biblatex_citestyle " << h_biblatex_citestyle << "\n"; + if (!h_multibib.empty()) + os << "\\multibib " << h_multibib << "\n"; + os << "\\use_indices " << h_use_indices << "\n" << "\\paperorientation " << h_paperorientation << '\n' << "\\suppress_date " << h_suppress_date << '\n' << "\\justification " << h_justification << '\n' - << "\\use_refstyle " << h_use_refstyle << '\n'; + << "\\use_refstyle " << h_use_refstyle << '\n' + << "\\use_minted " << h_use_minted << '\n' + << "\\use_lineno " << h_use_lineno << '\n'; + if (!h_lineno_options.empty()) + os << "\\lineno_options " << h_lineno_options << '\n'; if (!h_fontcolor.empty()) os << "\\fontcolor " << h_fontcolor << '\n'; if (!h_notefontcolor.empty()) @@ -1228,7 +2013,12 @@ bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiled os << "\\defskip " << h_defskip << "\n"; else os << "\\paragraph_indentation " << h_paragraph_indentation << "\n"; - os << "\\quotes_language " << h_quotes_language << "\n" + os << "\\is_math_indent " << h_is_mathindent << "\n"; + if (!h_mathindentation.empty()) + os << "\\math_indentation " << h_mathindentation << "\n"; + os << "\\math_numbering_side " << h_math_numbering_side << "\n"; + os << "\\quotes_style " << h_quotes_style << "\n" + << "\\dynamic_quotes " << h_dynamic_quotes << "\n" << "\\papercolumns " << h_papercolumns << "\n" << "\\papersides " << h_papersides << "\n" << "\\paperpagestyle " << h_paperpagestyle << "\n"; @@ -1236,6 +2026,7 @@ bool Preamble::writeLyXHeader(ostream & os, bool subdoc, string const & outfiled os << "\\listings_params " << h_listings_params << "\n"; os << "\\tracking_changes " << h_tracking_changes << "\n" << "\\output_changes " << h_output_changes << "\n" + << "\\change_bars " << h_change_bars << "\n" << "\\html_math_output " << h_html_math_output << "\n" << "\\html_css_as_file " << h_html_css_as_file << "\n" << "\\html_be_strict " << h_html_be_strict << "\n" @@ -1251,6 +2042,13 @@ void Preamble::parse(Parser & p, string const & forceclass, { // initialize fixed types special_columns_['D'] = 3; + parse(p, forceclass, false, tc); +} + + +void Preamble::parse(Parser & p, string const & forceclass, + bool detectEncoding, TeX2LyXDocClass & tc) +{ bool is_full_document = false; bool is_lyx_file = false; bool in_lyx_preamble = false; @@ -1266,11 +2064,19 @@ void Preamble::parse(Parser & p, string const & forceclass, } p.reset(); + if (detectEncoding && !is_full_document) + return; + while (is_full_document && p.good()) { + if (detectEncoding && h_inputencoding != "auto-legacy" && + h_inputencoding != "auto-legacy-plain") + return; + Token const & t = p.get_token(); #ifdef FILEDEBUG - cerr << "t: " << t << "\n"; + if (!detectEncoding) + cerr << "t: " << t << '\n'; #endif // @@ -1286,14 +2092,18 @@ void Preamble::parse(Parser & p, string const & forceclass, t.cat() == catBegin || t.cat() == catEnd || t.cat() == catAlign || - t.cat() == catParameter)) + t.cat() == catParameter)) { h_preamble << t.cs(); + continue; + } - else if (!in_lyx_preamble && - (t.cat() == catSpace || t.cat() == catNewline)) + if (!in_lyx_preamble && + (t.cat() == catSpace || t.cat() == catNewline)) { h_preamble << t.asInput(); + continue; + } - else if (t.cat() == catComment) { + if (t.cat() == catComment) { static regex const islyxfile("%% LyX .* created this file"); static regex const usercommands("User specified LaTeX commands"); @@ -1304,8 +2114,9 @@ void Preamble::parse(Parser & p, string const & forceclass, "% This document must be compiled with XeLaTeX "; if (comment.size() > magicXeLaTeX.size() && comment.substr(0, magicXeLaTeX.size()) == magicXeLaTeX - && h_inputencoding == "auto") { - cerr << "XeLaTeX comment found, switching to UTF8\n"; + && h_inputencoding == "auto-legacy") { + if (!detectEncoding) + cerr << "XeLaTeX comment found, switching to UTF8\n"; h_inputencoding = "utf8"; } smatch sub; @@ -1317,12 +2128,22 @@ void Preamble::parse(Parser & p, string const & forceclass, in_lyx_preamble = false; else if (!in_lyx_preamble) h_preamble << t.asInput(); + continue; + } + + if (t.cs() == "PassOptionsToPackage") { + string const poptions = p.getArg('{', '}'); + string const package = p.verbatim_item(); + extra_package_options_.insert(make_pair(package, poptions)); + continue; } - else if (t.cs() == "pagestyle") + if (t.cs() == "pagestyle") { h_paperpagestyle = p.verbatim_item(); + continue; + } - else if (t.cs() == "setdefaultlanguage") { + if (t.cs() == "setdefaultlanguage") { xetex = true; // We don't yet care about non-language variant options // because LyX doesn't support this yet, see bug #8214 @@ -1345,65 +2166,165 @@ void Preamble::parse(Parser & p, string const & forceclass, h_language = p.verbatim_item(); //finally translate the poyglossia name to a LyX name h_language = polyglossia2lyx(h_language); + continue; } - else if (t.cs() == "setotherlanguage") { + if (t.cs() == "setotherlanguage") { // We don't yet care about the option because LyX doesn't // support this yet, see bug #8214 p.hasOpt() ? p.getOpt() : string(); p.verbatim_item(); + continue; } - else if (t.cs() == "setmainfont") { - // we don't care about the option - p.hasOpt() ? p.getOpt() : string(); - h_font_roman = p.getArg('{', '}'); + if (t.cs() == "setmainfont") { + string fontopts = p.hasOpt() ? p.getArg('[', ']') : string(); + h_font_roman[1] = p.getArg('{', '}'); + if (!fontopts.empty()) { + vector opts = getVectorFromString(fontopts); + fontopts.clear(); + for (auto const & opt : opts) { + if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX") + // ignore + continue; + if (!fontopts.empty()) + fontopts += ", "; + fontopts += opt; + } + h_font_roman_opts = fontopts; + } + continue; } - else if (t.cs() == "setsansfont" || t.cs() == "setmonofont") { + if (t.cs() == "setsansfont" || t.cs() == "setmonofont") { // LyX currently only supports the scale option - string scale; + string scale, fontopts; if (p.hasOpt()) { - string fontopts = p.getArg('[', ']'); - // check if the option contains a scaling, if yes, extract it - string::size_type pos = fontopts.find("Scale"); - if (pos != string::npos) { - string::size_type i = fontopts.find(',', pos); - if (i == string::npos) - scale_as_percentage(fontopts.substr(pos + 1), scale); - else - scale_as_percentage(fontopts.substr(pos, i - pos), scale); + fontopts = p.getArg('[', ']'); + if (!fontopts.empty()) { + vector opts = getVectorFromString(fontopts); + fontopts.clear(); + for (auto const & opt : opts) { + if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX") + // ignore + continue; + if (prefixIs(opt, "Scale=")) { + scale_as_percentage(opt, scale); + continue; + } + if (!fontopts.empty()) + fontopts += ", "; + fontopts += opt; + } } } if (t.cs() == "setsansfont") { if (!scale.empty()) - h_font_sf_scale = scale; - h_font_sans = p.getArg('{', '}'); + h_font_sf_scale[1] = scale; + h_font_sans[1] = p.getArg('{', '}'); + if (!fontopts.empty()) + h_font_sans_opts = fontopts; } else { if (!scale.empty()) - h_font_tt_scale = scale; - h_font_typewriter = p.getArg('{', '}'); + h_font_tt_scale[1] = scale; + h_font_typewriter[1] = p.getArg('{', '}'); + if (!fontopts.empty()) + h_font_typewriter_opts = fontopts; } + continue; } - else if (t.cs() == "date") { + if (t.cs() == "babelfont") { + xetex = true; + h_use_non_tex_fonts = true; + h_language_package = "babel"; + if (h_inputencoding == "auto-legacy") + p.setEncoding("UTF-8"); + // we don't care about the lang option + string const lang = p.hasOpt() ? p.getArg('[', ']') : string(); + string const family = p.getArg('{', '}'); + string fontopts = p.hasOpt() ? p.getArg('[', ']') : string(); + string const fontname = p.getArg('{', '}'); + if (lang.empty() && family == "rm") { + h_font_roman[1] = fontname; + if (!fontopts.empty()) { + vector opts = getVectorFromString(fontopts); + fontopts.clear(); + for (auto const & opt : opts) { + if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX") + // ignore + continue; + if (!fontopts.empty()) + fontopts += ", "; + fontopts += opt; + } + h_font_roman_opts = fontopts; + } + continue; + } else if (lang.empty() && (family == "sf" || family == "tt")) { + string scale; + if (!fontopts.empty()) { + vector opts = getVectorFromString(fontopts); + fontopts.clear(); + for (auto const & opt : opts) { + if (opt == "Mapping=tex-text" || opt == "Ligatures=TeX") + // ignore + continue; + if (prefixIs(opt, "Scale=")) { + scale_as_percentage(opt, scale); + continue; + } + if (!fontopts.empty()) + fontopts += ", "; + fontopts += opt; + } + } + if (family == "sf") { + if (!scale.empty()) + h_font_sf_scale[1] = scale; + h_font_sans[1] = fontname; + if (!fontopts.empty()) + h_font_sans_opts = fontopts; + } else { + if (!scale.empty()) + h_font_tt_scale[1] = scale; + h_font_typewriter[1] = fontname; + if (!fontopts.empty()) + h_font_typewriter_opts = fontopts; + } + continue; + } else { + // not rm, sf or tt or lang specific + h_preamble << '\\' << t.cs(); + if (!lang.empty()) + h_preamble << '[' << lang << ']'; + h_preamble << '{' << family << '}'; + if (!fontopts.empty()) + h_preamble << '[' << fontopts << ']'; + h_preamble << '{' << fontname << '}' << '\n'; + continue; + } + } + + if (t.cs() == "date") { string argument = p.getArg('{', '}'); if (argument.empty()) h_suppress_date = "true"; else h_preamble << t.asInput() << '{' << argument << '}'; + continue; } - else if (t.cs() == "color") { + if (t.cs() == "color") { string const space = (p.hasOpt() ? p.getOpt() : string()); string argument = p.getArg('{', '}'); // check the case that a standard color is used if (space.empty() && is_known(argument, known_basic_colors)) { h_fontcolor = rgbcolor2code(argument); - preamble.registerAutomaticallyLoadedPackage("color"); + registerAutomaticallyLoadedPackage("color"); } else if (space.empty() && argument == "document_fontcolor") - preamble.registerAutomaticallyLoadedPackage("color"); + registerAutomaticallyLoadedPackage("color"); // check the case that LyX's document_fontcolor is defined // but not used for \color else { @@ -1415,15 +2336,16 @@ void Preamble::parse(Parser & p, string const & forceclass, // is parsed before this h_fontcolor = ""; } + continue; } - else if (t.cs() == "pagecolor") { + if (t.cs() == "pagecolor") { string argument = p.getArg('{', '}'); // check the case that a standard color is used if (is_known(argument, known_basic_colors)) { h_backgroundcolor = rgbcolor2code(argument); } else if (argument == "page_backgroundcolor") - preamble.registerAutomaticallyLoadedPackage("color"); + registerAutomaticallyLoadedPackage("color"); // check the case that LyX's page_backgroundcolor is defined // but not used for \pagecolor else { @@ -1432,24 +2354,28 @@ void Preamble::parse(Parser & p, string const & forceclass, // is parsed before this h_backgroundcolor = ""; } + continue; } - else if (t.cs() == "makeatletter") { + if (t.cs() == "makeatletter") { // LyX takes care of this p.setCatcode('@', catLetter); + continue; } - else if (t.cs() == "makeatother") { + if (t.cs() == "makeatother") { // LyX takes care of this p.setCatcode('@', catOther); + continue; } - else if (t.cs() == "makeindex") { + if (t.cs() == "makeindex") { // LyX will re-add this if a print index command is found p.skip_spaces(); + continue; } - else if (t.cs() == "newindex") { + if (t.cs() == "newindex") { string const indexname = p.getArg('[', ']'); string const shortcut = p.verbatim_item(); if (!indexname.empty()) @@ -1459,9 +2385,42 @@ void Preamble::parse(Parser & p, string const & forceclass, h_shortcut[index_number] = shortcut; index_number += 1; p.skip_spaces(); + continue; + } + + if (t.cs() == "addbibresource") { + string const options = p.getArg('[', ']'); + string const arg = removeExtension(p.getArg('{', '}')); + if (!options.empty()) { + // check if the option contains a bibencoding, if yes, extract it + string::size_type pos = options.find("bibencoding="); + string encoding; + if (pos != string::npos) { + string::size_type i = options.find(',', pos); + if (i == string::npos) + encoding = options.substr(pos + 1); + else + encoding = options.substr(pos, i - pos); + pos = encoding.find('='); + if (pos == string::npos) + encoding.clear(); + else + encoding = encoding.substr(pos + 1); + } + if (!encoding.empty()) + biblatex_encodings.push_back(normalize_filename(arg) + ' ' + encoding); + } + biblatex_bibliographies.push_back(arg); + continue; + } + + if (t.cs() == "bibliography") { + vector bibs = getVectorFromString(p.getArg('{', '}')); + biblatex_bibliographies.insert(biblatex_bibliographies.end(), bibs.begin(), bibs.end()); + continue; } - else if (t.cs() == "RS@ifundefined") { + if (t.cs() == "RS@ifundefined") { string const name = p.verbatim_item(); string const body1 = p.verbatim_item(); string const body2 = p.verbatim_item(); @@ -1477,9 +2436,10 @@ void Preamble::parse(Parser & p, string const & forceclass, << '{' << body2 << '}'; h_preamble << ss.str(); } + continue; } - else if (t.cs() == "AtBeginDocument") { + if (t.cs() == "AtBeginDocument") { string const name = p.verbatim_item(); // only non-lyxspecific stuff if (in_lyx_preamble && @@ -1505,15 +2465,16 @@ void Preamble::parse(Parser & p, string const & forceclass, ss << '{' << name << '}'; h_preamble << ss.str(); } + continue; } - else if (t.cs() == "newcommand" || t.cs() == "newcommandx" - || t.cs() == "renewcommand" || t.cs() == "renewcommandx" - || t.cs() == "providecommand" || t.cs() == "providecommandx" - || t.cs() == "DeclareRobustCommand" - || t.cs() == "DeclareRobustCommandx" - || t.cs() == "ProvideTextCommandDefault" - || t.cs() == "DeclareMathAccent") { + if (t.cs() == "newcommand" || t.cs() == "newcommandx" + || t.cs() == "renewcommand" || t.cs() == "renewcommandx" + || t.cs() == "providecommand" || t.cs() == "providecommandx" + || t.cs() == "DeclareRobustCommand" + || t.cs() == "DeclareRobustCommandx" + || t.cs() == "ProvideTextCommandDefault" + || t.cs() == "DeclareMathAccent") { bool star = false; if (p.next_token().character() == '*') { p.get_token(); @@ -1527,20 +2488,20 @@ void Preamble::parse(Parser & p, string const & forceclass, bool const was_in_lyx_preamble = in_lyx_preamble; // font settings if (name == "\\rmdefault") - if (is_known(body, known_roman_fonts)) { - h_font_roman = body; + if (is_known(body, known_roman_font_packages)) { + h_font_roman[0] = body; p.skip_spaces(); in_lyx_preamble = true; } if (name == "\\sfdefault") - if (is_known(body, known_sans_fonts)) { - h_font_sans = body; + if (is_known(body, known_sans_font_packages)) { + h_font_sans[0] = body; p.skip_spaces(); in_lyx_preamble = true; } if (name == "\\ttdefault") - if (is_known(body, known_typewriter_fonts)) { - h_font_typewriter = body; + if (is_known(body, known_typewriter_font_packages)) { + h_font_typewriter[0] = body; p.skip_spaces(); in_lyx_preamble = true; } @@ -1552,9 +2513,12 @@ void Preamble::parse(Parser & p, string const & forceclass, in_lyx_preamble = true; } - // remove the lyxdot definition that is re-added by LyX + // remove LyX-specific definitions that are re-added by LyX // if necessary - if (name == "\\lyxdot") { + // \lyxline is an ancient command that is converted by tex2lyx into + // a \rule therefore remove its preamble code + if (name == "\\lyxdot" || name == "\\lyxarrow" + || name == "\\lyxline" || name == "\\LyX") { p.skip_spaces(); in_lyx_preamble = true; } @@ -1570,7 +2534,8 @@ void Preamble::parse(Parser & p, string const & forceclass, ss << '*'; ss << '{' << name << '}' << opt1 << opt2 << '{' << body << "}"; - h_preamble << ss.str(); + if (prefixIs(t.cs(), "renew") || !contains(h_preamble.str(), ss.str())) + h_preamble << ss.str(); /* ostream & out = in_preamble ? h_preamble : os; out << "\\" << t.cs() << "{" << name << "}" @@ -1579,23 +2544,71 @@ void Preamble::parse(Parser & p, string const & forceclass, } // restore the in_lyx_preamble setting in_lyx_preamble = was_in_lyx_preamble; + continue; } - else if (t.cs() == "documentclass") { + if (t.cs() == "documentclass") { vector::iterator it; vector opts = split_options(p.getArg('[', ']')); + // FIXME This does not work for classes that have a + // different name in LyX than in LaTeX + h_textclass = p.getArg('{', '}'); + p.skip_spaces(); + // Force textclass if the user wanted it + if (!forceclass.empty()) + h_textclass = forceclass; + tc.setName(h_textclass); + if (!LayoutFileList::get().haveClass(h_textclass) || !tc.load()) { + cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl; + exit(EXIT_FAILURE); + } + + // Font sizes. + // Try those who are (most likely) known to all packages first handle_opt(opts, known_fontsizes, h_paperfontsize); delete_opt(opts, known_fontsizes); // delete "pt" at the end string::size_type i = h_paperfontsize.find("pt"); if (i != string::npos) h_paperfontsize.erase(i); + // Now those known specifically to the class + string fsize; + vector class_fsizes = getVectorFromString(tc.opt_fontsize(), "|"); + string const fsize_format = tc.fontsizeformat(); + for (auto const fsize : class_fsizes) { + string latexsize = subst(fsize_format, "$$s", fsize); + vector::iterator it = find(opts.begin(), opts.end(), latexsize); + if (it != opts.end()) { + h_paperfontsize = fsize; + opts.erase(it); + break; + } + } + // The documentclass options are always parsed before the options // of the babel call so that a language cannot overwrite the babel // options. handle_opt(opts, known_languages, h_language); delete_opt(opts, known_languages); + // math indentation + if ((it = find(opts.begin(), opts.end(), "fleqn")) + != opts.end()) { + h_is_mathindent = "1"; + opts.erase(it); + } + // formula numbering side + if ((it = find(opts.begin(), opts.end(), "leqno")) + != opts.end()) { + h_math_numbering_side = "left"; + opts.erase(it); + } + else if ((it = find(opts.begin(), opts.end(), "reqno")) + != opts.end()) { + h_math_numbering_side = "right"; + opts.erase(it); + } + // paper orientation if ((it = find(opts.begin(), opts.end(), "landscape")) != opts.end()) { h_paperorientation = "landscape"; @@ -1624,19 +2637,37 @@ void Preamble::parse(Parser & p, string const & forceclass, opts.erase(it); } // paper sizes - // some size options are known to any document classes, other sizes + // some size options are known by the document class, other sizes // are handled by the \geometry command of the geometry package - handle_opt(opts, known_class_paper_sizes, h_papersize); - delete_opt(opts, known_class_paper_sizes); + string paper; + vector class_psizes = getVectorFromString(tc.opt_pagesize(), "|"); + string const psize_format = tc.pagesizeformat(); + for (auto const psize : class_psizes) { + string latexsize = subst(psize_format, "$$s", psize); + vector::iterator it = find(opts.begin(), opts.end(), latexsize); + if (it != opts.end()) { + h_papersize = psize; + opts.erase(it); + break; + } + if (psize_format == "$$spaper") + continue; + // Also try with the default format since this is understood by + // most classes + latexsize = psize + "paper"; + it = find(opts.begin(), opts.end(), latexsize); + if (it != opts.end()) { + h_papersize = psize; + opts.erase(it); + break; + } + } // the remaining options h_options = join(opts, ","); - // FIXME This does not work for classes that have a - // different name in LyX than in LaTeX - h_textclass = p.getArg('{', '}'); - p.skip_spaces(); + continue; } - else if (t.cs() == "usepackage") { + if (t.cs() == "usepackage") { string const options = p.getArg('[', ']'); string const name = p.getArg('{', '}'); vector vecnames; @@ -1645,23 +2676,27 @@ void Preamble::parse(Parser & p, string const & forceclass, vector::const_iterator end = vecnames.end(); for (; it != end; ++it) handle_package(p, trimSpaceAndEol(*it), options, - in_lyx_preamble); + in_lyx_preamble, detectEncoding); + continue; } - else if (t.cs() == "inputencoding") { + if (t.cs() == "inputencoding") { string const encoding = p.getArg('{','}'); Encoding const * const enc = encodings.fromLaTeXName( encoding, Encoding::inputenc, true); - if (!enc) - cerr << "Unknown encoding " << encoding << ". Ignoring." << std::endl; - else { + if (!enc) { + if (!detectEncoding) + cerr << "Unknown encoding " << encoding + << ". Ignoring." << std::endl; + } else { if (!enc->unsafe()) h_inputencoding = enc->name(); p.setEncoding(enc->iconvName()); } + continue; } - else if (t.cs() == "newenvironment") { + if (t.cs() == "newenvironment") { string const name = p.getArg('{', '}'); string const opt1 = p.getFullOpt(); string const opt2 = p.getFullOpt(); @@ -1674,26 +2709,33 @@ void Preamble::parse(Parser & p, string const & forceclass, } add_known_environment(name, opt1, !opt2.empty(), from_utf8(beg), from_utf8(end)); - + continue; } - else if (t.cs() == "newtheorem") { + if (t.cs() == "newtheorem") { + bool star = false; + if (p.next_token().character() == '*') { + p.get_token(); + star = true; + } string const name = p.getArg('{', '}'); string const opt1 = p.getFullOpt(); string const opt2 = p.getFullOpt(); string const body = p.verbatim_item(); string const opt3 = p.getFullOpt(); + string const cmd = star ? "\\newtheorem*" : "\\newtheorem"; + + string const complete = cmd + "{" + name + '}' + + opt1 + opt2 + '{' + body + '}' + opt3; - add_known_theorem(name, opt1, !opt2.empty(), - from_utf8("\\newtheorem{" + name + '}' + - opt1 + opt2 + '{' + body + '}' + opt3)); + add_known_theorem(name, opt1, !opt2.empty(), from_utf8(complete)); if (!in_lyx_preamble) - h_preamble << "\\newtheorem{" << name << '}' - << opt1 << opt2 << '{' << '}' << opt3; + h_preamble << complete; + continue; } - else if (t.cs() == "def") { + if (t.cs() == "def") { string name = p.get_token().cs(); // In fact, name may be more than the name: // In the test case of bug 8116 @@ -1704,9 +2746,10 @@ void Preamble::parse(Parser & p, string const & forceclass, if (!in_lyx_preamble) h_preamble << "\\def\\" << name << '{' << p.verbatim_item() << "}"; + continue; } - else if (t.cs() == "newcolumntype") { + if (t.cs() == "newcolumntype") { string const name = p.getArg('{', '}'); trimSpaceAndEol(name); int nargs = 0; @@ -1720,9 +2763,10 @@ void Preamble::parse(Parser & p, string const & forceclass, if (nargs) h_preamble << "[" << nargs << "]"; h_preamble << "{" << p.verbatim_item() << "}"; + continue; } - else if (t.cs() == "setcounter") { + if (t.cs() == "setcounter") { string const name = p.getArg('{', '}'); string const content = p.getArg('{', '}'); if (name == "secnumdepth") @@ -1731,9 +2775,10 @@ void Preamble::parse(Parser & p, string const & forceclass, h_tocdepth = content; else h_preamble << "\\setcounter{" << name << "}{" << content << "}"; + continue; } - else if (t.cs() == "setlength") { + if (t.cs() == "setlength") { string const name = p.verbatim_item(); string const content = p.verbatim_item(); // the paragraphs are only not indented when \parindent is set to zero @@ -1751,20 +2796,29 @@ void Preamble::parse(Parser & p, string const & forceclass, h_defskip = "bigskip"; else h_defskip = translate_len(content); + } else if (name == "\\mathindent") { + h_mathindentation = translate_len(content); } else h_preamble << "\\setlength{" << name << "}{" << content << "}"; + continue; } - else if (t.cs() == "onehalfspacing") + if (t.cs() == "onehalfspacing") { h_spacing = "onehalf"; + continue; + } - else if (t.cs() == "doublespacing") + if (t.cs() == "doublespacing") { h_spacing = "double"; + continue; + } - else if (t.cs() == "setstretch") + if (t.cs() == "setstretch") { h_spacing = "other " + p.verbatim_item(); + continue; + } - else if (t.cs() == "synctex") { + if (t.cs() == "synctex") { // the scheme is \synctex=value // where value can only be "1" or "-1" h_output_sync = "1"; @@ -1775,21 +2829,24 @@ void Preamble::parse(Parser & p, string const & forceclass, if (value == "-") value += p.get_token().asInput(); h_output_sync_macro = "\\synctex=" + value; + continue; } - else if (t.cs() == "begin") { + if (t.cs() == "begin") { string const name = p.getArg('{', '}'); if (name == "document") break; h_preamble << "\\begin{" << name << "}"; + continue; } - else if (t.cs() == "geometry") { + if (t.cs() == "geometry") { vector opts = split_options(p.getArg('{', '}')); handle_geometry(opts); + continue; } - else if (t.cs() == "definecolor") { + if (t.cs() == "definecolor") { string const color = p.getArg('{', '}'); string const space = p.getArg('{', '}'); string const value = p.getArg('{', '}'); @@ -1810,12 +2867,15 @@ void Preamble::parse(Parser & p, string const & forceclass, << "}{" << space << "}{" << value << '}'; } + continue; } - else if (t.cs() == "bibliographystyle") + if (t.cs() == "bibliographystyle") { h_biblio_style = p.verbatim_item(); + continue; + } - else if (t.cs() == "jurabibsetup") { + if (t.cs() == "jurabibsetup") { // FIXME p.getArg('{', '}') is most probably wrong (it // does not handle nested braces). // Use p.verbatim_item() instead. @@ -1827,9 +2887,10 @@ void Preamble::parse(Parser & p, string const & forceclass, h_preamble << "\\jurabibsetup{" << join(jurabibsetup, ",") << '}'; } + continue; } - else if (t.cs() == "hypersetup") { + if (t.cs() == "hypersetup") { vector hypersetup = split_options(p.verbatim_item()); // add hypersetup to the hyperref package options @@ -1838,9 +2899,38 @@ void Preamble::parse(Parser & p, string const & forceclass, h_preamble << "\\hypersetup{" << join(hypersetup, ",") << '}'; } + continue; } - else if (is_known(t.cs(), known_if_3arg_commands)) { + if (t.cs() == "includeonly") { + vector includeonlys = getVectorFromString(p.getArg('{', '}')); + for (auto & iofile : includeonlys) { + string filename(normalize_filename(iofile)); + string const path = getMasterFilePath(true); + // We want to preserve relative/absolute filenames, + // therefore path is only used for testing + if (!makeAbsPath(filename, path).exists()) { + // The file extension is probably missing. + // Now try to find it out. + string const tex_name = + find_file(filename, path, + known_tex_extensions); + if (!tex_name.empty()) + filename = tex_name; + } + string outname; + if (makeAbsPath(filename, path).exists()) + fix_child_filename(filename); + else + cerr << "Warning: Could not find included file '" + << filename << "'." << endl; + outname = changeExtension(filename, "lyx"); + h_includeonlys.push_back(outname); + } + continue; + } + + if (is_known(t.cs(), known_if_3arg_commands)) { // prevent misparsing of \usepackage if it is used // as an argument (see e.g. our own output of // \@ifundefined above) @@ -1877,31 +2967,27 @@ void Preamble::parse(Parser & p, string const & forceclass, << '{' << arg2 << '}' << '{' << arg3 << '}'; } + continue; } - else if (is_known(t.cs(), known_if_commands)) { + if (is_known(t.cs(), known_if_commands)) { // must not parse anything in conditional code, since // LyX would output the parsed contents unconditionally if (!in_lyx_preamble) h_preamble << t.asInput(); handle_if(p, in_lyx_preamble); + continue; } - else if (!t.cs().empty() && !in_lyx_preamble) + if (!t.cs().empty() && !in_lyx_preamble) { h_preamble << '\\' << t.cs(); + continue; + } } // remove the whitespace p.skip_spaces(); - // Force textclass if the user wanted it - if (!forceclass.empty()) - h_textclass = forceclass; - tc.setName(h_textclass); - if (!tc.load()) { - cerr << "Error: Could not read layout file for textclass \"" << h_textclass << "\"." << endl; - exit(EXIT_FAILURE); - } if (h_papersides.empty()) { ostringstream ss; ss << tc.sides(); @@ -1934,6 +3020,57 @@ void Preamble::parse(Parser & p, string const & forceclass, h_options += ',' + lyx2babel(default_language); } } + + // Finally, set the quote style. + // LyX knows the following quotes styles: + // british, cjk, cjkangle, danish, english, french, german, + // polish, russian, swedish and swiss + // conversion list taken from + // https://en.wikipedia.org/wiki/Quotation_mark,_non-English_usage + // (quotes for kazakh are unknown) + // british + if (is_known(h_language, known_british_quotes_languages)) + h_quotes_style = "british"; + // cjk + else if (is_known(h_language, known_cjk_quotes_languages)) + h_quotes_style = "cjk"; + // cjkangle + else if (is_known(h_language, known_cjkangle_quotes_languages)) + h_quotes_style = "cjkangle"; + // danish + else if (is_known(h_language, known_danish_quotes_languages)) + h_quotes_style = "danish"; + // french + else if (is_known(h_language, known_french_quotes_languages)) + h_quotes_style = "french"; + // german + else if (is_known(h_language, known_german_quotes_languages)) + h_quotes_style = "german"; + // polish + else if (is_known(h_language, known_polish_quotes_languages)) + h_quotes_style = "polish"; + // russian + else if (is_known(h_language, known_russian_quotes_languages)) + h_quotes_style = "russian"; + // swedish + else if (is_known(h_language, known_swedish_quotes_languages)) + h_quotes_style = "swedish"; + // swiss + else if (is_known(h_language, known_swiss_quotes_languages)) + h_quotes_style = "swiss"; + // english + else if (is_known(h_language, known_english_quotes_languages)) + h_quotes_style = "english"; +} + + +string Preamble::parseEncoding(Parser & p, string const & forceclass) +{ + TeX2LyXDocClass dummy; + parse(p, forceclass, true, dummy); + if (h_inputencoding != "auto-legacy" && h_inputencoding != "auto-legacy-plain") + return h_inputencoding; + return ""; }