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
194 // Initialize only those variables that do not have a constructor.
195 InsetGraphics::InsetGraphics()
197 : use_bb(false), hiresbb(false), angle(0.0), origin(DEFAULT)
198 , keepaspectratio(false), scale(0.0), clip(false), draft(false)
201 : cacheHandle(0), pixmap(0), pixmapInitialized(false)
204 InsetGraphics::~InsetGraphics()
206 // Emits the hide signal to the dialog connected (if any)
211 InsetGraphics::statusMessage() const
213 char const * msg = 0;
215 #ifdef INSETGRAPHICS_INLINE_VIEW
217 case GraphicsCacheItem::UnknownError:
218 msg = _("Unknown Error");
221 case GraphicsCacheItem::Loading:
222 msg = _("Loading...");
225 case GraphicsCacheItem::ErrorReading:
226 msg = _("Error reading");
229 case GraphicsCacheItem::ErrorConverting:
230 msg = _("Error converting");
233 case GraphicsCacheItem::Loaded:
234 // No message to write.
238 msg = _("Inline view disabled");
244 int InsetGraphics::ascent(BufferView *, LyXFont const &) const
246 if (pixmapInitialized)
247 return cacheHandle->getHeight();
253 int InsetGraphics::descent(BufferView *, LyXFont const &) const
255 // this is not true if viewport is used and clip is not.
260 int InsetGraphics::width(BufferView *, LyXFont const & font) const
262 if (pixmapInitialized)
263 return cacheHandle->getWidth();
265 char const * msg = statusMessage();
266 int font_width = lyxfont::width(msg, font);
268 return max(50, font_width + 15);
273 void InsetGraphics::draw(BufferView * bv, LyXFont const & font,
274 int baseline, float & x, bool) const
276 Painter & paint = bv->painter();
278 int lwidth = width(bv, font);
279 int ldescent = descent(bv, font);
280 int lascent = ascent(bv, font);
282 // This will draw the graphics. If the graphics has not been loaded yet,
283 // we draw just a rectangle.
284 if (pixmapInitialized) {
286 paint.image(int(x) + 2, baseline - lascent,
287 lwidth - 4, lascent + ldescent,
290 #ifdef INSETGRAPHICS_INLINE_VIEW
291 // Get the image status, default to unknown error.
292 GraphicsCacheItem::ImageStatus status = GraphicsCacheItem::UnknownError;
294 status = cacheHandle->getImageStatus();
296 // Check if the image is now ready.
297 if (status == GraphicsCacheItem::Loaded) {
298 // It is, get it and inform the world.
299 pixmap = cacheHandle->getImage();
300 pixmapInitialized = true;
302 // Tell BufferView we need to be updated!
303 bv->text->status = LyXText::CHANGED_IN_DRAW;
308 char const * msg = statusMessage();
310 paint.rectangle(int(x) + 2, baseline - lascent,
315 // Print the message.
316 LyXFont msgFont(font);
317 msgFont.setFamily(LyXFont::SANS_FAMILY);
318 msgFont.setSize(LyXFont::SIZE_FOOTNOTE);
319 string const justname = OnlyFilename (params.filename);
320 paint.text(int(x + 8), baseline - lyxfont::maxAscent(msgFont) - 4,
323 msgFont.setSize(LyXFont::SIZE_TINY);
324 paint.text(int(x + 8), baseline - 4, msg, strlen(msg), msgFont);
328 // Add the image width to the row width.
333 void InsetGraphics::Edit(BufferView *bv, int, int, unsigned int)
335 bv->owner()->getDialogs() -> showGraphics(this);
339 Inset::EDITABLE InsetGraphics::Editable() const
345 void InsetGraphics::Write(Buffer const * buf, ostream & os) const
347 os << "GRAPHICS FormatVersion 1" << endl;
349 params.Write(buf, os);
353 // Baruch Even 2000-07-08
355 // A Thought for another way to read the file...
356 // The map should be a static part of the object or a static part of this
357 // file and should be filled during program start.
358 // The questions are:
359 // 1. Is this cleaner?
360 // 2. Is there no hidden performance costs?
362 // Regarding 2 I can already see that we will have two copies of the strings
363 // one in the data part of the program and one in the map, but that won't be
364 // more than say 2K (overestimation here), there is no real benefit to put
365 // it in the map since there aren't that many configuration items that will
366 // make it a faster solution, it might just be a bit cleaner.
367 // (a map stores either in a hash or a kind of a balanced tree).
369 void InsetGraphics::Read(Buffer const * buf, LyXLex & lex)
371 typedef map < string, enum TOKENS > ReadActionMap;
372 static ReadActionMap const readMap;
374 bool finished = false;
376 while (lex.IsOK() && !finished) {
379 string const token = lex.GetString();
380 lyxerr.debug() << "Token: '" << token << '\'' << endl;
385 ReadActionMap::const_iterator it =
388 if (it == readMap.end()) {
389 lyxerr << "Unknown keyword, skipping." << endl;
407 void InsetGraphics::Read(Buffer const * buf, LyXLex & lex)
409 bool finished = false;
411 while (lex.IsOK() && !finished) {
414 string const token = lex.GetString();
415 lyxerr.debug() << "Token: '" << token << '\'' << endl;
419 } else if (token == "\\end_inset") {
421 } else if (token == "FormatVersion") {
423 int version = lex.GetInteger();
426 << "This document was created with a newer Graphics widget"
427 ", You should use a newer version of LyX to read this"
430 // TODO: Possibly open up a dialog?
433 if (! params.Read(buf, lex, token))
434 lyxerr << "Unknown token, " << token << ",skipping." << endl;
442 void formatResize(ostream & os, string const & key,
443 InsetGraphicsParams::Resize resizeType, double size)
445 switch (resizeType) {
446 case InsetGraphicsParams::DEFAULT_SIZE:
449 case InsetGraphicsParams::CM:
450 os << key << '=' << size << "cm,";
453 case InsetGraphicsParams::INCH:
454 os << key << '=' << size << "in,";
457 case InsetGraphicsParams::PERCENT_PAGE:
458 os << key << '=' << size / 100 << "\\text" << key << ',';
461 case InsetGraphicsParams::PERCENT_COLUMN:
462 os << key << '=' << size / 100 << "\\column" << key << ',';
468 int InsetGraphics::Latex(Buffer const *buf, ostream & os,
469 bool /*fragile*/, bool/*fs*/) const
471 // MISSING: We have to decide how to do the order of the options
472 // that is dependent of order, like witdth, height, angle. Should
473 // we rotate before scale? Should we let the user decide?
474 // bool rot_before_scale; ?
476 // (BE) As a first step we should do a scale before rotate since this is
477 // more like the natural thought of how to do it.
478 // (BE) I believe that a priority list presented to the user with
479 // a default order would be the best, though it would be better to
480 // hide such a thing in an "Advanced options" dialog.
481 // (BE) This should go an advanced LaTeX options dialog.
483 // If there is no file specified, just output a message about it in
485 if (params.filename.empty()) {
486 os << "\\fbox{\\rule[-0.5in]{0pt}{1in}"
487 << _("empty figure path")
494 // Calculate the options part of the command, we must do it to a string
495 // stream since we might have a trailing comma that we would like to remove
496 // before writing it to the output stream.
497 std::ostringstream options;
499 formatResize(options, "width", params.widthResize, params.widthSize);
500 formatResize(options, "height", params.heightResize, params.heightSize);
502 if (params.rotateAngle != 0) {
504 << params.rotateAngle << ',';
508 if (bb.isSet() && use_bb) {
510 << bb.llx << ' ' << bb.lly << ' '
511 << bb.urx << ' ' << bb.ury << ',';
514 options << "hiresbb,";
516 if (viewport.isSet()) {
517 options << "viewport="
518 << viewport.llx << ' ' << viewport.lly << ' '
519 << viewport.urx << ' ' << viewport.ury << ',';
523 << trim.llx << ' ' << trim.lly << ' '
524 << trim.urx << ' ' << trim.ury << ',';
526 if (natheight.value() != 0) {
527 options << "natheight=" << natheight.asString() << ',';
529 if (natwidth.value() != 0) {
530 options << "natwidth=" << natwidth.asString() << ',';
533 options << "angle=" << angle << ',';
535 if (origin != DEFAULT) {
539 options << "origin=lt,";
542 options << "origin=lc,";
545 options << "origin=lB,";
548 options << "origin=lb,";
551 options << "origin=ct,";
554 options << "origin=c,";
557 options << "origin=cB,";
560 options << "origin=cb,";
563 options << "origin=rt,";
566 options << "origin=rc,";
569 options << "origin=rB,";
572 options << "origin=rb,";
576 if (g_width.value() != 0) {
577 options << "width=" << g_width.asString() << ',';
579 if (g_height.value() != 0) {
580 options << "height=" << g_height.asString() << ',';
582 if (totalheight.value() != 0) {
583 options << "totalheight=" << totalheight.asString() << ',';
585 if (keepaspectratio) {
586 options << "keepaspectratio,";
589 options << "scale=" << scale << ',';
598 options << "type=" << type << ',';
600 // These should be present only when type is used.
602 options << "ext=" << type << ',';
605 options << "read=" << type << ',';
607 if (!command.empty()) {
608 options << "command=" << type << ',';
613 string opts(options.str().c_str());
614 opts = strip(opts, ',');
617 // If it's not an inline image, surround it with the centering paragraph.
618 if (! params.inlineFigure) {
620 << "\\vspace{0.3cm}" << endl
621 << "{\\par\\centering ";
624 // Do we want subcaptions?
625 if (params.subcaption) {
626 os << "\\subfigure[" << params.subcaptionText << "]{";
629 // We never used the starred form, we use the "clip" option instead.
630 os << "\\includegraphics";
633 os << '[' << opts << ']';
636 // Make the filename relative to the lyx file
637 string filename = MakeRelPath(params.filename, OnlyPath(buf->fileName()));
639 // and remove the extension so the LaTeX will use whatever is
640 // appropriate (when there are several versions in different formats)
641 filename = ChangeExtension(filename, string());
643 os << '{' << filename << '}';
645 // Do we want a subcaption?
646 if (params.subcaption) {
647 // Close the subcaption command
651 // Is this an inline graphics?
652 if (!params.inlineFigure) {
653 os << " \\par}" << endl
654 << "\\vspace{0.3cm}" << endl;
657 // How do we decide to what format should we export?
658 const string empty_string = string();
659 const string eps_outfile = ChangeExtension(params.filename, "eps");
660 const string png_outfile = ChangeExtension(params.filename, "png");
662 Converter::Convert(buf, params.filename, eps_outfile, empty_string);
663 Converter::Convert(buf, params.filename, png_outfile, empty_string);
669 int InsetGraphics::Ascii(Buffer const *, ostream &, int) const
671 // No graphics in ascii output.
676 int InsetGraphics::Linuxdoc(Buffer const *, ostream &) const
678 // No graphics in LinuxDoc output. Should check how/what to add.
683 int InsetGraphics::DocBook(Buffer const *, ostream &) const
685 // No graphics in DocBook output. Should check how/what to add.
690 void InsetGraphics::Validate(LaTeXFeatures & features) const
692 // If we have no image, we should not require anything.
693 if (params.filename.empty())
696 features.graphicx = true;
698 if (params.subcaption)
699 features.subfigure = true;
702 // Update the inset after parameters changed (read from file or changed in
704 void InsetGraphics::updateInset() const
706 // If file changed...
708 #ifdef INSETGRAPHICS_INLINE_VIEW
709 GraphicsCache * gc = GraphicsCache::getInstance();
710 GraphicsCacheItem * temp = 0;
712 if (!params.filename.empty()) {
713 temp = gc->addFile(params.filename);
723 bool InsetGraphics::setParams(InsetGraphicsParams const & params)
725 // If nothing is changed, just return and say so.
726 if (this->params == params)
729 // Copy the new parameters.
730 this->params = params;
732 // Update the inset with the new parameters.
735 // We have changed data, report it.
739 InsetGraphicsParams InsetGraphics::getParams() const
744 Inset * InsetGraphics::Clone(Buffer const &) const
746 InsetGraphics * newInset = new InsetGraphics;
749 newInset->cacheHandle = cacheHandle->Clone();
751 newInset->cacheHandle = 0;
752 newInset->pixmap = pixmap;
753 newInset->pixmapInitialized = pixmapInitialized;
755 newInset->setParams(getParams());