Index: frmts/png/pngdataset.cpp
===================================================================
--- frmts/png/pngdataset.cpp	(revision 11880)
+++ frmts/png/pngdataset.cpp	(working copy)
@@ -99,6 +99,7 @@
 
     int	   bGeoTransformValid;
     double adfGeoTransform[6];
+    char  *pszProjection;
 
 
     void        CollectMetadata();
@@ -115,6 +116,8 @@
     virtual CPLErr GetGeoTransform( double * );
     virtual void FlushCache( void );
 
+    virtual const char *GetProjectionRef(void);
+
     // semi-private.
     jmp_buf     sSetJmpContext;
 
@@ -379,6 +382,7 @@
     adfGeoTransform[3] = 0.0;
     adfGeoTransform[4] = 0.0;
     adfGeoTransform[5] = 1.0;
+    pszProjection = NULL;
 }
 
 /************************************************************************/
@@ -396,11 +400,25 @@
     if( fpImage )
         VSIFCloseL( fpImage );
 
+    CPLFree(pszProjection);
+
     if( poColorTable != NULL )
         delete poColorTable;
 }
 
+
 /************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *PNGDataset::GetProjectionRef()
+
+{
+    return (pszProjection) ? pszProjection : "";
+}
+
+
+/************************************************************************/
 /*                          GetGeoTransform()                           */
 /************************************************************************/
 
@@ -608,19 +626,81 @@
     if( png_get_text( hPNG, psPNGInfo, &text_ptr, &nTextCount ) == 0 )
         return;
 
+    CPLFree(pszProjection);
+    pszProjection = NULL;
+    bGeoTransformValid = FALSE;
+
+    double tempGeoTransform[6];
+    int GDAL_GeoProjectionRef_num = 0;
+    int GDAL_GeoTransform_nb = 0;
     for( int iText = 0; iText < nTextCount; iText++ )
     {
-        char	*pszTag = CPLStrdup(text_ptr[iText].key);
-
-        for( int i = 0; pszTag[i] != '\0'; i++ )
+        const char* key = text_ptr[iText].key;
+        if (strncmp(key, "GDAL_", strlen("GDAL_")) == 0)
         {
-            if( pszTag[i] == ' ' || pszTag[i] == '=' || pszTag[i] == ':' )
-                pszTag[i] = '_';
+            key += strlen("GDAL_");
+            if (strncmp(key, "GDAL_Version_", strlen("GDAL_Version_")) == 0)
+            {
+            }
+            else if (strncmp(key, "GDAL_GeoTransform_", strlen("GDAL_GeoTransform_")) == 0)
+            {
+                int num = atoi(key + strlen("GDAL_GeoTransform_"));
+                if (num >= 0 && num < 6)
+                {
+                    tempGeoTransform[num] = CPLScanDouble( text_ptr[iText].text, 20, "C");
+                    GDAL_GeoTransform_nb++;
+                }
+            }
+            else if (GDAL_GeoProjectionRef_num >= 0 &&
+                    strncmp(key, "GDAL_GeoProjectionRef_", strlen("GDAL_GeoProjectionRef_")) == 0)
+            {
+                int num = atoi(key + strlen("GDAL_GeoProjectionRef_"));
+                if (num != GDAL_GeoProjectionRef_num)
+                {
+                    CPLError( CE_Warning, CPLE_NotSupported, 
+                    "Bad ordering of GDAL_GeoProjectionRef_XX keys.\n" );
+                    GDAL_GeoProjectionRef_num = -1;
+                }
+                else
+                {
+                    if (pszProjection)
+                    {
+                        pszProjection = (char*)CPLRealloc(pszProjection,
+                                        strlen(pszProjection) + strlen(text_ptr[iText].text) + 1);
+                        strcat(pszProjection, text_ptr[iText].text );
+                    }
+                    else
+                    {
+                        pszProjection = CPLStrdup( text_ptr[iText].text );
+                    }
+                    GDAL_GeoProjectionRef_num++;
+                }
+            }
+            else
+            {
+                char	*pszTag = CPLStrdup(key);
+        
+                for( int i = 0; pszTag[i] != '\0'; i++ )
+                {
+                    if( pszTag[i] == ' ' || pszTag[i] == '=' || pszTag[i] == ':' )
+                        pszTag[i] = '_';
+                }
+        
+                SetMetadataItem( pszTag, text_ptr[iText].text );
+                CPLFree( pszTag );
+            }
         }
-
-        SetMetadataItem( pszTag, text_ptr[iText].text );
-        CPLFree( pszTag );
     }
+    if (GDAL_GeoTransform_nb > 0 && GDAL_GeoTransform_nb != 6)
+    {
+        CPLError( CE_Warning, CPLE_AppDefined, 
+                  "Missing GDAL_GeoTransform_XX keys.\n" );
+    }
+    else if (GDAL_GeoTransform_nb == 6)
+    {
+        bGeoTransformValid = TRUE;
+        memcpy(adfGeoTransform, tempGeoTransform, sizeof(double)*6 );
+    }
 }
 
 /************************************************************************/
@@ -739,6 +819,7 @@
                   poDS->nBands );
         poDS->nBands = 1;
     }
+
     
 /* -------------------------------------------------------------------- */
 /*      We want to treat 1,2,4 bit images as eight bit.  This call      */
@@ -872,15 +953,62 @@
 /* -------------------------------------------------------------------- */
 /*      Check for world file.                                           */
 /* -------------------------------------------------------------------- */
-    poDS->bGeoTransformValid = 
+    double tempGeoTransform[6];
+    int  bGeoTransformValid =
         GDALReadWorldFile( poOpenInfo->pszFilename, NULL, 
-                           poDS->adfGeoTransform );
+                           tempGeoTransform );
 
-    if( !poDS->bGeoTransformValid )
-        poDS->bGeoTransformValid = 
+    if( !bGeoTransformValid )
+        bGeoTransformValid = 
             GDALReadWorldFile( poOpenInfo->pszFilename, ".wld", 
-                               poDS->adfGeoTransform );
+                               tempGeoTransform );
 
+    if( !bGeoTransformValid )
+        bGeoTransformValid = 
+            GDALReadWorldFile( poOpenInfo->pszFilename, ".pgw", 
+                               tempGeoTransform );
+    if( !bGeoTransformValid )
+        bGeoTransformValid = 
+            GDALReadWorldFile( poOpenInfo->pszFilename, ".pngw", 
+                               tempGeoTransform );
+    
+    if (bGeoTransformValid)
+    {
+        if (poDS->bGeoTransformValid)
+        {
+            int i;
+            for (i=0;i<6;i++)
+            {
+                if (fabs(tempGeoTransform[i] - poDS->adfGeoTransform[i]) > 1e-10)
+                    break;
+            }
+            if (i != 6)
+            {
+                CPLError( CE_Warning, CPLE_AppDefined, 
+                        "Using geotransform from worldfile but the PNG file itself "
+                        "contains GDAL_GeoTransform keys with different value.\n" );
+            }
+        }
+        poDS->bGeoTransformValid = bGeoTransformValid;
+        memcpy(poDS->adfGeoTransform, tempGeoTransform, 6 * sizeof(double));
+    }
+
+/* -------------------------------------------------------------------- */
+/*  Try to read projection file.                                        */
+/* -------------------------------------------------------------------- */
+    char* pszProjectionFromFile = poDS->ReadProjectionRefFromFile(poOpenInfo->pszFilename);
+    if (pszProjectionFromFile)
+    {
+        if (poDS->pszProjection && strcmp(pszProjectionFromFile, poDS->pszProjection) != 0)
+        {
+            CPLError( CE_Warning, CPLE_AppDefined, 
+                      "Using georef from proj file but the PNG file itself "
+                      "contains GDAL_GeoProjectionRef keys with different value.\n" );
+        }
+        CPLFree(poDS->pszProjection);
+        poDS->pszProjection = pszProjectionFromFile;
+    }
+
     return poDS;
 }
 
@@ -1105,7 +1233,121 @@
                           poCT->GetColorEntryCount(), NULL );
         }
     }
+    
+    /* Write GDAL metadata */
+    {
+        double      adfGeoTransform[6];
+        char        buffer1[79];
+        char        buffer2[79];
+        int         i;
+        
+        {
+            png_text text;
+            text.compression = PNG_TEXT_COMPRESSION_NONE;
+            text.key = "GDAL_GDAL_Version_Major";
+            sprintf(buffer2, "%d", GDAL_VERSION_MAJOR);
+            text.text = buffer2;
+            png_set_text(hPNG, psPNGInfo, &text, 1);
+            
+            text.compression = PNG_TEXT_COMPRESSION_NONE;
+            text.key = "GDAL_GDAL_Version_Minor";
+            sprintf(buffer2, "%d", GDAL_VERSION_MINOR);
+            text.text = buffer2;
+            png_set_text(hPNG, psPNGInfo, &text, 1);
+            
+            text.compression = PNG_TEXT_COMPRESSION_NONE;
+            text.key = "GDAL_GDAL_Version_Rev";
+            sprintf(buffer2, "%d", GDAL_VERSION_REV);
+            text.text = buffer2;
+            png_set_text(hPNG, psPNGInfo, &text, 1);
+            
+            text.compression = PNG_TEXT_COMPRESSION_NONE;
+            text.key = "GDAL_GDAL_Version_Build";
+            sprintf(buffer2, "%d", GDAL_VERSION_BUILD);
+            text.text = buffer2;
+            png_set_text(hPNG, psPNGInfo, &text, 1);
+        }
 
+        /* Write GeoTransform as tEXT if available */
+        if (poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None)
+        {
+            for(i=0; i < 6 ; i++)
+            {
+                png_text text;
+                text.compression = PNG_TEXT_COMPRESSION_NONE;
+                sprintf(buffer1, "GDAL_GDAL_GeoTransform_%d", i);
+                text.key = buffer1;
+                sprintf(buffer2, "%.10f", adfGeoTransform[i]);
+                text.text = buffer2;
+                png_set_text(hPNG, psPNGInfo, &text, 1);
+            }
+        }
+
+        /* Write ProjectionRef as tEXT if available */
+        const char* projectionRef = poSrcDS->GetProjectionRef();
+        if (projectionRef && projectionRef[0])
+        {
+            int offset = 0;
+            int projectionRefLen = strlen(projectionRef);
+            int num = 0;
+            while(offset < projectionRefLen)
+            {
+                png_text text;
+                text.compression = PNG_TEXT_COMPRESSION_NONE;
+                sprintf(buffer1, "GDAL_GDAL_GeoProjectionRef_%d", num++);
+                text.key = buffer1;
+                strncpy(buffer2, projectionRef + offset, 78);
+                buffer2[78] = 0;
+                int len = strlen(buffer2);
+                /* Try to cut at commas */
+                if (offset + len < projectionRefLen)
+                {
+                    for(i=len-1;i>=0;i--)
+                    {
+                        if (buffer2[i] == ',')
+                            break;
+                    }
+                    if (i > 0)
+                        buffer2[i+1] = 0;
+                }
+                offset += strlen(buffer2);
+                png_set_text(hPNG, psPNGInfo, &text, 1);
+            }
+        }
+        
+        /* Write metadata as tEXT if available */
+        char** metaData = poSrcDS->GetMetadata();
+        while(metaData && *metaData)
+        {
+            char* pszKey;
+            const char *pszVal = CPLParseNameValue(*metaData, &pszKey );
+            if (pszKey)
+            {
+                if (strlen(pszKey) > 78 - strlen("GDAL_"))
+                {
+                    CPLError( CE_Warning, CPLE_AppDefined, 
+                        "Key '%s' is too long to be written in PNG metadata.\n", pszKey );
+                }
+                else if (strlen(pszVal) > 78)
+                {
+                    CPLError( CE_Warning, CPLE_AppDefined, 
+                        "Value '%s' is too long to be written in PNG metadata.\n", pszVal );
+                }
+                else
+                {
+                    png_text text;
+                    text.compression = PNG_TEXT_COMPRESSION_NONE;
+                    sprintf(buffer1, "GDAL_%s", pszKey);
+                    text.key = buffer1;
+                    text.text = (char*)pszVal;
+                    png_set_text(hPNG, psPNGInfo, &text, 1);
+                }
+                CPLFree(pszKey);
+            }
+            metaData++;
+        }
+    }
+
     png_write_info( hPNG, psPNGInfo );
 
 /* -------------------------------------------------------------------- */
@@ -1280,7 +1522,7 @@
                                    "Byte UInt16" );
         poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, 
 "<CreationOptionList>\n"
-"   <Option name='WORLDFILE' type='boolean' description='Create world file'/>\n"
+"   <Option name='WORLDFILE' type='boolean' description='Write out world file'/>\n"
 "</CreationOptionList>\n" );
 
         poDriver->pfnOpen = PNGDataset::Open;
Index: frmts/jpeg/jpgdataset.cpp
===================================================================
--- frmts/jpeg/jpgdataset.cpp	(revision 11880)
+++ frmts/jpeg/jpgdataset.cpp	(working copy)
@@ -50,6 +50,11 @@
 void jpeg_vsiio_src (j_decompress_ptr cinfo, FILE * infile);
 void jpeg_vsiio_dest (j_compress_ptr cinfo, FILE * outfile);
 
+
+#define GDAL_MARKER     (0xe0 + 13)
+#define EXIF_MARKER     0xe1
+#define COM_MARKER      0xfe
+
 /************************************************************************/
 /* ==================================================================== */
 /*				JPGDataset				*/
@@ -68,6 +73,7 @@
 
     int	   bGeoTransformValid;
     double adfGeoTransform[6];
+    char*  pszProjection;
 
     FILE   *fpImage;
     int    nSubfileOffset;
@@ -96,6 +102,8 @@
 
     int    nQLevel;
     void   LoadDefaultTables(int);
+    
+    void   CollectMetaData();
 
     static void ErrorExit(j_common_ptr cinfo);
   public:
@@ -109,6 +117,7 @@
     virtual CPLErr GetGeoTransform( double * );
     static GDALDataset *Open( GDALOpenInfo * );
     static int          Identify( GDALOpenInfo * );
+    virtual const char *GetProjectionRef(void);
 };
 
 /************************************************************************/
@@ -324,10 +333,10 @@
             return FALSE;
 
         if( abyChunkHeader[0] != 0xFF 
-            || (abyChunkHeader[1] & 0xf0) != 0xe0 )
+            || !((abyChunkHeader[1] & 0xf0) == 0xe0 || abyChunkHeader[1] == COM_MARKER))
             return FALSE; // Not an APP chunk.
 
-        if( abyChunkHeader[1] == 0xe1 
+        if( abyChunkHeader[1] == EXIF_MARKER 
             && strncmp((const char *) abyChunkHeader + 4,"Exif",4) == 0 )
         {
             nTIFFHEADER = nChunkLoc + 10;
@@ -744,6 +753,7 @@
     adfGeoTransform[3] = 0.0;
     adfGeoTransform[4] = 0.0;
     adfGeoTransform[5] = 1.0;
+    pszProjection = NULL;
 }
 
 /************************************************************************/
@@ -757,6 +767,8 @@
 
     jpeg_abort_decompress( &sDInfo );
     jpeg_destroy_decompress( &sDInfo );
+    
+    CPLFree(pszProjection);
 
     if( fpImage != NULL )
         VSIFCloseL( fpImage );
@@ -1007,7 +1019,19 @@
     jpeg_start_decompress( &sDInfo );
 }
 
+
 /************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *JPGDataset::GetProjectionRef()
+
+{
+    return (pszProjection) ? pszProjection : "";
+}
+
+
+/************************************************************************/
 /*                          GetGeoTransform()                           */
 /************************************************************************/
 
@@ -1084,6 +1108,103 @@
 }
 
 /************************************************************************/
+/*                          CollectMetadata()                           */
+/************************************************************************/
+void JPGDataset::CollectMetaData()
+{
+    FILE* fp = fpImage;
+    /* -------------------------------------------------------------------- */
+    /*      Search for GDAL_MARKER chunk.                                   */
+    /* -------------------------------------------------------------------- */
+    GByte abyChunkHeader[10];
+    int nChunkLoc = 2;
+    
+    double tempGeoTransform[6];
+    int GDAL_GeoTransform_nb = 0;
+
+    CPLFree(pszProjection);
+    pszProjection = NULL;
+    bGeoTransformValid = FALSE;
+
+    for( ; TRUE; ) 
+    {
+        if( VSIFSeekL( fp, nChunkLoc, SEEK_SET ) != 0 )
+            break;
+
+        if( VSIFReadL( abyChunkHeader, sizeof(abyChunkHeader), 1, fp ) != 1 )
+            break;
+
+        if( abyChunkHeader[0] != 0xFF
+            || !((abyChunkHeader[1] & 0xf0) == 0xe0 || abyChunkHeader[1] == COM_MARKER))
+            break;
+
+        if( abyChunkHeader[1] == GDAL_MARKER 
+            && strncmp((const char *) abyChunkHeader + 4,"GDAL",4) == 0 )
+        {
+            int len = abyChunkHeader[2] * 256 + abyChunkHeader[3];
+            char* val = (char*)CPLMalloc(len);
+            if( VSIFSeekL( fp, nChunkLoc + 4, SEEK_SET ) != 0 )
+                break;
+            if( VSIFReadL( val, len, 1, fp ) != 1 )
+                break;
+            char* valOri = val;
+            val += strlen("GDAL") + 1;
+            
+            char* pszKey;
+            const char* pszValue = CPLParseNameValue(val, &pszKey);
+            if (pszKey)
+            {
+                if (strncmp(pszKey, "GDAL_Version_", strlen("GDAL_Version_")) == 0)
+                {
+                }
+                else if (strncmp(pszKey, "GDAL_GeoTransform_", strlen("GDAL_GeoTransform_")) == 0)
+                {
+                    int num = atoi(pszKey + strlen("GDAL_GeoTransform_"));
+                    if (num >= 0 && num < 6)
+                    {
+                        tempGeoTransform[num] = CPLScanDouble( pszValue, 20, "C");
+                        GDAL_GeoTransform_nb++;
+                    }
+                }
+                else if (strcmp(pszKey, "GDAL_GeoProjectionRef") == 0)
+                {
+                    pszProjection = CPLStrdup(pszValue);
+                }
+                else
+                {
+                    char	*pszTag = CPLStrdup(pszKey);
+            
+                    for( int i = 0; pszTag[i] != '\0'; i++ )
+                    {
+                        if( pszTag[i] == ' ' || pszTag[i] == '=' || pszTag[i] == ':' )
+                            pszTag[i] = '_';
+                    }
+            
+                    SetMetadataItem( pszTag, pszValue );
+                    CPLFree( pszTag );
+                }
+                CPLFree( pszKey );
+            }
+
+            CPLFree(valOri);
+        }
+
+        nChunkLoc += 2 + abyChunkHeader[2] * 256 + abyChunkHeader[3];
+    }
+
+    if (GDAL_GeoTransform_nb > 0 && GDAL_GeoTransform_nb != 6)
+    {
+        CPLError( CE_Warning, CPLE_AppDefined, 
+                  "Missing GDAL_GeoTransform_XX keys.\n" );
+    }
+    else if (GDAL_GeoTransform_nb == 6)
+    {
+        bGeoTransformValid = TRUE;
+        memcpy(adfGeoTransform, tempGeoTransform, sizeof(double)*6 );
+    }
+}
+
+/************************************************************************/
 /*                              Identify()                              */
 /************************************************************************/
 
@@ -1260,6 +1381,8 @@
         poDS->SetMetadata( poDS->papszMetadata );
     }
 
+    poDS->CollectMetaData();
+
     poDS->eAccess = GA_ReadOnly;
 
     poDS->sDInfo.err = jpeg_std_error( &(poDS->sJErr) );
@@ -1352,14 +1475,56 @@
 /* -------------------------------------------------------------------- */
 /*      Check for world file.                                           */
 /* -------------------------------------------------------------------- */
-    poDS->bGeoTransformValid = 
+    double tempGeoTransform[6];
+    int  bGeoTransformValid =
         GDALReadWorldFile( poOpenInfo->pszFilename, ".jgw", 
-                           poDS->adfGeoTransform )
+                           tempGeoTransform )
+        || GDALReadWorldFile( poOpenInfo->pszFilename, ".jpw", 
+                           tempGeoTransform )
         || GDALReadWorldFile( poOpenInfo->pszFilename, ".jpgw", 
-                              poDS->adfGeoTransform )
+                              tempGeoTransform )
+        || GDALReadWorldFile( poOpenInfo->pszFilename, ".jpegw", 
+                           tempGeoTransform )
         || GDALReadWorldFile( poOpenInfo->pszFilename, ".wld", 
-                              poDS->adfGeoTransform );
+                              tempGeoTransform );
 
+    if (bGeoTransformValid)
+    {
+        if (poDS->bGeoTransformValid)
+        {
+            int i;
+            for (i=0;i<6;i++)
+            {
+                if (fabs(tempGeoTransform[i] - poDS->adfGeoTransform[i]) > 1e-10)
+                    break;
+            }
+            if (i != 6)
+            {
+                CPLError( CE_Warning, CPLE_AppDefined, 
+                        "Using geotransform from worldfile but the JPG file itself "
+                        "contains GDAL_GeoTransform keys with different value.\n" );
+            }
+        }
+        poDS->bGeoTransformValid = bGeoTransformValid;
+        memcpy(poDS->adfGeoTransform, tempGeoTransform, 6 * sizeof(double));
+    }
+
+/* -------------------------------------------------------------------- */
+/*  Try to read projection file.                                        */
+/* -------------------------------------------------------------------- */
+    char* pszProjectionFromFile = poDS->ReadProjectionRefFromFile(poOpenInfo->pszFilename);
+    if (pszProjectionFromFile)
+    {
+        if (poDS->pszProjection && strcmp(pszProjectionFromFile, poDS->pszProjection) != 0)
+        {
+            CPLError( CE_Warning, CPLE_AppDefined, 
+                      "Using georef from proj file but the JPG file itself "
+                      "contains GDAL_GeoProjectionRef keys with different value.\n" );
+        }
+        CPLFree(poDS->pszProjection);
+        poDS->pszProjection = pszProjectionFromFile;
+    }
+
     return poDS;
 }
 
@@ -1525,6 +1690,77 @@
 
     jpeg_start_compress( &sCInfo, TRUE );
 
+    jpeg_write_marker ( &sCInfo, COM_MARKER, (const JOCTET*)"Created by GDAL", strlen("Created by GDAL"));
+    
+    /* Write GDAL metadata */
+    {
+        double adfGeoTransform[6];
+        
+        {
+            char buffer[80];
+            int len;
+            
+            sprintf(buffer, "GDAL_GDAL_Version_Major=%d", GDAL_VERSION_MAJOR);
+            len = strlen(buffer) + 1;
+            buffer[4] = 0;
+            jpeg_write_marker ( &sCInfo, GDAL_MARKER, (const JOCTET*)buffer, len);
+            
+            sprintf(buffer, "GDAL_GDAL_Version_Minor=%d", GDAL_VERSION_MINOR);
+            len = strlen(buffer) + 1;
+            buffer[4] = 0;
+            jpeg_write_marker ( &sCInfo, GDAL_MARKER, (const JOCTET*)buffer, len);
+            
+            sprintf(buffer, "GDAL_GDAL_Version_Rev=%d", GDAL_VERSION_REV);
+            len = strlen(buffer) + 1;
+            buffer[4] = 0;
+            jpeg_write_marker ( &sCInfo, GDAL_MARKER, (const JOCTET*)buffer, len);
+            
+            sprintf(buffer, "GDAL_GDAL_Version_Build=%d", GDAL_VERSION_BUILD);
+            len = strlen(buffer) + 1;
+            buffer[4] = 0;
+            jpeg_write_marker ( &sCInfo, GDAL_MARKER, (const JOCTET*)buffer, len);
+        }
+        
+         /* Write GeoTransform if available */
+        if (poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None)
+        {
+            int i;
+            for(i=0; i < 6 ; i++)
+            {
+                char buffer[80];
+                sprintf(buffer, "GDAL_GDAL_GeoTransform_%d=%.10f", i,adfGeoTransform[i]);
+                int len = strlen(buffer) + 1;
+                buffer[4] = 0;
+                jpeg_write_marker ( &sCInfo, GDAL_MARKER, (const JOCTET*)buffer, len);
+            }
+        }
+        
+        /* Write projectionRef if available */
+        const char* projectionRef = poSrcDS->GetProjectionRef();
+        if (projectionRef && projectionRef[0])
+        {
+            char* buffer = (char*)CPLMalloc(80 + strlen(projectionRef));
+            sprintf(buffer, "GDAL_GDAL_GeoProjectionRef=%s", projectionRef);
+            int len = strlen(buffer) + 1;
+            buffer[4] = 0;
+            jpeg_write_marker ( &sCInfo, GDAL_MARKER, (const JOCTET*)buffer, len);
+            CPLFree(buffer);
+        }
+        
+        /* Write metadata if available */
+        char** metaData = poSrcDS->GetMetadata();
+        while(metaData && *metaData)
+        {
+            char* buffer = (char*)CPLMalloc(80 + strlen(*metaData));
+            sprintf(buffer, "GDAL_%s", *metaData);
+            int len = strlen(buffer) + 1;
+            buffer[4] = 0;
+            jpeg_write_marker ( &sCInfo, GDAL_MARKER, (const JOCTET*)buffer, len);
+            CPLFree(buffer);
+            metaData++;
+        }
+    }
+    
 /* -------------------------------------------------------------------- */
 /*      Loop over image, copying image data.                            */
 /* -------------------------------------------------------------------- */
@@ -1637,7 +1873,7 @@
 "<CreationOptionList>\n"
 "   <Option name='PROGRESSIVE' type='boolean'/>\n"
 "   <Option name='QUALITY' type='int' description='good=100, bad=0, default=75'/>\n"
-"   <Option name='WORLDFILE' type='boolean'/>\n"
+"   <Option name='WORLDFILE' type='boolean' description='Write out world file'/>\n"
 "</CreationOptionList>\n" );
 
         poDriver->pfnIdentify = JPGDataset::Identify;
Index: frmts/gif/gifdataset.cpp
===================================================================
--- frmts/gif/gifdataset.cpp	(revision 11880)
+++ frmts/gif/gifdataset.cpp	(working copy)
@@ -67,6 +67,9 @@
 
     int	   bGeoTransformValid;
     double adfGeoTransform[6];
+    char  *pszProjection;
+    
+    void    CollectMetadata(SavedImage	*psImage);
 
   public:
                  GIFDataset();
@@ -75,6 +78,7 @@
     virtual CPLErr GetGeoTransform( double * );
     static GDALDataset *Open( GDALOpenInfo * );
     static int          Identify( GDALOpenInfo * );
+    virtual const char *GetProjectionRef(void);
 };
 
 /************************************************************************/
@@ -294,6 +298,7 @@
     adfGeoTransform[3] = 0.0;
     adfGeoTransform[4] = 0.0;
     adfGeoTransform[5] = 1.0;
+    pszProjection = NULL;
 }
 
 /************************************************************************/
@@ -311,6 +316,16 @@
 }
 
 /************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *GIFDataset::GetProjectionRef()
+
+{
+    return (pszProjection) ? pszProjection : "";
+}
+
+/************************************************************************/
 /*                          GetGeoTransform()                           */
 /************************************************************************/
 
@@ -327,6 +342,77 @@
 }
 
 /************************************************************************/
+/*                          CollectMetadata()                           */
+/************************************************************************/
+void GIFDataset::CollectMetadata(SavedImage	*psImage)
+{
+    int iExtBlock;
+    int GDAL_GeoTransform_nb = 0;
+    double tempGeoTransform[6];
+
+    CPLFree(pszProjection);
+    pszProjection = NULL;
+    bGeoTransformValid = FALSE;
+
+    for( iExtBlock = 0; iExtBlock < psImage->ExtensionBlockCount; iExtBlock++ )
+    {
+        char *pExtData;
+
+        if( psImage->ExtensionBlocks[iExtBlock].Function != 0xff )
+            continue;
+
+        pExtData = (char *)psImage->ExtensionBlocks[iExtBlock].Bytes;
+        char* pszKey;
+        const char* pszValue = CPLParseNameValue(pExtData, &pszKey);
+        if (pszKey && strncmp(pszKey, "GDAL_", strlen("GDAL_")) == 0)
+        {
+            char* oriKey = pszKey;
+            pszKey += strlen("GDAL_");
+            if (strncmp(pszKey, "GDAL_Version_", strlen("GDAL_Version_")) == 0)
+            {
+            }
+            else if (strncmp(pszKey, "GDAL_GeoTransform_", strlen("GDAL_GeoTransform_")) == 0)
+            {
+                int num = atoi(pszKey + strlen("GDAL_GeoTransform_"));
+                if (num >= 0 && num < 6)
+                {
+                    tempGeoTransform[num] = CPLScanDouble( pszValue, 20, "C");
+                    GDAL_GeoTransform_nb++;
+                }
+            }
+            else if (strcmp(pszKey, "GDAL_GeoProjectionRef") == 0)
+            {
+                pszProjection = CPLStrdup(pszValue);
+            }
+            else
+            {
+                char	*pszTag = CPLStrdup(pszKey);
+        
+                for( int i = 0; pszTag[i] != '\0'; i++ )
+                {
+                    if( pszTag[i] == ' ' || pszTag[i] == '=' || pszTag[i] == ':' )
+                        pszTag[i] = '_';
+                }
+        
+                SetMetadataItem( pszTag, pszValue );
+                CPLFree( pszTag );
+            }
+            CPLFree( oriKey );
+        }
+    }
+    if (GDAL_GeoTransform_nb > 0 && GDAL_GeoTransform_nb != 6)
+    {
+        CPLError( CE_Warning, CPLE_AppDefined, 
+                  "Missing GDAL_GeoTransform_XX keys.\n" );
+    }
+    else if (GDAL_GeoTransform_nb == 6)
+    {
+        bGeoTransformValid = TRUE;
+        memcpy(adfGeoTransform, tempGeoTransform, sizeof(double)*6 );
+    }
+}
+
+/************************************************************************/
 /*                                Open()                                */
 /************************************************************************/
 
@@ -404,11 +490,19 @@
     poDS->eAccess = GA_ReadOnly;
     poDS->hGifFile = hGifFile;
 
+    if (hGifFile->SavedImages == NULL)
+    {
+        CPLError( CE_Failure, CPLE_OpenFailed, "No images in gif file %s", poOpenInfo->pszFilename );
+        return NULL;
+    }
 /* -------------------------------------------------------------------- */
 /*      Capture some information from the file that is of interest.     */
 /* -------------------------------------------------------------------- */
     poDS->nRasterXSize = hGifFile->SavedImages[0].ImageDesc.Width;
     poDS->nRasterYSize = hGifFile->SavedImages[0].ImageDesc.Height;
+    
+    
+    poDS->CollectMetadata(&hGifFile->SavedImages[0]);
 
 /* -------------------------------------------------------------------- */
 /*      Create band information objects.                                */
@@ -429,11 +523,49 @@
 /* -------------------------------------------------------------------- */
 /*      Check for world file.                                           */
 /* -------------------------------------------------------------------- */
-    poDS->bGeoTransformValid = 
+    double tempGeoTransform[6];
+    int  bGeoTransformValid =
         GDALReadWorldFile( poOpenInfo->pszFilename, ".wld", 
-                           poDS->adfGeoTransform )
+                           tempGeoTransform )
         || GDALReadWorldFile( poOpenInfo->pszFilename, ".gfw", 
-                              poDS->adfGeoTransform );
+                             tempGeoTransform )
+        || GDALReadWorldFile( poOpenInfo->pszFilename, ".gifw", 
+                              tempGeoTransform );
+    if (bGeoTransformValid)
+    {
+        if (poDS->bGeoTransformValid)
+        {
+            int i;
+            for (i=0;i<6;i++)
+            {
+                if (fabs(tempGeoTransform[i] - poDS->adfGeoTransform[i]) > 1e-10)
+                    break;
+            }
+            if (i != 6)
+            {
+                CPLError( CE_Warning, CPLE_AppDefined, 
+                        "Using geotransform from worldfile but the GIF file itself "
+                        "contains GDAL_GeoTransform keys with different value.\n" );
+            }
+        }
+        poDS->bGeoTransformValid = bGeoTransformValid;
+        memcpy(poDS->adfGeoTransform, tempGeoTransform, 6 * sizeof(double));
+    }
+/* -------------------------------------------------------------------- */
+/*  Try to read projection file.                                        */
+/* -------------------------------------------------------------------- */
+    char* pszProjectionFromFile = poDS->ReadProjectionRefFromFile(poOpenInfo->pszFilename);
+    if (pszProjectionFromFile)
+    {
+        if (poDS->pszProjection && strcmp(pszProjectionFromFile, poDS->pszProjection) != 0)
+        {
+            CPLError( CE_Warning, CPLE_AppDefined, 
+                      "Using georef from proj file but the GIF file itself "
+                      "contains GDAL_GeoProjectionRef keys with different value.\n" );
+        }
+        CPLFree(poDS->pszProjection);
+        poDS->pszProjection = pszProjectionFromFile;
+    }
 
 /* -------------------------------------------------------------------- */
 /*      Support overviews.                                              */
@@ -566,10 +698,7 @@
 /*      Setup parameters.                                               */
 /* -------------------------------------------------------------------- */
     if (EGifPutScreenDesc(hGifFile, nXSize, nYSize, 
-                          psGifCT->ColorCount, 0,
-			  psGifCT) == GIF_ERROR ||
-	EGifPutImageDesc(hGifFile,
-			 0, 0, nXSize, nYSize, bInterlace, NULL) == GIF_ERROR )
+                          psGifCT->ColorCount, 255, psGifCT) == GIF_ERROR)
     {
         PrintGifError();
         CPLError( CE_Failure, CPLE_AppDefined, 
@@ -577,6 +706,81 @@
         return NULL;
     }
 
+    /* Support for transparency */
+    int bNoDataValue;
+    double noDataValue = poBand->GetNoDataValue(&bNoDataValue);
+    if (bNoDataValue && noDataValue >= 0 && noDataValue <= 255)
+    {
+        unsigned char extensionData[4];
+        extensionData[0] = 1; /*  Transparent Color Flag */
+        extensionData[1] = 0;
+        extensionData[2] = 0;
+        extensionData[3] = (unsigned char)noDataValue;
+        EGifPutExtension(hGifFile, 0xf9, 4, extensionData);
+    }
+    
+    /* Write GDAL metadata */
+    {
+        double adfGeoTransform[6];
+        
+        {
+            char buffer[80];
+            
+            sprintf(buffer, "GDAL_GDAL_Version_Major=%d", GDAL_VERSION_MAJOR);
+            EGifPutExtension(hGifFile, 0xff, strlen(buffer) + 1, buffer);
+            
+            sprintf(buffer, "GDAL_GDAL_Version_Minor=%d", GDAL_VERSION_MINOR);
+            EGifPutExtension(hGifFile, 0xff, strlen(buffer) + 1, buffer);
+            
+            sprintf(buffer, "GDAL_GDAL_Version_Rev=%d", GDAL_VERSION_REV);
+            EGifPutExtension(hGifFile, 0xff, strlen(buffer) + 1, buffer);
+            
+            sprintf(buffer, "GDAL_GDAL_Version_Build=%d", GDAL_VERSION_BUILD);
+            EGifPutExtension(hGifFile, 0xff, strlen(buffer) + 1, buffer);
+        }
+        
+         /* Write GeoTransform as APPLICATION_EXT_FUNC_CODE if available */
+        if (poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None)
+        {
+            int i;
+            for(i=0; i < 6 ; i++)
+            {
+                char buffer[80];
+                sprintf(buffer, "GDAL_GDAL_GeoTransform_%d=%.10f", i,adfGeoTransform[i]);
+                EGifPutExtension(hGifFile, 0xff, strlen(buffer) + 1, buffer);
+            }
+        }
+        
+        /* Write projectionRef as APPLICATION_EXT_FUNC_CODE if available */
+        const char* projectionRef = poSrcDS->GetProjectionRef();
+        if (projectionRef && projectionRef[0])
+        {
+            char* buffer = (char*)CPLMalloc(80 + strlen(projectionRef));
+            sprintf(buffer, "GDAL_GDAL_GeoProjectionRef=%s", projectionRef);
+            EGifPutExtension(hGifFile, 0xff, strlen(buffer) + 1, buffer);
+            CPLFree(buffer);
+        }
+        
+        /* Write metadata as APPLICATION_EXT_FUNC_CODE if available */
+        char** metaData = poSrcDS->GetMetadata();
+        while(metaData && *metaData)
+        {
+            char* buffer = (char*)CPLMalloc(80 + strlen(*metaData));
+            sprintf(buffer, "GDAL_%s", *metaData);
+            EGifPutExtension(hGifFile, 0xff, strlen(buffer) + 1, buffer);
+            CPLFree(buffer);
+            metaData++;
+        }
+    }
+
+    if (EGifPutImageDesc(hGifFile, 0, 0, nXSize, nYSize, bInterlace, NULL) == GIF_ERROR )
+    {
+        PrintGifError();
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Error writing gif file." );
+        return NULL;
+    }
+
 /* -------------------------------------------------------------------- */
 /*      Loop over image, copying image data.                            */
 /* -------------------------------------------------------------------- */
@@ -632,6 +836,23 @@
         return NULL;
     }
     
+    /* This is a hack to write a GIF89a instead of GIF87a */
+    /* (we have to, since we are using graphical extension block) */
+    /* EGifSpew would write GIF89a when it detects an extension block if we were using it */
+    /* As we don't, we could have used EGifSetGifVersion instead, but the version of libungif */
+    /* in GDAL has a bug : it writes on read-only memory ! */
+    /* (this is a well-known problem. Just google for "EGifSetGifVersion segfault") */
+    /* Most readers don't even care if it is GIF87a or GIF89a, but it is */
+    /* better to write the right version */
+
+    VSIFSeekL(fp, 0, SEEK_SET);
+    if (VSIFWriteL("GIF89a", 1, 6, fp) != 6)
+    {
+        CPLError( CE_Failure, CPLE_AppDefined, 
+                  "Error writing gif file." );
+        return NULL;
+    }
+    
     VSIFCloseL( fp );
 
 /* -------------------------------------------------------------------- */
@@ -711,7 +932,7 @@
         poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, 
 "<CreationOptionList>\n"
 "   <Option name='INTERLACING' type='boolean'/>\n"
-"   <Option name='WORLDFILE' type='boolean'/>\n"
+"   <Option name='WORLDFILE' type='boolean' description='Write out world file'/>\n"
 "</CreationOptionList>\n" );
 
         poDriver->pfnOpen = GIFDataset::Open;
Index: frmts/bmp/bmpdataset.cpp
===================================================================
--- frmts/bmp/bmpdataset.cpp	(revision 11880)
+++ frmts/bmp/bmpdataset.cpp	(working copy)
@@ -220,6 +220,7 @@
     GDALColorTable      *poColorTable;
     double              adfGeoTransform[6];
     int                 bGeoTransformValid;
+    char	       *pszProjection;
 
     char                *pszFilename;
     FILE                *fp;
@@ -241,6 +242,9 @@
 
     CPLErr              GetGeoTransform( double * padfTransform );
     virtual CPLErr      SetGeoTransform( double * );
+
+    virtual const char *GetProjectionRef(void);
+    virtual CPLErr      SetProjection( const char * _pszProjection);
 };
 
 /************************************************************************/
@@ -857,6 +861,7 @@
     poColorTable = NULL;
     memset( &sFileHeader, 0, sizeof(sFileHeader) );
     memset( &sInfoHeader, 0, sizeof(sInfoHeader) );
+    pszProjection = NULL;
 }
 
 /************************************************************************/
@@ -867,6 +872,7 @@
 {
     FlushCache();
 
+    CPLFree( pszProjection );
     if ( pabyColorTable )
         CPLFree( pabyColorTable );
     if ( poColorTable != NULL )
@@ -877,6 +883,30 @@
 }
 
 /************************************************************************/
+/*                          GetProjectionRef()                          */
+/************************************************************************/
+
+const char *BMPDataset::GetProjectionRef()
+
+{
+    return (pszProjection) ? pszProjection : "";
+}
+
+
+/************************************************************************/
+/*                           SetProjection()                            */
+/************************************************************************/
+CPLErr  BMPDataset::SetProjection( const char * _pszProjection)
+{
+    CPLErr              eErr = CE_None;
+
+    CPLFree(pszProjection);
+    pszProjection = CPLStrdup((_pszProjection) ? _pszProjection : "");
+
+    return eErr;
+}
+
+/************************************************************************/
 /*                          GetGeoTransform()                           */
 /************************************************************************/
 
@@ -1187,6 +1217,11 @@
 
     if( !poDS->bGeoTransformValid )
         poDS->bGeoTransformValid =
+            GDALReadWorldFile( poOpenInfo->pszFilename, ".bmw",
+                               poDS->adfGeoTransform );
+
+    if( !poDS->bGeoTransformValid )
+        poDS->bGeoTransformValid =
             GDALReadWorldFile( poOpenInfo->pszFilename, ".bpw",
                                poDS->adfGeoTransform );
 
@@ -1196,6 +1231,12 @@
                                poDS->adfGeoTransform );
 
 /* -------------------------------------------------------------------- */
+/*  Try to read projection file.                                        */
+/* -------------------------------------------------------------------- */
+    CPLFree(poDS->pszProjection);
+    poDS->pszProjection = poDS->ReadProjectionRefFromFile(poOpenInfo->pszFilename);
+
+/* -------------------------------------------------------------------- */
 /*      Check for overviews.                                            */
 /* -------------------------------------------------------------------- */
     poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
Index: gcore/gdal_priv.h
===================================================================
--- gcore/gdal_priv.h	(revision 11880)
+++ gcore/gdal_priv.h	(working copy)
@@ -222,6 +222,8 @@
     virtual const char *GetProjectionRef(void);
     virtual CPLErr SetProjection( const char * );
 
+    char* ReadProjectionRefFromFile(const char* pszFileName);
+
     virtual CPLErr GetGeoTransform( double * );
     virtual CPLErr SetGeoTransform( double * );
 
Index: gcore/gdaldataset.cpp
===================================================================
--- gcore/gdaldataset.cpp	(revision 11880)
+++ gcore/gdaldataset.cpp	(working copy)
@@ -30,6 +30,7 @@
 #include "gdal_priv.h"
 #include "cpl_string.h"
 #include "cpl_multiproc.h"
+#include "ogr_spatialref.h"
 
 CPL_CVSID("$Id$");
 
@@ -562,7 +563,94 @@
     return( "" );
 }
 
+
 /************************************************************************/
+/*                ReadProjectionRefFromFile()                           */
+/************************************************************************/
+
+char* GDALDataset::ReadProjectionRefFromFile(const char* pszFileName)
+{
+/* -------------------------------------------------------------------- */
+/*      Try to read projection file.                                    */
+/* -------------------------------------------------------------------- */
+    char        *pszDirname, *pszBasename;
+    const char  *pszPrjFilename;
+    char        *pszProjection = NULL;
+    char        **papszPrj = NULL;
+    VSIStatBufL   sStatBuf;
+
+    pszDirname = CPLStrdup(CPLGetPath(pszFileName));
+    pszBasename = CPLStrdup(CPLGetBasename(pszFileName));
+
+    pszPrjFilename = CPLFormCIFilename( pszDirname, pszBasename, "prj" );
+    int nRet = VSIStatL( pszPrjFilename, &sStatBuf );
+
+    if( nRet == 0 )
+    {
+        OGRSpatialReference     oSRS;
+        OGRErr                  err;
+
+        papszPrj = CSLLoad( pszPrjFilename );
+        
+        if (papszPrj && *papszPrj)
+        {
+            char** ptr = papszPrj;
+            char* mergedStr = (char*)malloc(1);
+
+            *mergedStr = 0;
+            while(*ptr)
+            {
+              mergedStr = (char*)realloc(mergedStr, strlen(mergedStr) + strlen(*ptr) + 1);
+              strcat(mergedStr, *ptr);
+              ptr++;
+            }
+
+            if (strncmp(mergedStr, "+", 1) == 0)
+            {
+                err = oSRS.importFromProj4 (mergedStr);
+            }
+            else
+            {
+                err = oSRS.SetWellKnownGeogCS (mergedStr);
+                if (err != OGRERR_NONE )
+                {
+                    char* tmp = mergedStr;
+                    err = oSRS.importFromWkt ( &tmp );
+                    if (err != OGRERR_NONE )
+                    {
+                        tmp = mergedStr;
+                        err = oSRS.importFromESRI( &tmp );
+                    }
+                }
+                if (err != OGRERR_NONE)
+                {
+                    err = oSRS.SetFromUserInput(mergedStr);
+                }
+            }
+
+            if (err == OGRERR_NONE )
+            {
+                oSRS.exportToWkt( &pszProjection );
+            }
+            else
+            {
+                CPLError(CE_Warning, CPLE_IllegalArg,
+                        "Unrecognized projection string in file %s", pszPrjFilename );
+            }
+
+            free( mergedStr );
+        }
+
+        CSLDestroy( papszPrj );
+    }
+
+    CPLFree( pszDirname );
+    CPLFree( pszBasename );
+
+    return pszProjection;
+}
+
+/************************************************************************/
 /*                        GDALGetProjectionRef()                        */
 /************************************************************************/
 

