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,
22 * Add messages in the empty rectangle to say how are we doing.
23 - Implemented, needs testing.
24 * Clean up GraphicsCacheItem(_pimpl)
25 * Pop up a dialog if the widget version is higher than what we accept.
26 * Prepare code to read FigInset insets to upgrade upwards
27 * Provide sed/awk/C code to downgrade from InsetGraphics to FigInset.
34 * If the image is from the clipart, and the document is moved to another
35 directory, the user is screwed. Need a way to handle it.
36 This amounts to a problem of when to use relative or absolute file paths
37 We should probably use what the user asks to use... but when he chooses
38 by the file dialog we normally get an absolute path and this may not be
40 * Bug in FileDlg class (src/filedlg.[hC]) when selecting a file and then
41 pressing ok, it counts as if no real selection done. Apparently
42 when choosing a file it doesn't update the select file input line.
43 * Inline viewing is still not completely operational, in fact it is no
44 disabled. To enable it enable the define:
45 INSETGRAPHICS_INLINE_VIEW
49 * How to support both PDF and PS output, should we do the conversion
50 or should we just give the bounding box and tell latex how to do the
52 I (Baruch Even) tend towards doing the conversion ourselves, otherwise
53 we need to give latex quite a few translation commands and from the
54 graphicx package docs it appears that it takes quite a bit of memory
55 on the side of TeXing.
59 * Add support for more features so that it will be better than insetfig.
60 * Keep aspect ratio radio button
62 * Work on inline viewing of image.
64 TODO Before initial production release:
65 * Replace insetfig everywhere
66 * Read it's file format
67 * Get created by all commands used to create figinset currently.
68 * Search for comments of the form
69 // INSET_GRAPHICS: remove this when InsetFig is thrown.
72 * Finish the basic To-do list.
73 * Extract the general logic of the dialog in order to allow easier porting
74 to Gnome/KDE, and put the general logic in frontends and the inherited
75 platform dependent code in the appropriate dirs.
77 TODO Extended features:
79 * Advanced Latex tab folder.
80 * Add even more options to make it better than insetfig.
81 * Support for complete control over the latex parameters for TeXperts
82 * What advanced features the users want to do?
83 Implement them in a non latex dependent way, but a logical way.
84 LyX should translate it to latex or any other fitting format.
85 * Add a way to roll the image file into the file format.
86 * When loading if the image is not found in the expected place, try
87 to find it in the clipart, or in the same directory with the image.
88 * Keep a tab on the image file, if it changes, update the lyx view.
89 * The image choosing dialog could show thumbnails of the image formats
90 it knows of, thus selection based on the image instead of based on
92 * Add support for the 'picins' package.
93 * Add support for the 'picinpar' package.
94 * Improve support for 'subfigure' - Allow to set the various options
101 * This is currently a moving target, I'm trying stuff and learning what
102 * is needed and how to accomplish it, since there is no predefined goal or
103 * way to go I invent it as I go.
105 * My current intention is for seperation from LaTeX, the basic needs are
106 * resizing and rotating, displaying on screen in various depths and printing
107 * conversion of depths (independent of the display depth). For this I'll
108 * provide a simple interface.
110 * The medium level includes clipping of the image, but in a limited way.
112 * For the LaTeX gurus I'll provide a complete control over the output, but
113 * this is latex dependent and guru dependent so I'd rather avoid doing this
114 * for the normal user. This stuff includes clipping, special image size
115 * specifications (\textwidth\minus 2in) which I see no way to generalize
116 * to non-latex specific way.
119 * 'graphicx' for the graphics inclusion.
120 * 'subfigure' for the subfigures.
124 * Current version is 1 (inset file format version), when changing it
125 * it should be changed in the Write() function when writing in one place
126 * and when reading one should change the version check and the error message.
128 * The filename is kept in the lyx file in a relative way, so as to allow
129 * moving the document file and its images with no problem.
133 * Apparently the PNG output is preferred over PDF images when doing PDF
134 * documents (i.e. prefer imagemagick eps2png over eps2pdf)
141 * Finish basic support:
142 * Inline image viewing
144 * Do Release quality support:
145 * Allow to change display depth
146 * Make default figure instead of InsetFig
147 * Add to LyX (probably after 1.1.6 is released)
150 * Output format conversion
151 * Print depth changes
152 * Image file tracking of changes.
155 * Image roll-in (how? when? why?)
156 * This means to add the image inside the LyX file, usefull when
157 * transferring the file around.
164 #pragma implementation
167 #include "insets/insetgraphics.h"
168 #include "insets/insetgraphicsParams.h"
169 #include "graphics/GraphicsCache.h"
170 #include "graphics/GraphicsCacheItem.h"
172 #include "frontends/Dialogs.h"
175 #include "BufferView.h"
176 #include "converter.h"
177 #include "frontends/support/LyXImage.h"
179 #include "lyx_gui_misc.h"
181 #include "support/FileInfo.h"
182 #include "support/filetools.h"
184 #include "font.h" // For the lyxfont class.
185 #include <algorithm> // For the std::max
195 // Initialize only those variables that do not have a constructor.
196 InsetGraphics::InsetGraphics()
198 : use_bb(false), hiresbb(false), angle(0.0), origin(DEFAULT)
199 , keepaspectratio(false), scale(0.0), clip(false), draft(false)
202 : cacheHandle(0), pixmap(0), pixmapInitialized(false)
205 InsetGraphics::~InsetGraphics()
207 // Emits the hide signal to the dialog connected (if any)
212 InsetGraphics::statusMessage() const
214 char const * msg = 0;
216 #ifdef INSETGRAPHICS_INLINE_VIEW
218 case GraphicsCacheItem::UnknownError:
219 msg = _("Unknown Error");
222 case GraphicsCacheItem::Loading:
223 msg = _("Loading...");
226 case GraphicsCacheItem::ErrorReading:
227 msg = _("Error reading");
230 case GraphicsCacheItem::ErrorConverting:
231 msg = _("Error converting");
234 case GraphicsCacheItem::Loaded:
235 // No message to write.
239 msg = _("Inline view disabled");
245 int InsetGraphics::ascent(BufferView *, LyXFont const &) const
247 if (pixmapInitialized)
248 return cacheHandle->getHeight();
254 int InsetGraphics::descent(BufferView *, LyXFont const &) const
256 // this is not true if viewport is used and clip is not.
261 int InsetGraphics::width(BufferView *, LyXFont const & font) const
263 if (pixmapInitialized)
264 return cacheHandle->getWidth();
266 char const * msg = statusMessage();
267 int font_width = lyxfont::width(msg, font);
269 return max(50, font_width + 15);
274 void InsetGraphics::draw(BufferView * bv, LyXFont const & font,
275 int baseline, float & x, bool) const
277 Painter & paint = bv->painter();
279 int lwidth = width(bv, font);
280 int ldescent = descent(bv, font);
281 int lascent = ascent(bv, font);
283 // This will draw the graphics. If the graphics has not been loaded yet,
284 // we draw just a rectangle.
285 if (pixmapInitialized) {
287 paint.image(int(x) + 2, baseline - lascent,
288 lwidth - 4, lascent + ldescent,
291 #ifdef INSETGRAPHICS_INLINE_VIEW
292 // Get the image status, default to unknown error.
293 GraphicsCacheItem::ImageStatus status = GraphicsCacheItem::UnknownError;
295 status = cacheHandle->getImageStatus();
297 // Check if the image is now ready.
298 if (status == GraphicsCacheItem::Loaded) {
299 // It is, get it and inform the world.
300 pixmap = cacheHandle->getImage();
301 pixmapInitialized = true;
303 // Tell BufferView we need to be updated!
304 bv->text->status = LyXText::CHANGED_IN_DRAW;
309 char const * msg = statusMessage();
311 paint.rectangle(int(x) + 2, baseline - lascent,
316 // Print the message.
317 LyXFont msgFont(font);
318 msgFont.setFamily(LyXFont::SANS_FAMILY);
319 msgFont.setSize(LyXFont::SIZE_FOOTNOTE);
320 string const justname = OnlyFilename (params.filename);
321 paint.text(int(x + 8), baseline - lyxfont::maxAscent(msgFont) - 4,
324 msgFont.setSize(LyXFont::SIZE_TINY);
325 paint.text(int(x + 8), baseline - 4, msg, strlen(msg), msgFont);
329 // Add the image width to the row width.
334 void InsetGraphics::Edit(BufferView *bv, int, int, unsigned int)
336 bv->owner()->getDialogs() -> showGraphics(this);
340 Inset::EDITABLE InsetGraphics::Editable() const
346 void InsetGraphics::Write(Buffer const * buf, ostream & os) const
348 os << "GRAPHICS FormatVersion 1" << endl;
350 params.Write(buf, os);
354 // Baruch Even 2000-07-08
356 // A Thought for another way to read the file...
357 // The map should be a static part of the object or a static part of this
358 // file and should be filled during program start.
359 // The questions are:
360 // 1. Is this cleaner?
361 // 2. Is there no hidden performance costs?
363 // Regarding 2 I can already see that we will have two copies of the strings
364 // one in the data part of the program and one in the map, but that won't be
365 // more than say 2K (overestimation here), there is no real benefit to put
366 // it in the map since there aren't that many configuration items that will
367 // make it a faster solution, it might just be a bit cleaner.
368 // (a map stores either in a hash or a kind of a balanced tree).
370 void InsetGraphics::Read(Buffer const * buf, LyXLex & lex)
372 typedef map < string, enum TOKENS > ReadActionMap;
373 static ReadActionMap const readMap;
375 bool finished = false;
377 while (lex.IsOK() && !finished) {
380 string const token = lex.GetString();
381 lyxerr.debug() << "Token: '" << token << '\'' << endl;
386 ReadActionMap::const_iterator it =
389 if (it == readMap.end()) {
390 lyxerr << "Unknown keyword, skipping." << endl;
408 void InsetGraphics::Read(Buffer const * buf, LyXLex & lex)
410 bool finished = false;
412 while (lex.IsOK() && !finished) {
415 string const token = lex.GetString();
416 lyxerr.debug() << "Token: '" << token << '\'' << endl;
420 } else if (token == "\\end_inset") {
422 } else if (token == "FormatVersion") {
424 int version = lex.GetInteger();
427 << "This document was created with a newer Graphics widget"
428 ", You should use a newer version of LyX to read this"
431 // TODO: Possibly open up a dialog?
434 if (! params.Read(buf, lex, token))
435 lyxerr << "Unknown token, " << token << ",skipping." << endl;
443 void formatResize(ostream & os, string const & key,
444 InsetGraphicsParams::Resize resizeType, double size)
446 switch (resizeType) {
447 case InsetGraphicsParams::DEFAULT_SIZE:
450 case InsetGraphicsParams::CM:
451 os << key << '=' << size << "cm,";
454 case InsetGraphicsParams::INCH:
455 os << key << '=' << size << "in,";
458 case InsetGraphicsParams::PERCENT_PAGE:
459 os << key << '=' << size / 100 << "\\text" << key << ',';
462 case InsetGraphicsParams::PERCENT_COLUMN:
463 os << key << '=' << size / 100 << "\\column" << key << ',';
469 int InsetGraphics::Latex(Buffer const *buf, ostream & os,
470 bool /*fragile*/, bool/*fs*/) const
472 // MISSING: We have to decide how to do the order of the options
473 // that is dependent of order, like witdth, height, angle. Should
474 // we rotate before scale? Should we let the user decide?
475 // bool rot_before_scale; ?
477 // (BE) As a first step we should do a scale before rotate since this is
478 // more like the natural thought of how to do it.
479 // (BE) I believe that a priority list presented to the user with
480 // a default order would be the best, though it would be better to
481 // hide such a thing in an "Advanced options" dialog.
482 // (BE) This should go an advanced LaTeX options dialog.
484 // If there is no file specified, just output a message about it in
486 if (params.filename.empty()) {
487 os << "\\fbox{\\rule[-0.5in]{0pt}{1in}"
488 << _("empty figure path")
495 // Calculate the options part of the command, we must do it to a string
496 // stream since we might have a trailing comma that we would like to remove
497 // before writing it to the output stream.
498 std::ostringstream options;
500 formatResize(options, "width", params.widthResize, params.widthSize);
501 formatResize(options, "height", params.heightResize, params.heightSize);
503 if (params.rotateAngle != 0) {
505 << params.rotateAngle << ',';
509 if (bb.isSet() && use_bb) {
511 << bb.llx << ' ' << bb.lly << ' '
512 << bb.urx << ' ' << bb.ury << ',';
515 options << "hiresbb,";
517 if (viewport.isSet()) {
518 options << "viewport="
519 << viewport.llx << ' ' << viewport.lly << ' '
520 << viewport.urx << ' ' << viewport.ury << ',';
524 << trim.llx << ' ' << trim.lly << ' '
525 << trim.urx << ' ' << trim.ury << ',';
527 if (natheight.value() != 0) {
528 options << "natheight=" << natheight.asString() << ',';
530 if (natwidth.value() != 0) {
531 options << "natwidth=" << natwidth.asString() << ',';
534 options << "angle=" << angle << ',';
536 if (origin != DEFAULT) {
540 options << "origin=lt,";
543 options << "origin=lc,";
546 options << "origin=lB,";
549 options << "origin=lb,";
552 options << "origin=ct,";
555 options << "origin=c,";
558 options << "origin=cB,";
561 options << "origin=cb,";
564 options << "origin=rt,";
567 options << "origin=rc,";
570 options << "origin=rB,";
573 options << "origin=rb,";
577 if (g_width.value() != 0) {
578 options << "width=" << g_width.asString() << ',';
580 if (g_height.value() != 0) {
581 options << "height=" << g_height.asString() << ',';
583 if (totalheight.value() != 0) {
584 options << "totalheight=" << totalheight.asString() << ',';
586 if (keepaspectratio) {
587 options << "keepaspectratio,";
590 options << "scale=" << scale << ',';
599 options << "type=" << type << ',';
601 // These should be present only when type is used.
603 options << "ext=" << type << ',';
606 options << "read=" << type << ',';
608 if (!command.empty()) {
609 options << "command=" << type << ',';
614 string opts(options.str().c_str());
615 opts = strip(opts, ',');
618 // If it's not an inline image, surround it with the centering paragraph.
619 if (! params.inlineFigure) {
621 << "\\vspace{0.3cm}" << endl
622 << "{\\par\\centering ";
625 // Do we want subcaptions?
626 if (params.subcaption) {
627 os << "\\subfigure[" << params.subcaptionText << "]{";
630 // We never used the starred form, we use the "clip" option instead.
631 os << "\\includegraphics";
634 os << '[' << opts << ']';
637 // Make the filename relative to the lyx file
638 string filename = MakeRelPath(params.filename, OnlyPath(buf->fileName()));
640 // and remove the extension so the LaTeX will use whatever is
641 // appropriate (when there are several versions in different formats)
642 filename = ChangeExtension(filename, string());
644 os << '{' << filename << '}';
646 // Do we want a subcaption?
647 if (params.subcaption) {
648 // Close the subcaption command
652 // Is this an inline graphics?
653 if (!params.inlineFigure) {
654 os << " \\par}" << endl
655 << "\\vspace{0.3cm}" << endl;
658 // How do we decide to what format should we export?
659 string extension = GetExtension(params.filename);
661 if (extension != "jpg")
662 Converter::Convert(buf,
663 params.filename, params.filename,
666 Converter::Convert(buf, params.filename, params.filename,
673 int InsetGraphics::Ascii(Buffer const *, ostream &, int) const
675 // No graphics in ascii output.
680 int InsetGraphics::Linuxdoc(Buffer const *, ostream &) const
682 // No graphics in LinuxDoc output. Should check how/what to add.
687 int InsetGraphics::DocBook(Buffer const *, ostream &) const
689 // No graphics in DocBook output. Should check how/what to add.
694 void InsetGraphics::Validate(LaTeXFeatures & features) const
696 // If we have no image, we should not require anything.
697 if (params.filename.empty())
700 features.graphicx = true;
702 if (params.subcaption)
703 features.subfigure = true;
706 // Update the inset after parameters changed (read from file or changed in
708 void InsetGraphics::updateInset() const
710 // If file changed...
712 #ifdef INSETGRAPHICS_INLINE_VIEW
713 GraphicsCache * gc = GraphicsCache::getInstance();
714 GraphicsCacheItem * temp = 0;
716 if (!params.filename.empty()) {
717 temp = gc->addFile(params.filename);
727 bool InsetGraphics::setParams(InsetGraphicsParams const & params)
729 // If nothing is changed, just return and say so.
730 if (this->params == params)
733 // Copy the new parameters.
734 this->params = params;
736 // Update the inset with the new parameters.
739 // We have changed data, report it.
743 InsetGraphicsParams InsetGraphics::getParams() const
748 Inset * InsetGraphics::Clone(Buffer const &) const
750 InsetGraphics * newInset = new InsetGraphics;
753 newInset->cacheHandle = cacheHandle->Clone();
755 newInset->cacheHandle = 0;
756 newInset->pixmap = pixmap;
757 newInset->pixmapInitialized = pixmapInitialized;
759 newInset->setParams(getParams());