Code

add breton win32 installer translation
[inkscape.git] / src / sp-image.cpp
1 #define __SP_IMAGE_C__
3 /*
4  * SVG <image> implementation
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Edward Flick (EAF)
9  *
10  * Copyright (C) 1999-2005 Authors
11  * Copyright (C) 2000-2001 Ximian, Inc.
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
20 #include <cstring>
21 #include <string>
22 #include <libnr/nr-matrix-fns.h>
23 //#define GDK_PIXBUF_ENABLE_BACKEND 1
24 //#include <gdk-pixbuf/gdk-pixbuf-io.h>
25 #include "display/nr-arena-image.h"
26 #include <display/curve.h>
28 //Added for preserveAspectRatio support -- EAF
29 #include "enums.h"
30 #include "attributes.h"
32 #include "print.h"
33 #include "brokenimage.xpm"
34 #include "document.h"
35 #include "sp-image.h"
36 #include "sp-clippath.h"
37 #include <glibmm/i18n.h>
38 #include "xml/quote.h"
39 #include <xml/repr.h>
41 #include "libnr/nr-matrix-fns.h"
43 #include "io/sys.h"
44 #include <png.h>
45 #if ENABLE_LCMS
46 #include "color-profile-fns.h"
47 #include "color-profile.h"
48 //#define DEBUG_LCMS
49 #ifdef DEBUG_LCMS
50 #include "prefs-utils.h"
51 #include <gtk/gtkmessagedialog.h>
52 #endif // DEBUG_LCMS
53 #endif // ENABLE_LCMS
54 /*
55  * SPImage
56  */
59 static void sp_image_class_init (SPImageClass * klass);
60 static void sp_image_init (SPImage * image);
62 static void sp_image_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr);
63 static void sp_image_release (SPObject * object);
64 static void sp_image_set (SPObject *object, unsigned int key, const gchar *value);
65 static void sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags);
66 static Inkscape::XML::Node *sp_image_write (SPObject *object, Inkscape::XML::Node *repr, guint flags);
68 static void sp_image_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags);
69 static void sp_image_print (SPItem * item, SPPrintContext *ctx);
70 static gchar * sp_image_description (SPItem * item);
71 static void sp_image_snappoints(SPItem const *item, SnapPointsIter p);
72 static NRArenaItem *sp_image_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
73 static NR::Matrix sp_image_set_transform (SPItem *item, NR::Matrix const &xform);
74 static void sp_image_set_curve(SPImage *image);
77 GdkPixbuf *sp_image_repr_read_image (const gchar *href, const gchar *absref, const gchar *base);
78 static GdkPixbuf *sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf);
79 static void sp_image_update_canvas_image (SPImage *image);
80 static GdkPixbuf * sp_image_repr_read_dataURI (const gchar * uri_data);
81 static GdkPixbuf * sp_image_repr_read_b64 (const gchar * uri_data);
83 static SPItemClass *parent_class;
86 extern "C"
87 {
88     void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length );
89     void user_write_data( png_structp png_ptr, png_bytep data, png_size_t length );
90     void user_flush_data( png_structp png_ptr );
92 }
95 #ifdef DEBUG_LCMS
96 extern guint update_in_progress;
97 #define DEBUG_MESSAGE(key, ...) \
98 {\
99     gint dump = prefs_get_int_attribute_limited("options.scislac", #key, 0, 0, 1);\
100     gint dumpD = prefs_get_int_attribute_limited("options.scislac", #key"D", 0, 0, 1);\
101     gint dumpD2 = prefs_get_int_attribute_limited("options.scislac", #key"D2", 0, 0, 1);\
102     dumpD &= ( (update_in_progress == 0) || dumpD2 );\
103     if ( dump )\
104     {\
105         g_message( __VA_ARGS__ );\
107     }\
108     if ( dumpD )\
109     {\
110         GtkWidget *dialog = gtk_message_dialog_new(NULL,\
111                                                    GTK_DIALOG_DESTROY_WITH_PARENT, \
112                                                    GTK_MESSAGE_INFO,    \
113                                                    GTK_BUTTONS_OK,      \
114                                                    __VA_ARGS__          \
115                                                    );\
116         g_signal_connect_swapped(dialog, "response",\
117                                  G_CALLBACK(gtk_widget_destroy),        \
118                                  dialog);                               \
119         gtk_widget_show_all( dialog );\
120     }\
122 #endif // DEBUG_LCMS
124 namespace Inkscape {
125 namespace IO {
127 class PushPull
129 public:
130     gboolean    first;
131     FILE*       fp;
132     guchar*     scratch;
133     gsize       size;
134     gsize       used;
135     gsize       offset;
136     GdkPixbufLoader *loader;
138     PushPull() : first(TRUE),
139                  fp(0),
140                  scratch(0),
141                  size(0),
142                  used(0),
143                  offset(0),
144                  loader(0) {};
146     gboolean readMore()
147     {
148         gboolean good = FALSE;
149         if ( offset )
150         {
151             g_memmove( scratch, scratch + offset, used - offset );
152             used -= offset;
153             offset = 0;
154         }
155         if ( used < size )
156         {
157             gsize space = size - used;
158             gsize got = fread( scratch + used, 1, space, fp );
159             if ( got )
160             {
161                 if ( loader )
162                 {
163                     GError *err = NULL;
164                     //g_message( " __read %d bytes", (int)got );
165                     if ( !gdk_pixbuf_loader_write( loader, scratch + used, got, &err ) )
166                     {
167                         //g_message("_error writing pixbuf data");
168                     }
169                 }
171                 used += got;
172                 good = TRUE;
173             }
174             else
175             {
176                 good = FALSE;
177             }
178         }
179         return good;
180     }
182     gsize available() const
183     {
184         return (used - offset);
185     }
187     gsize readOut( gpointer data, gsize length )
188     {
189         gsize giving = available();
190         if ( length < giving )
191         {
192             giving = length;
193         }
194         g_memmove( data, scratch + offset, giving );
195         offset += giving;
196         if ( offset >= used )
197         {
198             offset = 0;
199             used = 0;
200         }
201         return giving;
202     }
204     void clear()
205     {
206         offset = 0;
207         used = 0;
208     }
210 private:
211     PushPull& operator = (const PushPull& other);
212     PushPull(const PushPull& other);
213 };
215 void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length )
217 //    g_message( "user_read_data(%d)", length );
219     PushPull* youme = (PushPull*)png_get_io_ptr(png_ptr);
221     gsize filled = 0;
222     gboolean canRead = TRUE;
224     while ( filled < length && canRead )
225     {
226         gsize some = youme->readOut( data + filled, length - filled );
227         filled += some;
228         if ( filled < length )
229         {
230             canRead &= youme->readMore();
231         }
232     }
233 //    g_message("things out");
236 void user_write_data( png_structp /*png_ptr*/, png_bytep /*data*/, png_size_t /*length*/ )
238     //g_message( "user_write_data(%d)", length );
241 void user_flush_data( png_structp /*png_ptr*/ )
243     //g_message( "user_flush_data" );
246 GdkPixbuf*  pixbuf_new_from_file( const char *filename, GError **/*error*/ )
248     GdkPixbuf* buf = NULL;
249     PushPull youme;
250     gint dpiX = 0;
251     gint dpiY = 0;
253     //buf = gdk_pixbuf_new_from_file( filename, error );
254     dump_fopen_call( filename, "pixbuf_new_from_file" );
255     FILE* fp = fopen_utf8name( filename, "r" );
256     if ( fp )
257     {
258         GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
259         if ( loader )
260         {
261             GError *err = NULL;
263             // short buffer
264             guchar scratch[1024];
265             gboolean latter = FALSE;
266             gboolean isPng = FALSE;
267             png_structp pngPtr = NULL;
268             png_infop infoPtr = NULL;
269             //png_infop endPtr = NULL;
271             youme.fp = fp;
272             youme.scratch = scratch;
273             youme.size = sizeof(scratch);
274             youme.used = 0;
275             youme.offset = 0;
276             youme.loader = loader;
278             while ( !feof(fp) )
279             {
280                 if ( youme.readMore() )
281                 {
282                     if ( youme.first )
283                     {
284                         //g_message( "First data chunk" );
285                         youme.first = FALSE;
286                         isPng = !png_sig_cmp( scratch + youme.offset, 0, youme.available() );
287                         //g_message( "  png? %s", (isPng ? "Yes":"No") );
288                         if ( isPng )
289                         {
290                             pngPtr = png_create_read_struct( PNG_LIBPNG_VER_STRING,
291                                                              NULL,//(png_voidp)user_error_ptr,
292                                                              NULL,//user_error_fn,
293                                                              NULL//user_warning_fn
294                                 );
295                             if ( pngPtr )
296                             {
297                                 infoPtr = png_create_info_struct( pngPtr );
298                                 //endPtr = png_create_info_struct( pngPtr );
300                                 png_set_read_fn( pngPtr, &youme, user_read_data );
301                                 //g_message( "In" );
303                                 //png_read_info( pngPtr, infoPtr );
304                                 png_read_png( pngPtr, infoPtr, PNG_TRANSFORM_IDENTITY, NULL );
306                                 //g_message("out");
308                                 //png_read_end(pngPtr, endPtr);
310                                 /*
311                                 if ( png_get_valid( pngPtr, infoPtr, PNG_INFO_pHYs ) )
312                                 {
313                                     g_message("pHYs chunk now valid" );
314                                 }
315                                 if ( png_get_valid( pngPtr, infoPtr, PNG_INFO_sCAL ) )
316                                 {
317                                     g_message("sCAL chunk now valid" );
318                                 }
319                                 */
321                                 png_uint_32 res_x = 0;
322                                 png_uint_32 res_y = 0;
323                                 int unit_type = 0;
324                                 if ( png_get_pHYs( pngPtr, infoPtr, &res_x, &res_y, &unit_type) )
325                                 {
326 //                                     g_message( "pHYs yes (%d, %d) %d (%s)", (int)res_x, (int)res_y, unit_type,
327 //                                                (unit_type == 1? "per meter" : "unknown")
328 //                                         );
330 //                                     g_message( "    dpi: (%d, %d)",
331 //                                                (int)(0.5 + ((double)res_x)/39.37),
332 //                                                (int)(0.5 + ((double)res_y)/39.37) );
333                                     if ( unit_type == PNG_RESOLUTION_METER )
334                                     {
335                                         // TODO come up with a more accurate DPI setting
336                                         dpiX = (int)(0.5 + ((double)res_x)/39.37);
337                                         dpiY = (int)(0.5 + ((double)res_y)/39.37);
338                                     }
339                                 }
340                                 else
341                                 {
342 //                                     g_message( "pHYs no" );
343                                 }
345 /*
346                                 double width = 0;
347                                 double height = 0;
348                                 int unit = 0;
349                                 if ( png_get_sCAL(pngPtr, infoPtr, &unit, &width, &height) )
350                                 {
351                                     gchar* vals[] = {
352                                         "unknown", // PNG_SCALE_UNKNOWN
353                                         "meter", // PNG_SCALE_METER
354                                         "radian", // PNG_SCALE_RADIAN
355                                         "last", //
356                                         NULL
357                                     };
359                                     g_message( "sCAL: (%f, %f) %d (%s)",
360                                                width, height, unit,
361                                                ((unit >= 0 && unit < 3) ? vals[unit]:"???")
362                                         );
363                                 }
364 */
366 #if defined(PNG_sRGB_SUPPORTED)
367                                 {
368                                     int intent = 0;
369                                     if ( png_get_sRGB(pngPtr, infoPtr, &intent) ) {
370 //                                         g_message("Found an sRGB png chunk");
371                                     }
372                                 }
373 #endif // defined(PNG_sRGB_SUPPORTED)
375 #if defined(PNG_cHRM_SUPPORTED)
376                                 {
377                                     double white_x = 0;
378                                     double white_y = 0;
379                                     double red_x = 0;
380                                     double red_y = 0;
381                                     double green_x = 0;
382                                     double green_y = 0;
383                                     double blue_x = 0;
384                                     double blue_y = 0;
386                                     if ( png_get_cHRM(pngPtr, infoPtr,
387                                                       &white_x, &white_y,
388                                                       &red_x, &red_y,
389                                                       &green_x, &green_y,
390                                                       &blue_x, &blue_y) ) {
391 //                                         g_message("Found a cHRM png chunk");
392                                     }
393                                 }
394 #endif // defined(PNG_cHRM_SUPPORTED)
396 #if defined(PNG_gAMA_SUPPORTED)
397                                 {
398                                     double file_gamma = 0;
399                                     if ( png_get_gAMA(pngPtr, infoPtr, &file_gamma) ) {
400 //                                         g_message("Found a gAMA png chunk");
401                                     }
402                                 }
403 #endif // defined(PNG_gAMA_SUPPORTED)
405 #if defined(PNG_iCCP_SUPPORTED)
406                                 {
407                                     char* name = 0;
408                                     int compression_type = 0;
409                                     char* profile = 0;
410                                     png_uint_32 proflen = 0;
411                                     if ( png_get_iCCP(pngPtr, infoPtr, &name, &compression_type, &profile, &proflen) ) {
412 //                                         g_message("Found an iCCP chunk named [%s] with %d bytes and comp %d", name, proflen, compression_type);
413                                     }
414                                 }
415 #endif // defined(PNG_iCCP_SUPPORTED)
418                                 // now clean it up.
419                                 png_destroy_read_struct( &pngPtr, &infoPtr, NULL );//&endPtr );
420                             }
421                             else
422                             {
423 //                                 g_message("Error when creating PNG read struct");
424                             }
425                         }
426                     }
427                     else if ( !latter )
428                     {
429                         latter = TRUE;
430                         //g_message("  READing latter");
431                     }
432                     // Now clear out the buffer so we can read more.
433                     // (dumping out unused)
434                     youme.clear();
435                 }
436             }
438             gboolean ok = gdk_pixbuf_loader_close(loader, &err);
439             if ( ok )
440             {
441                 buf = gdk_pixbuf_loader_get_pixbuf( loader );
442                 if ( buf )
443                 {
444                     g_object_ref(buf);
446                     if ( dpiX )
447                     {
448                         gchar *tmp = g_strdup_printf( "%d", dpiX );
449                         if ( tmp )
450                         {
451 //                             g_message("Need to set DpiX: %s", tmp);
452                             //gdk_pixbuf_set_option( buf, "Inkscape::DpiX", tmp );
453                             g_free( tmp );
454                         }
455                     }
456                     if ( dpiY )
457                     {
458                         gchar *tmp = g_strdup_printf( "%d", dpiY );
459                         if ( tmp )
460                         {
461 //                             g_message("Need to set DpiY: %s", tmp);
462                             //gdk_pixbuf_set_option( buf, "Inkscape::DpiY", tmp );
463                             g_free( tmp );
464                         }
465                     }
466                 }
467             }
468             else
469             {
470                 // do something
471                 g_message("error loading pixbuf at close");
472             }
474             g_object_unref(loader);
475         }
476         else
477         {
478             g_message("error when creating pixbuf loader");
479         }
480         fclose( fp );
481         fp = NULL;
482     }
483     else
484     {
485         g_warning ("Unable to open linked file: %s", filename);
486     }
488 /*
489     if ( buf )
490     {
491         const gchar* bloop = gdk_pixbuf_get_option( buf, "Inkscape::DpiX" );
492         if ( bloop )
493         {
494             g_message("DPI X is [%s]", bloop);
495         }
496         bloop = gdk_pixbuf_get_option( buf, "Inkscape::DpiY" );
497         if ( bloop )
498         {
499             g_message("DPI Y is [%s]", bloop);
500         }
501     }
502 */
504     return buf;
510 GType
511 sp_image_get_type (void)
513         static GType image_type = 0;
514         if (!image_type) {
515                 GTypeInfo image_info = {
516                         sizeof (SPImageClass),
517                         NULL,   /* base_init */
518                         NULL,   /* base_finalize */
519                         (GClassInitFunc) sp_image_class_init,
520                         NULL,   /* class_finalize */
521                         NULL,   /* class_data */
522                         sizeof (SPImage),
523                         16,     /* n_preallocs */
524                         (GInstanceInitFunc) sp_image_init,
525                         NULL,   /* value_table */
526                 };
527                 image_type = g_type_register_static (sp_item_get_type (), "SPImage", &image_info, (GTypeFlags)0);
528         }
529         return image_type;
532 static void
533 sp_image_class_init (SPImageClass * klass)
535         GObjectClass * gobject_class;
536         SPObjectClass * sp_object_class;
537         SPItemClass * item_class;
539         gobject_class = (GObjectClass *) klass;
540         sp_object_class = (SPObjectClass *) klass;
541         item_class = (SPItemClass *) klass;
543         parent_class = (SPItemClass*)g_type_class_ref (sp_item_get_type ());
545         sp_object_class->build = sp_image_build;
546         sp_object_class->release = sp_image_release;
547         sp_object_class->set = sp_image_set;
548         sp_object_class->update = sp_image_update;
549         sp_object_class->write = sp_image_write;
551         item_class->bbox = sp_image_bbox;
552         item_class->print = sp_image_print;
553         item_class->description = sp_image_description;
554         item_class->show = sp_image_show;
555         item_class->snappoints = sp_image_snappoints;
556         item_class->set_transform = sp_image_set_transform;
559 static void
560 sp_image_init (SPImage *image)
562         image->x.unset();
563         image->y.unset();
564         image->width.unset();
565         image->height.unset();
566         image->aspect_align = SP_ASPECT_NONE;
567         image->curve = NULL;
570 static void
571 sp_image_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
573         if (((SPObjectClass *) parent_class)->build)
574                 ((SPObjectClass *) parent_class)->build (object, document, repr);
576         sp_object_read_attr (object, "xlink:href");
577         sp_object_read_attr (object, "x");
578         sp_object_read_attr (object, "y");
579         sp_object_read_attr (object, "width");
580         sp_object_read_attr (object, "height");
581         sp_object_read_attr (object, "preserveAspectRatio");
582         sp_object_read_attr (object, "color-profile");
584         /* Register */
585         sp_document_add_resource (document, "image", object);
588 static void
589 sp_image_release (SPObject *object)
591         SPImage *image;
593         image = SP_IMAGE (object);
595         if (SP_OBJECT_DOCUMENT (object)) {
596                 /* Unregister ourselves */
597                 sp_document_remove_resource (SP_OBJECT_DOCUMENT (object), "image", SP_OBJECT (object));
598         }
600         if (image->href) {
601                 g_free (image->href);
602                 image->href = NULL;
603         }
605         if (image->pixbuf) {
606                 gdk_pixbuf_unref (image->pixbuf);
607                 image->pixbuf = NULL;
608         }
610 #if ENABLE_LCMS
611         if (image->color_profile) {
612                 g_free (image->color_profile);
613                 image->color_profile = NULL;
614         }
615 #endif // ENABLE_LCMS
617     if (image->curve) {
618                 image->curve = sp_curve_unref (image->curve);
619         }
621         if (((SPObjectClass *) parent_class)->release)
622                 ((SPObjectClass *) parent_class)->release (object);
625 static void
626 sp_image_set (SPObject *object, unsigned int key, const gchar *value)
628         SPImage *image;
630         image = SP_IMAGE (object);
632         switch (key) {
633         case SP_ATTR_XLINK_HREF:
634                 g_free (image->href);
635                 image->href = (value) ? g_strdup (value) : NULL;
636                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_IMAGE_HREF_MODIFIED_FLAG);
637                 break;
638         case SP_ATTR_X:
639                 if (!image->x.readAbsolute(value)) {
640                     /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
641                         image->x.unset();
642                 }
643                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
644                 break;
645         case SP_ATTR_Y:
646                 if (!image->y.readAbsolute(value)) {
647                     /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
648                         image->y.unset();
649                 }
650                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
651                 break;
652         case SP_ATTR_WIDTH:
653                 if (!image->width.readAbsolute(value)) {
654                     /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
655                         image->width.unset();
656                 }
657                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
658                 break;
659         case SP_ATTR_HEIGHT:
660                 if (!image->height.readAbsolute(value)) {
661                     /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
662                         image->height.unset();
663                 }
664                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
665                 break;
666         case SP_ATTR_PRESERVEASPECTRATIO:
667                 /* Do setup before, so we can use break to escape */
668                 image->aspect_align = SP_ASPECT_NONE;
669                 image->aspect_clip = SP_ASPECT_MEET;
670                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
671                 if (value) {
672                         int len;
673                         gchar c[256];
674                         const gchar *p, *e;
675                         unsigned int align, clip;
676                         p = value;
677                         while (*p && *p == 32) p += 1;
678                         if (!*p) break;
679                         e = p;
680                         while (*e && *e != 32) e += 1;
681                         len = e - p;
682                         if (len > 8) break;
683                         memcpy (c, value, len);
684                         c[len] = 0;
685                         /* Now the actual part */
686                         if (!strcmp (c, "none")) {
687                                 align = SP_ASPECT_NONE;
688                         } else if (!strcmp (c, "xMinYMin")) {
689                                 align = SP_ASPECT_XMIN_YMIN;
690                         } else if (!strcmp (c, "xMidYMin")) {
691                                 align = SP_ASPECT_XMID_YMIN;
692                         } else if (!strcmp (c, "xMaxYMin")) {
693                                 align = SP_ASPECT_XMAX_YMIN;
694                         } else if (!strcmp (c, "xMinYMid")) {
695                                 align = SP_ASPECT_XMIN_YMID;
696                         } else if (!strcmp (c, "xMidYMid")) {
697                                 align = SP_ASPECT_XMID_YMID;
698                         } else if (!strcmp (c, "xMaxYMid")) {
699                                 align = SP_ASPECT_XMAX_YMID;
700                         } else if (!strcmp (c, "xMinYMax")) {
701                                 align = SP_ASPECT_XMIN_YMAX;
702                         } else if (!strcmp (c, "xMidYMax")) {
703                                 align = SP_ASPECT_XMID_YMAX;
704                         } else if (!strcmp (c, "xMaxYMax")) {
705                                 align = SP_ASPECT_XMAX_YMAX;
706                         } else {
707                                 break;
708                         }
709                         clip = SP_ASPECT_MEET;
710                         while (*e && *e == 32) e += 1;
711                         if (e) {
712                                 if (!strcmp (e, "meet")) {
713                                         clip = SP_ASPECT_MEET;
714                                 } else if (!strcmp (e, "slice")) {
715                                         clip = SP_ASPECT_SLICE;
716                                 } else {
717                                         break;
718                                 }
719                         }
720                         image->aspect_align = align;
721                         image->aspect_clip = clip;
722                 }
723                 break;
724 #if ENABLE_LCMS
725         case SP_PROP_COLOR_PROFILE:
726                 if ( image->color_profile ) {
727                     g_free (image->color_profile);
728                 }
729                 image->color_profile = (value) ? g_strdup (value) : NULL;
730 #ifdef DEBUG_LCMS
731                 if ( value ) {
732                     DEBUG_MESSAGE( lcmsFour, "<image> color-profile set to '%s'", value );
733                 } else {
734                     DEBUG_MESSAGE( lcmsFour, "<image> color-profile cleared" );
735                 }
736 #endif // DEBUG_LCMS
737                 // TODO check on this HREF_MODIFIED flag
738                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_IMAGE_HREF_MODIFIED_FLAG);
739                 break;
740 #endif // ENABLE_LCMS
741         default:
742                 if (((SPObjectClass *) (parent_class))->set)
743                         ((SPObjectClass *) (parent_class))->set (object, key, value);
744                 break;
745         }
746         
747         sp_image_set_curve(image); //creates a curve at the image's boundary for snapping
750 static void
751 sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags)
753     SPImage *image;
755     image = (SPImage *) object;
756     SPDocument *doc = SP_OBJECT_DOCUMENT(object);
758         if (((SPObjectClass *) (parent_class))->update)
759                 ((SPObjectClass *) (parent_class))->update (object, ctx, flags);
761         if (flags & SP_IMAGE_HREF_MODIFIED_FLAG) {
762                 if (image->pixbuf) {
763                         gdk_pixbuf_unref (image->pixbuf);
764                         image->pixbuf = NULL;
765                 }
766                 if (image->href) {
767                         GdkPixbuf *pixbuf;
768                         pixbuf = sp_image_repr_read_image (
769                     object->repr->attribute("xlink:href"),
770                     object->repr->attribute("sodipodi:absref"),
771                     doc->base);
772                         if (pixbuf) {
773                                 pixbuf = sp_image_pixbuf_force_rgba (pixbuf);
774 // BLIP
775 #if ENABLE_LCMS
776                                 if ( image->color_profile )
777                                 {
778                                     int imagewidth = gdk_pixbuf_get_width( pixbuf );
779                                     int imageheight = gdk_pixbuf_get_height( pixbuf );
780                                     int rowstride = gdk_pixbuf_get_rowstride( pixbuf );
781                                     guchar* px = gdk_pixbuf_get_pixels( pixbuf );
783                                     if ( px ) {
784 #ifdef DEBUG_LCMS
785                                         DEBUG_MESSAGE( lcmsFive, "in <image>'s sp_image_update. About to call colorprofile_get_handle()" );
786 #endif // DEBUG_LCMS
787                                         guint profIntent = Inkscape::RENDERING_INTENT_UNKNOWN;
788                                         cmsHPROFILE prof = Inkscape::colorprofile_get_handle( SP_OBJECT_DOCUMENT( object ),
789                                                                                               &profIntent,
790                                                                                               image->color_profile );
791                                         if ( prof ) {
792                                             icProfileClassSignature profileClass = cmsGetDeviceClass( prof );
793                                             if ( profileClass != icSigNamedColorClass ) {
794                                                 int intent = INTENT_PERCEPTUAL;
795                                                 switch ( profIntent ) {
796                                                     case Inkscape::RENDERING_INTENT_RELATIVE_COLORIMETRIC:
797                                                         intent = INTENT_RELATIVE_COLORIMETRIC;
798                                                         break;
799                                                     case Inkscape::RENDERING_INTENT_SATURATION:
800                                                         intent = INTENT_SATURATION;
801                                                         break;
802                                                     case Inkscape::RENDERING_INTENT_ABSOLUTE_COLORIMETRIC:
803                                                         intent = INTENT_ABSOLUTE_COLORIMETRIC;
804                                                         break;
805                                                     case Inkscape::RENDERING_INTENT_PERCEPTUAL:
806                                                     case Inkscape::RENDERING_INTENT_UNKNOWN:
807                                                     case Inkscape::RENDERING_INTENT_AUTO:
808                                                     default:
809                                                         intent = INTENT_PERCEPTUAL;
810                                                 }
811                                                 cmsHPROFILE destProf = cmsCreate_sRGBProfile();
812                                                 cmsHTRANSFORM transf = cmsCreateTransform( prof,
813                                                                                            TYPE_RGBA_8,
814                                                                                            destProf,
815                                                                                            TYPE_RGBA_8,
816                                                                                            intent, 0 );
817                                                 if ( transf ) {
818                                                     guchar* currLine = px;
819                                                     for ( int y = 0; y < imageheight; y++ ) {
820                                                         // Since the types are the same size, we can do the transformation in-place
821                                                         cmsDoTransform( transf, currLine, currLine, imagewidth );
822                                                         currLine += rowstride;
823                                                     }
825                                                     cmsDeleteTransform( transf );
826                                                 }
827 #ifdef DEBUG_LCMS
828                                                 else
829                                                 {
830                                                     DEBUG_MESSAGE( lcmsSix, "in <image>'s sp_image_update. Unable to create LCMS transform." );
831                                                 }
832 #endif // DEBUG_LCMS
833                                                 cmsCloseProfile( destProf );
834                                             }
835 #ifdef DEBUG_LCMS
836                                             else
837                                             {
838                                                 DEBUG_MESSAGE( lcmsSeven, "in <image>'s sp_image_update. Profile type is named color. Can't transform." );
839                                             }
840 #endif // DEBUG_LCMS
841                                         }
842 #ifdef DEBUG_LCMS
843                                         else
844                                         {
845                                             DEBUG_MESSAGE( lcmsEight, "in <image>'s sp_image_update. No profile found." );
846                                         }
847 #endif // DEBUG_LCMS
848                                     }
849                                 }
850 #endif // ENABLE_LCMS
851                                 image->pixbuf = pixbuf;
852                         }
853                 }
854         }
855         // preserveAspectRatio calculate bounds / clipping rectangle -- EAF
856         if (image->pixbuf && (image->aspect_align != SP_ASPECT_NONE)) {
857                         int imagewidth, imageheight;
858                         double x,y;
860                         imagewidth = gdk_pixbuf_get_width (image->pixbuf);
861                         imageheight = gdk_pixbuf_get_height (image->pixbuf);
863                         switch (image->aspect_align) {
864                         case SP_ASPECT_XMIN_YMIN:
865                                 x = 0.0;
866                                 y = 0.0;
867                                 break;
868                         case SP_ASPECT_XMID_YMIN:
869                                 x = 0.5;
870                                 y = 0.0;
871                                 break;
872                         case SP_ASPECT_XMAX_YMIN:
873                                 x = 1.0;
874                                 y = 0.0;
875                                 break;
876                         case SP_ASPECT_XMIN_YMID:
877                                 x = 0.0;
878                                 y = 0.5;
879                                 break;
880                         case SP_ASPECT_XMID_YMID:
881                                 x = 0.5;
882                                 y = 0.5;
883                                 break;
884                         case SP_ASPECT_XMAX_YMID:
885                                 x = 1.0;
886                                 y = 0.5;
887                                 break;
888                         case SP_ASPECT_XMIN_YMAX:
889                                 x = 0.0;
890                                 y = 1.0;
891                                 break;
892                         case SP_ASPECT_XMID_YMAX:
893                                 x = 0.5;
894                                 y = 1.0;
895                                 break;
896                         case SP_ASPECT_XMAX_YMAX:
897                                 x = 1.0;
898                                 y = 1.0;
899                                 break;
900                         default:
901                                 x = 0.0;
902                                 y = 0.0;
903                                 break;
904                         }
906                         if (image->aspect_clip == SP_ASPECT_SLICE) {
907                                 image->viewx = image->x.computed;
908                                 image->viewy = image->y.computed;
909                                 image->viewwidth = image->width.computed;
910                                 image->viewheight = image->height.computed;
911                                 if ((imagewidth*image->height.computed)>(image->width.computed*imageheight)) {
912                                         // Pixels aspect is wider than bounding box
913                                         image->trimheight = imageheight;
914                                         image->trimwidth = static_cast<int>(static_cast<double>(imageheight) * image->width.computed / image->height.computed);
915                                         image->trimy = 0;
916                                         image->trimx = static_cast<int>(static_cast<double>(imagewidth - image->trimwidth) * x);
917                                 } else {
918                                         // Pixels aspect is taller than bounding box
919                                         image->trimwidth = imagewidth;
920                                         image->trimheight = static_cast<int>(static_cast<double>(imagewidth) * image->height.computed / image->width.computed);
921                                         image->trimx = 0;
922                                         image->trimy = static_cast<int>(static_cast<double>(imageheight - image->trimheight) * y);
923                                 }
924                         } else {
925                                 // Otherwise, assume SP_ASPECT_MEET
926                                 image->trimx = 0;
927                                 image->trimy = 0;
928                                 image->trimwidth = imagewidth;
929                                 image->trimheight = imageheight;
930                                 if ((imagewidth*image->height.computed)>(image->width.computed*imageheight)) {
931                                         // Pixels aspect is wider than bounding boz
932                                         image->viewwidth = image->width.computed;
933                                         image->viewheight = image->viewwidth * imageheight / imagewidth;
934                                         image->viewx=image->x.computed;
935                                         image->viewy=(image->height.computed - image->viewheight) * y + image->y.computed;
936                                 } else {
937                                         // Pixels aspect is taller than bounding box
938                                         image->viewheight = image->height.computed;
939                                         image->viewwidth = image->viewheight * imagewidth / imageheight;
940                                         image->viewy=image->y.computed;
941                                         image->viewx=(image->width.computed - image->viewwidth) * x + image->x.computed;
942                                 }
943                         }
944         }
945         sp_image_update_canvas_image ((SPImage *) object);
948 static Inkscape::XML::Node *
949 sp_image_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
951         SPImage *image;
953         image = SP_IMAGE (object);
955         if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
956                 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
957                 repr = xml_doc->createElement("svg:image");
958         }
960         repr->setAttribute("xlink:href", image->href);
961         /* fixme: Reset attribute if needed (Lauris) */
962         if (image->x._set) sp_repr_set_svg_double(repr, "x", image->x.computed);
963         if (image->y._set) sp_repr_set_svg_double(repr, "y", image->y.computed);
964         if (image->width._set) sp_repr_set_svg_double(repr, "width", image->width.computed);
965         if (image->height._set) sp_repr_set_svg_double(repr, "height", image->height.computed);
966         repr->setAttribute("preserveAspectRatio", object->repr->attribute("preserveAspectRatio"));
967 #if ENABLE_LCMS
968         if (image->color_profile) repr->setAttribute("color-profile", image->color_profile);
969 #endif // ENABLE_LCMS
971         if (((SPObjectClass *) (parent_class))->write)
972                 ((SPObjectClass *) (parent_class))->write (object, repr, flags);
974         return repr;
977 static void
978 sp_image_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const /*flags*/)
980         SPImage const &image = *SP_IMAGE(item);
982         if ((image.width.computed > 0.0) && (image.height.computed > 0.0)) {
983                 double const x0 = image.x.computed;
984                 double const y0 = image.y.computed;
985                 double const x1 = x0 + image.width.computed;
986                 double const y1 = y0 + image.height.computed;
988                 nr_rect_union_pt(bbox, NR::Point(x0, y0) * transform);
989                 nr_rect_union_pt(bbox, NR::Point(x1, y0) * transform);
990                 nr_rect_union_pt(bbox, NR::Point(x1, y1) * transform);
991                 nr_rect_union_pt(bbox, NR::Point(x0, y1) * transform);
992         }
995 static void
996 sp_image_print (SPItem *item, SPPrintContext *ctx)
998         SPImage *image;
999         NRMatrix tp, ti, s, t;
1000         guchar *px;
1001         int w, h, rs, pixskip;
1003         image = SP_IMAGE (item);
1005         if (!image->pixbuf) return;
1006         if ((image->width.computed <= 0.0) || (image->height.computed <= 0.0)) return;
1008         px = gdk_pixbuf_get_pixels (image->pixbuf);
1009         w = gdk_pixbuf_get_width (image->pixbuf);
1010         h = gdk_pixbuf_get_height (image->pixbuf);
1011         rs = gdk_pixbuf_get_rowstride (image->pixbuf);
1012         pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
1014         if (image->aspect_align == SP_ASPECT_NONE) {
1015                 /* fixme: (Lauris) */
1016                 nr_matrix_set_translate (&tp, image->x.computed, image->y.computed);
1017                 nr_matrix_set_scale (&s, image->width.computed, -image->height.computed);
1018                 nr_matrix_set_translate (&ti, 0.0, -1.0);
1019         } else { // preserveAspectRatio
1020                 nr_matrix_set_translate (&tp, image->viewx, image->viewy);
1021                 nr_matrix_set_scale (&s, image->viewwidth, -image->viewheight);
1022                 nr_matrix_set_translate (&ti, 0.0, -1.0);
1023         }
1025         nr_matrix_multiply (&t, &s, &tp);
1026         nr_matrix_multiply (&t, &ti, &t);
1028         if (image->aspect_align == SP_ASPECT_NONE)
1029                 sp_print_image_R8G8B8A8_N (ctx, px, w, h, rs, &t, SP_OBJECT_STYLE (item));
1030         else // preserveAspectRatio
1031                 sp_print_image_R8G8B8A8_N (ctx, px + image->trimx*pixskip + image->trimy*rs, image->trimwidth, image->trimheight, rs, &t, SP_OBJECT_STYLE (item));
1034 static gchar *
1035 sp_image_description(SPItem *item)
1037         SPImage *image = SP_IMAGE(item);
1038         char *href_desc;
1039         if (image->href) {
1040             href_desc = (strncmp(image->href, "data:", 5) == 0)
1041                 ? g_strdup(_("embedded"))
1042                 : xml_quote_strdup(image->href);
1043         } else {
1044             g_warning("Attempting to call strncmp() with a null pointer.");
1045             href_desc = g_strdup("(null_pointer)"); // we call g_free() on href_desc
1046         }
1048         char *ret = ( image->pixbuf == NULL
1049                       ? g_strdup_printf(_("<b>Image with bad reference</b>: %s"), href_desc)
1050                       : g_strdup_printf(_("<b>Image</b> %d &#215; %d: %s"),
1051                                         gdk_pixbuf_get_width(image->pixbuf),
1052                                         gdk_pixbuf_get_height(image->pixbuf),
1053                                         href_desc) );
1054         g_free(href_desc);
1055         return ret;
1058 static NRArenaItem *
1059 sp_image_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
1061         int pixskip, rs;
1062         SPImage * image;
1063         NRArenaItem *ai;
1065         image = (SPImage *) item;
1067         ai = NRArenaImage::create(arena);
1069         if (image->pixbuf) {
1070                 pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
1071                 rs = gdk_pixbuf_get_rowstride (image->pixbuf);
1072                 nr_arena_image_set_style(NR_ARENA_IMAGE(ai), SP_OBJECT_STYLE(SP_OBJECT(item)));
1073                 if (image->aspect_align == SP_ASPECT_NONE)
1074                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai),
1075                                            gdk_pixbuf_get_pixels (image->pixbuf),
1076                                            gdk_pixbuf_get_width (image->pixbuf),
1077                                            gdk_pixbuf_get_height (image->pixbuf),
1078                                            rs);
1079                 else // preserveAspectRatio
1080                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai),
1081                                            gdk_pixbuf_get_pixels (image->pixbuf) + image->trimx*pixskip + image->trimy*rs,
1082                                            image->trimwidth,
1083                                            image->trimheight,
1084                                            rs);
1085         } else {
1086                 nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai), NULL, 0, 0, 0);
1087         }
1088         if (image->aspect_align == SP_ASPECT_NONE)
1089                 nr_arena_image_set_geometry (NR_ARENA_IMAGE (ai), image->x.computed, image->y.computed, image->width.computed, image->height.computed);
1090         else // preserveAspectRatio
1091                 nr_arena_image_set_geometry (NR_ARENA_IMAGE (ai), image->viewx, image->viewy, image->viewwidth, image->viewheight);
1093         return ai;
1096 /*
1097  * utility function to try loading image from href
1098  *
1099  * docbase/relative_src
1100  * absolute_src
1101  *
1102  */
1104 GdkPixbuf *
1105 sp_image_repr_read_image (const gchar *href, const gchar *absref, const gchar *base)
1107     const gchar *filename, *docbase;
1108     gchar *fullname;
1109     GdkPixbuf *pixbuf;
1111     filename = href;
1112     if (filename != NULL) {
1113         if (strncmp (filename,"file:",5) == 0) {
1114             fullname = g_filename_from_uri(filename, NULL, NULL);
1115             if (fullname) {
1116                 // TODO check this. Was doing a UTF-8 to filename conversion here.
1117                 pixbuf = Inkscape::IO::pixbuf_new_from_file (fullname, NULL);
1118                 if (pixbuf != NULL) return pixbuf;
1119             }
1120         } else if (strncmp (filename,"data:",5) == 0) {
1121             /* data URI - embedded image */
1122             filename += 5;
1123             pixbuf = sp_image_repr_read_dataURI (filename);
1124             if (pixbuf != NULL) return pixbuf;
1125         } else {
1127             if (!g_path_is_absolute (filename)) {
1128                 /* try to load from relative pos combined with document base*/
1129                 docbase = base;
1130                 if (!docbase) docbase = ".";
1131                 fullname = g_build_filename(docbase, filename, NULL);
1133                 // document base can be wrong (on the temporary doc when importing bitmap from a
1134                 // different dir) or unset (when doc is not saved yet), so we check for base+href existence first,
1135                 // and if it fails, we also try to use bare href regardless of its g_path_is_absolute
1136                 if (g_file_test (fullname, G_FILE_TEST_EXISTS) && !g_file_test (fullname, G_FILE_TEST_IS_DIR)) {
1137                     pixbuf = Inkscape::IO::pixbuf_new_from_file( fullname, NULL );
1138                     g_free (fullname);
1139                     if (pixbuf != NULL) return pixbuf;
1140                 }
1141             }
1143             /* try filename as absolute */
1144             if (g_file_test (filename, G_FILE_TEST_EXISTS) && !g_file_test (filename, G_FILE_TEST_IS_DIR)) {
1145                 pixbuf = Inkscape::IO::pixbuf_new_from_file( filename, NULL );
1146                 if (pixbuf != NULL) return pixbuf;
1147             }
1148         }
1149     }
1151     /* at last try to load from sp absolute path name */
1152     filename = absref;
1153     if (filename != NULL) {
1154         // using absref is outside of SVG rules, so we must at least warn the user
1155         if ( base != NULL && href != NULL )
1156                 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);
1157                 else
1158                     g_warning ("xlink:href did not resolve to a valid image file, now trying sodipodi:absref=\"%s\"", absref);
1160         pixbuf = Inkscape::IO::pixbuf_new_from_file( filename, NULL );
1161         if (pixbuf != NULL) return pixbuf;
1162     }
1163     /* Nope: We do not find any valid pixmap file :-( */
1164     pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **) brokenimage_xpm);
1166     /* It should be included xpm, so if it still does not does load, */
1167     /* our libraries are broken */
1168     g_assert (pixbuf != NULL);
1170     return pixbuf;
1173 static GdkPixbuf *
1174 sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf)
1176         GdkPixbuf * newbuf;
1178         if (gdk_pixbuf_get_has_alpha (pixbuf)) return pixbuf;
1180         newbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
1181         gdk_pixbuf_unref (pixbuf);
1183         return newbuf;
1186 /* We assert that realpixbuf is either NULL or identical size to pixbuf */
1188 static void
1189 sp_image_update_canvas_image (SPImage *image)
1191         int rs, pixskip;
1192         SPItem *item;
1193         SPItemView *v;
1195         item = SP_ITEM (image);
1197         if (image->pixbuf) {
1198                 /* fixme: We are slightly violating spec here (Lauris) */
1199                 if (!image->width._set) {
1200                         image->width.computed = gdk_pixbuf_get_width (image->pixbuf);
1201                 }
1202                 if (!image->height._set) {
1203                         image->height.computed = gdk_pixbuf_get_height (image->pixbuf);
1204                 }
1205         }
1207         for (v = item->display; v != NULL; v = v->next) {
1208                 pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
1209                 rs = gdk_pixbuf_get_rowstride (image->pixbuf);
1210                 nr_arena_image_set_style (NR_ARENA_IMAGE(v->arenaitem), SP_OBJECT_STYLE(SP_OBJECT(image)));
1211                 if (image->aspect_align == SP_ASPECT_NONE) {
1212                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (v->arenaitem),
1213                                            gdk_pixbuf_get_pixels (image->pixbuf),
1214                                            gdk_pixbuf_get_width (image->pixbuf),
1215                                            gdk_pixbuf_get_height (image->pixbuf),
1216                                            rs);
1217                         nr_arena_image_set_geometry (NR_ARENA_IMAGE (v->arenaitem),
1218                                              image->x.computed, image->y.computed,
1219                                              image->width.computed, image->height.computed);
1220                 } else { // preserveAspectRatio
1221                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (v->arenaitem),
1222                                            gdk_pixbuf_get_pixels (image->pixbuf) + image->trimx*pixskip + image->trimy*rs,
1223                                            image->trimwidth,
1224                                            image->trimheight,
1225                                            rs);
1226                         nr_arena_image_set_geometry (NR_ARENA_IMAGE (v->arenaitem),
1227                                              image->viewx, image->viewy,
1228                                              image->viewwidth, image->viewheight);
1229                 }
1230         }
1233 static void sp_image_snappoints(SPItem const *item, SnapPointsIter p)
1235     /* An image doesn't have any nodes to snap, but still we want to be able snap one image 
1236     to another. Therefore we will create some snappoints at the corner, similar to a rect. If
1237     the image is rotated, then the snappoints will rotate with it. Again, just like a rect.
1238     */
1239      
1240     g_assert(item != NULL);
1241     g_assert(SP_IS_IMAGE(item));
1243     if (item->clip_ref->getObject()) {
1244         //We are looking at a clipped image: do not return any snappoints, as these might be
1245         //far far away from the visible part from the clipped image
1246     } else {
1247         // The image has not been clipped: return its corners, which might be rotated for example
1248         SPImage &image = *SP_IMAGE(item);
1249         double const x0 = image.x.computed;
1250                 double const y0 = image.y.computed;
1251                 double const x1 = x0 + image.width.computed;
1252                 double const y1 = y0 + image.height.computed;
1253                 NR::Matrix const i2d (sp_item_i2d_affine (item));
1254                 *p = NR::Point(x0, y0) * i2d;
1255         *p = NR::Point(x0, y1) * i2d;
1256         *p = NR::Point(x1, y1) * i2d;
1257         *p = NR::Point(x1, y0) * i2d;
1258     }
1261 /*
1262  * Initially we'll do:
1263  * Transform x, y, set x, y, clear translation
1264  */
1266 static NR::Matrix
1267 sp_image_set_transform(SPItem *item, NR::Matrix const &xform)
1269         SPImage *image = SP_IMAGE(item);
1271         /* Calculate position in parent coords. */
1272         NR::Point pos( NR::Point(image->x.computed, image->y.computed) * xform );
1274         /* This function takes care of translation and scaling, we return whatever parts we can't
1275            handle. */
1276         NR::Matrix ret(NR::transform(xform));
1277         NR::Point const scale(hypot(ret[0], ret[1]),
1278                               hypot(ret[2], ret[3]));
1279         if ( scale[NR::X] > 1e-9 ) {
1280                 ret[0] /= scale[NR::X];
1281                 ret[1] /= scale[NR::X];
1282         } else {
1283                 ret[0] = 1.0;
1284                 ret[1] = 0.0;
1285         }
1286         if ( scale[NR::Y] > 1e-9 ) {
1287                 ret[2] /= scale[NR::Y];
1288                 ret[3] /= scale[NR::Y];
1289         } else {
1290                 ret[2] = 0.0;
1291                 ret[3] = 1.0;
1292         }
1294         image->width = image->width.computed * scale[NR::X];
1295         image->height = image->height.computed * scale[NR::Y];
1297         /* Find position in item coords */
1298         pos = pos * ret.inverse();
1299         image->x = pos[NR::X];
1300         image->y = pos[NR::Y];
1302         item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1304         return ret;
1307 static GdkPixbuf *
1308 sp_image_repr_read_dataURI (const gchar * uri_data)
1309 {       GdkPixbuf * pixbuf = NULL;
1311         gint data_is_image = 0;
1312         gint data_is_base64 = 0;
1314         const gchar * data = uri_data;
1316         while (*data) {
1317                 if (strncmp (data,"base64",6) == 0) {
1318                         /* base64-encoding */
1319                         data_is_base64 = 1;
1320                         data_is_image = 1; // Illustrator produces embedded images without MIME type, so we assume it's image no matter what
1321                         data += 6;
1322                 }
1323                 else if (strncmp (data,"image/png",9) == 0) {
1324                         /* PNG image */
1325                         data_is_image = 1;
1326                         data += 9;
1327                 }
1328                 else if (strncmp (data,"image/jpg",9) == 0) {
1329                         /* JPEG image */
1330                         data_is_image = 1;
1331                         data += 9;
1332                 }
1333                 else if (strncmp (data,"image/jpeg",10) == 0) {
1334                         /* JPEG image */
1335                         data_is_image = 1;
1336                         data += 10;
1337                 }
1338                 else { /* unrecognized option; skip it */
1339                         while (*data) {
1340                                 if (((*data) == ';') || ((*data) == ',')) break;
1341                                 data++;
1342                         }
1343                 }
1344                 if ((*data) == ';') {
1345                         data++;
1346                         continue;
1347                 }
1348                 if ((*data) == ',') {
1349                         data++;
1350                         break;
1351                 }
1352         }
1354         if ((*data) && data_is_image && data_is_base64) {
1355                 pixbuf = sp_image_repr_read_b64 (data);
1356         }
1358         return pixbuf;
1361 static GdkPixbuf *
1362 sp_image_repr_read_b64 (const gchar * uri_data)
1363 {       GdkPixbuf * pixbuf = NULL;
1364         GdkPixbufLoader * loader = NULL;
1366         gint j;
1367         gint k;
1368         gint l;
1369         gint b;
1370         gint len;
1371         gint eos = 0;
1372         gint failed = 0;
1374         guint32 bits;
1376         static const gchar B64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1378         const gchar* btr = uri_data;
1380         gchar ud[4];
1382         guchar bd[57];
1384         loader = gdk_pixbuf_loader_new ();
1386         if (loader == NULL) return NULL;
1388         while (eos == 0) {
1389                 l = 0;
1390                 for (j = 0; j < 19; j++) {
1391                         len = 0;
1392                         for (k = 0; k < 4; k++) {
1393                                 while (isspace ((int) (*btr))) {
1394                                         if ((*btr) == '\0') break;
1395                                         btr++;
1396                                 }
1397                                 if (eos) {
1398                                         ud[k] = 0;
1399                                         continue;
1400                                 }
1401                                 if (((*btr) == '\0') || ((*btr) == '=')) {
1402                                         eos = 1;
1403                                         ud[k] = 0;
1404                                         continue;
1405                                 }
1406                                 ud[k] = 64;
1407                                 for (b = 0; b < 64; b++) { /* There must a faster way to do this... ?? */
1408                                         if (B64[b] == (*btr)) {
1409                                                 ud[k] = (gchar) b;
1410                                                 break;
1411                                         }
1412                                 }
1413                                 if (ud[k] == 64) { /* data corruption ?? */
1414                                         eos = 1;
1415                                         ud[k] = 0;
1416                                         continue;
1417                                 }
1418                                 btr++;
1419                                 len++;
1420                         }
1421                         bits = (guint32) ud[0];
1422                         bits = (bits << 6) | (guint32) ud[1];
1423                         bits = (bits << 6) | (guint32) ud[2];
1424                         bits = (bits << 6) | (guint32) ud[3];
1425                         bd[l++] = (guchar) ((bits & 0xff0000) >> 16);
1426                         if (len > 2) {
1427                                 bd[l++] = (guchar) ((bits & 0xff00) >>  8);
1428                         }
1429                         if (len > 3) {
1430                                 bd[l++] = (guchar)  (bits & 0xff);
1431                         }
1432                 }
1434                 if (!gdk_pixbuf_loader_write (loader, (const guchar *) bd, (size_t) l, NULL)) {
1435                         failed = 1;
1436                         break;
1437                 }
1438         }
1440         gdk_pixbuf_loader_close (loader, NULL);
1442         if (!failed) pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
1444         return pixbuf;
1447 static void
1448 sp_image_set_curve(SPImage *image) 
1450     //create a curve at the image's boundary for snapping
1451     if ((image->height.computed < 1e-18) || (image->width.computed < 1e-18) || (image->clip_ref->getObject())) {
1452         if (image->curve) {
1453             image->curve = sp_curve_unref(image->curve);
1454         }
1455         return;
1456     }
1457     
1458     NRRect rect;
1459         sp_image_bbox(image, &rect, NR::identity(), 0);
1460         NR::Maybe<NR::Rect> rect2 = rect.upgrade();
1461         SPCurve *c = sp_curve_new_from_rect(rect2);
1462         
1463     if (image->curve) {
1464         image->curve = sp_curve_unref(image->curve);
1465     }
1466     
1467     if (c) {
1468         image->curve = sp_curve_ref(c);
1469     }
1470     
1471     sp_curve_unref(c);    
1474 /**
1475  * Return duplicate of curve (if any exists) or NULL if there is no curve
1476  */
1477 SPCurve *
1478 sp_image_get_curve (SPImage *image)
1480         if (image->curve) {
1481                 return sp_curve_copy(image->curve);
1482         }
1483         return NULL;
1486 /*
1487   Local Variables:
1488   mode:c++
1489   c-file-style:"stroustrup"
1490   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1491   indent-tabs-mode:nil
1492   fill-column:99
1493   End:
1494 */
1495 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :