1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2/* GdkPixbuf library - XPM image loader
3 *
4 * Copyright (C) 1999 Mark Crichton
5 * Copyright (C) 1999 The Free Software Foundation
6 *
7 * Authors: Mark Crichton <crichton@gimp.org>
8 * Federico Mena-Quintero <federico@gimp.org>
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
22 */
23
24#include "config.h"
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <glib.h>
29#ifdef HAVE_UNISTD_H
30#include <unistd.h> /* for unlink */
31#endif
32#include <errno.h>
33#include <glib/gstdio.h>
34#include <glib/gi18n-lib.h>
35#include "gdk-pixbuf-core.h"
36#include "gdk-pixbuf-io.h"
37
38
39
40
41/* I have must have done something to deserve this.
42 * XPM is such a crappy format to handle.
43 * This code is an ugly hybrid from gdkpixmap.c
44 * modified to respect transparent colors.
45 * It's still a mess, though.
46 */
47
48enum buf_op {
49 op_header,
50 op_cmap,
51 op_body
52};
53
54typedef struct {
55 gchar *color_string;
56 guint16 red;
57 guint16 green;
58 guint16 blue;
59 gint transparent;
60} XPMColor;
61
62struct file_handle {
63 FILE *infile;
64 gchar *buffer;
65 guint buffer_size;
66};
67
68struct mem_handle {
69 const gchar **data;
70 int offset;
71};
72
73/* The following 2 routines (parse_color, find_color) come from Tk, via the Win32
74 * port of GDK. The licensing terms on these (longer than the functions) is:
75 *
76 * This software is copyrighted by the Regents of the University of
77 * California, Sun Microsystems, Inc., and other parties. The following
78 * terms apply to all files associated with the software unless explicitly
79 * disclaimed in individual files.
80 *
81 * The authors hereby grant permission to use, copy, modify, distribute,
82 * and license this software and its documentation for any purpose, provided
83 * that existing copyright notices are retained in all copies and that this
84 * notice is included verbatim in any distributions. No written agreement,
85 * license, or royalty fee is required for any of the authorized uses.
86 * Modifications to this software may be copyrighted by their authors
87 * and need not follow the licensing terms described here, provided that
88 * the new terms are clearly indicated on the first page of each file where
89 * they apply.
90 *
91 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
92 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
93 * ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
94 * DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
95 * POSSIBILITY OF SUCH DAMAGE.
96 *
97 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
98 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
99 * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
100 * IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
101 * NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
102 * MODIFICATIONS.
103 *
104 * GOVERNMENT USE: If you are acquiring this software on behalf of the
105 * U.S. government, the Government shall have only "Restricted Rights"
106 * in the software and related documentation as defined in the Federal
107 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
108 * are acquiring the software on behalf of the Department of Defense, the
109 * software shall be classified as "Commercial Computer Software" and the
110 * Government shall have only "Restricted Rights" as defined in Clause
111 * 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the
112 * authors grant the U.S. Government and others acting in its behalf
113 * permission to use and distribute the software in accordance with the
114 * terms specified in this license.
115 */
116
117#include "xpm-color-table.h"
118
119/*
120 *----------------------------------------------------------------------
121 *
122 * find_color --
123 *
124 * This routine finds the color entry that corresponds to the
125 * specified color.
126 *
127 * Results:
128 * Returns non-zero on success. The RGB values of the XColor
129 * will be initialized to the proper values on success.
130 *
131 * Side effects:
132 * None.
133 *
134 *----------------------------------------------------------------------
135 */
136
137static int
138compare_xcolor_entries (const void *a, const void *b)
139{
140 return g_ascii_strcasecmp (s1: (const char *) a,
141 s2: color_names + ((const XPMColorEntry *)b)->name_offset);
142}
143
144static gboolean
145find_color(const char *name,
146 XPMColor *colorPtr)
147{
148 XPMColorEntry *found;
149
150 found = bsearch (key: name, base: xColors, G_N_ELEMENTS (xColors), size: sizeof (XPMColorEntry),
151 compar: compare_xcolor_entries);
152 if (found == NULL)
153 return FALSE;
154
155 colorPtr->red = (found->red * 65535) / 255;
156 colorPtr->green = (found->green * 65535) / 255;
157 colorPtr->blue = (found->blue * 65535) / 255;
158
159 return TRUE;
160}
161
162/*
163 *----------------------------------------------------------------------
164 *
165 * parse_color --
166 *
167 * Partial implementation of X color name parsing interface.
168 *
169 * Results:
170 * Returns TRUE on success.
171 *
172 * Side effects:
173 * None.
174 *
175 *----------------------------------------------------------------------
176 */
177
178static gboolean
179parse_color (const char *spec,
180 XPMColor *colorPtr)
181{
182 if (spec[0] == '#') {
183 int i, red, green, blue;
184
185 if ((i = strlen (s: spec + 1)) % 3) {
186 return FALSE;
187 }
188 i /= 3;
189
190 if (i == 4) {
191 if (sscanf (s: spec + 1, format: "%4x%4x%4x", &red, &green, &blue) != 3)
192 return FALSE;
193 colorPtr->red = red;
194 colorPtr->green = green;
195 colorPtr->blue = blue;
196 } else if (i == 1) {
197 if (sscanf (s: spec + 1, format: "%1x%1x%1x", &red, &green, &blue) != 3)
198 return FALSE;
199 colorPtr->red = (red * 65535) / 15;
200 colorPtr->green = (green * 65535) / 15;
201 colorPtr->blue = (blue * 65535) / 15;
202 } else if (i == 2) {
203 if (sscanf (s: spec + 1, format: "%2x%2x%2x", &red, &green, &blue) != 3)
204 return FALSE;
205 colorPtr->red = (red * 65535) / 255;
206 colorPtr->green = (green * 65535) / 255;
207 colorPtr->blue = (blue * 65535) / 255;
208 } else /* if (i == 3) */ {
209 if (sscanf (s: spec + 1, format: "%3x%3x%3x", &red, &green, &blue) != 3)
210 return FALSE;
211 colorPtr->red = (red * 65535) / 4095;
212 colorPtr->green = (green * 65535) / 4095;
213 colorPtr->blue = (blue * 65535) / 4095;
214 }
215 } else {
216 if (!find_color(name: spec, colorPtr))
217 return FALSE;
218 }
219 return TRUE;
220}
221
222static gint
223xpm_seek_string (FILE *infile, const gchar *str)
224{
225 char instr[1024];
226
227 while (!feof (stream: infile)) {
228 if (fscanf (stream: infile, format: "%1023s", instr) < 0)
229 return FALSE;
230 if (strcmp (s1: instr, s2: str) == 0)
231 return TRUE;
232 }
233
234 return FALSE;
235}
236
237static gint
238xpm_seek_char (FILE *infile, gchar c)
239{
240 gint b, oldb;
241
242 while ((b = getc (stream: infile)) != EOF) {
243 if (c != b && b == '/') {
244 b = getc (stream: infile);
245 if (b == EOF)
246 return FALSE;
247
248 else if (b == '*') { /* we have a comment */
249 b = -1;
250 do {
251 oldb = b;
252 b = getc (stream: infile);
253 if (b == EOF)
254 return FALSE;
255 } while (!(oldb == '*' && b == '/'));
256 }
257 } else if (c == b)
258 return TRUE;
259 }
260
261 return FALSE;
262}
263
264static gint
265xpm_read_string (FILE *infile, gchar **buffer, guint *buffer_size)
266{
267 gint c;
268 guint cnt = 0, bufsiz, ret = FALSE;
269 gchar *buf;
270
271 buf = *buffer;
272 bufsiz = *buffer_size;
273 if (buf == NULL) {
274 bufsiz = 10 * sizeof (gchar);
275 buf = g_new (gchar, bufsiz);
276 }
277
278 do {
279 c = getc (stream: infile);
280 } while (c != EOF && c != '"');
281
282 if (c != '"')
283 goto out;
284
285 while ((c = getc (stream: infile)) != EOF) {
286 if (cnt == bufsiz) {
287 guint new_size = bufsiz * 2;
288
289 if (new_size > bufsiz)
290 bufsiz = new_size;
291 else
292 goto out;
293
294 buf = g_realloc (mem: buf, n_bytes: bufsiz);
295 buf[bufsiz - 1] = '\0';
296 }
297
298 if (c != '"')
299 buf[cnt++] = c;
300 else {
301 buf[cnt] = 0;
302 ret = TRUE;
303 break;
304 }
305 }
306
307 out:
308 buf[bufsiz - 1] = '\0'; /* ensure null termination for errors */
309 *buffer = buf;
310 *buffer_size = bufsiz;
311 return ret;
312}
313
314static gchar *
315xpm_extract_color (const gchar *buffer)
316{
317 const gchar *p = &buffer[0];
318 gint new_key = 0;
319 gint key = 0;
320 gint current_key = 1;
321 gint space = 128;
322 gchar word[129], color[129], current_color[129];
323 gchar *r;
324
325 word[0] = '\0';
326 color[0] = '\0';
327 current_color[0] = '\0';
328 while (1) {
329 /* skip whitespace */
330 for (; *p != '\0' && g_ascii_isspace (*p); p++) {
331 }
332 /* copy word */
333 for (r = word; *p != '\0' && !g_ascii_isspace (*p) && r - word < sizeof (word) - 1; p++, r++) {
334 *r = *p;
335 }
336 *r = '\0';
337 if (*word == '\0') {
338 if (color[0] == '\0') /* incomplete colormap entry */
339 return NULL;
340 else /* end of entry, still store the last color */
341 new_key = 1;
342 }
343 else if (key > 0 && color[0] == '\0') /* next word must be a color name part */
344 new_key = 0;
345 else {
346 if (strcmp (s1: word, s2: "c") == 0)
347 new_key = 5;
348 else if (strcmp (s1: word, s2: "g") == 0)
349 new_key = 4;
350 else if (strcmp (s1: word, s2: "g4") == 0)
351 new_key = 3;
352 else if (strcmp (s1: word, s2: "m") == 0)
353 new_key = 2;
354 else if (strcmp (s1: word, s2: "s") == 0)
355 new_key = 1;
356 else
357 new_key = 0;
358 }
359 if (new_key == 0) { /* word is a color name part */
360 if (key == 0) /* key expected */
361 return NULL;
362 /* accumulate color name */
363 if (color[0] != '\0') {
364 strncat (dest: color, src: " ", n: space);
365 space -= MIN (space, 1);
366 }
367 strncat (dest: color, src: word, n: space);
368 space -= MIN (space, strlen (word));
369 }
370 else { /* word is a key */
371 if (key > current_key) {
372 current_key = key;
373 strcpy (dest: current_color, src: color);
374 }
375 space = 128;
376 color[0] = '\0';
377 key = new_key;
378 if (*p == '\0') break;
379 }
380
381 }
382 if (current_key > 1)
383 return g_strdup (str: current_color);
384 else
385 return NULL;
386}
387
388/* (almost) direct copy from gdkpixmap.c... loads an XPM from a file */
389
390static const gchar *
391file_buffer (enum buf_op op, gpointer handle)
392{
393 struct file_handle *h = handle;
394
395 switch (op) {
396 case op_header:
397 if (xpm_seek_string (infile: h->infile, str: "XPM") != TRUE)
398 break;
399
400 if (xpm_seek_char (infile: h->infile, c: '{') != TRUE)
401 break;
402 /* Fall through to the next xpm_seek_char. */
403
404 case op_cmap:
405 xpm_seek_char (infile: h->infile, c: '"');
406 if (fseek (stream: h->infile, off: -1, SEEK_CUR) != 0)
407 return NULL;
408 /* Fall through to the xpm_read_string. */
409
410 case op_body:
411 if(!xpm_read_string (infile: h->infile, buffer: &h->buffer, buffer_size: &h->buffer_size))
412 return NULL;
413 return h->buffer;
414
415 default:
416 g_assert_not_reached ();
417 }
418
419 return NULL;
420}
421
422/* This reads from memory */
423static const gchar *
424mem_buffer (enum buf_op op, gpointer handle)
425{
426 struct mem_handle *h = handle;
427 switch (op) {
428 case op_header:
429 case op_cmap:
430 case op_body:
431 if (h->data[h->offset]) {
432 const gchar* retval;
433
434 retval = h->data[h->offset];
435 h->offset += 1;
436 return retval;
437 }
438 break;
439
440 default:
441 g_assert_not_reached ();
442 break;
443 }
444
445 return NULL;
446}
447
448/* This function does all the work. */
449static GdkPixbuf *
450pixbuf_create_from_xpm (const gchar * (*get_buf) (enum buf_op op, gpointer handle), gpointer handle,
451 GError **error)
452{
453 gint w, h, n_col, cpp, x_hot, y_hot, items;
454 gint cnt, xcnt, ycnt, wbytes, n;
455 gint is_trans = FALSE;
456 const gchar *buffer;
457 gchar *name_buf;
458 gchar pixel_str[32];
459 GHashTable *color_hash;
460 XPMColor *colors, *color, *fallbackcolor;
461 guchar *pixtmp;
462 GdkPixbuf *pixbuf = NULL;
463 gint rowstride;
464
465 fallbackcolor = NULL;
466
467 buffer = (*get_buf) (op_header, handle);
468 if (!buffer) {
469 g_set_error_literal (err: error,
470 GDK_PIXBUF_ERROR,
471 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
472 _("No XPM header found"));
473 return NULL;
474 }
475 items = sscanf (s: buffer, format: "%d %d %d %d %d %d", &w, &h, &n_col, &cpp, &x_hot, &y_hot);
476
477 if (items != 4 && items != 6) {
478 g_set_error_literal (err: error,
479 GDK_PIXBUF_ERROR,
480 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
481 _("Invalid XPM header"));
482 return NULL;
483 }
484
485 if (w <= 0) {
486 g_set_error_literal (err: error,
487 GDK_PIXBUF_ERROR,
488 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
489 _("XPM file has image width <= 0"));
490 return NULL;
491
492 }
493 if (h <= 0) {
494 g_set_error_literal (err: error,
495 GDK_PIXBUF_ERROR,
496 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
497 _("XPM file has image height <= 0"));
498 return NULL;
499
500 }
501 /* Check from libXpm's ParsePixels() */
502 if ((h > 0 && w >= UINT_MAX / h) ||
503 w * h >= UINT_MAX / sizeof(unsigned int)) {
504 g_set_error_literal (err: error,
505 GDK_PIXBUF_ERROR,
506 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
507 _("Invalid XPM header"));
508 return NULL;
509 }
510 if (cpp <= 0 || cpp >= 32 || w >= G_MAXINT / cpp) {
511 g_set_error_literal (err: error,
512 GDK_PIXBUF_ERROR,
513 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
514 _("XPM has invalid number of chars per pixel"));
515 return NULL;
516 }
517 if (n_col <= 0 ||
518 n_col >= G_MAXINT / (cpp + 1) ||
519 n_col >= G_MAXINT / sizeof (XPMColor)) {
520 g_set_error_literal (err: error,
521 GDK_PIXBUF_ERROR,
522 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
523 _("XPM file has invalid number of colors"));
524 return NULL;
525 }
526
527 /* The hash is used for fast lookups of color from chars */
528 color_hash = g_hash_table_new (hash_func: g_str_hash, key_equal_func: g_str_equal);
529
530 name_buf = g_try_malloc (n_bytes: n_col * (cpp + 1));
531 if (!name_buf) {
532 g_set_error_literal (err: error,
533 GDK_PIXBUF_ERROR,
534 code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
535 _("Cannot allocate memory for loading XPM image"));
536 g_hash_table_destroy (hash_table: color_hash);
537 return NULL;
538 }
539 colors = (XPMColor *) g_try_malloc (n_bytes: sizeof (XPMColor) * n_col);
540 if (!colors) {
541 g_set_error_literal (err: error,
542 GDK_PIXBUF_ERROR,
543 code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
544 _("Cannot allocate memory for loading XPM image"));
545 g_hash_table_destroy (hash_table: color_hash);
546 g_free (mem: name_buf);
547 return NULL;
548 }
549
550 for (cnt = 0; cnt < n_col; cnt++) {
551 gchar *color_name;
552
553 buffer = (*get_buf) (op_cmap, handle);
554 if (!buffer) {
555 g_set_error_literal (err: error,
556 GDK_PIXBUF_ERROR,
557 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
558 _("Cannot read XPM colormap"));
559 goto out;
560 }
561
562 color = &colors[cnt];
563 color->color_string = &name_buf[cnt * (cpp + 1)];
564 strncpy (dest: color->color_string, src: buffer, n: cpp);
565 color->color_string[cpp] = 0;
566 buffer += strlen (s: color->color_string);
567 color->transparent = FALSE;
568
569 color_name = xpm_extract_color (buffer);
570
571 if ((color_name == NULL) || (g_ascii_strcasecmp (s1: color_name, s2: "None") == 0)
572 || (parse_color (spec: color_name, colorPtr: color) == FALSE)) {
573 color->transparent = TRUE;
574 color->red = 0;
575 color->green = 0;
576 color->blue = 0;
577 is_trans = TRUE;
578 }
579
580 g_free (mem: color_name);
581 g_hash_table_insert (hash_table: color_hash, key: color->color_string, value: color);
582
583 if (cnt == 0)
584 fallbackcolor = color;
585 }
586
587 pixbuf = gdk_pixbuf_new (colorspace: GDK_COLORSPACE_RGB, has_alpha: is_trans, bits_per_sample: 8, width: w, height: h);
588
589 if (!pixbuf) {
590 g_set_error_literal (err: error,
591 GDK_PIXBUF_ERROR,
592 code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
593 _("Cannot allocate memory for loading XPM image"));
594 goto out;
595 }
596
597 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
598
599 wbytes = w * cpp;
600
601 for (ycnt = 0; ycnt < h; ycnt++) {
602 pixtmp = gdk_pixbuf_get_pixels (pixbuf) + ycnt * rowstride;
603
604 buffer = (*get_buf) (op_body, handle);
605 if ((!buffer) || (strlen (s: buffer) < wbytes)) {
606 /* Advertised width doesn't match pixels */
607 g_set_error_literal (err: error,
608 GDK_PIXBUF_ERROR,
609 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
610 _("Dimensions do not match data"));
611 goto out;
612 }
613
614 for (n = 0, xcnt = 0; n < wbytes; n += cpp, xcnt++) {
615 strncpy (dest: pixel_str, src: &buffer[n], n: cpp);
616 pixel_str[cpp] = 0;
617
618 color = g_hash_table_lookup (hash_table: color_hash, key: pixel_str);
619
620 /* Bad XPM...punt */
621 if (!color)
622 color = fallbackcolor;
623
624 *pixtmp++ = color->red >> 8;
625 *pixtmp++ = color->green >> 8;
626 *pixtmp++ = color->blue >> 8;
627
628 if (is_trans && color->transparent)
629 *pixtmp++ = 0;
630 else if (is_trans)
631 *pixtmp++ = 0xFF;
632 }
633 }
634
635 g_hash_table_destroy (hash_table: color_hash);
636 g_free (mem: colors);
637 g_free (mem: name_buf);
638
639 if (items == 6) {
640 gchar hot[10];
641 g_snprintf (string: hot, n: 10, format: "%d", x_hot);
642 gdk_pixbuf_set_option (pixbuf, key: "x_hot", value: hot);
643 g_snprintf (string: hot, n: 10, format: "%d", y_hot);
644 gdk_pixbuf_set_option (pixbuf, key: "y_hot", value: hot);
645
646 }
647
648 return pixbuf;
649
650out:
651 g_hash_table_destroy (hash_table: color_hash);
652 g_free (mem: colors);
653 g_free (mem: name_buf);
654
655 g_clear_object (&pixbuf);
656 return NULL;
657}
658
659/* Shared library entry point for file loading */
660static GdkPixbuf *
661gdk_pixbuf__xpm_image_load (FILE *f,
662 GError **error)
663{
664 GdkPixbuf *pixbuf;
665 struct file_handle h;
666
667 memset (s: &h, c: 0, n: sizeof (h));
668 h.infile = f;
669 pixbuf = pixbuf_create_from_xpm (get_buf: file_buffer, handle: &h, error);
670 g_free (mem: h.buffer);
671
672 return pixbuf;
673}
674
675/* Shared library entry point for memory loading */
676static GdkPixbuf *
677gdk_pixbuf__xpm_image_load_xpm_data (const gchar **data)
678{
679 GdkPixbuf *pixbuf;
680 struct mem_handle h;
681 GError *error = NULL;
682
683 h.data = data;
684 h.offset = 0;
685
686 pixbuf = pixbuf_create_from_xpm (get_buf: mem_buffer, handle: &h, error: &error);
687
688 if (error) {
689 g_warning ("Inline XPM data is broken: %s", error->message);
690 g_error_free (error);
691 error = NULL;
692 }
693
694 return pixbuf;
695}
696
697/* Progressive loader */
698typedef struct _XPMContext XPMContext;
699struct _XPMContext
700{
701 GdkPixbufModulePreparedFunc prepared_func;
702 GdkPixbufModuleUpdatedFunc updated_func;
703 gpointer user_data;
704
705 gchar *tempname;
706 FILE *file;
707 gboolean all_okay;
708};
709
710/*
711 * FIXME xpm loading progressively is not properly implemented.
712 * Instead we will buffer to a file then load that file when done.
713 * This is very broken but it should be relatively simple to fix
714 * in the future.
715 */
716static gpointer
717gdk_pixbuf__xpm_image_begin_load (GdkPixbufModuleSizeFunc size_func,
718 GdkPixbufModulePreparedFunc prepared_func,
719 GdkPixbufModuleUpdatedFunc updated_func,
720 gpointer user_data,
721 GError **error)
722{
723 XPMContext *context;
724 gint fd;
725
726 g_assert (size_func != NULL);
727 g_assert (prepared_func != NULL);
728 g_assert (updated_func != NULL);
729
730 context = g_new (XPMContext, 1);
731 context->prepared_func = prepared_func;
732 context->updated_func = updated_func;
733 context->user_data = user_data;
734 context->all_okay = TRUE;
735 fd = g_file_open_tmp (tmpl: "gdkpixbuf-xpm-tmp.XXXXXX", name_used: &context->tempname,
736 NULL);
737 if (fd < 0) {
738 g_free (mem: context);
739 return NULL;
740 }
741
742 context->file = fdopen (fd: fd, modes: "w+");
743 if (context->file == NULL) {
744 g_free (mem: context->tempname);
745 g_free (mem: context);
746 return NULL;
747 }
748
749 return context;
750}
751
752static gboolean
753gdk_pixbuf__xpm_image_stop_load (gpointer data,
754 GError **error)
755{
756 XPMContext *context = (XPMContext*) data;
757 GdkPixbuf *pixbuf;
758 gboolean retval = FALSE;
759
760 g_return_val_if_fail (data != NULL, FALSE);
761
762 fflush (stream: context->file);
763 rewind (stream: context->file);
764 if (context->all_okay) {
765 pixbuf = gdk_pixbuf__xpm_image_load (f: context->file, error);
766
767 if (pixbuf != NULL) {
768 (* context->prepared_func) (pixbuf,
769 NULL,
770 context->user_data);
771 (* context->updated_func) (pixbuf,
772 0, 0,
773 gdk_pixbuf_get_width (pixbuf),
774 gdk_pixbuf_get_height (pixbuf),
775 context->user_data);
776 g_object_unref (object: pixbuf);
777
778 retval = TRUE;
779 }
780 }
781
782 fclose (stream: context->file);
783 g_unlink (filename: context->tempname);
784 g_free (mem: context->tempname);
785 g_free (mem: (XPMContext *) context);
786
787 return retval;
788}
789
790static gboolean
791gdk_pixbuf__xpm_image_load_increment (gpointer data,
792 const guchar *buf,
793 guint size,
794 GError **error)
795{
796 XPMContext *context = (XPMContext *) data;
797
798 g_return_val_if_fail (data != NULL, FALSE);
799
800 if (fwrite (ptr: buf, size: sizeof (guchar), n: size, s: context->file) != size) {
801 gint save_errno = errno;
802 context->all_okay = FALSE;
803 g_set_error_literal (err: error,
804 G_FILE_ERROR,
805 code: g_file_error_from_errno (err_no: save_errno),
806 _("Failed to write to temporary file when loading XPM image"));
807 return FALSE;
808 }
809
810 return TRUE;
811}
812
813#ifndef INCLUDE_xpm
814#define MODULE_ENTRY(function) G_MODULE_EXPORT void function
815#else
816#define MODULE_ENTRY(function) void _gdk_pixbuf__xpm_ ## function
817#endif
818
819MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
820{
821 module->load = gdk_pixbuf__xpm_image_load;
822 module->load_xpm_data = gdk_pixbuf__xpm_image_load_xpm_data;
823 module->begin_load = gdk_pixbuf__xpm_image_begin_load;
824 module->stop_load = gdk_pixbuf__xpm_image_stop_load;
825 module->load_increment = gdk_pixbuf__xpm_image_load_increment;
826}
827
828MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
829{
830 static const GdkPixbufModulePattern signature[] = {
831 { "/* XPM */", NULL, 100 },
832 { NULL, NULL, 0 }
833 };
834 static const gchar *mime_types[] = {
835 "image/x-xpixmap",
836 NULL
837 };
838 static const gchar *extensions[] = {
839 "xpm",
840 NULL
841 };
842
843 info->name = "xpm";
844 info->signature = (GdkPixbufModulePattern *) signature;
845 info->description = NC_("image format", "XPM");
846 info->mime_types = (gchar **) mime_types;
847 info->extensions = (gchar **) extensions;
848 info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
849 info->license = "LGPL";
850}
851

source code of gtk/subprojects/gdk-pixbuf/gdk-pixbuf/io-xpm.c