]> git.lyx.org Git - lyx.git/blob - src/insets/insetgraphics.C
* Make the graphics files conform strictly to the Pimpl idiom by moving
[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.connect(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().as_grfxParams(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()->isDrawable();
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(*this, *bv);
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 needed!
682         //      Return the original filename as is, because we do not know
683         //      what the user decide.
684         if (buf->niceFile)
685                 return orig_file;
686
687         // We're going to be running the exported buffer through the LaTeX
688         // compiler, so must ensure that LaTeX can cope with the graphics
689         // file format.
690
691         // Perform all these manipulations on a temporary file if possible.
692         // If we are not using a temp dir, then temp_file contains the
693         // original file.
694         // to allow files with the same name in different dirs
695         // we manipulate the original file "any.dir/file.ext"
696         // to "any_dir_file.ext"! changing the dots in the
697         // dirname is important for the use of ChangeExtension
698         lyxerr[Debug::GRAPHICS]
699                 << "\tthe orig file is: " << orig_file_with_path << endl;
700
701         if (lyxrc.use_tempdir) {
702                 string const ext_tmp = GetExtension(orig_file_with_path);
703                 // without ext and /
704                 temp_file = subst(
705                         ChangeExtension(orig_file_with_path, string()), "/", "_");
706                 // without dots and again with ext
707                 temp_file = ChangeExtension(
708                         subst(temp_file, ".", "_"), ext_tmp);
709                 // now we have any_dir_file.ext
710                 temp_file = MakeAbsPath(temp_file, buf->tmppath);
711                 lyxerr[Debug::GRAPHICS]
712                         << "\tchanged to: " << temp_file << endl;
713
714                 // if the file doen't exists, copy it into the tempdi
715                 if (!IsFileReadable(temp_file)) {
716                         bool const success = lyx::copy(orig_file_with_path, temp_file);
717                         lyxerr[Debug::GRAPHICS]
718                                 << "\tcopying from " << orig_file_with_path << " to "
719                                 << temp_file
720                                 << (success ? " succeeded\n" : " failed\n");
721                         if (!success) {
722                                 Alert::alert(_("Cannot copy file"), orig_file_with_path,
723                                         _("into tempdir"));
724                                 return orig_file;
725                         }
726                 }
727         }
728
729         string const to = findTargetFormat(from);
730         lyxerr[Debug::GRAPHICS]
731                 << "\t we have: from " << from << " to " << to << '\n';
732         if (from == to) {
733                 // No conversion is needed. LaTeX can handle the graphic file as is.
734                 // This is true even if the orig_file is compressed. We have to return
735                 // the orig_file_with_path, maybe it is a zipped one
736                 if (lyxrc.use_tempdir)
737                         return RemoveExtension(temp_file);
738                 return RemoveExtension(orig_file_with_path);
739         }
740
741         string const outfile_base = RemoveExtension(temp_file);
742         lyxerr[Debug::GRAPHICS]
743                 << "\tThe original file is " << orig_file << "\n"
744                 << "\tA copy has been made and convert is to be called with:\n"
745                 << "\tfile to convert = " << temp_file << '\n'
746                 << "\toutfile_base = " << outfile_base << '\n'
747                 << "\t from " << from << " to " << to << '\n';
748
749         // if no special converter defined, than we take the default one
750         // from ImageMagic: convert from:inname.from to:outname.to
751         if (!converters.convert(buf, temp_file, outfile_base, from, to)) {
752                 string const command =
753                         "convert " +
754                         from + ':' + temp_file + ' ' +
755                         to + ':' + outfile_base + '.' + to;
756                 lyxerr[Debug::GRAPHICS]
757                         << "No converter defined! I use convert from ImageMagic:\n\t"
758                         << command << endl;
759                 Systemcall one;
760                 one.startscript(Systemcall::Wait, command);
761                 if (!IsFileReadable(ChangeExtension(outfile_base, to)))
762                         Alert::alert(_("Cannot convert Image (not existing file?)"),
763                                 _("No information for converting from ")
764                                 + from + _(" to ") + to);
765         }
766
767         return RemoveExtension(temp_file);
768 }
769
770
771 int InsetGraphics::latex(Buffer const *buf, ostream & os,
772                          bool /*fragile*/, bool/*fs*/) const
773 {
774         // If there is no file specified or not existing,
775         // just output a message about it in the latex output.
776         lyxerr[Debug::GRAPHICS]
777                 << "insetgraphics::latex: Filename = "
778                 << params().filename << endl;
779
780         // A missing (e)ps-extension is no problem for LaTeX, so
781         // we have to test three different cases
782         string const file_(MakeAbsPath(params().filename, buf->filePath()));
783         bool const file_exists =
784                 !file_.empty() &&
785                 (IsFileReadable(file_) ||               // original
786                  IsFileReadable(file_ + ".eps") ||      // original.eps
787                  IsFileReadable(file_ + ".ps"));        // original.ps
788         string const message = file_exists ?
789                 string() : string("bb = 0 0 200 100, draft, type=eps]");
790         // if !message.empty() than there was no existing file
791         // "filename(.(e)ps)" found. In this case LaTeX
792         // draws only a rectangle with the above bb and the
793         // not found filename in it.
794         lyxerr[Debug::GRAPHICS]
795                 << "\tMessage = \"" << message << '\"' << endl;
796
797         // These variables collect all the latex code that should be before and
798         // after the actual includegraphics command.
799         string before;
800         string after;
801         // Do we want subcaptions?
802         if (params().subcaption) {
803                 before += "\\subfigure[" + params().subcaptionText + "]{";
804                 after = '}';
805         }
806         // We never use the starred form, we use the "clip" option instead.
807         before += "\\includegraphics";
808
809         // Write the options if there are any.
810         string const opts = createLatexOptions();
811         lyxerr[Debug::GRAPHICS] << "\tOpts = " << opts << endl;
812
813         if (!opts.empty() && !message.empty())
814                 before += ("[%\n" + opts + ',' + message);
815         else if (!message.empty())
816                 before += ("[%\n" + message);
817         else if (!opts.empty())
818                 before += ("[%\n" + opts + ']');
819
820         lyxerr[Debug::GRAPHICS]
821                 << "\tBefore = " << before
822                 << "\n\tafter = " << after << endl;
823
824         // Make the filename relative to the lyx file
825         // and remove the extension so the LaTeX will use whatever is
826         // appropriate (when there are several versions in different formats)
827         string const latex_str = message.empty() ?
828                 (before + '{' + os::external_path(prepareFile(buf)) + '}' + after) :
829                 (before + '{' + params().filename + " not found!}" + after);
830         os << latex_str;
831
832         // Return how many newlines we issued.
833         int const newlines =
834                 int(lyx::count(latex_str.begin(), latex_str.end(),'\n') + 1);
835
836         return newlines;
837 }
838
839
840 int InsetGraphics::ascii(Buffer const *, ostream & os, int) const
841 {
842         // No graphics in ascii output. Possible to use gifscii to convert
843         // images to ascii approximation.
844         // 1. Convert file to ascii using gifscii
845         // 2. Read ascii output file and add it to the output stream.
846         // at least we send the filename
847         os << '<' << _("Graphic file:") << params().filename << ">\n";
848         return 0;
849 }
850
851
852 int InsetGraphics::linuxdoc(Buffer const *, ostream &) const
853 {
854         // No graphics in LinuxDoc output. Should check how/what to add.
855         return 0;
856 }
857
858
859 // For explanation on inserting graphics into DocBook checkout:
860 // http://linuxdoc.org/LDP/LDP-Author-Guide/inserting-pictures.html
861 // See also the docbook guide at http://www.docbook.org/
862 int InsetGraphics::docbook(Buffer const *, ostream & os,
863                            bool /*mixcont*/) const
864 {
865         // In DocBook v5.0, the graphic tag will be eliminated from DocBook, will
866         // need to switch to MediaObject. However, for now this is sufficient and
867         // easier to use.
868         os << "<graphic fileref=\"&" << graphic_label << ";\">";
869         return 0;
870 }
871
872
873 void InsetGraphics::validate(LaTeXFeatures & features) const
874 {
875         // If we have no image, we should not require anything.
876         if (params().filename.empty())
877                 return ;
878
879         features.includeFile(graphic_label, RemoveExtension(params().filename));
880
881         features.require("graphicx");
882
883         if (params().subcaption)
884                 features.require("subfigure");
885 }
886
887
888 void InsetGraphics::statusChanged()
889 {
890         current_view->updateInset(this, false);
891 }
892
893
894 bool InsetGraphics::setParams(InsetGraphicsParams const & p,
895                               string const & filepath)
896 {
897         // If nothing is changed, just return and say so.
898         if (params() == p && !p.filename.empty()) {
899                 return false;
900         }
901
902         // Copy the new parameters.
903         params_ = p;
904
905         // Update the inset with the new parameters.
906         cache_->update(MakeAbsPath(params().filename, filepath));
907
908         // We have changed data, report it.
909         return true;
910 }
911
912
913 InsetGraphicsParams const & InsetGraphics::params() const
914 {
915         return params_;
916 }
917
918
919 Inset * InsetGraphics::clone(Buffer const & buffer, bool same_id) const
920 {
921         return new InsetGraphics(*this, buffer.filePath(), same_id);
922 }