Index: io-jpeg.c
===================================================================
--- io-jpeg.c	(revision 17616)
+++ io-jpeg.c	(working copy)
@@ -277,11 +277,135 @@
 	}
 }
 
+
+const char leth[]  = {0x49, 0x49, 0x2a, 0x00}; // Little endian TIFF header
+const char beth[]  = {0x4d, 0x4d, 0x00, 0x2a}; // Big endian TIFF header
+const char types[] = {0x00, 0x01, 0x01, 0x02, 0x04, 0x08, 0x00, 0x08, 0x00, 0x04, 0x08}; // size in bytes for EXIF types
+ 
+#define DE_ENDIAN16(val) endian == G_BIG_ENDIAN ? GUINT16_FROM_BE(val) : GUINT16_FROM_LE(val)
+#define DE_ENDIAN32(val) endian == G_BIG_ENDIAN ? GUINT32_FROM_BE(val) : GUINT32_FROM_LE(val)
+ 
+#define ENDIAN16_IT(val) endian == G_BIG_ENDIAN ? GUINT16_TO_BE(val) : GUINT16_TO_LE(val)
+#define ENDIAN32_IT(val) endian == G_BIG_ENDIAN ? GUINT32_TO_BE(val) : GUINT32_TO_LE(val)
+ 
+#define EXIF_JPEG_MARKER   JPEG_APP0+1
+#define EXIF_IDENT_STRING  "Exif\000\000"
+
+static gint 
+get_orientation (j_decompress_ptr cinfo)
+{
+	/* This function looks through upto the first 64 Kb of data in the JPEG file to 
+	   determine if an EXIF Orientation tag is present and if so return its value (1-8). 
+	   If no EXIF Orientation tag is found 0 (zero) is returned. This routine expects
+	   the file to contain a true EXIF JPEG image with an EXIF APP1 following immediatelly
+	   after the SOI header. Orientation tags in malpositioned EXIF APP1 sections may not 
+	   be recognized. */
+
+ 	guint   i;              // index into working buffer
+ 	guint   tag;            // endianed version of 'etag' in call
+	guint   ret;            // Return value
+ 	guint   offset;        	// de-endianed offset in various situations
+ 	guint   tags;           // number of tags in current ifd
+ 	guint   type;           // de-endianed type of tag used as index into types[]
+ 	guint   count;          // de-endianed count of elements in a tag
+        guint   tiff = 0;   	// offset to active tiff header
+        guint   endian = 0;   	// detected endian of data
+ 	guint   readsize = 0;   // number of read bytes from file into buffer
+
+	jpeg_saved_marker_ptr exif_marker;  // Location of the Exif APP1 marker
+	jpeg_saved_marker_ptr cmarker;	    // Location to check for Exif APP1 marker
+
+	/* check for Exif marker */
+	exif_marker = NULL;
+	cmarker = cinfo->marker_list;
+	while (cmarker) {
+		if (cmarker->marker == EXIF_JPEG_MARKER) {
+			/* Do we need to check for both Endian styles here? */
+			if (!memcmp (cmarker->data, EXIF_IDENT_STRING, 6)) {
+				exif_marker = cmarker;
+				}
+			}
+		cmarker=cmarker->next;
+	}
+	   
+	if (exif_marker == NULL)
+		return 0;
+
+        // Check for TIFF header and catch endianess
+ 	i = 0;
+
+	// Should be in the first 32 bytes if it is a true EXIF JPEG 
+ 	while (i < 32){
+ 
+ 		// Little endian TIFF header
+ 		if (bcmp (&exif_marker->data[i], leth, 4) == 0){ 
+ 			endian = G_LITTLE_ENDIAN;
+                }
+ 
+ 		// Big endian TIFF header
+ 		else if (bcmp (&exif_marker->data[i], beth, 4) == 0){ 
+ 			endian = G_BIG_ENDIAN;
+                }
+ 
+ 		// Keep looking through buffer
+ 		else {
+ 			i++;
+ 			continue;
+ 		}
+ 		// We have found either big or little endian TIFF header
+ 		tiff = i;
+ 		break;
+        }
+ 
+ 	// So did we find a TIFF header or did we just hit end of buffer?
+ 	if (tiff == 0) return 0;
+ 
+        // Endian the tag to be able to look for it easier
+        tag     = ENDIAN16_IT(0x112);
+ 
+        // Read out the offset
+        offset  = DE_ENDIAN32(*((unsigned long*)(&exif_marker->data[i] + 4)));
+ 	i       = i + offset;
+ 
+        // Check through IFD0 
+ 	while ((i < readsize - 2)){
+ 	  
+ 		tags    = DE_ENDIAN16(*((unsigned short*)(&exif_marker->data[i])));
+ 		i       = i + 2;
+ 
+ 		// Check this IFD for tags of interest
+ 		while (tags-- && i < readsize - 12 ){
+ 			type   = DE_ENDIAN16(*((unsigned short*)(&exif_marker->data[i + 2])));
+ 			count  = DE_ENDIAN32(*((unsigned long*) (&exif_marker->data[i + 4])));
+ 
+ 			// Orientation tag?
+ 			if (bcmp (&exif_marker->data[i], (char *)&tag, 2) == 0){ 
+ 
+				// Read a orientation TAG value
+
+				// Check that type and count fields are ok.
+				if (type != 3 || count != 1) return 0;
+
+				// Return value
+				ret = DE_ENDIAN16(*((unsigned short*)(&exif_marker->data[i + 8])));
+				return ret <= 8 ? ret : 0;
+ 			}
+ 					
+ 			i = i + 12;
+ 		}
+		break; // We are just interested in IFD0 so let's bail out
+        }
+ 	return 0; // No EXIF Orientation tag found
+}
+
+
 /* Shared library entry point */
 static GdkPixbuf *
 gdk_pixbuf__jpeg_image_load (FILE *f, GError **error)
 {
-	gint i;
+	gint   i;
+	int     is_otag;
+	char   otag_str[5];
 	GdkPixbuf * volatile pixbuf = NULL;
 	guchar *dptr;
 	guchar *lines[4]; /* Used to expand rows, via rec_outbuf_height, 
@@ -332,7 +456,12 @@
 	src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
 	src->pub.next_input_byte = NULL; /* until buffer loaded */
 
+	jpeg_save_markers (&cinfo, EXIF_JPEG_MARKER, 0xffff);
 	jpeg_read_header (&cinfo, TRUE);
+
+	/* check for orientation tag */
+	is_otag = get_orientation (&cinfo);
+	
 	jpeg_start_decompress (&cinfo);
 	cinfo.do_fancy_upsampling = FALSE;
 	cinfo.do_block_smoothing = FALSE;
@@ -357,6 +486,13 @@
 		return NULL;
 	}
 
+	/* if orientation tag was found set an option to remember its value */
+	if (is_otag) {
+		snprintf(otag_str, sizeof(otag_str), "%d", is_otag);
+		gdk_pixbuf_set_option(pixbuf, "orientation", otag_str);
+	}
+
+
 	dptr = pixbuf->pixels;
 
 	/* decompress all the lines, a few at a time */
@@ -623,14 +759,16 @@
                                        GError **error)
 {
 	JpegProgContext *context = (JpegProgContext *)data;
-	struct jpeg_decompress_struct *cinfo;
-	my_src_ptr  src;
-	guint       num_left, num_copy;
-	guint       last_bytes_left;
-	guint       spinguard;
-	gboolean    first;
-	const guchar *bufhd;
-	gint        width, height;
+	struct           jpeg_decompress_struct *cinfo;
+	my_src_ptr       src;
+	guint            num_left, num_copy;
+	guint            last_bytes_left;
+	guint            spinguard;
+	gboolean         first;
+	const guchar    *bufhd;
+	gint             width, height;
+        int              is_otag;
+	char             otag_str[5];
 
 	g_return_val_if_fail (context != NULL, FALSE);
 	g_return_val_if_fail (buf != NULL, FALSE);
@@ -704,7 +842,8 @@
 		/* try to load jpeg header */
 		if (!context->got_header) {
 			int rc;
-			
+		
+			jpeg_save_markers (cinfo, EXIF_JPEG_MARKER, 0xffff);
 			rc = jpeg_read_header (cinfo, TRUE);
 			context->src_initialized = TRUE;
 			
@@ -712,7 +851,10 @@
 				continue;
 			
 			context->got_header = TRUE;
-			
+
+			/* check for orientation tag */
+			is_otag = get_orientation (cinfo);
+		
 			width = cinfo->image_width;
 			height = cinfo->image_height;
 			if (context->size_func) {
@@ -748,7 +890,13 @@
                                              _("Couldn't allocate memory for loading JPEG file"));
                                 return FALSE;
 			}
-			
+		
+		        /* if orientation tag was found set an option to remember its value */
+		        if (is_otag) {
+                		snprintf(otag_str, sizeof(otag_str), "%d", is_otag);
+		                gdk_pixbuf_set_option (context->pixbuf, "orientation", otag_str);
+		        }
+
 			/* Use pixbuf buffer to store decompressed data */
 			context->dptr = context->pixbuf->pixels;
 			
@@ -757,7 +905,7 @@
 				(* context->prepared_func) (context->pixbuf,
 							    NULL,
 							    context->user_data);
-			
+
 		} else if (!context->did_prescan) {
 			int rc;			
 			
