1 /*
2 * go-image.c: Image formats
3 *
4 * Copyright (C) 2004, 2005 Jody Goldberg (jody@gnome.org)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 #include <goffice/goffice-config.h>
23 #include <goffice/goffice.h>
24 #include <string.h>
25 #include <gsf/gsf-utils.h>
26 #include <gsf/gsf-impl-utils.h>
27 #include <glib/gi18n-lib.h>
28 #include <librsvg/rsvg.h>
29
30 /**
31 * GOImageFormat:
32 * @GO_IMAGE_FORMAT_SVG: SVG.
33 * @GO_IMAGE_FORMAT_PNG: PNG.
34 * @GO_IMAGE_FORMAT_JPG: JPEG.
35 * @GO_IMAGE_FORMAT_PDF: PDF.
36 * @GO_IMAGE_FORMAT_PS: PostScript.
37 * @GO_IMAGE_FORMAT_EMF: Enhanced Metafile.
38 * @GO_IMAGE_FORMAT_WMF: Windows Metafile.
39 * @GO_IMAGE_FORMAT_EPS: Encapsulated Postscript.
40 * @GO_IMAGE_FORMAT_UNKNOWN: unknown.
41 **/
42
43 /**
44 * GOImageFormatInfo:
45 * @format: #GOImageFormat.
46 * @name: image type name.
47 * @desc: image type description.
48 * @ext: file extension.
49 * @has_pixbuf_saver: can be saved as pixbuf.
50 * @is_dpi_useful: depends upon device resolution.
51 * @alpha_support: supports transparency.
52 **/
53
54 /**
55 * GOImageClass:
56 * @parent_klass: parent class.
57 * @get_pixbuf: gets a pixbuf with the same size as the image.
58 * @get_scaled_pixbuf: gets a scaled pixbuf respecting the original aspect ratio.
59 * @save: saves the image.
60 * @load_attr: loads the images properties.
61 * @load_data: loads the image data.
62 * @draw: draws the image.
63 * @differ: returns %TRUE if the two images are different.
64 **/
65
66 static GOImageFormatInfo *pixbuf_image_format_infos = NULL;
67 static GHashTable *pixbuf_mimes = NULL;
68 static unsigned pixbuf_format_nbr = 0;
69 static gboolean pixbuf_format_done = FALSE;
70
71 #define PIXBUF_IMAGE_FORMAT_OFFSET (1+GO_IMAGE_FORMAT_UNKNOWN)
72
73 static void go_image_build_pixbuf_format_infos (void);
74
75 /**
76 * go_mime_to_image_format:
77 * @mime_type: a mime type string
78 *
79 * returns: file extension for the given mime type.
80 **/
81
82 char *
go_mime_to_image_format(char const * mime_type)83 go_mime_to_image_format (char const *mime_type)
84 {
85 guint i;
86 const char* exceptions[] = {
87 "image/svg", "svg",
88 "image/svg+xml", "svg",
89 "image/svg-xml", "svg",
90 "image/vnd.adobe.svg+xml", "svg",
91 "text/xml-svg", "svg",
92 "image/wmf", "wmf",
93 "image/emf", "emf",
94 "application/pdf", "pdf",
95 "application/postscript", "ps",
96 "image/x-eps", "eps",
97 };
98
99 for (i = 0; i < G_N_ELEMENTS (exceptions); i += 2)
100 if (strcmp (mime_type, exceptions[i]) == 0)
101 return g_strdup (exceptions[i + 1]);
102
103 go_image_build_pixbuf_format_infos ();
104
105 return g_strdup (g_hash_table_lookup (pixbuf_mimes, mime_type));
106 }
107
108 /**
109 * go_image_format_to_mime:
110 * @format: a file extension string
111 *
112 * returns: corresponding mime type.
113 **/
114
115 char *
go_image_format_to_mime(char const * format)116 go_image_format_to_mime (char const *format)
117 {
118 char *ret = NULL;
119 guint i;
120 GSList *ptr, *pixbuf_fmts;
121 static const char* const formats[] = {
122 "svg", "image/svg,image/svg+xml",
123 "wmf", "image/x-wmf",
124 "emf", "image/x-emf",
125 "pdf", "application/pdf",
126 "ps", "application/postscript",
127 "eps", "image/x-eps",
128 };
129
130 if (format == NULL)
131 return NULL;
132
133 for (i = 0; i < G_N_ELEMENTS (formats); i += 2)
134 if (strcmp (format, formats[i]) == 0)
135 return g_strdup (formats[i + 1]);
136
137 /* Not a format we have special knowledge about - ask gdk-pixbuf */
138 pixbuf_fmts = gdk_pixbuf_get_formats ();
139 for (ptr = pixbuf_fmts; ptr != NULL; ptr = ptr->next) {
140 GdkPixbufFormat *pfmt = ptr->data;
141 gchar *name = gdk_pixbuf_format_get_name (pfmt);
142 int cmp = strcmp (format, name);
143 g_free (name);
144 if (cmp == 0) {
145 gchar **mimes =
146 gdk_pixbuf_format_get_mime_types (pfmt);
147 ret = g_strjoinv (",", mimes);
148 g_strfreev (mimes);
149 break;
150 }
151 }
152 g_slist_free (pixbuf_fmts);
153
154 return ret;
155 }
156
157 static GOImageFormatInfo const image_format_infos[GO_IMAGE_FORMAT_UNKNOWN] = {
158 {GO_IMAGE_FORMAT_SVG, (char *) "svg", (char *) N_("SVG (vector graphics)"),
159 (char *) "svg", FALSE, FALSE, TRUE},
160 {GO_IMAGE_FORMAT_PNG, (char *) "png", (char *) N_("PNG (raster graphics)"),
161 (char *) "png", TRUE, TRUE, TRUE},
162 {GO_IMAGE_FORMAT_JPG, (char *) "jpeg", (char *) N_("JPEG (photograph)"),
163 (char *) "jpg", TRUE, TRUE, FALSE},
164 {GO_IMAGE_FORMAT_PDF, (char *) "pdf", (char *) N_("PDF (portable document format)"),
165 (char *) "pdf", FALSE, FALSE, TRUE},
166 {GO_IMAGE_FORMAT_PS, (char *) "ps", (char *) N_("PS (postscript)"),
167 (char *) "ps", FALSE, TRUE, TRUE},
168 {GO_IMAGE_FORMAT_EMF, (char *) "emf", (char *) N_("EMF (extended metafile)"),
169 (char *) "emf", FALSE, FALSE, TRUE},
170 {GO_IMAGE_FORMAT_WMF, (char *) "wmf", (char *) N_("WMF (windows metafile)"),
171 (char *) "wmf", FALSE, FALSE, TRUE},
172 {GO_IMAGE_FORMAT_EPS, (char *) "eps", (char *) N_("EPS (encapsulated postscript)"),
173 (char *) "eps", FALSE, TRUE, TRUE},
174 };
175
176 /* Making GOFormatInfo as a boxed type, as they are static, ref and unref don't
177 * need to do anything */
178 static GOImageFormatInfo *
go_image_format_info_ref(GOImageFormatInfo * info)179 go_image_format_info_ref (GOImageFormatInfo *info)
180 {
181 return info;
182 }
183
184 static void
go_image_format_info_unref(GOImageFormatInfo const * info)185 go_image_format_info_unref (GOImageFormatInfo const *info)
186 {
187 }
188
189 GType
go_image_format_info_get_type(void)190 go_image_format_info_get_type (void)
191 {
192 static GType t = 0;
193
194 if (t == 0)
195 t = g_boxed_type_register_static ("GOImageFormatInfo",
196 (GBoxedCopyFunc)go_image_format_info_ref,
197 (GBoxedFreeFunc)go_image_format_info_unref);
198 return t;
199 }
200
201 static void
go_image_build_pixbuf_format_infos(void)202 go_image_build_pixbuf_format_infos (void)
203 {
204 GdkPixbufFormat *fmt;
205 GSList *l, *pixbuf_fmts;
206 GOImageFormatInfo *format_info;
207 gchar **exts, **mimes;
208 unsigned i, j;
209
210 if (pixbuf_format_done)
211 return;
212
213 pixbuf_fmts = gdk_pixbuf_get_formats ();
214 pixbuf_format_nbr = g_slist_length (pixbuf_fmts);
215
216 if (pixbuf_format_nbr > 0) {
217 pixbuf_image_format_infos = g_new (GOImageFormatInfo, pixbuf_format_nbr);
218 pixbuf_mimes = g_hash_table_new_full
219 (g_str_hash, g_str_equal, g_free, g_free);
220
221 for (l = pixbuf_fmts, i = 1, format_info = pixbuf_image_format_infos;
222 l != NULL;
223 l = l->next, i++, format_info++) {
224 fmt = (GdkPixbufFormat *)l->data;
225
226 format_info->format = GO_IMAGE_FORMAT_UNKNOWN + i;
227 format_info->name = gdk_pixbuf_format_get_name (fmt);
228 format_info->desc = gdk_pixbuf_format_get_description (fmt);
229 exts = gdk_pixbuf_format_get_extensions (fmt);
230 format_info->ext = g_strdup (exts[0]);
231 if (format_info->ext == NULL)
232 format_info->ext = format_info->name;
233 g_strfreev (exts);
234 format_info->has_pixbuf_saver = gdk_pixbuf_format_is_writable (fmt);
235 format_info->is_dpi_useful = FALSE;
236 format_info->alpha_support = FALSE;
237
238 mimes = gdk_pixbuf_format_get_mime_types (fmt);
239 for (j = 0; mimes[j]; j++) {
240 g_hash_table_insert
241 (pixbuf_mimes,
242 g_strdup((gpointer) mimes[j]),
243 g_strdup((gpointer) format_info->name));
244 }
245 g_strfreev (mimes);
246 }
247 }
248
249 g_slist_free (pixbuf_fmts);
250 pixbuf_format_done = TRUE;
251 }
252
253 /**
254 * go_image_get_format_info:
255 * @format: a #GOImageFormat
256 *
257 * Retrieves information associated to @format.
258 *
259 * Returns: a #GOImageFormatInfo struct.
260 **/
261
262 GOImageFormatInfo const *
go_image_get_format_info(GOImageFormat format)263 go_image_get_format_info (GOImageFormat format)
264 {
265 if (format == GO_IMAGE_FORMAT_UNKNOWN)
266 return NULL;
267 if (format > GO_IMAGE_FORMAT_UNKNOWN)
268 go_image_build_pixbuf_format_infos ();
269
270 g_return_val_if_fail (format >= 0 &&
271 format <= GO_IMAGE_FORMAT_UNKNOWN + pixbuf_format_nbr, NULL);
272 if (format < GO_IMAGE_FORMAT_UNKNOWN)
273 return &image_format_infos[format];
274
275 return &pixbuf_image_format_infos[format - PIXBUF_IMAGE_FORMAT_OFFSET];
276 }
277
278 /**
279 * go_image_get_format_from_name:
280 * @name: a string
281 *
282 * returns: corresponding #GOImageFormat.
283 **/
284
285 GOImageFormat
go_image_get_format_from_name(char const * name)286 go_image_get_format_from_name (char const *name)
287 {
288 unsigned i;
289
290 if (name == NULL || strcmp (name, "unknown") == 0)
291 return GO_IMAGE_FORMAT_UNKNOWN;
292
293 go_image_build_pixbuf_format_infos ();
294
295 for (i = 0; i < GO_IMAGE_FORMAT_UNKNOWN; i++) {
296 if (strcmp (name, image_format_infos[i].name) == 0)
297 return image_format_infos[i].format;
298 }
299
300 for (i = 0; i < pixbuf_format_nbr; i++) {
301 if (strcmp (name, pixbuf_image_format_infos[i].name) == 0)
302 return pixbuf_image_format_infos[i].format;
303 }
304
305 g_warning ("[GOImage::get_format_from_name] Unknown format name (%s)", name);
306 return GO_IMAGE_FORMAT_UNKNOWN;
307 }
308
309 /**
310 * go_image_get_formats_with_pixbuf_saver:
311 *
312 * Returns: (element-type GOImageFormat) (transfer container): a list of #GOImageFormat that can be created
313 * from a pixbuf.
314 **/
315
316 GSList *
go_image_get_formats_with_pixbuf_saver(void)317 go_image_get_formats_with_pixbuf_saver (void)
318 {
319 GSList *list = NULL;
320 unsigned i;
321
322 for (i = 0; i < GO_IMAGE_FORMAT_UNKNOWN; i++)
323 if (image_format_infos[i].has_pixbuf_saver)
324 list = g_slist_prepend (list, GUINT_TO_POINTER (i));
325
326 /* TODO: before enabling this code, we must remove duplicate in pixbuf_image_format_infos */
327 #if 0
328 go_image_build_pixbuf_format_infos ();
329
330 for (i = 0; i < pixbuf_format_nbr; i++) {
331 if (pixbuf_image_format_infos[i].has_pixbuf_saver)
332 list = g_slist_prepend (list, GUINT_TO_POINTER (i + PIXBUF_IMAGE_FORMAT_OFFSET));
333 }
334 #endif
335
336 return list;
337 }
338
339 /*********************************
340 * GOImage object implementation *
341 *********************************/
342
343 static GObjectClass *parent_klass;
344
345 enum {
346 IMAGE_PROP_0,
347 IMAGE_PROP_WIDTH,
348 IMAGE_PROP_HEIGHT
349 };
350
351 static void
go_image_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)352 go_image_set_property (GObject *obj, guint param_id,
353 GValue const *value, GParamSpec *pspec)
354 {
355 GOImage *image = GO_IMAGE (obj);
356 gboolean size_changed = FALSE;
357 guint n;
358
359 switch (param_id) {
360 case IMAGE_PROP_WIDTH:
361 n = g_value_get_uint (value);
362 if (n != image->width) {
363 image->width = n;
364 size_changed = TRUE;
365 }
366 break;
367 case IMAGE_PROP_HEIGHT:
368 n = g_value_get_uint (value);
369 if (n != image->height) {
370 image->height = n;
371 size_changed = TRUE;
372 }
373 break;
374
375 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
376 return; /* NOTE : RETURN */
377 }
378
379 if (size_changed) {
380 g_free (image->data);
381 image->data = NULL;
382 }
383 }
384
385 static void
go_image_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)386 go_image_get_property (GObject *obj, guint param_id,
387 GValue *value, GParamSpec *pspec)
388 {
389 GOImage *image = GO_IMAGE (obj);
390
391 switch (param_id) {
392 case IMAGE_PROP_WIDTH:
393 g_value_set_uint (value, image->width);
394 break;
395 case IMAGE_PROP_HEIGHT:
396 g_value_set_uint (value, image->height);
397 break;
398
399 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
400 return; /* NOTE : RETURN */
401 }
402 }
403
404 static void
go_image_finalize(GObject * obj)405 go_image_finalize (GObject *obj)
406 {
407 GOImage *image = GO_IMAGE (obj);
408 g_free (image->data);
409 if (image->thumbnail)
410 g_object_unref (image->thumbnail);
411 if (image->pixbuf)
412 g_object_unref (image->pixbuf);
413 g_free (image->name);
414 (parent_klass->finalize) (obj);
415 }
416
417 /* default implementation for unsupported images */
418 static void
go_image_draw_fb(GOImage * image,cairo_t * cr)419 go_image_draw_fb (GOImage *image, cairo_t *cr)
420 {
421 #ifdef GOFFICE_WITH_GTK
422 if (image->pixbuf) {
423 cairo_rectangle (cr, 0., 0., image->width, image->height);
424 gdk_cairo_set_source_pixbuf (cr, image->pixbuf, 0, 0);
425 cairo_fill (cr);
426 } else {
427 GdkPixbuf *placeholder = gtk_icon_theme_load_icon
428 (gtk_icon_theme_get_default (),
429 "unknown_image", 100, 0, NULL);
430 double dx, dy;
431 int n;
432 if (placeholder == NULL)
433 return;
434 n = go_fake_floor (image->width / gdk_pixbuf_get_width (placeholder));
435 dx = (image->width - n * gdk_pixbuf_get_width (placeholder)) / 2.;
436 n = go_fake_floor (image->height / gdk_pixbuf_get_height (placeholder));
437 dy = (image->height - n * gdk_pixbuf_get_height (placeholder)) / 2.;
438 cairo_save (cr);
439 cairo_rectangle (cr, 0., 0., image->width, image->height);
440 cairo_clip (cr);
441 cairo_rectangle (cr, -dx, -dy, image->width + dx, image->height + dy);
442 gdk_cairo_set_source_pixbuf (cr, placeholder, 0, 0);
443 cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
444 cairo_fill (cr);
445 cairo_restore (cr);
446 g_object_unref (placeholder);
447 }
448 #endif
449 }
450
451 static GdkPixbuf *
go_image_get_pixbuf_fb(GOImage * image)452 go_image_get_pixbuf_fb (GOImage *image)
453 {
454 #ifdef GOFFICE_WITH_GTK
455 cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
456 image->width,
457 image->height);
458 cairo_t *cr = cairo_create (surface);
459 GdkPixbuf *ret, *placeholder = gtk_icon_theme_load_icon
460 (gtk_icon_theme_get_default (),
461 "unknown_image", 100, 0, NULL);
462 double dx, dy;
463 int n;
464 if (placeholder == NULL)
465 return NULL;
466 n = go_fake_floor (image->width / gdk_pixbuf_get_width (placeholder));
467 dx = (image->width - n * gdk_pixbuf_get_width (placeholder)) / 2.;
468 n = go_fake_floor (image->height / gdk_pixbuf_get_height (placeholder));
469 dy = (image->height - n * gdk_pixbuf_get_height (placeholder)) / 2.;
470 cairo_rectangle (cr, 0., 0., image->width, image->height);
471 cairo_clip (cr);
472 cairo_rectangle (cr, -dx, -dy, image->width + dx, image->height + dy);
473 gdk_cairo_set_source_pixbuf (cr, placeholder, 0, 0);
474 cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
475 cairo_fill (cr);
476 cairo_destroy (cr);
477 ret = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, image->width, image->height);
478 if (cairo_image_surface_get_stride (surface) != gdk_pixbuf_get_rowstride (ret)) {
479 g_object_unref (ret);
480 ret = NULL;
481 } else
482 go_cairo_convert_data_from_pixbuf (gdk_pixbuf_get_pixels (ret),
483 cairo_image_surface_get_data (surface),
484 image->width, image->height,
485 gdk_pixbuf_get_rowstride (ret));
486 cairo_surface_destroy (surface);
487 return ret;
488 #else
489 return NULL;
490 #endif
491 }
492
493 static GdkPixbuf *
go_image_get_scaled_pixbuf_fb(GOImage * image,int width,int height)494 go_image_get_scaled_pixbuf_fb (GOImage *image, int width, int height)
495 {
496 if (image->pixbuf == NULL)
497 image->pixbuf = go_image_get_pixbuf_fb (image);
498 return gdk_pixbuf_scale_simple (image->pixbuf, width, height, GDK_INTERP_BILINEAR);
499 }
500
501 static void
go_image_class_init(GOImageClass * klass)502 go_image_class_init (GOImageClass *klass)
503 {
504 GObjectClass *obj_klass = (GObjectClass *) klass;
505 obj_klass->finalize = go_image_finalize;
506 obj_klass->set_property = go_image_set_property;
507 obj_klass->get_property = go_image_get_property;
508 parent_klass = g_type_class_peek_parent (klass);
509 g_object_class_install_property (obj_klass, IMAGE_PROP_WIDTH,
510 g_param_spec_uint ("width", _("Width"),
511 _("Image width in pixels"),
512 0, G_MAXUINT16, 0, G_PARAM_READWRITE));
513 g_object_class_install_property (obj_klass, IMAGE_PROP_HEIGHT,
514 g_param_spec_uint ("height", _("Height"),
515 _("Image height in pixels"),
516 0, G_MAXUINT16, 0, G_PARAM_READWRITE));
517
518 klass->draw = go_image_draw_fb;
519 klass->get_pixbuf = go_image_get_pixbuf_fb;
520 klass->get_scaled_pixbuf = go_image_get_scaled_pixbuf_fb;
521 }
522
GSF_CLASS_ABSTRACT(GOImage,go_image,go_image_class_init,NULL,G_TYPE_OBJECT)523 GSF_CLASS_ABSTRACT (GOImage, go_image,
524 go_image_class_init, NULL,
525 G_TYPE_OBJECT)
526
527 void
528 go_image_draw (GOImage *image, cairo_t *cr)
529 {
530 g_return_if_fail (GO_IS_IMAGE (image));
531 ((GOImageClass *) G_OBJECT_GET_CLASS (image))->draw (image, cr);
532 }
533
534 #define GO_THUMBNAIL_SIZE 64
535
536 /**
537 * go_image_get_thumbnail:
538 * @image: #GOImage
539 *
540 * Generates a thumbnail for @image if not already done and returns it, adding
541 a reference. The pixbuf is scaled so that its width and height are not larger
542 * than 64 pixels, and preserving the aspect ratio.
543 * Returns: (transfer full): the thumbnail for @image
544 **/
545 GdkPixbuf const *
go_image_get_thumbnail(GOImage * image)546 go_image_get_thumbnail (GOImage *image)
547 {
548 g_return_val_if_fail (GO_IS_IMAGE (image), NULL);
549 if (image->thumbnail == NULL)
550 image->thumbnail = go_image_get_scaled_pixbuf (image, GO_THUMBNAIL_SIZE, GO_THUMBNAIL_SIZE);
551 return image->thumbnail;
552 }
553
554 /**
555 * go_image_get_pixbuf:
556 * @image: #GOImage
557 *
558 * Builds a pixbuf from the image if not already done and returns it, adding
559 * a reference.
560 * Returns: (transfer full): the pixbuf for @image
561 **/
562 GdkPixbuf *
go_image_get_pixbuf(GOImage * image)563 go_image_get_pixbuf (GOImage *image)
564 {
565 g_return_val_if_fail (GO_IS_IMAGE (image), NULL);
566 if (image->pixbuf == NULL)
567 image->pixbuf = ((GOImageClass *) G_OBJECT_GET_CLASS (image))->get_pixbuf (image);
568 return g_object_ref (image->pixbuf);
569 }
570
571 /**
572 * go_image_get_scaled_pixbuf:
573 * @image: #GOImage
574 * @width: the new pixbuf width
575 * @height: the new pixbuf height
576 *
577 * Builds a scaled pixbuf from the image and returns it. The caller needs to
578 * unref it. The pixbuf is scaled so that its width and height are not larger
579 * than, respectively, @width and @height, and preserving the aspect ratio.
580 * Returns: (transfer full): the scaled pixbuf for @image
581 **/
582 GdkPixbuf *
go_image_get_scaled_pixbuf(GOImage * image,int width,int height)583 go_image_get_scaled_pixbuf (GOImage *image, int width, int height)
584 {
585 g_return_val_if_fail (GO_IS_IMAGE (image), NULL);
586 if (image->width > width || image->height > height) {
587 if (image->width * height > image->height * width) {
588 height = width * image->height / image->width;
589 } else {
590 width = height * image->width / image->height;
591 }
592 } else
593 return ((GOImageClass *) G_OBJECT_GET_CLASS (image))->get_pixbuf (image);
594 return ((GOImageClass *) G_OBJECT_GET_CLASS (image))->get_scaled_pixbuf (image, width, height);
595 }
596
597 GOImage *
go_image_new_from_file(char const * filename,GError ** error)598 go_image_new_from_file (char const *filename, GError **error)
599 {
600 char *mime, *name;
601 GOImageFormat format;
602
603 if (!filename) {
604 return NULL;
605 }
606 mime = go_get_mime_type (filename);
607 if (!mime) {
608 return NULL;
609 }
610 name = go_mime_to_image_format (mime);
611 g_free (mime);
612 if (!name) {
613 return NULL;
614 }
615 format = go_image_get_format_from_name (name);
616 g_free (name);
617 switch (format) {
618 case GO_IMAGE_FORMAT_SVG:
619 return go_svg_new_from_file (filename, error);
620 case GO_IMAGE_FORMAT_EMF:
621 case GO_IMAGE_FORMAT_WMF:
622 return go_emf_new_from_file (filename, error);
623 case GO_IMAGE_FORMAT_EPS:
624 return go_spectre_new_from_file (filename, error);
625 case GO_IMAGE_FORMAT_PS: /* we might support ps with libspectre */
626 case GO_IMAGE_FORMAT_PDF:
627 case GO_IMAGE_FORMAT_UNKNOWN:
628 break;
629 default: {
630 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (filename, error);
631 if (pixbuf) {
632 GOImage *image = go_pixbuf_new_from_pixbuf (pixbuf);
633 g_object_unref (pixbuf);
634 return image;
635 }
636 }
637 }
638 return NULL;
639 }
640
641 GOImage *
go_image_new_from_data(char const * type,guint8 const * data,gsize length,char ** format,GError ** error)642 go_image_new_from_data (char const *type, guint8 const *data, gsize length, char **format, GError **error)
643 {
644 char *real_type = NULL;
645 GOImage *image = NULL;
646 if (type == NULL || *type == 0) {
647 char *mime_type = go_get_mime_type_for_data (data, length);
648 real_type = go_mime_to_image_format (mime_type);
649 g_free (mime_type);
650 type = real_type;
651 }
652 if (type == NULL) {
653 g_warning ("unrecognized image format");
654 return NULL;
655 }
656 if (data == NULL || length == 0) {
657 image = NULL;
658 type = "unknown";
659 } else if (!strcmp (type, "svg")) {
660 image = go_svg_new_from_data (data, length, error);
661 } else if (!strcmp (type, "emf") || !strcmp (type, "wmf")) {
662 image = go_emf_new_from_data (data, length, error);
663 } else if (!strcmp (type, "eps")) {
664 image = go_spectre_new_from_data (data, length, error);
665 } else {
666 image = go_pixbuf_new_from_data (type, data, length, error);
667 }
668 if (image == NULL) {
669 #ifdef GOFFICE_WITH_GTK
670 GdkScreen *screen = gdk_screen_get_default ();
671 GtkIconTheme *theme = screen
672 ? gtk_icon_theme_get_default()
673 : gtk_icon_theme_new();
674 GdkPixbuf *placeholder;
675 const char *icon_name;
676
677 if (theme) {
678 if (gtk_icon_theme_has_icon (theme, "unknown_image"))
679 icon_name = "unknown_image";
680 else if (gtk_icon_theme_has_icon (theme, "unknown"))
681 icon_name = "unknown";
682 else
683 icon_name = gtk_icon_theme_get_example_icon_name (theme);
684
685 placeholder = gtk_icon_theme_load_icon (theme, icon_name, 100, 0, NULL);
686 image = go_pixbuf_new_from_pixbuf (placeholder);
687 g_object_unref (placeholder);
688
689 if (!screen)
690 g_object_unref (theme);
691 }
692 #endif
693 }
694 if (format)
695 *format = g_strdup (type);
696 g_free (real_type);
697 return image;
698 }
699
700 GOImage *
go_image_new_for_format(char const * format)701 go_image_new_for_format (char const *format)
702 {
703 GType gtype = go_image_type_for_format (format);
704 return (gtype > 0)? g_object_new (gtype, NULL): NULL;
705 }
706
707 GType
go_image_type_for_format(char const * format)708 go_image_type_for_format (char const *format)
709 {
710 g_return_val_if_fail (format && *format, 0);
711 if (!strcmp (format, "svg"))
712 return GO_TYPE_SVG;
713 if (!strcmp (format, "emf") || !strcmp (format, "wmf"))
714 return GO_TYPE_EMF;
715 if (!strcmp (format, "eps"))
716 return GO_TYPE_SPECTRE;
717 if (go_image_get_format_from_name (format) != GO_IMAGE_FORMAT_UNKNOWN)
718 return GO_TYPE_PIXBUF;
719 return 0;
720 }
721
722 guint8 const *
go_image_get_data(GOImage * image,gsize * length)723 go_image_get_data (GOImage *image, gsize *length)
724 {
725 g_return_val_if_fail (image, NULL);
726 if (length)
727 *length = image->data_length;
728 return image->data;
729 }
730
731 void
go_image_set_name(GOImage * image,char const * name)732 go_image_set_name (GOImage *image, char const *name)
733 {
734 g_return_if_fail (GO_IS_IMAGE (image));
735 g_free (image->name);
736 image->name = (name)? g_strdup (name): NULL;
737 }
738
739 char const *
go_image_get_name(GOImage const * image)740 go_image_get_name (GOImage const *image)
741 {
742 g_return_val_if_fail (GO_IS_IMAGE (image), NULL);
743 return image->name;
744 }
745
746 gboolean
go_image_differ(GOImage * first,GOImage * second)747 go_image_differ (GOImage *first, GOImage *second)
748 {
749 g_return_val_if_fail (GO_IS_IMAGE (first), FALSE);
750 g_return_val_if_fail (GO_IS_IMAGE (second), FALSE);
751 if (G_OBJECT_TYPE (first) != G_OBJECT_TYPE (second))
752 return TRUE;
753 if (first->width != second->width || first->height != second->height)
754 return TRUE;
755 return ((GOImageClass *) G_OBJECT_GET_CLASS (first))->differ (first, second);
756 }
757
758 void
go_image_save(GOImage * image,GsfXMLOut * output)759 go_image_save (GOImage *image, GsfXMLOut *output)
760 {
761 g_return_if_fail (GO_IS_IMAGE (image) && image->name);
762 gsf_xml_out_start_element (output, "GOImage");
763 gsf_xml_out_add_cstr (output, "name", image->name);
764 gsf_xml_out_add_cstr (output, "type", G_OBJECT_TYPE_NAME (image));
765 gsf_xml_out_add_int (output, "width", image->width);
766 gsf_xml_out_add_int (output, "height", image->height);
767 ((GOImageClass *) G_OBJECT_GET_CLASS (image))->save (image, output);
768 gsf_xml_out_end_element (output);
769 }
770
771 void
go_image_load_attrs(GOImage * image,GsfXMLIn * xin,xmlChar const ** attrs)772 go_image_load_attrs (GOImage *image, GsfXMLIn *xin, xmlChar const **attrs)
773 {
774 xmlChar const **attr;
775 g_return_if_fail (image != NULL);
776 for (attr = attrs; attr != NULL && attr[0] && attr[1] ; attr += 2)
777 if (0 == strcmp (attr[0], "width"))
778 image->width = strtol (attr[1], NULL, 10);
779 else if (0 == strcmp (attr[0], "height"))
780 image->height= strtol (attr[1], NULL, 10);
781 else
782 ((GOImageClass *) G_OBJECT_GET_CLASS (image))->load_attr (image, attr[0], attr[1]);
783 }
784
785 void
go_image_load_data(GOImage * image,GsfXMLIn * xin)786 go_image_load_data (GOImage *image, GsfXMLIn *xin)
787 {
788 ((GOImageClass *) G_OBJECT_GET_CLASS (image))->load_data (image, xin);
789 }
790
791 void
_go_image_changed(GOImage * image,double width,double height)792 _go_image_changed (GOImage *image, double width, double height)
793 {
794 image->width = width;
795 image->height = height;
796 if (image->thumbnail) {
797 g_object_unref (image->thumbnail);
798 image->thumbnail = NULL;
799 }
800 }
801
802 double
go_image_get_width(GOImage const * image)803 go_image_get_width (GOImage const *image)
804 {
805 g_return_val_if_fail (GO_IS_IMAGE (image), 0.);
806 return image->width;
807 }
808
809 double
go_image_get_height(GOImage const * image)810 go_image_get_height (GOImage const *image)
811 {
812 g_return_val_if_fail (GO_IS_IMAGE (image), 0.);
813 return image->height;
814 }
815
816 static double _go_image_dpi_x = 96., _go_image_dpi_y = 96.;
817
818 void
go_image_set_default_dpi(double dpi_x,double dpi_y)819 go_image_set_default_dpi (double dpi_x, double dpi_y)
820 {
821 _go_image_dpi_x = dpi_x;
822 _go_image_dpi_y = dpi_y;
823 }
824
825 void
go_image_get_default_dpi(double * dpi_x,double * dpi_y)826 go_image_get_default_dpi (double *dpi_x, double *dpi_y)
827 {
828 *dpi_x = _go_image_dpi_x;
829 *dpi_y = _go_image_dpi_y;
830 }
831
832 GOImageFormatInfo const *
go_image_get_info(GOImage * image)833 go_image_get_info (GOImage *image)
834 {
835 if (GO_IS_PIXBUF (image)) {
836 GOImageFormat f;
837 char *typ;
838 g_object_get (image, "image-type", &typ, NULL);
839 f = go_image_get_format_from_name (typ);
840 g_free (typ);
841 return go_image_get_format_info (f);
842 }
843
844 /* Dubious */
845 if (GO_IS_EMF (image))
846 return go_image_get_format_info (GO_IMAGE_FORMAT_EMF);
847 if (GO_IS_SVG (image))
848 return go_image_get_format_info (GO_IMAGE_FORMAT_SVG);
849 if (GO_IS_SPECTRE (image))
850 return go_image_get_format_info (GO_IMAGE_FORMAT_EPS);
851 return NULL;
852 }
853