]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/gdk-pixbuf-io.c
Updated Norwegian translation. Updated some.
[~andy/gtk] / gdk-pixbuf / gdk-pixbuf-io.c
1 /* GdkPixbuf library - Main loading interface.
2  *
3  * Copyright (C) 1999 The Free Software Foundation
4  *
5  * Authors: Miguel de Icaza <miguel@gnu.org>
6  *          Federico Mena-Quintero <federico@gimp.org>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #include <config.h>
25 #include <string.h>
26 #include <glib.h>
27 #include <errno.h>
28 #include "gdk-pixbuf-private.h"
29 #include "gdk-pixbuf-io.h"
30
31 \f
32
33 static gboolean
34 pixbuf_check_png (guchar *buffer, int size)
35 {
36         if (size < 28)
37                 return FALSE;
38
39         if (buffer [0] != 0x89 ||
40             buffer [1] != 'P' ||
41             buffer [2] != 'N' ||
42             buffer [3] != 'G' ||
43             buffer [4] != 0x0d ||
44             buffer [5] != 0x0a ||
45             buffer [6] != 0x1a ||
46             buffer [7] != 0x0a)
47                 return FALSE;
48
49         return TRUE;
50 }
51
52 static gboolean
53 pixbuf_check_jpeg (guchar *buffer, int size)
54 {
55         if (size < 10)
56                 return FALSE;
57
58         if (buffer [0] != 0xff || buffer [1] != 0xd8)
59                 return FALSE;
60
61         return TRUE;
62 }
63
64 static gboolean
65 pixbuf_check_tiff (guchar *buffer, int size)
66 {
67         if (size < 10)
68                 return FALSE;
69
70         if (buffer [0] == 'M' &&
71             buffer [1] == 'M' &&
72             buffer [2] == 0   &&
73             buffer [3] == 0x2a)
74                 return TRUE;
75
76         if (buffer [0] == 'I' &&
77             buffer [1] == 'I' &&
78             buffer [2] == 0x2a &&
79             buffer [3] == 0)
80                 return TRUE;
81
82         return FALSE;
83 }
84
85 static gboolean
86 pixbuf_check_gif (guchar *buffer, int size)
87 {
88         if (size < 20)
89                 return FALSE;
90
91         if (strncmp (buffer, "GIF8", 4) == 0)
92                 return TRUE;
93
94         return FALSE;
95 }
96
97 static gboolean
98 pixbuf_check_xpm (guchar *buffer, int size)
99 {
100         if (size < 20)
101                 return FALSE;
102
103         if (strncmp (buffer, "/* XPM */", 9) == 0)
104                 return TRUE;
105
106         return FALSE;
107 }
108
109 static gboolean
110 pixbuf_check_pnm (guchar *buffer, int size)
111 {
112         if (size < 20)
113                 return FALSE;
114
115         if (buffer [0] == 'P') {
116                 if (buffer [1] == '1' ||
117                     buffer [1] == '2' ||
118                     buffer [1] == '3' ||
119                     buffer [1] == '4' ||
120                     buffer [1] == '5' ||
121                     buffer [1] == '6')
122                         return TRUE;
123         }
124         return FALSE;
125 }
126 static gboolean
127 pixbuf_check_sunras (guchar *buffer, int size)
128 {
129         if (size < 32)
130                 return FALSE;
131
132         if (buffer [0] != 0x59 ||
133             buffer [1] != 0xA6 ||
134             buffer [2] != 0x6A ||
135             buffer [3] != 0x95)
136                 return FALSE;
137
138         return TRUE;
139 }
140
141 static gboolean
142 pixbuf_check_ico (guchar *buffer, int size)
143 {
144         /* Note that this may cause false positives, but .ico's don't
145            have a magic number.*/
146         if (size < 6)
147                 return FALSE;
148         if (buffer [0] != 0x0 ||
149             buffer [1] != 0x0 ||
150             ((buffer [2] != 0x1)&&(buffer[2]!=0x2)) ||
151             buffer [3] != 0x0 ||
152             buffer [5] != 0x0 )
153                 return FALSE;
154
155         return TRUE;
156 }
157
158
159 static gboolean
160 pixbuf_check_bmp (guchar *buffer, int size)
161 {
162         if (size < 20)
163                 return FALSE;
164
165         if (buffer [0] != 'B' || buffer [1] != 'M')
166                 return FALSE;
167
168         return TRUE;
169 }
170
171 static gboolean
172 pixbuf_check_wbmp (guchar *buffer, int size)
173 {
174   if(size < 10) /* at least */
175     return FALSE;
176
177   if(buffer[0] == '\0' /* We only handle type 0 wbmp's for now */)
178     return TRUE;
179
180   return FALSE;
181 }
182
183
184 static gboolean
185 pixbuf_check_xbm (guchar *buffer, int size)
186 {
187         if (size < 20)
188                 return FALSE;
189
190         if (buffer [0] != '#'
191             || buffer [1] != 'd'
192             || buffer [2] != 'e'
193             || buffer [3] != 'f'
194             || buffer [4] != 'i'
195             || buffer [5] != 'n'
196             || buffer [6] != 'e'
197             || buffer [7] != ' ')
198                 return FALSE;
199
200         return TRUE;
201 }
202
203 static GdkPixbufModule file_formats [] = {
204         { "png",  pixbuf_check_png, NULL,  NULL, NULL, NULL, NULL, NULL, NULL, },
205         { "jpeg", pixbuf_check_jpeg, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
206         { "tiff", pixbuf_check_tiff, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
207         { "gif",  pixbuf_check_gif, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
208 #define XPM_FILE_FORMAT_INDEX 4
209         { "xpm",  pixbuf_check_xpm, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
210         { "pnm",  pixbuf_check_pnm, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
211         { "ras",  pixbuf_check_sunras, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
212         { "ico",  pixbuf_check_ico, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
213         { "bmp",  pixbuf_check_bmp, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
214         { "wbmp", pixbuf_check_wbmp, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
215         { "xbm",  pixbuf_check_xbm, NULL,  NULL, NULL, NULL, NULL, NULL, NULL },
216         { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
217 };
218
219 #ifdef USE_GMODULE 
220 static gboolean
221 pixbuf_module_symbol (GModule *module, const char *module_name, const char *symbol_name, gpointer *symbol)
222 {
223         char *full_symbol_name = g_strconcat ("gdk_pixbuf__", module_name, "_", symbol_name, NULL);
224         gboolean return_value;
225
226         return_value = g_module_symbol (module, full_symbol_name, symbol);
227         g_free (full_symbol_name);
228         
229         return return_value;
230 }
231
232 #ifdef G_OS_WIN32
233
234 static char *
235 get_libdir (void)
236 {
237   static char *libdir = NULL;
238
239   if (libdir == NULL)
240     libdir = g_win32_get_package_installation_subdirectory
241       (GETTEXT_PACKAGE,
242        g_strdup_printf ("gdk_pixbug-%d.%d.dll",
243                         GDK_PIXBUF_MAJOR, GDK_PIXBUF_MINOR),
244        "loaders");
245
246   return libdir;
247 }
248
249 #define PIXBUF_LIBDIR get_libdir ()
250
251 #endif
252
253 /* actually load the image handler - gdk_pixbuf_get_module only get a */
254 /* reference to the module to load, it doesn't actually load it       */
255 /* perhaps these actions should be combined in one function           */
256 gboolean
257 gdk_pixbuf_load_module (GdkPixbufModule *image_module,
258                         GError         **error)
259 {
260         char *module_name;
261         char *path;
262         GModule *module;
263         gpointer sym;
264         char *name;
265         gboolean retval;
266         char *dir;
267         
268         g_return_val_if_fail (image_module->module == NULL, FALSE);
269
270         name = image_module->module_name;
271         
272         module_name = g_strconcat ("pixbufloader-", name, NULL);
273
274         /* This would be an exploit in an suid binary using gdk-pixbuf,
275          * but see http://www.gtk.org/setuid.html or the BugTraq
276          * archives for why you should report this as a bug against
277          * setuid apps using this library rather than the library
278          * itself.
279          */
280         dir = g_getenv ("GDK_PIXBUF_MODULEDIR");
281
282         if (dir == NULL || *dir == '\0') {
283                 
284                 path = g_module_build_path (PIXBUF_LIBDIR, module_name);
285                 module = g_module_open (path, G_MODULE_BIND_LAZY);
286         } else {
287                 path = g_module_build_path (dir, module_name);
288                 module = g_module_open (path, G_MODULE_BIND_LAZY);                
289         }        
290
291         if (!module) {
292                 g_set_error (error,
293                              GDK_PIXBUF_ERROR,
294                              GDK_PIXBUF_ERROR_FAILED,
295                              _("Unable to load image-loading module: %s: %s"),
296                              path, g_module_error ());
297                 g_free (module_name);
298                 g_free (path);
299                 return FALSE;
300         }
301
302         g_free (module_name);
303
304         image_module->module = module;        
305         
306         if (pixbuf_module_symbol (module, name, "fill_vtable", &sym)) {
307                 ModuleFillVtableFunc func = sym;
308                 (* func) (image_module);
309                 retval = TRUE;
310         } else {
311                 g_set_error (error,
312                              GDK_PIXBUF_ERROR,
313                              GDK_PIXBUF_ERROR_FAILED,
314                              _("Image-loading module %s does not export the proper interface; perhaps it's from a different GTK version?"),
315                              path);
316                 retval = FALSE;
317         }
318
319         g_free (path);
320
321         return retval;
322 }
323 #else
324
325 #define mname(type,fn) gdk_pixbuf__ ## type ## _ ##fn
326 #define m_fill_vtable(type) extern void mname(type,fill_vtable) (GdkPixbufModule *module)
327
328 m_fill_vtable (png);
329 m_fill_vtable (bmp);
330 m_fill_vtable (wbmp);
331 m_fill_vtable (gif);
332 m_fill_vtable (ico);
333 m_fill_vtable (jpeg);
334 m_fill_vtable (pnm);
335 m_fill_vtable (ras);
336 m_fill_vtable (tiff);
337 m_fill_vtable (xpm);
338 m_fill_vtable (xbm);
339
340 gboolean
341 gdk_pixbuf_load_module (GdkPixbufModule *image_module,
342                         GError         **error)
343 {
344         ModuleFillVtableFunc fill_vtable = NULL;
345         image_module->module = (void *) 1;
346
347         if (FALSE) {
348                 /* Ugly hack so we can use else if unconditionally below ;-) */
349         }
350         
351 #ifdef INCLUDE_png      
352         else if (strcmp (image_module->module_name, "png") == 0){
353                 fill_vtable = mname (png, fill_vtable);
354         }
355 #endif
356
357 #ifdef INCLUDE_bmp      
358         else if (strcmp (image_module->module_name, "bmp") == 0){
359                 fill_vtable = mname (bmp, fill_vtable);
360         }
361 #endif
362
363 #ifdef INCLUDE_wbmp
364         else if (strcmp (image_module->module_name, "wbmp") == 0){
365                 fill_vtable = mname (wbmp, fill_vtable);
366         }
367 #endif
368
369 #ifdef INCLUDE_gif
370         else if (strcmp (image_module->module_name, "gif") == 0){
371                 fill_vtable = mname (gif, fill_vtable);
372         }
373 #endif
374
375 #ifdef INCLUDE_ico
376         else if (strcmp (image_module->module_name, "ico") == 0){
377                 fill_vtable = mname (ico, fill_vtable);
378         }
379 #endif
380
381 #ifdef INCLUDE_jpeg
382         else if (strcmp (image_module->module_name, "jpeg") == 0){
383                 fill_vtable = mname (jpeg, fill_vtable);
384         }
385 #endif
386
387 #ifdef INCLUDE_pnm
388         else if (strcmp (image_module->module_name, "pnm") == 0){
389                 fill_vtable = mname (pnm, fill_vtable);
390         }
391 #endif
392
393 #ifdef INCLUDE_ras
394         else if (strcmp (image_module->module_name, "ras") == 0){
395                 fill_vtable = mname (ras, fill_vtable);
396         }
397 #endif
398
399 #ifdef INCLUDE_tiff
400         else if (strcmp (image_module->module_name, "tiff") == 0){
401                 fill_vtable = mname (tiff, fill_vtable);
402         }
403 #endif
404
405 #ifdef INCLUDE_xpm
406         else if (strcmp (image_module->module_name, "xpm") == 0){
407                 fill_vtable = mname (xpm, fill_vtable);
408         }
409 #endif
410
411 #ifdef INCLUDE_xbm
412         else if (strcmp (image_module->module_name, "xbm") == 0){
413                 fill_vtable = mname (xbm, fill_vtable);
414         }
415 #endif
416
417         
418         if (fill_vtable) {
419                 (* fill_vtable) (image_module);
420                 return TRUE;
421         } else {
422                 g_set_error (error,
423                              GDK_PIXBUF_ERROR,
424                              GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
425                              _("Image type '%s' is not supported"),
426                              image_module->module_name);
427
428                 return FALSE;
429         }
430 }
431
432
433 #endif
434
435 \f
436
437 GdkPixbufModule *
438 gdk_pixbuf_get_named_module (const char *name,
439                              GError **error)
440 {
441         int i;
442
443         for (i = 0; file_formats [i].module_name; i++) {
444                 if (!strcmp(name, file_formats[i].module_name))
445                         return &(file_formats[i]);
446         }
447
448         g_set_error (error,
449                      GDK_PIXBUF_ERROR,
450                      GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
451                      _("Image type '%s' is not supported"),
452                      name);
453         
454         return NULL;
455 }
456
457 GdkPixbufModule *
458 gdk_pixbuf_get_module (guchar *buffer, guint size,
459                        const gchar *filename,
460                        GError **error)
461 {
462         int i;
463
464         for (i = 0; file_formats [i].module_name; i++) {
465                 if ((* file_formats [i].format_check) (buffer, size))
466                         return &(file_formats[i]);
467         }
468
469         if (filename)
470                 g_set_error (error,
471                              GDK_PIXBUF_ERROR,
472                              GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
473                              _("Couldn't recognize the image file format for file '%s'"),
474                              filename);        
475         else
476                 g_set_error (error,
477                              GDK_PIXBUF_ERROR,
478                              GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
479                              _("Unrecognized image file format"));
480
481         
482         return NULL;
483 }
484
485 /**
486  * gdk_pixbuf_new_from_file:
487  * @filename: Name of file to load.
488  * @error: Return location for an error
489  *
490  * Creates a new pixbuf by loading an image from a file.  The file format is
491  * detected automatically. If NULL is returned, then @error will be set.
492  * Possible errors are in the #GDK_PIXBUF_ERROR and #G_FILE_ERROR domains.
493  *
494  * Return value: A newly-created pixbuf with a reference count of 1, or NULL if
495  * any of several error conditions occurred:  the file could not be opened,
496  * there was no loader for the file's format, there was not enough memory to
497  * allocate the image buffer, or the image file contained invalid data.
498  **/
499 GdkPixbuf *
500 gdk_pixbuf_new_from_file (const char *filename,
501                           GError    **error)
502 {
503         GdkPixbuf *pixbuf;
504         int size;
505         FILE *f;
506         guchar buffer [128];
507         GdkPixbufModule *image_module;
508
509         g_return_val_if_fail (filename != NULL, NULL);
510
511         f = fopen (filename, "rb");
512         if (!f) {
513                 g_set_error (error,
514                              G_FILE_ERROR,
515                              g_file_error_from_errno (errno),
516                              _("Failed to open file '%s': %s"),
517                              filename, g_strerror (errno));
518                 return NULL;
519         }
520
521         size = fread (&buffer, 1, sizeof (buffer), f);
522         if (size == 0) {
523                 g_set_error (error,
524                              GDK_PIXBUF_ERROR,
525                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
526                              _("Image file '%s' contains no data"),
527                              filename);
528                 
529                 fclose (f);
530                 return NULL;
531         }
532
533         image_module = gdk_pixbuf_get_module (buffer, size, filename, error);
534         if (image_module == NULL) {
535                 fclose (f);
536                 return NULL;
537         }
538
539         if (image_module->module == NULL)
540                 if (!gdk_pixbuf_load_module (image_module, error)) {
541                         fclose (f);
542                         return NULL;
543                 }
544
545         if (image_module->load == NULL) {
546                 g_set_error (error,
547                              GDK_PIXBUF_ERROR,
548                              GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION,
549                              _("Don't know how to load the image in file '%s'"),
550                              filename);
551                 
552                 fclose (f);
553                 return NULL;
554         }
555
556         fseek (f, 0, SEEK_SET);
557         pixbuf = (* image_module->load) (f, error);
558         fclose (f);
559
560         if (pixbuf == NULL && error != NULL && *error == NULL) {
561                 /* I don't trust these crufty longjmp()'ing image libs
562                  * to maintain proper error invariants, and I don't
563                  * want user code to segfault as a result. We need to maintain
564                  * the invariant that error gets set if NULL is returned.
565                  */
566                 
567                 g_warning ("Bug! gdk-pixbuf loader '%s' didn't set an error on failure.", image_module->module_name);
568                 g_set_error (error,
569                              GDK_PIXBUF_ERROR,
570                              GDK_PIXBUF_ERROR_FAILED,
571                              _("Failed to load image '%s': reason not known, probably a corrupt image file"),
572                              filename);
573                 
574         } else if (error != NULL && *error != NULL) {
575
576           /* Add the filename to the error message */
577           GError *e = *error;
578           gchar *old;
579           
580           old = e->message;
581
582           e->message = g_strdup_printf (_("Failed to load image '%s': %s"),
583                                         filename, old);
584
585           g_free (old);
586         }
587                 
588         return pixbuf;
589 }
590
591 /**
592  * gdk_pixbuf_new_from_xpm_data:
593  * @data: Pointer to inline XPM data.
594  *
595  * Creates a new pixbuf by parsing XPM data in memory.  This data is commonly
596  * the result of including an XPM file into a program's C source.
597  *
598  * Return value: A newly-created pixbuf with a reference count of 1.
599  **/
600 GdkPixbuf *
601 gdk_pixbuf_new_from_xpm_data (const char **data)
602 {
603         GdkPixbuf *(* load_xpm_data) (const char **data);
604         GdkPixbuf *pixbuf;
605         GError *error = NULL;
606
607         if (file_formats[XPM_FILE_FORMAT_INDEX].module == NULL) {
608                 if (!gdk_pixbuf_load_module (&file_formats[XPM_FILE_FORMAT_INDEX], &error)) {
609                         g_warning ("Error loading XPM image loader: %s", error->message);
610                         g_error_free (error);
611                         return FALSE;
612                 }
613         }
614           
615         if (file_formats[XPM_FILE_FORMAT_INDEX].module == NULL) {
616                 g_warning ("Can't find gdk-pixbuf module for parsing inline XPM data");
617                 return NULL;
618         } else if (file_formats[XPM_FILE_FORMAT_INDEX].load_xpm_data == NULL) {
619                 g_warning ("gdk-pixbuf XPM module lacks XPM data capability");
620                 return NULL;
621         } else
622                 load_xpm_data = file_formats[XPM_FILE_FORMAT_INDEX].load_xpm_data;
623
624         pixbuf = (* load_xpm_data) (data);
625         return pixbuf;
626 }
627
628 static void
629 collect_save_options (va_list   opts,
630                       gchar  ***keys,
631                       gchar  ***vals)
632 {
633   gchar *key;
634   gchar *val;
635   gchar *next;
636   gint count;
637
638   count = 0;
639   *keys = NULL;
640   *vals = NULL;
641   
642   next = va_arg (opts, gchar*);
643   while (next)
644     {
645       key = next;
646       val = va_arg (opts, gchar*);
647
648       ++count;
649
650       /* woo, slow */
651       *keys = g_realloc (*keys, sizeof(gchar*) * (count + 1));
652       *vals = g_realloc (*vals, sizeof(gchar*) * (count + 1));
653       
654       (*keys)[count-1] = g_strdup (key);
655       (*vals)[count-1] = g_strdup (val);
656
657       (*keys)[count] = NULL;
658       (*vals)[count] = NULL;
659       
660       next = va_arg (opts, gchar*);
661     }
662 }
663
664 static gboolean
665 gdk_pixbuf_real_save (GdkPixbuf     *pixbuf, 
666                       FILE          *filehandle, 
667                       const char    *type, 
668                       gchar        **keys,
669                       gchar        **values,
670                       GError       **error)
671 {
672        GdkPixbufModule *image_module = NULL;       
673
674        image_module = gdk_pixbuf_get_named_module (type, error);
675
676        if (image_module == NULL)
677                return FALSE;
678        
679        if (image_module->module == NULL)
680                if (!gdk_pixbuf_load_module (image_module, error))
681                        return FALSE;
682
683        if (image_module->save == NULL) {
684                g_set_error (error,
685                             GDK_PIXBUF_ERROR,
686                             GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION,
687                             _("This build of gdk-pixbuf does not support saving the image format: %s"),
688                             type);
689                return FALSE;
690        }
691                
692        return (* image_module->save) (filehandle, pixbuf,
693                                       keys, values,
694                                       error);
695 }
696
697  
698 /**
699  * gdk_pixbuf_save:
700  * @pixbuf: pointer to GdkPixbuf.
701  * @filename: Name of file to save.
702  * @type: name of file format.
703  * @error: return location for error, or NULL
704  * @Varargs: list of key-value save options
705  *
706  * Saves pixbuf to a file in @type, which is currently "jpeg" or
707  * "png".  If @error is set, FALSE will be returned. Possible errors include those
708  * in the #GDK_PIXBUF_ERROR domain and those in the #G_FILE_ERROR domain.
709  *
710  * The variable argument list should be NULL-terminated; if not empty,
711  * it should contain pairs of strings that modify the save
712  * parameters. For example:
713  *
714  * <programlisting>
715  * gdk_pixbuf_save (pixbuf, handle, "jpeg", &error,
716  *                  "quality", "100", NULL);
717  * </programlisting>
718  *
719  * The only save parameter that currently exists is the "quality" field
720  * for JPEG images; its value should be in the range [0,100].
721  *
722  * Return value: whether an error was set
723  **/
724
725 gboolean
726 gdk_pixbuf_save (GdkPixbuf  *pixbuf, 
727                  const char *filename, 
728                  const char *type, 
729                  GError    **error,
730                  ...)
731 {
732         gchar **keys = NULL;
733         gchar **values = NULL;
734         va_list args;
735         gboolean result;
736         
737         va_start (args, error);
738         
739         collect_save_options (args, &keys, &values);
740         
741         va_end (args);
742
743         result = gdk_pixbuf_savev (pixbuf, filename, type,
744                                    keys, values,
745                                    error);
746
747         g_strfreev (keys);
748         g_strfreev (values);
749
750         return result;
751 }
752
753 /**
754  * gdk_pixbuf_savev:
755  * @pixbuf: pointer to GdkPixbuf.
756  * @filename: Name of file to save.
757  * @type: name of file format.
758  * @option_keys: name of options to set, NULL-terminated
759  * @option_values: values for named options
760  * @error: return location for error, or NULL
761  *
762  * Saves pixbuf to a file in @type, which is currently "jpeg" or "png".
763  * If @error is set, FALSE will be returned. See gdk_pixbuf_save () for more
764  * details.
765  *
766  * Return value: whether an error was set
767  **/
768
769 gboolean
770 gdk_pixbuf_savev (GdkPixbuf  *pixbuf, 
771                   const char *filename, 
772                   const char *type,
773                   char      **option_keys,
774                   char      **option_values,
775                   GError    **error)
776 {
777         FILE *f = NULL;
778         gboolean result;
779         
780        
781         g_return_val_if_fail (filename != NULL, FALSE);
782         g_return_val_if_fail (type != NULL, FALSE);
783        
784         f = fopen (filename, "wb");
785         
786         if (f == NULL) {
787                 g_set_error (error,
788                              G_FILE_ERROR,
789                              g_file_error_from_errno (errno),
790                              _("Failed to open '%s' for writing: %s"),
791                              filename, g_strerror (errno));
792                 return FALSE;
793         }
794
795        
796        result = gdk_pixbuf_real_save (pixbuf, f, type,
797                                       option_keys, option_values,
798                                       error);
799        
800        
801        if (!result) {
802                g_return_val_if_fail (error == NULL || *error != NULL, FALSE);
803                fclose (f);
804                return FALSE;
805        }
806
807        if (fclose (f) < 0) {
808                g_set_error (error,
809                             G_FILE_ERROR,
810                             g_file_error_from_errno (errno),
811                             _("Failed to close '%s' while writing image, all data may not have been saved: %s"),
812                             filename, g_strerror (errno));
813                return FALSE;
814        }
815        
816        return TRUE;
817 }