]> Pileus Git - ~andy/gtk/blobdiff - gdk-pixbuf/io-pnm.c
[quartz] Delete the typedef of GdkDevicePrivate
[~andy/gtk] / gdk-pixbuf / io-pnm.c
index dfa5cb3467f062631d607866fdb887d883e9bb6d..f86826e6788348fb969772a5c4598ec140ac3f2e 100644 (file)
@@ -22,8 +22,7 @@
  * Boston, MA 02111-1307, USA.
  */
 
-#include <config.h>
-#include <ctype.h>
+#include "config.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -54,8 +53,9 @@ typedef struct {
 } PnmIOBuffer;
 
 typedef struct {
-       ModuleUpdatedNotifyFunc updated_func;
-       ModulePreparedNotifyFunc prepared_func;
+       GdkPixbufModuleUpdatedFunc updated_func;
+       GdkPixbufModulePreparedFunc prepared_func;
+       GdkPixbufModuleSizeFunc size_func;
        gpointer user_data;
        
        GdkPixbuf *pixbuf;
@@ -82,8 +82,9 @@ typedef struct {
 } PnmLoaderContext;
 
 static GdkPixbuf   *gdk_pixbuf__pnm_image_load          (FILE *f, GError **error);
-static gpointer    gdk_pixbuf__pnm_image_begin_load     (ModulePreparedNotifyFunc func, 
-                                                        ModuleUpdatedNotifyFunc func2,
+static gpointer    gdk_pixbuf__pnm_image_begin_load     (GdkPixbufModuleSizeFunc size_func, 
+                                                         GdkPixbufModulePreparedFunc func, 
+                                                        GdkPixbufModuleUpdatedFunc func2,
                                                         gpointer user_data,
                                                         GError **error);
 static gboolean    gdk_pixbuf__pnm_image_stop_load      (gpointer context, GError **error);
@@ -94,13 +95,6 @@ static gboolean    gdk_pixbuf__pnm_image_load_increment (gpointer context,
 static void explode_bitmap_into_buf              (PnmLoaderContext *context);
 static void explode_gray_into_buf                (PnmLoaderContext *context);
 
-/* Destroy notification function for the pixbuf */
-static void
-free_buffer (guchar *pixels, gpointer data)
-{
-       g_free (pixels);
-}
-
 
 /* explode bitmap data into rgb components         */
 /* we need to know what the row so we can          */
@@ -114,19 +108,17 @@ explode_bitmap_into_buf (PnmLoaderContext *context)
        guchar *from, *to, data;
        gint bit;
        guchar *dptr;
-       gint wid, x, y;
+       gint wid, x;
        
        g_return_if_fail (context != NULL);
        g_return_if_fail (context->dptr != NULL);
        
        /* I'm no clever bit-hacker so I'm sure this can be optimized */
        dptr = context->dptr;
-       y    = context->output_row;
        wid  = context->width;
        
        from = dptr + ((wid - 1) / 8);
        to   = dptr + (wid - 1) * 3;
-/*     bit  = 7 - (((y+1)*wid-1) % 8); */
        bit  = 7 - ((wid-1) % 8);
        
        /* get first byte and align properly */
@@ -141,7 +133,7 @@ explode_bitmap_into_buf (PnmLoaderContext *context)
                to -= 3;
                bit++;
                
-               if (bit > 7) {
+               if (bit > 7 && x > 0) {
                        from--;
                        data = from[0];
                        bit = 0;
@@ -198,12 +190,12 @@ pnm_skip_whitespace (PnmIOBuffer *inbuf, GError **error)
                        for ( ; *inptr != '\n' && inptr < inend; inptr++)
                                ;
                        
-                       if ( *inptr != '\n' ) {
+                       if ( inptr == inend || *inptr != '\n' ) {
                                /* couldn't read whole comment */
                                return PNM_SUSPEND;
                        }
                        
-               } else if (!isspace (*inptr)) {
+               } else if (!g_ascii_isspace (*inptr)) {
                        inbuf->byte = inptr;
                        inbuf->nbytes = (guint) (inend - inptr);
                        return PNM_OK;
@@ -218,17 +210,21 @@ pnm_skip_whitespace (PnmIOBuffer *inbuf, GError **error)
 
 /* read next number from buffer */
 static gint
-pnm_read_next_value (PnmIOBuffer *inbuf, guint *value, GError **error)
+pnm_read_next_value (PnmIOBuffer *inbuf, gint max_length, guint *value, GError **error)
 {
        register guchar *inptr, *word, *p;
-       guchar *inend, buf[128];
+       guchar *inend, buf[129];
        gchar *endptr;
        gint retval;
+       glong result;
        
        g_return_val_if_fail (inbuf != NULL, PNM_FATAL_ERR);
        g_return_val_if_fail (inbuf->byte != NULL, PNM_FATAL_ERR);
        g_return_val_if_fail (value != NULL, PNM_FATAL_ERR);
        
+       if (max_length < 0)
+               max_length = 128;
+
        /* skip white space */
        if ((retval = pnm_skip_whitespace (inbuf, error)) != PNM_OK)
                return retval;
@@ -237,24 +233,25 @@ pnm_read_next_value (PnmIOBuffer *inbuf, guint *value, GError **error)
        inptr = inbuf->byte;
        
        /* copy this pnm 'word' into a temp buffer */
-       for (p = inptr, word = buf; (p < inend) && !isspace (*p) && (p - inptr < 128); p++, word++)
+       for (p = inptr, word = buf; (p < inend) && !g_ascii_isspace (*p) && (*p != '#') && (p - inptr < max_length); p++, word++)
                *word = *p;
        *word = '\0';
        
        /* hmmm, there must be more data to this 'word' */
-       if (!isspace (*p))
-               return PNM_SUSPEND;
+       if (p == inend || (!g_ascii_isspace (*p) && (*p != '#')  && (p - inptr < max_length)))
+           return PNM_SUSPEND;
        
        /* get the value */
-       *value = strtol (buf, &endptr, 10);
-       if (*endptr != '\0') {
-               g_set_error (error,
-                            GDK_PIXBUF_ERROR,
-                            GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                            _("PNM loader expected to find an integer, but didn't"));
+       result = strtol ((gchar *)buf, &endptr, 10);
+       if (*endptr != '\0' || result < 0 || result > G_MAXUINT) {
+               g_set_error_literal (error,
+                                     GDK_PIXBUF_ERROR,
+                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                     _("PNM loader expected to find an integer, but didn't"));
                return PNM_FATAL_ERR;
        }
-       
+       *value = result;
+
        inbuf->byte = p;
        inbuf->nbytes = (guint) (inend - p);
        
@@ -279,10 +276,10 @@ pnm_read_header (PnmLoaderContext *context)
                        return PNM_SUSPEND;
                
                if (*inbuf->byte != 'P') {
-                       g_set_error (context->error,
-                                    GDK_PIXBUF_ERROR,
-                                    GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                                    _("PNM file has an incorrect initial byte"));
+                       g_set_error_literal (context->error,
+                                             GDK_PIXBUF_ERROR,
+                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                             _("PNM file has an incorrect initial byte"));
                        return PNM_FATAL_ERR;
                }
                
@@ -309,10 +306,10 @@ pnm_read_header (PnmLoaderContext *context)
                        context->type = PNM_FORMAT_PPM_RAW;
                        break;
                default:
-                       g_set_error (context->error,
-                                    GDK_PIXBUF_ERROR,
-                                    GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                                    _("PNM file is not in a recognized PNM subformat"));
+                       g_set_error_literal (context->error,
+                                             GDK_PIXBUF_ERROR,
+                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                             _("PNM file is not in a recognized PNM subformat"));
                        return PNM_FATAL_ERR;
                }
                
@@ -327,17 +324,17 @@ pnm_read_header (PnmLoaderContext *context)
                /* read the pixmap width */
                guint width = 0;
                
-               retval = pnm_read_next_value (inbuf, &width,
+               retval = pnm_read_next_value (inbuf, -1, &width,
                                              context->error);
                
                if (retval != PNM_OK) 
                        return retval;
                
                if (!width) {
-                       g_set_error (context->error,
-                                    GDK_PIXBUF_ERROR,
-                                    GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                                    _("PNM file has an image width of 0"));
+                       g_set_error_literal (context->error,
+                                             GDK_PIXBUF_ERROR,
+                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                             _("PNM file has an image width of 0"));
                        return PNM_FATAL_ERR;
                }
                
@@ -348,17 +345,17 @@ pnm_read_header (PnmLoaderContext *context)
                /* read the pixmap height */
                guint height = 0;
                
-               retval = pnm_read_next_value (inbuf, &height,
+               retval = pnm_read_next_value (inbuf, -1, &height,
                                              context->error);
                
                if (retval != PNM_OK)
                        return retval;
                
                if (!height) {
-                       g_set_error (context->error,
-                                    GDK_PIXBUF_ERROR,
-                                    GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                                    _("PNM file has an image height of 0"));
+                       g_set_error_literal (context->error,
+                                             GDK_PIXBUF_ERROR,
+                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                             _("PNM file has an image height of 0"));
                        return PNM_FATAL_ERR;
                }
                
@@ -371,19 +368,28 @@ pnm_read_header (PnmLoaderContext *context)
        case PNM_FORMAT_PGM:
        case PNM_FORMAT_PGM_RAW:
                if (!context->maxval) {
-                       retval = pnm_read_next_value (inbuf, &context->maxval,
+                       retval = pnm_read_next_value (inbuf, -1, &context->maxval,
                                                      context->error);
                        
                        if (retval != PNM_OK)
                                return retval;
                        
                        if (context->maxval == 0) {
-                               g_set_error (context->error,
-                                            GDK_PIXBUF_ERROR,
-                                            GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                                            _("Maximum color value in PNM file is 0"));
+                               g_set_error_literal (context->error,
+                                                     GDK_PIXBUF_ERROR,
+                                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                                     _("Maximum color value in PNM file is 0"));
                                return PNM_FATAL_ERR;
                        }
+
+                       if (context->maxval > 65535) {
+                               g_set_error_literal (context->error,
+                                                     GDK_PIXBUF_ERROR,
+                                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                                     _("Maximum color value in PNM file is too large"));
+                               return PNM_FATAL_ERR;
+                       }
+
                }
                break;
        default:
@@ -417,12 +423,14 @@ pnm_read_raw_scanline (PnmLoaderContext *context)
                numpix = inbuf->nbytes / 3;
                break;
        default:
-               g_set_error (context->error,
-                            GDK_PIXBUF_ERROR,
-                            GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                            _("Raw PNM image type is invalid"));
+               g_set_error_literal (context->error,
+                                     GDK_PIXBUF_ERROR,
+                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                     _("Raw PNM image type is invalid"));
                return PNM_FATAL_ERR;
        }
+       if(context->maxval>255) 
+               numpix/=2;
        
        numpix = MIN (numpix, context->width - context->output_col);
        
@@ -445,12 +453,14 @@ pnm_read_raw_scanline (PnmLoaderContext *context)
                offset = context->output_col * 3;
                break;
        default:
-               g_set_error (context->error,
-                            GDK_PIXBUF_ERROR,
-                            GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                            _("Raw PNM image type is invalid"));
+               g_set_error_literal (context->error,
+                                     GDK_PIXBUF_ERROR,
+                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                     _("Raw PNM image type is invalid"));
                return PNM_FATAL_ERR;
        }
+       if(context->maxval>255) 
+               numbytes*=2;                            
        
        switch (context->type) {
        case PNM_FORMAT_PBM_RAW:
@@ -464,6 +474,17 @@ pnm_read_raw_scanline (PnmLoaderContext *context)
                if (context->maxval == 255) {
                        /* special-case optimization */
                        memcpy (dest, inbuf->byte, numbytes);
+               } else if(context->maxval == 65535) {
+                       /* optimized version of the next case */
+                       for(i=0; i < numbytes ; i+=2) {
+                               *dest++=inbuf->byte[i];
+                       }
+               } else if(context->maxval > 255) {
+                       /* scale down to 256 colors */
+                       for(i=0; i < numbytes ; i+=2) {
+                               guint v=inbuf->byte[i]*256+inbuf->byte[i+1];
+                               *dest++=v*255/context->maxval;
+                       }
                } else {
                        for (i = 0; i < numbytes; i++) {
                                guchar *byte = inbuf->byte + i;
@@ -477,10 +498,10 @@ pnm_read_raw_scanline (PnmLoaderContext *context)
                }
                break;
        default:
-               g_set_error (context->error,
-                            GDK_PIXBUF_ERROR,
-                            GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                            _("Raw PNM image type is invalid"));
+               g_set_error_literal (context->error,
+                                     GDK_PIXBUF_ERROR,
+                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                     _("Raw PNM image type is invalid"));
                return PNM_FATAL_ERR;
        }
        
@@ -504,105 +525,90 @@ pnm_read_raw_scanline (PnmLoaderContext *context)
 }
 
 static gint
-pnm_read_ascii_scanline (PnmLoaderContext *context)
+pnm_read_ascii_mono_scanline (PnmLoaderContext *context)
 {
        PnmIOBuffer *inbuf;
-       guint offset;
-       guint value, numval, i;
-       guchar data;
-       guchar mask;
+       guint value;
+       gint retval;
+       guchar *dptr;
+       gint max_length;
+
+       if (context->type == PNM_FORMAT_PBM)
+               max_length = 1;
+       else
+               max_length = -1;
+
+       inbuf = &context->inbuf;
+
+       context->dptr = context->pixels + context->output_row * context->rowstride;
+
+       dptr = context->dptr + context->output_col * 3;
+
+       while (TRUE) {
+               retval = pnm_read_next_value (inbuf, max_length, &value, context->error);
+               if (retval != PNM_OK)
+                       return retval;
+
+               if (context->type == PNM_FORMAT_PBM) {
+                       value = value ? 0 : 0xff;
+               }
+               else {
+                       /* scale the color up or down to an 8-bit color depth */
+                       if (value > context->maxval)
+                               value = 255;
+                       else
+                               value = (guchar)(255 * value / context->maxval);
+               }
+                       
+               *dptr++ = value;
+               *dptr++ = value;
+               *dptr++ = value;
+
+               context->output_col++;
+
+               if (context->output_col == context->width) {
+                       context->output_col = 0;
+                       context->output_row++;
+                       break;
+               }
+       }
+
+       return PNM_OK;
+}
+
+static gint
+pnm_read_ascii_color_scanline (PnmLoaderContext *context)
+{
+       PnmIOBuffer *inbuf;
+       guint value, i;
        guchar *dptr;
        gint retval;
        
-       g_return_val_if_fail (context != NULL, PNM_FATAL_ERR);
-       
-       data = mask = 0;
-       
        inbuf = &context->inbuf;
        
        context->dptr = context->pixels + context->output_row * context->rowstride;
        
-       switch (context->type) {
-       case PNM_FORMAT_PBM:
-               numval = MIN (8, context->width - context->output_col);
-               offset = context->output_col / 8;
-               break;
-       case PNM_FORMAT_PGM:
-               numval = 1;
-               offset = context->output_col;
-               break;
-       case PNM_FORMAT_PPM:
-               numval = 3;
-               offset = context->output_col * 3;
-               break;
-               
-       default:
-               g_set_error (context->error,
-                            GDK_PIXBUF_ERROR,
-                            GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                            _("PNM image format is invalid"));
-
-               return PNM_FATAL_ERR;
-       }
-       
-       dptr = context->dptr + offset + context->scan_state;
+       dptr = context->dptr + context->output_col * 3 + context->scan_state;
        
        while (TRUE) {
-               if (context->type == PNM_FORMAT_PBM) {
-                       mask = 0x80;
-                       data = 0;
-                       numval = MIN (8, context->width - context->output_col);
-               }
-               
-               for (i = context->scan_state; i < numval; i++) {
-                       retval = pnm_read_next_value (inbuf, &value,
-                                                     context->error);
+               for (i = context->scan_state; i < 3; i++) {
+                       retval = pnm_read_next_value (inbuf, -1, &value, context->error);
                        if (retval != PNM_OK) {
                                /* save state and return */
                                context->scan_state = i;
                                return retval;
                        }
                        
-                       switch (context->type) {
-                       case PNM_FORMAT_PBM:
-                               if (value)
-                                       data |= mask;
-                               mask >>= 1;
-                               
-                               break;
-                       case PNM_FORMAT_PGM:
-                       case PNM_FORMAT_PPM:
-                               /* scale the color to an 8-bit color depth */
-                               if (value > context->maxval)
-                                       *dptr++ = 255;
-                               else
-                                       *dptr++ = (guchar)(255 * value / context->maxval);
-                               break;
-                       default:
-                               g_set_error (context->error,
-                                            GDK_PIXBUF_ERROR,
-                                            GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                                            _("PNM image format is invalid"));
-                               return PNM_FATAL_ERR;
-                               break;
-                       }
+                       if (value > context->maxval)
+                               *dptr++ = 255;
+                       else
+                               *dptr++ = (guchar)(255 * value / context->maxval);
                }
                
                context->scan_state = 0;
-               
-               if (context->type == PNM_FORMAT_PBM) {
-                       *dptr++ = data;
-                       context->output_col += numval;
-               } else {
-                       context->output_col++;
-               }
+               context->output_col++;
                
                if (context->output_col == context->width) {
-                       if (context->type == PNM_FORMAT_PBM)
-                               explode_bitmap_into_buf (context);
-                       else if (context->type == PNM_FORMAT_PGM)
-                               explode_gray_into_buf (context);
-                       
                        context->output_col = 0;
                        context->output_row++;
                        break;
@@ -632,16 +638,20 @@ pnm_read_scanline (PnmLoaderContext *context)
                break;
        case PNM_FORMAT_PBM:
        case PNM_FORMAT_PGM:
+               retval = pnm_read_ascii_mono_scanline (context);
+               if (retval != PNM_OK)
+                       return retval;
+               break;          
        case PNM_FORMAT_PPM:
-               retval = pnm_read_ascii_scanline (context);
+               retval = pnm_read_ascii_color_scanline (context);
                if (retval != PNM_OK)
                        return retval;
                break;
        default:
-               g_set_error (context->error,
-                            GDK_PIXBUF_ERROR,
-                            GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
-                            _("PNM image loader does not support this PNM subformat"));
+               g_set_error_literal (context->error,
+                                     GDK_PIXBUF_ERROR,
+                                     GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
+                                     _("PNM image loader does not support this PNM subformat"));
 
                return PNM_FATAL_ERR;
        }
@@ -677,7 +687,7 @@ gdk_pixbuf__pnm_image_load (FILE *f, GError **error)
        
        inbuf = &context.inbuf;
        
-       while (!feof (f)) {
+       while (TRUE) {
                guint num_to_read;
                
                /* keep buffer as full as possible */
@@ -689,11 +699,14 @@ gdk_pixbuf__pnm_image_load (FILE *f, GError **error)
                nbytes = fread (inbuf->buffer + inbuf->nbytes, 1, num_to_read, f);
                
                /* error checking */
-               if (nbytes == 0 && ferror (f)) {
+               if (nbytes == 0) {
                        /* we ran out of data? */
                        if (context.pixbuf)
-                               gdk_pixbuf_unref (context.pixbuf);
-                       g_warning ("io-pnm.c: Ran out of data.\n");
+                               g_object_unref (context.pixbuf);
+                       g_set_error_literal (error,
+                                             GDK_PIXBUF_ERROR,
+                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                             _("Premature end-of-file encountered"));
                        return NULL;
                }
                
@@ -713,24 +726,51 @@ gdk_pixbuf__pnm_image_load (FILE *f, GError **error)
                
                /* scan until we hit image data */
                if (!context.did_prescan) {
-                       retval = pnm_skip_whitespace (inbuf,
-                                                     context.error);
-                       if (retval == PNM_FATAL_ERR)
-                               return NULL;
-                       else if (retval == PNM_SUSPEND)
-                               continue;
-                       
+                       switch (context.type) {
+                       case PNM_FORMAT_PBM_RAW:
+                       case PNM_FORMAT_PGM_RAW:
+                       case PNM_FORMAT_PPM_RAW:
+                               if (inbuf->nbytes <= 0)
+                                       continue;
+                               /* raw formats require exactly one whitespace */
+                               if (!g_ascii_isspace(*(inbuf->byte))) 
+                                       {
+                                               g_set_error_literal (error,
+                                                                     GDK_PIXBUF_ERROR,
+                                                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                                                     _("Raw PNM formats require exactly one whitespace before sample data"));
+                                               return NULL;
+                                       }
+                               inbuf->nbytes--;
+                               inbuf->byte++;
+                               break;
+                       default:
+                               retval = pnm_skip_whitespace (inbuf,
+                                                             context.error);
+                               if (retval == PNM_FATAL_ERR)
+                                       return NULL;
+                               else if (retval == PNM_SUSPEND)
+                                       continue;
+                               break;
+                       }
                        context.did_prescan = TRUE;
                        context.output_row = 0;
                        context.output_col = 0;
                        
-                       context.rowstride = context.width * 3;
-                       context.pixels = g_malloc (context.height * context.width * 3);
+                       context.pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
+                                                        context.width, context.height);
                        
-                       if (!context.pixels) {
+                       if (!context.pixbuf) {
                                /* Failed to allocate memory */
-                               g_warning ("Couldn't allocate pixel buf");
+                               g_set_error_literal (error,
+                                                     GDK_PIXBUF_ERROR,
+                                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+                                                     _("Cannot allocate memory for loading PNM image"));
+                               return NULL;
                        }
+
+                       context.rowstride = context.pixbuf->rowstride;
+                       context.pixels = context.pixbuf->pixels;
                }
                
                /* if we got here we're reading image data */
@@ -741,7 +781,7 @@ gdk_pixbuf__pnm_image_load (FILE *f, GError **error)
                                break;
                        } else if (retval == PNM_FATAL_ERR) {
                                if (context.pixbuf)
-                                       gdk_pixbuf_unref (context.pixbuf);
+                                       g_object_unref (context.pixbuf);
 
                                return NULL;
                        }
@@ -753,10 +793,7 @@ gdk_pixbuf__pnm_image_load (FILE *f, GError **error)
                        break;
        }
        
-       return gdk_pixbuf_new_from_data (context.pixels, GDK_COLORSPACE_RGB, FALSE, 8,
-                                        context.width, context.height, 
-                                        context.width * 3, free_buffer, NULL);
-
+       return context.pixbuf;
 }
 
 /* 
@@ -766,14 +803,23 @@ gdk_pixbuf__pnm_image_load (FILE *f, GError **error)
  */
 
 static gpointer
-gdk_pixbuf__pnm_image_begin_load (ModulePreparedNotifyFunc prepared_func, 
-                                 ModuleUpdatedNotifyFunc  updated_func,
+gdk_pixbuf__pnm_image_begin_load (GdkPixbufModuleSizeFunc size_func, 
+                                  GdkPixbufModulePreparedFunc prepared_func, 
+                                 GdkPixbufModuleUpdatedFunc  updated_func,
                                  gpointer user_data,
                                  GError **error)
 {
        PnmLoaderContext *context;
        
-       context = g_new0 (PnmLoaderContext, 1);
+       context = g_try_malloc (sizeof (PnmLoaderContext));
+       if (!context) {
+               g_set_error_literal (error, GDK_PIXBUF_ERROR, 
+                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+                                     _("Insufficient memory to load PNM context struct"));
+               return NULL;
+       }
+       memset (context, 0, sizeof (PnmLoaderContext));
+       context->size_func = size_func;
        context->prepared_func = prepared_func;
        context->updated_func  = updated_func;
        context->user_data = user_data;
@@ -809,15 +855,21 @@ gdk_pixbuf__pnm_image_stop_load (gpointer data,
        g_return_val_if_fail (context != NULL, TRUE);
        
        if (context->pixbuf)
-               gdk_pixbuf_unref (context->pixbuf);
+               g_object_unref (context->pixbuf);
 
+#if 0
+       /* We should ignore trailing newlines and we can't
+          generally complain about trailing stuff at all, since 
+          pnm allows to put multiple images in a file
+       */
        if (context->inbuf.nbytes > 0) {
-               g_set_error (error,
-                            GDK_PIXBUF_ERROR,
-                            GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
-                            _("Unexpected end of PNM image data"));
+               g_set_error_literal (error,
+                                     GDK_PIXBUF_ERROR,
+                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                     _("Unexpected end of PNM image data"));
                retval = FALSE;
        }
+#endif
        
        g_free (context);
 
@@ -838,9 +890,7 @@ gdk_pixbuf__pnm_image_load_increment (gpointer data,
 {
        PnmLoaderContext *context = (PnmLoaderContext *)data;
        PnmIOBuffer *inbuf;
-       guchar *old_byte;
-       guint old_nbytes;
-       guchar *bufhd;
+       const guchar *bufhd;
        guint num_left, spinguard;
        gint retval;
        
@@ -851,8 +901,6 @@ gdk_pixbuf__pnm_image_load_increment (gpointer data,
        
        bufhd = buf;
        inbuf = &context->inbuf;
-       old_nbytes = inbuf->nbytes;
-       old_byte  = inbuf->byte;
        
        num_left = size;
        spinguard = 0;
@@ -892,17 +940,46 @@ gdk_pixbuf__pnm_image_load_increment (gpointer data,
                        
                        context->got_header = TRUE;
                }
+
+               if (context->size_func) {
+                       gint w = context->width;
+                       gint h = context->height;
+                       (*context->size_func) (&w, &h, context->user_data);
+                       
+                       if (w == 0 || h == 0) 
+                               return FALSE;
+               }
+               
                
                /* scan until we hit image data */
                if (!context->did_prescan) {
-                       retval = pnm_skip_whitespace (inbuf,
-                                                     context->error);
-                       
-                       if (retval == PNM_FATAL_ERR)
-                               return FALSE;
-                       else if (retval == PNM_SUSPEND)
-                               continue;
-                                               
+                       switch (context->type) {
+                       case PNM_FORMAT_PBM_RAW:
+                       case PNM_FORMAT_PGM_RAW:
+                       case PNM_FORMAT_PPM_RAW:
+                               if (inbuf->nbytes <= 0)
+                                       continue;
+                               /* raw formats require exactly one whitespace */
+                               if (!g_ascii_isspace(*(inbuf->byte)))
+                                       {
+                                               g_set_error_literal (error,
+                                                                     GDK_PIXBUF_ERROR,
+                                                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
+                                                                     _("Raw PNM formats require exactly one whitespace before sample data"));
+                                               return FALSE;
+                                       }
+                               inbuf->nbytes--;
+                               inbuf->byte++;
+                               break;
+                       default:
+                               retval = pnm_skip_whitespace (inbuf,
+                                                             context->error);
+                               if (retval == PNM_FATAL_ERR)
+                                       return FALSE;
+                               else if (retval == PNM_SUSPEND)
+                                       continue;
+                               break;
+                       }
                        context->did_prescan = TRUE;
                        context->output_row = 0;
                        context->output_col = 0;
@@ -914,10 +991,10 @@ gdk_pixbuf__pnm_image_load_increment (gpointer data,
                                                          context->height);
                        
                        if (context->pixbuf == NULL) {
-                               g_set_error (error,
-                                            GDK_PIXBUF_ERROR,
-                                            GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
-                                            _("Insufficient memory to load PNM file"));
+                               g_set_error_literal (error,
+                                                     GDK_PIXBUF_ERROR,
+                                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+                                                     _("Insufficient memory to load PNM file"));
                                return FALSE;
                        }
                        
@@ -925,9 +1002,10 @@ gdk_pixbuf__pnm_image_load_increment (gpointer data,
                        context->rowstride = context->pixbuf->rowstride;
                        
                        /* Notify the client that we are ready to go */
-                       (* context->prepared_func) (context->pixbuf,
-                                                   NULL,
-                                                   context->user_data);
+                       if (context->prepared_func)
+                               (* context->prepared_func) (context->pixbuf,
+                                                           NULL,
+                                                           context->user_data);
                }
                
                /* if we got here we're reading image data */
@@ -937,10 +1015,8 @@ gdk_pixbuf__pnm_image_load_increment (gpointer data,
                        if (retval == PNM_SUSPEND) {
                                break;
                        } else if (retval == PNM_FATAL_ERR) {
-                               if (context->pixbuf)
-                                       gdk_pixbuf_unref (context->pixbuf);
                                return FALSE;
-                       } else if (retval == PNM_OK) {  
+                       } else if (retval == PNM_OK && context->updated_func) { 
                                /* send updated signal */
                                (* context->updated_func) (context->pixbuf,
                                                           0, 
@@ -960,11 +1036,51 @@ gdk_pixbuf__pnm_image_load_increment (gpointer data,
        return TRUE;
 }
 
-void
-gdk_pixbuf__pnm_fill_vtable (GdkPixbufModule *module)
+#ifndef INCLUDE_pnm
+#define MODULE_ENTRY(function) G_MODULE_EXPORT void function
+#else
+#define MODULE_ENTRY(function) void _gdk_pixbuf__pnm_ ## function
+#endif
+
+MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
 {
-  module->load = gdk_pixbuf__pnm_image_load;
-  module->begin_load = gdk_pixbuf__pnm_image_begin_load;
-  module->stop_load = gdk_pixbuf__pnm_image_stop_load;
-  module->load_increment = gdk_pixbuf__pnm_image_load_increment;
+       module->load = gdk_pixbuf__pnm_image_load;
+       module->begin_load = gdk_pixbuf__pnm_image_begin_load;
+       module->stop_load = gdk_pixbuf__pnm_image_stop_load;
+       module->load_increment = gdk_pixbuf__pnm_image_load_increment;
+}
+
+MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
+{
+       static GdkPixbufModulePattern signature[] = {
+               { "P1", NULL, 100 },
+               { "P2", NULL, 100 },
+               { "P3", NULL, 100 },
+               { "P4", NULL, 100 },
+               { "P5", NULL, 100 },
+               { "P6", NULL, 100 },
+               { NULL, NULL, 0 }
+       };
+       static gchar * mime_types[] = {
+               "image/x-portable-anymap",
+               "image/x-portable-bitmap",
+               "image/x-portable-graymap",
+               "image/x-portable-pixmap",
+               NULL
+       };
+       static gchar * extensions[] = {
+               "pnm",
+               "pbm",
+               "pgm",
+               "ppm",
+               NULL
+       };
+
+       info->name = "pnm";
+       info->signature = signature;
+       info->description = N_("The PNM/PBM/PGM/PPM image format family");
+       info->mime_types = mime_types;
+       info->extensions = extensions;
+       info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
+       info->license = "LGPL";
 }