X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FBufferParams.cpp;h=50870c57b2e826065de252e82530658e374b7e26;hb=e335c764b47f8154973be212b446d9c13972cdf3;hp=55c2fa1c98ff982a15b7c6e31d4bdae241b44e4d;hpb=d1a77eadc1cf16ee3e0bd5008c6a8f74f3087442;p=lyx.git diff --git a/src/BufferParams.cpp b/src/BufferParams.cpp index 55c2fa1c98..50870c57b2 100644 --- a/src/BufferParams.cpp +++ b/src/BufferParams.cpp @@ -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" @@ -87,11 +91,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,7 +121,8 @@ ParSepTranslator const init_parseptranslator() ParSepTranslator const & parseptranslator() { - static ParSepTranslator translator = init_parseptranslator(); + static ParSepTranslator const translator = + init_parseptranslator(); return translator; } @@ -146,7 +146,8 @@ QuotesLangTranslator const init_quoteslangtranslator() QuotesLangTranslator const & quoteslangtranslator() { - static QuotesLangTranslator translator = init_quoteslangtranslator(); + static QuotesLangTranslator const translator = + init_quoteslangtranslator(); return translator; } @@ -196,7 +197,8 @@ static PaperSizeTranslator initPaperSizeTranslator() PaperSizeTranslator const & papersizetranslator() { - static PaperSizeTranslator translator = initPaperSizeTranslator(); + static PaperSizeTranslator const translator = + initPaperSizeTranslator(); return translator; } @@ -215,7 +217,8 @@ PaperOrientationTranslator const init_paperorientationtranslator() PaperOrientationTranslator const & paperorientationtranslator() { - static PaperOrientationTranslator translator = init_paperorientationtranslator(); + static PaperOrientationTranslator const translator = + init_paperorientationtranslator(); return translator; } @@ -234,7 +237,7 @@ SidesTranslator const init_sidestranslator() SidesTranslator const & sidestranslator() { - static SidesTranslator translator = init_sidestranslator(); + static SidesTranslator const translator = init_sidestranslator(); return translator; } @@ -254,7 +257,8 @@ PackageTranslator const init_packagetranslator() PackageTranslator const & packagetranslator() { - static PackageTranslator translator = init_packagetranslator(); + static PackageTranslator const translator = + init_packagetranslator(); return translator; } @@ -274,7 +278,8 @@ CiteEngineTypeTranslator const init_citeenginetypetranslator() CiteEngineTypeTranslator const & citeenginetypetranslator() { - static CiteEngineTypeTranslator translator = init_citeenginetypetranslator(); + static CiteEngineTypeTranslator const translator = + init_citeenginetypetranslator(); return translator; } @@ -296,10 +301,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 @@ -321,11 +355,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 @@ -351,6 +390,8 @@ 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; @@ -360,29 +401,35 @@ BufferParams::BufferParams() papersize = PAPER_DEFAULT; orientation = ORIENTATION_PORTRAIT; use_geometry = false; - cite_engine_.push_back("basic"); - cite_engine_type_ = ENGINE_TYPE_DEFAULT; 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,9 +462,13 @@ 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; } @@ -447,6 +498,14 @@ map const & BufferParams::auto_packages() { static map 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["amsmath"] = N_("The LaTeX package amsmath is only used if AMS formula types or symbols from the AMS math toolbars are inserted into formulas"); @@ -485,6 +544,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; @@ -590,6 +655,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(); @@ -598,13 +665,31 @@ string BufferParams::readToken(Lexer & lex, string const & token, // be available. string tcp; LayoutFileList & bcl = LayoutFileList::get(); - if (!filepath.empty()) - tcp = bcl.addLocalLayout(classname, filepath.absFileName()); + 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()) - setBaseClass(tcp); - else - setBaseClass(classname); + 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()) { @@ -623,7 +708,21 @@ 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); @@ -647,6 +746,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") { @@ -672,17 +784,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") { @@ -692,11 +804,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; @@ -744,9 +860,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(); @@ -814,7 +930,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; @@ -925,24 +1041,55 @@ string BufferParams::readToken(Lexer & lex, string const & token, return token; } - return string(); + return result; } -void BufferParams::writeFile(ostream & os) const +namespace { + // Quote argument if it contains spaces + string quoteIfNeeded(string const & str) { + if (contains(str, ' ')) + return "\"" + str + "\""; + return str; + } +} + + +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(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"; } @@ -993,20 +1140,20 @@ void BufferParams::writeFile(ostream & os) const << convert(maintain_unincluded_children) << '\n'; // local layout information - string const local_layout = getLocalLayout(false); + 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"; } - string const forced_local_layout = getLocalLayout(true); + docstring const forced_local_layout = getLocalLayout(true); if (!forced_local_layout.empty()) { // remove '\n' from the end - string const tmplocal = rtrim(forced_local_layout, "\n"); + docstring const tmplocal = rtrim(forced_local_layout, "\n"); os << "\\begin_forced_local_layout\n" - << tmplocal + << to_utf8(tmplocal) << "\n\\end_forced_local_layout\n"; } @@ -1016,20 +1163,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(useNonTeXFonts) << "\n\\font_sc " << convert(fonts_expert_sc) << "\n\\font_osf " << convert(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(use_microtype) << '\n'; os << "\\graphics " << graphics_driver << '\n'; os << "\\default_output_format " << default_output_format << '\n'; os << "\\output_sync " << output_sync << '\n'; @@ -1170,9 +1324,15 @@ void BufferParams::writeFile(ostream & os) const } } - os << "\\tracking_changes " << convert(trackChanges) << '\n' - << "\\output_changes " << convert(outputChanges) << '\n' - << "\\html_math_output " << html_math_output << '\n' + os << "\\tracking_changes " + << (save_transient_properties ? convert(track_changes) : "false") + << '\n'; + + os << "\\output_changes " + << (save_transient_properties ? convert(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(html_be_strict) << '\n'; @@ -1194,7 +1354,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"); @@ -1280,15 +1440,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)) - features.require("polyglossia"); + 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()); @@ -1306,6 +1476,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"; @@ -1449,13 +1624,14 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features, string const ams = features.loadAMSPackages(); bool const ot1 = (font_encoding() == "default" || font_encoding() == "OT1"); bool const use_newtxmath = - theLaTeXFonts().getLaTeXFont(from_ascii(fonts_math)).getUsedPackage( + 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"; @@ -1471,32 +1647,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")) { - docstring extra_encoding; - if (features.mustProvide("textgreek")) - extra_encoding += from_ascii("LGR"); - if (features.mustProvide("textcyr")) { - if (!extra_encoding.empty()) - extra_encoding.push_back(','); - extra_encoding += from_ascii("T2A"); - } - if (!extra_encoding.empty() && !font_encoding().empty()) - extra_encoding.push_back(','); - 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[" << extra_encoding - << from_ascii(font_encoding()) - << ",LFE,LAE]{fontenc}\n"; - } else { - os << "\\usepackage[" << extra_encoding - << 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 fontencs = font_encodings(); + // get font encodings of secondary languages + features.getFontEncodings(fontencs); + if (!fontencs.empty()) { + os << "\\usepackage[" + << from_ascii(getStringFromVector(fontencs)) << "]{fontenc}\n"; } } @@ -1530,21 +1690,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; @@ -1759,25 +1904,28 @@ 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, varioref or vietnamese, // we have to call babel before @@ -1787,15 +1935,15 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features, || 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")) { @@ -1813,16 +1961,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 @@ -1834,58 +1982,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 @@ -1897,11 +2092,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 @@ -1936,67 +2130,82 @@ 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, hyperref and varioref 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 const polylangs = features.getPolyglossiaLanguages(); for (std::map::const_iterator mit = polylangs.begin(); mit != polylangs.end() ; ++mit) { - lyxpreamble += "\\setotherlanguage"; + os << "\\setotherlanguage"; if (!mit->second.empty()) - lyxpreamble += "[" + from_ascii(mit->second) + "]"; - lyxpreamble += "{" + from_ascii(mit->first) + "}\n"; + os << "[" << from_ascii(mit->second) << "]"; + os << "{" << from_ascii(mit->first) << "}\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; } @@ -2033,7 +2242,7 @@ bool BufferParams::hasClassDefaults() const DocumentClass const & BufferParams::documentClass() const { - return *doc_class_.get(); + return *doc_class_; } @@ -2047,6 +2256,7 @@ void BufferParams::setDocumentClass(DocumentClassConstPtr tc) { // evil, but this function is evil doc_class_ = const_pointer_cast(tc); + invalidateConverterCache(); } @@ -2100,11 +2310,12 @@ LayoutFileIndex const & BufferParams::baseClassID() const } -void BufferParams::makeDocumentClass() +void BufferParams::makeDocumentClass(bool const clone) { if (!baseClass()) return; + invalidateConverterCache(); LayoutModuleList mods; LayoutModuleList::iterator it = layout_modules_.begin(); LayoutModuleList::iterator en = layout_modules_.end(); @@ -2116,14 +2327,15 @@ void BufferParams::makeDocumentClass() for (; it != en; ++it) mods.push_back(*it); - doc_class_ = getDocumentClass(*baseClass(), mods); + doc_class_ = getDocumentClass(*baseClass(), mods, clone); TextClass::ReturnValues success = TextClass::OK; if (!forced_local_layout_.empty()) - success = doc_class_->read(forced_local_layout_, TextClass::MODULE); + 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(local_layout_, TextClass::MODULE); + 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); @@ -2143,16 +2355,16 @@ bool BufferParams::citationModuleCanBeAdded(string const & modName) const } -std::string BufferParams::getLocalLayout(bool forced) const +docstring BufferParams::getLocalLayout(bool forced) const { if (forced) - return doc_class_->forcedLayouts(); + return from_utf8(doc_class_->forcedLayouts()); else return local_layout_; } -void BufferParams::setLocalLayout(string const & layout, bool forced) +void BufferParams::setLocalLayout(docstring const & layout, bool forced) { if (forced) forced_local_layout_ = layout; @@ -2175,60 +2387,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 backs = backends(); - for (vector::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; } -vector BufferParams::exportableFormats(bool only_viewable) const +FormatList const & BufferParams::exportableFormats(bool only_viewable) const { + FormatList & cached = only_viewable ? + pimpl_->viewableFormatList : pimpl_->exportableFormatList; + bool & valid = only_viewable ? + pimpl_->isViewCacheValid : pimpl_->isExportCacheValid; + if (valid) + return cached; + vector const backs = backends(); set excludes; if (useNonTeXFonts) { excludes.insert("latex"); excludes.insert("pdflatex"); } - vector result = + FormatList result = theConverters().getReachable(backs[0], only_viewable, true, excludes); for (vector::const_iterator it = backs.begin() + 1; it != backs.end(); ++it) { - vector 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()); } - return result; -} - - -bool BufferParams::isExportableFormat(string const & format) const -{ - typedef vector 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; } @@ -2239,17 +2441,14 @@ vector 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"); - } - v.push_back("luatex"); - v.push_back("dviluatex"); - v.push_back("xetex"); - } else if (buffmt == "xetex") { - v.push_back("xetex"); - // FIXME: need to test all languages (bug 8205) - if (!language || !language->isPolyglossiaExclusive()) { + 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"); } @@ -2263,7 +2462,7 @@ vector 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; @@ -2284,7 +2483,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") @@ -2321,7 +2520,7 @@ string BufferParams::getDefaultOutputFormat() const return default_output_format; if (isDocBook() || encoding().package() == Encoding::japanese) { - vector const formats = exportableFormats(true); + FormatList const & formats = exportableFormats(true); if (formats.empty()) return string(); // return the first we find @@ -2345,7 +2544,7 @@ Font const BufferParams::getFont() const } -InsetQuotes::QuoteLanguage BufferParams::getQuoteStyle(string const qs) const +InsetQuotes::QuoteLanguage BufferParams::getQuoteStyle(string const & qs) const { return quoteslangtranslator().find(qs); } @@ -2375,7 +2574,7 @@ 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")); } @@ -2389,9 +2588,9 @@ void BufferParams::readLocalLayout(Lexer & lex, bool forced) if (forced) forced_local_layout_ = - lex.getLongString("\\end_forced_local_layout"); + lex.getLongString(from_ascii("\\end_forced_local_layout")); else - local_layout_ = lex.getLongString("\\end_local_layout"); + local_layout_ = lex.getLongString(from_ascii("\\end_local_layout")); } @@ -2733,7 +2932,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 const BufferParams::font_encodings() const +{ + string doc_fontenc = (fontenc == "global") ? lyxrc.fontenc : fontenc; + + vector 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 fencs = getVectorFromString(language->fontenc()); + vector::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; } @@ -2774,36 +2997,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 encodings = - features.getEncodingSet(doc_encoding); + // Create list of inputenc options: + set 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::const_iterator it = encodings.begin(); set::const_iterator const end = encodings.end(); @@ -2818,7 +3038,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" @@ -2834,10 +3058,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(encoding().latexName()) - << "]{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" @@ -2847,6 +3077,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"; + } } } @@ -2864,9 +3103,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(); @@ -2895,28 +3134,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 @@ -2929,26 +3168,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); @@ -2958,10 +3197,13 @@ 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.fromLyXName("utf8-plain")); if (inputenc == "auto" || inputenc == "default") @@ -3046,4 +3288,10 @@ vector BufferParams::citeStyles() const return styles; } +void BufferParams::invalidateConverterCache() const +{ + pimpl_->isExportCacheValid = false; + pimpl_->isViewCacheValid = false; +} + } // namespace lyx