]> git.lyx.org Git - lyx.git/blobdiff - src/BufferParams.cpp
Load ucs before utf8x inputenc with recent ucs versions
[lyx.git] / src / BufferParams.cpp
index 4a70ed884db1ae8ebda0713f15226a52b3a3f8ef..d25dd05bac2e55da31f0afe52be4446e7a0e0fb1 100644 (file)
 #include "LayoutFile.h"
 #include "BranchList.h"
 #include "Buffer.h"
-#include "buffer_funcs.h"
 #include "Bullet.h"
 #include "CiteEnginesList.h"
 #include "Color.h"
 #include "ColorSet.h"
 #include "Converter.h"
 #include "Encoding.h"
+#include "Format.h"
 #include "IndicesList.h"
 #include "Language.h"
 #include "LaTeXFeatures.h"
 #include "LaTeXFonts.h"
-#include "Length.h"
-#include "ModuleList.h"
 #include "Font.h"
 #include "Lexer.h"
 #include "LyXRC.h"
 #include "frontends/alert.h"
 
 #include "insets/InsetListingsParams.h"
+#include "insets/InsetQuotes.h"
 
 #include "support/convert.h"
 #include "support/debug.h"
-#include "support/docstream.h"
 #include "support/FileName.h"
 #include "support/filetools.h"
 #include "support/gettext.h"
+#include "support/Length.h"
 #include "support/Messages.h"
 #include "support/mutex.h"
 #include "support/Package.h"
@@ -74,7 +73,8 @@ 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", ""
+       "british", "swedishg", "french", "frenchin", "russian", "cjk", "cjkangle",
+       "hungarian", ""
 };
 
 
@@ -137,26 +137,27 @@ ParSepTranslator const & parseptranslator()
 
 
 // Quotes style
-typedef Translator<string, InsetQuotesParams::QuoteStyle> QuotesStyleTranslator;
+typedef Translator<string, QuoteStyle> QuotesStyleTranslator;
 
 
 QuotesStyleTranslator const init_quotesstyletranslator()
 {
        QuotesStyleTranslator translator
-               (string_quotes_style[0], InsetQuotesParams::EnglishQuotes);
-       translator.addPair(string_quotes_style[1], InsetQuotesParams::SwedishQuotes);
-       translator.addPair(string_quotes_style[2], InsetQuotesParams::GermanQuotes);
-       translator.addPair(string_quotes_style[3], InsetQuotesParams::PolishQuotes);
-       translator.addPair(string_quotes_style[4], InsetQuotesParams::SwissQuotes);
-       translator.addPair(string_quotes_style[5], InsetQuotesParams::DanishQuotes);
-       translator.addPair(string_quotes_style[6], InsetQuotesParams::PlainQuotes);
-       translator.addPair(string_quotes_style[7], InsetQuotesParams::BritishQuotes);
-       translator.addPair(string_quotes_style[8], InsetQuotesParams::SwedishGQuotes);
-       translator.addPair(string_quotes_style[9], InsetQuotesParams::FrenchQuotes);
-       translator.addPair(string_quotes_style[10], InsetQuotesParams::FrenchINQuotes);
-       translator.addPair(string_quotes_style[11], InsetQuotesParams::RussianQuotes);
-       translator.addPair(string_quotes_style[12], InsetQuotesParams::CJKQuotes);
-       translator.addPair(string_quotes_style[13], InsetQuotesParams::CJKAngleQuotes);
+               (string_quotes_style[0], QuoteStyle::English);
+       translator.addPair(string_quotes_style[1], QuoteStyle::Swedish);
+       translator.addPair(string_quotes_style[2], QuoteStyle::German);
+       translator.addPair(string_quotes_style[3], QuoteStyle::Polish);
+       translator.addPair(string_quotes_style[4], QuoteStyle::Swiss);
+       translator.addPair(string_quotes_style[5], QuoteStyle::Danish);
+       translator.addPair(string_quotes_style[6], QuoteStyle::Plain);
+       translator.addPair(string_quotes_style[7], QuoteStyle::British);
+       translator.addPair(string_quotes_style[8], QuoteStyle::SwedishG);
+       translator.addPair(string_quotes_style[9], QuoteStyle::French);
+       translator.addPair(string_quotes_style[10], QuoteStyle::FrenchIN);
+       translator.addPair(string_quotes_style[11], QuoteStyle::Russian);
+       translator.addPair(string_quotes_style[12], QuoteStyle::CJK);
+       translator.addPair(string_quotes_style[13], QuoteStyle::CJKAngle);
+       translator.addPair(string_quotes_style[14], QuoteStyle::Hungarian);
        return translator;
 }
 
@@ -340,6 +341,7 @@ public:
 
        AuthorList authorlist;
        BranchList branchlist;
+       WordLangTable spellignore;
        Bullet temp_bullets[4];
        Bullet user_defined_bullets[4];
        IndicesList indiceslist;
@@ -395,7 +397,7 @@ BufferParams::BufferParams()
        paragraph_separation = ParagraphIndentSeparation;
        is_math_indent = false;
        math_numbering_side = DEFAULT;
-       quotes_style = InsetQuotesParams::EnglishQuotes;
+       quotes_style = QuoteStyle::English;
        dynamic_quotes = false;
        fontsize = "default";
 
@@ -449,6 +451,8 @@ BufferParams::BufferParams()
        listings_params = string();
        pagestyle = "default";
        tablestyle = "default";
+       float_alignment = "class";
+       float_placement = "class";
        suppress_date = false;
        justification = true;
        // no color is the default (white)
@@ -459,7 +463,9 @@ BufferParams::BufferParams()
        isfontcolor = false;
        // light gray is the default font color for greyed-out notes
        notefontcolor = lyx::rgbFromHexName("#cccccc");
+       isnotefontcolor = false;
        boxbgcolor = lyx::rgbFromHexName("#ff0000");
+       isboxbgcolor = false;
        compressed = lyxrc.save_compressed;
        for (int iter = 0; iter < 4; ++iter) {
                user_defined_bullet(iter) = ITEMIZE_DEFAULTS[iter];
@@ -471,6 +477,8 @@ BufferParams::BufferParams()
        html_math_output = MathML;
        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;
@@ -564,7 +572,7 @@ AuthorList const & BufferParams::authors() const
 }
 
 
-void BufferParams::addAuthor(Author a)
+void BufferParams::addAuthor(Author const & a)
 {
        author_map_[a.bufferId()] = pimpl_->authorlist.record(a);
 }
@@ -594,6 +602,35 @@ IndicesList const & BufferParams::indiceslist() const
 }
 
 
+WordLangTable & BufferParams::spellignore()
+{
+       return pimpl_->spellignore;
+}
+
+
+WordLangTable const & BufferParams::spellignore() const
+{
+       return pimpl_->spellignore;
+}
+
+
+bool BufferParams::spellignored(WordLangTuple const & wl) const
+{
+       bool has_item = false;
+       vector<WordLangTuple> il = spellignore();
+       vector<WordLangTuple>::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;
+}
+
+
 Bullet & BufferParams::temp_bullet(lyx::size_type const index)
 {
        LASSERT(index < 4, return pimpl_->temp_bullets[0]);
@@ -698,9 +735,10 @@ BufferParams::MathNumber BufferParams::getMathNumber() const
 
 
 string BufferParams::readToken(Lexer & lex, string const & token,
-       FileName const & filepath)
+       FileName const & filename)
 {
        string result;
+       FileName const & filepath = filename.onlyPath();
 
        if (token == "\\textclass") {
                lex.next();
@@ -769,6 +807,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") {
@@ -986,14 +1026,13 @@ string BufferParams::readToken(Lexer & lex, string const & token,
                        }
                        if (tok == "\\color") {
                                lex.eatLine();
-                               string color = lex.getString();
+                               vector<string> const colors = getVectorFromString(lex.getString(), " ");
+                               string const lmcolor = colors.front();
+                               string dmcolor;
+                               if (colors.size() > 1)
+                                       dmcolor = colors.back();
                                if (branch_ptr)
-                                       branch_ptr->setColor(color);
-                               // Update also the Color table:
-                               if (color == "none")
-                                       color = lcolor.getX11Name(Color_background);
-                               // FIXME UNICODE
-                               lcolor.setColor(to_utf8(branch), color);
+                                       branch_ptr->setColors(lmcolor, dmcolor);
                        }
                }
        } else if (token == "\\index") {
@@ -1020,12 +1059,20 @@ string BufferParams::readToken(Lexer & lex, string const & token,
                                        index_ptr->setColor(color);
                                // Update also the Color table:
                                if (color == "none")
-                                       color = lcolor.getX11Name(Color_background);
+                                       color = lcolor.getX11HexName(Color_background);
                                // FIXME UNICODE
                                if (!shortcut.empty())
-                                       lcolor.setColor(to_utf8(shortcut), color);
+                                       lcolor.setColor(to_utf8(shortcut)+ "@" + filename.absFileName(), color);
                        }
                }
+       } else if (token == "\\spellchecker_ignore") {
+               lex.eatLine();
+               docstring wl = lex.getDocString();
+               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") {
                lex.eatLine();
                istringstream ss(lex.getString());
@@ -1049,11 +1096,17 @@ string BufferParams::readToken(Lexer & lex, string const & token,
                string color = lex.getString();
                notefontcolor = lyx::rgbFromHexName(color);
                lcolor.setColor("notefontcolor", color);
+               lcolor.setLaTeXName("notefontcolor", "note_fontcolor");
+               lcolor.setGUIName("notefontcolor", N_("greyedout inset text"));
+               // set a local name for the painter
+               lcolor.setColor("notefontcolor@" + filename.absFileName(), color);
+               isnotefontcolor = true;
        } else if (token == "\\boxbgcolor") {
                lex.eatLine();
                string color = lex.getString();
                boxbgcolor = lyx::rgbFromHexName(color);
-               lcolor.setColor("boxbgcolor", color);
+               lcolor.setColor("boxbgcolor@" + filename.absFileName(), color);
+               isboxbgcolor = true;
        } else if (token == "\\paperwidth") {
                lex >> paperwidth;
        } else if (token == "\\paperheight") {
@@ -1134,6 +1187,14 @@ string BufferParams::readToken(Lexer & lex, string const & token,
        } else if (token == "\\html_latex_end") {
                lex.eatLine();
                html_latex_end = lex.getString();
+       } else if (token == "\\docbook_table_output") {
+               int temp;
+               lex >> temp;
+               docbook_table_output = static_cast<TableOutput>(temp);
+       } else if (token == "\\docbook_mathml_prefix") {
+               int temp;
+               lex >> temp;
+               docbook_mathml_prefix = static_cast<MathMLNameSpacePrefix>(temp);
        } else if (token == "\\output_sync") {
                lex >> output_sync;
        } else if (token == "\\output_sync_macro") {
@@ -1196,6 +1257,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
@@ -1366,20 +1436,20 @@ void BufferParams::writeFile(ostream & os, Buffer const * buf) const
        if (!lineno_opts.empty())
                os << "\\lineno_options " << lineno_opts << '\n';
 
-       if (isbackgroundcolor == true)
+       if (isbackgroundcolor)
                os << "\\backgroundcolor " << lyx::X11hexname(backgroundcolor) << '\n';
-       if (isfontcolor == true)
+       if (isfontcolor)
                os << "\\fontcolor " << lyx::X11hexname(fontcolor) << '\n';
-       if (notefontcolor != lyx::rgbFromHexName("#cccccc"))
+       if (isnotefontcolor)
                os << "\\notefontcolor " << lyx::X11hexname(notefontcolor) << '\n';
-       if (boxbgcolor != lyx::rgbFromHexName("#ff0000"))
+       if (isboxbgcolor)
                os << "\\boxbgcolor " << lyx::X11hexname(boxbgcolor) << '\n';
 
        for (auto const & br : branchlist()) {
                os << "\\branch " << to_utf8(br.branch())
                   << "\n\\selected " << br.isSelected()
                   << "\n\\filename_suffix " << br.hasFileNameSuffix()
-                  << "\n\\color " << lyx::X11hexname(br.color())
+                  << "\n\\color " << br.lightModeColor() << " " << br.darkModeColor()
                   << "\n\\end_branch"
                   << "\n";
        }
@@ -1392,6 +1462,12 @@ void BufferParams::writeFile(ostream & os, Buffer const * buf) const
                   << "\n";
        }
 
+       for (auto const & si : spellignore()) {
+               os << "\\spellchecker_ignore " << si.lang()->lang()
+                  << " " << to_utf8(si.word())
+                  << "\n";
+       }
+
        if (!paperwidth.empty())
                os << "\\paperwidth "
                   << VSpace(paperwidth).asLyXCommand() << '\n';
@@ -1447,7 +1523,7 @@ void BufferParams::writeFile(ostream & os, Buffer const * buf) const
                os << "default";
        }
        os << "\n\\quotes_style "
-          << string_quotes_style[quotes_style]
+          << string_quotes_style[static_cast<int>(quotes_style)]
           << "\n\\dynamic_quotes " << dynamic_quotes
           << "\n\\papercolumns " << columns
           << "\n\\papersides " << sides
@@ -1491,6 +1567,9 @@ void BufferParams::writeFile(ostream & os, Buffer const * buf) const
           << "\\html_css_as_file " << html_css_as_file << '\n'
           << "\\html_be_strict " << convert<string>(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<string>(html_math_img_scale) << '\n';
        if (!html_latex_start.empty())
@@ -1514,8 +1593,8 @@ void BufferParams::validate(LaTeXFeatures & features) const
                                  LaTeXFeatures::isAvailable("xcolor");
 
                switch (features.runparams().flavor) {
-               case OutputParams::LATEX:
-               case OutputParams::DVILUATEX:
+               case Flavor::LaTeX:
+               case Flavor::DviLuaTeX:
                        if (xcolorulem) {
                                features.require("ct-xcolor-ulem");
                                features.require("ulem");
@@ -1524,9 +1603,9 @@ void BufferParams::validate(LaTeXFeatures & features) const
                                features.require("ct-none");
                        }
                        break;
-               case OutputParams::LUATEX:
-               case OutputParams::PDFLATEX:
-               case OutputParams::XETEX:
+               case Flavor::LuaTeX:
+               case Flavor::PdfLaTeX:
+               case Flavor::XeTeX:
                        if (xcolorulem) {
                                features.require("ct-xcolor-ulem");
                                features.require("ulem");
@@ -1620,6 +1699,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.isAvailable("LaTeX-2022/06/01")
+           && !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 "
+                                   "preamble code 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
@@ -1732,8 +1861,7 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
                docstring options_encodable;
                Encoding const * const enc = features.runparams().encoding;
                if (enc) {
-                       for (size_t n = 0; n < strOptions.size(); ++n) {
-                               char_type c = strOptions[n];
+                       for (char_type c : strOptions) {
                                if (!enc->encodable(c)) {
                                        docstring const glyph(1, c);
                                        LYXERR0("Uncodable character '"
@@ -1780,7 +1908,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";
@@ -1820,7 +1949,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
@@ -1970,7 +2099,7 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
        }
 
        // only output when the background color is not default
-       if (isbackgroundcolor == true) {
+       if (isbackgroundcolor) {
                // only require color here, the background color will be defined
                // in LaTeXFeatures.cpp to avoid interferences with the LaTeX
                // package pdfpages
@@ -1979,7 +2108,7 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
        }
 
        // only output when the font color is not default
-       if (isfontcolor == true) {
+       if (isfontcolor) {
                // only require color here, the font color will be defined
                // in LaTeXFeatures.cpp to avoid interferences with the LaTeX
                // package pdfpages
@@ -2060,9 +2189,9 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
        if (output_sync) {
                if (!output_sync_macro.empty())
                        os << from_utf8(output_sync_macro) +"\n";
-               else if (features.runparams().flavor == OutputParams::LATEX)
+               else if (features.runparams().flavor == Flavor::LaTeX)
                        os << "\\usepackage[active]{srcltx}\n";
-               else if (features.runparams().flavor == OutputParams::PDFLATEX)
+               else
                        os << "\\synctex=-1\n";
        }
 
@@ -2175,8 +2304,7 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
                docstring uncodable_glyphs;
                Encoding const * const enc = features.runparams().encoding;
                if (enc) {
-                       for (size_t n = 0; n < preamble.size(); ++n) {
-                               char_type c = preamble[n];
+                       for (char_type c : preamble) {
                                if (!enc->encodable(c)) {
                                        docstring const glyph(1, c);
                                        LYXERR0("Uncodable character '"
@@ -2232,8 +2360,10 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
        // use \PassOptionsToPackage here because the user could have
        // already loaded subfig in the preamble.
        if (features.mustProvide("subfig"))
-               atlyxpreamble << "\\@ifundefined{showcaptionsetup}{}{%\n"
-                                " \\PassOptionsToPackage{caption=false}{subfig}}\n"
+               atlyxpreamble << "\\ifdefined\\showcaptionsetup\n"
+                                " % Caption package is used. Advise subfig not to load it again.\n"
+                                " \\PassOptionsToPackage{caption=false}{subfig}\n"
+                                "\\fi\n"
                                 "\\usepackage{subfig}\n";
 
        // Itemize bullet settings need to be last in case the user
@@ -2340,7 +2470,7 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
            && !features.isProvided("xunicode")) {
                // The `xunicode` package officially only supports XeTeX,
                //  but also works with LuaTeX. We work around its XeTeX test.
-               if (features.runparams().flavor != OutputParams::XETEX) {
+               if (features.runparams().flavor != Flavor::XeTeX) {
                        os << "% Pretend to xunicode that we are XeTeX\n"
                           << "\\def\\XeTeXpicfile{}\n";
                }
@@ -2557,7 +2687,7 @@ LayoutFileIndex const & BufferParams::baseClassID() const
 }
 
 
-void BufferParams::makeDocumentClass(bool const clone)
+void BufferParams::makeDocumentClass(bool clone, bool internal)
 {
        if (!baseClass())
                return;
@@ -2567,7 +2697,7 @@ void BufferParams::makeDocumentClass(bool const clone)
        for (auto const & mod : layout_modules_)
                mods.push_back(mod);
 
-       doc_class_ = getDocumentClass(*baseClass(), mods, cite_engine_, clone);
+       doc_class_ = getDocumentClass(*baseClass(), mods, cite_engine_, clone, internal);
 
        TextClass::ReturnValues success = TextClass::OK;
        if (!forced_local_layout_.empty())
@@ -2653,10 +2783,12 @@ FormatList const & BufferParams::exportableFormats(bool only_viewable) const
                  excludes.insert("xetex");
        }
 
-       FormatList result;
-       for (auto const & b : backs) {
-               FormatList r =
-                       theConverters().getReachable(b, only_viewable, false, excludes);
+       FormatList result =
+               theConverters().getReachable(backs[0], only_viewable, true, excludes);
+       vector<string>::const_iterator it = backs.begin() + 1;
+       for (; it != backs.end(); ++it) {
+               FormatList r = theConverters().getReachable(*it, only_viewable,
+                                                                                                       false, excludes);
                result.insert(result.end(), r.begin(), r.end());
        }
        sort(result.begin(), result.end(), Format::formatSorter);
@@ -2705,7 +2837,7 @@ vector<string> BufferParams::backends() const
 }
 
 
-OutputParams::FLAVOR BufferParams::getOutputFlavor(string const & format) const
+Flavor BufferParams::getOutputFlavor(string const & format) const
 {
        string const dformat = (format.empty() || format == "default") ?
                getDefaultOutputFormat() : format;
@@ -2715,24 +2847,26 @@ OutputParams::FLAVOR BufferParams::getOutputFlavor(string const & format) const
        if (it != default_flavors_.end())
                return it->second;
 
-       OutputParams::FLAVOR result = OutputParams::LATEX;
+       Flavor result = Flavor::LaTeX;
 
        // FIXME It'd be better not to hardcode this, but to do
        //       something with formats.
        if (dformat == "xhtml")
-               result = OutputParams::HTML;
+               result = Flavor::Html;
+       else if (dformat == "docbook5")
+               result = Flavor::DocBook5;
        else if (dformat == "text")
-               result = OutputParams::TEXT;
+               result = Flavor::Text;
        else if (dformat == "lyx")
-               result = OutputParams::LYX;
+               result = Flavor::LyX;
        else if (dformat == "pdflatex")
-               result = OutputParams::PDFLATEX;
+               result = Flavor::PdfLaTeX;
        else if (dformat == "xetex")
-               result = OutputParams::XETEX;
+               result = Flavor::XeTeX;
        else if (dformat == "luatex")
-               result = OutputParams::LUATEX;
+               result = Flavor::LuaTeX;
        else if (dformat == "dviluatex")
-               result = OutputParams::DVILUATEX;
+               result = Flavor::DviLuaTeX;
        else {
                // Try to determine flavor of default output format
                vector<string> backs = backends();
@@ -2780,7 +2914,7 @@ Font const BufferParams::getFont() const
 }
 
 
-InsetQuotesParams::QuoteStyle BufferParams::getQuoteStyle(string const & qs) const
+QuoteStyle BufferParams::getQuoteStyle(string const & qs) const
 {
        return quotesstyletranslator().find(qs);
 }
@@ -2808,6 +2942,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" :
@@ -3163,12 +3307,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();
 }
 
 
@@ -3183,9 +3328,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);
@@ -3246,8 +3392,8 @@ void BufferParams::writeEncodingPreamble(otexstream & os,
                // Create list of inputenc options:
                set<string> encoding_set;
                // luainputenc fails with more than one encoding
-               if (features.runparams().flavor != OutputParams::LUATEX
-                       && features.runparams().flavor != OutputParams::DVILUATEX)
+               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);
 
@@ -3271,8 +3417,8 @@ void BufferParams::writeEncodingPreamble(otexstream & os,
                                        os << ',';
                                os << from_ascii(doc_encoding);
                        }
-                       if (features.runparams().flavor == OutputParams::LUATEX
-                           || features.runparams().flavor == OutputParams::DVILUATEX)
+                       if (features.runparams().flavor == Flavor::LuaTeX
+                           || features.runparams().flavor == Flavor::DviLuaTeX)
                                os << "]{luainputenc}\n";
                        else
                                os << "]{inputenc}\n";
@@ -3294,9 +3440,21 @@ void BufferParams::writeEncodingPreamble(otexstream & os,
                        if (features.isRequired("japanese")
                            || features.isProvided("inputenc"))
                                break;
-                       os << "\\usepackage[" << from_ascii(encoding().latexName());
-                       if (features.runparams().flavor == OutputParams::LUATEX
-                           || features.runparams().flavor == OutputParams::DVILUATEX)
+                       string const doc_encoding = encoding().latexName();
+                       // 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.isAvailable("ucs-2022/08/07")
+                           && !features.isProvided("ucs"))
+                               os << "\\usepackage{ucs}\n";
+                       os << "\\usepackage[" << from_ascii(doc_encoding);
+                       if (features.runparams().flavor == Flavor::LuaTeX
+                           || features.runparams().flavor == Flavor::DviLuaTeX)
                                os << "]{luainputenc}\n";
                        else
                                os << "]{inputenc}\n";
@@ -3357,7 +3515,7 @@ string const BufferParams::loadFonts(LaTeXFeatures & features) const
                bool const babelfonts = features.useBabel()
                                && features.isAvailable("babel-2017/11/03");
                string const texmapping =
-                       (features.runparams().flavor == OutputParams::XETEX) ?
+                       (features.runparams().flavor == Flavor::XeTeX) ?
                        "Mapping=tex-text" : "Ligatures=TeX";
                if (fontsRoman() != "default") {
                        if (babelfonts)
@@ -3437,10 +3595,11 @@ 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() == "default");
+       bool const nomath = (fontsMath() != "auto");
 
        // ROMAN FONTS
        os << theLaTeXFonts().getLaTeXFont(from_ascii(fontsRoman())).getLaTeXCode(
@@ -3498,7 +3657,7 @@ string const & BufferParams::defaultBiblioStyle() const
 }
 
 
-bool const & BufferParams::fullAuthorList() const
+bool BufferParams::fullAuthorList() const
 {
        return documentClass().fullAuthorList();
 }
@@ -3540,11 +3699,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)
@@ -3564,7 +3783,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()) {
@@ -3598,6 +3817,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);