Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
sp-image.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * SVG <image> implementation
4 *
5 * Authors:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * Edward Flick (EAF)
8 * Abhishek Sharma
9 * Jon A. Cruz <jon@joncruz.org>
10 *
11 * Copyright (C) 1999-2005 Authors
12 * Copyright (C) 2000-2001 Ximian, Inc.
13 *
14 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
15 */
16
17#include "sp-image.h"
18
19#include <cstring>
20#include <algorithm>
21#include <string>
22
23#include <giomm/error.h>
24#include <glib/gstdio.h>
25#include <glibmm/i18n.h>
26
27#include <2geom/rect.h>
28#include <2geom/transforms.h>
29
30// Added for preserveAspectRatio support -- EAF
31#include "attributes.h"
32#include "document.h"
33#include "print.h"
34#include "snap-candidate.h"
35#include "snap-preferences.h"
36#include "preferences.h"
37
39#include "display/cairo-utils.h"
40#include "display/curve.h"
41#include "xml/quote.h"
43
44#include "colors/cms/system.h"
45#include "colors/manager.h"
46#include "colors/document-cms.h"
47
48//#define DEBUG_LCMS
49#ifdef DEBUG_LCMS
50#define DEBUG_MESSAGE(key, ...)\
51{\
52 g_message( __VA_ARGS__ );\
53}
54#include <gtk/gtk.h>
55#else
56#define DEBUG_MESSAGE(key, ...)
57#endif // DEBUG_LCMS
58/*
59 * SPImage
60 */
61
62// TODO: give these constants better names:
63#define MAGIC_EPSILON 1e-9
64#define MAGIC_EPSILON_TOO 1e-18
65// TODO: also check if it is correct to be using two different epsilon values
66
67static void sp_image_set_curve(SPImage *image);
70
71#ifdef DEBUG_LCMS
72extern guint update_in_progress;
73#define DEBUG_MESSAGE_SCISLAC(key, ...) \
74{\
75 Inkscape::Preferences *prefs = Inkscape::Preferences::get();\
76 bool dump = prefs->getBool("/options/scislac/" #key);\
77 bool dumpD = prefs->getBool("/options/scislac/" #key "D");\
78 bool dumpD2 = prefs->getBool("/options/scislac/" #key "D2");\
79 dumpD &&= ( (update_in_progress == 0) || dumpD2 );\
80 if ( dump )\
81 {\
82 g_message( __VA_ARGS__ );\
83\
84 }\
85 if ( dumpD )\
86 {\
87 GtkWidget *dialog = gtk_message_dialog_new(NULL,\
88 GTK_DIALOG_DESTROY_WITH_PARENT, \
89 GTK_MESSAGE_INFO, \
90 GTK_BUTTONS_OK, \
91 __VA_ARGS__ \
92 );\
93 g_signal_connect_swapped(dialog, "response",\
94 G_CALLBACK(gtk_widget_destroy), \
95 dialog); \
96 gtk_widget_set_visible(dialog, true);\
97 }\
98}
99#else // DEBUG_LCMS
100#define DEBUG_MESSAGE_SCISLAC(key, ...)
101#endif // DEBUG_LCMS
102
104
105 this->x.unset();
106 this->y.unset();
107 this->width.unset();
108 this->height.unset();
109 this->clipbox = Geom::Rect();
110 this->sx = this->sy = 1.0;
111 this->ox = this->oy = 0.0;
112 this->dpi = 96.00;
113 this->prev_width = 0.0;
114 this->prev_height = 0.0;
115
116 this->href = nullptr;
117 this->color_profile = nullptr;
118}
119
120SPImage::~SPImage() = default;
121
124
126 this->readAttr(SPAttr::X);
127 this->readAttr(SPAttr::Y);
128 this->readAttr(SPAttr::WIDTH);
133
134 /* Register */
135 document->addResource("image", this);
136}
137
139 if (this->document) {
140 // Unregister ourselves
141 this->document->removeResource("image", this);
142 }
143
144 if (this->href) {
145 g_free (this->href);
146 this->href = nullptr;
147 }
148
149 pixbuf.reset();
150
151 if (this->color_profile) {
152 g_free (this->color_profile);
153 this->color_profile = nullptr;
154 }
155
156 curve.reset();
157
159}
160
161void SPImage::set(SPAttr key, const gchar* value) {
162 switch (key) {
164 g_free (this->href);
165 this->href = (value) ? g_strdup (value) : nullptr;
167 break;
168
169 case SPAttr::X:
170 /* ex, em not handled correctly. */
171 if (!this->x.read(value)) {
172 this->x.unset();
173 }
174
176 break;
177
178 case SPAttr::Y:
179 /* ex, em not handled correctly. */
180 if (!this->y.read(value)) {
181 this->y.unset();
182 }
183
185 break;
186
187 case SPAttr::WIDTH:
188 /* ex, em not handled correctly. */
189 if (!this->width.read(value)) {
190 this->width.unset();
191 }
192
194 break;
195
196 case SPAttr::HEIGHT:
197 /* ex, em not handled correctly. */
198 if (!this->height.read(value)) {
199 this->height.unset();
200 }
201
203 break;
204
205 case SPAttr::SVG_DPI:
207 break;
208
212 break;
213
215 if ( this->color_profile ) {
216 g_free (this->color_profile);
217 }
218
219 this->color_profile = (value) ? g_strdup (value) : nullptr;
220
221 if ( value ) {
222 DEBUG_MESSAGE( lcmsFour, "<this> color-profile set to '%s'", value );
223 } else {
224 DEBUG_MESSAGE( lcmsFour, "<this> color-profile cleared" );
225 }
226
227 // TODO check on this HREF_MODIFIED flag
229 break;
230
231
232 default:
233 SPItem::set(key, value);
234 break;
235 }
236
237 sp_image_set_curve(this); //creates a curve at the image's boundary for snapping
238}
239
240// BLIP
241void SPImage::update(SPCtx *ctx, unsigned int flags) {
242 SPItem::update(ctx, flags);
243
244 if (flags & SP_IMAGE_HREF_MODIFIED_FLAG) {
245 pixbuf.reset();
246 if (href) {
247 Inkscape::Pixbuf *pb = nullptr;
248 double svgdpi = 96;
249 if (getRepr()->attribute("inkscape:svg-dpi")) {
250 svgdpi = g_ascii_strtod(getRepr()->attribute("inkscape:svg-dpi"), nullptr);
251 }
252 dpi = svgdpi;
254 getRepr()->attribute("sodipodi:absref"),
256 if (!pb) {
257 missing = true;
258 // Passing in our previous size allows us to preserve the image's expected size.
259 auto broken_width = width._set ? width.computed : 640;
260 auto broken_height = height._set ? height.computed : 640;
262 }
263 else {
264 missing = false;
265 }
266
267 if (pb) {
268 if (color_profile) {
269 if (auto cp = document->getDocumentCMS().getSpace(color_profile)) {
271 // XXX TODO cp->transformToRGB(pb);
272 }
273 }
274 pb->ensurePixelFormat(Inkscape::Pixbuf::PF_CAIRO); // Expected by rendering code, so convert now before making immutable.
275 pixbuf = std::shared_ptr<Inkscape::Pixbuf>(pb);
276 }
277 }
278 }
279
281
282 // Why continue without a pixbuf? So we can display "Missing Image" png.
283 // Eventually, we should properly support SVG image type (i.e. render it ourselves).
284 if (this->pixbuf) {
285 if (!this->x._set) {
286 this->x.unit = SVGLength::PX;
287 this->x.computed = 0;
288 }
289
290 if (!this->y._set) {
291 this->y.unit = SVGLength::PX;
292 this->y.computed = 0;
293 }
294
295 if (!this->width._set) {
296 this->width.unit = SVGLength::PX;
297 this->width.computed = this->pixbuf->width();
298 }
299
300 if (!this->height._set) {
301 this->height.unit = SVGLength::PX;
302 this->height.computed = this->pixbuf->height();
303 }
304 }
305
306 // Calculate x, y, width, height from parent/initial viewport, see sp-root.cpp
307 this->calcDimsFromParentViewport(ictx);
308
309 // Image creates a new viewport
310 ictx->viewport = Geom::Rect::from_xywh(this->x.computed, this->y.computed,
311 this->width.computed, this->height.computed);
312
313 this->clipbox = ictx->viewport;
314
315 this->ox = this->x.computed;
316 this->oy = this->y.computed;
317
318 if (this->pixbuf) {
319
320 // Viewbox is either from SVG (not supported) or dimensions of pixbuf (PNG, JPG)
321 this->viewBox = Geom::Rect::from_xywh(0, 0, this->pixbuf->width(), this->pixbuf->height());
322 this->viewBox_set = true;
323
324 // SPItemCtx rctx =
325 get_rctx( ictx );
326
327 this->ox = c2p[4];
328 this->oy = c2p[5];
329 this->sx = c2p[0];
330 this->sy = c2p[3];
331 }
332
333 // TODO: eliminate ox, oy, sx, sy
334
336
337 // don't crash with missing xlink:href attribute
338 if (!this->pixbuf) {
339 return;
340 }
341
342 double proportion_pixbuf = this->pixbuf->height() / (double)this->pixbuf->width();
343 double proportion_image = this->height.computed / (double)this->width.computed;
344 if (this->prev_width &&
345 (this->prev_width != this->pixbuf->width() || this->prev_height != this->pixbuf->height())) {
346 if (std::abs(this->prev_width - this->pixbuf->width()) > std::abs(this->prev_height - this->pixbuf->height())) {
347 proportion_pixbuf = this->pixbuf->width() / (double)this->pixbuf->height();
348 proportion_image = this->width.computed / (double)this->height.computed;
349 if (proportion_pixbuf != proportion_image) {
351 this->getRepr()->setAttributeSvgDouble("width", new_height);
352 }
353 }
354 else {
356 double new_width = this->width.computed * proportion_pixbuf;
357 this->getRepr()->setAttributeSvgDouble("height", new_width);
358 }
359 }
360 }
361 this->prev_width = this->pixbuf->width();
362 this->prev_height = this->pixbuf->height();
363}
364
365void SPImage::modified(unsigned int flags) {
366// SPItem::onModified(flags);
367
368 if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
369 for (auto &v : views) {
370 auto img = cast<Inkscape::DrawingImage>(v.drawingitem.get());
371 img->setStyle(style);
372 }
373 }
374}
375
377 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
378 repr = xml_doc->createElement("svg:image");
379 }
380
382
383 /* fixme: Reset attribute if needed (Lauris) */
384 if (this->x._set) {
385 repr->setAttributeSvgDouble("x", this->x.computed);
386 }
387
388 if (this->y._set) {
389 repr->setAttributeSvgDouble("y", this->y.computed);
390 }
391
392 if (this->width._set) {
393 repr->setAttributeSvgDouble("width", this->width.computed);
394 }
395
396 if (this->height._set) {
397 repr->setAttributeSvgDouble("height", this->height.computed);
398 }
399 repr->setAttribute("inkscape:svg-dpi", this->getRepr()->attribute("inkscape:svg-dpi"));
400
401 this->write_preserveAspectRatio(repr);
402
403 if (this->color_profile) {
404 repr->setAttribute("color-profile", this->color_profile);
405 }
406
407 SPItem::write(xml_doc, repr, flags);
408
409 return repr;
410}
411
412Geom::OptRect SPImage::bbox(Geom::Affine const &transform, SPItem::BBoxType /*type*/) const {
414
415 if ((this->width.computed > 0.0) && (this->height.computed > 0.0)) {
416 bbox = Geom::Rect::from_xywh(this->x.computed, this->y.computed, this->width.computed, this->height.computed);
417 *bbox *= transform;
418 }
419
420 return bbox;
421}
422
424 if (pixbuf && width.computed > 0.0 && height.computed > 0.0) {
425 auto pb = *pixbuf;
426 pb.ensurePixelFormat(Inkscape::Pixbuf::PF_GDK);
427
428 guchar *px = pb.pixels();
429 int w = pb.width();
430 int h = pb.height();
431 int rs = pb.rowstride();
432
433 double vx = this->ox;
434 double vy = this->oy;
435
436 Geom::Affine t;
438 Geom::Scale s(this->sx, this->sy);
439 t = s * tp;
440 ctx->image_R8G8B8A8_N(px, w, h, rs, t, this->style);
441 }
442}
443
444const char* SPImage::typeName() const {
445 return "image";
446}
447
448const char* SPImage::displayName() const {
449 return _("Image");
450}
451
459
460gchar* SPImage::description() const {
461 char *href_desc;
462
463 if (this->href) {
464 href_desc = (strncmp(this->href, "data:", 5) == 0)
465 ? g_strdup(_("embedded"))
466 : xml_quote_strdup(this->href);
467 } else {
468 g_warning("Attempting to call strncmp() with a null pointer.");
469 href_desc = g_strdup("(null_pointer)"); // we call g_free() on href_desc
470 }
471
472 char *ret = ( !pixbuf
473 ? g_strdup_printf(_("[bad reference]: %s"), href_desc)
474 : g_strdup_printf(_("%d &#215; %d: %s"),
475 pixbuf->width(),
476 pixbuf->height(),
477 href_desc) );
478
479 if (!pixbuf && document)
480 {
481 Inkscape::Pixbuf * pb = nullptr;
482 double svgdpi = 96;
483 if (this->getRepr()->attribute("inkscape:svg-dpi")) {
484 svgdpi = g_ascii_strtod(this->getRepr()->attribute("inkscape:svg-dpi"), nullptr);
485 }
486 pb = readImage(Inkscape::getHrefAttribute(*this->getRepr()).second,
487 this->getRepr()->attribute("sodipodi:absref"),
488 this->document->getDocumentBase(), svgdpi);
489
490 if (pb) {
491 ret = g_strdup_printf(_("%d &#215; %d: %s"),
492 pb->width(),
493 pb->height(),
494 href_desc);
495 delete pb;
496 } else {
497 ret = g_strdup(_("{Broken Image}"));
498 }
499 }
500
502 return ret;
503}
504
505Inkscape::DrawingItem* SPImage::show(Inkscape::Drawing &drawing, unsigned int /*key*/, unsigned int /*flags*/) {
507
509
510 return ai;
511}
512
513
514Inkscape::Pixbuf *SPImage::readImage(gchar const *href, gchar const *absref, gchar const *base, double svgdpi)
515{
516 Inkscape::Pixbuf *inkpb = nullptr;
517
518 gchar const *filename = href;
519
520 if (filename != nullptr) {
521 if (g_ascii_strncasecmp(filename, "data:", 5) == 0) {
522 /* data URI - embedded image */
523 filename += 5;
525 } else {
527
528 if (url.hasScheme("file")) {
529 auto native = url.toNativeFilename();
531 } else {
532 try {
533 auto contents = url.getContents();
535 } catch (const Gio::Error &e) {
536 g_warning("URI::getContents failed for '%.100s'", href);
537 }
538 }
539 }
540
541 if (inkpb != nullptr) {
542 return inkpb;
543 }
544 }
545
546 /* at last try to load from sp absolute path name */
547 filename = absref;
548 if (filename != nullptr) {
549 // using absref is outside of SVG rules, so we must at least warn the user
550 if ( base != nullptr && href != nullptr ) {
551 g_warning ("<image xlink:href=\"%s\"> did not resolve to a valid image file (base dir is %s), now trying sodipodi:absref=\"%s\"", href, base, absref);
552 } else {
553 g_warning ("xlink:href did not resolve to a valid image file, now trying sodipodi:absref=\"%s\"", absref);
554 }
555
557 if (inkpb != nullptr) {
558 return inkpb;
559 }
560 }
561 return inkpb;
562}
563
564static std::string broken_image_svg = R"A(
565<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}">
566 <defs>
567 <symbol id="nope" style="fill:none;stroke:#ffffff;stroke-width:3" viewBox="0 0 10 10" preserveAspectRatio="{aspect}">
568 <circle cx="0" cy="0" r="10" style="fill:#a40000;stroke:#cc0000" />
569 <line x1="0" x2="0" y1="-5" y2="5" transform="rotate(45)" />
570 <line x1="0" x2="0" y1="-5" y2="5" transform="rotate(-45)" />
571 </symbol>
572 </defs>
573 <rect width="100%" height="100%" style="fill:white;stroke:#cc0000;stroke-width:6%" />
574 <use xlink:href="#nope" width="30%" height="30%" x="50%" y="50%" />
575</svg>
576
577)A";
578
583{
584 // Limit the size of the broken image raster. smaller than the size in cairo-utils.
586 double dpi = prefs->getDouble("/dialogs/import/defaultxdpi/value", 96.0);
587 width = std::min(width, dpi * 20);
588 height = std::min(height, dpi * 20);
589
590 // Cheap templating for size allows for dynamic sized svg
591 std::string copy = broken_image_svg;
592 copy.replace(copy.find("{width}"), std::string("{width}").size(), std::to_string(width));
593 copy.replace(copy.find("{height}"), std::string("{height}").size(), std::to_string(height));
594
595 // Aspect attempts to make the image better for different ratios of images we might be dropped into
596 copy.replace(copy.find("{aspect}"), std::string("{aspect}").size(),
597 width > height ? "xMinYMid" : "xMidYMin");
598
599 auto inkpb = Inkscape::Pixbuf::create_from_buffer(copy, 0, "brokenimage.svg");
600
601 /* It's included here so if it still does not does load, our libraries are broken! */
602 g_assert (inkpb != nullptr);
603
604 return inkpb;
605}
606
607/* We assert that realpixbuf is either NULL or identical size to pixbuf */
608static void
610{
611 ai->setStyle(image->style);
612 ai->setPixbuf(image->pixbuf);
613 ai->setOrigin(Geom::Point(image->ox, image->oy));
614 ai->setScale(image->sx, image->sy);
615 ai->setClipbox(image->clipbox);
616}
617
619{
620 for (auto &v : image->views) {
621 sp_image_update_arenaitem(image, cast<Inkscape::DrawingImage>(v.drawingitem.get()));
622 }
623}
624
625void SPImage::snappoints(std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) const {
626 /* An image doesn't have any nodes to snap, but still we want to be able snap one image
627 to another. Therefore we will create some snappoints at the corner, similar to a rect. If
628 the image is rotated, then the snappoints will rotate with it. Again, just like a rect.
629 */
630
631 if (this->getClipObject()) {
632 //We are looking at a clipped image: do not return any snappoints, as these might be
633 //far far away from the visible part from the clipped image
634 //TODO Do return snappoints, but only when within visual bounding box
635 } else {
637 // The image has not been clipped: return its corners, which might be rotated for example
638 double const x0 = this->x.computed;
639 double const y0 = this->y.computed;
640 double const x1 = x0 + this->width.computed;
641 double const y1 = y0 + this->height.computed;
642
643 Geom::Affine const i2d (this->i2dt_affine ());
644
649 }
650 }
651}
652
653/*
654 * Initially we'll do:
655 * Transform x, y, set x, y, clear translation
656 */
657
659 /* Calculate position in parent coords. */
660 Geom::Point pos( Geom::Point(this->x.computed, this->y.computed) * xform );
661
662 /* This function takes care of translation and scaling, we return whatever parts we can't
663 handle. */
664 Geom::Affine ret(Geom::Affine(xform).withoutTranslation());
665 Geom::Point const scale(hypot(ret[0], ret[1]),
666 hypot(ret[2], ret[3]));
667
668 if ( scale[Geom::X] > MAGIC_EPSILON ) {
669 ret[0] /= scale[Geom::X];
670 ret[1] /= scale[Geom::X];
671 } else {
672 ret[0] = 1.0;
673 ret[1] = 0.0;
674 }
675
676 if ( scale[Geom::Y] > MAGIC_EPSILON ) {
677 ret[2] /= scale[Geom::Y];
678 ret[3] /= scale[Geom::Y];
679 } else {
680 ret[2] = 0.0;
681 ret[3] = 1.0;
682 }
683
684 this->width = this->width.computed * scale[Geom::X];
685 this->height = this->height.computed * scale[Geom::Y];
686
687 /* Find position in item coords */
688 pos = pos * ret.inverse();
689 this->x = pos[Geom::X];
690 this->y = pos[Geom::Y];
691
692 return ret;
693}
694
696{
697 //create a curve at the image's boundary for snapping
698 if ((image->height.computed < MAGIC_EPSILON_TOO) || (image->width.computed < MAGIC_EPSILON_TOO) || (image->getClipObject())) {
699 } else {
701
702 if (rect->isFinite()) {
703 image->curve.emplace(*rect, true);
704 }
705 }
706}
707
712{
713 return curve ? &*curve : nullptr;
714}
715
717{
718 bool free_data = false;
719
720 // check whether the pixbuf has MIME data
721 guchar *data = nullptr;
722 gsize len = 0;
723 std::string data_mimetype;
724
725 data = const_cast<guchar *>(pb->getMimeData(len, data_mimetype));
726
727 if (data == nullptr) {
728 // if there is no supported MIME data, embed as PNG
729 data_mimetype = "image/png";
730 gdk_pixbuf_save_to_buffer(pb->getPixbufRaw(), reinterpret_cast<gchar**>(&data), &len, "png", nullptr, nullptr);
731 free_data = true;
732 }
733
734 // Save base64 encoded data in image node
735 // this formula taken from Glib docs
736 gsize needed_size = len * 4 / 3 + len * 4 / (3 * 72) + 7;
737 needed_size += 5 + 8 + data_mimetype.size(); // 5 bytes for data: + 8 for ;base64,
738
739 gchar *buffer = (gchar *) g_malloc(needed_size);
740 gchar *buf_work = buffer;
741 buf_work += g_sprintf(buffer, "data:%s;base64,", data_mimetype.c_str());
742
743 gint state = 0;
744 gint save = 0;
745 gsize written = 0;
746 written += g_base64_encode_step(data, len, TRUE, buf_work, &state, &save);
747 written += g_base64_encode_close(TRUE, buf_work + written, &state, &save);
748 buf_work[written] = 0; // null terminate
749
750 // TODO: this is very wasteful memory-wise.
751 // It would be better to only keep the binary data around,
752 // and base64 encode on the fly when saving the XML.
753 Inkscape::setHrefAttribute(*image_node, buffer);
754
755 g_free(buffer);
756 if (free_data) g_free(data);
757}
758
759void sp_embed_svg(Inkscape::XML::Node *image_node, std::string const &fn)
760{
761 if (!g_file_test(fn.c_str(), G_FILE_TEST_EXISTS)) {
762 return;
763 }
764 GStatBuf stdir;
765 int val = g_stat(fn.c_str(), &stdir);
766 if (val == 0 && stdir.st_mode & S_IFDIR){
767 return;
768 }
769
770 // we need to load the entire file into memory,
771 // since we'll store it as MIME data
772 gchar *data = nullptr;
773 gsize len = 0;
774 GError *error = nullptr;
775
776 if (g_file_get_contents(fn.c_str(), &data, &len, &error)) {
777
778 if (error != nullptr) {
779 std::cerr << "Pixbuf::create_from_file: " << error->message << std::endl;
780 std::cerr << " (" << fn << ")" << std::endl;
781 return;
782 }
783
784 std::string data_mimetype = "image/svg+xml";
785
786
787 // Save base64 encoded data in image node
788 // this formula taken from Glib docs
789 gsize needed_size = len * 4 / 3 + len * 4 / (3 * 72) + 7;
790 needed_size += 5 + 8 + data_mimetype.size(); // 5 bytes for data: + 8 for ;base64,
791
792 gchar *buffer = (gchar *) g_malloc(needed_size);
793 gchar *buf_work = buffer;
794 buf_work += g_sprintf(buffer, "data:%s;base64,", data_mimetype.c_str());
795
796 gint state = 0;
797 gint save = 0;
798 gsize written = 0;
799 written += g_base64_encode_step(reinterpret_cast<guchar *>(data), len, TRUE, buf_work, &state, &save);
800 written += g_base64_encode_close(TRUE, buf_work + written, &state, &save);
801 buf_work[written] = 0; // null terminate
802
803 // TODO: this is very wasteful memory-wise.
804 // It would be better to only keep the binary data around,
805 // and base64 encode on the fly when saving the XML.
806 Inkscape::setHrefAttribute(*image_node, buffer);
807
808 g_free(buffer);
809 g_free(data);
810 }
811}
812
814{
815 if ( href && pixbuf && pixbuf->modificationTime()) {
816 // It *might* change
817
818 GStatBuf st;
819 memset(&st, 0, sizeof(st));
820 int val = 0;
821 if (g_file_test (pixbuf->originalPath().c_str(), G_FILE_TEST_EXISTS)){
822 val = g_stat(pixbuf->originalPath().c_str(), &st);
823 }
824 if ( !val ) {
825 // stat call worked. Check time now
826 if ( st.st_mtime != pixbuf->modificationTime() ) {
828 }
829 }
830 }
831}
832
842{
843 area *= i2doc_affine().inverse();
844
845 // Apply the image's viewbox and scal to get us image pixels
846 area *= Geom::Translate(-x.computed, -y.computed);
847 area *= Geom::Scale(pixbuf->width() / width.computed, pixbuf->height() / height.computed);
848
849 // Any precision problems and we choose to retain more pixels (roundOut)
850 return cropToArea(area.roundOutwards());
851}
852
862{
863 // Contrain requested area to the available pixels.
864 auto px = Geom::IntRect::from_xywh(0.0, 0.0, pixbuf->width(), pixbuf->height());
865 auto px_area = area & px;
866 if (!px_area)
867 return false;
868
869 if (auto pb = pixbuf->cropTo(*px_area)) {
870 // Crop ended up with bad pixels, this should rarely happen.
871 if (pb->width() <= 0 || pb->height() <= 0)
872 return false;
873
874 // Cropping is done, now embed this image back into image tag.
875 sp_embed_image(getRepr(), pb);
876
877 // Our new image has new sizes, so adjust image tag's internal viewbox
878 auto repr = getRepr();
879 auto scale_x = px.width() / width.computed;
880 auto scale_y = px.height() / height.computed;
881 repr->setAttributeSvgDouble("x", this->x.computed + (px_area->left() / scale_x));
882 repr->setAttributeSvgDouble("y", this->y.computed + (px_area->top() / scale_y));
883 repr->setAttributeSvgDouble("width", px_area->width() / scale_x);
884 repr->setAttributeSvgDouble("height", px_area->height() / scale_y);
885
886 return true;
887 }
888 return false;
889}
890
891/*
892 Local Variables:
893 mode:c++
894 c-file-style:"stroustrup"
895 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
896 indent-tabs-mode:nil
897 fill-column:99
898 End:
899*/
900// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
double scale
Definition aa.cpp:228
Lookup dictionary for attributes/properties.
SPAttr
Definition attributes.h:27
@ COLOR_PROFILE
@ XLINK_HREF
@ PRESERVEASPECTRATIO
Cairo integration helpers.
3x3 matrix representing an affine transformation.
Definition affine.h:70
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
Axis aligned, non-empty, generic rectangle.
static CRect from_xywh(Coord x, Coord y, Coord w, Coord h)
Create rectangle from origin and dimensions.
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Two-dimensional point that doubles as a vector.
Definition point.h:66
Axis aligned, non-empty rectangle.
Definition rect.h:92
IntRect roundOutwards() const
Return the smallest integer rectangle which contains this one.
Definition rect.h:141
Scaling from the origin.
Definition transforms.h:150
Translation by a vector.
Definition transforms.h:115
std::shared_ptr< Space::CMS > getSpace(std::string const &name) const
Get the specific color space from the list of available spaces.
void setClipbox(Geom::Rect const &box)
void setScale(double sx, double sy)
void setPixbuf(std::shared_ptr< Inkscape::Pixbuf const > pb)
void setOrigin(Geom::Point const &o)
void setStyle(SPStyle const *style, SPStyle const *context_style=nullptr) override
Process information related to the new style.
SVG drawing item for display.
Class to hold image data for raster images.
Definition cairo-utils.h:31
guchar const * getMimeData(gsize &len, std::string &mimetype) const
Retrieves the original compressed data for the surface, if any.
GdkPixbuf * getPixbufRaw(bool convert_format=true)
Converts the pixbuf to GdkPixbuf pixel format.
int width() const
void ensurePixelFormat(PixelFormat fmt)
Convert the internal pixel format between CAIRO and GDK formats.
static Pixbuf * create_from_file(std::string const &fn, double svgddpi=0)
static Pixbuf * create_from_buffer(std::string const &, double svgddpi=0, std::string const &fn="")
int height() const
static Pixbuf * create_from_data_uri(gchar const *uri, double svgdpi=0)
Preference storage class.
Definition preferences.h:66
double getDouble(Glib::ustring const &pref_path, double def=0.0, Glib::ustring const &unit="")
Retrieve a floating point value.
static Preferences * get()
Access the singleton Preferences object.
Storing of snapping preferences.
bool isTargetSnappable(Inkscape::SnapTargetType const target) const
Represents an URI as per RFC 2396.
Definition uri.h:36
static URI from_href_and_basedir(char const *href, char const *basedir)
Convenience function for the common use case given a xlink:href attribute and a local directory as th...
Definition uri.cpp:214
Interface for refcounted XML nodes.
Definition node.h:80
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
bool setAttributeSvgDouble(Util::const_char_ptr key, double val)
For attributes where an exponent is allowed.
Definition node.cpp:111
Wrapper around a Geom::PathVector object.
Definition curve.h:26
void calcDimsFromParentViewport(const SPItemCtx *ictx, bool assign_to_set=false, SPDimensions const *use=nullptr)
Update computed x/y/width/height for "percent" units and/or from its referencing clone parent.
SVGLength y
SVGLength height
SVGLength width
SVGLength x
Typed SVG document implementation.
Definition document.h:103
bool removeResource(char const *key, SPObject *object)
char const * getDocumentBase() const
Definition document.h:235
bool addResource(char const *key, SPObject *object)
Inkscape::Colors::DocumentCMS & getDocumentCMS()
Definition document.h:167
static Inkscape::Pixbuf * readImage(gchar const *href, gchar const *absref, gchar const *base, double svgdpi=0)
Definition sp-image.cpp:514
void set(SPAttr key, char const *value) override
Definition sp-image.cpp:161
double prev_height
Definition sp-image.h:43
double sy
Definition sp-image.h:40
Inkscape::DrawingItem * show(Inkscape::Drawing &drawing, unsigned int key, unsigned int flags) override
Definition sp-image.cpp:505
double dpi
Definition sp-image.h:42
Geom::OptRect bbox(Geom::Affine const &transform, SPItem::BBoxType type) const override
Definition sp-image.cpp:412
static Inkscape::Pixbuf * getBrokenImage(double width, double height)
Load a standard broken image svg, used if we fail to load pixbufs from the href.
Definition sp-image.cpp:582
void release() override
Definition sp-image.cpp:138
double oy
Definition sp-image.h:41
const char * displayName() const override
The item's type name as a translated human string.
Definition sp-image.cpp:448
std::shared_ptr< Inkscape::Pixbuf const > pixbuf
Definition sp-image.h:50
double prev_width
Definition sp-image.h:43
char * color_profile
Definition sp-image.h:48
bool cropToArea(Geom::Rect area)
Crop the image (remove pixels) based on the area rectangle and translate image to componsate for move...
Definition sp-image.cpp:841
char * href
Definition sp-image.h:47
Geom::Rect clipbox
Definition sp-image.h:39
double sx
Definition sp-image.h:40
void print(SPPrintContext *ctx) override
Definition sp-image.cpp:423
void update(SPCtx *ctx, unsigned int flags) override
Definition sp-image.cpp:241
const char * typeName() const override
The item's type name, not node tag name.
Definition sp-image.cpp:444
void modified(unsigned int flags) override
Definition sp-image.cpp:365
~SPImage() override
char * description() const override
Definition sp-image.cpp:460
std::optional< SPCurve > curve
Definition sp-image.h:45
Geom::Affine set_transform(Geom::Affine const &transform) override
Definition sp-image.cpp:658
double ox
Definition sp-image.h:41
void snappoints(std::vector< Inkscape::SnapCandidatePoint > &p, Inkscape::SnapPreferences const *snapprefs) const override
Definition sp-image.cpp:625
bool missing
Definition sp-image.h:51
Inkscape::URI getURI() const
Return this image's href as a URI object.
Definition sp-image.cpp:455
SPCurve const * get_curve() const
Return a borrowed pointer to curve (if any exists) or NULL if there is no curve.
Definition sp-image.cpp:711
Inkscape::XML::Node * write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, unsigned int flags) override
Definition sp-image.cpp:376
void build(SPDocument *document, Inkscape::XML::Node *repr) override
Definition sp-image.cpp:122
void refresh_if_outdated()
Definition sp-image.cpp:813
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::Affine i2dt_affine() const
Returns the transformation from item to desktop coords.
Definition sp-item.cpp:1828
void update(SPCtx *ctx, unsigned int flags) override
Definition sp-item.cpp:787
Inkscape::XML::Node * write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, unsigned int flags) override
Definition sp-item.cpp:858
void set(SPAttr key, char const *value) override
Definition sp-item.cpp:566
void release() override
Definition sp-item.cpp:542
Geom::Affine transform
Definition sp-item.h:138
@ VISUAL_BBOX
Definition sp-item.h:118
Geom::Affine i2doc_affine() const
Returns the accumulated transformation of the item and all its ancestors, including root's viewport.
Definition sp-item.cpp:1823
void build(SPDocument *document, Inkscape::XML::Node *repr) override
Definition sp-item.cpp:519
std::vector< SPItemView > views
Definition sp-item.h:163
SPClipPath * getClipObject() const
Definition sp-item.cpp:102
Inkscape::XML::Node * repr
Definition sp-object.h:193
SPDocument * document
Definition sp-object.h:188
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
void readAttr(char const *key)
Read value of key attribute from XML node into object.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
void requestDisplayUpdate(unsigned int flags)
Queues an deferred update of this object's display.
Geom::Rect viewBox
Definition viewbox.h:35
Geom::Affine c2p
Definition viewbox.h:43
void set_preserveAspectRatio(const gchar *value)
Definition viewbox.cpp:98
void write_preserveAspectRatio(Inkscape::XML::Node *repr) const
Write preserveAspectRatio attribute to XML, if set.
Definition viewbox.cpp:305
SPItemCtx get_rctx(const SPItemCtx *ictx, double scale_none=1.0)
Definition viewbox.cpp:260
bool viewBox_set
Definition viewbox.h:34
bool read(char const *str)
bool _set
Definition svg-length.h:41
void unset(Unit u=NONE, float v=0, float c=0)
Unit unit
Definition svg-length.h:44
float computed
Definition svg-length.h:50
Access operating system wide information about color management.
const double w
Definition conic-4.cpp:19
vector< vpsc::Rectangle * > rs
Bitmap image belonging to an SVG drawing.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
std::unique_ptr< Magick::Image > image
Affine identity()
Create an identity matrix.
Definition affine.h:210
@ SNAPSOURCE_IMG_CORNER
Definition snap-enums.h:54
void setHrefAttribute(XML::Node &node, Util::const_char_ptr value)
If the 'href' attribute already exists for the given node, then set a new value for it.
@ SNAPTARGET_IMG_CORNER
Definition snap-enums.h:116
std::pair< char const *, char const * > getHrefAttribute(XML::Node const &node)
Get the 'href' or 'xlink:href' (fallback) attribute from an XML node.
static cairo_user_data_key_t key
Singleton class to access the preferences file in a convenient way.
char * xml_quote_strdup(char const *src)
Definition quote.cpp:43
TODO: insert short description here.
Axis-aligned rectangle.
auto len
Definition safe-printf.h:21
Some utility classes to store various kinds of snap candidates.
static void sp_image_set_curve(SPImage *image)
Definition sp-image.cpp:695
static void sp_image_update_arenaitem(SPImage *img, Inkscape::DrawingImage *ai)
Definition sp-image.cpp:609
guint update_in_progress
void sp_embed_image(Inkscape::XML::Node *image_node, Inkscape::Pixbuf *pb)
Definition sp-image.cpp:716
static void sp_image_update_canvas_image(SPImage *image)
Definition sp-image.cpp:618
static std::string broken_image_svg
Definition sp-image.cpp:564
void sp_embed_svg(Inkscape::XML::Node *image_node, std::string const &fn)
Definition sp-image.cpp:759
SVG <image> implementation.
void sp_embed_image(Inkscape::XML::Node *imgnode, Inkscape::Pixbuf *pb)
Definition sp-image.cpp:716
static const Point data[]
Interface for XML documents.
Definition document.h:43
Unused.
Definition sp-object.h:94
Contains transformations to document/viewport and the viewport size.
Definition sp-item.h:92
Geom::Rect viewport
Viewport size.
Definition sp-item.h:97
Definition curve.h:24
double height
double width
Affine transformation classes.