]> git.lyx.org Git - lyx.git/blob - src/insets/insetgraphics.C
some more changes
[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 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                 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 }