1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995-2000 the LyX Team.
8 * This file Copyright 2000 Baruch Even.
9 * ====================================================== */
12 How to use it for now:
13 * The lyxfunc 'graphics-insert' will insert this inset into the document.
18 * Make the inline viewing work, there is a preliminary work going on,
20 * Support automatic image format conversion, create both a PNG and EPS output.
23 * Add messages in the empty rectangle (in the buffer view) to say how are
25 - Implemented, needs testing.
26 * Clean up GraphicsCacheItem(_pimpl)
27 * Pop up a dialog if the widget version is higher than what we accept.
28 * Prepare code to read FigInset insets to upgrade upwards
29 * Provide sed/awk/C code to downgrade from InsetGraphics to FigInset.
36 * If the image is from the clipart, and the document is moved to another
37 directory, the user is screwed. Need a way to handle it.
38 This amounts to a problem of when to use relative or absolute file paths
39 We should probably use what the user asks to use... but when he chooses
40 by the file dialog we normally get an absolute path and this may not be
42 * Bug in FileDlg class (src/filedlg.[hC]) when selecting a file and then
43 pressing ok, it counts as if no real selection done. Apparently
44 when choosing a file it doesn't update the select file input line.
45 * Inline viewing is still not completely operational, in fact it is now
46 disabled. To enable it enable the define:
47 INSETGRAPHICS_INLINE_VIEW
48 * If we are trying to create a file in a read-only directory and there
49 are graphics that need converting, the converting will fail because
50 it is done in-place, into the same directory as the original image.
52 TODO Before initial production release:
53 * Replace insetfig everywhere
54 * Read it's file format
55 * Get created by all commands used to create figinset currently.
56 * Search for comments of the form
57 // INSET_GRAPHICS: remove this when InsetFig is thrown.
60 * Finish the basic To-do list.
61 * Extract the general logic of the dialog in order to allow easier porting
62 to Gnome/KDE, and put the general logic in frontends and the inherited
63 platform dependent code in the appropriate dirs.
65 TODO Extended features:
67 * Advanced Latex tab folder.
68 * Add support for more features so that it will be better than insetfig.
69 * Keep aspect ratio radio button
70 * Support for complete control over the latex parameters for TeXperts
71 * What advanced features the users want to do?
72 Implement them in a non latex dependent way, but a logical way.
73 LyX should translate it to latex or any other fitting format.
74 * Add a way to roll the image file into the file format.
75 * When loading, if the image is not found in the expected place, try
76 to find it in the clipart, or in the same directory with the image.
77 * Keep a tab on the image file, if it changes, update the lyx view.
78 * The image choosing dialog could show thumbnails of the image formats
79 it knows of, thus selection based on the image instead of based on
81 * Add support for the 'picins' package.
82 * Add support for the 'picinpar' package.
83 * Improve support for 'subfigure' - Allow to set the various options
90 * This is currently a moving target, I'm trying stuff and learning what
91 * is needed and how to accomplish it, since there is no predefined goal or
92 * way to go I invent it as I go.
94 * My current intention is for seperation from LaTeX, the basic needs are
95 * resizing and rotating, displaying on screen in various depths and printing
96 * conversion of depths (independent of the display depth). For this I'll
97 * provide a simple interface.
99 * The medium level includes clipping of the image, but in a limited way.
101 * For the LaTeX gurus I'll provide a complete control over the output, but
102 * this is latex dependent and guru dependent so I'd rather avoid doing this
103 * for the normal user. This stuff includes clipping, special image size
104 * specifications (\textwidth\minus 2in) which I see no way to generalize
105 * to non-latex specific way.
108 * 'graphicx' for the graphics inclusion.
109 * 'subfigure' for the subfigures.
113 * Current version is 1 (inset file format version), when changing it
114 * it should be changed in the Write() function when writing in one place
115 * and when reading one should change the version check and the error message.
117 * The filename is kept in the lyx file in a relative way, so as to allow
118 * moving the document file and its images with no problem.
122 * Apparently the PNG output is preferred over PDF images when doing PDF
123 * documents (i.e. prefer imagemagick eps2png over eps2pdf)
129 #pragma implementation
132 #define INSETGRAPHICS_INLINE_VIEW
134 #include "insets/insetgraphics.h"
135 #include "insets/insetgraphicsParams.h"
136 #include "graphics/GraphicsCache.h"
137 #include "graphics/GraphicsCacheItem.h"
139 #include "frontends/Dialogs.h"
142 #include "BufferView.h"
143 #include "converter.h"
144 #include "frontends/support/LyXImage.h"
146 #include "lyx_gui_misc.h"
147 #include "support/FileInfo.h"
148 #include "support/filetools.h"
149 #include "support/lyxlib.h"
152 #include "font.h" // For the lyxfont class.
153 #include <algorithm> // For the std::max
154 #include "support/lyxmanip.h"
157 extern string system_tempdir;
163 // This function is a utility function
165 string const RemoveExtension(string const & filename)
167 return ChangeExtension(filename, string());
171 // Initialize only those variables that do not have a constructor.
172 InsetGraphics::InsetGraphics()
173 : cacheHandle(0), pixmap(0), updateImage(false)
176 InsetGraphics::~InsetGraphics()
178 // Emits the hide signal to the dialog connected (if any)
183 InsetGraphics::statusMessage() const
185 char const * msg = 0;
187 #ifdef INSETGRAPHICS_INLINE_VIEW
189 switch (cacheHandle->getImageStatus()) {
190 case GraphicsCacheItem::UnknownError:
191 msg = _("Unknown Error");
194 case GraphicsCacheItem::Loading:
195 msg = _("Loading...");
198 case GraphicsCacheItem::ErrorReading:
199 msg = _("Error reading");
202 case GraphicsCacheItem::ErrorConverting:
203 msg = _("Error converting");
206 case GraphicsCacheItem::Loaded:
207 // No message to write.
212 msg = _("Inline view disabled");
218 int InsetGraphics::ascent(BufferView *, LyXFont const &) const
221 return pixmap->getHeight();
227 int InsetGraphics::descent(BufferView *, LyXFont const &) const
229 // this is not true if viewport is used and clip is not.
234 int InsetGraphics::width(BufferView *, LyXFont const & font) const
237 return pixmap->getWidth();
239 char const * msg = statusMessage();
243 font_width = lyxfont::width(msg, font);
245 return max(50, font_width + 15);
249 void InsetGraphics::draw(BufferView * bv, LyXFont const & font,
250 int baseline, float & x, bool) const
252 Painter & paint = bv->painter();
254 int ldescent = descent(bv, font);
255 int lascent = ascent(bv, font);
256 int lwidth = width(bv, font);
258 // Make sure x is updated upon exit from this routine
262 // This will draw the graphics. If the graphics has not been loaded yet,
263 // we draw just a rectangle.
266 paint.image(int(old_x) + 2, baseline - lascent,
267 lwidth - 4, lascent + ldescent,
270 #ifdef INSETGRAPHICS_INLINE_VIEW
276 // Get the image status, default to unknown error.
277 GraphicsCacheItem::ImageStatus status = GraphicsCacheItem::UnknownError;
279 status = cacheHandle->getImageStatus();
281 // Check if the image is now ready.
282 if (status == GraphicsCacheItem::Loaded) {
283 // It is, get it and inform the world.
284 pixmap = cacheHandle->getImage();
286 // Tell BufferView we need to be updated!
287 bv->text->status = LyXText::CHANGED_IN_DRAW;
292 char const * msg = statusMessage();
294 paint.rectangle(int(old_x) + 2, baseline - lascent,
299 // Print the message.
300 LyXFont msgFont(font);
301 msgFont.setFamily(LyXFont::SANS_FAMILY);
302 msgFont.setSize(LyXFont::SIZE_FOOTNOTE);
303 string const justname = OnlyFilename (params.filename);
304 paint.text(int(old_x) + 8,
305 baseline - lyxfont::maxAscent(msgFont) - 4,
308 msgFont.setSize(LyXFont::SIZE_TINY);
309 paint.text(int(old_x) + 8, baseline - 4,
310 msg, strlen(msg), msgFont);
316 void InsetGraphics::Edit(BufferView *bv, int, int, unsigned int)
318 bv->owner()->getDialogs()->showGraphics(this);
322 Inset::EDITABLE InsetGraphics::Editable() const
328 void InsetGraphics::Write(Buffer const * buf, ostream & os) const
330 os << "GRAPHICS FormatVersion 1" << endl;
332 params.Write(buf, os);
336 void InsetGraphics::Read(Buffer const * buf, LyXLex & lex)
338 bool finished = false;
340 while (lex.IsOK() && !finished) {
343 string const token = lex.GetString();
344 lyxerr.debug() << "Token: '" << token << '\'' << endl;
348 } else if (token == "\\end_inset") {
350 } else if (token == "FormatVersion") {
352 int version = lex.GetInteger();
355 << "This document was created with a newer Graphics widget"
356 ", You should use a newer version of LyX to read this"
359 // TODO: Possibly open up a dialog?
362 if (! params.Read(buf, lex, token))
363 lyxerr << "Unknown token, " << token << ", skipping." << endl;
371 void formatResize(ostream & os, string const & key,
372 InsetGraphicsParams::Resize resizeType, double size)
374 switch (resizeType) {
375 case InsetGraphicsParams::DEFAULT_SIZE:
378 case InsetGraphicsParams::CM:
379 os << key << '=' << size << "cm,";
382 case InsetGraphicsParams::INCH:
383 os << key << '=' << size << "in,";
386 case InsetGraphicsParams::PERCENT_PAGE:
387 os << key << '=' << size / 100 << "\\text" << key << ',';
390 case InsetGraphicsParams::PERCENT_COLUMN:
391 os << key << '=' << size / 100 << "\\column" << key << ',';
398 InsetGraphics::createLatexOptions() const
400 // Calculate the options part of the command, we must do it to a string
401 // stream since we might have a trailing comma that we would like to remove
402 // before writing it to the output stream.
403 std::ostringstream options;
405 formatResize(options, "width", params.widthResize, params.widthSize);
406 formatResize(options, "height", params.heightResize, params.heightSize);
408 if (params.rotateAngle != 0) {
410 << params.rotateAngle << ',';
413 string opts = options.str().c_str();
414 opts = strip(opts, ',');
422 InsetGraphics::prepareFile(Buffer const *buf) const
425 // do_convert = Do we need to convert the file?
426 // nice = Do we create a nice version?
427 // This is used when exporting the latex file only.
431 // return original filename
434 // convert_place = temp directory
435 // return new filename in temp directory
437 // convert_place = original file directory
438 // return original filename without the extension
441 // Get the extension (format) of the original file.
442 string const extension = GetExtension(params.filename);
444 // Are we creating a PDF or a PS file?
445 // (Should actually mean, are we usind latex or pdflatex).
446 string const image_target = (lyxrc.pdf_mode ? "png" : "eps");
448 if (extension == image_target)
449 return params.filename;
452 if (!buf->niceFile) {
453 string const temp = AddName(buf->tmppath, params.filename);
454 outfile = RemoveExtension(temp);
456 string const path = OnlyPath(buf->fileName());
457 string const relname = MakeRelPath(params.filename, path);
458 outfile = RemoveExtension(relname);
461 converters.Convert(buf, params.filename, outfile, extension, image_target);
466 int InsetGraphics::Latex(Buffer const *buf, ostream & os,
467 bool /*fragile*/, bool/*fs*/) const
469 // MISSING: We have to decide how to do the order of the options
470 // that is dependent of order, like width, height, angle. Should
471 // we rotate before scale? Should we let the user decide?
472 // bool rot_before_scale; ?
474 // (BE) As a first step we should do a scale before rotate since this is
475 // more like the natural thought of how to do it.
476 // (BE) I believe that a priority list presented to the user with
477 // a default order would be the best, though it would be better to
478 // hide such a thing in an "Advanced options" dialog.
479 // (BE) This should go an advanced LaTeX options dialog.
481 // If there is no file specified, just output a message about it in
483 if (params.filename.empty()) {
484 os << "\\fbox{\\rule[-0.5in]{0pt}{1in}"
485 << _("empty figure path")
488 return 1; // One end of line marker added to the stream.
491 // Keep count of newlines that we issued.
494 // This variables collect all the latex code that should be before and
495 // after the actual includegraphics command.
499 // If it's not an inline image, surround it with the centering paragraph.
500 if (! params.inlineFigure) {
501 before += "\n" "\\vspace{0.3cm}\n" "{\\par\\centering ";
502 after = " \\par}\n" "\\vspace{0.3cm}\n" + after;
506 // Do we want subcaptions?
507 if (params.subcaption) {
508 before += "\\subfigure[" + params.subcaptionText + "]{";
512 // We never use the starred form, we use the "clip" option instead.
513 os << before << "\\includegraphics";
515 // Write the options if there are any.
516 string const opts = createLatexOptions();
518 os << '[' << opts << ']';
521 // Make the filename relative to the lyx file
522 // and remove the extension so the LaTeX will use whatever is
523 // appropriate (when there are several versions in different formats)
524 string const filename = prepareFile(buf);
526 os << '{' << filename << '}' << after;
528 // Return how many newlines we issued.
533 int InsetGraphics::Ascii(Buffer const *, ostream &, int) const
535 // No graphics in ascii output. Possible to use gifscii to convert
536 // images to ascii approximation.
538 // 1. Convert file to ascii using gifscii
539 // 2. Read ascii output file and add it to the output stream.
545 int InsetGraphics::Linuxdoc(Buffer const *, ostream &) const
547 // No graphics in LinuxDoc output. Should check how/what to add.
551 // For explanation on inserting graphics into DocBook checkout:
552 // http://linuxdoc.org/LDP/LDP-Author-Guide/inserting-pictures.html
553 // See also the docbook guide at http://www.docbook.org/
554 int InsetGraphics::DocBook(Buffer const * buf, ostream & os) const
556 // Change the path to be relative to the main file.
557 string const buffer_dir = OnlyPath(buf->fileName());
558 string const filename = RemoveExtension(MakeRelPath(params.filename, buffer_dir));
560 // In DocBook v5.0, the graphic tag will be eliminated from DocBook, will
561 // need to switch to MediaObject. However, for now this is sufficient and
563 os << "<graphic fileref=\"" << filename << "\"></graphic>";
568 void InsetGraphics::Validate(LaTeXFeatures & features) const
570 // If we have no image, we should not require anything.
571 if (params.filename.empty())
574 features.graphicx = true;
576 if (params.subcaption)
577 features.subfigure = true;
580 // Update the inset after parameters changed (read from file or changed in
582 void InsetGraphics::updateInset() const
584 #ifdef INSETGRAPHICS_INLINE_VIEW
586 GraphicsCache * gc = GraphicsCache::getInstance();
587 GraphicsCacheItem * temp = 0;
589 if (!params.filename.empty()) {
590 temp = gc->addFile(params.filename);
601 bool InsetGraphics::setParams(InsetGraphicsParams const & params)
603 // If nothing is changed, just return and say so.
604 if (this->params == params)
607 // Copy the new parameters.
608 this->params = params;
610 // Update the inset with the new parameters.
613 // We have changed data, report it.
617 InsetGraphicsParams InsetGraphics::getParams() const
622 Inset * InsetGraphics::Clone(Buffer const &) const
624 InsetGraphics * newInset = new InsetGraphics;
627 newInset->cacheHandle = cacheHandle->Clone();
629 newInset->cacheHandle = 0;
630 newInset->pixmap = pixmap;
631 newInset->updateImage = updateImage;
633 newInset->setParams(getParams());