#include "MetricsInfo.h"
#include "Mover.h"
#include "OutputParams.h"
+#include "output_xhtml.h"
#include "sgml.h"
+#include "texstream.h"
#include "TocBackend.h"
#include "frontends/alert.h"
#include "support/os.h"
#include "support/Systemcall.h"
-#include <boost/tuple/tuple.hpp>
-
#include <algorithm>
#include <sstream>
+#include <tuple>
using namespace std;
using namespace lyx::support;
/// Note that \p format may be unknown (i. e. an empty string)
string findTargetFormat(string const & format, OutputParams const & runparams)
{
- // Are we using latex or XeTeX/pdflatex?
+ // Are we using latex or XeTeX/LuaTeX/pdflatex?
if (runparams.flavor == OutputParams::PDFLATEX
- || runparams.flavor == OutputParams::XETEX) {
+ || runparams.flavor == OutputParams::XETEX
+ || runparams.flavor == OutputParams::LUATEX) {
LYXERR(Debug::GRAPHICS, "findTargetFormat: PDF mode");
Format const * const f = formats.getFormat(format);
// Convert vector graphics to pdf
if (f && f->vectorFormat())
- return "pdf";
+ return "pdf6";
// pdflatex can use jpeg, png and pdf directly
if (format == "jpg")
return format;
}
// for HTML, we leave the known formats and otherwise convert to png
if (runparams.flavor == OutputParams::HTML) {
+ Format const * const f = formats.getFormat(format);
+ // Convert vector graphics to svg
+ if (f && f->vectorFormat() && theConverters().isReachable(format, "svg"))
+ return "svg";
+ // Leave the known formats alone
if (format == "jpg" || format == "png" || format == "gif")
return format;
+ // Convert everything else to png
return "png";
}
// If it's postscript, we always do eps.
}
-void readInsetGraphics(Lexer & lex, string const & bufpath,
+void readInsetGraphics(Lexer & lex, Buffer const & buf, bool allowOrigin,
InsetGraphicsParams & params)
{
bool finished = false;
if (token == "\\end_inset") {
finished = true;
} else {
- if (!params.Read(lex, token, bufpath))
+ if (!params.Read(lex, token, buf, allowOrigin))
lyxerr << "Unknown token, "
<< token
<< ", skipping."
} // namespace anon
-InsetGraphics::InsetGraphics(Buffer & buf)
- : graphic_label(sgml::uniqueID(from_ascii("graph"))),
+InsetGraphics::InsetGraphics(Buffer * buf)
+ : Inset(buf), graphic_label(sgml::uniqueID(from_ascii("graph"))),
graphic_(new RenderGraphic(this))
{
- Inset::setBuffer(buf);
}
void InsetGraphics::doDispatch(Cursor & cur, FuncRequest & cmd)
{
- switch (cmd.action) {
+ switch (cmd.action()) {
case LFUN_INSET_EDIT: {
InsetGraphicsParams p = params();
if (!cmd.argument().empty())
}
case LFUN_INSET_MODIFY: {
+ if (cmd.getArg(0) != "graphics") {
+ Inset::doDispatch(cur, cmd);
+ break;
+ }
+
InsetGraphicsParams p;
string2params(to_utf8(cmd.argument()), buffer(), p);
if (p.filename.empty()) {
- cur.noUpdate();
+ cur.noScreenUpdate();
break;
}
+ cur.recordUndo();
setParams(p);
// if the inset is part of a graphics group, all the
// other members should be updated too.
break;
case LFUN_GRAPHICS_RELOAD:
+ params_.filename.refresh();
graphic_->reload();
break;
bool InsetGraphics::getStatus(Cursor & cur, FuncRequest const & cmd,
FuncStatus & flag) const
{
- switch (cmd.action) {
- case LFUN_INSET_EDIT:
+ switch (cmd.action()) {
case LFUN_INSET_MODIFY:
+ if (cmd.getArg(0) != "graphics")
+ return Inset::getStatus(cur, cmd, flag);
+ // fall through
+ case LFUN_INSET_EDIT:
case LFUN_INSET_DIALOG_UPDATE:
case LFUN_GRAPHICS_RELOAD:
flag.setEnabled(true);
{
lex.setContext("InsetGraphics::read");
//lex >> "Graphics";
- readInsetGraphics(lex, buffer().filePath(), params_);
+ readInsetGraphics(lex, buffer(), true, params_);
graphic_->update(params().as_grfxParams());
}
// stream since we might have a trailing comma that we would like to remove
// before writing it to the output stream.
ostringstream options;
- if (!params().bb.empty())
- options << "bb=" << rtrim(params().bb) << ',';
+ if (!params().bbox.empty())
+ options << "bb=" << params().bbox.xl.asLatexString() << ' '
+ << params().bbox.yb.asLatexString() << ' '
+ << params().bbox.xr.asLatexString() << ' '
+ << params().bbox.yt.asLatexString() << ',';
if (params().draft)
options << "draft,";
if (params().clip)
case Length::PPW: // Percent of PageWidth
case Length::PLW: // Percent of LineWidth
case Length::PTH: // Percent of TextHeight
- case Length::PPH: // Percent of Paper
+ case Length::PPH: // Percent of PaperHeight
// Sigh, this will go wrong.
result << len.value() << "%";
break;
if (!params().scale.empty() && !float_equal(scl, 0.0, 0.05)) {
if (!float_equal(scl, 100.0, 0.05))
options << " scale=\""
- << static_cast<int>( (scl) + 0.5 )
+ << support::iround(scl)
<< "\" ";
} else {
if (!params().width.zero()) {
LYXERR(Debug::GRAPHICS,
to_utf8(bformat(_("Could not copy the file\n%1$s\n"
"into the temporary directory."),
- from_utf8(file_in.absFilename()))));
+ from_utf8(file_in.absFileName()))));
}
GraphicsCopyStatus status = success ? SUCCESS : FAILURE;
pair<GraphicsCopyStatus, FileName> const
copyToDirIfNeeded(DocFileName const & file, string const & dir)
{
- string const file_in = file.absFilename();
+ string const file_in = file.absFileName();
string const only_path = onlyPath(file_in);
- if (rtrim(onlyPath(file_in) , "/") == rtrim(dir, "/"))
- return make_pair(IDENTICAL_PATHS, file_in);
+ if (rtrim(only_path, "/") == rtrim(dir, "/"))
+ return make_pair(IDENTICAL_PATHS, FileName(file_in));
- string mangled = file.mangledFilename();
- if (file.isZipped()) {
+ string mangled = file.mangledFileName();
+ if (formats.isZippedFile(file)) {
// We need to change _eps.gz to .eps.gz. The mangled name is
- // still unique because of the counter in mangledFilename().
- // We can't just call mangledFilename() with the zip
+ // still unique because of the counter in mangledFileName().
+ // We can't just call mangledFileName() with the zip
// extension removed, because base.eps and base.eps.gz may
// have different content but would get the same mangled
// name in this case.
- string const base = removeExtension(file.unzippedFilename());
+ string const base = removeExtension(file.unzippedFileName());
string::size_type const ext_len = file_in.length() - base.length();
mangled[mangled.length() - ext_len] = '.';
}
if (params().filename.empty())
return string();
- string const orig_file = params().filename.absFilename();
+ string const orig_file = params().filename.absFileName();
// this is for dryrun and display purposes, do not use latexFilename
- string const rel_file = params().filename.relFilename(buffer().filePath());
+ string const rel_file = params().filename.relFileName(buffer().filePath());
// previewing source code, no file copying or file format conversion
if (runparams.dryrun)
// We are not going to change the extension or using the name of the
// temporary file, the code is already complicated enough.
if (runparams.inComment || !params().filename.isReadableFile())
- return params().filename.outputFilename(masterBuffer->filePath());
+ return params().filename.outputFileName(masterBuffer->filePath());
// We place all temporary files in the master buffer's temp dir.
// This is possible because we use mangled file names.
// we move it to a temp dir or uncompress it.
FileName temp_file;
GraphicsCopyStatus status;
- boost::tie(status, temp_file) =
- copyToDirIfNeeded(params().filename, temp_path);
+ tie(status, temp_file) = copyToDirIfNeeded(params().filename, temp_path);
if (status == FAILURE)
return orig_file;
// "nice" means that the buffer is exported to LaTeX format but not
// run through the LaTeX compiler.
string output_file = runparams.nice ?
- params().filename.outputFilename(masterBuffer->filePath()) :
- onlyFilename(temp_file.absFilename());
-
- if (runparams.nice && !isValidLaTeXFilename(output_file)) {
- frontend::Alert::warning(_("Invalid filename"),
- _("The following filename is likely to cause trouble "
- "when running the exported file through LaTeX: ") +
- from_utf8(output_file));
+ params().filename.outputFileName(masterBuffer->filePath()) :
+ onlyFileName(temp_file.absFileName());
+
+ if (runparams.nice) {
+ if (!isValidLaTeXFileName(output_file)) {
+ frontend::Alert::warning(_("Invalid filename"),
+ _("The following filename will cause troubles "
+ "when running the exported file through LaTeX: ") +
+ from_utf8(output_file));
+ }
+ // only show DVI-specific warning when export format is plain latex
+ if (!isValidDVIFileName(output_file)
+ && runparams.flavor == OutputParams::LATEX) {
+ frontend::Alert::warning(_("Problematic filename for DVI"),
+ _("The following filename can cause troubles "
+ "when running the exported file through LaTeX "
+ "and opening the resulting DVI: ") +
+ from_utf8(output_file), true);
+ }
}
FileName source_file = runparams.nice ? FileName(params().filename) : temp_file;
- string const tex_format = (runparams.flavor == OutputParams::LATEX) ?
- "latex" : "pdflatex";
-
- // If the file is compressed and we have specified that it
- // should not be uncompressed, then just return its name and
- // let LaTeX do the rest!
- if (params().filename.isZipped()) {
- if (params().noUnzip) {
- // We don't know whether latex can actually handle
- // this file, but we can't check, because that would
- // mean to unzip the file and thereby making the
- // noUnzip parameter meaningless.
- LYXERR(Debug::GRAPHICS, "\tpass zipped file to LaTeX.");
-
- FileName const bb_orig_file =
- FileName(changeExtension(orig_file, "bb"));
- if (runparams.nice) {
- runparams.exportdata->addExternalFile(tex_format,
- bb_orig_file,
- changeExtension(output_file, "bb"));
- } else {
- // LaTeX needs the bounding box file in the
- // tmp dir
- FileName bb_file =
- FileName(changeExtension(temp_file.absFilename(), "bb"));
- boost::tie(status, bb_file) =
- copyFileIfNeeded(bb_orig_file, bb_file);
- if (status == FAILURE)
- return orig_file;
- runparams.exportdata->addExternalFile(tex_format,
- bb_file);
- }
- runparams.exportdata->addExternalFile(tex_format,
- source_file, output_file);
- runparams.exportdata->addExternalFile("dvi",
- source_file, output_file);
- // We can't strip the extension, because we don't know
- // the unzipped file format
- return latex_path(output_file, EXCLUDE_EXTENSION);
- }
+ // determine the export format
+ string const tex_format = flavor2format(runparams.flavor);
+ if (formats.isZippedFile(params().filename)) {
FileName const unzipped_temp_file =
- FileName(unzippedFileName(temp_file.absFilename()));
+ FileName(unzippedFileName(temp_file.absFileName()));
output_file = unzippedFileName(output_file);
- source_file = FileName(unzippedFileName(source_file.absFilename()));
+ source_file = FileName(unzippedFileName(source_file.absFileName()));
if (compare_timestamps(unzipped_temp_file, temp_file) > 0) {
// temp_file has been unzipped already and
// orig_file has not changed in the meantime.
// the file format from the extension, so we must
// change it.
FileName const new_file =
- FileName(changeExtension(temp_file.absFilename(), ext));
+ FileName(changeExtension(temp_file.absFileName(), ext));
if (temp_file.moveTo(new_file)) {
temp_file = new_file;
output_file = changeExtension(output_file, ext);
source_file =
- FileName(changeExtension(source_file.absFilename(), ext));
+ FileName(changeExtension(source_file.absFileName(), ext));
} else {
LYXERR(Debug::GRAPHICS, "Could not rename file `"
<< temp_file << "' to `" << new_file << "'.");
}
// so the source and destination formats are different
- FileName const to_file = FileName(changeExtension(temp_file.absFilename(), ext));
+ FileName const to_file = FileName(changeExtension(temp_file.absFileName(), ext));
string const output_to_file = changeExtension(output_file, ext);
// Do we need to perform the conversion?
}
-int InsetGraphics::latex(odocstream & os,
- OutputParams const & runparams) const
+void InsetGraphics::latex(otexstream & os,
+ OutputParams const & runparams) const
{
// If there is no file specified or not existing,
// just output a message about it in the latex output.
LYXERR(Debug::GRAPHICS, "insetgraphics::latex: Filename = "
- << params().filename.absFilename());
+ << params().filename.absFileName());
bool const file_exists = !params().filename.empty()
&& params().filename.isReadableFile();
- string const message = file_exists ?
- string() : string("bb = 0 0 200 100, draft, type=eps");
- // if !message.empty() then there was no existing file
- // "filename" found. In this case LaTeX
+ string message;
+ if (!file_exists) {
+ if (params().bbox.empty())
+ message = "bb = 0 0 200 100";
+ if (!params().draft) {
+ if (!message.empty())
+ message += ", ";
+ message += "draft";
+ }
+ if (!message.empty())
+ message += ", ";
+ message += "type=eps";
+ }
+ // If no existing file "filename" was found LaTeX
// draws only a rectangle with the above bb and the
// not found filename in it.
LYXERR(Debug::GRAPHICS, "\tMessage = \"" << message << '\"');
// Convert the file if necessary.
// Remove the extension so LaTeX will use whatever is appropriate
// (when there are several versions in different formats)
- latex_str += prepareFile(runparams);
+ string file_path = prepareFile(runparams);
+ latex_str += file_path;
latex_str += '}' + after;
// FIXME UNICODE
os << from_utf8(latex_str);
LYXERR(Debug::GRAPHICS, "InsetGraphics::latex outputting:\n" << latex_str);
- // Return how many newlines we issued.
- return int(count(latex_str.begin(), latex_str.end(),'\n'));
}
-int InsetGraphics::plaintext(odocstream & os, OutputParams const &) const
+int InsetGraphics::plaintext(odocstringstream & os,
+ OutputParams const &, size_t) const
{
// No graphics in ascii output. Possible to use gifscii to convert
// images to ascii approximation.
// FIXME: We have no idea what the encoding of the filename is
docstring const str = bformat(buffer().B_("Graphics file: %1$s"),
- from_utf8(params().filename.absFilename()));
+ from_utf8(params().filename.absFileName()));
os << '<' << str << '>';
return 2 + str.size();
}
+string InsetGraphics::prepareHTMLFile(OutputParams const & runparams) const
+{
+ // The following code depends on non-empty filenames
+ if (params().filename.empty())
+ return string();
+
+ if (!params().filename.isReadableFile())
+ return string();
+
+ // The master buffer. This is useful when there are multiple levels
+ // of include files
+ Buffer const * masterBuffer = buffer().masterBuffer();
+
+ // We place all temporary files in the master buffer's temp dir.
+ // This is possible because we use mangled file names.
+ // FIXME We may want to put these files in some special temporary
+ // directory.
+ string const temp_path = masterBuffer->temppath();
+
+ // Copy to temporary directory.
+ FileName temp_file;
+ GraphicsCopyStatus status;
+ tie(status, temp_file) = copyToDirIfNeeded(params().filename, temp_path);
+
+ if (status == FAILURE)
+ return string();
+
+ string const from = formats.getFormatFromFile(temp_file);
+ if (from.empty()) {
+ LYXERR(Debug::GRAPHICS, "\tCould not get file format.");
+ return string();
+ }
+
+ string const to = findTargetFormat(from, runparams);
+ string const ext = formats.extension(to);
+ string const orig_file = params().filename.absFileName();
+ string output_file = onlyFileName(temp_file.absFileName());
+ LYXERR(Debug::GRAPHICS, "\t we have: from " << from << " to " << to);
+ LYXERR(Debug::GRAPHICS, "\tthe orig file is: " << orig_file);
+
+ if (from == to) {
+ // source and destination formats are the same
+ runparams.exportdata->addExternalFile("xhtml", temp_file, output_file);
+ return output_file;
+ }
+
+ // so the source and destination formats are different
+ FileName const to_file = FileName(changeExtension(temp_file.absFileName(), ext));
+ string const output_to_file = changeExtension(output_file, ext);
+
+ // Do we need to perform the conversion?
+ // Yes if to_file does not exist or if temp_file is newer than to_file
+ if (compare_timestamps(temp_file, to_file) < 0) {
+ // FIXME UNICODE
+ LYXERR(Debug::GRAPHICS,
+ to_utf8(bformat(_("No conversion of %1$s is needed after all"),
+ from_utf8(orig_file))));
+ runparams.exportdata->addExternalFile("xhtml", to_file, output_to_file);
+ return output_to_file;
+ }
+
+ LYXERR(Debug::GRAPHICS,"\tThe original file is " << orig_file << "\n"
+ << "\tA copy has been made and convert is to be called with:\n"
+ << "\tfile to convert = " << temp_file << '\n'
+ << "\t from " << from << " to " << to);
+
+ // FIXME (Abdel 12/08/06): Is there a need to show these errors?
+ ErrorList el;
+ bool const success =
+ theConverters().convert(&buffer(), temp_file, to_file, params().filename,
+ from, to, el, Converters::try_default | Converters::try_cache);
+ if (!success)
+ return string();
+ runparams.exportdata->addExternalFile("xhtml", to_file, output_to_file);
+ return output_to_file;
+}
+
+
+docstring InsetGraphics::xhtml(XHTMLStream & xs, OutputParams const & op) const
+{
+ string const output_file = prepareHTMLFile(op);
+
+ if (output_file.empty()) {
+ LYXERR0("InsetGraphics::xhtml: Unable to prepare file `"
+ << params().filename << "' for output. File missing?");
+ string const attr = "src='" + params().filename.absFileName()
+ + "' alt='image: " + output_file + "'";
+ xs << html::CompTag("img", attr);
+ return docstring();
+ }
+
+ // FIXME XHTML
+ // We aren't doing anything with the crop and rotate parameters, and it would
+ // really be better to do width and height conversion, rather than to output
+ // these parameters here.
+ string imgstyle;
+ bool const havewidth = !params().width.zero();
+ bool const haveheight = !params().height.zero();
+ if (havewidth || haveheight) {
+ if (havewidth)
+ imgstyle += "width:" + params().width.asHTMLString() + ";";
+ if (haveheight)
+ imgstyle += " height:" + params().height.asHTMLString() + ";";
+ } else if (params().scale != "100") {
+ // Note that this will not have the same effect as in LaTeX export:
+ // There, the image will be scaled from its original size. Here, the
+ // percentage will be interpreted by the browser, and the image will
+ // be scaled to a percentage of the window size.
+ imgstyle = "width:" + params().scale + "%;";
+ }
+ if (!imgstyle.empty())
+ imgstyle = "style='" + imgstyle + "' ";
+
+ string const attr = imgstyle + "src='" + output_file + "' alt='image: "
+ + output_file + "'";
+ xs << html::CompTag("img", attr);
+ return docstring();
+}
+
+
void InsetGraphics::validate(LaTeXFeatures & features) const
{
// If we have no image, we should not require anything.
return;
features.includeFile(graphic_label,
- removeExtension(params().filename.absFilename()));
+ removeExtension(params().filename.absFileName()));
features.require("graphicx");
if (features.runparams().nice) {
- Buffer const * masterBuffer = features.buffer().masterBuffer();
- string const rel_file = removeExtension(
- params().filename.relFilename(masterBuffer->filePath()));
+ string const rel_file = params().filename.onlyFileNameWithoutExt();
if (contains(rel_file, "."))
features.require("lyxdot");
}
}
-void InsetGraphics::addToToc(DocIterator const & cpit)
+void InsetGraphics::addToToc(DocIterator const & cpit, bool output_active,
+ UpdateType) const
{
- TocBackend & backend = buffer().tocBackend();
-
//FIXME UNICODE
docstring const str = from_utf8(params_.filename.onlyFileName());
- backend.toc("graphics").push_back(TocItem(cpit, 0, str));
+ buffer().tocBackend().toc("graphics")->push_back(TocItem(cpit, 0, str, output_active));
}
-docstring InsetGraphics::contextMenu(BufferView const &, int, int) const
+string InsetGraphics::contextMenuName() const
{
- return from_ascii("context-graphics");
+ return "context-graphics";
}
lex.setContext("InsetGraphics::string2params");
lex >> "graphics";
params = InsetGraphicsParams();
- readInsetGraphics(lex, buffer.filePath(), params);
+ readInsetGraphics(lex, buffer, false, params);
}
return data.str();
}
+
+docstring InsetGraphics::toolTip(BufferView const &, int, int) const
+{
+ return from_utf8(params().filename.onlyFileName());
+}
+
namespace graphics {
void getGraphicsGroups(Buffer const & b, set<string> & ids)
InsetGraphics & ins = static_cast<InsetGraphics &>(*it);
InsetGraphicsParams inspar = ins.getParams();
if (params.groupId == inspar.groupId) {
- b.undo().recordUndo(it);
+ b.undo().recordUndo(CursorData(it));
params.filename = inspar.filename;
ins.setParams(params);
}