gdk-pixbuf-io.c 114 KB
Newer Older
1
/* GdkPixbuf library - Main loading interface.
Arturo Espinosa's avatar
Arturo Espinosa committed
2
 *
3
4
5
6
7
8
 * Copyright (C) 1999 The Free Software Foundation
 *
 * Authors: Miguel de Icaza <miguel@gnu.org>
 *          Federico Mena-Quintero <federico@gimp.org>
 *
 * This library is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
11
12
13
14
15
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * 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, see <http://www.gnu.org/licenses/>.
Arturo Espinosa's avatar
Arturo Espinosa committed
20
 */
21

22
#include "config.h"
23

24
25
#include <stdlib.h>
#include <stdio.h>
26
#include <string.h>
27
#include <errno.h>
28
29
30
31
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

32
33
34
#include <glib.h>
#include <gio/gio.h>

35
#include "gdk-pixbuf-private.h"
36
#include "gdk-pixbuf-loader.h"
37
#include "gdk-pixdata.h"
Arturo Espinosa's avatar
Arturo Espinosa committed
38

39
40
#include <glib/gstdio.h>

41
42
43
44
45
#ifdef G_OS_WIN32
#define STRICT
#include <windows.h>
#undef STRICT
#endif
46
47
48
#ifdef OS_DARWIN
#include <mach-o/dyld.h>
#endif
49

50
/**
51
52
53
54
 * GdkPixbufModule:
 * @module_name: the name of the module, usually the same as the
 *  usual file extension for images of this type, eg. "xpm", "jpeg" or "png".
 * @module_path: the path from which the module is loaded.
55
56
 * @module: the loaded `GModule`.
 * @info: a `GdkPixbufFormat` holding information about the module.
57
58
59
60
61
62
 * @load: loads an image from a file.
 * @load_xpm_data: loads an image from data in memory.
 * @begin_load: begins an incremental load.
 * @stop_load: stops an incremental load.
 * @load_increment: continues an incremental load.
 * @load_animation: loads an animation from a file.
63
64
 * @save: saves a `GdkPixbuf` to a file.
 * @save_to_callback: saves a `GdkPixbuf` by calling the given `GdkPixbufSaveFunc`.
65
 * @is_save_option_supported: returns whether a save option key is supported by the module
66
 * 
67
68
 * A `GdkPixbufModule` contains the necessary functions to load and save
 * images in a certain file format.
69
 * 
70
71
72
73
74
 * If `GdkPixbuf` has been compiled with `GModule` support, it can be extended
 * by modules which can load (and perhaps also save) new image and animation
 * formats.
 *
 * ## Implementing modules
75
 * 
76
77
78
79
80
81
82
83
84
 * The `GdkPixbuf` interfaces needed for implementing modules are contained in
 * `gdk-pixbuf-io.h` (and `gdk-pixbuf-animation.h` if the module supports
 * animations). They are not covered by the same stability guarantees as the
 * regular GdkPixbuf API. To underline this fact, they are protected by the
 * `GDK_PIXBUF_ENABLE_BACKEND` pre-processor symbol.
 *
 * Each loadable module must contain a `GdkPixbufModuleFillVtableFunc` function
 * named `fill_vtable`, which will get called when the module
 * is loaded and must set the function pointers of the `GdkPixbufModule`.
85
86
 * 
 * In order to make format-checking work before actually loading the modules
87
88
89
 * (which may require calling `dlopen` to load image libraries), modules export
 * their signatures (and other information) via the `fill_info` function. An
 * external utility, `gdk-pixbuf-query-loaders`, uses this to create a text
90
 * file containing a list of all available loaders and  their signatures.
91
 * This file is then read at runtime by `GdkPixbuf` to obtain the list of
92
 * available loaders and their signatures. 
93
94
 * 
 * Modules may only implement a subset of the functionality available via
95
 * `GdkPixbufModule`. If a particular functionality is not implemented, the
96
 * `fill_vtable` function will simply not set the corresponding
97
98
99
100
101
102
 * function pointers of the `GdkPixbufModule` structure. If a module supports
 * incremental loading (i.e. provides `begin_load`, `stop_load` and
 * `load_increment`), it doesn't have to implement `load`, since `GdkPixbuf`
 * can supply a generic `load` implementation wrapping the incremental loading.
 *
 * ## Installing modules
103
104
 * 
 * Installing a module is a two-step process:
105
106
107
108
109
110
111
 *
 *  - copy the module file(s) to the loader directory (normally
 *    `$libdir/gdk-pixbuf-2.0/$version/loaders`, unless overridden by the
 *    environment variable `GDK_PIXBUF_MODULEDIR`)
 *  - call `gdk-pixbuf-query-loaders` to update the module file (normally
 *    `$libdir/gdk-pixbuf-2.0/$version/loaders.cache`, unless overridden
 *    by the environment variable `GDK_PIXBUF_MODULE_FILE`)
112
113
 */

114
115
static gint 
format_check (GdkPixbufModule *module, guchar *buffer, int size)
116
{
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
        int i, j;
        gchar m;
        GdkPixbufModulePattern *pattern;
        gboolean anchored;
        guchar *prefix;
        gchar *mask;

        for (pattern = module->info->signature; pattern->prefix; pattern++) {
                if (pattern->mask && pattern->mask[0] == '*') {
                        prefix = (guchar *)pattern->prefix + 1;
                        mask = pattern->mask + 1;
                        anchored = FALSE;
                }
                else {
                        prefix = (guchar *)pattern->prefix;
                        mask = pattern->mask;
                        anchored = TRUE;
                }
                for (i = 0; i < size; i++) {
                        for (j = 0; i + j < size && prefix[j] != 0; j++) {
                                m = mask ? mask[j] : ' ';
                                if (m == ' ') {
                                        if (buffer[i + j] != prefix[j])
                                                break;
                                }
                                else if (m == '!') {
                                        if (buffer[i + j] == prefix[j])
                                                break;
                                }
                                else if (m == 'z') {
                                        if (buffer[i + j] != 0)
                                                break;
                                }
                                else if (m == 'n') {
                                        if (buffer[i + j] == 0)
                                                break;
                                }
                        } 

                        if (prefix[j] == 0) 
                                return pattern->relevance;

                        if (anchored)
                                break;
                }
        }
        return 0;
164
165
}

166
167
G_LOCK_DEFINE_STATIC (init_lock);

168
static gboolean file_formats_inited;
169
static GSList *file_formats = NULL;
170

171
static gboolean gdk_pixbuf_io_init (void);
172

173
static GSList *
Matthias Clasen's avatar
Matthias Clasen committed
174
get_file_formats (void)
175
{
176
        G_LOCK (init_lock);
177
178
179
        if (file_formats == NULL ||
            !file_formats_inited)
                file_formats_inited = gdk_pixbuf_io_init ();
180
181
182
        G_UNLOCK (init_lock);
        
        return file_formats;
183
184
}

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#ifdef G_OS_WIN32

/* DllMain function needed to tuck away the gdk-pixbuf DLL handle */

static HMODULE gdk_pixbuf_dll;

BOOL WINAPI
DllMain (HINSTANCE hinstDLL,
         DWORD     fdwReason,
         LPVOID    lpvReserved)
{
        switch (fdwReason) {
        case DLL_PROCESS_ATTACH:
                gdk_pixbuf_dll = (HMODULE) hinstDLL;
                break;
        }

  return TRUE;
}
#endif

206

207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#ifdef GDK_PIXBUF_RELOCATABLE

gchar *
gdk_pixbuf_get_toplevel (void)
{
  static gchar *toplevel = NULL;

  if (toplevel == NULL) {
#if defined(G_OS_WIN32)
    toplevel = g_win32_get_package_installation_directory_of_module (gdk_pixbuf_dll);
#elif defined(OS_DARWIN)
    char pathbuf[PATH_MAX + 1];
    uint32_t  bufsize = sizeof(pathbuf);
    gchar *bin_dir;

    _NSGetExecutablePath(pathbuf, &bufsize);
    bin_dir = g_dirname(pathbuf);
    toplevel = g_build_path (G_DIR_SEPARATOR_S, bin_dir, "..", NULL);
    g_free (bin_dir);
#elif defined (OS_LINUX)
    gchar *exe_path, *bin_dir;

    exe_path = g_file_read_link ("/proc/self/exe", NULL);
    bin_dir = g_dirname(exe_path);
    toplevel = g_build_path (G_DIR_SEPARATOR_S, bin_dir, "..", NULL);
    g_free (exe_path);
    g_free (bin_dir);
#else
#error "Relocations not supported for this platform"
#endif
  }
  return toplevel;
}

#endif  /* GDK_PIXBUF_RELOCATABLE */


244
#ifdef USE_GMODULE 
245
246

static gboolean
247
scan_string (const char **pos, GString *out)
248
{
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
        const char *p = *pos, *q = *pos;
        char *tmp, *tmp2;
        gboolean quoted;
        
        while (g_ascii_isspace (*p))
                p++;
        
        if (!*p)
                return FALSE;
        else if (*p == '"') {
                p++;
                quoted = FALSE;
                for (q = p; (*q != '"') || quoted; q++) {
                        if (!*q)
                                return FALSE;
                        quoted = (*q == '\\') && !quoted;
                }
                
                tmp = g_strndup (p, q - p);
                tmp2 = g_strcompress (tmp);
                g_string_truncate (out, 0);
                g_string_append (out, tmp2);
                g_free (tmp);
                g_free (tmp2);
        }
        
        q++;
        *pos = q;
        
        return TRUE;
279
280
}

281
static gboolean
282
scan_int (const char **pos, int *out)
283
{
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
        int i = 0;
        char buf[32];
        const char *p = *pos;
        
        while (g_ascii_isspace (*p))
                p++;
        
        if (*p < '0' || *p > '9')
                return FALSE;
        
        while ((*p >= '0') && (*p <= '9') && i < sizeof (buf)) {
                buf[i] = *p;
                i++;
                p++;
        }
        
        if (i == sizeof (buf))
                return FALSE;
        else
                buf[i] = '\0';
        
        *out = atoi (buf);
        
        *pos = p;

        return TRUE;
310
311
}

312
static gboolean
313
skip_space (const char **pos)
314
{
315
316
317
318
        const char *p = *pos;
        
        while (g_ascii_isspace (*p))
                p++;
319
  
320
321
322
        *pos = p;
        
        return !(*p == '\0');
323
}
324
325
326

#ifdef GDK_PIXBUF_RELOCATABLE

327
static char *
328
get_libdir (void)
329
{
330
  static char *libdir = NULL;
331

332
  if (libdir == NULL)
333
          libdir = g_build_filename (gdk_pixbuf_get_toplevel (), "lib", NULL);
334

335
  return libdir;
336
337
}

338
339
#undef GDK_PIXBUF_LIBDIR
#define GDK_PIXBUF_LIBDIR get_libdir()
340

341
342
343
344
345
346
#endif  /* GDK_PIXBUF_RELOCATABLE */

/* In case we have a relative module path in the loaders cache
 * prepend the toplevel dir */
static gchar *
build_module_path (const gchar *path)
347
{
348
349
350
351
352
#ifdef GDK_PIXBUF_RELOCATABLE
        if (g_path_is_absolute (path)) {
                return g_strdup (path);
        } else {
                return g_build_filename (gdk_pixbuf_get_toplevel (), path, NULL);
353
        }
354
355
356
#else
        return g_strdup (path);
#endif
357
358
}

359
360
static gchar *
gdk_pixbuf_get_module_file (void)
361
{
362
  gchar *result = g_strdup (g_getenv ("GDK_PIXBUF_MODULE_FILE"));
363

364
  if (!result)
Matthias Clasen's avatar
Matthias Clasen committed
365
          result = g_build_filename (GDK_PIXBUF_LIBDIR, "gdk-pixbuf-2.0", GDK_PIXBUF_BINARY_VERSION, "loaders.cache", NULL);
366

367
  return result;
368
369
}

370
#endif  /* USE_GMODULE */
371

372
373
374

static gboolean
gdk_pixbuf_load_module_unlocked (GdkPixbufModule *image_module,
375
                                 GError         **error);
376

377
378
379
static gboolean
gdk_pixbuf_io_init_modules (const char  *filename,
                            GError     **error)
380
{
381
#ifdef USE_GMODULE
382
383
384
385
386
387
        GIOChannel *channel;
        gchar *line_buf;
        gsize term;
        GString *tmp_buf = g_string_new (NULL);
        gboolean have_error = FALSE;
        GdkPixbufModule *module = NULL;
388
        int flags = 0;
389
390
        int n_patterns = 0;
        GdkPixbufModulePattern *pattern;
391
        GError *local_error = NULL;
392
        guint num_formats;
Emmanuele Bassi's avatar
Emmanuele Bassi committed
393

394
        channel = g_io_channel_new_file (filename, "r",  &local_error);
395
        if (!channel) {
396
                char *filename_utf8 = g_filename_display_name (filename);
397
398
399
400
401
402
403
404
                g_set_error (error,
                             G_IO_ERROR,
                             G_IO_ERROR_INVALID_ARGUMENT,
                             "Cannot open pixbuf loader module file '%s': %s\n\n"
                             "This likely means that your installation is broken.\n"
                             "Try running the command\n"
                             "  gdk-pixbuf-query-loaders > %s\n"
                             "to make things work again for the time being.",
405
                             filename_utf8, local_error->message, filename_utf8);
406
                g_clear_error (&local_error);
407
                g_string_free (tmp_buf, TRUE);
408
                g_free (filename_utf8);
409
                return FALSE;
410
        }
411
412

        num_formats = g_slist_length (file_formats);
413
414
415
416
417
418
419
420
421
        
        while (!have_error && g_io_channel_read_line (channel, &line_buf, NULL, &term, NULL) == G_IO_STATUS_NORMAL) {
                const char *p;
                
                p = line_buf;

                line_buf[term] = 0;

                if (!skip_space (&p)) {
422
                        /* Blank line marking the end of a module */
423
424
425
426
427
428
429
430
431
432
433
434
                        if (module && *p != '#') {
                                file_formats = g_slist_prepend (file_formats, module);
                                module = NULL;
                        }
                        
                        goto next_line;
                }

                if (*p == '#') 
                        goto next_line;
                
                if (!module) {
435
                        /* Read a module location */
436
437
438
439
440
441
442
443
                        module = g_new0 (GdkPixbufModule, 1);
                        n_patterns = 0;
                        
                        if (!scan_string (&p, tmp_buf)) {
                                g_warning ("Error parsing loader info in '%s'\n  %s", 
                                           filename, line_buf);
                                have_error = TRUE;
                        }
444
                        module->module_path = build_module_path (tmp_buf->str);
445
446
447
448
449
450
451
452
453
454
455
                }
                else if (!module->module_name) {
                        module->info = g_new0 (GdkPixbufFormat, 1);
                        if (!scan_string (&p, tmp_buf)) {
                                g_warning ("Error parsing loader info in '%s'\n  %s", 
                                           filename, line_buf);
                                have_error = TRUE;
                        }
                        module->info->name =  g_strdup (tmp_buf->str);
                        module->module_name = module->info->name;

456
                        flags = 0;
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
                        if (!scan_int (&p, &flags)) {
                                g_warning ("Error parsing loader info in '%s'\n  %s", 
                                           filename, line_buf);
                                have_error = TRUE;
                        }
                        module->info->flags = flags;
                        
                        if (!scan_string (&p, tmp_buf)) {
                                g_warning ("Error parsing loader info in '%s'\n  %s", 
                                           filename, line_buf);
                                have_error = TRUE;
                        }                       
                        if (tmp_buf->str[0] != 0)
                                module->info->domain = g_strdup (tmp_buf->str);

                        if (!scan_string (&p, tmp_buf)) {
                                g_warning ("Error parsing loader info in '%s'\n  %s", 
                                           filename, line_buf);
                                have_error = TRUE;
                        }                       
                        module->info->description = g_strdup (tmp_buf->str);

                        if (scan_string (&p, tmp_buf)) {
                                module->info->license = g_strdup (tmp_buf->str);
                        }
                }
                else if (!module->info->mime_types) {
                        int n = 1;
                        module->info->mime_types = g_new0 (gchar*, 1);
                        while (scan_string (&p, tmp_buf)) {
                                if (tmp_buf->str[0] != 0) {
                                        module->info->mime_types =
                                                g_realloc (module->info->mime_types, (n + 1) * sizeof (gchar*));
                                        module->info->mime_types[n - 1] = g_strdup (tmp_buf->str);
                                        module->info->mime_types[n] = NULL;
                                        n++;
                                }
                        }
                }
                else if (!module->info->extensions) {
                        int n = 1;
                        module->info->extensions = g_new0 (gchar*, 1);
                        while (scan_string (&p, tmp_buf)) {
                                if (tmp_buf->str[0] != 0) {
                                        module->info->extensions =
                                                g_realloc (module->info->extensions, (n + 1) * sizeof (gchar*));
                                        module->info->extensions[n - 1] = g_strdup (tmp_buf->str);
                                        module->info->extensions[n] = NULL;
                                        n++;
                                }
                        }
                }
                else {
                        n_patterns++;
                        module->info->signature = (GdkPixbufModulePattern *)
                                g_realloc (module->info->signature, (n_patterns + 1) * sizeof (GdkPixbufModulePattern));
                        pattern = module->info->signature + n_patterns;
                        pattern->prefix = NULL;
                        pattern->mask = NULL;
                        pattern->relevance = 0;
                        pattern--;
                        if (!scan_string (&p, tmp_buf))
                                goto context_error;
                        pattern->prefix = g_strdup (tmp_buf->str);
                        
                        if (!scan_string (&p, tmp_buf))
                                goto context_error;
                        if (*tmp_buf->str)
                                pattern->mask = g_strdup (tmp_buf->str);
                        else
                                pattern->mask = NULL;
                        
                        if (!scan_int (&p, &pattern->relevance))
                                goto context_error;
                        
                        goto next_line;

                context_error:
                        g_free (pattern->prefix);
                        g_free (pattern->mask);
                        g_free (pattern);
                        g_warning ("Error parsing loader info in '%s'\n  %s", 
                                   filename, line_buf);
                        have_error = TRUE;
                }
        next_line:
                g_free (line_buf);
        }
        g_string_free (tmp_buf, TRUE);
        g_io_channel_unref (channel);
547
548

        if (g_slist_length (file_formats) <= num_formats) {
549
                char *filename_utf8 = g_filename_display_name (filename);
550
551
552
553
                g_set_error (error,
                             G_IO_ERROR,
                             G_IO_ERROR_NOT_INITIALIZED,
                             "No new GdkPixbufModule loaded from '%s'",
554
555
                             filename_utf8);
                g_free (filename_utf8);
556
557
                return FALSE;
        }
558
#endif
559
        return TRUE;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
560
}
561

562
563
/**
 * gdk_pixbuf_init_modules:
564
565
 * @path: Path to directory where the `loaders.cache` is installed
 * @error: return location for a `GError`
566
 *
567
 * Initalizes the gdk-pixbuf loader modules referenced by the `loaders.cache`
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
 * file present inside that directory.
 *
 * This is to be used by applications that want to ship certain loaders
 * in a different location from the system ones.
 *
 * This is needed when the OS or runtime ships a minimal number of loaders
 * so as to reduce the potential attack surface of carefully crafted image
 * files, especially for uncommon file types. Applications that require
 * broader image file types coverage, such as image viewers, would be
 * expected to ship the gdk-pixbuf modules in a separate location, bundled
 * with the application in a separate directory from the OS or runtime-
 * provided modules.
 *
 * Since: 2.40
 */
gboolean
gdk_pixbuf_init_modules (const char  *path,
			 GError     **error)
{
	char *filename;
	gboolean ret;

	g_return_val_if_fail (path != NULL, FALSE);
	filename = g_build_filename (path, "loaders.cache", NULL);
	ret = gdk_pixbuf_io_init_modules (filename, error);
	g_free (filename);
	return ret;
}

597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
static void
gdk_pixbuf_io_init_builtin (void)
{
#define load_one_builtin_module(format)                                 G_STMT_START { \
        GdkPixbufModule *__builtin_module = g_new0 (GdkPixbufModule, 1);               \
        __builtin_module->module_name = #format;                                       \
        if (gdk_pixbuf_load_module_unlocked (__builtin_module, NULL))                  \
                file_formats = g_slist_prepend (file_formats, __builtin_module);       \
        else                                                                           \
                g_free (__builtin_module);                              } G_STMT_END

#ifdef INCLUDE_ani
        load_one_builtin_module (ani);
#endif
#ifdef INCLUDE_png
        load_one_builtin_module (png);
#endif
#ifdef INCLUDE_bmp
        load_one_builtin_module (bmp);
#endif
#ifdef INCLUDE_gif
        load_one_builtin_module (gif);
#endif
#ifdef INCLUDE_ico
        load_one_builtin_module (ico);
#endif
#ifdef INCLUDE_jpeg
        load_one_builtin_module (jpeg);
#endif
#ifdef INCLUDE_pnm
        load_one_builtin_module (pnm);
#endif
#ifdef INCLUDE_tiff
        load_one_builtin_module (tiff);
#endif
#ifdef INCLUDE_xpm
        load_one_builtin_module (xpm);
#endif
#ifdef INCLUDE_xbm
        load_one_builtin_module (xbm);
#endif
#ifdef INCLUDE_tga
        load_one_builtin_module (tga);
#endif
#ifdef INCLUDE_icns
        load_one_builtin_module (icns);
#endif
#ifdef INCLUDE_qtif
        load_one_builtin_module (qtif);
#endif
#ifdef INCLUDE_gdiplus
        /* We don't bother having the GDI+ loaders individually selectable
         * for building in or not.
         */
        load_one_builtin_module (ico);
        load_one_builtin_module (wmf);
        load_one_builtin_module (emf);
        load_one_builtin_module (bmp);
        load_one_builtin_module (gif);
        load_one_builtin_module (jpeg);
        load_one_builtin_module (tiff);
#endif
#ifdef INCLUDE_gdip_png
        /* Except the gdip-png loader which normally isn't built at all even */
        load_one_builtin_module (png);
#endif

#undef load_one_builtin_module
}

667
static gboolean
668
669
gdk_pixbuf_io_init (void)
{
670
	char *module_file;
671
	gboolean ret;
672

673
	gdk_pixbuf_io_init_builtin ();
674
#ifdef USE_GMODULE
675
	module_file = gdk_pixbuf_get_module_file ();
676
#endif
677
	ret = gdk_pixbuf_io_init_modules (module_file, NULL);
678
	g_free (module_file);
679
	return ret;
680
}
Federico Mena Quintero's avatar
Federico Mena Quintero committed
681

682
#define module(type) \
683
684
  extern void _gdk_pixbuf__##type##_fill_info   (GdkPixbufFormat *info);   \
  extern void _gdk_pixbuf__##type##_fill_vtable (GdkPixbufModule *module)
685
686

module (png);
687
module (jpeg);
688
689
690
691
module (gif);
module (ico);
module (ani);
module (xpm);
692
693
694
module (tiff);
module (pnm);
module (bmp);
695
696
module (xbm);
module (tga);
697
module (icns);
Kevin Peng's avatar
Kevin Peng committed
698
module (qtif);
699
700
701
702
703
704
705
706
module (gdip_ico);
module (gdip_wmf);
module (gdip_emf);
module (gdip_bmp);
module (gdip_gif);
module (gdip_jpeg);
module (gdip_png);
module (gdip_tiff);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
707

708
709
#undef module

710
711
712
713
714
/* actually load the image handler - gdk_pixbuf_get_module only get a */
/* reference to the module to load, it doesn't actually load it       */
/* perhaps these actions should be combined in one function           */
static gboolean
gdk_pixbuf_load_module_unlocked (GdkPixbufModule *image_module,
715
                                 GError         **error)
Federico Mena Quintero's avatar
Federico Mena Quintero committed
716
{
717
        GdkPixbufModuleFillInfoFunc fill_info = NULL;
718
        GdkPixbufModuleFillVtableFunc fill_vtable = NULL;
719
                
720
721
        if (image_module->module != NULL)
               return TRUE;
722

723
724
725
726
727
728
#define try_module(format,id)                                           \
        if (fill_info == NULL &&                                        \
            strcmp (image_module->module_name, #format) == 0) {         \
                fill_info = _gdk_pixbuf__##id##_fill_info;              \
                fill_vtable = _gdk_pixbuf__##id##_fill_vtable;  \
        }
729

730
731
732
733
734
735
736
737
738
739
740
741
#ifdef INCLUDE_gdiplus
        try_module (ico,gdip_ico);
        try_module (wmf,gdip_wmf);
        try_module (emf,gdip_emf);
        try_module (bmp,gdip_bmp);
        try_module (gif,gdip_gif);
        try_module (jpeg,gdip_jpeg);
        try_module (tiff,gdip_tiff);
#endif
#ifdef INCLUDE_gdip_png
        try_module (png,gdip_png);
#endif
742
#ifdef INCLUDE_png
743
        try_module (png,png);
744
#endif
745
#ifdef INCLUDE_bmp
746
        try_module (bmp,bmp);
747
748
#endif
#ifdef INCLUDE_gif
749
        try_module (gif,gif);
750
751
#endif
#ifdef INCLUDE_ico
752
        try_module (ico,ico);
753
#endif
754
#ifdef INCLUDE_ani
755
        try_module (ani,ani);
756
#endif
757
#ifdef INCLUDE_jpeg
758
        try_module (jpeg,jpeg);
759
760
#endif
#ifdef INCLUDE_pnm
761
        try_module (pnm,pnm);
762
763
#endif
#ifdef INCLUDE_tiff
764
        try_module (tiff,tiff);
765
#endif
766
#ifdef INCLUDE_xpm
767
        try_module (xpm,xpm);
768
#endif
769
#ifdef INCLUDE_xbm
770
        try_module (xbm,xbm);
771
#endif
772
#ifdef INCLUDE_tga
773
        try_module (tga,tga);
774
#endif
775
#ifdef INCLUDE_icns
776
        try_module (icns,icns);
777
#endif
Kevin Peng's avatar
Kevin Peng committed
778
#ifdef INCLUDE_qtif
779
        try_module (qtif,qtif);
Kevin Peng's avatar
Kevin Peng committed
780
#endif
781
782

#undef try_module
783
        
784
        if (fill_vtable) {
785
                image_module->module = (void *) 1;
786
                (* fill_vtable) (image_module);
787
788
789
790
                if (image_module->info == NULL) {
                        image_module->info = g_new0 (GdkPixbufFormat, 1);
                        (* fill_info) (image_module->info);
                }
791
                return TRUE;
792
793
        }
        else 
794
#ifdef USE_GMODULE
795
796
797
798
799
800
801
802
803
        {
                char *path;
                GModule *module;
                gpointer sym;

                path = image_module->module_path;
                module = g_module_open (path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);

                if (!module) {
804
                        char *path_utf8 = g_filename_display_name (path);
805
806
807
808
                        g_set_error (error,
                                     GDK_PIXBUF_ERROR,
                                     GDK_PIXBUF_ERROR_FAILED,
                                     _("Unable to load image-loading module: %s: %s"),
809
810
                                     path_utf8, g_module_error ());
                        g_free (path_utf8);
811
812
813
814
                        return FALSE;
                }

                image_module->module = module;        
815
        
816
817
818
819
820
                if (g_module_symbol (module, "fill_vtable", &sym)) {
                        fill_vtable = (GdkPixbufModuleFillVtableFunc) sym;
                        (* fill_vtable) (image_module);
                        return TRUE;
                } else {
821
                        char *path_utf8 = g_filename_display_name (path);
822
823
824
                        g_set_error (error,
                                     GDK_PIXBUF_ERROR,
                                     GDK_PIXBUF_ERROR_FAILED,
825
                                     _("Image-loading module %s does not export the proper interface; perhaps it’s from a different gdk-pixbuf version?"),
826
827
                                     path_utf8);
                        g_free (path_utf8);
828
829
830
                        return FALSE;
                }
        }
831
#else
832
833
834
        g_set_error (error,
                     GDK_PIXBUF_ERROR,
                     GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
835
                     _("Image type “%s” is not supported"),
836
837
                     image_module->module_name);
        return FALSE;
838
839
#endif  /* !USE_GMODULE */
}
840

841
842
843

gboolean
_gdk_pixbuf_load_module (GdkPixbufModule *image_module,
844
                         GError         **error)
845
{
846
        gboolean ret;
847

848
        G_LOCK (init_lock);
849

850
851
        ret = gdk_pixbuf_load_module_unlocked (image_module, error);

852
        G_UNLOCK (init_lock);
853

854
        return ret;
855
}
Arturo Espinosa's avatar
Arturo Espinosa committed
856

857
858


859
GdkPixbufModule *
860
861
_gdk_pixbuf_get_named_module (const char *name,
                              GError **error)
862
{
863
        GSList *modules;
864

865
866
        for (modules = get_file_formats (); modules; modules = g_slist_next (modules)) {
                GdkPixbufModule *module = (GdkPixbufModule *)modules->data;
867

868
869
                if (module->info->disabled)
                        continue;
870

871
872
873
                if (!strcmp (name, module->module_name))
                        return module;
        }
874

Havoc Pennington's avatar
Havoc Pennington committed
875
876
877
        g_set_error (error,
                     GDK_PIXBUF_ERROR,
                     GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
878
                     _("Image type “%s” is not supported"),
Havoc Pennington's avatar
Havoc Pennington committed
879
880
                     name);
        
881
        return NULL;
882
883
}

884
GdkPixbufModule *
885
886
887
_gdk_pixbuf_get_module (guchar *buffer, guint size,
                        const gchar *filename,
                        GError **error)
888
{
889
        GSList *modules;
890

891
892
        GdkPixbufModule *selected = NULL;
        gchar *display_name = NULL;
893
#ifdef GDK_PIXBUF_USE_GIO_MIME
894
895
896
897
898
899
900
        gchar *mime_type;
        gchar **mimes;
        gchar *type;
        gint j;
        gboolean uncertain;

        mime_type = g_content_type_guess (NULL, buffer, size, &uncertain);
901
        if ((uncertain || g_str_equal (mime_type, "text/plain") || g_str_equal (mime_type, "application/gzip")) && filename != NULL) {
William Jon McCann's avatar
William Jon McCann committed
902
                g_free (mime_type);
903
                mime_type = g_content_type_guess (filename, buffer, size, NULL);
William Jon McCann's avatar
William Jon McCann committed
904
        }
905
906
907
908
909
910
911
912
913
914
915

        for (modules = get_file_formats (); modules; modules = g_slist_next (modules)) {
                GdkPixbufModule *module = (GdkPixbufModule *)modules->data;
                GdkPixbufFormat *info = module->info;

                if (info->disabled)
                        continue;

                mimes = info->mime_types;
                for (j = 0; mimes[j] != NULL; j++) {
                        type = g_content_type_from_mime_type (mimes[j]);
916
                        if (g_content_type_equals (type, mime_type)) {
917
918
919
920
921
922
                                g_free (type);
                                selected = module;
                                break;
                        }
                        g_free (type);
                }
923

924
925
926
                if (selected != NULL)
                        break;

927
928
929
930
931
932
		/* Make sure the builtin GdkPixdata support works even without mime sniffing */
		if (strcmp (info->name, "GdkPixdata") == 0 &&
		    format_check (module, buffer, size) == 100) {
			selected = module;
			break;
		}
933
934
        }
        g_free (mime_type);
935
#else
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
        gint score, best = 0;

        for (modules = get_file_formats (); modules; modules = g_slist_next (modules)) {
                GdkPixbufModule *module = (GdkPixbufModule *)modules->data;

                if (module->info->disabled)
                        continue;

                score = format_check (module, buffer, size);
                if (score > best) {
                        best = score; 
                        selected = module;
                }
                if (score >= 100) 
                        break;
        }
952
953
#endif

954
955
        if (selected != NULL)
                return selected;
956

Havoc Pennington's avatar
Havoc Pennington committed
957
        if (filename)
958
959
960
961
962
        {
                display_name = g_filename_display_name (filename);
                g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
963
                             _("Couldn’t recognize the image file format for file “%s”"),
964
965
966
                             display_name);
                g_free (display_name);
        }
Havoc Pennington's avatar
Havoc Pennington committed
967
        else
968
969
970
971
                g_set_error_literal (error,
                                     GDK_PIXBUF_ERROR,
                                     GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
                                     _("Unrecognized image file format"));
Havoc Pennington's avatar
Havoc Pennington committed
972

973

974
        return NULL;
975
976
}

Matthias Clasen's avatar
Matthias Clasen committed
977
978
979
980
981
982
983
984
985
986
987
988
989
990
static
GdkPixbufModule *
_gdk_pixbuf_get_module_for_file (FILE *f, const gchar *filename, GError **error)
{
        guchar buffer[SNIFF_BUFFER_SIZE];
        int size;

        size = fread (&buffer, 1, sizeof (buffer), f);
        if (size == 0) {
		gchar *display_name;
        	display_name = g_filename_display_name (filename);      
                g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
991
                             _("Image file “%s” contains no data"),
Matthias Clasen's avatar
Matthias Clasen committed
992
993
994
995
996
997
998
999
                             display_name);
                g_free (display_name);
                return NULL;
        }

	return _gdk_pixbuf_get_module (buffer, size, filename, error);
}

1000
1001
1002
1003
1004
1005
1006
static void
noop_size_notify (gint     *width,
		  gint     *height,
		  gpointer  data)
{
}

1007
1008
static void
prepared_notify (GdkPixbuf *pixbuf, 
1009
1010
                 GdkPixbufAnimation *anim, 
                 gpointer user_data)
1011
{
1012
1013
1014
        if (pixbuf != NULL)
                g_object_ref (pixbuf);
        *((GdkPixbuf **)user_data) = pixbuf;
1015
1016
}

1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
static void
noop_updated_notify (GdkPixbuf *pixbuf,
		     int        x,
		     int        y,
		     int        width,
		     int        height,
		     gpointer   user_data)
{
}

Matthias Clasen's avatar
Matthias Clasen committed
1027
1028
1029
1030
1031
1032
static GdkPixbuf *
generic_load_incrementally (GdkPixbufModule *module, FILE *f, GError **error)
{
        GdkPixbuf *pixbuf = NULL;
	gpointer context;

1033
	context = module->begin_load (noop_size_notify, prepared_notify, noop_updated_notify, &pixbuf, error);
Matthias Clasen's avatar
Matthias Clasen committed
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
        
	if (!context)
		goto out;
                
	while (!feof (f) && !ferror (f)) {
		guchar buffer[LOAD_BUFFER_SIZE];
		size_t length;

		length = fread (buffer, 1, sizeof (buffer), f);
		if (length > 0) {
			if (!module->load_increment (context, buffer, length, error)) {
				module->stop_load (context, NULL);
				if (pixbuf != NULL) {
					g_object_unref (pixbuf);
					pixbuf = NULL;
				}
				goto out;
			}
		}
	}

	if (!module->stop_load (context, error)) {
		if (pixbuf != NULL) {
			g_object_unref (pixbuf);
			pixbuf = NULL;
		}
	}

out:
	return pixbuf;
}

1066
GdkPixbuf *
Matthias Clasen's avatar
Matthias Clasen committed
1067
_gdk_pixbuf_generic_image_load (GdkPixbufModule *module, FILE *f, GError **error)
1068
{
1069
1070
1071
1072
1073
        GdkPixbuf *pixbuf = NULL;

        if (module->load != NULL) {
                pixbuf = (* module->load) (f, error);
        } else if (module->begin_load != NULL) {
Matthias Clasen's avatar
Matthias Clasen committed
1074
        	pixbuf = generic_load_incrementally (module, f, error);
1075
        } else if (module->load_animation != NULL) {
Matthias Clasen's avatar
Matthias Clasen committed
1076
1077
		GdkPixbufAnimation *animation;

1078
1079
1080
1081
1082
1083
1084
1085
                animation = (* module->load_animation) (f, error);
                if (animation != NULL) {
                        pixbuf = gdk_pixbuf_animation_get_static_image (animation);

                        g_object_ref (pixbuf);
                        g_object_unref (animation);
                }
        }
1086

1087
        return pixbuf;
1088
1089
}

Federico Mena Quintero's avatar
Federico Mena Quintero committed
1090
/**
1091
 * gdk_pixbuf_new_from_file: (constructor)
1092
 * @filename: (type filename): Name of file to load, in the GLib file
1093
1094
 *   name encoding
 * @error: (out): Return location for an error
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1095
 *
1096
 * Creates a new pixbuf by loading an image from a file.
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1097
 *
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
 * The file format is detected automatically.
 *
 * If `NULL` is returned, then @error will be set. Possible errors are:
 *
 *  - the file could not be opened
 *  - there is no loader for the file's format
 *  - there is not enough memory to allocate the image buffer
 *  - the image buffer contains invalid data
 *
 * The error domains are `GDK_PIXBUF_ERROR` and `G_FILE_ERROR`.
 *
 * Return value: (transfer full) (nullable): A newly-created pixbuf
 */
1111
GdkPixbuf *
Havoc Pennington's avatar
Havoc Pennington committed
1112
1113
gdk_pixbuf_new_from_file (const char *filename,
                          GError    **error)
Arturo Espinosa's avatar
Arturo Espinosa committed
1114
{
1115
1116
1117
1118
1119
        GdkPixbuf *pixbuf;
        FILE *f;
        GdkPixbufModule *image_module;

        g_return_val_if_fail (filename != NULL, NULL);
1120
        g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1121
1122
1123
1124
        
        f = g_fopen (filename, "rb");
        if (!f) {
                gint save_errno = errno;
Matthias Clasen's avatar
Matthias Clasen committed
1125
1126
		gchar *display_name;
        	display_name = g_filename_display_name (filename);      
Havoc Pennington's avatar
Havoc Pennington committed
1127
1128
                g_set_error (error,
                             G_FILE_ERROR,
1129
                             g_file_error_from_errno (save_errno),
1130
                             _("Failed to open file “%s”: %s"),
1131
                             display_name,
1132
                             g_strerror (save_errno));
1133
                g_free (display_name);
1134
                return NULL;
Havoc Pennington's avatar
Havoc Pennington committed
1135
        }
1136

Matthias Clasen's avatar
Matthias Clasen committed
1137
        image_module = _gdk_pixbuf_get_module_for_file (f, filename, error);
Havoc Pennington's avatar
Havoc Pennington committed
1138
1139
1140
1141
        if (image_module == NULL) {
                fclose (f);
                return NULL;
        }
1142

1143
        if (!_gdk_pixbuf_load_module (image_module, error)) {
1144
1145
                fclose (f);
                return NULL;
1146
        }
1147

1148
1149
1150
        fseek (f, 0, SEEK_SET);
        pixbuf = _gdk_pixbuf_generic_image_load (image_module, f, error);
        fclose (f);
1151

Havoc Pennington's avatar
Havoc Pennington committed
1152
        if (pixbuf == NULL && error != NULL && *error == NULL) {
1153

Havoc Pennington's avatar
Havoc Pennington committed
1154
1155
1156
                /* I don't trust these crufty longjmp()'ing image libs
                 * to maintain proper error invariants, and I don't
                 * want user code to segfault as a result. We need to maintain
1157
                 * the invariant that error gets set if NULL is returned.
Havoc Pennington's avatar
Havoc Pennington committed
1158
                 */
1159

Matthias Clasen's avatar
Matthias Clasen committed
1160
1161
		gchar *display_name;
        	display_name = g_filename_display_name (filename);      
Havoc Pennington's avatar
Havoc Pennington committed
1162
1163
1164
1165
                g_warning ("Bug! gdk-pixbuf loader '%s' didn't set an error on failure.", image_module->module_name);
                g_set_error (error,
                             GDK_PIXBUF_ERROR,
                             GDK_PIXBUF_ERROR_FAILED,
1166
                             _("Failed to load image “%s”: reason not known, probably a corrupt image file"),
1167
                             display_name);
Matthias Clasen's avatar
Matthias Clasen committed
1168
		g_free (display_name);
Havoc Pennington's avatar
Havoc Pennington committed
1169
        } else if (error != NULL && *error != NULL) {
Matthias Clasen's avatar
Matthias Clasen committed
1170
1171
1172
1173
1174
1175
1176
		/* Add the filename to the error message */
		GError *e = *error;
		gchar  *old;
		gchar *display_name;

        	display_name = g_filename_display_name (filename);      
		old = e->message;
1177
		e->message = g_strdup_printf (_("Failed to load image “%s”: %s"),
Matthias Clasen's avatar
Matthias Clasen committed
1178
1179
1180
1181
					      display_name,
					      old);
		g_free (old);
		g_free (display_name);
Havoc Pennington's avatar
Havoc Pennington committed
1182
        }
1183

1184
        return pixbuf;
Arturo Espinosa's avatar
Arturo Espinosa committed
1185
}
1186

1187
1188
#ifdef G_OS_WIN32

1189
1190
/**
 * gdk_pixbuf_new_from_file_utf8:
1191
 * @filename: (type filename): Name of file to load, in the GLib file name encoding
1192
1193
1194
1195
 * @error: Return location for an error
 *
 * Same as gdk_pixbuf_new_from_file()
 *
1196
 * Return value: A newly-created pixbuf with a reference count of 1, or `NULL` if
1197
1198
1199
1200
 * any of several error conditions occurred:  the file could not be opened,
 * there was no loader for the file's format, there was not enough memory to
 * allocate the image buffer, or the image file contained invalid data.
 **/
1201
GdkPixbuf *
1202
1203
gdk_pixbuf_new_from_file_utf8 (const char *filename,
                                GError    **error)
1204
{
1205
    return gdk_pixbuf_new_from_file (filename, error);
1206
}
1207