packages feed

hsexif 0.2.0.1 → 0.3.0.0

raw patch · 3 files changed

+90/−23 lines, 3 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Graphics.HsExif: ExifRational :: Int -> Int -> ExifValue
+ Graphics.HsExif: ExifRational :: !Int -> !Int -> ExifValue
- Graphics.HsExif: ExifText :: String -> ExifValue
+ Graphics.HsExif: ExifText :: !String -> ExifValue
- Graphics.HsExif: ExifUnknown :: Word16 -> Int -> ExifValue
+ Graphics.HsExif: ExifUnknown :: !Word16 -> !Int -> ExifValue
- Graphics.HsExif: readExifDateTime :: String -> LocalTime
+ Graphics.HsExif: readExifDateTime :: String -> Maybe LocalTime

Files

Graphics/HsExif.hs view
@@ -1,18 +1,23 @@ {-# OPTIONS_GHC -fno-warn-missing-signatures #-} -- | Ability to work with the EXIF data contained in JPEG files. module Graphics.HsExif (-	ExifTag(..),-	TagLocation(..),-	ExifValue(..),+	-- $intro++	-- * Main functions 	parseFileExif, 	parseExif,++	-- * Higher-level helper functions 	readExifDateTime, 	getDateTimeOriginal, 	getOrientation, 	ImageOrientation(..), 	RotationDirection(..), -	-- now all known exif tags+	-- * The ExifValue type+	ExifValue(..),++	-- * All known exif tags 	exposureTime, 	fnumber, 	exposureProgram,@@ -49,6 +54,10 @@ 	model, 	software, 	copyright,++	-- * If you need to declare your own exif tags+	ExifTag(..),+	TagLocation(..), ) where  import Data.Binary.Get@@ -56,6 +65,7 @@ import Control.Monad (liftM, unless) import qualified Data.ByteString.Char8 as Char8 import Data.Word+import Data.Char (isDigit, ord, chr) import Data.Int (Int32) import Data.List import Data.Maybe (fromMaybe)@@ -74,14 +84,14 @@ -- of the value, simply use 'show'. data ExifValue = ExifNumber Int 	-- ^ An exif number. Could be short, int, signed or not.-	| ExifText String+	| ExifText !String 	-- ^ ASCII text.-	| ExifRational Int Int+	| 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.-	| ExifUnknown Word16 Int -- type then value+	| ExifUnknown !Word16 !Int -- type then value 	-- ^ Unknown exif value type. Maybe float? If the JPEG file is not 	-- corrupted, please send it to me. 	deriving Eq@@ -328,21 +338,54 @@ signedInt32ToInt :: Word32 -> Int signedInt32ToInt w = fromIntegral (fromIntegral w :: Int32) +-- i had this as runGetM and reusing in parseExif,+-- sadly fail is not implemented for Either.+-- will do for now.+runMaybeGet :: Get a -> B.ByteString -> Maybe a+runMaybeGet get bs = case runGetOrFail get bs of+	Left _ -> Nothing+	Right (_,_,x) -> Just x+ -- | Decode an EXIF date time value.-readExifDateTime :: String -> LocalTime-readExifDateTime dateStr = -	-- i know more elegant ways to code this.. parsec, regex, text..-	-- but i don't want to bring in too many dependencies to this library.-	-- the date is like "YYYY:MM:DD HH:MM:SS"-	LocalTime-		(fromGregorian (read $ take 4 dateStr) (read $ take 2 . drop 5 $ dateStr) (read $ take 2 . drop 8 $ dateStr))-		(TimeOfDay (read $ take 2 . drop 11 $ dateStr) (read $ take 2 . drop 14 $ dateStr) (read $ take 2 . drop 17 $ dateStr))+-- Will return 'Nothing' in case parsing fails.+readExifDateTime :: String -> Maybe LocalTime+readExifDateTime dateStr = runMaybeGet getExifDateTime $ B.pack $ map (fromIntegral . ord) dateStr +getExifDateTime :: Get LocalTime+getExifDateTime = do+	year <- readDigit 4+	month <- getCharValue ':' >> readDigit 2+	day <- getCharValue ':' >> readDigit 2+	hour <- getCharValue ' ' >> readDigit 2+	minute <- getCharValue ':' >> readDigit 2+	second <- getCharValue ':' >> readDigit 2+	return $ LocalTime (fromGregorian year month day) (TimeOfDay hour minute second)+	where+		readDigit x = liftM read $ count x getDigit++count :: Int -> Get a -> Get [a]+count n p | n <= 0 = return []+        | otherwise = sequence (replicate n p)+	+ -- | Extract the date and time when the picture was taken -- from the EXIF information. getDateTimeOriginal :: Map ExifTag ExifValue -> Maybe LocalTime-getDateTimeOriginal exifData = liftM (readExifDateTime . show) $ Map.lookup dateTimeOriginal exifData+getDateTimeOriginal exifData = Map.lookup dateTimeOriginal exifData >>= readExifDateTime . show  +getCharWhere :: (Char->Bool) -> Get Char+getCharWhere wher = do+	char <- liftM (chr . fromIntegral) getWord8+	if wher char+		then return char+		else fail "no parse"++getDigit :: Get Char+getDigit = getCharWhere isDigit++getCharValue :: Char -> Get Char+getCharValue char = getCharWhere (==char)+ data RotationDirection = MinusNinety 	| Ninety 	| HundredAndEighty@@ -355,6 +398,7 @@ 	deriving (Show, Eq)  -- | Extract the image orientation from the EXIF information.+-- Will return 'Nothing' on parse error. getOrientation :: Map ExifTag ExifValue -> Maybe ImageOrientation getOrientation exifData = do 	rotationVal <- Map.lookup orientation exifData@@ -367,4 +411,18 @@ 		ExifNumber 6 -> Just $ Rotation MinusNinety 		ExifNumber 7 -> Just $ MirrorRotation Ninety 		ExifNumber 8 -> Just $ Rotation Ninety-		_ -> error $ show rotationVal+		_ -> Nothing++-- $intro+--+-- EXIF parsing from JPEG files.+-- EXIF tags are enumerated as ExifTag values, check 'exposureTime' for instance.+-- 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+-- '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'.
hsexif.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                hsexif-version:             0.2.0.1+version:             0.3.0.0 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.
tests/Tests.hs view
@@ -22,18 +22,19 @@ 		describe "basic parsing" $ testBasic imageContents 		describe "extract picture date" $ testDate exifData 		describe "image orientation" $ testOrientation exifData+		describe "read exif date time" $ testReadExifDateTime  testNotAJpeg :: B.ByteString -> Spec testNotAJpeg imageContents = it "returns empty list if not a JPEG" $-	assertEqual "doesn't match" (Left "Not a JPEG file") (parseExif imageContents)+	assertEqual' (Left "Not a JPEG file") (parseExif imageContents)  testNoExif :: B.ByteString -> Spec testNoExif imageContents = it "returns empty list if no EXIF" $-	assertEqual "doesn't match" (Left "No EXIF in JPEG") (parseExif imageContents)+	assertEqual' (Left "No EXIF in JPEG") (parseExif imageContents)  testBasic :: B.ByteString -> Spec testBasic imageContents = it "parses a simple JPEG" $-	assertEqual "doesn't match" (Right $ Map.fromList [+	assertEqual' (Right $ Map.fromList [ 		(exposureTime, ExifRational 1 160), 		(fnumber, ExifRational 0 10), 		(exposureProgram, ExifNumber 2),@@ -90,9 +91,17 @@  testDate :: Map ExifTag ExifValue -> Spec testDate exifData = it "extracts the date correctly" $-	assertEqual "doesn't match" (Just $ LocalTime (fromGregorian 2013 10 2) (TimeOfDay 20 33 33))+	assertEqual' (Just $ LocalTime (fromGregorian 2013 10 2) (TimeOfDay 20 33 33)) 		(getDateTimeOriginal exifData)  testOrientation :: Map ExifTag ExifValue -> Spec testOrientation exifData = it "reads exif orientation" $-	assertEqual "doesn't match" (Just Normal) $ getOrientation exifData+	assertEqual' (Just Normal) $ getOrientation exifData++testReadExifDateTime :: Spec+testReadExifDateTime = it "reads exif date time" $ do+	assertEqual' (Just $ LocalTime (fromGregorian 2013 10 2) (TimeOfDay 20 33 33)) (readExifDateTime "2013:10:02 20:33:33")+	assertEqual' Nothing (readExifDateTime "2013:10:02 20:33:3")++assertEqual' :: (Show a, Eq a) => a -> a -> Assertion+assertEqual' = assertEqual "doesn't match"