[Rawstudio-dev] [patch] Exif support for saving jpegs

Daniel Falk daniel-rawstudio at mbx.zapto.org
Tue Jun 10 16:01:03 CEST 2008


Hi,

I'd like to submit a patch to bring exif support in, at least for saving 
jpegs.  This new code would use the exiv2 library, and much of the code 
is borrowed from UFRaw.  I'm not a C developer by any means, and this is 
my first time submitting a patch to anything, but please tell me what 
you think.  I welcome any advice and guidance you can give.

Thanks,
Daniel Falk
-------------- next part --------------
Index: src/rawstudio.c
===================================================================
--- src/rawstudio.c	(revision 1866)
+++ src/rawstudio.c	(working copy)
@@ -530,7 +530,7 @@
 			else if (quality < 0)
 				quality = 0;
 
-			rs_jpeg_save(pixbuf, filename, quality, rs_cms_get_profile_filename(cms, PROFILE_EXPORT));
+			rs_jpeg_save(pixbuf, filename, quality, rs_cms_get_profile_filename(cms, PROFILE_EXPORT), photo);
 			g_object_unref(pixbuf);
 			break;
 		case FILETYPE_PNG:
Index: src/rs-exiv.cc
===================================================================
--- src/rs-exiv.cc	(revision 0)
+++ src/rs-exiv.cc	(revision 0)
@@ -0,0 +1,104 @@
+extern "C" {
+#include "rs-exiv.hh"
+}
+
+#include <exiv2/image.hpp>
+#include <exiv2/exif.hpp>
+#include <sstream>
+#include <cassert>
+
+#define MAX_BYTES_IN_MARKER  65533  /* maximum data len of a JPEG marker */
+
+void delete_irrelevant_keys(Exiv2::ExifData &exifData);
+
+
+/*
+ **Code borrowed and heavily and adapted from UFRaw**
+ 
+ read_exif()
+ ------------
+ Returns true if the exif information was retrieved successfully, false
+ otherwise.
+ * filename specifies the path to the raw file containing the exif data
+ * out_exif_buffer is an output parameter and returns a pointer to the exif 
+ buffer
+ * out_exif_buffer_len is also an output parameter that specifies the length of 
+ that buffer.
+ */
+extern "C" gboolean read_exif(
+							  const gchar *filename
+							  , guchar **out_exif_buffer
+							  , guint *out_exif_buffer_len)
+{
+	Exiv2::Image::AutoPtr image;
+	image = Exiv2::ImageFactory::open(filename);
+
+	assert(image.get() != 0);
+    image->readMetadata();
+
+    Exiv2::ExifData &exifData = image->exifData();
+
+	if (exifData.empty())
+	{
+		std::string error(filename);
+		error += ": No Exif data found in the file";
+		throw Exiv2::Error(1, error);
+		return FALSE;
+	}
+	
+    /* Reset orientation tag since rawstudio already rotates the image (right?) */
+    Exiv2::ExifData::iterator pos;
+	
+	pos = exifData.findKey(Exiv2::ExifKey("Exif.Image.Orientation"));
+	
+	if ( pos != exifData.end() )
+		pos->setValue("1"); /* 1 = Normal orientation */
+
+	//delete keys that aren't pertinent to the output jpeg file
+	delete_irrelevant_keys(exifData);
+
+
+	Exiv2::DataBuf buf(exifData.copy());
+	
+    const unsigned char ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
+	
+    // If buffer too big for JPEG, try deleting some stuff.
+
+    if ( buf.size_ + sizeof(ExifHeader) > MAX_BYTES_IN_MARKER )
+	{
+		if ( (pos = exifData.findKey(Exiv2::ExifKey("Exif.Photo.MakerNote"))) != exifData.end() )
+		{
+			exifData.erase(pos);
+			buf = exifData.copy();
+		}
+    }
+
+	if ( buf.size_ + sizeof(ExifHeader) > MAX_BYTES_IN_MARKER )
+	{
+		exifData.eraseThumbnail();
+		buf = exifData.copy();
+    }
+    
+	*out_exif_buffer_len = buf.size_ + sizeof(ExifHeader);
+	*out_exif_buffer = g_new(unsigned char, *out_exif_buffer_len);
+	memcpy(*out_exif_buffer, ExifHeader, sizeof(ExifHeader));
+    memcpy(*out_exif_buffer + sizeof(ExifHeader), buf.pData_, buf.size_);
+	
+	return TRUE;
+}
+
+void delete_irrelevant_keys(Exiv2::ExifData &exifData)
+{
+    Exiv2::ExifData::iterator pos;
+
+    // Delete original TIFF data, which is irrelevant
+    if ( (pos=exifData.findKey(Exiv2::ExifKey("Exif.Image.StripOffsets")))
+	    != exifData.end() )
+	exifData.erase(pos);
+    if ( (pos=exifData.findKey(Exiv2::ExifKey("Exif.Image.RowsPerStrip")))
+	    != exifData.end() )
+	exifData.erase(pos);
+    if ( (pos=exifData.findKey(Exiv2::ExifKey("Exif.Image.StripByteCounts")))
+	    != exifData.end() )
+	exifData.erase(pos);
+}
Index: src/rs-jpeg.c
===================================================================
--- src/rs-jpeg.c	(revision 1866)
+++ src/rs-jpeg.c	(working copy)
@@ -27,10 +27,13 @@
 #include "rawstudio.h"
 #include "rs-image.h"
 #include "rs-jpeg.h"
+#include "rs-exiv.hh"
 
+
 /* This function is an almost verbatim copy from little cms. Thanks Marti, you rock! */
 
 #define ICC_MARKER  (JPEG_APP0 + 2) /* JPEG marker code for ICC */
+#define EXIF_MARKER  (JPEG_APP0 + 1) /* JPEG marker code for EXIF */
 #define ICC_OVERHEAD_LEN  14        /* size of non-profile data in APP2 */
 #define MAX_BYTES_IN_MARKER  65533  /* maximum data len of a JPEG marker */
 #define MAX_DATA_BYTES_IN_MARKER  (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)
@@ -77,7 +80,7 @@
 
 gboolean
 rs_jpeg_save(GdkPixbuf *pixbuf, const gchar *filename, const gint quality,
-	const gchar *profile_filename)
+	const gchar *profile_filename, RS_PHOTO *photo)
 {
 	struct jpeg_compress_struct cinfo;
 	struct jpeg_error_mgr jerr;
@@ -115,14 +118,32 @@
 				g_free(buffer);
 			}
 	}
+	
+	
+	//write exif data
+	guchar *exif_buffer = NULL;
+	guint exif_buffer_len = 0;
+	
+	if(read_exif(photo->filename, &exif_buffer, (guint *)&exif_buffer_len))
+	{
+	    if (exif_buffer_len <= MAX_BYTES_IN_MARKER)
+		{
+			jpeg_write_marker(&cinfo, EXIF_MARKER, exif_buffer, exif_buffer_len);
+			g_free(exif_buffer);
+		}
+	}
+	
+	
 	while (cinfo.next_scanline < cinfo.image_height)
 	{
 		row_pointer[0] = GET_PIXBUF_PIXEL(pixbuf, 0, cinfo.next_scanline);
 		if (jpeg_write_scanlines(&cinfo, row_pointer, 1) != 1)
 			break;
 	}
+	
 	jpeg_finish_compress(&cinfo);
 	fclose(outfile);
 	jpeg_destroy_compress(&cinfo);
 	return(TRUE);
 }
+
Index: src/rs-jpeg.h
===================================================================
--- src/rs-jpeg.h	(revision 1866)
+++ src/rs-jpeg.h	(working copy)
@@ -21,6 +21,6 @@
 #define RS_JPEG_H
 
 extern gboolean rs_jpeg_save(GdkPixbuf *pixbuf, const gchar *filename,
-	const gint quality, const gchar *profile_filename);
+	const gint quality, const gchar *profile_filename, RS_PHOTO *photo);
 
 #endif
Index: src/rs-exiv.hh
===================================================================
--- src/rs-exiv.hh	(revision 0)
+++ src/rs-exiv.hh	(revision 0)
@@ -0,0 +1,3 @@
+#include <glib.h>
+
+gboolean read_exif(const gchar *filename, guchar **exif_buffer, guint *exif_buffer_len);


More information about the Rawstudio-dev mailing list