]> git.lyx.org Git - lyx.git/blobdiff - src/BufferParams.cpp
Implement dynamic quotation marks
[lyx.git] / src / BufferParams.cpp
index 403a2cbbe1fa0d2a46ea3abb3c6f899a58053ab7..c48e1cfcca971fdcbcd4147a39666035f58b9584 100644 (file)
@@ -20,6 +20,7 @@
 #include "Author.h"
 #include "LayoutFile.h"
 #include "BranchList.h"
+#include "Buffer.h"
 #include "buffer_funcs.h"
 #include "Bullet.h"
 #include "Color.h"
@@ -37,6 +38,7 @@
 #include "LyXRC.h"
 #include "OutputParams.h"
 #include "Spacing.h"
+#include "texstream.h"
 #include "TexRow.h"
 #include "VSpace.h"
 #include "PDFOptions.h"
@@ -52,6 +54,8 @@
 #include "support/filetools.h"
 #include "support/gettext.h"
 #include "support/Messages.h"
+#include "support/mutex.h"
+#include "support/Package.h"
 #include "support/Translator.h"
 #include "support/lstrings.h"
 
@@ -67,8 +71,9 @@ static char const * const string_paragraph_separation[] = {
 };
 
 
-static char const * const string_quotes_language[] = {
-       "english", "swedish", "german", "polish", "french", "danish", ""
+static char const * const string_quotes_style[] = {
+       "english", "swedish", "german", "polish", "swiss", "danish", "plain",
+       "british", "swedishg", "french", "frenchin", "russian", ""
 };
 
 
@@ -87,11 +92,6 @@ static char const * const string_orientation[] = {
 };
 
 
-static char const * const string_footnotekinds[] = {
-       "footnote", "margin", "fig", "tab", "alg", "wide-fig", "wide-tab", ""
-};
-
-
 static char const * const tex_graphics[] = {
        "default", "dvialw", "dvilaser", "dvipdf", "dvipdfm", "dvipdfmx",
        "dvips", "dvipsone", "dvitops", "dviwin", "dviwindo", "dvi2ps", "emtex",
@@ -122,31 +122,39 @@ ParSepTranslator const init_parseptranslator()
 
 ParSepTranslator const & parseptranslator()
 {
-       static ParSepTranslator translator = init_parseptranslator();
+       static ParSepTranslator const translator =
+               init_parseptranslator();
        return translator;
 }
 
 
-// Quotes language
-typedef Translator<string, InsetQuotes::QuoteLanguage> QuotesLangTranslator;
+// Quotes style
+typedef Translator<string, InsetQuotesParams::QuoteStyle> QuotesStyleTranslator;
 
 
-QuotesLangTranslator const init_quoteslangtranslator()
+QuotesStyleTranslator const init_quotesstyletranslator()
 {
-       QuotesLangTranslator translator
-               (string_quotes_language[0], InsetQuotes::EnglishQuotes);
-       translator.addPair(string_quotes_language[1], InsetQuotes::SwedishQuotes);
-       translator.addPair(string_quotes_language[2], InsetQuotes::GermanQuotes);
-       translator.addPair(string_quotes_language[3], InsetQuotes::PolishQuotes);
-       translator.addPair(string_quotes_language[4], InsetQuotes::FrenchQuotes);
-       translator.addPair(string_quotes_language[5], InsetQuotes::DanishQuotes);
+       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);
        return translator;
 }
 
 
-QuotesLangTranslator const & quoteslangtranslator()
+QuotesStyleTranslator const & quotesstyletranslator()
 {
-       static QuotesLangTranslator translator = init_quoteslangtranslator();
+       static QuotesStyleTranslator const translator =
+               init_quotesstyletranslator();
        return translator;
 }
 
@@ -196,7 +204,8 @@ static PaperSizeTranslator initPaperSizeTranslator()
 
 PaperSizeTranslator const & papersizetranslator()
 {
-       static PaperSizeTranslator translator = initPaperSizeTranslator();
+       static PaperSizeTranslator const translator =
+               initPaperSizeTranslator();
        return translator;
 }
 
@@ -215,7 +224,8 @@ PaperOrientationTranslator const init_paperorientationtranslator()
 
 PaperOrientationTranslator const & paperorientationtranslator()
 {
-       static PaperOrientationTranslator translator = init_paperorientationtranslator();
+       static PaperOrientationTranslator const translator =
+           init_paperorientationtranslator();
        return translator;
 }
 
@@ -234,7 +244,7 @@ SidesTranslator const init_sidestranslator()
 
 SidesTranslator const & sidestranslator()
 {
-       static SidesTranslator translator = init_sidestranslator();
+       static SidesTranslator const translator = init_sidestranslator();
        return translator;
 }
 
@@ -254,7 +264,8 @@ PackageTranslator const init_packagetranslator()
 
 PackageTranslator const & packagetranslator()
 {
-       static PackageTranslator translator = init_packagetranslator();
+       static PackageTranslator const translator =
+               init_packagetranslator();
        return translator;
 }
 
@@ -267,13 +278,15 @@ CiteEngineTypeTranslator const init_citeenginetypetranslator()
 {
        CiteEngineTypeTranslator translator("authoryear", ENGINE_TYPE_AUTHORYEAR);
        translator.addPair("numerical", ENGINE_TYPE_NUMERICAL);
+       translator.addPair("default", ENGINE_TYPE_DEFAULT);
        return translator;
 }
 
 
 CiteEngineTypeTranslator const & citeenginetypetranslator()
 {
-       static CiteEngineTypeTranslator translator = init_citeenginetypetranslator();
+       static CiteEngineTypeTranslator const translator =
+               init_citeenginetypetranslator();
        return translator;
 }
 
@@ -295,10 +308,39 @@ SpaceTranslator const init_spacetranslator()
 
 SpaceTranslator const & spacetranslator()
 {
-       static SpaceTranslator translator = init_spacetranslator();
+       static SpaceTranslator const translator = init_spacetranslator();
        return translator;
 }
 
+
+bool inSystemDir(FileName const & document_dir, string & system_dir)
+{
+       // A document is assumed to be in a system LyX directory (not
+       // necessarily the system directory of the running instance)
+       // if both "configure.py" and "chkconfig.ltx" are found in
+       // either document_dir/../ or document_dir/../../.
+       // If true, the system directory path is returned in system_dir
+       // with a trailing path separator.
+
+       string const msg = "Checking whether document is in a system dir...";
+
+       string dir = document_dir.absFileName();
+
+       for (int i = 0; i < 2; ++i) {
+               dir = addPath(dir, "..");
+               if (!fileSearch(dir, "configure.py").empty() &&
+                   !fileSearch(dir, "chkconfig.ltx").empty()) {
+                       LYXERR(Debug::FILES, msg << " yes");
+                       system_dir = addPath(FileName(dir).realPath(), "");
+                       return true;
+               }
+       }
+
+       LYXERR(Debug::FILES, msg << " no");
+       system_dir = string();
+       return false;
+}
+
 } // anon namespace
 
 
@@ -320,11 +362,16 @@ public:
        VSpace defskip;
        PDFOptions pdfoptions;
        LayoutFileIndex baseClass_;
+       FormatList exportableFormatList;
+       FormatList viewableFormatList;
+       bool isViewCacheValid;
+       bool isExportCacheValid;
 };
 
 
 BufferParams::Impl::Impl()
-       : defskip(VSpace::MEDSKIP), baseClass_(string(""))
+       : defskip(VSpace::MEDSKIP), baseClass_(string("")),
+         isViewCacheValid(false), isExportCacheValid(false)
 {
        // set initial author
        // FIXME UNICODE
@@ -335,8 +382,7 @@ BufferParams::Impl::Impl()
 BufferParams::Impl *
 BufferParams::MemoryTraits::clone(BufferParams::Impl const * ptr)
 {
-       LASSERT(ptr, /**/);
-
+       LBUFERR(ptr);
        return new BufferParams::Impl(*ptr);
 }
 
@@ -351,38 +397,47 @@ BufferParams::BufferParams()
        : pimpl_(new Impl)
 {
        setBaseClass(defaultBaseclass());
+       cite_engine_.push_back("basic");
+       cite_engine_type_ = ENGINE_TYPE_DEFAULT;
        makeDocumentClass();
        paragraph_separation = ParagraphIndentSeparation;
-       quotes_language = InsetQuotes::EnglishQuotes;
+       quotes_style = InsetQuotesParams::EnglishQuotes;
+       dynamic_quotes = false;
        fontsize = "default";
 
        /*  PaperLayout */
        papersize = PAPER_DEFAULT;
        orientation = ORIENTATION_PORTRAIT;
        use_geometry = false;
-       cite_engine_.push_back("basic");
-       cite_engine_type_ = ENGINE_TYPE_NUMERICAL;
        biblio_style = "plain";
        use_bibtopic = false;
        use_indices = false;
-       trackChanges = false;
-       outputChanges = false;
+       save_transient_properties = true;
+       track_changes = false;
+       output_changes = false;
        use_default_options = true;
        maintain_unincluded_children = false;
        secnumdepth = 3;
        tocdepth = 3;
        language = default_language;
        fontenc = "global";
-       fonts_roman = "default";
-       fonts_sans = "default";
-       fonts_typewriter = "default";
-       fonts_math = "auto";
+       fonts_roman[0] = "default";
+       fonts_roman[1] = "default";
+       fonts_sans[0] = "default";
+       fonts_sans[1] = "default";
+       fonts_typewriter[0] = "default";
+       fonts_typewriter[1] = "default";
+       fonts_math[0] = "auto";
+       fonts_math[1] = "auto";
        fonts_default_family = "default";
        useNonTeXFonts = false;
+       use_microtype = false;
        fonts_expert_sc = false;
        fonts_old_figures = false;
-       fonts_sans_scale = 100;
-       fonts_typewriter_scale = 100;
+       fonts_sans_scale[0] = 100;
+       fonts_sans_scale[1] = 100;
+       fonts_typewriter_scale[0] = 100;
+       fonts_typewriter_scale[1] = 100;
        inputenc = "auto";
        lang_package = "default";
        graphics_driver = "default";
@@ -415,15 +470,19 @@ BufferParams::BufferParams()
        html_math_output = MathML;
        html_math_img_scale = 1.0;
        html_css_as_file = false;
+       display_pixel_ratio = 1.0;
 
        output_sync = false;
        use_refstyle = true;
+
+       // map current author
+       author_map_[pimpl_->authorlist.get(0).bufferId()] = 0;
 }
 
 
 docstring BufferParams::B_(string const & l10n) const
 {
-       LASSERT(language, /**/);
+       LASSERT(language, return from_utf8(l10n));
        return getMessages(language->code()).get(l10n);
 }
 
@@ -443,18 +502,39 @@ void BufferParams::use_package(std::string const & p, BufferParams::Package u)
 }
 
 
-vector<string> const & BufferParams::auto_packages()
+map<string, string> const & BufferParams::auto_packages()
 {
-       static vector<string> packages;
+       static map<string, string> packages;
        if (packages.empty()) {
+               // We could have a race condition here that two threads
+               // discover an empty map at the same time and want to fill
+               // it, but that is no problem, since the same contents is
+               // filled in twice then. Having the locker inside the
+               // packages.empty() condition has the advantage that we
+               // don't need the mutex overhead for simple reading.
+               static Mutex mutex;
+               Mutex::Locker locker(&mutex);
                // adding a package here implies a file format change!
-               packages.push_back("amsmath");
-               packages.push_back("amssymb");
-               packages.push_back("esint");
-               packages.push_back("mathdots");
-               packages.push_back("mathtools");
-               packages.push_back("mhchem");
-               packages.push_back("undertilde");
+               packages["amsmath"] =
+                       N_("The LaTeX package amsmath is only used if AMS formula types or symbols from the AMS math toolbars are inserted into formulas");
+               packages["amssymb"] =
+                       N_("The LaTeX package amssymb is only used if symbols from the AMS math toolbars are inserted into formulas");
+               packages["cancel"] =
+                       N_("The LaTeX package cancel is only used if \\cancel commands are used in formulas");
+               packages["esint"] =
+                       N_("The LaTeX package esint is only used if special integral symbols are inserted into formulas");
+               packages["mathdots"] =
+                       N_("The LaTeX package mathdots is only used if the command \\iddots is inserted into formulas");
+               packages["mathtools"] =
+                       N_("The LaTeX package mathtools is only used if some mathematical relations are inserted into formulas");
+               packages["mhchem"] =
+                       N_("The LaTeX package mhchem is only used if either the command \\ce or \\cf is inserted into formulas");
+               packages["stackrel"] =
+                       N_("The LaTeX package stackrel is only used if the command \\stackrel with subscript is inserted into formulas");
+               packages["stmaryrd"] =
+                       N_("The LaTeX package stmaryrd is only used if symbols from the St Mary's Road symbol font for theoretical computer science are inserted into formulas");
+               packages["undertilde"] =
+                       N_("The LaTeX package undertilde is only used if you use the math frame decoration 'utilde'");
        }
        return packages;
 }
@@ -472,6 +552,12 @@ AuthorList const & BufferParams::authors() const
 }
 
 
+void BufferParams::addAuthor(Author a)
+{
+       author_map_[a.bufferId()] = pimpl_->authorlist.record(a);
+}
+
+
 BranchList & BufferParams::branchlist()
 {
        return pimpl_->branchlist;
@@ -498,28 +584,28 @@ IndicesList const & BufferParams::indiceslist() const
 
 Bullet & BufferParams::temp_bullet(lyx::size_type const index)
 {
-       LASSERT(index < 4, /**/);
+       LASSERT(index < 4, return pimpl_->temp_bullets[0]);
        return pimpl_->temp_bullets[index];
 }
 
 
 Bullet const & BufferParams::temp_bullet(lyx::size_type const index) const
 {
-       LASSERT(index < 4, /**/);
+       LASSERT(index < 4, return pimpl_->temp_bullets[0]);
        return pimpl_->temp_bullets[index];
 }
 
 
 Bullet & BufferParams::user_defined_bullet(lyx::size_type const index)
 {
-       LASSERT(index < 4, /**/);
+       LASSERT(index < 4, return pimpl_->temp_bullets[0]);
        return pimpl_->user_defined_bullets[index];
 }
 
 
 Bullet const & BufferParams::user_defined_bullet(lyx::size_type const index) const
 {
-       LASSERT(index < 4, /**/);
+       LASSERT(index < 4, return pimpl_->temp_bullets[0]);
        return pimpl_->user_defined_bullets[index];
 }
 
@@ -577,6 +663,8 @@ void BufferParams::setDefSkip(VSpace const & vs)
 string BufferParams::readToken(Lexer & lex, string const & token,
        FileName const & filepath)
 {
+       string result;
+
        if (token == "\\textclass") {
                lex.next();
                string const classname = lex.getString();
@@ -585,12 +673,31 @@ string BufferParams::readToken(Lexer & lex, string const & token,
                // be available.
                string tcp;
                LayoutFileList & bcl = LayoutFileList::get();
-               if (tcp.empty() && !filepath.empty())
-                       tcp = bcl.addLocalLayout(classname, filepath.absFileName());
-               if (!tcp.empty())
-                       setBaseClass(tcp);
-               else
-                       setBaseClass(classname);
+               if (!filepath.empty()) {
+                       // If classname is an absolute path, the document is
+                       // using a local layout file which could not be accessed
+                       // by a relative path. In this case the path is correct
+                       // even if the document was moved to a different
+                       // location. However, we will have a problem if the
+                       // document was generated on a different platform.
+                       bool isabsolute = FileName::isAbsolute(classname);
+                       string const classpath = onlyPath(classname);
+                       string const path = isabsolute ? classpath
+                               : FileName(addPath(filepath.absFileName(),
+                                               classpath)).realPath();
+                       string const oldpath = isabsolute ? string()
+                               : FileName(addPath(origin, classpath)).realPath();
+                       tcp = bcl.addLocalLayout(onlyFileName(classname), path, oldpath);
+               }
+               // that returns non-empty if a "local" layout file is found.
+               if (!tcp.empty()) {
+                       result = to_utf8(makeRelPath(from_utf8(onlyPath(tcp)),
+                                               from_utf8(filepath.absFileName())));
+                       if (result.empty())
+                               result = ".";
+                       setBaseClass(onlyFileName(tcp));
+               } else
+                       setBaseClass(onlyFileName(classname));
                // We assume that a tex class exists for local or unknown
                // layouts so this warning, will only be given for system layouts.
                if (!baseClass()->isTeXClassAvailable()) {
@@ -609,12 +716,28 @@ string BufferParams::readToken(Lexer & lex, string const & token,
                                                 "See section 3.1.2.2 (Class Availability) of the\n"
                                                 "User's Guide for more information."), desc, prereqs);
                        frontend::Alert::warning(_("Document class not available"),
-                                      msg);
+                                      msg, true);
+               }
+       } else if (token == "\\save_transient_properties") {
+               lex >> save_transient_properties;
+       } else if (token == "\\origin") {
+               lex.eatLine();
+               origin = lex.getString();
+               string const sysdirprefix = "/systemlyxdir/";
+               if (prefixIs(origin, sysdirprefix)) {
+                       string docsys;
+                       if (inSystemDir(filepath, docsys))
+                               origin.replace(0, sysdirprefix.length() - 1, docsys);
+                       else
+                               origin.replace(0, sysdirprefix.length() - 1,
+                                       package().system_support().absFileName());
                }
        } else if (token == "\\begin_preamble") {
                readPreamble(lex);
        } else if (token == "\\begin_local_layout") {
-               readLocalLayout(lex);
+               readLocalLayout(lex, false);
+       } else if (token == "\\begin_forced_local_layout") {
+               readLocalLayout(lex, true);
        } else if (token == "\\begin_modules") {
                readModules(lex);
        } else if (token == "\\begin_removed_modules") {
@@ -631,6 +754,19 @@ string BufferParams::readToken(Lexer & lex, string const & token,
        } else if (token == "\\master") {
                lex.eatLine();
                master = lex.getString();
+               if (!filepath.empty() && FileName::isAbsolute(origin)) {
+                       bool const isabs = FileName::isAbsolute(master);
+                       FileName const abspath(isabs ? master : origin + master);
+                       bool const moved = filepath != FileName(origin);
+                       if (moved && abspath.exists()) {
+                               docstring const path = isabs
+                                       ? from_utf8(master)
+                                       : from_utf8(abspath.realPath());
+                               docstring const refpath =
+                                       from_utf8(filepath.absFileName());
+                               master = to_utf8(makeRelPath(path, refpath));
+                       }
+               }
        } else if (token == "\\suppress_date") {
                lex >> suppress_date;
        } else if (token == "\\justification") {
@@ -656,17 +792,17 @@ string BufferParams::readToken(Lexer & lex, string const & token,
                lex.eatLine();
                fontenc = lex.getString();
        } else if (token == "\\font_roman") {
-               lex.eatLine();
-               fonts_roman = lex.getString();
+               lex >> fonts_roman[0];
+               lex >> fonts_roman[1];
        } else if (token == "\\font_sans") {
-               lex.eatLine();
-               fonts_sans = lex.getString();
+               lex >> fonts_sans[0];
+               lex >> fonts_sans[1];
        } else if (token == "\\font_typewriter") {
-               lex.eatLine();
-               fonts_typewriter = lex.getString();
+               lex >> fonts_typewriter[0];
+               lex >> fonts_typewriter[1];
        } else if (token == "\\font_math") {
-               lex.eatLine();
-               fonts_math = lex.getString();
+               lex >> fonts_math[0];
+               lex >> fonts_math[1];
        } else if (token == "\\font_default_family") {
                lex >> fonts_default_family;
        } else if (token == "\\use_non_tex_fonts") {
@@ -676,11 +812,15 @@ string BufferParams::readToken(Lexer & lex, string const & token,
        } else if (token == "\\font_osf") {
                lex >> fonts_old_figures;
        } else if (token == "\\font_sf_scale") {
-               lex >> fonts_sans_scale;
+               lex >> fonts_sans_scale[0];
+               lex >> fonts_sans_scale[1];
        } else if (token == "\\font_tt_scale") {
-               lex >> fonts_typewriter_scale;
+               lex >> fonts_typewriter_scale[0];
+               lex >> fonts_typewriter_scale[1];
        } else if (token == "\\font_cjk") {
                lex >> fonts_cjk;
+       } else if (token == "\\use_microtype") {
+               lex >> use_microtype;
        } else if (token == "\\paragraph_separation") {
                string parsep;
                lex >> parsep;
@@ -696,10 +836,12 @@ string BufferParams::readToken(Lexer & lex, string const & token,
                if (pimpl_->defskip.kind() == VSpace::DEFSKIP)
                        // that is invalid
                        pimpl_->defskip = VSpace(VSpace::MEDSKIP);
-       } else if (token == "\\quotes_language") {
-               string quotes_lang;
-               lex >> quotes_lang;
-               quotes_language = quoteslangtranslator().find(quotes_lang);
+       } else if (token == "\\quotes_style") {
+               string qstyle;
+               lex >> qstyle;
+               quotes_style = quotesstyletranslator().find(qstyle);
+       } else if (token == "\\dynamic_quotes") {
+               lex >> dynamic_quotes;
        } else if (token == "\\papersize") {
                string ppsize;
                lex >> ppsize;
@@ -728,9 +870,9 @@ string BufferParams::readToken(Lexer & lex, string const & token,
        } else if (token == "\\use_indices") {
                lex >> use_indices;
        } else if (token == "\\tracking_changes") {
-               lex >> trackChanges;
+               lex >> track_changes;
        } else if (token == "\\output_changes") {
-               lex >> outputChanges;
+               lex >> output_changes;
        } else if (token == "\\branch") {
                lex.eatLine();
                docstring branch = lex.getDocString();
@@ -798,7 +940,7 @@ string BufferParams::readToken(Lexer & lex, string const & token,
                istringstream ss(lex.getString());
                Author a;
                ss >> a;
-               author_map[a.bufferId()] = pimpl_->authorlist.record(a);
+               addAuthor(a);
        } else if (token == "\\paperorientation") {
                string orient;
                lex >> orient;
@@ -815,10 +957,12 @@ string BufferParams::readToken(Lexer & lex, string const & token,
                lex.eatLine();
                string color = lex.getString();
                notefontcolor = lyx::rgbFromHexName(color);
+               lcolor.setColor("notefontcolor", color);
        } else if (token == "\\boxbgcolor") {
                lex.eatLine();
                string color = lex.getString();
                boxbgcolor = lyx::rgbFromHexName(color);
+               lcolor.setColor("boxbgcolor", color);
        } else if (token == "\\paperwidth") {
                lex >> paperwidth;
        } else if (token == "\\paperheight") {
@@ -907,24 +1051,55 @@ string BufferParams::readToken(Lexer & lex, string const & token,
                return token;
        }
 
-       return string();
+       return result;
+}
+
+
+namespace {
+       // Quote argument if it contains spaces
+       string quoteIfNeeded(string const & str) {
+               if (contains(str, ' '))
+                       return "\"" + str + "\"";
+               return str;
+       }
 }
 
 
-void BufferParams::writeFile(ostream & os) const
+void BufferParams::writeFile(ostream & os, Buffer const * buf) const
 {
        // The top of the file is written by the buffer.
        // Prints out the buffer info into the .lyx file given by file
 
+       os << "\\save_transient_properties "
+          << convert<string>(save_transient_properties) << '\n';
+
+       // the document directory (must end with a path separator)
+       // realPath() is used to resolve symlinks, while addPath(..., "")
+       // ensures a trailing path separator.
+       string docsys;
+       string filepath = addPath(buf->fileName().onlyPath().realPath(), "");
+       string const sysdir = inSystemDir(FileName(filepath), docsys) ? docsys
+                       : addPath(package().system_support().realPath(), "");
+       string const relpath =
+               to_utf8(makeRelPath(from_utf8(filepath), from_utf8(sysdir)));
+       if (!prefixIs(relpath, "../") && !FileName::isAbsolute(relpath))
+               filepath = addPath("/systemlyxdir", relpath);
+       else if (!save_transient_properties || !lyxrc.save_origin)
+               filepath = "unavailable";
+       os << "\\origin " << quoteIfNeeded(filepath) << '\n';
+
        // the textclass
-       os << "\\textclass " << baseClass()->name() << '\n';
+       os << "\\textclass "
+          << quoteIfNeeded(buf->includedFilePath(addName(buf->layoutPos(),
+                                               baseClass()->name()), "layout"))
+          << '\n';
 
        // then the preamble
        if (!preamble.empty()) {
                // remove '\n' from the end of preamble
-               string const tmppreamble = rtrim(preamble, "\n");
+               docstring const tmppreamble = rtrim(preamble, "\n");
                os << "\\begin_preamble\n"
-                  << tmppreamble
+                  << to_utf8(tmppreamble)
                   << "\n\\end_preamble\n";
        }
 
@@ -975,13 +1150,22 @@ void BufferParams::writeFile(ostream & os) const
           << convert<string>(maintain_unincluded_children) << '\n';
 
        // local layout information
+       docstring const local_layout = getLocalLayout(false);
        if (!local_layout.empty()) {
                // remove '\n' from the end
-               string const tmplocal = rtrim(local_layout, "\n");
+               docstring const tmplocal = rtrim(local_layout, "\n");
                os << "\\begin_local_layout\n"
-                  << tmplocal
+                  << to_utf8(tmplocal)
                   << "\n\\end_local_layout\n";
        }
+       docstring const forced_local_layout = getLocalLayout(true);
+       if (!forced_local_layout.empty()) {
+               // remove '\n' from the end
+               docstring const tmplocal = rtrim(forced_local_layout, "\n");
+               os << "\\begin_forced_local_layout\n"
+                  << to_utf8(tmplocal)
+                  << "\n\\end_forced_local_layout\n";
+       }
 
        // then the text parameters
        if (language != ignore_language)
@@ -989,20 +1173,27 @@ void BufferParams::writeFile(ostream & os) const
        os << "\\language_package " << lang_package
           << "\n\\inputencoding " << inputenc
           << "\n\\fontencoding " << fontenc
-          << "\n\\font_roman " << fonts_roman
-          << "\n\\font_sans " << fonts_sans
-          << "\n\\font_typewriter " << fonts_typewriter
-          << "\n\\font_math " << fonts_math
+          << "\n\\font_roman \"" << fonts_roman[0]
+          << "\" \"" << fonts_roman[1] << '"'
+          << "\n\\font_sans \"" << fonts_sans[0]
+          << "\" \"" << fonts_sans[1] << '"'
+          << "\n\\font_typewriter \"" << fonts_typewriter[0]
+          << "\" \"" << fonts_typewriter[1] << '"'
+          << "\n\\font_math \"" << fonts_math[0]
+          << "\" \"" << fonts_math[1] << '"'
           << "\n\\font_default_family " << fonts_default_family
           << "\n\\use_non_tex_fonts " << convert<string>(useNonTeXFonts)
           << "\n\\font_sc " << convert<string>(fonts_expert_sc)
           << "\n\\font_osf " << convert<string>(fonts_old_figures)
-          << "\n\\font_sf_scale " << fonts_sans_scale
-          << "\n\\font_tt_scale " << fonts_typewriter_scale
+          << "\n\\font_sf_scale " << fonts_sans_scale[0]
+          << ' ' << fonts_sans_scale[1]
+          << "\n\\font_tt_scale " << fonts_typewriter_scale[0]
+          << ' ' << fonts_typewriter_scale[1]
           << '\n';
        if (!fonts_cjk.empty()) {
                os << "\\font_cjk " << fonts_cjk << '\n';
        }
+       os << "\\use_microtype " << convert<string>(use_microtype) << '\n';
        os << "\\graphics " << graphics_driver << '\n';
        os << "\\default_output_format " << default_output_format << '\n';
        os << "\\output_sync " << output_sync << '\n';
@@ -1021,10 +1212,11 @@ void BufferParams::writeFile(ostream & os) const
 
        os << "\\papersize " << string_papersize[papersize]
           << "\n\\use_geometry " << convert<string>(use_geometry);
-       vector<string> const & packages = auto_packages();
-       for (size_t i = 0; i < packages.size(); ++i)
-               os << "\n\\use_package " << packages[i] << ' '
-                  << use_package(packages[i]);
+       map<string, string> const & packages = auto_packages();
+       for (map<string, string>::const_iterator it = packages.begin();
+            it != packages.end(); ++it)
+               os << "\n\\use_package " << it->first << ' '
+                  << use_package(it->first);
 
        os << "\n\\cite_engine ";
 
@@ -1117,8 +1309,9 @@ void BufferParams::writeFile(ostream & os) const
                os << "\n\\paragraph_indentation " << getIndentation().asLyXCommand();
        else
                os << "\n\\defskip " << getDefSkip().asLyXCommand();
-       os << "\n\\quotes_language "
-          << string_quotes_language[quotes_language]
+       os << "\n\\quotes_style "
+          << string_quotes_style[quotes_style]
+          << "\n\\dynamic_quotes " << dynamic_quotes
           << "\n\\papercolumns " << columns
           << "\n\\papersides " << sides
           << "\n\\paperpagestyle " << pagestyle << '\n';
@@ -1142,9 +1335,15 @@ void BufferParams::writeFile(ostream & os) const
                }
        }
 
-       os << "\\tracking_changes " << convert<string>(trackChanges) << '\n'
-          << "\\output_changes " << convert<string>(outputChanges) << '\n'
-          << "\\html_math_output " << html_math_output << '\n'
+       os << "\\tracking_changes "
+          << (save_transient_properties ? convert<string>(track_changes) : "false")
+          << '\n';
+
+       os << "\\output_changes "
+          << (save_transient_properties ? convert<string>(output_changes) : "false")
+          << '\n';
+
+       os << "\\html_math_output " << html_math_output << '\n'
           << "\\html_css_as_file " << html_css_as_file << '\n'
           << "\\html_be_strict " << convert<string>(html_be_strict) << '\n';
 
@@ -1166,7 +1365,7 @@ void BufferParams::validate(LaTeXFeatures & features) const
        if (columns > 1 && language->rightToLeft())
                features.require("rtloutputdblcol");
 
-       if (outputChanges) {
+       if (output_changes) {
                bool dvipost    = LaTeXFeatures::isAvailable("dvipost");
                bool xcolorulem = LaTeXFeatures::isAvailable("ulem") &&
                                  LaTeXFeatures::isAvailable("xcolor");
@@ -1252,15 +1451,25 @@ void BufferParams::validate(LaTeXFeatures & features) const
                if (pdfoptions().colorlinks)
                        features.require("color");
        }
+       if (!listings_params.empty()) {
+               // do not test validity because listings_params is
+               // supposed to be valid
+               string par =
+                       InsetListingsParams(listings_params).separatedParams(true);
+               // we can't support all packages, but we should load the color package
+               if (par.find("\\color", 0) != string::npos)
+                       features.require("color");
+       }
 
        // some languages are only available via polyglossia
-       if (features.runparams().flavor == OutputParams::XETEX
-           && (features.hasPolyglossiaExclusiveLanguages()
-               || useNonTeXFonts))
+       if (features.hasPolyglossiaExclusiveLanguages())
                features.require("polyglossia");
 
-       if (useNonTeXFonts && fonts_math != "auto")
+       if (useNonTeXFonts && fontsMath() != "auto")
                features.require("unicode-math");
+       
+       if (use_microtype)
+               features.require("microtype");
 
        if (!language->requires().empty())
                features.require(language->requires());
@@ -1278,6 +1487,11 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
        // are doing!
        if (features.mustProvide("fix-cm"))
                os << "\\RequirePackage{fix-cm}\n";
+       // Likewise for fixltx2e. If other packages conflict with this policy,
+       // treat it as a package bug (and report it!)
+       // See http://www.latex-project.org/cgi-bin/ltxbugs2html?pr=latex/4407
+       if (features.mustProvide("fixltx2e"))
+               os << "\\RequirePackage{fixltx2e}\n";
 
        os << "\\documentclass";
 
@@ -1417,13 +1631,18 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
        os << '{' << from_ascii(tclass.latexname()) << "}\n";
        // end of \documentclass defs
 
-       // if we use fontspec, we have to load the AMS packages here
+       // if we use fontspec or newtxmath, we have to load the AMS packages here
        string const ams = features.loadAMSPackages();
-       if (useNonTeXFonts && !ams.empty())
+       bool const ot1 = (font_encoding() == "default" || font_encoding() == "OT1");
+       bool const use_newtxmath =
+               theLaTeXFonts().getLaTeXFont(from_ascii(fontsMath())).getUsedPackage(
+                       ot1, false, false) == "newtxmath";
+       if ((useNonTeXFonts || use_newtxmath) && !ams.empty())
                os << from_ascii(ams);
 
        if (useNonTeXFonts) {
-               os << "\\usepackage{fontspec}\n";
+               if (!features.isProvided("fontspec"))
+                       os << "\\usepackage{fontspec}\n";
                if (features.mustProvide("unicode-math")
                    && features.isAvailable("unicode-math"))
                        os << "\\usepackage{unicode-math}\n";
@@ -1439,20 +1658,16 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
                   << from_ascii(fonts_default_family) << "}\n";
 
        // set font encoding
-       // for arabic_arabi and farsi we also need to load the LAE and
-       // LFE encoding
-       // XeTeX and LuaTeX (with OS fonts) work without fontenc
-       if (font_encoding() != "default" && language->lang() != "japanese"
-           && !useNonTeXFonts && !features.isProvided("fontenc")) {
-               size_t fars = language_options.str().find("farsi");
-               size_t arab = language_options.str().find("arabic");
-               if (language->lang() == "arabic_arabi"
-                       || language->lang() == "farsi" || fars != string::npos
-                       || arab != string::npos) {
-                       os << "\\usepackage[" << from_ascii(font_encoding())
-                          << ",LFE,LAE]{fontenc}\n";
-               } else {
-                       os << "\\usepackage[" << from_ascii(font_encoding())
+       // XeTeX and LuaTeX (with OS fonts) do not need fontenc
+       if (!useNonTeXFonts && !features.isProvided("fontenc")
+           && font_encoding() != "default") {
+               // get main font encodings
+               vector<string> fontencs = font_encodings();
+               // get font encodings of secondary languages
+               features.getFontEncodings(fontencs);
+               if (!fontencs.empty()) {
+                       os << "\\usepackage["
+                          << from_ascii(getStringFromVector(fontencs))
                           << "]{fontenc}\n";
                }
        }
@@ -1486,21 +1701,6 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
                os << "}\n";
        }
 
-       if (!listings_params.empty() || features.isRequired("listings"))
-               os << "\\usepackage{listings}\n";
-
-       if (!listings_params.empty()) {
-               os << "\\lstset{";
-               // do not test validity because listings_params is
-               // supposed to be valid
-               string par =
-                       InsetListingsParams(listings_params).separatedParams(true);
-               // we can't support all packages, but we should load the color package
-               if (par.find("\\color", 0) != string::npos)
-                       features.require("color");
-               os << from_utf8(par)
-                  << "}\n";
-       }
        if (!features.isProvided("geometry")
            && (use_geometry || nonstandard_papersize)) {
                odocstringstream ods;
@@ -1715,41 +1915,46 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
        }
 
        // Now insert the LyX specific LaTeX commands...
-       docstring lyxpreamble;
        features.resolveAlternatives();
+       features.expandMultiples();
 
        if (output_sync) {
                if (!output_sync_macro.empty())
-                       lyxpreamble += from_utf8(output_sync_macro) +"\n";
+                       os << from_utf8(output_sync_macro) +"\n";
                else if (features.runparams().flavor == OutputParams::LATEX)
-                       lyxpreamble += "\\usepackage[active]{srcltx}\n";
+                       os << "\\usepackage[active]{srcltx}\n";
                else if (features.runparams().flavor == OutputParams::PDFLATEX)
-                       lyxpreamble += "\\synctex=-1\n";
+                       os << "\\synctex=-1\n";
        }
 
+       // The package options (via \PassOptionsToPackage)
+       os << from_ascii(features.getPackageOptions());
+
        // due to interferences with babel and hyperref, the color package has to
        // be loaded (when it is not already loaded) before babel when hyperref
        // is used with the colorlinks option, see
        // http://www.lyx.org/trac/ticket/5291
        // we decided therefore to load color always before babel, see
        // http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg144349.html
-       lyxpreamble += from_ascii(features.getColorOptions());
+       os << from_ascii(features.getColorOptions());
 
-       // If we use hyperref, jurabib, japanese, or vietnamese, we have to call babel before them.
+       // If we use hyperref, jurabib, japanese, varioref or vietnamese,
+       // we have to call babel before
        if (use_babel
            && (features.isRequired("jurabib")
                || features.isRequired("hyperref")
+               || features.isRequired("varioref")
                || features.isRequired("vietnamese")
                || features.isRequired("japanese"))) {
+                       os << features.getBabelPresettings();
                        // FIXME UNICODE
-                       lyxpreamble += from_utf8(features.getBabelPresettings());
-                       lyxpreamble += from_utf8(babelCall(language_options.str(),
-                                                          features.needBabelLangOptions())) + '\n';
-                       lyxpreamble += from_utf8(features.getBabelPostsettings());
+                       os << from_utf8(babelCall(language_options.str(),
+                                                 features.needBabelLangOptions())) + '\n';
+                       os << features.getBabelPostsettings();
        }
 
        // The optional packages;
-       lyxpreamble += from_ascii(features.getPackages());
+       os << from_ascii(features.getPackages());
 
        // Additional Indices
        if (features.isRequired("splitidx")) {
@@ -1767,16 +1972,16 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
                                                  "representable in the current encoding and therefore have been omitted:\n%1$s."),
                                                indexname_latex.second));
                        }
-                       lyxpreamble += "\\newindex[";
-                       lyxpreamble += indexname_latex.first;
-                       lyxpreamble += "]{";
-                       lyxpreamble += escape(iit->shortcut());
-                       lyxpreamble += "}\n";
+                       os << "\\newindex[";
+                       os << indexname_latex.first;
+                       os << "]{";
+                       os << escape(iit->shortcut());
+                       os << "}\n";
                }
        }
 
        // Line spacing
-       lyxpreamble += from_utf8(spacing().writePreamble(features.isProvided("SetSpace")));
+       os << from_utf8(spacing().writePreamble(features.isProvided("SetSpace")));
 
        // PDF support.
        // * Hyperref manual: "Make sure it comes last of your loaded
@@ -1788,58 +1993,105 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
        //   avoid errors with algorithm floats.
        // use hyperref explicitly if it is required
        if (features.isRequired("hyperref")) {
-               // pass what we have to stream here, since we need
-               // to access the stream itself in PDFOptions.
-               os << lyxpreamble;
-
                OutputParams tmp_params = features.runparams();
                pdfoptions().writeLaTeX(tmp_params, os,
                                        features.isProvided("hyperref"));
-               // set back for the rest
-               lyxpreamble.clear();
                // correctly break URLs with hyperref and dvi output
                if (features.runparams().flavor == OutputParams::LATEX
                    && features.isAvailable("breakurl"))
-                       lyxpreamble += "\\usepackage{breakurl}\n";
+                       os << "\\usepackage{breakurl}\n";
        } else if (features.isRequired("nameref"))
                // hyperref loads this automatically
-               lyxpreamble += "\\usepackage{nameref}\n";
+               os << "\\usepackage{nameref}\n";
 
        // bibtopic needs to be loaded after hyperref.
        // the dot provides the aux file naming which LyX can detect.
        if (features.mustProvide("bibtopic"))
-               lyxpreamble += "\\usepackage[dot]{bibtopic}\n";
+               os << "\\usepackage[dot]{bibtopic}\n";
 
        // Will be surrounded by \makeatletter and \makeatother when not empty
-       docstring atlyxpreamble;
+       otexstringstream atlyxpreamble;
 
        // Some macros LyX will need
-       docstring tmppreamble(features.getMacros());
-
-       if (!tmppreamble.empty())
-               atlyxpreamble += "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
-                       "LyX specific LaTeX commands.\n"
-                       + tmppreamble + '\n';
-
+       {
+               TexString tmppreamble = features.getMacros();
+               if (!tmppreamble.str.empty())
+                       atlyxpreamble << "\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
+                                        "LyX specific LaTeX commands.\n"
+                                     << move(tmppreamble)
+                                     << '\n';
+       }
        // the text class specific preamble
-       tmppreamble = features.getTClassPreamble();
-       if (!tmppreamble.empty())
-               atlyxpreamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
-                       "Textclass specific LaTeX commands.\n"
-                       + tmppreamble + '\n';
-
+       {
+               docstring tmppreamble = features.getTClassPreamble();
+               if (!tmppreamble.empty())
+                       atlyxpreamble << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
+                                        "Textclass specific LaTeX commands.\n"
+                                     << tmppreamble
+                                     << '\n';
+       }
        // suppress date if selected
        // use \@ifundefined because we cannot be sure that every document class
        // has a \date command
        if (suppress_date)
-               atlyxpreamble += "\\@ifundefined{date}{}{\\date{}}\n";
+               atlyxpreamble << "\\@ifundefined{date}{}{\\date{}}\n";
 
        /* the user-defined preamble */
-       if (!containsOnly(preamble, " \n\t"))
+       if (!containsOnly(preamble, " \n\t")) {
                // FIXME UNICODE
-               atlyxpreamble += "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
-                       "User specified LaTeX commands.\n"
-                       + from_utf8(preamble) + '\n';
+               atlyxpreamble << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "
+                                "User specified LaTeX commands.\n";
+
+               // Check if the user preamble contains uncodable glyphs
+               odocstringstream user_preamble;
+               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];
+                               if (!enc->encodable(c)) {
+                                       docstring const glyph(1, c);
+                                       LYXERR0("Uncodable character '"
+                                               << glyph
+                                               << "' in user preamble!");
+                                       uncodable_glyphs += glyph;
+                                       if (features.runparams().dryrun) {
+                                               user_preamble << "<" << _("LyX Warning: ")
+                                                  << _("uncodable character") << " '";
+                                               user_preamble.put(c);
+                                               user_preamble << "'>";
+                                       }
+                               } else
+                                       user_preamble.put(c);
+                       }
+               } else
+                       user_preamble << preamble;
+
+               // On BUFFER_VIEW|UPDATE, warn user if we found uncodable glyphs
+               if (!features.runparams().dryrun && !uncodable_glyphs.empty()) {
+                       frontend::Alert::warning(
+                               _("Uncodable character in user preamble"),
+                               support::bformat(
+                                 _("The user preamble 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));
+               }
+               atlyxpreamble << user_preamble.str() << '\n';
+       }
+
+       // footmisc must be loaded after setspace
+       // Load it here to avoid clashes with footmisc loaded in the user
+       // preamble. For that reason we also pass the options via
+       // \PassOptionsToPackage in getPreamble() and not here.
+       if (features.mustProvide("footmisc"))
+               atlyxpreamble << "\\usepackage{footmisc}\n";
 
        // subfig loads internally the LaTeX package "caption". As
        // caption is a very popular package, users will load it in
@@ -1851,11 +2103,10 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
        // koma's own caption commands are used instead of caption. We
        // use \PassOptionsToPackage here because the user could have
        // already loaded subfig in the preamble.
-       if (features.isRequired("subfig")) {
-               atlyxpreamble += "\\@ifundefined{showcaptionsetup}{}{%\n"
-                       " \\PassOptionsToPackage{caption=false}{subfig}}\n"
-                       "\\usepackage{subfig}\n";
-       }
+       if (features.mustProvide("subfig"))
+               atlyxpreamble << "\\@ifundefined{showcaptionsetup}{}{%\n"
+                                " \\PassOptionsToPackage{caption=false}{subfig}}\n"
+                                "\\usepackage{subfig}\n";
 
        // Itemize bullet settings need to be last in case the user
        // defines their own bullets that use a package included
@@ -1890,66 +2141,85 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
        }
 
        if (!bullets_def.empty())
-               atlyxpreamble += bullets_def + "}\n\n";
+               atlyxpreamble << bullets_def << "}\n\n";
 
        if (!atlyxpreamble.empty())
-               lyxpreamble += "\n\\makeatletter\n"
-                       + atlyxpreamble + "\\makeatother\n\n";
+               os << "\n\\makeatletter\n"
+                  << atlyxpreamble.release()
+                  << "\\makeatother\n\n";
 
        // We try to load babel late, in case it interferes with other packages.
-       // Jurabib and Hyperref have to be called after babel, though.
+       // Jurabib, hyperref, varioref, bicaption and listings (bug 8995) have to be
+       // called after babel, though.
        if (use_babel && !features.isRequired("jurabib")
            && !features.isRequired("hyperref")
+               && !features.isRequired("varioref")
            && !features.isRequired("vietnamese")
            && !features.isRequired("japanese")) {
+               os << features.getBabelPresettings();
                // FIXME UNICODE
-               lyxpreamble += from_utf8(features.getBabelPresettings());
-               lyxpreamble += from_utf8(babelCall(language_options.str(),
-                                                  features.needBabelLangOptions())) + '\n';
-               lyxpreamble += from_utf8(features.getBabelPostsettings());
+               os << from_utf8(babelCall(language_options.str(),
+                                         features.needBabelLangOptions())) + '\n';
+               os << features.getBabelPostsettings();
+       }
+       if (features.isRequired("bicaption"))
+               os << "\\usepackage{bicaption}\n";
+       if (!listings_params.empty() || features.mustProvide("listings"))
+               os << "\\usepackage{listings}\n";
+       if (!listings_params.empty()) {
+               os << "\\lstset{";
+               // do not test validity because listings_params is
+               // supposed to be valid
+               string par =
+                       InsetListingsParams(listings_params).separatedParams(true);
+               os << from_utf8(par);
+               os << "}\n";
        }
 
        // xunicode needs to be loaded at least after amsmath, amssymb,
        // esint and the other packages that provide special glyphs
-       if (features.runparams().flavor == OutputParams::XETEX)
-               lyxpreamble += "\\usepackage{xunicode}\n";
+       // The package only supports XeTeX currently.
+       if (features.runparams().flavor == OutputParams::XETEX
+           && useNonTeXFonts)
+               os << "\\usepackage{xunicode}\n";
 
        // Polyglossia must be loaded last
        if (use_polyglossia) {
                // call the package
-               lyxpreamble += "\\usepackage{polyglossia}\n";
+               os << "\\usepackage{polyglossia}\n";
                // set the main language
-               lyxpreamble += "\\setdefaultlanguage";
+               os << "\\setdefaultlanguage";
                if (!language->polyglossiaOpts().empty())
-                       lyxpreamble += "[" + from_ascii(language->polyglossiaOpts()) + "]";
-               lyxpreamble += "{" + from_ascii(language->polyglossia()) + "}\n";
+                       os << "[" << from_ascii(language->polyglossiaOpts()) << "]";
+               os << "{" << from_ascii(language->polyglossia()) << "}\n";
                // now setup the other languages
-               std::map<std::string, std::string> const polylangs =
+               set<string> const polylangs =
                        features.getPolyglossiaLanguages();
-               for (std::map<std::string, std::string>::const_iterator mit = polylangs.begin();
+               for (set<string>::const_iterator mit = polylangs.begin();
                     mit != polylangs.end() ; ++mit) {
-                       lyxpreamble += "\\setotherlanguage";
-                       if (!mit->second.empty())
-                               lyxpreamble += "[" + from_ascii(mit->second) + "]";
-                       lyxpreamble += "{" + from_ascii(mit->first) + "}\n";
+                       // We do not output the options here; they are output in
+                       // the language switch commands. This is safer if multiple
+                       // varieties are used.
+                       if (*mit == language->polyglossia())
+                               continue;
+                       os << "\\setotherlanguage";
+                       os << "{" << from_ascii(*mit) << "}\n";
                }
        }
 
        // Load custom language package here
        if (features.langPackage() == LaTeXFeatures::LANG_PACK_CUSTOM) {
                if (lang_package == "default")
-                       lyxpreamble += from_utf8(lyxrc.language_custom_package);
+                       os << from_utf8(lyxrc.language_custom_package);
                else
-                       lyxpreamble += from_utf8(lang_package);
-               lyxpreamble += '\n';
+                       os << from_utf8(lang_package);
+               os << '\n';
        }
 
        docstring const i18npreamble =
                features.getTClassI18nPreamble(use_babel, use_polyglossia);
        if (!i18npreamble.empty())
-               lyxpreamble += i18npreamble + '\n';
-
-       os << lyxpreamble;
+               os << i18npreamble + '\n';
 
        return use_babel;
 }
@@ -1986,7 +2256,7 @@ bool BufferParams::hasClassDefaults() const
 
 DocumentClass const & BufferParams::documentClass() const
 {
-       return *doc_class_.get();
+       return *doc_class_;
 }
 
 
@@ -2000,6 +2270,7 @@ void BufferParams::setDocumentClass(DocumentClassConstPtr tc)
 {
        // evil, but this function is evil
        doc_class_ = const_pointer_cast<DocumentClass>(tc);
+       invalidateConverterCache();
 }
 
 
@@ -2053,40 +2324,66 @@ LayoutFileIndex const & BufferParams::baseClassID() const
 }
 
 
-void BufferParams::makeDocumentClass()
+void BufferParams::makeDocumentClass(bool const clone)
 {
        if (!baseClass())
                return;
 
+       invalidateConverterCache();
        LayoutModuleList mods;
-       LayoutModuleList::iterator it;
-       LayoutModuleList::iterator en;
-
-       it = layout_modules_.begin();
-       en = layout_modules_.end();
+       LayoutModuleList::iterator it = layout_modules_.begin();
+       LayoutModuleList::iterator en = layout_modules_.end();
        for (; it != en; ++it)
                mods.push_back(*it);
+
        it = cite_engine_.begin();
        en = cite_engine_.end();
        for (; it != en; ++it)
                mods.push_back(*it);
-       doc_class_ = getDocumentClass(*baseClass(), mods);
 
-       if (!local_layout.empty()) {
-               TextClass::ReturnValues success =
-                       doc_class_->read(local_layout, TextClass::MODULE);
-               if (success != TextClass::OK && success != TextClass::OK_OLDFORMAT) {
-                       docstring const msg = _("Error reading internal layout information");
-                       frontend::Alert::warning(_("Read Error"), msg);
-               }
+       doc_class_ = getDocumentClass(*baseClass(), mods, clone);
+
+       TextClass::ReturnValues success = TextClass::OK;
+       if (!forced_local_layout_.empty())
+               success = doc_class_->read(to_utf8(forced_local_layout_),
+                                          TextClass::MODULE);
+       if (!local_layout_.empty() &&
+           (success == TextClass::OK || success == TextClass::OK_OLDFORMAT))
+               success = doc_class_->read(to_utf8(local_layout_), TextClass::MODULE);
+       if (success != TextClass::OK && success != TextClass::OK_OLDFORMAT) {
+               docstring const msg = _("Error reading internal layout information");
+               frontend::Alert::warning(_("Read Error"), msg);
        }
 }
 
 
-bool BufferParams::moduleCanBeAdded(string const & modName) const
+bool BufferParams::layoutModuleCanBeAdded(string const & modName) const
+{
+       return layout_modules_.moduleCanBeAdded(modName, baseClass());
+}
+
+
+bool BufferParams::citationModuleCanBeAdded(string const & modName) const
 {
-       return cite_engine_.moduleCanBeAdded(modName, baseClass()) &&
-               layout_modules_.moduleCanBeAdded(modName, baseClass());
+       return cite_engine_.moduleCanBeAdded(modName, baseClass());
+}
+
+
+docstring BufferParams::getLocalLayout(bool forced) const
+{
+       if (forced)
+               return from_utf8(doc_class_->forcedLayouts());
+       else
+               return local_layout_;
+}
+
+
+void BufferParams::setLocalLayout(docstring const & layout, bool forced)
+{
+       if (forced)
+               forced_local_layout_ = layout;
+       else
+               local_layout_ = layout;
 }
 
 
@@ -2104,71 +2401,50 @@ bool BufferParams::addLayoutModule(string const & modName)
 
 string BufferParams::bufferFormat() const
 {
-       string format = documentClass().outputFormat();
-       if (format == "latex") {
-               if (useNonTeXFonts)
-                       return "xetex";
-               if (encoding().package() == Encoding::japanese)
-                       return "platex";
-       }
-       return format;
+       return documentClass().outputFormat();
 }
 
 
-bool BufferParams::isExportable(string const & format) const
+bool BufferParams::isExportable(string const & format, bool need_viewable) const
 {
-       vector<string> backs = backends();
-       for (vector<string>::const_iterator it = backs.begin();
-            it != backs.end(); ++it)
-               if (theConverters().isReachable(*it, format))
+       FormatList const & formats = exportableFormats(need_viewable);
+       FormatList::const_iterator fit = formats.begin();
+       FormatList::const_iterator end = formats.end();
+       for (; fit != end ; ++fit) {
+               if ((*fit)->name() == format)
                        return true;
+       }
        return false;
 }
 
 
-namespace {
-
-bool formatSorter(Format const * lhs, Format const * rhs)
+FormatList const & BufferParams::exportableFormats(bool only_viewable) const
 {
-       return _(lhs->prettyname()) < _(rhs->prettyname());
-}
+       FormatList & cached = only_viewable ?
+                       pimpl_->viewableFormatList : pimpl_->exportableFormatList;
+       bool & valid = only_viewable ? 
+                       pimpl_->isViewCacheValid : pimpl_->isExportCacheValid;
+       if (valid)
+               return cached;
 
-}
-
-
-vector<Format const *> BufferParams::exportableFormats(bool only_viewable) const
-{
        vector<string> const backs = backends();
        set<string> excludes;
        if (useNonTeXFonts) {
                excludes.insert("latex");
                excludes.insert("pdflatex");
        }
-       vector<Format const *> result =
+       FormatList result =
                theConverters().getReachable(backs[0], only_viewable, true, excludes);
        for (vector<string>::const_iterator it = backs.begin() + 1;
             it != backs.end(); ++it) {
-               vector<Format const *>  r =
-                       theConverters().getReachable(*it, only_viewable, false, excludes);
+               FormatList r = theConverters().getReachable(*it, only_viewable, 
+                               false, excludes);
                result.insert(result.end(), r.begin(), r.end());
        }
-       sort(result.begin(), result.end(), formatSorter);
-       return result;
-}
-
-
-bool BufferParams::isExportableFormat(string const & format) const
-{
-       typedef vector<Format const *> Formats;
-       Formats formats;
-       formats = exportableFormats(true);
-       Formats::const_iterator fit = formats.begin();
-       Formats::const_iterator end = formats.end();
-       for (; fit != end ; ++fit) {
-               if ((*fit)->name() == format)
-                       return true;
-       }
-       return false;
+       sort(result.begin(), result.end(), Format::formatSorter);
+       cached = result;
+       valid = true;
+       return cached;
 }
 
 
@@ -2179,17 +2455,17 @@ vector<string> BufferParams::backends() const
 
        // FIXME: Don't hardcode format names here, but use a flag
        if (buffmt == "latex") {
-               if (!useNonTeXFonts) {
-                       v.push_back("pdflatex");
-                       v.push_back("latex");
+               if (encoding().package() == Encoding::japanese)
+                       v.push_back("platex");
+               else {
+                       if (!useNonTeXFonts) {
+                               v.push_back("pdflatex");
+                               v.push_back("latex");
+                       }
+                       v.push_back("xetex");
+                       v.push_back("luatex");
+                       v.push_back("dviluatex");
                }
-               v.push_back("luatex");
-               v.push_back("dviluatex");
-               v.push_back("xetex");
-       } else if (buffmt == "xetex") {
-               v.push_back("xetex");
-               v.push_back("luatex");
-               v.push_back("dviluatex");
        } else
                v.push_back(buffmt);
 
@@ -2200,7 +2476,7 @@ vector<string> BufferParams::backends() const
 }
 
 
-OutputParams::FLAVOR BufferParams::getOutputFlavor(string const format) const
+OutputParams::FLAVOR BufferParams::getOutputFlavor(string const format) const
 {
        string const dformat = (format.empty() || format == "default") ?
                getDefaultOutputFormat() : format;
@@ -2221,7 +2497,7 @@ OutputParams::FLAVOR BufferParams::getOutputFlavor(string const format) const
        else if (dformat == "lyx")
                result = OutputParams::LYX;
        else if (dformat == "pdflatex")
-               result = OutputParams::PDFLATEX;
+               result = OutputParams::PDFLATEX;
        else if (dformat == "xetex")
                result = OutputParams::XETEX;
        else if (dformat == "luatex")
@@ -2257,14 +2533,15 @@ string BufferParams::getDefaultOutputFormat() const
            && default_output_format != "default")
                return default_output_format;
        if (isDocBook()
-           || useNonTeXFonts
            || encoding().package() == Encoding::japanese) {
-               vector<Format const *> const formats = exportableFormats(true);
+               FormatList const & formats = exportableFormats(true);
                if (formats.empty())
                        return string();
                // return the first we find
                return formats.front()->name();
        }
+       if (useNonTeXFonts)
+               return lyxrc.default_otf_view_format;
        return lyxrc.default_view_format;
 }
 
@@ -2281,9 +2558,9 @@ Font const BufferParams::getFont() const
 }
 
 
-InsetQuotes::QuoteLanguage BufferParams::getQuoteStyle(string const qs) const
+InsetQuotesParams::QuoteStyle BufferParams::getQuoteStyle(string const & qs) const
 {
-       return quoteslangtranslator().find(qs);
+       return quotesstyletranslator().find(qs);
 }
 
 
@@ -2311,17 +2588,23 @@ void BufferParams::readPreamble(Lexer & lex)
                lyxerr << "Error (BufferParams::readPreamble):"
                        "consistency check failed." << endl;
 
-       preamble = lex.getLongString("\\end_preamble");
+       preamble = lex.getLongString(from_ascii("\\end_preamble"));
 }
 
 
-void BufferParams::readLocalLayout(Lexer & lex)
+void BufferParams::readLocalLayout(Lexer & lex, bool forced)
 {
-       if (lex.getString() != "\\begin_local_layout")
+       string const expected = forced ? "\\begin_forced_local_layout" :
+                                        "\\begin_local_layout";
+       if (lex.getString() != expected)
                lyxerr << "Error (BufferParams::readLocalLayout):"
                        "consistency check failed." << endl;
 
-       local_layout = lex.getLongString("\\end_local_layout");
+       if (forced)
+               forced_local_layout_ =
+                       lex.getLongString(from_ascii("\\end_forced_local_layout"));
+       else
+               local_layout_ = lex.getLongString(from_ascii("\\end_local_layout"));
 }
 
 
@@ -2663,7 +2946,31 @@ string const BufferParams::dvips_options() const
 
 string const BufferParams::font_encoding() const
 {
-       return (fontenc == "global") ? lyxrc.fontenc : fontenc;
+       return font_encodings().empty() ? "default" : font_encodings().back();
+}
+
+
+vector<string> const BufferParams::font_encodings() const
+{
+       string doc_fontenc = (fontenc == "global") ? lyxrc.fontenc : fontenc;
+
+       vector<string> fontencs;
+
+       // "default" means "no explicit font encoding"
+       if (doc_fontenc != "default") {
+               fontencs = getVectorFromString(doc_fontenc);
+               if (!language->fontenc().empty()
+                   && ascii_lowercase(language->fontenc()) != "none") {
+                       vector<string> fencs = getVectorFromString(language->fontenc());
+                       vector<string>::const_iterator fit = fencs.begin();
+                       for (; fit != fencs.end(); ++fit) {
+                               if (find(fontencs.begin(), fontencs.end(), *fit) == fontencs.end())
+                                       fontencs.push_back(*fit);
+                       }
+               }
+       }
+
+       return fontencs;
 }
 
 
@@ -2704,36 +3011,33 @@ docstring BufferParams::getGraphicsDriver(string const & package) const
 void BufferParams::writeEncodingPreamble(otexstream & os,
                                         LaTeXFeatures & features) const
 {
-       // XeTeX does not need this
-       if (features.runparams().flavor == OutputParams::XETEX)
-               return;
-       // LuaTeX neither, but with tex fonts, we need to load
-       // the luainputenc package.
-       if (features.runparams().flavor == OutputParams::LUATEX
-               || features.runparams().flavor == OutputParams::DVILUATEX) {
-               if (!useNonTeXFonts && inputenc != "default"
-                   && ((inputenc == "auto" && language->encoding()->package() == Encoding::inputenc)
-                       || (inputenc != "auto" && encoding().package() == Encoding::inputenc))) {
-                       os << "\\usepackage[utf8]{luainputenc}\n";
-               }
+       // XeTeX/LuaTeX: (see also #9740)
+       // With Unicode fonts we use utf8-plain without encoding package.
+       // With TeX fonts, we cannot use utf8-plain, but "inputenc" fails.
+       // XeTeX must use ASCII encoding (see Buffer.cpp),
+       //  for LuaTeX, we load "luainputenc" (see below).
+       if (useNonTeXFonts || features.runparams().flavor == OutputParams::XETEX)
                return;
-       }
+
        if (inputenc == "auto") {
                string const doc_encoding =
                        language->encoding()->latexName();
                Encoding::Package const package =
                        language->encoding()->package();
 
-               // Create a list with all the input encodings used
-               // in the document
-               set<string> encodings =
-                       features.getEncodingSet(doc_encoding);
+               // Create list of inputenc options:
+               set<string> encodings;
+               // luainputenc fails with more than one encoding
+               if (!features.runparams().isFullUnicode()) // if we reach this point, this means LuaTeX with TeX fonts
+                       // list all input encodings used in the document
+                       encodings = features.getEncodingSet(doc_encoding);
 
                // If the "japanese" package (i.e. pLaTeX) is used,
                // inputenc must be omitted.
                // see http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg129680.html
                if ((!encodings.empty() || package == Encoding::inputenc)
-                   && !features.isRequired("japanese")) {
+                   && !features.isRequired("japanese")
+                   && !features.isProvided("inputenc")) {
                        os << "\\usepackage[";
                        set<string>::const_iterator it = encodings.begin();
                        set<string>::const_iterator const end = encodings.end();
@@ -2748,7 +3052,11 @@ void BufferParams::writeEncodingPreamble(otexstream & os,
                                        os << ',';
                                os << from_ascii(doc_encoding);
                        }
-                       os << "]{inputenc}\n";
+                       if (features.runparams().flavor == OutputParams::LUATEX
+                           || features.runparams().flavor == OutputParams::DVILUATEX)
+                               os << "]{luainputenc}\n";
+                       else
+                               os << "]{inputenc}\n";
                }
                if (package == Encoding::CJK || features.mustProvide("CJK")) {
                        if (language->encoding()->name() == "utf8-cjk"
@@ -2764,10 +3072,16 @@ void BufferParams::writeEncodingPreamble(otexstream & os,
                        break;
                case Encoding::inputenc:
                        // do not load inputenc if japanese is used
-                       if (features.isRequired("japanese"))
+                       // or if the class provides inputenc
+                       if (features.isRequired("japanese")
+                           || features.isProvided("inputenc"))
                                break;
-                       os << "\\usepackage[" << from_ascii(inputenc)
-                          << "]{inputenc}\n";
+                       os << "\\usepackage[" << from_ascii(encoding().latexName());
+                       if (features.runparams().flavor == OutputParams::LUATEX
+                           || features.runparams().flavor == OutputParams::DVILUATEX)
+                               os << "]{luainputenc}\n";
+                       else
+                               os << "]{inputenc}\n";
                        break;
                case Encoding::CJK:
                        if (encoding().name() == "utf8-cjk"
@@ -2777,6 +3091,15 @@ void BufferParams::writeEncodingPreamble(otexstream & os,
                                os << "\\usepackage{CJK}\n";
                        break;
                }
+               // Load the CJK package if needed by a secondary language.
+               // If the main encoding is some variant of UTF8, use CJKutf8.
+               if (encoding().package() != Encoding::CJK && features.mustProvide("CJK")) {
+                       if (encoding().iconvName() == "UTF-8"
+                           && LaTeXFeatures::isAvailable("CJKutf8"))
+                               os << "\\usepackage{CJKutf8}\n";
+                       else
+                               os << "\\usepackage{CJK}\n";
+               }
        }
 }
 
@@ -2794,9 +3117,9 @@ string const BufferParams::parseFontName(string const & name) const
 
 string const BufferParams::loadFonts(LaTeXFeatures & features) const
 {
-       if (fonts_roman == "default" && fonts_sans == "default"
-           && fonts_typewriter == "default"
-           && (fonts_math == "default" || fonts_math == "auto"))
+       if (fontsRoman() == "default" && fontsSans() == "default"
+           && fontsTypewriter() == "default"
+           && (fontsMath() == "default" || fontsMath() == "auto"))
                //nothing to do
                return string();
 
@@ -2825,28 +3148,28 @@ string const BufferParams::loadFonts(LaTeXFeatures & features) const
                string const texmapping =
                        (features.runparams().flavor == OutputParams::XETEX) ?
                        "Mapping=tex-text" : "Ligatures=TeX";
-               if (fonts_roman != "default") {
+               if (fontsRoman() != "default") {
                        os << "\\setmainfont[" << texmapping;
                        if (fonts_old_figures)
                                os << ",Numbers=OldStyle";
-                       os << "]{" << parseFontName(fonts_roman) << "}\n";
+                       os << "]{" << parseFontName(fontsRoman()) << "}\n";
                }
-               if (fonts_sans != "default") {
-                       string const sans = parseFontName(fonts_sans);
-                       if (fonts_sans_scale != 100)
+               if (fontsSans() != "default") {
+                       string const sans = parseFontName(fontsSans());
+                       if (fontsSansScale() != 100)
                                os << "\\setsansfont[Scale="
-                                  << float(fonts_sans_scale) / 100
+                                  << float(fontsSansScale()) / 100
                                   << "," << texmapping << "]{"
                                   << sans << "}\n";
                        else
                                os << "\\setsansfont[" << texmapping << "]{"
                                   << sans << "}\n";
                }
-               if (fonts_typewriter != "default") {
-                       string const mono = parseFontName(fonts_typewriter);
-                       if (fonts_typewriter_scale != 100)
+               if (fontsTypewriter() != "default") {
+                       string const mono = parseFontName(fontsTypewriter());
+                       if (fontsTypewriterScale() != 100)
                                os << "\\setmonofont[Scale="
-                                  << float(fonts_typewriter_scale) / 100
+                                  << float(fontsTypewriterScale()) / 100
                                   << "]{"
                                   << mono << "}\n";
                        else
@@ -2859,26 +3182,26 @@ string const BufferParams::loadFonts(LaTeXFeatures & features) const
        // Tex Fonts
        bool const ot1 = (font_encoding() == "default" || font_encoding() == "OT1");
        bool const dryrun = features.runparams().dryrun;
-       bool const complete = (fonts_sans == "default" && fonts_typewriter == "default");
-       bool const nomath = (fonts_math == "default");
+       bool const complete = (fontsSans() == "default" && fontsTypewriter() == "default");
+       bool const nomath = (fontsMath() == "default");
 
        // ROMAN FONTS
-       os << theLaTeXFonts().getLaTeXFont(from_ascii(fonts_roman)).getLaTeXCode(
+       os << theLaTeXFonts().getLaTeXFont(from_ascii(fontsRoman())).getLaTeXCode(
                dryrun, ot1, complete, fonts_expert_sc, fonts_old_figures,
                nomath);
 
        // SANS SERIF
-       os << theLaTeXFonts().getLaTeXFont(from_ascii(fonts_sans)).getLaTeXCode(
+       os << theLaTeXFonts().getLaTeXFont(from_ascii(fontsSans())).getLaTeXCode(
                dryrun, ot1, complete, fonts_expert_sc, fonts_old_figures,
-               nomath, fonts_sans_scale);
+               nomath, fontsSansScale());
 
        // MONOSPACED/TYPEWRITER
-       os << theLaTeXFonts().getLaTeXFont(from_ascii(fonts_typewriter)).getLaTeXCode(
+       os << theLaTeXFonts().getLaTeXFont(from_ascii(fontsTypewriter())).getLaTeXCode(
                dryrun, ot1, complete, fonts_expert_sc, fonts_old_figures,
-               nomath, fonts_typewriter_scale);
+               nomath, fontsTypewriterScale());
 
        // MATH
-       os << theLaTeXFonts().getLaTeXFont(from_ascii(fonts_math)).getLaTeXCode(
+       os << theLaTeXFonts().getLaTeXFont(from_ascii(fontsMath())).getLaTeXCode(
                dryrun, ot1, complete, fonts_expert_sc, fonts_old_figures,
                nomath);
 
@@ -2888,15 +3211,18 @@ string const BufferParams::loadFonts(LaTeXFeatures & features) const
 
 Encoding const & BufferParams::encoding() const
 {
-       // FIXME: actually, we should check for the flavor
-       // or runparams.isFullyUnicode() here:
-       // This check will not work with XeTeX/LuaTeX and tex fonts.
-       // Thus we have to reset the encoding in Buffer::makeLaTeXFile.
+       // Main encoding for LaTeX output.
+       // 
+       // Exception: XeTeX with 8-bit TeX fonts requires ASCII (see #9740).
+       // As the "flavor" is only known once export started, this
+       // cannot be handled here. Instead, runparams.encoding is set
+       // to ASCII in Buffer::makeLaTeXFile (for export)
+       // and Buffer::writeLaTeXSource (for preview).
        if (useNonTeXFonts)
-               return *(encodings.fromLaTeXName("utf8-plain"));
+               return *(encodings.fromLyXName("utf8-plain"));
        if (inputenc == "auto" || inputenc == "default")
                return *language->encoding();
-       Encoding const * const enc = encodings.fromLaTeXName(inputenc);
+       Encoding const * const enc = encodings.fromLyXName(inputenc);
        if (enc)
                return *enc;
        LYXERR0("Unknown inputenc value `" << inputenc
@@ -2976,4 +3302,10 @@ vector<CitationStyle> BufferParams::citeStyles() const
        return styles;
 }
 
+void BufferParams::invalidateConverterCache() const
+{
+       pimpl_->isExportCacheValid = false;
+       pimpl_->isViewCacheValid = false;
+}
+
 } // namespace lyx