]> git.lyx.org Git - lyx.git/blobdiff - src/BufferParams.cpp
Amend 9de9ec1e88fab
[lyx.git] / src / BufferParams.cpp
index ec4cc0d60aed42f5ca4c94d4ea8d8bc3f1c2042c..5ea6e15677e4f81ecfae54fac3e55259e88e1b50 100644 (file)
@@ -74,7 +74,7 @@ static char const * const string_paragraph_separation[] = {
 static char const * const string_quotes_style[] = {
        "english", "swedish", "german", "polish", "swiss", "danish", "plain",
        "british", "swedishg", "french", "frenchin", "russian", "cjk", "cjkangle",
-       "hungarian", ""
+       "hungarian", "hebrew", ""
 };
 
 
@@ -158,6 +158,7 @@ QuotesStyleTranslator const init_quotesstyletranslator()
        translator.addPair(string_quotes_style[12], QuoteStyle::CJK);
        translator.addPair(string_quotes_style[13], QuoteStyle::CJKAngle);
        translator.addPair(string_quotes_style[14], QuoteStyle::Hungarian);
+       translator.addPair(string_quotes_style[15], QuoteStyle::Hebrew);
        return translator;
 }
 
@@ -341,7 +342,7 @@ public:
 
        AuthorList authorlist;
        BranchList branchlist;
-       IgnoreList spellignore;
+       WordLangTable spellignore;
        Bullet temp_bullets[4];
        Bullet user_defined_bullets[4];
        IndicesList indiceslist;
@@ -370,6 +371,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()));
 }
 
 
@@ -484,6 +488,7 @@ BufferParams::BufferParams()
        shell_escape = false;
        output_sync = false;
        use_refstyle = true;
+       use_formatted_ref = false;
        use_minted = false;
        use_lineno = false;
 
@@ -602,16 +607,13 @@ IndicesList const & BufferParams::indiceslist() const
 }
 
 
-typedef std::vector<WordLangTuple> IgnoreList;
-
-
-IgnoreList & BufferParams::spellignore()
+WordLangTable & BufferParams::spellignore()
 {
        return pimpl_->spellignore;
 }
 
 
-IgnoreList const & BufferParams::spellignore() const
+WordLangTable const & BufferParams::spellignore() const
 {
        return pimpl_->spellignore;
 }
@@ -810,6 +812,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") {
@@ -1202,6 +1206,8 @@ string BufferParams::readToken(Lexer & lex, string const & token,
                lex >> output_sync_macro;
        } else if (token == "\\use_refstyle") {
                lex >> use_refstyle;
+       } else if (token == "\\use_formatted_ref") {
+               lex >> use_formatted_ref;
        } else if (token == "\\use_minted") {
                lex >> use_minted;
        } else if (token == "\\use_lineno") {
@@ -1258,6 +1264,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
@@ -1421,6 +1436,7 @@ void BufferParams::writeFile(ostream & os, Buffer const * buf) const
           << "\n\\suppress_date " << convert<string>(suppress_date)
           << "\n\\justification " << convert<string>(justification)
           << "\n\\use_refstyle " << use_refstyle
+          << "\n\\use_formatted_ref " << use_formatted_ref
           << "\n\\use_minted " << use_minted
           << "\n\\use_lineno " << use_lineno
           << '\n';
@@ -1577,7 +1593,8 @@ void BufferParams::validate(LaTeXFeatures & features) const
 {
        features.require(documentClass().required());
 
-       if (columns > 1 && language->rightToLeft())
+       if (columns > 1 && language->rightToLeft()
+           && !features.runparams().isFullUnicode())
                features.require("rtloutputdblcol");
 
        if (output_changes) {
@@ -1603,7 +1620,6 @@ void BufferParams::validate(LaTeXFeatures & features) const
                                features.require("ulem");
                                features.require("xcolor");
                                // improves color handling in PDF output
-                               features.require("pdfcolmk");
                        } else {
                                features.require("ct-none");
                        }
@@ -1691,6 +1707,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
@@ -1850,7 +1916,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";
@@ -1859,8 +1926,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"))
@@ -1890,7 +1967,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<string> fontencs = font_encodings();
                // get font encodings of secondary languages
@@ -1920,6 +1997,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")).
@@ -2074,6 +2153,7 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
        if (paragraph_separation) {
                // when skip separation
                string psopt;
+               bool default_skip = false;
                switch (getDefSkip().kind()) {
                case VSpace::SMALLSKIP:
                        psopt = "\\smallskipamount";
@@ -2086,6 +2166,7 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
                        break;
                case VSpace::HALFLINE:
                        // default (no option)
+                       default_skip = true;
                        break;
                case VSpace::FULLLINE:
                        psopt = "\\baselineskip";
@@ -2096,12 +2177,20 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
                default:
                        break;
                }
-               if (!features.isProvided("parskip")) {
+               if (features.isProvided("parskip")) {
+                       // package already loaded (with arbitrary options)
+                       // change parskip value only
+                       if (!psopt.empty())
+                               os << "\\setlength{\\parskip}{" + psopt + "}\n";
+                       else if (default_skip)
+                               // explicitly reset default (might have been changed
+                               // in a class or package)
+                               os << "\\parskip=.5\\baselineskip plus 2pt\\relax\n";
+               } else {
+                       // load parskip package with required option
                        if (!psopt.empty())
                                psopt = "[skip=" + psopt + "]";
                        os << "\\usepackage" + psopt + "{parskip}\n";
-               } else {
-                       os << "\\setlength{\\parskip}{" + psopt + "}\n";
                }
        } else {
                // when separation by indentation
@@ -2132,7 +2221,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";
        }
 
@@ -2216,7 +2305,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
@@ -2873,6 +2962,33 @@ bool BufferParams::isLiterate() const
 }
 
 
+bool BufferParams::hasPackageOption(string const package, string const opt) const
+{
+       for (auto const & p : documentClass().packageOptions())
+               if (package == p.first && opt == p.second)
+                       return true;
+       return false;
+}
+
+
+bool BufferParams::useBidiPackage(OutputParams const & rp) const
+{
+       return (rp.use_polyglossia
+               // as of babel 3.29, bidi=bidi-* is supported by babel
+               // So we check whether we use a respective version and
+               // whethert bidi-r or bidi-l have been requested either via class
+               // or package options
+               || (rp.use_babel
+                   && LaTeXFeatures::isAvailableAtLeastFrom("babel", 2019, 4, 3)
+                   && (hasPackageOption("babel", "bidi-r")
+                       || hasPackageOption("babel", "bidi-l")
+                       || contains(options, "bidi-r")
+                       || contains(options, "bidi-l")))
+               )
+               && rp.flavor == Flavor::XeTeX;
+}
+
+
 void BufferParams::readPreamble(Lexer & lex)
 {
        if (lex.getString() != "\\begin_preamble")
@@ -2883,6 +2999,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" :
@@ -3238,12 +3364,13 @@ string const BufferParams::dvips_options() const
 
 string const BufferParams::main_font_encoding() const
 {
-       if (font_encodings().empty()) {
+       vector<string> 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();
 }
 
 
@@ -3258,9 +3385,10 @@ vector<string> 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<string> fencs = getVectorFromString(language->fontenc(*this));
+                       vector<string> fencs = getVectorFromString(lfe);
                        for (auto & fe : fencs) {
                                if (find(fontencs.begin(), fontencs.end(), fe) == fontencs.end())
                                        fontencs.push_back(fe);
@@ -3312,56 +3440,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<string> 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<string>::const_iterator it = encoding_set.begin();
-                       set<string>::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<string> 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
@@ -3369,20 +3492,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";
 }
 
 
@@ -3430,7 +3559,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";
@@ -3439,11 +3568,11 @@ string const BufferParams::loadFonts(LaTeXFeatures & features) const
                                os << "\\babelfont{rm}[";
                        else
                                os << "\\setmainfont[";
-                       if (!font_roman_opts.empty())
-                               os << font_roman_opts << ',';
                        os << texmapping;
                        if (fonts_roman_osf)
                                os << ",Numbers=OldStyle";
+                       if (!font_roman_opts.empty())
+                               os << ',' << font_roman_opts;
                        os << "]{" << parseFontName(fontsRoman()) << "}\n";
                }
                if (fontsSans() != "default") {
@@ -3457,10 +3586,10 @@ string const BufferParams::loadFonts(LaTeXFeatures & features) const
                                   << float(fontsSansScale()) / 100 << ',';
                                if (fonts_sans_osf)
                                        os << "Numbers=OldStyle,";
+                               os << texmapping;
                                if (!font_sans_opts.empty())
-                                       os << font_sans_opts << ',';
-                               os << texmapping << "]{"
-                                  << sans << "}\n";
+                                       os << ',' << font_sans_opts;
+                               os << "]{" << sans << "}\n";
                        } else {
                                if (babelfonts)
                                        os << "\\babelfont{sf}[";
@@ -3468,10 +3597,10 @@ string const BufferParams::loadFonts(LaTeXFeatures & features) const
                                        os << "\\setsansfont[";
                                if (fonts_sans_osf)
                                        os << "Numbers=OldStyle,";
+                               os << texmapping;
                                if (!font_sans_opts.empty())
-                                       os << font_sans_opts << ',';
-                               os << texmapping << "]{"
-                                  << sans << "}\n";
+                                       os << ',' << font_sans_opts;
+                               os << "]{" << sans << "}\n";
                        }
                }
                if (fontsTypewriter() != "default") {
@@ -3512,7 +3641,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");
@@ -3615,11 +3745,71 @@ vector<CitationStyle> 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)
@@ -3639,7 +3829,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()) {
@@ -3673,6 +3863,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);
@@ -3693,5 +3884,11 @@ string const BufferParams::bibFileEncoding(string const & file) const
 }
 
 
+BufferParams const & defaultBufferParams()
+{
+       static BufferParams default_params;
+       return default_params;
+}
+
 
 } // namespace lyx