]> git.lyx.org Git - lyx.git/blob - src/insets/insetgraphics.C
dccbbe27a2cd5cbd420683369feedfe38642c701
[lyx.git] / src / insets / insetgraphics.C
1 /* This file is part of
2  * ======================================================
3  *
4  *           LyX, The Document Processor
5  *
6  *           Copyright 1995-2002 the LyX Team.
7  *
8  * \author Baruch Even
9  * \author Herbert Voss <voss@lyx.org>
10  * ====================================================== */
11
12 /*
13 Known BUGS:
14
15     * If the image is from the clipart, and the document is moved to another
16       directory, the user is screwed. Need a way to handle it.
17       This amounts to a problem of when to use relative or absolute file paths
18       We should probably use what the user asks to use... but when he chooses
19       by the file dialog we normally get an absolute path and this may not be
20       what the user meant.
21
22       Note that browseRelFile in helper_funcs.* provides a file name
23       which is relative if it is at reference path (here puffer path)
24       level or below, and an absolute path if the file name is not a
25       `natural' relative file name. In any case,
26               MakeAbsPath(filename, buf->filePath())
27       is guaranteed to provide the correct absolute path. This is what is
28       done know for include insets. Feel free to ask me -- JMarc
29       14/01/2002
30
31 TODO Before initial production release:
32
33     * What advanced features the users want to do?
34       Implement them in a non latex dependent way, but a logical way.
35       LyX should translate it to latex or any other fitting format.
36     * Add a way to roll the image file into the file format.
37     * When loading, if the image is not found in the expected place, try
38       to find it in the clipart, or in the same directory with the image.
39     * Keep a tab on the image file, if it changes, update the lyx view.
40     * The image choosing dialog could show thumbnails of the image formats
41       it knows of, thus selection based on the image instead of based on
42       filename.
43     * Add support for the 'picins' package.
44     * Add support for the 'picinpar' package.
45     * Improve support for 'subfigure' - Allow to set the various options
46       that are possible.
47 */
48
49 /* NOTES:
50  * Fileformat:
51  * Current version is 1 (inset file format version), when changing it
52  * it should be changed in the Write() function when writing in one place
53  * and when reading one should change the version check and the error message.
54  * The filename is kept in  the lyx file in a relative way, so as to allow
55  * moving the document file and its images with no problem.
56  *
57  *
58  * Conversions:
59  *   Postscript output means EPS figures.
60  *
61  *   PDF output is best done with PDF figures if it's a direct conversion
62  *   or PNG figures otherwise.
63  *      Image format
64  *      from        to
65  *      EPS         epstopdf
66  *      PS          ps2pdf
67  *      JPG/PNG     direct
68  *      PDF         direct
69  *      others      PNG
70  */
71
72 #include <config.h>
73
74 #ifdef __GNUG__
75 #pragma implementation
76 #endif
77
78 #include "insets/insetgraphics.h"
79 #include "insets/insetgraphicsParams.h"
80
81 #include "graphics/GraphicsLoader.h"
82 #include "graphics/GraphicsImage.h"
83
84 #include "frontends/LyXView.h"
85 #include "lyxtext.h"
86 #include "buffer.h"
87 #include "BufferView.h"
88 #include "converter.h"
89 #include "frontends/Painter.h"
90 #include "lyxrc.h"
91 #include "frontends/font_metrics.h"
92 #include "debug.h"
93 #include "gettext.h"
94 #include "LaTeXFeatures.h"
95
96 #include "frontends/Dialogs.h"
97 #include "frontends/Alert.h"
98 #include "frontends/controllers/helper_funcs.h" // getVectorFromString
99
100 #include "support/LAssert.h"
101 #include "support/filetools.h"
102 #include "support/lyxalgo.h" // lyx::count
103 #include "support/path.h"
104 #include "support/systemcall.h"
105 #include "support/os.h"
106
107 #include <boost/bind.hpp>
108 #include <boost/signals/trackable.hpp>
109
110 #include <algorithm> // For the std::max
111
112 // Very, Very UGLY!
113 extern BufferView * current_view;
114
115 extern string system_tempdir;
116
117 using std::ostream;
118 using std::endl;
119
120 ///////////////////////////////////////////////////////////////////////////
121 int const VersionNumber = 1;
122 ///////////////////////////////////////////////////////////////////////////
123
124 namespace {
125
126 // This function is a utility function
127 // ... that should be with ChangeExtension ...
128 inline
129 string const RemoveExtension(string const & filename)
130 {
131         return ChangeExtension(filename, string());
132 }
133
134 } // namespace anon
135
136
137 namespace {
138
139 string const uniqueID()
140 {
141         static unsigned int seed = 1000;
142
143         ostringstream ost;
144         ost << "graph" << ++seed;
145
146         // Needed if we use lyxstring.
147         return ost.str().c_str();
148 }
149
150 } // namespace anon
151
152
153 struct InsetGraphics::Cache : boost::signals::trackable
154 {
155         ///
156         Cache(InsetGraphics &);
157         ///
158         void update(string const & file_with_path);
159
160         ///
161         int old_ascent;
162         ///
163         grfx::Loader loader;
164
165 private:
166         ///
167         InsetGraphics & parent_;
168 };
169
170
171 InsetGraphics::Cache::Cache(InsetGraphics & p)
172         : old_ascent(0), parent_(p)
173 {
174         loader.statusChanged.connect(
175                 boost::bind(&InsetGraphics::statusChanged, &parent_));
176 }
177
178
179 void InsetGraphics::Cache::update(string const & file_with_path)
180 {
181         lyx::Assert(!file_with_path.empty());
182
183         string const path = OnlyPath(file_with_path);
184         loader.reset(file_with_path, parent_.params().asGParams(path));
185 }
186
187
188 InsetGraphics::InsetGraphics()
189         : graphic_label(uniqueID()),
190           cache_(new Cache(*this))
191 {}
192
193
194 InsetGraphics::InsetGraphics(InsetGraphics const & ig,
195                              string const & filepath,
196                              bool same_id)
197         : Inset(ig, same_id),
198           graphic_label(uniqueID()),
199           cache_(new Cache(*this))
200 {
201         setParams(ig.params(), filepath);
202 }
203
204
205 InsetGraphics::~InsetGraphics()
206 {
207         // Emits the hide signal to the dialog connected (if any)
208         hideDialog();
209 }
210
211
212 string const InsetGraphics::statusMessage() const
213 {
214         string msg;
215
216         switch (cache_->loader.status()) {
217         case grfx::WaitingToLoad:
218                 msg = _("Waiting for draw request to start loading...");
219                 break;
220         case grfx::Loading:
221                 msg = _("Loading...");
222                 break;
223         case grfx::Converting:
224                 msg = _("Converting to loadable format...");
225                 break;
226         case grfx::Loaded:
227                 msg = _("Loaded into memory. Must now generate pixmap.");
228                 break;
229         case grfx::ScalingEtc:
230                 msg = _("Scaling etc...");
231                 break;
232         case grfx::Ready:
233                 msg = _("Ready to display");
234                 break;
235         case grfx::ErrorNoFile:
236                 msg = _("No file found!");
237                 break;
238         case grfx::ErrorConverting:
239                 msg = _("Error converting to loadable format");
240                 break;
241         case grfx::ErrorLoading:
242                 msg = _("Error loading file into memory");
243                 break;
244         case grfx::ErrorGeneratingPixmap:
245                 msg = _("Error generating the pixmap");
246                 break;
247         case grfx::ErrorUnknown:
248                 msg = _("No image");
249                 break;
250         }
251
252         return msg;
253 }
254
255
256 bool InsetGraphics::imageIsDrawable() const
257 {
258         if (!cache_->loader.image() || cache_->loader.status() != grfx::Ready)
259                 return false;
260
261         return cache_->loader.image()->getPixmap() != 0;
262 }
263
264
265 int InsetGraphics::ascent(BufferView *, LyXFont const &) const
266 {
267         cache_->old_ascent = 50;
268         if (imageIsDrawable())
269                 cache_->old_ascent = cache_->loader.image()->getHeight();
270         return cache_->old_ascent;
271 }
272
273
274 int InsetGraphics::descent(BufferView *, LyXFont const &) const
275 {
276         return 0;
277 }
278
279
280 int InsetGraphics::width(BufferView *, LyXFont const & font) const
281 {
282         if (imageIsDrawable())
283                 return cache_->loader.image()->getWidth();
284         else {
285                 int font_width = 0;
286
287                 LyXFont msgFont(font);
288                 msgFont.setFamily(LyXFont::SANS_FAMILY);
289
290                 string const justname = OnlyFilename (params().filename);
291                 if (!justname.empty()) {
292                         msgFont.setSize(LyXFont::SIZE_FOOTNOTE);
293                         font_width = font_metrics::width(justname, msgFont);
294                 }
295
296                 string const msg = statusMessage();
297                 if (!msg.empty()) {
298                         msgFont.setSize(LyXFont::SIZE_TINY);
299                         int const msg_width = font_metrics::width(msg, msgFont);
300                         font_width = std::max(font_width, msg_width);
301                 }
302
303                 return std::max(50, font_width + 15);
304         }
305 }
306
307
308 void InsetGraphics::draw(BufferView * bv, LyXFont const & font,
309                          int baseline, float & x, bool) const
310 {
311         int oasc = cache_->old_ascent;
312
313         int ldescent = descent(bv, font);
314         int lascent  = ascent(bv, font);
315         int lwidth   = width(bv, font);
316
317         // we may have changed while someone other was drawing us so better
318         // to not draw anything as we surely call to redraw ourself soon.
319         // This is not a nice thing to do and should be fixed properly somehow.
320         // But I still don't know the best way to go. So let's do this like this
321         // for now (Jug 20020311)
322         if (lascent != oasc) {
323                 return;
324         }
325
326         // Make sure now that x is updated upon exit from this routine
327         int old_x = int(x);
328         x += lwidth;
329
330         if (cache_->loader.status() == grfx::WaitingToLoad) {
331                 cache_->loader.startLoading();
332         }
333
334         // This will draw the graphics. If the graphics has not been loaded yet,
335         // we draw just a rectangle.
336         Painter & paint = bv->painter();
337
338         if (imageIsDrawable()) {
339                 paint.image(old_x + 2, baseline - lascent,
340                             lwidth - 4, lascent + ldescent,
341                             *cache_->loader.image());
342
343         } else {
344
345                 paint.rectangle(old_x + 2, baseline - lascent,
346                                 lwidth - 4,
347                                 lascent + ldescent);
348
349                 // Print the file name.
350                 LyXFont msgFont(font);
351                 msgFont.setFamily(LyXFont::SANS_FAMILY);
352                 string const justname = OnlyFilename (params().filename);
353                 if (!justname.empty()) {
354                         msgFont.setSize(LyXFont::SIZE_FOOTNOTE);
355                         paint.text(old_x + 8,
356                                    baseline - font_metrics::maxAscent(msgFont) - 4,
357                                    justname, msgFont);
358                 }
359
360                 // Print the message.
361                 string const msg = statusMessage();
362                 if (!msg.empty()) {
363                         msgFont.setSize(LyXFont::SIZE_TINY);
364                         paint.text(old_x + 8, baseline - 4, msg, msgFont);
365                 }
366         }
367
368         // the status message may mean we changed size, so indicate
369         // we need a row redraw
370 #if 0
371         if (old_status_ != grfx::ErrorUnknown && old_status_ != cached_status_) {
372                 bv->getLyXText()->status(bv, LyXText::CHANGED_IN_DRAW);
373         }
374 #endif
375
376         // Reset the cache, ready for the next draw request
377 #if 0
378         cached_status_ = grfx::ErrorUnknown;
379         cached_image_.reset();
380         cache_filled_ = false;
381 #endif
382 }
383
384
385 void InsetGraphics::edit(BufferView *bv, int, int, mouse_button::state)
386 {
387         bv->owner()->getDialogs()->showGraphics(this);
388 }
389
390
391 void InsetGraphics::edit(BufferView * bv, bool)
392 {
393         edit(bv, 0, 0, mouse_button::none);
394 }
395
396
397 Inset::EDITABLE InsetGraphics::editable() const
398 {
399         return IS_EDITABLE;
400 }
401
402
403 void InsetGraphics::write(Buffer const *, ostream & os) const
404 {
405         os << "Graphics FormatVersion " << VersionNumber << '\n';
406         params().Write(os);
407 }
408
409
410 void InsetGraphics::read(Buffer const * buf, LyXLex & lex)
411 {
412         string const token = lex.getString();
413
414         if (token == "Graphics")
415                 readInsetGraphics(lex);
416         else if (token == "Figure") // Compatibility reading of FigInset figures.
417                 readFigInset(lex);
418         else
419                 lyxerr[Debug::GRAPHICS] << "Not a Graphics or Figure inset!\n";
420
421         cache_->update(MakeAbsPath(params().filename, buf->filePath()));
422 }
423
424
425 void InsetGraphics::readInsetGraphics(LyXLex & lex)
426 {
427         bool finished = false;
428
429         while (lex.isOK() && !finished) {
430                 lex.next();
431
432                 string const token = lex.getString();
433                 lyxerr[Debug::GRAPHICS] << "Token: '" << token << '\''
434                                     << std::endl;
435
436                 if (token.empty()) {
437                         continue;
438                 } else if (token == "\\end_inset") {
439                         finished = true;
440                 } else if (token == "FormatVersion") {
441                         lex.next();
442                         int version = lex.getInteger();
443                         if (version > VersionNumber)
444                                 lyxerr
445                                 << "This document was created with a newer Graphics widget"
446                                 ", You should use a newer version of LyX to read this"
447                                 " file."
448                                 << std::endl;
449                         // TODO: Possibly open up a dialog?
450                 }
451                 else {
452                         if (! params_.Read(lex, token))
453                                 lyxerr << "Unknown token, " << token << ", skipping."
454                                         << std::endl;
455                 }
456         }
457 }
458
459 // FormatVersion < 1.0  (LyX < 1.2)
460 void InsetGraphics::readFigInset(LyXLex & lex)
461 {
462         std::vector<string> const oldUnits =
463                 getVectorFromString("pt,cm,in,p%,c%");
464         bool finished = false;
465         // set the display default
466         if (lyxrc.display_graphics == "mono")
467             params_.display = InsetGraphicsParams::MONOCHROME;
468         else if (lyxrc.display_graphics == "gray")
469             params_.display = InsetGraphicsParams::GRAYSCALE;
470         else if (lyxrc.display_graphics == "color")
471             params_.display = InsetGraphicsParams::COLOR;
472         else
473             params_.display = InsetGraphicsParams::NONE;
474         while (lex.isOK() && !finished) {
475                 lex.next();
476
477                 string const token = lex.getString();
478                 lyxerr[Debug::GRAPHICS] << "Token: " << token << endl;
479
480                 if (token.empty())
481                         continue;
482                 else if (token == "\\end_inset") {
483                         finished = true;
484                 } else if (token == "file") {
485                         if (lex.next()) {
486                                 params_.filename = lex.getString();
487                         }
488                 } else if (token == "extra") {
489                         if (lex.next());
490                         // kept for backwards compability. Delete in 0.13.x
491                 } else if (token == "subcaption") {
492                         if (lex.eatLine())
493                                 params_.subcaptionText = lex.getString();
494                 } else if (token == "label") {
495                         if (lex.next());
496                         // kept for backwards compability. Delete in 0.13.x
497                 } else if (token == "angle") {
498                         if (lex.next()) {
499                                 params_.rotate = true;
500                                 params_.rotateAngle = lex.getFloat();
501                         }
502                 } else if (token == "size") {
503                         if (lex.next())
504                                 params_.lyxwidth = LyXLength(lex.getString()+"pt");
505                         if (lex.next())
506                                 params_.lyxheight = LyXLength(lex.getString()+"pt");
507                         params_.lyxsize_type = InsetGraphicsParams::WH;
508                 } else if (token == "flags") {
509                         if (lex.next())
510                                 switch (lex.getInteger()) {
511                                 case 1: params_.display = InsetGraphicsParams::MONOCHROME;
512                                     break;
513                                 case 2: params_.display = InsetGraphicsParams::GRAYSCALE;
514                                     break;
515                                 case 3: params_.display = InsetGraphicsParams::COLOR;
516                                     break;
517                                 }
518                 } else if (token == "subfigure") {
519                         params_.subcaption = true;
520                 } else if (token == "width") {
521                     if (lex.next()) {
522                         int i = lex.getInteger();
523                         if (lex.next()) {
524                             if (i == 5) {
525                                 params_.scale = lex.getInteger();
526                                 params_.size_type = InsetGraphicsParams::SCALE;
527                             } else {
528                                 params_.width = LyXLength(lex.getString()+oldUnits[i]);
529                                 params_.size_type = InsetGraphicsParams::WH;
530                             }
531                         }
532                     }
533                 } else if (token == "height") {
534                     if (lex.next()) {
535                         int i = lex.getInteger();
536                         if (lex.next()) {
537                             params_.height = LyXLength(lex.getString()+oldUnits[i]);
538                             params_.size_type = InsetGraphicsParams::WH;
539                         }
540                     }
541                 }
542         }
543 }
544
545 string const InsetGraphics::createLatexOptions() const
546 {
547         // Calculate the options part of the command, we must do it to a string
548         // stream since we might have a trailing comma that we would like to remove
549         // before writing it to the output stream.
550         ostringstream options;
551         if (!params().bb.empty())
552             options << "  bb=" << strip(params().bb) << ",\n";
553         if (params().draft)
554             options << "  draft,\n";
555         if (params().clip)
556             options << "  clip,\n";
557         if (params().size_type == InsetGraphicsParams::WH) {
558             if (!params().width.zero())
559                 options << "  width=" << params().width.asLatexString() << ",\n";
560             if (!params().height.zero())
561                 options << "  height=" << params().height.asLatexString() << ",\n";
562         } else if (params().size_type == InsetGraphicsParams::SCALE) {
563             if (params().scale > 0)
564                 options << "  scale=" << double(params().scale)/100.0 << ",\n";
565         }
566         if (params().keepAspectRatio)
567             options << "  keepaspectratio,\n";
568         // Make sure it's not very close to zero, a float can be effectively
569         // zero but not exactly zero.
570         if (!lyx::float_equal(params().rotateAngle, 0, 0.001) && params().rotate) {
571             options << "  angle=" << params().rotateAngle << ",\n";
572             if (!params().rotateOrigin.empty()) {
573                 options << "  origin=" << params().rotateOrigin[0];
574                 if (contains(params().rotateOrigin,"Top"))
575                     options << 't';
576                 else if (contains(params().rotateOrigin,"Bottom"))
577                     options << 'b';
578                 else if (contains(params().rotateOrigin,"Baseline"))
579                     options << 'B';
580                 options << ",\n";
581             }
582         }
583         if (!params().special.empty())
584             options << params().special << ",\n";
585         string opts = options.str().c_str();
586         return opts.substr(0,opts.size()-2);    // delete last ",\n"
587 }
588
589 namespace {
590 string findTargetFormat(string const & suffix)
591 {
592         // lyxrc.pdf_mode means:
593         // Are we creating a PDF or a PS file?
594         // (Should actually mean, are we using latex or pdflatex).
595         if (lyxrc.pdf_mode) {
596                 lyxerr[Debug::GRAPHICS] << "findTargetFormat: PDF mode\n";
597                 if (contains(suffix,"ps") || suffix == "pdf")
598                         return "pdf";
599                 else if (suffix == "jpg")       // pdflatex can use jpeg
600                         return suffix;
601                 else
602                         return "png";           // and also png
603         }
604         // If it's postscript, we always do eps.
605         lyxerr[Debug::GRAPHICS] << "findTargetFormat: PostScript mode\n";
606         if (suffix != "ps")                     // any other than ps
607             return "eps";                       // is changed to eps
608         else
609             return suffix;                      // let ps untouched
610 }
611
612 } // Anon. namespace
613
614
615 string const InsetGraphics::prepareFile(Buffer const *buf) const
616 {
617         // LaTeX can cope if the graphics file doesn't exist, so just return the
618         // filename.
619         string const orig_file = params().filename;
620         string orig_file_with_path =
621                 MakeAbsPath(orig_file, buf->filePath());
622         lyxerr[Debug::GRAPHICS] << "[InsetGraphics::prepareFile] orig_file = "
623                     << orig_file << "\n\twith path: "
624                     << orig_file_with_path << endl;
625
626         if (!IsFileReadable(orig_file_with_path))
627                 return orig_file;
628
629         // If the file is compressed and we have specified that it should not be
630         // uncompressed, then just return its name and let LaTeX do the rest!
631
632         // maybe that other zip extensions also be useful, especially the
633         // ones that may be declared in texmf/tex/latex/config/graphics.cfg.
634         // for example:
635         /* -----------snip-------------
636           {\DeclareGraphicsRule{.pz}{eps}{.bb}{}%
637            \DeclareGraphicsRule{.eps.Z}{eps}{.eps.bb}{}%
638            \DeclareGraphicsRule{.ps.Z}{eps}{.ps.bb}{}%
639            \DeclareGraphicsRule{.ps.gz}{eps}{.ps.bb}{}%
640            \DeclareGraphicsRule{.eps.gz}{eps}{.eps.bb}{}}}%
641          -----------snip-------------*/
642
643         bool const zipped = zippedFile(orig_file_with_path);
644         if (zipped)
645                 lyxerr[Debug::GRAPHICS] << "\twe have a zipped file ("
646                         << getExtFromContents(orig_file_with_path) << ")\n";
647         if (params().noUnzip && zipped) {
648                 lyxerr[Debug::GRAPHICS]
649                         << "\tpass file unzipped to LaTeX but with full path.\n";
650                 // latex needs an absolue path, otherwise the coresponding
651                 // *.eps.bb file isn't found
652                 return orig_file_with_path;
653         }
654
655         string temp_file(orig_file);
656         // Uncompress the file if necessary. If it has been uncompressed in
657         // a previous call to prepareFile, do nothing.
658         if (zipped) {
659                 temp_file = MakeAbsPath(OnlyFilename(temp_file), buf->tmppath);
660                 lyxerr[Debug::GRAPHICS]
661                         << "\ttemp_file: " << temp_file << endl;
662                 if (!IsFileReadable(temp_file)) {
663                         bool const success = lyx::copy(orig_file_with_path, temp_file);
664                         lyxerr[Debug::GRAPHICS]
665                                 << "\tCopying zipped file from "
666                                 << orig_file_with_path << " to " << temp_file
667                                 << (success ? " succeeded\n" : " failed\n");
668                 } else
669                         lyxerr[Debug::GRAPHICS]
670                                 << "\tzipped file " << temp_file
671                                 << " exists! Maybe no tempdir ...\n";
672                 orig_file_with_path = unzipFile(temp_file);
673                 lyxerr[Debug::GRAPHICS]
674                         << "\tunzipped to " << orig_file_with_path << endl;
675         }
676         string const from = getExtFromContents(orig_file_with_path);
677
678         // "nice" means that the buffer is exported to LaTeX format but not
679         //        run through the LaTeX compiler.
680         // if (nice)
681         //     No conversion of the graphics file is needed.
682         //     Return the original filename without any extension.
683         if (buf->niceFile)
684                 return RemoveExtension(orig_file);
685
686         // We're going to be running the exported buffer through the LaTeX
687         // compiler, so must ensure that LaTeX can cope with the graphics
688         // file format.
689
690         // Perform all these manipulations on a temporary file if possible.
691         // If we are not using a temp dir, then temp_file contains the
692         // original file.
693         // to allow files with the same name in different dirs
694         // we manipulate the original file "any.dir/file.ext"
695         // to "any_dir_file.ext"! changing the dots in the
696         // dirname is important for the use of ChangeExtension
697         lyxerr[Debug::GRAPHICS]
698                 << "\tthe orig file is: " << orig_file_with_path << endl;
699
700         if (lyxrc.use_tempdir) {
701                 string const ext_tmp = GetExtension(orig_file_with_path);
702                 // without ext and /
703                 temp_file = subst(
704                         ChangeExtension(orig_file_with_path, string()), "/", "_");
705                 // without dots and again with ext
706                 temp_file = ChangeExtension(
707                         subst(temp_file, ".", "_"), ext_tmp);
708                 // now we have any_dir_file.ext
709                 temp_file = MakeAbsPath(temp_file, buf->tmppath);
710                 lyxerr[Debug::GRAPHICS]
711                         << "\tchanged to: " << temp_file << endl;
712
713                 // if the file doen't exists, copy it into the tempdi
714                 if (!IsFileReadable(temp_file)) {
715                         bool const success = lyx::copy(orig_file_with_path, temp_file);
716                         lyxerr[Debug::GRAPHICS]
717                                 << "\tcopying from " << orig_file_with_path << " to "
718                                 << temp_file
719                                 << (success ? " succeeded\n" : " failed\n");
720                         if (!success) {
721                                 Alert::alert(_("Cannot copy file"), orig_file_with_path,
722                                         _("into tempdir"));
723                                 return orig_file;
724                         }
725                 }
726         }
727
728         string const to = findTargetFormat(from);
729         lyxerr[Debug::GRAPHICS]
730                 << "\t we have: from " << from << " to " << to << '\n';
731         if (from == to) {
732                 // No conversion is needed. LaTeX can handle the graphic file as is.
733                 // This is true even if the orig_file is compressed. We have to return
734                 // the orig_file_with_path, maybe it is a zipped one
735                 return lyxrc.use_tempdir ? temp_file : orig_file_with_path;
736         }
737
738         string const outfile_base = RemoveExtension(temp_file);
739         lyxerr[Debug::GRAPHICS]
740                 << "\tThe original file is " << orig_file << "\n"
741                 << "\tA copy has been made and convert is to be called with:\n"
742                 << "\tfile to convert = " << temp_file << '\n'
743                 << "\toutfile_base = " << outfile_base << '\n'
744                 << "\t from " << from << " to " << to << '\n';
745
746         // if no special converter defined, than we take the default one
747         // from ImageMagic: convert from:inname.from to:outname.to
748         if (!converters.convert(buf, temp_file, outfile_base, from, to)) {
749                 string const command =
750                         "convert " +
751                         from + ':' + temp_file + ' ' +
752                         to + ':' + outfile_base + '.' + to;
753                 lyxerr[Debug::GRAPHICS]
754                         << "No converter defined! I use convert from ImageMagic:\n\t"
755                         << command << endl;
756                 Systemcall one;
757                 one.startscript(Systemcall::Wait, command);
758                 if (!IsFileReadable(ChangeExtension(outfile_base, to)))
759                         Alert::alert(_("Cannot convert Image (not existing file?)"),
760                                 _("No information for converting from ")
761                                 + from + _(" to ") + to);
762         }
763
764         return RemoveExtension(temp_file);
765 }
766
767
768 int InsetGraphics::latex(Buffer const *buf, ostream & os,
769                          bool /*fragile*/, bool/*fs*/) const
770 {
771         // If there is no file specified or not existing,
772         // just output a message about it in the latex output.
773         lyxerr[Debug::GRAPHICS]
774                 << "insetgraphics::latex: Filename = "
775                 << params().filename << endl;
776
777         // A missing (e)ps-extension is no problem for LaTeX, so
778         // we have to test three different cases
779         string const file_(MakeAbsPath(params().filename, buf->filePath()));
780         bool const file_exists =
781                 !file_.empty() &&
782                 (IsFileReadable(file_) ||               // original
783                  IsFileReadable(file_ + ".eps") ||      // original.eps
784                  IsFileReadable(file_ + ".ps"));        // original.ps
785         string const message = file_exists ?
786                 string() : string("bb = 0 0 200 100, draft, type=eps]");
787         // if !message.empty() than there was no existing file
788         // "filename(.(e)ps)" found. In this case LaTeX
789         // draws only a rectangle with the above bb and the
790         // not found filename in it.
791         lyxerr[Debug::GRAPHICS]
792                 << "\tMessage = \"" << message << '\"' << endl;
793
794         // These variables collect all the latex code that should be before and
795         // after the actual includegraphics command.
796         string before;
797         string after;
798         // Do we want subcaptions?
799         if (params().subcaption) {
800                 before += "\\subfigure[" + params().subcaptionText + "]{";
801                 after = '}';
802         }
803         // We never use the starred form, we use the "clip" option instead.
804         before += "\\includegraphics";
805
806         // Write the options if there are any.
807         string const opts = createLatexOptions();
808         lyxerr[Debug::GRAPHICS] << "\tOpts = " << opts << endl;
809
810         if (!opts.empty() && !message.empty())
811                 before += ("[%\n" + opts + ',' + message);
812         else if (!message.empty())
813                 before += ("[%\n" + message);
814         else if (!opts.empty())
815                 before += ("[%\n" + opts + ']');
816
817         lyxerr[Debug::GRAPHICS]
818                 << "\tBefore = " << before
819                 << "\n\tafter = " << after << endl;
820
821         // Make the filename relative to the lyx file
822         // and remove the extension so the LaTeX will use whatever is
823         // appropriate (when there are several versions in different formats)
824         string const latex_str = message.empty() ?
825                 (before + '{' + os::external_path(prepareFile(buf)) + '}' + after) :
826                 (before + '{' + params().filename + " not found!}" + after);
827         os << latex_str;
828
829         // Return how many newlines we issued.
830         int const newlines =
831                 int(lyx::count(latex_str.begin(), latex_str.end(),'\n') + 1);
832
833         return newlines;
834 }
835
836
837 int InsetGraphics::ascii(Buffer const *, ostream & os, int) const
838 {
839         // No graphics in ascii output. Possible to use gifscii to convert
840         // images to ascii approximation.
841         // 1. Convert file to ascii using gifscii
842         // 2. Read ascii output file and add it to the output stream.
843         // at least we send the filename
844         os << '<' << _("Graphic file:") << params().filename << ">\n";
845         return 0;
846 }
847
848
849 int InsetGraphics::linuxdoc(Buffer const *, ostream &) const
850 {
851         // No graphics in LinuxDoc output. Should check how/what to add.
852         return 0;
853 }
854
855
856 // For explanation on inserting graphics into DocBook checkout:
857 // http://linuxdoc.org/LDP/LDP-Author-Guide/inserting-pictures.html
858 // See also the docbook guide at http://www.docbook.org/
859 int InsetGraphics::docbook(Buffer const *, ostream & os,
860                            bool /*mixcont*/) const
861 {
862         // In DocBook v5.0, the graphic tag will be eliminated from DocBook, will
863         // need to switch to MediaObject. However, for now this is sufficient and
864         // easier to use.
865         os << "<graphic fileref=\"&" << graphic_label << ";\">";
866         return 0;
867 }
868
869
870 void InsetGraphics::validate(LaTeXFeatures & features) const
871 {
872         // If we have no image, we should not require anything.
873         if (params().filename.empty())
874                 return ;
875
876         features.includeFile(graphic_label, RemoveExtension(params().filename));
877
878         features.require("graphicx");
879
880         if (params().subcaption)
881                 features.require("subfigure");
882 }
883
884
885 void InsetGraphics::statusChanged()
886 {
887         current_view->updateInset(this, false);
888 }
889
890
891 bool InsetGraphics::setParams(InsetGraphicsParams const & p,
892                               string const & filepath)
893 {
894         // If nothing is changed, just return and say so.
895         if (params() == p && !p.filename.empty()) {
896                 return false;
897         }
898
899         // Copy the new parameters.
900         params_ = p;
901
902         // Update the inset with the new parameters.
903         cache_->update(MakeAbsPath(params().filename, filepath));
904
905         // We have changed data, report it.
906         return true;
907 }
908
909
910 InsetGraphicsParams const & InsetGraphics::params() const
911 {
912         return params_;
913 }
914
915
916 Inset * InsetGraphics::clone(Buffer const & buffer, bool same_id) const
917 {
918         return new InsetGraphics(*this, buffer.filePath(), same_id);
919 }