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 * Add the GraphicsCache and FormatTranslator in order to get inline
19 viewing of the figures.
26 * If the image is from the clipart, and the document is moved to another
27 directory, the user is screwed. Need a way to handle it.
28 This amounts to a problem of when to use relative or absolute file paths
29 We should probably use what the user asks to use... but when he chooses
30 by the file dialog we normally get an absolute path and this may not be
32 * Bug in FileDlg class (src/filedlg.[hC]) when selecting a file and then
33 pressing ok, it counts as if no real selection done. Apparently it
34 when choosing a file it doesn't update the select file input line.
38 * How to support both PDF and PS output, should we do the conversion
39 or should we just give the bounding box and tell latex how to do the
41 I (Baruch Even) tend towards doing the conversion ourselves, otherwise
42 we need to give latex quite a few translation commands and from the
43 graphicx package docs it appears that it takes quite a bit of memory
44 on the side of TeXing.
46 * How do we handle the inline viewing? we may need to show the same image
47 in several formats (color, monochrome, grayscale) or even in different
48 sizes, not to mention rotations!
52 * Add support for more features so that it will be better than insetfig.
53 * Keep aspect ratio radio button
55 * Create the GraphicsCache and FormatTranslator
56 * Add inline viewing of image.
58 TODO Before initial production release:
59 * Replace insetfig everywhere
60 * Read it's file format
61 * Get created by all commands used to create figinset currently.
62 * Search for comments of the form
63 // INSET_GRAPHICS: remove this when InsetFig is thrown.
66 * Pop up a dialog if the widget version is higher than what we accept.
67 * Finish the basic To-do list.
68 * Extract the general logic of the dialog in order to allow easier porting
69 to Gnome/KDE, and put the general logic in frontends and the inherited
70 platform dependent code in the appropriate dirs.
72 TODO Extended features:
74 * Advanced Latex tab folder.
75 * Add even more options to make it better than insetfig.
76 * Support for complete control over the latex parameters for TeXperts
77 * What advanced features the users want to do?
78 Implement them in a non latex dependent way, but a logical way.
79 LyX should translate it to latex or any other fitting format.
80 * Add a way to roll the image file into the file format.
81 * When loading if the image is not found in the expected place, try
82 to find it in the clipart, or in the same directory with the image.
83 * If the dialog had no real change from previous time, do not mark document
85 * Keep a tab on the image file, if it changes, update the lyx view.
86 * The image choosing dialog could show thumbnails of the image formats
87 it knows of, thus selection based on the image instead of based on
94 * This is currently a moving target, I'm trying stuff and learning what
95 * is needed and how to accomplish it, since there is no predefined goal or
96 * way to go I invent it as I go.
98 * My current intention is for seperation from LaTeX, the basic needs are
99 * resizing and rotating, displaying on screen in various depths and printing
100 * conversion of depths (independent of the display depth). For this I'll
101 * provide a simple interface.
103 * The medium level includes clipping of the image, but in a limited way.
105 * For the LaTeX gurus I'll provide a complete control over the output, but
106 * this is latex dependent and guru dependent so I'd rather avoid doing this
107 * for the normal user. This stuff includes clipping, special image size
108 * specifications (\textwidth\minus 2in) which I see no way to generalize
109 * to non-latex specific way.
112 * 'graphicx' for the graphics inclusion.
113 * 'subfigure' for the subfigures.
117 * Current version is 1 (inset file format version), when changing it
118 * it should be changed in the Write() function when writing in one place
119 * and when reading one should change the version check and the error message.
121 * The filename is kept in the lyx file in a relative way, so as to allow
122 * moving the document file and its images with no problem.
126 * Apparently the PNG output is preferred over PDF images when doing PDF
127 * documents (i.e. prefer imagemagick eps2png over eps2pdf)
134 * Finish basic support:
135 * Inline image viewing
136 * Get into lyx-devel as an unactivated inset for the benefit of those
137 * who really need it.
139 * Do Release quality support:
140 * Allow to change display depth
141 * Make default figure instead of InsetFig
142 * Add to LyX (probably after 1.1.6 is released)
145 * Output format conversion
146 * Print depth changes
147 * Image file tracking of changes.
150 * Image roll-in (how? when? why?)
151 * This means to add the image inside the LyX file, usefull when
152 * transferring the file around.
159 #pragma implementation
162 #include "insets/insetgraphics.h"
163 #include "insets/insetgraphicsParams.h"
164 #include "graphics/GraphicsCache.h"
165 #include "graphics/GraphicsCacheItem.h"
167 #include "frontends/Dialogs.h"
170 #include "BufferView.h"
172 #include "lyx_gui_misc.h"
174 #include "support/FileInfo.h"
175 #include "support/filetools.h"
185 // Initialize only those variables that do not have a constructor.
186 InsetGraphics::InsetGraphics()
188 : use_bb(false), hiresbb(false), angle(0.0), origin(DEFAULT)
189 , keepaspectratio(false), scale(0.0), clip(false), draft(false)
192 : cacheHandle(0), pixmapInitialized(false)
195 InsetGraphics::~InsetGraphics()
197 // Emits the hide signal to the dialog connected (if any)
201 int InsetGraphics::ascent(BufferView *, LyXFont const &) const
203 if (pixmapInitialized)
204 return cacheHandle->getHeight();
210 int InsetGraphics::descent(BufferView *, LyXFont const &) const
212 // this is not true if viewport is used and clip is not.
217 int InsetGraphics::width(BufferView *, LyXFont const &) const
219 if (pixmapInitialized)
220 return cacheHandle->getWidth();
226 void InsetGraphics::draw(BufferView * bv, LyXFont const & font,
227 int baseline, float & x, bool) const
229 Painter & paint = bv->painter();
231 int lwidth = width(bv, font);
232 int ldescent = descent(bv, font);
233 int lascent = ascent(bv, font);
235 // This will draw the graphics. If the graphics has not been loaded yet,
236 // we draw just a rectangle.
237 if (pixmapInitialized) {
239 paint.pixmap(int(x) + 2, baseline - lascent,
240 lwidth - 4, lascent + ldescent,
243 paint.rectangle(int(x) + 2, baseline - lascent,
247 // Check if the image is now ready.
249 (cacheHandle->getImageStatus() == GraphicsCacheItem::Loaded)) {
250 pixmap = cacheHandle->getImage();
251 pixmapInitialized = true;
253 // Tell BufferView we need to be updated!
254 bv->text->status = LyXText::CHANGED_IN_DRAW;
258 // Add the image width to the row width.
263 void InsetGraphics::Edit(BufferView *bv, int, int, unsigned int)
265 bv->owner()->getDialogs() -> showGraphics(this);
269 Inset::EDITABLE InsetGraphics::Editable() const
275 void InsetGraphics::Write(Buffer const * buf, ostream & os) const
277 os << "GRAPHICS FormatVersion 1" << endl;
279 params.Write(buf, os);
283 // Baruch Even 2000-07-08
285 // A Thought for another way to read the file...
286 // The map should be a static part of the object or a static part of this
287 // file and should be filled during program start.
288 // The questions are:
289 // 1. Is this cleaner?
290 // 2. Is there no hidden performance costs?
292 // Regarding 2 I can already see that we will have two copies of the strings
293 // one in the data part of the program and one in the map, but that won't be
294 // more than say 2K (overestimation here), there is no real benefit to put
295 // it in the map since there aren't that many configuration items that will
296 // make it a faster solution, it might just be a bit cleaner.
297 // (a map stores either in a hash or a kind of a balanced tree).
299 void InsetGraphics::Read(Buffer const * buf, LyXLex & lex)
301 typedef map < string, enum TOKENS > ReadActionMap;
302 static ReadActionMap const readMap;
304 bool finished = false;
306 while (lex.IsOK() && !finished) {
309 string const token = lex.GetString();
310 lyxerr.debug() << "Token: '" << token << '\'' << endl;
315 ReadActionMap::const_iterator it =
318 if (it == readMap.end()) {
319 lyxerr << "Unknown keyword, skipping." << endl;
337 void InsetGraphics::Read(Buffer const * buf, LyXLex & lex)
339 bool finished = false;
341 while (lex.IsOK() && !finished) {
344 string const token = lex.GetString();
345 lyxerr.debug() << "Token: '" << token << '\'' << endl;
349 } else if (token == "\\end_inset") {
351 } else if (token == "FormatVersion") {
353 int version = lex.GetInteger();
356 << "This document was created with a newer Graphics widget"
357 ", You should use a newer version of LyX to read this"
360 // TODO: Possibly open up a dialog?
363 if (! params.Read(buf, lex, token))
364 lyxerr << "Unknown token, " << token << ",skipping." << endl;
372 void formatResize(ostream & os, string const & key,
373 InsetGraphicsParams::Resize resizeType, double size)
375 switch (resizeType) {
376 case InsetGraphicsParams::DEFAULT_SIZE:
379 case InsetGraphicsParams::CM:
380 os << key << '=' << size << "cm,";
383 case InsetGraphicsParams::INCH:
384 os << key << '=' << size << "in,";
387 case InsetGraphicsParams::PERCENT_PAGE:
388 os << key << '=' << size / 100 << "\\text" << key << ',';
391 case InsetGraphicsParams::PERCENT_COLUMN:
392 os << key << '=' << size / 100 << "\\column" << key << ',';
398 int InsetGraphics::Latex(Buffer const *buf, ostream & os,
399 bool /*fragile*/, bool/*fs*/) const
401 // MISSING: We have to decide how to do the order of the options
402 // that is dependent of order, like witdth, height, angle. Should
403 // we rotate before scale? Should we let the user decide?
404 // bool rot_before_scale; ?
406 // (BE) As a first step we should do a scale before rotate since this is
407 // more like the natural thought of how to do it.
408 // (BE) I believe that a priority list presented to the user with
409 // a default order would be the best, though it would be better to
410 // hide such a thing in an "Advanced options" dialog.
411 // (BE) This should go an advanced LaTeX options dialog.
413 // If there is no file specified, just output a message about it in
415 if (params.filename.empty()) {
416 os << "\\fbox{\\rule[-0.5in]{0pt}{1in}"
417 << _("empty figure path")
424 // Calculate the options part of the command, we must do it to a string
425 // stream since we might have a trailing comma that we would like to remove
426 // before writing it to the output stream.
427 std::ostringstream options;
429 formatResize(options, "width", params.widthResize, params.widthSize);
430 formatResize(options, "height", params.heightResize, params.heightSize);
432 if (params.rotateAngle != 0) {
434 << params.rotateAngle << ',';
438 if (bb.isSet() && use_bb) {
440 << bb.llx << ' ' << bb.lly << ' '
441 << bb.urx << ' ' << bb.ury << ',';
444 options << "hiresbb,";
446 if (viewport.isSet()) {
447 options << "viewport="
448 << viewport.llx << ' ' << viewport.lly << ' '
449 << viewport.urx << ' ' << viewport.ury << ',';
453 << trim.llx << ' ' << trim.lly << ' '
454 << trim.urx << ' ' << trim.ury << ',';
456 if (natheight.value() != 0) {
457 options << "natheight=" << natheight.asString() << ',';
459 if (natwidth.value() != 0) {
460 options << "natwidth=" << natwidth.asString() << ',';
463 options << "angle=" << angle << ',';
465 if (origin != DEFAULT) {
469 options << "origin=lt,";
472 options << "origin=lc,";
475 options << "origin=lB,";
478 options << "origin=lb,";
481 options << "origin=ct,";
484 options << "origin=c,";
487 options << "origin=cB,";
490 options << "origin=cb,";
493 options << "origin=rt,";
496 options << "origin=rc,";
499 options << "origin=rB,";
502 options << "origin=rb,";
506 if (g_width.value() != 0) {
507 options << "width=" << g_width.asString() << ',';
509 if (g_height.value() != 0) {
510 options << "height=" << g_height.asString() << ',';
512 if (totalheight.value() != 0) {
513 options << "totalheight=" << totalheight.asString() << ',';
515 if (keepaspectratio) {
516 options << "keepaspectratio,";
519 options << "scale=" << scale << ',';
528 options << "type=" << type << ',';
530 // These should be present only when type is used.
532 options << "ext=" << type << ',';
535 options << "read=" << type << ',';
537 if (!command.empty()) {
538 options << "command=" << type << ',';
543 string opts(options.str().c_str());
544 opts = strip(opts, ',');
547 // If it's not an inline image, surround it with the centering paragraph.
548 if (! params.inlineFigure) {
550 << "\\vspace{0.3cm}" << endl
551 << "{\\par\\centering ";
554 // Do we want subcaptions?
555 if (params.subcaption) {
556 os << "\\subfigure[" << params.subcaptionText << "]{";
559 // We never used the starred form, we use the "clip" option instead.
560 os << "\\includegraphics";
563 os << '[' << opts << ']';
566 // Make the filename relative to the lyx file
567 string filename = MakeRelPath(params.filename, OnlyPath(buf->fileName()));
569 // and remove the extension so the LaTeX will use whatever is
570 // appropriate (when there are several versions in different formats)
571 filename = ChangeExtension(filename, string());
573 os << '{' << filename << '}';
575 // Do we want a subcaption?
576 if (params.subcaption) {
577 // Close the subcaption command
581 // Is this an inline graphics?
582 if (!params.inlineFigure) {
583 os << " \\par}" << endl
584 << "\\vspace{0.3cm}" << endl;
587 // How do we decide to what format should we export?
588 // cacheHandle->>export(ImageType::EPS);
589 // cacheHandle->>export(ImageType::PNG);
595 int InsetGraphics::Ascii(Buffer const *, ostream &, int) const
597 // No graphics in ascii output.
602 int InsetGraphics::Linuxdoc(Buffer const *, ostream &) const
604 // No graphics in LinuxDoc output. Should check how/what to add.
609 int InsetGraphics::DocBook(Buffer const *, ostream &) const
611 // No graphics in DocBook output. Should check how/what to add.
616 void InsetGraphics::Validate(LaTeXFeatures & features) const
618 // If we have no image, we should not require anything.
619 if (params.filename.empty())
622 features.graphicx = true;
624 if (params.subcaption)
625 features.subfigure = true;
628 // Update the inset after parameters changed (read from file or changed in
630 void InsetGraphics::updateInset()
632 // If file changed...
634 GraphicsCache * gc = GraphicsCache::getInstance();
635 GraphicsCacheItem * temp = 0;
637 if (!params.filename.empty()) {
638 temp = gc->addFile(params.filename);
645 bool InsetGraphics::setParams(InsetGraphicsParams const & params)
647 // If nothing is changed, just return and say so.
648 if (this->params == params)
651 // Copy the new parameters.
652 this->params = params;
654 // Update the inset with the new parameters.
657 // We have changed data, report it.
661 InsetGraphicsParams InsetGraphics::getParams() const
666 Inset * InsetGraphics::Clone() const
668 InsetGraphics * newInset = new InsetGraphics;
670 newInset->cacheHandle = cacheHandle;
671 newInset->pixmap = pixmap;
672 newInset->pixmapInitialized = pixmapInitialized;
674 newInset->setParams(getParams());