lyx_mirror/src/insets/insetgraphics.C

1051 lines
28 KiB
C++
Raw Normal View History

/* This file is part of
* ======================================================
*
* LyX, The Document Processor
*
* Copyright 1995-2002 the LyX Team.
*
* \author Baruch Even
* \author Herbert Voss <voss@lyx.org>
* ====================================================== */
/*
Known BUGS:
* If the image is from the clipart, and the document is moved to another
directory, the user is screwed. Need a way to handle it.
This amounts to a problem of when to use relative or absolute file paths
We should probably use what the user asks to use... but when he chooses
by the file dialog we normally get an absolute path and this may not be
what the user meant.
Note that browseRelFile in helper_funcs.* provides a file name
which is relative if it is at reference path (here puffer path)
level or below, and an absolute path if the file name is not a
`natural' relative file name. In any case,
MakeAbsPath(filename, buf->filePath())
is guaranteed to provide the correct absolute path. This is what is
done know for include insets. Feel free to ask me -- JMarc
14/01/2002
TODO Before initial production release:
* What advanced features the users want to do?
Implement them in a non latex dependent way, but a logical way.
LyX should translate it to latex or any other fitting format.
* Add a way to roll the image file into the file format.
* When loading, if the image is not found in the expected place, try
to find it in the clipart, or in the same directory with the image.
* Keep a tab on the image file, if it changes, update the lyx view.
* The image choosing dialog could show thumbnails of the image formats
it knows of, thus selection based on the image instead of based on
filename.
* Add support for the 'picins' package.
* Add support for the 'picinpar' package.
* Improve support for 'subfigure' - Allow to set the various options
that are possible.
*/
/* NOTES:
* Fileformat:
* Current version is 1 (inset file format version), when changing it
* it should be changed in the Write() function when writing in one place
* and when reading one should change the version check and the error message.
* The filename is kept in the lyx file in a relative way, so as to allow
* moving the document file and its images with no problem.
*
*
* Conversions:
* Postscript output means EPS figures.
*
* PDF output is best done with PDF figures if it's a direct conversion
* or PNG figures otherwise.
* Image format
* from to
* EPS epstopdf
* PS ps2pdf
* JPG/PNG direct
* PDF direct
* others PNG
*/
#include <config.h>
#ifdef __GNUG__
#pragma implementation
#endif
#include "insets/insetgraphics.h"
#include "insets/insetgraphicsParams.h"
#include "graphics/GraphicsCache.h"
#include "graphics/GraphicsCacheItem.h"
#include "graphics/GraphicsImage.h"
#include "frontends/LyXView.h"
#include "lyxtext.h"
#include "buffer.h"
#include "BufferView.h"
#include "converter.h"
#include "frontends/Painter.h"
#include "lyxrc.h"
#include "frontends/font_metrics.h"
#include "debug.h"
#include "gettext.h"
#include "LaTeXFeatures.h"
#include "frontends/Dialogs.h"
#include "frontends/Alert.h"
#include "frontends/controllers/helper_funcs.h" // getVectorFromString
#include "support/LAssert.h"
#include "support/filetools.h"
#include "support/lyxalgo.h" // lyx::count
#include "support/path.h"
#include "support/systemcall.h"
#include "support/os.h"
#include <boost/bind.hpp>
#include <algorithm> // For the std::max
// Very, Very UGLY!
extern BufferView * current_view;
extern string system_tempdir;
using std::ostream;
using std::endl;
///////////////////////////////////////////////////////////////////////////
int const VersionNumber = 1;
///////////////////////////////////////////////////////////////////////////
namespace {
// This function is a utility function
// ... that should be with ChangeExtension ...
inline
string const RemoveExtension(string const & filename)
{
return ChangeExtension(filename, string());
}
} // namespace anon
namespace {
string const uniqueID()
{
static unsigned int seed = 1000;
ostringstream ost;
ost << "graph" << ++seed;
// Needed if we use lyxstring.
return ost.str().c_str();
}
} // namespace anon
struct InsetGraphics::Cache
{
///
Cache(InsetGraphics &);
///
~Cache();
///
void reset(grfx::GraphicPtr const &);
///
bool empty() const { return !graphic_.get(); }
///
grfx::ImageStatus status() const;
///
void setStatus(grfx::ImageStatus);
///
grfx::GImage * image() const;
///
string const filename() const;
///
void update(string const & file_with_path);
///
void modify();
///
int old_ascent;
///
grfx::GraphicPtr graphic_;
private:
/// The connection to cache_->statusChanged.
boost::signals::connection cc_;
///
grfx::ImageStatus status_;
///
grfx::GParams params_;
///
grfx::ImagePtr modified_image_;
///
InsetGraphics & parent_;
};
InsetGraphics::Cache::Cache(InsetGraphics & p)
: old_ascent(0), status_(grfx::ErrorUnknown), parent_(p)
{}
InsetGraphics::Cache::~Cache()
{
string const old_file = filename();
graphic_.reset();
if (!old_file.empty()) {
grfx::GCache & gc = grfx::GCache::get();
gc.remove(old_file);
}
}
void InsetGraphics::Cache::reset(grfx::GraphicPtr const & graphic)
{
string const old_file = filename();
string const new_file = graphic.get() ? graphic->filename() : string();
if (old_file == new_file)
return;
graphic_ = graphic;
if (!old_file.empty()) {
grfx::GCache & gc = grfx::GCache::get();
gc.remove(old_file);
}
}
grfx::ImageStatus InsetGraphics::Cache::status() const
{
return status_;
}
void InsetGraphics::Cache::setStatus(grfx::ImageStatus new_status)
{
status_ = new_status;
}
grfx::GImage * InsetGraphics::Cache::image() const
{
return modified_image_.get();
}
string const InsetGraphics::Cache::filename() const
{
return empty() ? string() : graphic_->filename();
}
void InsetGraphics::Cache::update(string const & file_with_path)
{
lyx::Assert(!file_with_path.empty());
// Check whether the file has changed.
string current_file = filename();
if (current_file == file_with_path) {
modify();
return;
}
// It /has/ changed.
// Remove the connection to any previous grfx::CacheItems
grfx::GCache & gc = grfx::GCache::get();
if (!current_file.empty() && gc.inCache(current_file)) {
graphic_.reset();
gc.remove(current_file);
cc_.disconnect();
}
// Update the cache to point to the new file
if (!gc.inCache(file_with_path))
gc.add(file_with_path);
graphic_ = gc.graphic(file_with_path);
cc_ = graphic_->statusChanged.connect(
boost::bind(&InsetGraphics::statusChanged, &parent_));
setStatus(graphic_->status());
if (status() == grfx::Loaded)
modify();
}
void InsetGraphics::Cache::modify()
{
// The image has not been loaded from file
if (!graphic_->image().get())
return;
string const path = OnlyPath(filename());
grfx::GParams params = parent_.params().asGParams(path);
if (params == params_)
return;
params_ = params;
setStatus(grfx::ScalingEtc);
modified_image_.reset(graphic_->image()->clone());
modified_image_->clip(params);
modified_image_->rotate(params);
modified_image_->scale(params);
bool const success = modified_image_->setPixmap(params);
if (success) {
setStatus(grfx::Loaded);
} else {
modified_image_.reset();
setStatus(grfx::ErrorScalingEtc);
}
}
InsetGraphics::InsetGraphics()
: graphic_label(uniqueID()),
cache_(new Cache(*this))
{}
InsetGraphics::InsetGraphics(InsetGraphics const & ig,
string const & filepath,
bool same_id)
: Inset(ig, same_id),
graphic_label(uniqueID()),
cache_(new Cache(*this))
{
setParams(ig.params(), filepath);
}
InsetGraphics::~InsetGraphics()
{
cache_->reset(grfx::GraphicPtr());
// Emits the hide signal to the dialog connected (if any)
hideDialog();
}
string const InsetGraphics::statusMessage() const
{
string msg;
switch (cache_->status()) {
case grfx::WaitingToLoad:
msg = _("Waiting for draw request to start loading...");
break;
case grfx::Loading:
msg = _("Loading...");
break;
case grfx::Converting:
msg = _("Converting to loadable format...");
break;
case grfx::ScalingEtc:
msg = _("Scaling etc...");
break;
case grfx::Loaded:
msg = _("Loaded.");
break;
case grfx::ErrorNoFile:
msg = _("No file found!");
break;
case grfx::ErrorLoading:
msg = _("Error loading file into memory");
break;
case grfx::ErrorConverting:
msg = _("Error converting to loadable format");
break;
case grfx::ErrorScalingEtc:
msg = _("Error scaling etc");
break;
case grfx::ErrorUnknown:
msg = _("No image");
break;
}
return msg;
}
bool InsetGraphics::imageIsDrawable() const
{
if (!cache_->image() || cache_->status() != grfx::Loaded)
return false;
return cache_->image()->getPixmap() != 0;
}
int InsetGraphics::ascent(BufferView *, LyXFont const &) const
{
cache_->old_ascent = 50;
if (imageIsDrawable())
cache_->old_ascent = cache_->image()->getHeight();
return cache_->old_ascent;
}
int InsetGraphics::descent(BufferView *, LyXFont const &) const
{
return 0;
}
int InsetGraphics::width(BufferView *, LyXFont const & font) const
{
if (imageIsDrawable())
return cache_->image()->getWidth();
else {
int font_width = 0;
LyXFont msgFont(font);
msgFont.setFamily(LyXFont::SANS_FAMILY);
string const justname = OnlyFilename (params().filename);
if (!justname.empty()) {
msgFont.setSize(LyXFont::SIZE_FOOTNOTE);
font_width = font_metrics::width(justname, msgFont);
}
string const msg = statusMessage();
if (!msg.empty()) {
msgFont.setSize(LyXFont::SIZE_TINY);
int const msg_width = font_metrics::width(msg, msgFont);
font_width = std::max(font_width, msg_width);
}
return std::max(50, font_width + 15);
}
}
void InsetGraphics::draw(BufferView * bv, LyXFont const & font,
int baseline, float & x, bool) const
{
int oasc = cache_->old_ascent;
int ldescent = descent(bv, font);
int lascent = ascent(bv, font);
int lwidth = width(bv, font);
// we may have changed while someone other was drawing us so better
// to not draw anything as we surely call to redraw ourself soon.
// This is not a nice thing to do and should be fixed properly somehow.
// But I still don't know the best way to go. So let's do this like this
// for now (Jug 20020311)
if (lascent != oasc) {
return;
}
// Make sure now that x is updated upon exit from this routine
int old_x = int(x);
x += lwidth;
if (cache_->status() == grfx::WaitingToLoad) {
cache_->graphic_->startLoading();
}
// This will draw the graphics. If the graphics has not been loaded yet,
// we draw just a rectangle.
Painter & paint = bv->painter();
if (imageIsDrawable()) {
paint.image(old_x + 2, baseline - lascent,
lwidth - 4, lascent + ldescent,
*cache_->image());
} else {
paint.rectangle(old_x + 2, baseline - lascent,
lwidth - 4,
lascent + ldescent);
// Print the file name.
LyXFont msgFont(font);
msgFont.setFamily(LyXFont::SANS_FAMILY);
string const justname = OnlyFilename (params().filename);
if (!justname.empty()) {
msgFont.setSize(LyXFont::SIZE_FOOTNOTE);
paint.text(old_x + 8,
baseline - font_metrics::maxAscent(msgFont) - 4,
justname, msgFont);
}
// Print the message.
string const msg = statusMessage();
if (!msg.empty()) {
msgFont.setSize(LyXFont::SIZE_TINY);
paint.text(old_x + 8, baseline - 4, msg, msgFont);
}
}
// the status message may mean we changed size, so indicate
// we need a row redraw
#if 0
if (old_status_ != grfx::ErrorUnknown && old_status_ != cached_status_) {
bv->getLyXText()->status(bv, LyXText::CHANGED_IN_DRAW);
}
#endif
// Reset the cache, ready for the next draw request
#if 0
cached_status_ = grfx::ErrorUnknown;
cached_image_.reset();
cache_filled_ = false;
#endif
}
void InsetGraphics::edit(BufferView *bv, int, int, mouse_button::state)
{
bv->owner()->getDialogs()->showGraphics(this);
}
void InsetGraphics::edit(BufferView * bv, bool)
{
edit(bv, 0, 0, mouse_button::none);
}
Inset::EDITABLE InsetGraphics::editable() const
{
return IS_EDITABLE;
}
void InsetGraphics::write(Buffer const *, ostream & os) const
{
os << "Graphics FormatVersion " << VersionNumber << '\n';
params().Write(os);
}
void InsetGraphics::read(Buffer const * buf, LyXLex & lex)
{
string const token = lex.getString();
if (token == "Graphics")
readInsetGraphics(lex);
else if (token == "Figure") // Compatibility reading of FigInset figures.
readFigInset(lex);
else
lyxerr[Debug::GRAPHICS] << "Not a Graphics or Figure inset!\n";
cache_->update(MakeAbsPath(params().filename, buf->filePath()));
}
void InsetGraphics::readInsetGraphics(LyXLex & lex)
{
bool finished = false;
while (lex.isOK() && !finished) {
lex.next();
string const token = lex.getString();
lyxerr[Debug::GRAPHICS] << "Token: '" << token << '\''
<< std::endl;
if (token.empty()) {
continue;
} else if (token == "\\end_inset") {
finished = true;
} else if (token == "FormatVersion") {
lex.next();
int version = lex.getInteger();
if (version > VersionNumber)
lyxerr
<< "This document was created with a newer Graphics widget"
", You should use a newer version of LyX to read this"
" file."
<< std::endl;
// TODO: Possibly open up a dialog?
}
else {
if (! params_.Read(lex, token))
lyxerr << "Unknown token, " << token << ", skipping."
<< std::endl;
}
}
}
// FormatVersion < 1.0 (LyX < 1.2)
void InsetGraphics::readFigInset(LyXLex & lex)
{
std::vector<string> const oldUnits =
getVectorFromString("pt,cm,in,p%,c%");
bool finished = false;
// set the display default
if (lyxrc.display_graphics == "mono")
params_.display = InsetGraphicsParams::MONOCHROME;
else if (lyxrc.display_graphics == "gray")
params_.display = InsetGraphicsParams::GRAYSCALE;
else if (lyxrc.display_graphics == "color")
params_.display = InsetGraphicsParams::COLOR;
else
params_.display = InsetGraphicsParams::NONE;
while (lex.isOK() && !finished) {
lex.next();
string const token = lex.getString();
lyxerr[Debug::GRAPHICS] << "Token: " << token << endl;
if (token.empty())
continue;
else if (token == "\\end_inset") {
finished = true;
} else if (token == "file") {
if (lex.next()) {
params_.filename = lex.getString();
}
} else if (token == "extra") {
if (lex.next());
// kept for backwards compability. Delete in 0.13.x
} else if (token == "subcaption") {
if (lex.eatLine())
params_.subcaptionText = lex.getString();
} else if (token == "label") {
if (lex.next());
// kept for backwards compability. Delete in 0.13.x
} else if (token == "angle") {
if (lex.next()) {
params_.rotate = true;
params_.rotateAngle = lex.getFloat();
}
} else if (token == "size") {
if (lex.next())
params_.lyxwidth = LyXLength(lex.getString()+"pt");
if (lex.next())
params_.lyxheight = LyXLength(lex.getString()+"pt");
params_.lyxsize_type = InsetGraphicsParams::WH;
} else if (token == "flags") {
if (lex.next())
switch (lex.getInteger()) {
case 1: params_.display = InsetGraphicsParams::MONOCHROME;
break;
case 2: params_.display = InsetGraphicsParams::GRAYSCALE;
break;
case 3: params_.display = InsetGraphicsParams::COLOR;
break;
}
} else if (token == "subfigure") {
params_.subcaption = true;
} else if (token == "width") {
if (lex.next()) {
int i = lex.getInteger();
if (lex.next()) {
if (i == 5) {
params_.scale = lex.getInteger();
params_.size_type = InsetGraphicsParams::SCALE;
} else {
params_.width = LyXLength(lex.getString()+oldUnits[i]);
params_.size_type = InsetGraphicsParams::WH;
}
}
}
} else if (token == "height") {
if (lex.next()) {
int i = lex.getInteger();
if (lex.next()) {
params_.height = LyXLength(lex.getString()+oldUnits[i]);
params_.size_type = InsetGraphicsParams::WH;
}
}
}
}
}
string const InsetGraphics::createLatexOptions() const
{
// Calculate the options part of the command, we must do it to a string
// 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=" << strip(params().bb) << ",\n";
if (params().draft)
options << " draft,\n";
if (params().clip)
options << " clip,\n";
if (params().size_type == InsetGraphicsParams::WH) {
if (!params().width.zero())
options << " width=" << params().width.asLatexString() << ",\n";
if (!params().height.zero())
options << " height=" << params().height.asLatexString() << ",\n";
} else if (params().size_type == InsetGraphicsParams::SCALE) {
if (params().scale > 0)
options << " scale=" << double(params().scale)/100.0 << ",\n";
}
if (params().keepAspectRatio)
options << " keepaspectratio,\n";
// Make sure it's not very close to zero, a float can be effectively
// zero but not exactly zero.
if (!lyx::float_equal(params().rotateAngle, 0, 0.001) && params().rotate) {
options << " angle=" << params().rotateAngle << ",\n";
if (!params().rotateOrigin.empty()) {
options << " origin=" << params().rotateOrigin[0];
if (contains(params().rotateOrigin,"Top"))
options << 't';
else if (contains(params().rotateOrigin,"Bottom"))
options << 'b';
else if (contains(params().rotateOrigin,"Baseline"))
options << 'B';
options << ",\n";
}
}
if (!params().special.empty())
options << params().special << ",\n";
string opts = options.str().c_str();
return opts.substr(0,opts.size()-2); // delete last ",\n"
}
namespace {
string findTargetFormat(string const & suffix)
{
// lyxrc.pdf_mode means:
// Are we creating a PDF or a PS file?
// (Should actually mean, are we using latex or pdflatex).
if (lyxrc.pdf_mode) {
lyxerr[Debug::GRAPHICS] << "findTargetFormat: PDF mode\n";
if (contains(suffix,"ps") || suffix == "pdf")
return "pdf";
else if (suffix == "jpg") // pdflatex can use jpeg
return suffix;
else
return "png"; // and also png
}
// If it's postscript, we always do eps.
lyxerr[Debug::GRAPHICS] << "findTargetFormat: PostScript mode\n";
if (suffix != "ps") // any other than ps
return "eps"; // is changed to eps
else
return suffix; // let ps untouched
}
} // Anon. namespace
string const InsetGraphics::prepareFile(Buffer const *buf) const
{
// LaTeX can cope if the graphics file doesn't exist, so just return the
// filename.
string const orig_file = params().filename;
string orig_file_with_path =
MakeAbsPath(orig_file, buf->filePath());
lyxerr[Debug::GRAPHICS] << "[InsetGraphics::prepareFile] orig_file = "
<< orig_file << "\n\twith path: "
<< orig_file_with_path << endl;
if (!IsFileReadable(orig_file_with_path))
return orig_file;
// 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!
// maybe that other zip extensions also be useful, especially the
// ones that may be declared in texmf/tex/latex/config/graphics.cfg.
// for example:
/* -----------snip-------------
{\DeclareGraphicsRule{.pz}{eps}{.bb}{}%
\DeclareGraphicsRule{.eps.Z}{eps}{.eps.bb}{}%
\DeclareGraphicsRule{.ps.Z}{eps}{.ps.bb}{}%
\DeclareGraphicsRule{.ps.gz}{eps}{.ps.bb}{}%
\DeclareGraphicsRule{.eps.gz}{eps}{.eps.bb}{}}}%
-----------snip-------------*/
bool const zipped = zippedFile(orig_file_with_path);
if (zipped)
lyxerr[Debug::GRAPHICS] << "\twe have a zipped file ("
<< getExtFromContents(orig_file_with_path) << ")\n";
if (params().noUnzip && zipped) {
lyxerr[Debug::GRAPHICS]
<< "\tpass file unzipped to LaTeX but with full path.\n";
// latex needs an absolue path, otherwise the coresponding
// *.eps.bb file isn't found
return orig_file_with_path;
}
string temp_file(orig_file);
// Uncompress the file if necessary. If it has been uncompressed in
// a previous call to prepareFile, do nothing.
if (zipped) {
temp_file = MakeAbsPath(OnlyFilename(temp_file), buf->tmppath);
lyxerr[Debug::GRAPHICS]
<< "\ttemp_file: " << temp_file << endl;
if (!IsFileReadable(temp_file)) {
bool const success = lyx::copy(orig_file_with_path, temp_file);
lyxerr[Debug::GRAPHICS]
<< "\tCopying zipped file from "
<< orig_file_with_path << " to " << temp_file
<< (success ? " succeeded\n" : " failed\n");
} else
lyxerr[Debug::GRAPHICS]
<< "\tzipped file " << temp_file
<< " exists! Maybe no tempdir ...\n";
orig_file_with_path = unzipFile(temp_file);
lyxerr[Debug::GRAPHICS]
<< "\tunzipped to " << orig_file_with_path << endl;
}
string const from = getExtFromContents(orig_file_with_path);
// "nice" means that the buffer is exported to LaTeX format but not
// run through the LaTeX compiler.
// if (nice)
// No conversion of the graphics file is needed.
// Return the original filename without any extension.
if (buf->niceFile)
return RemoveExtension(orig_file);
// We're going to be running the exported buffer through the LaTeX
// compiler, so must ensure that LaTeX can cope with the graphics
// file format.
// Perform all these manipulations on a temporary file if possible.
// If we are not using a temp dir, then temp_file contains the
// original file.
// to allow files with the same name in different dirs
// we manipulate the original file "any.dir/file.ext"
// to "any_dir_file.ext"! changing the dots in the
// dirname is important for the use of ChangeExtension
lyxerr[Debug::GRAPHICS]
<< "\tthe orig file is: " << orig_file_with_path << endl;
if (lyxrc.use_tempdir) {
string const ext_tmp = GetExtension(orig_file_with_path);
// without ext and /
temp_file = subst(
ChangeExtension(orig_file_with_path, string()), "/", "_");
// without dots and again with ext
temp_file = ChangeExtension(
subst(temp_file, ".", "_"), ext_tmp);
// now we have any_dir_file.ext
temp_file = MakeAbsPath(temp_file, buf->tmppath);
lyxerr[Debug::GRAPHICS]
<< "\tchanged to: " << temp_file << endl;
// if the file doen't exists, copy it into the tempdi
if (!IsFileReadable(temp_file)) {
bool const success = lyx::copy(orig_file_with_path, temp_file);
lyxerr[Debug::GRAPHICS]
<< "\tcopying from " << orig_file_with_path << " to "
<< temp_file
<< (success ? " succeeded\n" : " failed\n");
if (!success) {
Alert::alert(_("Cannot copy file"), orig_file_with_path,
_("into tempdir"));
return orig_file;
}
}
}
string const to = findTargetFormat(from);
lyxerr[Debug::GRAPHICS]
<< "\t we have: from " << from << " to " << to << '\n';
if (from == to) {
// No conversion is needed. LaTeX can handle the graphic file as is.
// This is true even if the orig_file is compressed. We have to return
// the orig_file_with_path, maybe it is a zipped one
return lyxrc.use_tempdir ? temp_file : orig_file_with_path;
}
string const outfile_base = RemoveExtension(temp_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'
<< "\toutfile_base = " << outfile_base << '\n'
<< "\t from " << from << " to " << to << '\n';
// if no special converter defined, than we take the default one
// from ImageMagic: convert from:inname.from to:outname.to
if (!converters.convert(buf, temp_file, outfile_base, from, to)) {
string const command =
"convert " +
from + ':' + temp_file + ' ' +
to + ':' + outfile_base + '.' + to;
lyxerr[Debug::GRAPHICS]
<< "No converter defined! I use convert from ImageMagic:\n\t"
<< command << endl;
Systemcall one;
one.startscript(Systemcall::Wait, command);
if (!IsFileReadable(ChangeExtension(outfile_base, to)))
Alert::alert(_("Cannot convert Image (not existing file?)"),
_("No information for converting from ")
+ from + _(" to ") + to);
}
return RemoveExtension(temp_file);
}
int InsetGraphics::latex(Buffer const *buf, ostream & os,
bool /*fragile*/, bool/*fs*/) 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 << endl;
// A missing (e)ps-extension is no problem for LaTeX, so
// we have to test three different cases
string const file_(MakeAbsPath(params().filename, buf->filePath()));
bool const file_exists =
!file_.empty() &&
(IsFileReadable(file_) || // original
IsFileReadable(file_ + ".eps") || // original.eps
IsFileReadable(file_ + ".ps")); // original.ps
string const message = file_exists ?
string() : string("bb = 0 0 200 100, draft, type=eps]");
// if !message.empty() than there was no existing file
// "filename(.(e)ps)" found. In this case LaTeX
// draws only a rectangle with the above bb and the
// not found filename in it.
lyxerr[Debug::GRAPHICS]
<< "\tMessage = \"" << message << '\"' << endl;
// These variables collect all the latex code that should be before and
// after the actual includegraphics command.
string before;
string after;
// Do we want subcaptions?
if (params().subcaption) {
before += "\\subfigure[" + params().subcaptionText + "]{";
after = '}';
}
// We never use the starred form, we use the "clip" option instead.
before += "\\includegraphics";
// Write the options if there are any.
string const opts = createLatexOptions();
lyxerr[Debug::GRAPHICS] << "\tOpts = " << opts << endl;
if (!opts.empty() && !message.empty())
before += ("[%\n" + opts + ',' + message);
else if (!message.empty())
before += ("[%\n" + message);
else if (!opts.empty())
before += ("[%\n" + opts + ']');
lyxerr[Debug::GRAPHICS]
<< "\tBefore = " << before
<< "\n\tafter = " << after << endl;
// Make the filename relative to the lyx file
// and remove the extension so the LaTeX will use whatever is
// appropriate (when there are several versions in different formats)
string const latex_str = message.empty() ?
(before + '{' + os::external_path(prepareFile(buf)) + '}' + after) :
(before + '{' + params().filename + " not found!}" + after);
os << latex_str;
// Return how many newlines we issued.
int const newlines =
int(lyx::count(latex_str.begin(), latex_str.end(),'\n') + 1);
return newlines;
}
int InsetGraphics::ascii(Buffer const *, ostream & os, int) const
{
// No graphics in ascii output. Possible to use gifscii to convert
// images to ascii approximation.
// 1. Convert file to ascii using gifscii
// 2. Read ascii output file and add it to the output stream.
// at least we send the filename
os << '<' << _("Graphic file:") << params().filename << ">\n";
return 0;
}
int InsetGraphics::linuxdoc(Buffer const *, ostream &) const
{
// No graphics in LinuxDoc output. Should check how/what to add.
return 0;
}
// For explanation on inserting graphics into DocBook checkout:
// http://linuxdoc.org/LDP/LDP-Author-Guide/inserting-pictures.html
// See also the docbook guide at http://www.docbook.org/
int InsetGraphics::docbook(Buffer const *, ostream & os,
bool /*mixcont*/) const
{
// In DocBook v5.0, the graphic tag will be eliminated from DocBook, will
// need to switch to MediaObject. However, for now this is sufficient and
// easier to use.
os << "<graphic fileref=\"&" << graphic_label << ";\">";
return 0;
}
void InsetGraphics::validate(LaTeXFeatures & features) const
{
// If we have no image, we should not require anything.
if (params().filename.empty())
return ;
features.includeFile(graphic_label, RemoveExtension(params().filename));
features.require("graphicx");
if (params().subcaption)
features.require("subfigure");
}
void InsetGraphics::statusChanged()
{
cache_->setStatus(cache_->graphic_->status());
if (cache_->status() == grfx::Loaded)
cache_->modify();
current_view->updateInset(this, false);
}
bool InsetGraphics::setParams(InsetGraphicsParams const & p,
string const & filepath)
{
// If nothing is changed, just return and say so.
if (params() == p && !p.filename.empty()) {
return false;
}
// Copy the new parameters.
params_ = p;
// Update the inset with the new parameters.
cache_->update(MakeAbsPath(params().filename, filepath));
// We have changed data, report it.
return true;
}
InsetGraphicsParams const & InsetGraphics::params() const
{
return params_;
}
Inset * InsetGraphics::clone(Buffer const & buffer, bool same_id) const
{
return new InsetGraphics(*this, buffer.filePath(), same_id);
}