]> git.lyx.org Git - lyx.git/blob - src/insets/insetgraphics.C
364fb9fa97505c3ddc5cf456a64792e4f718add1
[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()->getPixmap() != 0;
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();
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 of the graphics file is needed.
683         //     Return the original filename without any extension.
684         if (buf->niceFile)
685                 return RemoveExtension(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                 return lyxrc.use_tempdir ? temp_file : orig_file_with_path;
737         }
738
739         string const outfile_base = RemoveExtension(temp_file);
740         lyxerr[Debug::GRAPHICS]
741                 << "\tThe original file is " << orig_file << "\n"
742                 << "\tA copy has been made and convert is to be called with:\n"
743                 << "\tfile to convert = " << temp_file << '\n'
744                 << "\toutfile_base = " << outfile_base << '\n'
745                 << "\t from " << from << " to " << to << '\n';
746
747         // if no special converter defined, than we take the default one
748         // from ImageMagic: convert from:inname.from to:outname.to
749         if (!converters.convert(buf, temp_file, outfile_base, from, to)) {
750                 string const command =
751                         "convert " +
752                         from + ':' + temp_file + ' ' +
753                         to + ':' + outfile_base + '.' + to;
754                 lyxerr[Debug::GRAPHICS]
755                         << "No converter defined! I use convert from ImageMagic:\n\t"
756                         << command << endl;
757                 Systemcall one;
758                 one.startscript(Systemcall::Wait, command);
759                 if (!IsFileReadable(ChangeExtension(outfile_base, to)))
760                         Alert::alert(_("Cannot convert Image (not existing file?)"),
761                                 _("No information for converting from ")
762                                 + from + _(" to ") + to);
763         }
764
765         return RemoveExtension(temp_file);
766 }
767
768
769 int InsetGraphics::latex(Buffer const *buf, ostream & os,
770                          bool /*fragile*/, bool/*fs*/) const
771 {
772         // If there is no file specified or not existing,
773         // just output a message about it in the latex output.
774         lyxerr[Debug::GRAPHICS]
775                 << "insetgraphics::latex: Filename = "
776                 << params().filename << endl;
777
778         // A missing (e)ps-extension is no problem for LaTeX, so
779         // we have to test three different cases
780         string const file_(MakeAbsPath(params().filename, buf->filePath()));
781         bool const file_exists =
782                 !file_.empty() &&
783                 (IsFileReadable(file_) ||               // original
784                  IsFileReadable(file_ + ".eps") ||      // original.eps
785                  IsFileReadable(file_ + ".ps"));        // original.ps
786         string const message = file_exists ?
787                 string() : string("bb = 0 0 200 100, draft, type=eps]");
788         // if !message.empty() than there was no existing file
789         // "filename(.(e)ps)" found. In this case LaTeX
790         // draws only a rectangle with the above bb and the
791         // not found filename in it.
792         lyxerr[Debug::GRAPHICS]
793                 << "\tMessage = \"" << message << '\"' << endl;
794
795         // These variables collect all the latex code that should be before and
796         // after the actual includegraphics command.
797         string before;
798         string after;
799         // Do we want subcaptions?
800         if (params().subcaption) {
801                 before += "\\subfigure[" + params().subcaptionText + "]{";
802                 after = '}';
803         }
804         // We never use the starred form, we use the "clip" option instead.
805         before += "\\includegraphics";
806
807         // Write the options if there are any.
808         string const opts = createLatexOptions();
809         lyxerr[Debug::GRAPHICS] << "\tOpts = " << opts << endl;
810
811         if (!opts.empty() && !message.empty())
812                 before += ("[%\n" + opts + ',' + message);
813         else if (!message.empty())
814                 before += ("[%\n" + message);
815         else if (!opts.empty())
816                 before += ("[%\n" + opts + ']');
817
818         lyxerr[Debug::GRAPHICS]
819                 << "\tBefore = " << before
820                 << "\n\tafter = " << after << endl;
821
822         // Make the filename relative to the lyx file
823         // and remove the extension so the LaTeX will use whatever is
824         // appropriate (when there are several versions in different formats)
825         string const latex_str = message.empty() ?
826                 (before + '{' + os::external_path(prepareFile(buf)) + '}' + after) :
827                 (before + '{' + params().filename + " not found!}" + after);
828         os << latex_str;
829
830         // Return how many newlines we issued.
831         int const newlines =
832                 int(lyx::count(latex_str.begin(), latex_str.end(),'\n') + 1);
833
834         return newlines;
835 }
836
837
838 int InsetGraphics::ascii(Buffer const *, ostream & os, int) const
839 {
840         // No graphics in ascii output. Possible to use gifscii to convert
841         // images to ascii approximation.
842         // 1. Convert file to ascii using gifscii
843         // 2. Read ascii output file and add it to the output stream.
844         // at least we send the filename
845         os << '<' << _("Graphic file:") << params().filename << ">\n";
846         return 0;
847 }
848
849
850 int InsetGraphics::linuxdoc(Buffer const *, ostream &) const
851 {
852         // No graphics in LinuxDoc output. Should check how/what to add.
853         return 0;
854 }
855
856
857 // For explanation on inserting graphics into DocBook checkout:
858 // http://linuxdoc.org/LDP/LDP-Author-Guide/inserting-pictures.html
859 // See also the docbook guide at http://www.docbook.org/
860 int InsetGraphics::docbook(Buffer const *, ostream & os,
861                            bool /*mixcont*/) const
862 {
863         // In DocBook v5.0, the graphic tag will be eliminated from DocBook, will
864         // need to switch to MediaObject. However, for now this is sufficient and
865         // easier to use.
866         os << "<graphic fileref=\"&" << graphic_label << ";\">";
867         return 0;
868 }
869
870
871 void InsetGraphics::validate(LaTeXFeatures & features) const
872 {
873         // If we have no image, we should not require anything.
874         if (params().filename.empty())
875                 return ;
876
877         features.includeFile(graphic_label, RemoveExtension(params().filename));
878
879         features.require("graphicx");
880
881         if (params().subcaption)
882                 features.require("subfigure");
883 }
884
885
886 void InsetGraphics::statusChanged()
887 {
888         current_view->updateInset(this, false);
889 }
890
891
892 bool InsetGraphics::setParams(InsetGraphicsParams const & p,
893                               string const & filepath)
894 {
895         // If nothing is changed, just return and say so.
896         if (params() == p && !p.filename.empty()) {
897                 return false;
898         }
899
900         // Copy the new parameters.
901         params_ = p;
902
903         // Update the inset with the new parameters.
904         cache_->update(MakeAbsPath(params().filename, filepath));
905
906         // We have changed data, report it.
907         return true;
908 }
909
910
911 InsetGraphicsParams const & InsetGraphics::params() const
912 {
913         return params_;
914 }
915
916
917 Inset * InsetGraphics::clone(Buffer const & buffer, bool same_id) const
918 {
919         return new InsetGraphics(*this, buffer.filePath(), same_id);
920 }