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