diff --git a/Graphics/ExifTags.hs b/Graphics/ExifTags.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/ExifTags.hs
@@ -0,0 +1,190 @@
+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
+module Graphics.ExifTags where
+
+import Data.Word
+import Text.Printf (printf)
+import qualified Data.Map as Map
+import Data.Map (Map)
+
+import Graphics.Types
+import Graphics.PrettyPrinters
+
+exifSubIfdTag :: String -> Word16 -> (ExifValue -> String)-> ExifTag
+exifSubIfdTag d = ExifTag ExifSubIFD (Just d)
+
+exifIfd0Tag :: String -> Word16 -> (ExifValue -> String) -> ExifTag
+exifIfd0Tag d = ExifTag IFD0 (Just d)
+
+exifGpsTag :: String -> Word16 -> (ExifValue -> String) -> ExifTag
+exifGpsTag d = ExifTag GpsSubIFD (Just d)
+
+withFormat :: String -> ExifValue -> String
+withFormat fmt = printf fmt . show
+
+asFpWithFormat :: String -> ExifValue -> String
+asFpWithFormat fmt = printf fmt . formatAsFloatingPoint 2
+
+exposureTime		= exifSubIfdTag "exposureTime" 0x829a $ withFormat "%s sec."
+fnumber			= exifSubIfdTag "fnumber" 0x829d $ withFormat "f/%s"
+exposureProgram		= exifSubIfdTag "exposureProgram" 0x8822 ppExposureProgram
+spectralSensitivity	= exifSubIfdTag "spectralSensitivity" 0x8824 show
+isoSpeedRatings		= exifSubIfdTag "isoSpeedRatings" 0x8827 show
+oecf			= exifSubIfdTag "OECF" 0x8828 show
+exifVersion		= exifSubIfdTag "exifVersion" 0x9000 ppExifVersion
+dateTimeOriginal	= exifSubIfdTag "dateTimeOriginal" 0x9003 show
+dateTimeDigitized	= exifSubIfdTag "dateTimeDigitized" 0x9004 show
+componentConfiguration	= exifSubIfdTag "componentConfiguration" 0x9101 ppComponentConfiguration
+compressedBitsPerPixel	= exifSubIfdTag "compressedBitsPerPixel" 0x9102 (formatAsFloatingPoint 2)
+shutterSpeedValue	= exifSubIfdTag "shutterSpeedValue" 0x9201 $ withFormat "%s sec."
+apertureValue		= exifSubIfdTag "apertureValue" 0x9202 show
+brightnessValue		= exifSubIfdTag "brightnessValue" 0x9203 show
+exposureBiasValue	= exifSubIfdTag "exposureBiasValue" 0x9204 $ asFpWithFormat "%s EV"
+maxApertureValue	= exifSubIfdTag "maxApertureValue" 0x9205 $ withFormat "f/%s"
+subjectDistance		= exifSubIfdTag "subjectDistance" 0x9206 show
+meteringMode		= exifSubIfdTag "meteringMode" 0x9207 ppMeteringMode
+lightSource		= exifSubIfdTag "lightSource" 0x9208 ppLightSource
+flash			= exifSubIfdTag "flash" 0x9209 ppFlash
+focalLength		= exifSubIfdTag "focalLength" 0x920a $ asFpWithFormat "%s mm"
+subjectArea		= exifSubIfdTag "subjectArea" 0x9214 show
+makerNote		= exifSubIfdTag "makerNote" 0x927c show
+userComment		= exifSubIfdTag "userComment" 0x9286 show
+subSecTime		= exifSubIfdTag "subSecTime" 0x9290 show
+subSecTimeOriginal	= exifSubIfdTag "subSecTimeOriginal" 0x9291 show
+subSecTimeDigitized	= exifSubIfdTag "subSecTimeDigitized" 0x9292 show
+flashPixVersion		= exifSubIfdTag "flashPixVersion" 0xa000 ppFlashPixVersion
+colorSpace		= exifSubIfdTag "colorSpace" 0xa001 ppColorSpace
+exifImageWidth		= exifSubIfdTag "exifImageWidth" 0xa002 show
+exifImageHeight		= exifSubIfdTag "exifImageHeight" 0xa003 show
+relatedSoundFile	= exifSubIfdTag "relatedSoundFile" 0xa004 show
+flashEnergy		= exifSubIfdTag "flashEnergy" 0xa20b show
+spatialFrequencyResponse= exifSubIfdTag "spatialFrequencyResponse" 0xa20c show
+focalPlaneXResolution	= exifSubIfdTag "focalPlaneXResolution" 0xa20e show
+focalPlaneYResolution	= exifSubIfdTag "focalPlaneYResolution" 0xa20f show
+focalPlaneResolutionUnit= exifSubIfdTag "focalPlaneResolutionUnit" 0xa210 ppFocalPlaneResolutionUnit
+subjectLocation		= exifSubIfdTag "subjectLocation" 0xa214 show
+exposureIndex		= exifSubIfdTag "exposureIndex" 0xa215 show
+sensingMethod		= exifSubIfdTag "sensingMethod" 0xa217 ppSensingMethod
+fileSource		= exifSubIfdTag "fileSource" 0xa300 show
+sceneType		= exifSubIfdTag "sceneType" 0xa301 ppSceneType
+cfaPattern		= exifSubIfdTag "cfaPattern" 0xa302 show
+customRendered		= exifSubIfdTag "customRendered" 0xa401 ppCustomRendered
+exposureMode		= exifSubIfdTag "exposureMode" 0xa402 ppExposureMode
+whiteBalance		= exifSubIfdTag "whiteBalance" 0xa403 ppWhiteBalance
+digitalZoomRatio	= exifSubIfdTag "digitalZoomRatio" 0xa404 show
+focalLengthIn35mmFilm	= exifSubIfdTag "focalLengthIn35mmFilm" 0xa405 $ asFpWithFormat "%s mm"
+sceneCaptureType	= exifSubIfdTag "sceneCaptureType" 0xa406 ppSceneCaptureType
+gainControl		= exifSubIfdTag "gainControl" 0xa407 ppGainControl
+contrast		= exifSubIfdTag "contrast" 0xa408 ppContrastSharpness
+saturation		= exifSubIfdTag "saturation" 0xa409 ppSaturation
+sharpness		= exifSubIfdTag "sharpness" 0xa40a ppContrastSharpness
+deviceSettingDescription= exifSubIfdTag "deviceSettingDescription" 0xa40b show
+subjectDistanceRange	= exifSubIfdTag "subjectDistanceRange" 0xa40c ppSubjectDistanceRange
+imageUniqueId		= exifSubIfdTag "imageUniqueId" 0xa420 show
+exifInteroperabilityOffset=exifSubIfdTag "exifInteroperabilityOffset" 0xa005 show
+
+imageDescription	= exifIfd0Tag "imageDescription" 0x010e show
+make			= exifIfd0Tag "make" 0x010f show
+model			= exifIfd0Tag "model" 0x0110 show
+orientation		= exifIfd0Tag "orientation" 0x0112 ppOrientation
+xResolution		= exifIfd0Tag "xResolution" 0x011a show
+yResolution		= exifIfd0Tag "xResolution" 0x011b show
+resolutionUnit		= exifIfd0Tag "resolutionUnit" 0x0128 ppResolutionUnit
+software		= exifIfd0Tag "software" 0x0131 show
+dateTime		= exifIfd0Tag "dateTime" 0x0132 show
+artist			= exifIfd0Tag "artist" 0x013b show
+whitePoint		= exifIfd0Tag "whitePoint" 0x013e show
+primaryChromaticities	= exifIfd0Tag "primaryChromaticities" 0x013f show
+yCbCrCoefficients	= exifIfd0Tag "yCbCrCoefficients" 0x0211 show
+yCbCrPositioning	= exifIfd0Tag "yCbCrPositioning" 0x0213 ppYCbCrPositioning
+referenceBlackWhite	= exifIfd0Tag "referenceBlackWhite" 0x0214 show
+copyright		= exifIfd0Tag "copyright" 0x8298 show
+exifIfdOffset		= exifIfd0Tag "exifIfdOffset" 0x8769 show
+gpsTagOffset		= exifIfd0Tag "gpsTagOffset" 0x8825 show
+printImageMatching	= exifIfd0Tag "printImageMatching" 0xc4a5 show
+
+gpsVersionID		= exifGpsTag "gpsVersionID" 0x0000 show
+gpsLatitudeRef		= exifGpsTag "gpsLatitudeRef" 0x0001 show
+gpsLatitude		= exifGpsTag "gpsLatitude" 0x0002 ppGpsLongLat
+gpsLongitudeRef		= exifGpsTag "gpsLongitudeRef" 0x0003 show
+gpsLongitude		= exifGpsTag "gpsLongitude" 0x0004 ppGpsLongLat
+gpsAltitudeRef		= exifGpsTag "gpsAltitudeRef" 0x0005 ppGpsAltitudeRef
+gpsAltitude		= exifGpsTag "gpsAltitude" 0x0006 show
+gpsTimeStamp		= exifGpsTag "gpsTimeStamp" 0x0007 show
+gpsSatellites		= exifGpsTag "gpsSatellites" 0x0008 show
+gpsStatus		= exifGpsTag "gpsStatus" 0x0009 show
+gpsMeasureMode		= exifGpsTag "gpsMeasureMode" 0x000a show
+gpsDop			= exifGpsTag "gpsDop" 0x000b show
+gpsSpeedRef		= exifGpsTag "gpsSpeedRef" 0x000c show
+gpsSpeed		= exifGpsTag "gpsSpeed" 0x000d show
+gpsTrackRef		= exifGpsTag "gpsTrackRef" 0x000e show
+gpsTrack		= exifGpsTag "gpsTrack" 0x000f show
+gpsImgDirectionRef	= exifGpsTag "gpsImgDirectionRef" 0x0010 show
+gpsImgDirection		= exifGpsTag "gpsImgDirection" 0x0011 show
+gpsMapDatum		= exifGpsTag "gpsMapDatum" 0x0012 show
+gpsDestLatitudeRef	= exifGpsTag "gpsDestLatitudeRef" 0x0013 show
+gpsDestLatitude		= exifGpsTag "gpsDestLatitude" 0x0014 show
+gpsDestLongitudeRef	= exifGpsTag "gpsDestLongitudeRef" 0x0015 show
+gpsDestLongitude	= exifGpsTag "gpsDestLongitude" 0x0016 show
+gpsDestBearingRef	= exifGpsTag "gpsDestBearingRef" 0x0017 show
+gpsDestBearing		= exifGpsTag "gpsDestBearing" 0x0018 show
+gpsDestDistanceRef	= exifGpsTag "gpsDestDistanceRef" 0x0019 show
+gpsDestDistance		= exifGpsTag "gpsDestDistance" 0x001a show
+gpsProcessingMethod	= exifGpsTag "gpsProcessingMethod" 0x001b show
+gpsAreaInformation	= exifGpsTag "gpsAreaInformation" 0x001c show
+gpsDateStamp		= exifGpsTag "gpsDateStamp" 0x001d show
+gpsDifferential		= exifGpsTag "gpsDifferential" 0x001e show
+
+allExifTags :: [ExifTag]
+allExifTags = [exposureTime, fnumber, exposureProgram, isoSpeedRatings,
+	exifVersion, dateTimeOriginal, dateTimeDigitized, componentConfiguration,
+	compressedBitsPerPixel, shutterSpeedValue, apertureValue, brightnessValue,
+	exposureBiasValue, maxApertureValue, subjectDistance, meteringMode,
+	lightSource, flash, focalLength, makerNote, userComment,
+	exifImageWidth, exifImageHeight, relatedSoundFile, focalPlaneXResolution,
+	focalPlaneYResolution, focalPlaneResolutionUnit, sensingMethod, fileSource,
+	sceneType, orientation, make, model, software, copyright,
+	spectralSensitivity, oecf, subjectArea, subSecTime, subSecTimeOriginal,
+	subSecTimeDigitized, flashPixVersion, colorSpace, flashEnergy,
+	spatialFrequencyResponse, subjectLocation, exposureIndex, cfaPattern,
+	customRendered, exposureMode, whiteBalance, digitalZoomRatio,
+	focalLengthIn35mmFilm, sceneCaptureType, gainControl, contrast,
+	saturation, sharpness, deviceSettingDescription, subjectDistanceRange,
+	imageUniqueId, exifInteroperabilityOffset, imageDescription,
+	xResolution, yResolution, resolutionUnit, dateTime, whitePoint,
+	primaryChromaticities, yCbCrPositioning, yCbCrCoefficients, referenceBlackWhite,
+	exifIfdOffset, printImageMatching, gpsTagOffset, artist,
+	gpsVersionID, gpsLatitudeRef, gpsLatitude, gpsLongitudeRef, gpsLongitude,
+	gpsAltitudeRef, gpsAltitude, gpsTimeStamp, gpsSatellites, gpsStatus,
+	gpsMeasureMode, gpsDop, gpsSpeedRef, gpsSpeed, gpsTrackRef, gpsTrack,
+	gpsImgDirectionRef, gpsImgDirection, gpsMapDatum, gpsDestLatitudeRef,
+	gpsDestLatitude, gpsDestLongitudeRef, gpsDestLongitude, gpsDestBearingRef,
+	gpsDestBearing, gpsDestDistanceRef, gpsDestDistance, gpsProcessingMethod,
+	gpsAreaInformation, gpsDateStamp, gpsDifferential]
+
+-- | Extract the GPS latitude and longitude where the picture was taken
+-- (if it is present in the EXIF)
+getGpsLatitudeLongitude :: Map ExifTag ExifValue -> Maybe (Double, Double)
+getGpsLatitudeLongitude exifData = do
+	(ExifText latRef) <- Map.lookup gpsLatitudeRef exifData
+	latDec <- Map.lookup gpsLatitude exifData >>= gpsDecodeToDecimalDegrees 
+	let signedLatDec = case latRef of
+		"S" -> -latDec
+		_ -> latDec
+	(ExifText longRef) <- Map.lookup gpsLongitudeRef exifData
+	longDec <- Map.lookup gpsLongitude exifData >>= gpsDecodeToDecimalDegrees 
+	let signedLongDec = case longRef of
+		"W" -> -longDec
+		_ -> longDec
+	return (signedLatDec, signedLongDec)
+
+gpsDecodeToDecimalDegrees :: ExifValue -> Maybe Double
+gpsDecodeToDecimalDegrees (ExifRationalList intPairs) = case fmap intPairToFloating intPairs of
+			(degrees:minutes:seconds:[]) -> Just $ degrees + minutes / 60 + seconds / 3600
+			_ -> Nothing
+	where
+		intPairToFloating (n, d) = fromIntegral n / fromIntegral d
+gpsDecodeToDecimalDegrees _ = Nothing
+
+ppGpsLongLat :: ExifValue -> String
+ppGpsLongLat v = maybe "invalid GPS data" fmt $ gpsDecodeToDecimalDegrees v
+	where fmt = printf "%.6f"
diff --git a/Graphics/HsExif.hs b/Graphics/HsExif.hs
--- a/Graphics/HsExif.hs
+++ b/Graphics/HsExif.hs
@@ -14,6 +14,7 @@
 	ImageOrientation(..),
 	RotationDirection(..),
 	getGpsLatitudeLongitude,
+	wasFlashFired,
 	formatAsFloatingPoint,
 
 	-- * The ExifValue type
@@ -142,7 +143,6 @@
 import Data.Binary.Get
 import Data.Binary.Put
 import qualified Data.ByteString.Lazy as B
-import qualified Data.ByteString as BS
 import Control.Monad (liftM, unless, replicateM)
 import qualified Data.ByteString.Char8 as Char8
 import Data.Word
@@ -154,49 +154,10 @@
 import Data.Map (Map)
 import Data.Time.LocalTime
 import Data.Time.Calendar
-import Numeric (showHex)
-import Text.Printf
-
--- | An exif value.
--- 
--- If you want a string describing the contents
--- of the value, simply use 'show'.
-data ExifValue = ExifNumber !Int
-	-- ^ An exif number. Originally it could have been short, int, signed or not.
-	| ExifText !String
-	-- ^ ASCII text.
-	| ExifRational !Int !Int
-	-- ^ A rational number (numerator, denominator).
-	-- Sometimes we're used to it as rational (exposition time: 1/160),
-	-- sometimes as float (exposure compensation, we rather think -0.75)
-	-- 'show' will display it as 1/160.
-	| ExifNumberList ![Int]
-	-- ^ List of numbers. Originally they could have been short, int, signed or not.
-	| ExifRationalList ![(Int,Int)]
-	-- ^ A list of rational numbers (numerator, denominator).
-	-- Sometimes we're used to it as rational (exposition time: 1/160),
-	-- sometimes as float (exposure compensation, we rather think -0.75)
-	-- 'show' will display it as 1/160.
-	| ExifUndefined !BS.ByteString
-	-- ^ The undefined type in EXIF means that the contents are not
-	-- specified and up to the manufacturer. In effect it's exactly
-	-- a bytestring. Sometimes it's text with ASCII or UNICODE at the
-	-- beginning, often it's binary in nature.
-	| ExifUnknown !Word16 !Int !Int
-	-- ^ Unknown exif value type. All EXIF 2.3 types are
-	-- supported, it could be a newer file.
-	-- The parameters are type, count then value
-	deriving Eq
+import Data.Bits ((.&.))
 
-instance Show ExifValue where
-	show (ExifNumber v) = show v
-	show (ExifText v) = v
-	show (ExifRational n d) = show n ++ "/" ++ show d
-	show (ExifUnknown t c v) = show "Unknown exif type. Type: " ++ show t 
-		++ " count: " ++ show c ++ " value: " ++ show v
-	show (ExifNumberList l) = show l
-	show (ExifRationalList l) = show l
-	show (ExifUndefined bs) = show bs
+import Graphics.Types (ExifValue(..), ExifTag(..), TagLocation(..), formatAsFloatingPoint)
+import Graphics.ExifTags
 
 -- see http://www.media.mit.edu/pia/Research/deepview/exif.html
 -- and http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf
@@ -326,187 +287,9 @@
 			entryContents = value
 		}
 
--- | Location of the tag in the JPG file structure.
--- Normally you don't need to fiddle with this,
--- except maybe if the library doesn't know the particular
--- exif tag you're interested in.
--- Rather check the list of supported exif tags, like
--- 'exposureTime' and so on.
-data TagLocation = ExifSubIFD | IFD0 | GpsSubIFD
-	deriving (Show, Eq, Ord)
-
--- | An exif tag. Normally you don't need to fiddle with this,
--- except maybe if the library doesn't know the particular
--- exif tag you're interested in.
--- Rather check the list of supported exif tags, like
--- 'exposureTime' and so on.
-data ExifTag = ExifTag
-	{
-		tagLocation :: TagLocation,
-		-- ^ In which part of the JPEG file the exif tag was found
-		tagDesc :: Maybe String,
-		-- ^ Description of the exif tag (exposureTime, fnumber...) or if unknown, Nothing.
-		-- This should be purely for debugging purposes, to compare tags use == on ExifTag
-		-- or compare the tagKey.
-		tagKey :: Word16
-		-- ^ Exif tag key, the number uniquely identifying this tag.
-	}
-	deriving (Eq, Ord)
-
-instance Show ExifTag where
-	show (ExifTag _ (Just d) _) = d
-	show (ExifTag l _ v) = "Unknown tag, location: " ++ show l
-		++ ", value: 0x" ++ showHex v ""
-
-exifSubIfdTag :: String -> Word16 -> ExifTag
-exifSubIfdTag d = ExifTag ExifSubIFD (Just d)
-
-exifIfd0Tag :: String -> Word16 -> ExifTag
-exifIfd0Tag d = ExifTag IFD0 (Just d)
-
-exifGpsTag :: String -> Word16 -> ExifTag
-exifGpsTag d = ExifTag GpsSubIFD (Just d)
-
-exposureTime		= exifSubIfdTag "exposureTime" 0x829a
-fnumber			= exifSubIfdTag "fnumber" 0x829d
-exposureProgram		= exifSubIfdTag "exposureProgram" 0x8822
-spectralSensitivity	= exifSubIfdTag "spectralSensitivity" 0x8824
-isoSpeedRatings		= exifSubIfdTag "isoSpeedRatings" 0x8827
-oecf			= exifSubIfdTag "OECF" 0x8828
-exifVersion		= exifSubIfdTag "exifVersion" 0x9000
-dateTimeOriginal	= exifSubIfdTag "dateTimeOriginal" 0x9003
-dateTimeDigitized	= exifSubIfdTag "dateTimeDigitized" 0x9004
-componentConfiguration	= exifSubIfdTag "componentConfiguration" 0x9101
-compressedBitsPerPixel	= exifSubIfdTag "compressedBitsPerPixel" 0x9102
-shutterSpeedValue	= exifSubIfdTag "shutterSpeedValue" 0x9201
-apertureValue		= exifSubIfdTag "apertureValue" 0x9202
-brightnessValue		= exifSubIfdTag "brightnessValue" 0x9203
-exposureBiasValue	= exifSubIfdTag "exposureBiasValue" 0x9204
-maxApertureValue	= exifSubIfdTag "maxApertureValue" 0x9205
-subjectDistance		= exifSubIfdTag "subjectDistance" 0x9206
-meteringMode		= exifSubIfdTag "meteringMode" 0x9207
-lightSource		= exifSubIfdTag "lightSource" 0x9208
-flash			= exifSubIfdTag "flash" 0x9209
-focalLength		= exifSubIfdTag "focalLength" 0x920a
-subjectArea		= exifSubIfdTag "subjectArea" 0x9214
-makerNote		= exifSubIfdTag "makerNote" 0x927c
-userComment		= exifSubIfdTag "userComment" 0x9286
-subSecTime		= exifSubIfdTag "subSecTime" 0x9290
-subSecTimeOriginal	= exifSubIfdTag "subSecTimeOriginal" 0x9291
-subSecTimeDigitized	= exifSubIfdTag "subSecTimeDigitized" 0x9292
-flashPixVersion		= exifSubIfdTag "flashPixVersion" 0xa000
-colorSpace		= exifSubIfdTag "colorSpace" 0xa001
-exifImageWidth		= exifSubIfdTag "exifImageWidth" 0xa002
-exifImageHeight		= exifSubIfdTag "exifImageHeight" 0xa003
-relatedSoundFile	= exifSubIfdTag "relatedSoundFile" 0xa004
-flashEnergy		= exifSubIfdTag "flashEnergy" 0xa20b
-spatialFrequencyResponse= exifSubIfdTag "spatialFrequencyResponse" 0xa20c
-focalPlaneXResolution	= exifSubIfdTag "focalPlaneXResolution" 0xa20e
-focalPlaneYResolution	= exifSubIfdTag "focalPlaneYResolution" 0xa20f
-focalPlaneResolutionUnit= exifSubIfdTag "focalPlaneResolutionUnit" 0xa210
-subjectLocation		= exifSubIfdTag "subjectLocation" 0xa214
-exposureIndex		= exifSubIfdTag "exposureIndex" 0xa215
-sensingMethod		= exifSubIfdTag "sensingMethod" 0xa217
-fileSource		= exifSubIfdTag "fileSource" 0xa300
-sceneType		= exifSubIfdTag "sceneType" 0xa301
-cfaPattern		= exifSubIfdTag "cfaPattern" 0xa302
-customRendered		= exifSubIfdTag "customRendered" 0xa401
-exposureMode		= exifSubIfdTag "exposureMode" 0xa402
-whiteBalance		= exifSubIfdTag "whiteBalance" 0xa403
-digitalZoomRatio	= exifSubIfdTag "digitalZoomRatio" 0xa404
-focalLengthIn35mmFilm	= exifSubIfdTag "focalLengthIn35mmFilm" 0xa405
-sceneCaptureType	= exifSubIfdTag "sceneCaptureType" 0xa406
-gainControl		= exifSubIfdTag "gainControl" 0xa407
-contrast		= exifSubIfdTag "contrast" 0xa408
-saturation		= exifSubIfdTag "saturation" 0xa409
-sharpness		= exifSubIfdTag "sharpness" 0xa40a
-deviceSettingDescription= exifSubIfdTag "deviceSettingDescription" 0xa40b
-subjectDistanceRange	= exifSubIfdTag "subjectDistanceRange" 0xa40c
-imageUniqueId		= exifSubIfdTag "imageUniqueId" 0xa420
-exifInteroperabilityOffset=exifSubIfdTag "exifInteroperabilityOffset" 0xa005
-
-imageDescription	= exifIfd0Tag "imageDescription" 0x010e
-make			= exifIfd0Tag "make" 0x010f
-model			= exifIfd0Tag "model" 0x0110
-orientation		= exifIfd0Tag "orientation" 0x0112
-xResolution		= exifIfd0Tag "xResolution" 0x011a
-yResolution		= exifIfd0Tag "xResolution" 0x011b
-resolutionUnit		= exifIfd0Tag "resolutionUnit" 0x0128
-software		= exifIfd0Tag "software" 0x0131
-dateTime		= exifIfd0Tag "dateTime" 0x0132
-artist			= exifIfd0Tag "artist" 0x013b
-whitePoint		= exifIfd0Tag "whitePoint" 0x013e
-primaryChromaticities	= exifIfd0Tag "primaryChromaticities" 0x013f
-yCbCrCoefficients	= exifIfd0Tag "yCbCrCoefficients" 0x0211
-yCbCrPositioning	= exifIfd0Tag "yCbCrPositioning" 0x0213
-referenceBlackWhite	= exifIfd0Tag "referenceBlackWhite" 0x0214
-copyright		= exifIfd0Tag "copyright" 0x8298
-exifIfdOffset		= exifIfd0Tag "exifIfdOffset" 0x8769
-gpsTagOffset		= exifIfd0Tag "gpsTagOffset" 0x8825
-printImageMatching	= exifIfd0Tag "printImageMatching" 0xc4a5
-
-gpsVersionID		= exifGpsTag "gpsVersionID" 0x0000
-gpsLatitudeRef		= exifGpsTag "gpsLatitudeRef" 0x0001
-gpsLatitude		= exifGpsTag "gpsLatitude" 0x0002
-gpsLongitudeRef		= exifGpsTag "gpsLongitudeRef" 0x0003
-gpsLongitude		= exifGpsTag "gpsLongitude" 0x0004
-gpsAltitudeRef		= exifGpsTag "gpsAltitudeRef" 0x0005
-gpsAltitude		= exifGpsTag "gpsAltitude" 0x0006
-gpsTimeStamp		= exifGpsTag "gpsTimeStamp" 0x0007
-gpsSatellites		= exifGpsTag "gpsSatellites" 0x0008
-gpsStatus		= exifGpsTag "gpsStatus" 0x0009
-gpsMeasureMode		= exifGpsTag "gpsMeasureMode" 0x000a
-gpsDop			= exifGpsTag "gpsDop" 0x000b
-gpsSpeedRef		= exifGpsTag "gpsSpeedRef" 0x000c
-gpsSpeed		= exifGpsTag "gpsSpeed" 0x000d
-gpsTrackRef		= exifGpsTag "gpsTrackRef" 0x000e
-gpsTrack		= exifGpsTag "gpsTrack" 0x000f
-gpsImgDirectionRef	= exifGpsTag "gpsImgDirectionRef" 0x0010
-gpsImgDirection		= exifGpsTag "gpsImgDirection" 0x0011
-gpsMapDatum		= exifGpsTag "gpsMapDatum" 0x0012
-gpsDestLatitudeRef	= exifGpsTag "gpsDestLatitudeRef" 0x0013
-gpsDestLatitude		= exifGpsTag "gpsDestLatitude" 0x0014
-gpsDestLongitudeRef	= exifGpsTag "gpsDestLongitudeRef" 0x0015
-gpsDestLongitude	= exifGpsTag "gpsDestLongitude" 0x0016
-gpsDestBearingRef	= exifGpsTag "gpsDestBearingRef" 0x0017
-gpsDestBearing		= exifGpsTag "gpsDestBearing" 0x0018
-gpsDestDistanceRef	= exifGpsTag "gpsDestDistanceRef" 0x0019
-gpsDestDistance		= exifGpsTag "gpsDestDistance" 0x001a
-gpsProcessingMethod	= exifGpsTag "gpsProcessingMethod" 0x001b
-gpsAreaInformation	= exifGpsTag "gpsAreaInformation" 0x001c
-gpsDateStamp		= exifGpsTag "gpsDateStamp" 0x001d
-gpsDifferential		= exifGpsTag "gpsDifferential" 0x001e
-
-allExifTags :: [ExifTag]
-allExifTags = [exposureTime, fnumber, exposureProgram, isoSpeedRatings,
-	exifVersion, dateTimeOriginal, dateTimeDigitized, componentConfiguration,
-	compressedBitsPerPixel, shutterSpeedValue, apertureValue, brightnessValue,
-	exposureBiasValue, maxApertureValue, subjectDistance, meteringMode,
-	lightSource, flash, focalLength, makerNote, userComment,
-	exifImageWidth, exifImageHeight, relatedSoundFile, focalPlaneXResolution,
-	focalPlaneYResolution, focalPlaneResolutionUnit, sensingMethod, fileSource,
-	sceneType, orientation, make, model, software, copyright,
-	spectralSensitivity, oecf, subjectArea, subSecTime, subSecTimeOriginal,
-	subSecTimeDigitized, flashPixVersion, colorSpace, flashEnergy,
-	spatialFrequencyResponse, subjectLocation, exposureIndex, cfaPattern,
-	customRendered, exposureMode, whiteBalance, digitalZoomRatio,
-	focalLengthIn35mmFilm, sceneCaptureType, gainControl, contrast,
-	saturation, sharpness, deviceSettingDescription, subjectDistanceRange,
-	imageUniqueId, exifInteroperabilityOffset, imageDescription,
-	xResolution, yResolution, resolutionUnit, dateTime, whitePoint,
-	primaryChromaticities, yCbCrPositioning, yCbCrCoefficients, referenceBlackWhite,
-	exifIfdOffset, printImageMatching, gpsTagOffset, artist,
-	gpsVersionID, gpsLatitudeRef, gpsLatitude, gpsLongitudeRef, gpsLongitude,
-	gpsAltitudeRef, gpsAltitude, gpsTimeStamp, gpsSatellites, gpsStatus,
-	gpsMeasureMode, gpsDop, gpsSpeedRef, gpsSpeed, gpsTrackRef, gpsTrack,
-	gpsImgDirectionRef, gpsImgDirection, gpsMapDatum, gpsDestLatitudeRef,
-	gpsDestLatitude, gpsDestLongitudeRef, gpsDestLongitude, gpsDestBearingRef,
-	gpsDestBearing, gpsDestDistanceRef, gpsDestDistance, gpsProcessingMethod,
-	gpsAreaInformation, gpsDateStamp, gpsDifferential]
-
 getExifTag :: TagLocation -> Word16 -> ExifTag
-getExifTag l v = fromMaybe (ExifTag l Nothing v) $ find (isSameTag l v) allExifTags
-	where isSameTag l1 v1 (ExifTag l2 _ v2) = l1 == l2 && v1 == v2
+getExifTag l v = fromMaybe (ExifTag l Nothing v show) $ find (isSameTag l v) allExifTags
+	where isSameTag l1 v1 (ExifTag l2 _ v2 _) = l1 == l2 && v1 == v2
 
 data ValueHandler = ValueHandler
 	{
@@ -698,6 +481,8 @@
 	second <- getCharValue ':' >> readDigit 2
 	-- the realToFrac is workaround for a GHC 7.8.0->7.8.2 bug:
 	-- https://ghc.haskell.org/trac/ghc/ticket/9231
+	-- actually generates a warning and it's good, reminds me to
+	-- remove it someday, since 7.8.3 is out and with the fix.
 	return $ LocalTime (fromGregorian year month day) (TimeOfDay hour minute $ realToFrac second)
 	where
 		readDigit x = liftM read $ count x getDigit
@@ -752,44 +537,15 @@
 		ExifNumber 8 -> Just $ Rotation Ninety
 		_ -> Nothing
 
--- | Extract the GPS latitude and longitude where the picture was taken
--- (if it is present in the EXIF)
-getGpsLatitudeLongitude :: Map ExifTag ExifValue -> Maybe (Double, Double)
-getGpsLatitudeLongitude exifData = do
-	(ExifText latRef) <- Map.lookup gpsLatitudeRef exifData
-	latDec <- Map.lookup gpsLatitude exifData >>= gpsDecodeToDecimalDegrees 
-	let signedLatDec = case latRef of
-		"S" -> -latDec
-		_ -> latDec
-	(ExifText longRef) <- Map.lookup gpsLongitudeRef exifData
-	longDec <- Map.lookup gpsLongitude exifData >>= gpsDecodeToDecimalDegrees 
-	let signedLongDec = case longRef of
-		"W" -> -longDec
-		_ -> longDec
-	return (signedLatDec, signedLongDec)
-
-gpsDecodeToDecimalDegrees :: ExifValue -> Maybe Double
-gpsDecodeToDecimalDegrees (ExifRationalList intPairs) = case fmap intPairToFloating intPairs of
-			(degrees:minutes:seconds:[]) -> Just $ degrees + minutes / 60 + seconds / 3600
-			_ -> Nothing
-	where
-		intPairToFloating (n, d) = fromIntegral n / fromIntegral d
-gpsDecodeToDecimalDegrees _ = Nothing
-
--- | Format the exif value as floating-point if it makes sense,
--- otherwise use the default 'show' implementation.
--- The first parameter lets you specify how many digits after
--- the comma to format in the result string.
--- The special behaviour applies only for 'ExifRational' and 'ExifRationalList'.
-formatAsFloatingPoint :: Int -> ExifValue -> String
-formatAsFloatingPoint n (ExifRational num den) = formatNumDenAsString n num den
-formatAsFloatingPoint n (ExifRationalList values) = intercalate ", " $ foldl' step [] values
-	where step soFar (num,den) = soFar ++ [formatNumDenAsString n num den]
-formatAsFloatingPoint _ v = show v
-
-formatNumDenAsString :: Int -> Int -> Int -> String
-formatNumDenAsString n num den = printf formatString (fromIntegral num / fromIntegral den :: Double)
-	where formatString = "%." ++ show n ++ "f"
+-- | Will return Just True if the flash was fired, Just False
+-- if it was not, and Nothing if the file does not contain
+-- the information.
+wasFlashFired :: Map ExifTag ExifValue -> Maybe Bool
+wasFlashFired exifData = do
+	flashVal <- Map.lookup flash exifData
+	case flashVal of
+		ExifNumber n -> Just $ n .&. 1 /= 0
+		_ -> Nothing
 
 -- $intro
 --
@@ -798,9 +554,15 @@
 -- If you use the predefined ExifTag values, you don't care about details
 -- of the ExifTag type, however you should check out the 'ExifValue' type.
 --
--- You start from a JPEG file, you can parse its exif tags as a 'Map' of
+-- Regarding the ExifTag type there is however a field of that type that may
+-- interest you: 'prettyPrinter'. It's a function that'll format nicely an exif value
+-- for that exif tag as a String.
+-- For instance for the 'flash' ExifTag, it'll say whether the flash was
+-- fired or not, if there was return light and so on.
+--
+-- Generally speaking, you start from a JPEG file, you can parse its exif tags as a 'Map' of
 -- 'ExifTag' to 'ExifValue' using 'parseExif' or 'parseFileExif'.
 -- You can enumerate the map or 'lookup' the tags that interest you.
 --
 -- There are also a couple of higher-level helpers like 'getOrientation',
--- 'getDateTimeOriginal'.
+-- 'getDateTimeOriginal', 'wasFlashFired' and 'getGpsLatitudeLongitude'.
diff --git a/Graphics/PrettyPrinters.hs b/Graphics/PrettyPrinters.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/PrettyPrinters.hs
@@ -0,0 +1,198 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+module Graphics.PrettyPrinters where
+
+import Graphics.Types (ExifValue(..))
+import Data.List (foldl')
+import Text.Printf (printf)
+import qualified Data.Map as Map
+import Data.Char (chr)
+import qualified Data.ByteString as BS
+import Control.Arrow (first)
+
+ppResolutionUnit :: ExifValue -> String
+ppResolutionUnit = fromNumberMap [(1, "No absolute unit"),
+			(2, "Inch"),
+			(3, "Centimeter")]
+
+ppYCbCrPositioning :: ExifValue -> String
+ppYCbCrPositioning = fromNumberMap [(1, "Centered"), (2, "Co-sited")]
+
+ppExposureProgram :: ExifValue -> String
+ppExposureProgram = fromNumberMap [(0, "Not defined"),
+			(1, "Manual"),
+			(2, "Normal program"),
+			(3, "Aperture priority"),
+			(4, "Shutter priority"),
+			(5, "Creative program (biased toward depth of field)"),
+			(6, "Action program (biased toward fast shutter speed)"),
+			(7, "Portrait mode (for closeup photos with the background out of focus)"),
+			(8, "Landscape mode (for landscape photos with the background in focus)")]
+
+ppMeteringMode :: ExifValue -> String
+ppMeteringMode = fromNumberMap [(0, "Unknown"),
+			(1, "Average"),
+			(2, "Center-weighted average"),
+			(3, "Spot"),
+			(4, "MultiSpot"),
+			(5, "Pattern"),
+			(6, "Partial"),
+			(255, "other")]
+
+ppLightSource :: ExifValue -> String
+ppLightSource = fromNumberMap [(0, "Unknown"),
+			(1, "Daylight"),
+			(2, "Fluorescent"),
+			(3, "Tungsten (incandescent light)"),
+			(4, "Flash"),
+			(9, "Fine weather"),
+			(10, "Cloudy weather"),
+			(11, "Shade"),
+			(12, "Daylight fluorescent (D 5700 - 7100K)"),
+			(13, "Day white fluorescent (N 4600 - 5400K)"),
+			(14, "Cool white fluorescent (W 3900 - 4500K)"),
+			(15, "White fluorescent (WW 3200 - 3700K)"),
+			(17, "Standard light A"),
+			(18, "Standard light B"),
+			(19, "Standard light C"),
+			(20, "D55"),
+			(21, "D65"),
+			(22, "D75"),
+			(23, "D50"),
+			(24, "ISO studio tungsten"),
+			(255, "Other light source")]
+
+ppFlash :: ExifValue -> String
+ppFlash = fromNumberMap [(0, "Flash did not fire"),
+			(1, "Flash fired"),
+			(5, "Strobe return light not detected"),
+			(7, "Strobe return light detected"),
+			(9, "Flash fired, compulsory flash mode"),
+			(0x0D, "Flash fired, compulsory flash mode, return light not detected"),
+			(0x0F, "Flash fired, compulsory flash mode, return light detected"),
+			(0x10, "Flash did not fire, compulsory flash mode"),
+			(0x18, "Flash did not fire, auto mode"),
+			(0x19, "Flash fired, auto mode"),
+			(0x1D, "Flash fired, auto mode, return light not detected"),
+			(0x1F, "Flash fired, auto mode, return light detected"),
+			(0x20, "No flash function"),
+			(0x41, "Flash fired, red-eye reduction mode"),
+			(0x45, "Flash fired, red-eye reduction mode, return light not detected"),
+			(0x47, "Flash fired, red-eye reduction mode, return light detected"),
+			(0x49, "Flash fired, compulsory flash mode, red-eye reduction mode"),
+			(0x4D, "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected"),
+			(0x4F, "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected"),
+			(0x59, "Flash fired, auto mode, red-eye reduction mode"),
+			(0x5D, "Flash fired, auto mode, return light not detected, red-eye reduction mode"),
+			(0x5F, "Flash fired, auto mode, return light detected, red-eye reduction mode")]
+
+ppColorSpace :: ExifValue -> String
+ppColorSpace = fromNumberMap [(1, "sRGB"), (65535, "Uncalibrated")]
+
+ppCustomRendered :: ExifValue -> String
+ppCustomRendered = fromNumberMap [(0, "Normal process"), (1, "Custom process")]
+
+ppExposureMode :: ExifValue -> String
+ppExposureMode = fromNumberMap [(0, "Auto exposure"),
+			(1, "Manual exposure"),
+			(2, "Auto bracket")]
+
+ppWhiteBalance :: ExifValue -> String
+ppWhiteBalance = fromNumberMap [(0, "Auto white balance"),
+			(1, "Manual white balance")]
+
+ppSceneCaptureType :: ExifValue -> String
+ppSceneCaptureType = fromNumberMap [(0, "Standard"),
+			(1, "Landscape"),
+			(2, "Portrait"),
+			(3, "Night scene")]
+
+ppGainControl :: ExifValue -> String
+ppGainControl = fromNumberMap [(0, "Normal"),
+			(1, "Low gain up"),
+			(2, "High gain up"),
+			(3, "Low gain down"),
+			(4, "High gain down")]
+
+ppContrastSharpness :: ExifValue -> String
+ppContrastSharpness = fromNumberMap [(0, "Normal"), (1, "Soft"), (2, "Hard")]
+
+ppSaturation :: ExifValue -> String
+ppSaturation = fromNumberMap [(0, "Normal"),
+			(1, "Low saturation"),
+			(2, "High saturation")]
+
+ppSensingMethod :: ExifValue -> String
+ppSensingMethod = fromNumberMap [(1, "Not defined"),
+			(2, "One-chip color area sensor"),
+			(3, "Two-chip color area sensor"),
+			(4, "Three-chip color area sensor"),
+			(5, "Color sequential area sensor"),
+			(7, "Trilinear sensor"),
+			(8, "Color sequential linear sensor")]
+
+ppSubjectDistanceRange :: ExifValue -> String
+ppSubjectDistanceRange = fromNumberMap [(0, "Unknown"),
+			(1, "Macro"),
+			(2, "Close view"),
+			(3, "Distance view")]
+
+ppFocalPlaneResolutionUnit :: ExifValue -> String
+ppFocalPlaneResolutionUnit = fromNumberMap [(1, "No absolute unit of measurement"),
+			(2, "Inch"),
+			(3, "Centimeter")]
+
+ppOrientation :: ExifValue -> String
+ppOrientation = fromNumberMap [(1, "Top-left"),
+			(2, "Top-right"),
+			(3, "Bottom-right"),
+			(4, "Bottom-left"),
+			(5, "Left-top"),
+			(6, "Right-top"),
+			(7, "Right-bottom"),
+			(8, "Left-bottom")]
+
+ppSceneType :: ExifValue -> String
+ppSceneType = fromNumberMap [(1, "Directly photographed")]
+
+ppGpsAltitudeRef :: ExifValue -> String
+ppGpsAltitudeRef = fromNumberMap [(0, "Sea level"),
+    			(1, "Below sea level")]
+
+ppComponentConfiguration :: ExifValue -> String
+ppComponentConfiguration (ExifUndefined bs) = foldl' addComponent [] numbers
+	where
+		numbers :: [Int] = fmap fromIntegral $ BS.unpack bs
+		addComponent soFar c = soFar ++ formatComponent c
+		formatComponent c = case c of
+			0 -> "-"
+			1 -> "Y"
+			2 -> "Cb"
+			3 -> "Cr"
+			4 -> "R"
+			5 -> "G"
+			6 -> "B"
+			_ -> "?"
+ppComponentConfiguration v@_ = unknown v
+
+ppFlashPixVersion :: ExifValue -> String
+ppFlashPixVersion = formatVersion "FlashPix version %.1f"
+
+ppExifVersion :: ExifValue -> String
+ppExifVersion = formatVersion "Exif version %.2f"
+
+formatVersion :: String -> ExifValue -> String
+formatVersion fmt (ExifUndefined s) = printf fmt num
+	where
+		num :: Float = read asStr / 10.0
+		asStr = fmap (chr . fromIntegral) $ BS.unpack s
+formatVersion _ v@_ = unknown v
+
+fromNumberMap :: [(Int, String)] -> ExifValue -> String
+fromNumberMap m = fromMap convertedMap
+	where convertedMap = fmap (first ExifNumber) m
+
+fromMap :: [(ExifValue, String)] -> ExifValue -> String
+fromMap m v = Map.findWithDefault (unknown v) v $ Map.fromList m
+
+unknown :: Show a => a -> String
+unknown v = "Unknown value: " ++ show v
diff --git a/Graphics/Types.hs b/Graphics/Types.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/Types.hs
@@ -0,0 +1,108 @@
+module Graphics.Types where
+
+import qualified Data.ByteString as BS
+import Data.Word
+import Numeric (showHex)
+import Data.Function (on)
+import Data.List
+import Text.Printf (printf)
+
+-- | An exif value.
+-- 
+-- If you want a string describing the contents
+-- of the value, simply use 'show'.
+data ExifValue = ExifNumber !Int
+	-- ^ An exif number. Originally it could have been short, int, signed or not.
+	| ExifText !String
+	-- ^ ASCII text.
+	| ExifRational !Int !Int
+	-- ^ A rational number (numerator, denominator).
+	-- Sometimes we're used to it as rational (exposition time: 1/160),
+	-- sometimes as float (exposure compensation, we rather think -0.75)
+	-- 'show' will display it as 1/160.
+	| ExifNumberList ![Int]
+	-- ^ List of numbers. Originally they could have been short, int, signed or not.
+	| ExifRationalList ![(Int,Int)]
+	-- ^ A list of rational numbers (numerator, denominator).
+	-- Sometimes we're used to it as rational (exposition time: 1/160),
+	-- sometimes as float (exposure compensation, we rather think -0.75)
+	-- 'show' will display it as 1/160.
+	| ExifUndefined !BS.ByteString
+	-- ^ The undefined type in EXIF means that the contents are not
+	-- specified and up to the manufacturer. In effect it's exactly
+	-- a bytestring. Sometimes it's text with ASCII or UNICODE at the
+	-- beginning, often it's binary in nature.
+	| ExifUnknown !Word16 !Int !Int
+	-- ^ Unknown exif value type. All EXIF 2.3 types are
+	-- supported, it could be a newer file.
+	-- The parameters are type, count then value
+	deriving (Eq, Ord)
+
+instance Show ExifValue where
+	show (ExifNumber v) = show v
+	show (ExifText v) = v
+	show (ExifRational n d) = show n ++ "/" ++ show d
+	show (ExifUnknown t c v) = show "Unknown exif type. Type: " ++ show t 
+		++ " count: " ++ show c ++ " value: " ++ show v
+	show (ExifNumberList l) = show l
+	show (ExifRationalList l) = show l
+	show (ExifUndefined bs) = show bs
+
+-- | Location of the tag in the JPG file structure.
+-- Normally you don't need to fiddle with this,
+-- except maybe if the library doesn't know the particular
+-- exif tag you're interested in.
+-- Rather check the list of supported exif tags, like
+-- 'exposureTime' and so on.
+data TagLocation = ExifSubIFD | IFD0 | GpsSubIFD
+	deriving (Show, Eq, Ord)
+
+-- | An exif tag. Normally you don't need to fiddle with this,
+-- except maybe if the library doesn't know the particular
+-- exif tag you're interested in.
+-- Rather check the list of supported exif tags, like
+-- 'exposureTime' and so on.
+data ExifTag = ExifTag
+	{
+		tagLocation :: TagLocation,
+		-- ^ In which part of the JPEG file the exif tag was found
+		tagDesc :: Maybe String,
+		-- ^ Description of the exif tag (exposureTime, fnumber...) or if unknown, Nothing.
+		-- This should be purely for debugging purposes, to compare tags use == on ExifTag
+		-- or compare the tagKey.
+		tagKey :: Word16,
+		-- ^ Exif tag key, the number uniquely identifying this tag.
+		prettyPrinter :: ExifValue -> String
+		-- ^ A function that'll display nicely an exif value for that exif tag.
+		-- For instance for the 'flash' ExifTag, it'll say whether the flash was
+		-- fired or not, if there was return light and so on.
+	}
+
+instance Show ExifTag where
+	show (ExifTag _ (Just d) _ _) = d
+	show (ExifTag l _ v _) = "Unknown tag, location: " ++ show l
+		++ ", value: 0x" ++ showHex v ""
+
+instance Eq ExifTag where
+	t1 == t2 = tagKey t1 == tagKey t2 && tagLocation t1 == tagLocation t2
+
+instance Ord ExifTag where
+	compare t1 t2 = if locCmp /= EQ then locCmp else tagCmp
+		where
+			locCmp = (compare `on` tagLocation) t1 t2 
+			tagCmp = (compare `on` tagKey) t1 t2
+
+-- | Format the exif value as floating-point if it makes sense,
+-- otherwise use the default 'show' implementation.
+-- The first parameter lets you specify how many digits after
+-- the comma to format in the result string.
+-- The special behaviour applies only for 'ExifRational' and 'ExifRationalList'.
+formatAsFloatingPoint :: Int -> ExifValue -> String
+formatAsFloatingPoint n (ExifRational num den) = formatNumDenAsString n num den
+formatAsFloatingPoint n (ExifRationalList values) = intercalate ", " $ foldl' step [] values
+	where step soFar (num,den) = soFar ++ [formatNumDenAsString n num den]
+formatAsFloatingPoint _ v = show v
+
+formatNumDenAsString :: Int -> Int -> Int -> String
+formatNumDenAsString n num den = printf formatString (fromIntegral num / fromIntegral den :: Double)
+	where formatString = "%." ++ show n ++ "f"
diff --git a/hsexif.cabal b/hsexif.cabal
--- a/hsexif.cabal
+++ b/hsexif.cabal
@@ -2,7 +2,7 @@
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                hsexif
-version:             0.5.0.2
+version:             0.5.0.3
 synopsis:            EXIF handling library in pure Haskell
 description:         The hsexif library provides functions for working with EXIF data
                      contained in JPEG files. Currently it only supports reading the data.
@@ -19,7 +19,7 @@
 
 library
   exposed-modules:     Graphics.HsExif
-  -- other-modules:       
+  other-modules:       Graphics.Types, Graphics.PrettyPrinters, Graphics.ExifTags
   -- other-extensions:    
   build-depends:       base >=4.6 && <5,
                        binary >=0.7 && <0.8,
diff --git a/tests/Tests.hs b/tests/Tests.hs
--- a/tests/Tests.hs
+++ b/tests/Tests.hs
@@ -10,6 +10,7 @@
 import Data.Time.LocalTime
 import Data.Time.Calendar
 import Data.Char (chr)
+import Control.Monad (liftM)
 
 main :: IO ()
 main = do
@@ -29,7 +30,9 @@
 		describe "read exif date time" testReadExifDateTime
 		describe "read GPS lat long" $ testReadGpsLatLong gpsExifData
 		describe "read GPS lat long -- no data" $ testReadGpsLatLongNoData exifData
-		describe "test formatAsFloatingPoint" $ testFormatAsFloatingPoint
+		describe "test formatAsFloatingPoint" testFormatAsFloatingPoint
+		describe "pretty printing" $ testPrettyPrint gpsExifData
+		describe "flash fired" $ testFlashFired exifData
 
 testNotAJpeg :: B.ByteString -> Spec
 testNotAJpeg imageContents = it "returns empty list if not a JPEG" $
@@ -127,6 +130,62 @@
 testFormatAsFloatingPoint = it "properly formats as floating point" $ do
 	assertEqual' "0.75" $ formatAsFloatingPoint 2 $ ExifRational 3 4
 	assertEqual' "0.75, -0.50, 0.25" $ formatAsFloatingPoint 2 $ ExifRationalList [(3,4),(-1,2),(1,4)]
+
+testPrettyPrint :: Map ExifTag ExifValue -> Spec
+testPrettyPrint exifData = it "pretty prints many tags properly" $ do
+	checkPrettyPrinter orientation "Top-left" exifData
+	checkPrettyPrinter flash "Flash did not fire, auto mode" exifData
+	checkPrettyPrinter exposureTime "1/200 sec." exifData
+	checkPrettyPrinter exposureProgram "Normal program" exifData
+	checkPrettyPrinter exifVersion "Exif version 2.30" exifData
+	checkPrettyPrinter componentConfiguration "YCbCr" exifData
+	checkPrettyPrinter exposureBiasValue "0.00 EV" exifData
+	checkPrettyPrinter meteringMode "Pattern" exifData
+	checkPrettyPrinter lightSource "Unknown" exifData
+	checkPrettyPrinter flashPixVersion "FlashPix version 1.0" exifData
+	checkPrettyPrinter colorSpace "sRGB" exifData
+	checkPrettyPrinter customRendered "Normal process" exifData
+	checkPrettyPrinter exposureMode "Auto exposure" exifData
+	checkPrettyPrinter whiteBalance "Auto white balance" exifData
+	checkPrettyPrinter sceneCaptureType "Standard" exifData
+	checkPrettyPrinter gainControl "Normal" exifData
+	checkPrettyPrinter contrast "Normal" exifData
+	checkPrettyPrinter saturation "Normal" exifData
+	checkPrettyPrinter resolutionUnit "Inch" exifData
+	checkPrettyPrinter yCbCrPositioning "Co-sited" exifData
+	checkPrettyPrinter gpsLatitude "50.217917" exifData
+
+checkPrettyPrinter :: ExifTag -> String -> Map ExifTag ExifValue -> Assertion
+checkPrettyPrinter tag str exifData = assertEqual' (Just str) $ liftM (prettyPrinter tag) $ Map.lookup tag exifData
+
+testFlashFired :: Map ExifTag ExifValue -> Spec
+testFlashFired exifData = it "properly reads whether the flash was fired" $ do
+	assertEqual' (Just False) $ wasFlashFired exifData
+	assertEqual' Nothing $ wasFlashFired Map.empty
+	assertEqual' (Just False) $ wasFlashFired $ makeExifMapWithFlash 0
+	assertEqual' (Just True) $ wasFlashFired $ makeExifMapWithFlash 1
+	assertEqual' (Just True) $ wasFlashFired $ makeExifMapWithFlash 5
+	assertEqual' (Just True) $ wasFlashFired $ makeExifMapWithFlash 7
+	assertEqual' (Just True) $ wasFlashFired $ makeExifMapWithFlash 9
+	assertEqual' (Just True) $ wasFlashFired $ makeExifMapWithFlash 0x0D
+	assertEqual' (Just True) $ wasFlashFired $ makeExifMapWithFlash 0x0F
+	assertEqual' (Just False) $ wasFlashFired $ makeExifMapWithFlash 0x10
+	assertEqual' (Just False) $ wasFlashFired $ makeExifMapWithFlash 0x18
+	assertEqual' (Just True) $ wasFlashFired $ makeExifMapWithFlash 0x19
+	assertEqual' (Just True) $ wasFlashFired $ makeExifMapWithFlash 0x1D
+	assertEqual' (Just True) $ wasFlashFired $ makeExifMapWithFlash 0x1F
+	assertEqual' (Just False) $ wasFlashFired $ makeExifMapWithFlash 0x20
+	assertEqual' (Just True) $ wasFlashFired $ makeExifMapWithFlash 0x41
+	assertEqual' (Just True) $ wasFlashFired $ makeExifMapWithFlash 0x45
+	assertEqual' (Just True) $ wasFlashFired $ makeExifMapWithFlash 0x47
+	assertEqual' (Just True) $ wasFlashFired $ makeExifMapWithFlash 0x4D
+	assertEqual' (Just True) $ wasFlashFired $ makeExifMapWithFlash 0x4F
+	assertEqual' (Just True) $ wasFlashFired $ makeExifMapWithFlash 0x59
+	assertEqual' (Just True) $ wasFlashFired $ makeExifMapWithFlash 0x5D
+	assertEqual' (Just True) $ wasFlashFired $ makeExifMapWithFlash 0x5F
+
+makeExifMapWithFlash :: Int -> Map ExifTag ExifValue
+makeExifMapWithFlash flashV = Map.fromList [(flash, ExifNumber flashV)]
 
 assertEqual' :: (Show a, Eq a) => a -> a -> Assertion
 assertEqual' = assertEqual "doesn't match"
