X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBufferParams.cpp;h=a6ccd4ed546ff25ce2a632ce5731bac52dce7543;hb=4ed0312c51704780af1c452d3a82a84171b3725a;hp=4927741e50b44dbb0cb4d7a73e3f874a50e606e9;hpb=07396ab2445720b21dc8195816eb1aee1f2ae3a7;p=lyx.git diff --git a/src/BufferParams.cpp b/src/BufferParams.cpp index 4927741e50..a6ccd4ed54 100644 --- a/src/BufferParams.cpp +++ b/src/BufferParams.cpp @@ -341,7 +341,7 @@ public: AuthorList authorlist; BranchList branchlist; - IgnoreList spellignore; + WordLangTable spellignore; Bullet temp_bullets[4]; Bullet user_defined_bullets[4]; IndicesList indiceslist; @@ -370,6 +370,9 @@ BufferParams::Impl::Impl() authorlist.record(Author(from_utf8(lyxrc.user_name), from_utf8(lyxrc.user_email), from_utf8(lyxrc.user_initials))); + // set comparison author + authorlist.record(Author(from_utf8("Document Comparison"), + docstring(), docstring())); } @@ -478,6 +481,7 @@ BufferParams::BufferParams() html_math_img_scale = 1.0; html_css_as_file = false; docbook_table_output = HTMLTable; + docbook_mathml_prefix = MPrefix; display_pixel_ratio = 1.0; shell_escape = false; @@ -601,18 +605,32 @@ IndicesList const & BufferParams::indiceslist() const } -typedef std::vector IgnoreList; +WordLangTable & BufferParams::spellignore() +{ + return pimpl_->spellignore; +} -IgnoreList & BufferParams::spellignore() +WordLangTable const & BufferParams::spellignore() const { return pimpl_->spellignore; } -IgnoreList const & BufferParams::spellignore() const +bool BufferParams::spellignored(WordLangTuple const & wl) const { - return pimpl_->spellignore; + bool has_item = false; + vector il = spellignore(); + vector::const_iterator it = il.begin(); + for (; it != il.end(); ++it) { + if (it->lang()->code() != wl.lang()->code()) + continue; + if (it->word() == wl.word()) { + has_item = true; + break; + } + } + return has_item; } @@ -792,6 +810,8 @@ string BufferParams::readToken(Lexer & lex, string const & token, origin.replace(0, sysdirprefix.length() - 1, package().system_support().absFileName()); } + } else if (token == "\\begin_metadata") { + readDocumentMetadata(lex); } else if (token == "\\begin_preamble") { readPreamble(lex); } else if (token == "\\begin_local_layout") { @@ -1051,9 +1071,9 @@ string BufferParams::readToken(Lexer & lex, string const & token, } else if (token == "\\spellchecker_ignore") { lex.eatLine(); docstring wl = lex.getDocString(); - docstring langcode; - docstring word = split(wl, langcode, ' '); - Language const * lang = languages.getFromCode(to_ascii(langcode)); + docstring language; + docstring word = split(wl, language, ' '); + Language const * lang = languages.getLanguage(to_ascii(language)); if (lang) spellignore().push_back(WordLangTuple(word, lang)); } else if (token == "\\author") { @@ -1174,6 +1194,10 @@ string BufferParams::readToken(Lexer & lex, string const & token, int temp; lex >> temp; docbook_table_output = static_cast(temp); + } else if (token == "\\docbook_mathml_prefix") { + int temp; + lex >> temp; + docbook_mathml_prefix = static_cast(temp); } else if (token == "\\output_sync") { lex >> output_sync; } else if (token == "\\output_sync_macro") { @@ -1236,6 +1260,15 @@ void BufferParams::writeFile(ostream & os, Buffer const * buf) const baseClass()->name()), "layout")) << '\n'; + // then document metadata + if (!document_metadata.empty()) { + // remove '\n' from the end of document_metadata + docstring const tmpmd = rtrim(document_metadata, "\n"); + os << "\\begin_metadata\n" + << to_utf8(tmpmd) + << "\n\\end_metadata\n"; + } + // then the preamble if (!preamble.empty()) { // remove '\n' from the end of preamble @@ -1433,7 +1466,7 @@ void BufferParams::writeFile(ostream & os, Buffer const * buf) const } for (auto const & si : spellignore()) { - os << "\\spellchecker_ignore " << si.lang()->code() + os << "\\spellchecker_ignore " << si.lang()->lang() << " " << to_utf8(si.word()) << "\n"; } @@ -1538,6 +1571,7 @@ void BufferParams::writeFile(ostream & os, Buffer const * buf) const << "\\html_be_strict " << convert(html_be_strict) << '\n'; os << "\\docbook_table_output " << docbook_table_output << '\n'; + os << "\\docbook_mathml_prefix " << docbook_mathml_prefix << '\n'; if (html_math_img_scale != 1.0) os << "\\html_math_img_scale " << convert(html_math_img_scale) << '\n'; @@ -1668,6 +1702,56 @@ void BufferParams::validate(LaTeXFeatures & features) const bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features, FileName const & filepath) const { + // DocumentMetadata must come before anything else + if (features.isAvailableAtLeastFrom("LaTeX", 2022, 6) + && !containsOnly(document_metadata, " \n\t")) { + // Check if the user preamble contains uncodable glyphs + odocstringstream doc_metadata; + docstring uncodable_glyphs; + Encoding const * const enc = features.runparams().encoding; + if (enc) { + for (char_type c : document_metadata) { + if (!enc->encodable(c)) { + docstring const glyph(1, c); + LYXERR0("Uncodable character '" + << glyph + << "' in document metadata!"); + uncodable_glyphs += glyph; + if (features.runparams().dryrun) { + doc_metadata << "<" << _("LyX Warning: ") + << _("uncodable character") << " '"; + doc_metadata.put(c); + doc_metadata << "'>"; + } + } else + doc_metadata.put(c); + } + } else + doc_metadata << document_metadata; + + // On BUFFER_VIEW|UPDATE, warn user if we found uncodable glyphs + if (!features.runparams().dryrun && !uncodable_glyphs.empty()) { + frontend::Alert::warning( + _("Uncodable character in document metadata"), + support::bformat( + _("The metadata of your document contains glyphs " + "that are unknown in the current document encoding " + "(namely %1$s).\nThese glyphs are omitted " + " from the output, which may result in " + "incomplete output." + "\n\nPlease select an appropriate " + "document encoding\n" + "(such as utf8) or change the " + "metadata accordingly."), + uncodable_glyphs)); + } + if (!doc_metadata.str().empty()) { + os << "\\DocumentMetadata{\n" + << doc_metadata.str() + << "}\n"; + } + } + // http://www.tug.org/texmf-dist/doc/latex/base/fixltx2e.pdf // !! To use the Fix-cm package, load it before \documentclass, and use the command // \RequirePackage to do so, rather than the normal \usepackage @@ -1827,7 +1911,8 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features, // if we use fontspec or newtxmath, we have to load the AMS packages here string const ams = features.loadAMSPackages(); - bool const ot1 = (main_font_encoding() == "default" || main_font_encoding() == "OT1"); + string const main_font_enc = features.runparams().main_fontenc; + bool const ot1 = (main_font_enc == "default" || main_font_enc == "OT1"); bool const use_newtxmath = theLaTeXFonts().getLaTeXFont(from_ascii(fontsMath())).getUsedPackage( ot1, false, false) == "newtxmath"; @@ -1836,8 +1921,18 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features, if (useNonTeXFonts) { // Babel (as of 2017/11/03) loads fontspec itself + // However, it does so only if a non-default font is requested via \babelfont + // Thus load fontspec if this is not the case and we need fontspec features + bool const babel_needfontspec = + !features.isAvailableAtLeastFrom("babel", 2017, 11, 3) + || (fontsRoman() == "default" + && fontsSans() == "default" + && fontsTypewriter() == "default" + // these need fontspec features + && (features.isRequired("textquotesinglep") + || features.isRequired("textquotedblp"))); if (!features.isProvided("fontspec") - && !(features.useBabel() && features.isAvailable("babel-2017/11/03"))) + && (!features.useBabel() || babel_needfontspec)) os << "\\usepackage{fontspec}\n"; if (features.mustProvide("unicode-math") && features.isAvailable("unicode-math")) @@ -1867,7 +1962,7 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features, // set font encoding // non-TeX fonts use font encoding TU (set by fontspec) if (!useNonTeXFonts && !features.isProvided("fontenc") - && main_font_encoding() != "default") { + && main_font_enc != "default") { // get main font encodings vector fontencs = font_encodings(); // get font encodings of secondary languages @@ -1897,6 +1992,8 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features, if (!features.runparams().includeall && !included_children_.empty()) { os << "\\includeonly{"; bool first = true; + // we do not use "auto const &" here, because incfile is modified later + // coverity[auto_causes_copy] for (auto incfile : included_children_) { FileName inc = makeAbsPath(incfile, filepath.absFileName()); string mangled = DocFileName(changeExtension(inc.absFileName(), ".tex")). @@ -2109,7 +2206,7 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features, os << from_utf8(output_sync_macro) +"\n"; else if (features.runparams().flavor == Flavor::LaTeX) os << "\\usepackage[active]{srcltx}\n"; - else if (features.runparams().flavor == Flavor::PdfLaTeX) + else os << "\\synctex=-1\n"; } @@ -2193,7 +2290,7 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features, if (!tmppreamble.str.empty()) atlyxpreamble << "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% " "LyX specific LaTeX commands.\n" - << move(tmppreamble) + << std::move(tmppreamble) << '\n'; } // the text class specific preamble @@ -2860,6 +2957,16 @@ void BufferParams::readPreamble(Lexer & lex) } +void BufferParams::readDocumentMetadata(Lexer & lex) +{ + if (lex.getString() != "\\begin_metadata") + lyxerr << "Error (BufferParams::readDocumentMetadata):" + "consistency check failed." << endl; + + document_metadata = lex.getLongString(from_ascii("\\end_metadata")); +} + + void BufferParams::readLocalLayout(Lexer & lex, bool forced) { string const expected = forced ? "\\begin_forced_local_layout" : @@ -3215,12 +3322,13 @@ string const BufferParams::dvips_options() const string const BufferParams::main_font_encoding() const { - if (font_encodings().empty()) { + vector const fencs = font_encodings(); + if (fencs.empty()) { if (ascii_lowercase(language->fontenc(*this)) == "none") return "none"; return "default"; } - return font_encodings().back(); + return fencs.back(); } @@ -3235,9 +3343,10 @@ vector const BufferParams::font_encodings() const if (!doc_fontenc.empty()) // If we have a custom setting, we use only that! return getVectorFromString(doc_fontenc); - if (!language->fontenc(*this).empty() + string const lfe = language->fontenc(*this); + if (!lfe.empty() && ascii_lowercase(language->fontenc(*this)) != "none") { - vector fencs = getVectorFromString(language->fontenc(*this)); + vector fencs = getVectorFromString(lfe); for (auto & fe : fencs) { if (find(fontencs.begin(), fontencs.end(), fe) == fontencs.end()) fontencs.push_back(fe); @@ -3289,56 +3398,51 @@ void BufferParams::writeEncodingPreamble(otexstream & os, if (useNonTeXFonts) return; + string const doc_encoding = encoding().latexName(); + Encoding::Package const package = encoding().package(); + // (dvi)lualatex uses luainputenc rather than inputenc + string const inputenc_package = + (features.runparams().flavor == Flavor::LuaTeX + || features.runparams().flavor == Flavor::DviLuaTeX) + ? "luainputenc" : "inputenc"; + if (inputenc == "auto-legacy") { - string const doc_encoding = - language->encoding()->latexName(); - Encoding::Package const package = - language->encoding()->package(); - - // Create list of inputenc options: - set encoding_set; - // luainputenc fails with more than one encoding - if (features.runparams().flavor != Flavor::LuaTeX - && features.runparams().flavor != Flavor::DviLuaTeX) - // list all input encodings used in the document - encoding_set = features.getEncodingSet(doc_encoding); - - // The "japanese" babel-language requires the pLaTeX engine + // The "japanese" babel language requires the pLaTeX engine // which conflicts with "inputenc". // See http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129680.html - if ((!encoding_set.empty() || package == Encoding::inputenc) - && !features.isRequired("japanese") + if (!features.isRequired("japanese") && !features.isProvided("inputenc")) { - os << "\\usepackage["; - set::const_iterator it = encoding_set.begin(); - set::const_iterator const end = encoding_set.end(); - if (it != end) { - os << from_ascii(*it); - ++it; - } - for (; it != end; ++it) - os << ',' << from_ascii(*it); if (package == Encoding::inputenc) { - if (!encoding_set.empty()) - os << ','; - os << from_ascii(doc_encoding); + // Main language requires (lua)inputenc + os << "\\usepackage[" << doc_encoding << "]{" + << inputenc_package << "}\n"; + } else { + // We might have an additional language that requires inputenc + set encoding_set = features.getEncodingSet(doc_encoding); + bool inputenc = false; + for (auto const & enc : encoding_set) { + if (encodings.fromLaTeXName(enc) + && encodings.fromLaTeXName(enc)->package() == Encoding::inputenc) { + inputenc = true; + break; + } + } + if (inputenc) + // load (lua)inputenc without options + // (the encoding is loaded later) + os << "\\usepackage{" << inputenc_package << "}\n"; } - if (features.runparams().flavor == Flavor::LuaTeX - || features.runparams().flavor == Flavor::DviLuaTeX) - os << "]{luainputenc}\n"; - else - os << "]{inputenc}\n"; } } else if (inputenc != "auto-legacy-plain") { - switch (encoding().package()) { + switch (package) { case Encoding::none: case Encoding::CJK: case Encoding::japanese: if (encoding().iconvName() != "UTF-8" - && !features.runparams().isFullUnicode()) - // don't default to [utf8]{inputenc} with TeXLive >= 18 - os << "\\ifdefined\\UseRawInputEncoding\n" - << " \\UseRawInputEncoding\\fi\n"; + && !features.runparams().isFullUnicode() + && features.isAvailableAtLeastFrom("LaTeX", 2018, 4)) + // don't default to [utf8]{inputenc} with LaTeX >= 2018/04 + os << "\\UseRawInputEncoding\n"; break; case Encoding::inputenc: // do not load inputenc if japanese is used @@ -3346,20 +3450,26 @@ void BufferParams::writeEncodingPreamble(otexstream & os, if (features.isRequired("japanese") || features.isProvided("inputenc")) break; - os << "\\usepackage[" << from_ascii(encoding().latexName()); - if (features.runparams().flavor == Flavor::LuaTeX - || features.runparams().flavor == Flavor::DviLuaTeX) - os << "]{luainputenc}\n"; - else - os << "]{inputenc}\n"; + // The 2022 release of ucs.sty uses the default utf8 + // inputenc encoding with 'utf8x' inputenc if the ucs + // package is not loaded before inputenc. + // This breaks existing documents that use utf8x + // and also makes utf8x redundant. + // Thus we load ucs.sty in order to keep functionality + // that would otherwise be silently dropped. + if (doc_encoding == "utf8x" + && features.isAvailableAtLeastFrom("ucs", 2022, 8, 7) + && !features.isProvided("ucs")) + os << "\\usepackage{ucs}\n"; + os << "\\usepackage[" << doc_encoding << "]{" + << inputenc_package << "}\n"; break; } } - if (inputenc == "auto-legacy-plain" || features.isRequired("japanese")) { - // don't default to [utf8]{inputenc} with TeXLive >= 18 - os << "\\ifdefined\\UseRawInputEncoding\n"; - os << " \\UseRawInputEncoding\\fi\n"; - } + if ((inputenc == "auto-legacy-plain" || features.isRequired("japanese")) + && features.isAvailableAtLeastFrom("LaTeX", 2018, 4)) + // don't default to [utf8]{inputenc} with LaTeX >= 2018/04 + os << "\\UseRawInputEncoding\n"; } @@ -3407,7 +3517,7 @@ string const BufferParams::loadFonts(LaTeXFeatures & features) const // As of 2017/11/03, Babel has its own higher-level // interface on top of fontspec that is to be used. bool const babelfonts = features.useBabel() - && features.isAvailable("babel-2017/11/03"); + && features.isAvailableAtLeastFrom("babel", 2017, 11, 3); string const texmapping = (features.runparams().flavor == Flavor::XeTeX) ? "Mapping=tex-text" : "Ligatures=TeX"; @@ -3489,7 +3599,8 @@ string const BufferParams::loadFonts(LaTeXFeatures & features) const } // Tex Fonts - bool const ot1 = (main_font_encoding() == "default" || main_font_encoding() == "OT1"); + bool const ot1 = (features.runparams().main_fontenc == "default" + || features.runparams().main_fontenc == "OT1"); bool const dryrun = features.runparams().dryrun; bool const complete = (fontsSans() == "default" && fontsTypewriter() == "default"); bool const nomath = (fontsMath() != "auto"); @@ -3592,11 +3703,71 @@ vector BufferParams::citeStyles() const } -string const BufferParams::bibtexCommand() const +string const BufferParams::getBibtexCommand(string const cmd, bool const warn) const +{ + // split from options + string command_in; + split(cmd, command_in, ' '); + + // Look if the requested command is available. If so, use that. + for (auto const & alts : lyxrc.bibtex_alternatives) { + string command_prov; + split(alts, command_prov, ' '); + if (command_in == command_prov) + return cmd; + } + + // If not, find the most suitable fallback for the current cite framework, + // and warn. Note that we omit options in any such case. + string fallback; + if (useBiblatex()) { + // For Biblatex, we prefer biber (also for Japanese) + // and try to fall back to bibtex8 + if (lyxrc.bibtex_alternatives.find("biber") != lyxrc.bibtex_alternatives.end()) + fallback = "biber"; + else if (lyxrc.bibtex_alternatives.find("bibtex8") != lyxrc.bibtex_alternatives.end()) + fallback = "bibtex8"; + } + // For classic BibTeX and as last resort for biblatex, try bibtex + if (fallback.empty()) { + if (lyxrc.bibtex_alternatives.find("bibtex") != lyxrc.bibtex_alternatives.end()) + fallback = "bibtex"; + } + + if (!warn) + return fallback; + + if (fallback.empty()) { + frontend::Alert::warning( + _("No bibliography processor found!"), + support::bformat( + _("The bibliography processor requested by this document " + "(%1$s) is not available and no appropriate " + "alternative has been found. " + "No bibliography and references will be generated.\n" + "Please fix your installation!"), + from_utf8(cmd))); + } else { + frontend::Alert::warning( + _("Requested bibliography processor not found!"), + support::bformat( + _("The bibliography processor requested by this document " + "(%1$s) is not available. " + "As a fallback, '%2$s' will be used, options are omitted. " + "This might result in errors or unwanted changes in " + "the bibliography. Please check carefully!\n" + "It is suggested to install the missing processor."), + from_utf8(cmd), from_utf8(fallback))); + } + return fallback; +} + + +string const BufferParams::bibtexCommand(bool const warn) const { // Return document-specific setting if available if (bibtex_command != "default") - return bibtex_command; + return getBibtexCommand(bibtex_command, warn); // If we have "default" in document settings, consult the prefs // 1. Japanese (uses a specific processor) @@ -3616,7 +3787,7 @@ string const BufferParams::bibtexCommand() const // 2. All other languages else if (lyxrc.bibtex_command != "automatic") // Return the specified program, if "automatic" is not set - return lyxrc.bibtex_command; + return getBibtexCommand(lyxrc.bibtex_command, warn); // 3. Automatic: find the most suitable for the current cite framework if (useBiblatex()) { @@ -3650,6 +3821,7 @@ void BufferParams::copyForAdvFR(const BufferParams & bp) { string const & lang = bp.language->lang(); setLanguage(lang); + quotes_style = bp.quotes_style; layout_modules_ = bp.layout_modules_; string const & doc_class = bp.documentClass().name(); setBaseClass(doc_class); @@ -3670,5 +3842,11 @@ string const BufferParams::bibFileEncoding(string const & file) const } +BufferParams const & defaultBufferParams() +{ + static BufferParams default_params; + return default_params; +} + } // namespace lyx