#include "Author.h"
#include "LayoutFile.h"
#include "BranchList.h"
+#include "Buffer.h"
#include "buffer_funcs.h"
#include "Bullet.h"
#include "Color.h"
#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"
};
-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",
: pimpl_(new Impl)
{
setBaseClass(defaultBaseclass());
+ cite_engine_.push_back("basic");
+ cite_engine_type_ = ENGINE_TYPE_DEFAULT;
makeDocumentClass();
paragraph_separation = ParagraphIndentSeparation;
quotes_language = InsetQuotes::EnglishQuotes;
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;
+ track_changes = false;
+ output_changes = false;
use_default_options = true;
maintain_unincluded_children = false;
secnumdepth = 3;
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<string, string> const & BufferParams::auto_packages()
{
- // FIXME THREAD
- // It is extremely unlikely that there could be a problem here, but...
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["amsmath"] =
N_("The LaTeX package amsmath is only used if AMS formula types or symbols from the AMS math toolbars are inserted into formulas");
string BufferParams::readToken(Lexer & lex, string const & token,
FileName const & filepath)
{
+ string result;
+
if (token == "\\textclass") {
lex.next();
string const classname = lex.getString();
// 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()) {
"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 == "\\origin") {
+ lex.eatLine();
+ origin = lex.getString();
+ string const sysdirprefix = "/systemlyxdir/";
+ if (prefixIs(origin, sysdirprefix)) {
+ origin.replace(0, sysdirprefix.length() - 1,
+ package().system_support().absFileName());
}
} else if (token == "\\begin_preamble") {
readPreamble(lex);
} 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") {
} 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();
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
+ // the document directory
+ string filepath = buf->filePath();
+ string const sysdir = package().system_support().absFileName();
+ if (prefixIs(filepath, sysdir))
+ filepath.replace(0, sysdir.length(), "/systemlyxdir/");
+ else if (!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()) {
}
}
- os << "\\tracking_changes " << convert<string>(trackChanges) << '\n'
- << "\\output_changes " << convert<string>(outputChanges) << '\n'
+ os << "\\tracking_changes " << convert<string>(track_changes) << '\n'
+ << "\\output_changes " << convert<string>(output_changes) << '\n'
<< "\\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';
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");
}
// some languages are only available via polyglossia
- if (features.runparams().flavor == OutputParams::XETEX
+ if ((features.runparams().flavor == OutputParams::XETEX
+ || features.runparams().flavor == OutputParams::LUATEX)
&& (features.hasPolyglossiaExclusiveLanguages()
|| useNonTeXFonts))
features.require("polyglossia");
// 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";
<< 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<string> fontencs = font_encodings();
+ // get font encodings of secondary languages
+ features.getFontEncodings(fontencs);
+ if (!fontencs.empty()) {
+ os << "\\usepackage["
+ << from_ascii(getStringFromVector(fontencs))
<< "]{fontenc}\n";
}
}
lyxpreamble += "\\synctex=-1\n";
}
+ // The package options (via \PassOptionsToPackage)
+ lyxpreamble += 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
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';
+ "User specified LaTeX commands.\n";
+
+ // Check if the user preamble contains uncodable glyphs
+ docstring const u_preamble = from_utf8(preamble);
+ odocstringstream user_preamble;
+ docstring uncodable_glyphs;
+ Encoding const * const enc = features.runparams().encoding;
+ if (enc) {
+ for (size_t n = 0; n < u_preamble.size(); ++n) {
+ char_type c = u_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 << u_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
+ atlyxpreamble + "\\makeatother\n\n";
// We try to load babel late, in case it interferes with other packages.
- // Jurabib, hyperref, varioref and listings (bug 8995) have to be
+ // 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.needBabelLangOptions())) + '\n';
lyxpreamble += from_utf8(features.getBabelPostsettings());
}
+ if (features.isRequired("bicaption"))
+ lyxpreamble += "\\usepackage{bicaption}\n";
if (!listings_params.empty() || features.isRequired("listings"))
lyxpreamble += "\\usepackage{listings}\n";
if (!listings_params.empty()) {
string format = documentClass().outputFormat();
if (format == "latex") {
if (useNonTeXFonts)
- return "xetex";
+ return "xetex"; // FIXME: why not "luatex"?
if (encoding().package() == Encoding::japanese)
return "platex";
}
}
-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;
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")
}
-InsetQuotes::QuoteLanguage BufferParams::getQuoteStyle(string const qs) const
+InsetQuotes::QuoteLanguage BufferParams::getQuoteStyle(string const & qs) const
{
return quoteslangtranslator().find(qs);
}
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;
}
void BufferParams::writeEncodingPreamble(otexstream & os,
LaTeXFeatures & features) const
{
- // XeTeX does not need this
- if (features.runparams().flavor == OutputParams::XETEX)
+ // "inputenc" package not required with non-TeX fonts.
+ if (useNonTeXFonts)
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";
- }
+ // "inputenc" fails with XeTeX (even in 8-bit compatiblitly mode) and with TeX fonts,
+ // (this is a bug in the "inputenc" package see #9740).
+ if (features.runparams().flavor == OutputParams::XETEX)
return;
- }
+ // For LuaTeX with TeX fonts, we can load
+ // the "luainputenc" package with the specified encoding(s) (see below).
+
if (inputenc == "auto") {
string const doc_encoding =
language->encoding()->latexName();
// 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();
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"
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"
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.
+ // FIXME: additionally, we must check for runparams().flavor == XeTeX
+ // or runparams.isFullUnicode() to care for the combination
+ // of XeTeX and TeX-fonts (see #9740).
+ // Currently, we reset the encoding in Buffer::makeLaTeXFile
+ // (for export) and Buffer::writeLaTeXSource (for preview).
if (useNonTeXFonts)
return *(encodings.fromLyXName("utf8-plain"));
if (inputenc == "auto" || inputenc == "default")