hsexif 0.4.0.1 → 0.5.0.0
raw patch · 3 files changed
+41/−37 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Graphics.HsExif: readGpsLatitudeLongitude :: Map ExifTag ExifValue -> Maybe (Double, Double)
+ Graphics.HsExif: getGpsLatitudeLongitude :: Map ExifTag ExifValue -> Maybe (Double, Double)
Files
- Graphics/HsExif.hs +32/−33
- hsexif.cabal +1/−1
- tests/Tests.hs +8/−3
Graphics/HsExif.hs view
@@ -13,7 +13,7 @@ getOrientation, ImageOrientation(..), RotationDirection(..),- readGpsLatitudeLongitude,+ getGpsLatitudeLongitude, -- * The ExifValue type ExifValue(..),@@ -142,7 +142,7 @@ import Data.Binary.Put import qualified Data.ByteString.Lazy as B import qualified Data.ByteString as BS-import Control.Monad (liftM, unless)+import Control.Monad (liftM, unless, replicateM) import qualified Data.ByteString.Char8 as Char8 import Data.Word import Data.Char (isDigit, ord, chr)@@ -239,7 +239,7 @@ getWord32 Intel = getWord32le getWord32 Motorola = getWord32be -putWord32 :: ByteAlign -> (Word32 -> Put)+putWord32 :: ByteAlign -> Word32 -> Put putWord32 Intel = putWord32le putWord32 Motorola = putWord32be @@ -514,16 +514,16 @@ readMany :: ByteAlign -> Int -> Get ExifValue } -readNumberList decoder = \byteAlign components -> liftM (ExifNumberList . fmap fromIntegral)- $ readManyInternal byteAlign components- where readManyInternal byteAlign components = count components (decoder byteAlign)+readNumberList :: Integral a => (ByteAlign -> Get a) -> ByteAlign -> Int -> Get ExifValue+readNumberList decoder byteAlign components = liftM (ExifNumberList . fmap fromIntegral)+ $ count components (decoder byteAlign) unsignedByteValueHandler = ValueHandler { dataTypeId = 1, dataLength = 1, readSingle = \_ -> liftM (ExifNumber . fromIntegral) getWord8,- readMany = readNumberList (\_ -> getWord8)+ readMany = readNumberList $ const getWord8 } asciiStringValueHandler = ValueHandler@@ -538,7 +538,7 @@ { dataTypeId = 3, dataLength = 2,- readSingle = \byteAlign -> liftM (ExifNumber . fromIntegral) $ getWord16 byteAlign,+ readSingle = liftM (ExifNumber . fromIntegral) . getWord16, readMany = readNumberList getWord16 } @@ -546,7 +546,7 @@ { dataTypeId = 4, dataLength = 4,- readSingle = \byteAlign -> liftM (ExifNumber . fromIntegral) $ getWord32 byteAlign,+ readSingle = liftM (ExifNumber . fromIntegral) . getWord32, readMany = readNumberList getWord32 } @@ -561,8 +561,7 @@ dataTypeId = 5, dataLength = 8, readSingle = readRationalContents ExifRational,- readMany = let readManyInternal byteAlign components = count components (readRationalContents (,) byteAlign) in- \byteAlign components -> liftM ExifRationalList $ readManyInternal byteAlign components+ readMany = \byteAlign components -> liftM ExifRationalList $ count components (readRationalContents (,) byteAlign) } signedByteValueHandler = ValueHandler@@ -570,7 +569,7 @@ dataTypeId = 6, dataLength = 1, readSingle = \_ -> liftM (ExifNumber . signedInt8ToInt) getWord8,- readMany = readNumberList (\_ -> liftM signedInt8ToInt getWord8)+ readMany = readNumberList (liftM signedInt8ToInt . const getWord8) } undefinedValueHandler = ValueHandler@@ -585,16 +584,16 @@ { dataTypeId = 8, dataLength = 2,- readSingle = \byteAlign -> liftM (ExifNumber . signedInt16ToInt) $ getWord16 byteAlign,- readMany = readNumberList (\b -> liftM signedInt16ToInt $ getWord16 b)+ readSingle = liftM (ExifNumber . signedInt16ToInt) . getWord16,+ readMany = readNumberList (liftM signedInt16ToInt . getWord16) } signedLongValueHandler = ValueHandler { dataTypeId = 9, dataLength = 4,- readSingle = \byteAlign -> liftM (ExifNumber . signedInt32ToInt) $ getWord32 byteAlign,- readMany = readNumberList (\b -> liftM signedInt32ToInt $ getWord32 b)+ readSingle = liftM (ExifNumber . signedInt32ToInt) . getWord32,+ readMany = readNumberList (liftM signedInt32ToInt . getWord32) } readSignedRationalContents :: (Int -> Int -> a) -> ByteAlign -> Get a@@ -608,8 +607,7 @@ dataTypeId = 10, dataLength = 8, readSingle = readSignedRationalContents ExifRational,- readMany = let readManyInternal byteAlign components = count components (readSignedRationalContents (,) byteAlign) in- \byteAlign components -> liftM ExifRationalList $ readManyInternal byteAlign components+ readMany = \byteAlign components -> liftM ExifRationalList $ count components (readSignedRationalContents (,) byteAlign) } valueHandlers :: [ValueHandler]@@ -643,12 +641,12 @@ getHandler typeId = find ((==typeId) . dataTypeId) valueHandlers decodeEntryWithHandler :: ByteAlign -> Int -> ValueHandler -> IfEntry -> Get ExifValue-decodeEntryWithHandler byteAlign tiffHeaderStart handler entry = do- if dataLength handler * (entryNoComponents entry) <= 4- then return $ parseInline byteAlign handler entry inlineBs+decodeEntryWithHandler byteAlign tiffHeaderStart handler entry =+ if dataLength handler * entryNoComponents entry <= 4+ then do+ let inlineBs = runPut $ putWord32 byteAlign $ entryContents entry+ return $ parseInline byteAlign handler entry inlineBs else parseOffset byteAlign tiffHeaderStart handler entry- where- inlineBs = runPut $ putWord32 byteAlign $ entryContents entry parseInline :: ByteAlign -> ValueHandler -> IfEntry -> B.ByteString -> ExifValue parseInline byteAlign handler entry bytestring =@@ -702,7 +700,7 @@ count :: Int -> Get a -> Get [a] count n p | n <= 0 = return []- | otherwise = sequence (replicate n p)+ | otherwise = replicateM n p -- | Extract the date and time when the picture was taken@@ -752,26 +750,27 @@ -- | Extract the GPS latitude and longitude where the picture was taken -- (if it is present in the EXIF)-readGpsLatitudeLongitude :: Map ExifTag ExifValue -> Maybe (Double, Double)-readGpsLatitudeLongitude exifData = do+getGpsLatitudeLongitude :: Map ExifTag ExifValue -> Maybe (Double, Double)+getGpsLatitudeLongitude exifData = do (ExifText latRef) <- Map.lookup gpsLatitudeRef exifData- latDec <- liftM gpsDecodeToDecimalDegrees $ Map.lookup gpsLatitude exifData+ latDec <- Map.lookup gpsLatitude exifData >>= gpsDecodeToDecimalDegrees let signedLatDec = case latRef of "S" -> -latDec _ -> latDec (ExifText longRef) <- Map.lookup gpsLongitudeRef exifData- longDec <- liftM gpsDecodeToDecimalDegrees $ Map.lookup gpsLongitude exifData+ longDec <- Map.lookup gpsLongitude exifData >>= gpsDecodeToDecimalDegrees let signedLongDec = case longRef of "W" -> -longDec _ -> longDec return (signedLatDec, signedLongDec) -gpsDecodeToDecimalDegrees :: ExifValue -> Double-gpsDecodeToDecimalDegrees (ExifRationalList intPairs) = floatings !! 0 + floatings !! 1 / 60 + floatings !! 2 / 3600+gpsDecodeToDecimalDegrees :: ExifValue -> Maybe Double+gpsDecodeToDecimalDegrees (ExifRationalList intPairs) = case fmap (uncurry intsToFloating) intPairs of+ (degrees:minutes:seconds:[]) -> Just $ degrees + minutes / 60 + seconds / 3600+ _ -> Nothing where- floatings = fmap (uncurry intsToFloating) intPairs- intsToFloating n d = (fromIntegral n) / (fromIntegral d)-gpsDecodeToDecimalDegrees _ = error "gpsDecodeToDecimalDegrees not called on a rational list!?" -- no way to prevent this at compile time???+ intsToFloating n d = fromIntegral n / fromIntegral d+gpsDecodeToDecimalDegrees _ = Nothing -- $intro --
hsexif.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: hsexif-version: 0.4.0.1+version: 0.5.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
@@ -26,8 +26,9 @@ describe "basic parsing" $ testBasic imageContents describe "extract picture date" $ testDate exifData describe "image orientation" $ testOrientation exifData- describe "read exif date time" $ testReadExifDateTime+ describe "read exif date time" testReadExifDateTime describe "read GPS lat long" $ testReadGpsLatLong gpsExifData+ describe "read GPS lat long -- no data" $ testReadGpsLatLongNoData exifData testNotAJpeg :: B.ByteString -> Spec testNotAJpeg imageContents = it "returns empty list if not a JPEG" $@@ -94,7 +95,7 @@ where -- the makerNote is HUGE. so test it separately. parsed = (\(Right x) -> x) $ parseExif imageContents- cleanedParsed = Map.fromList $ filter (\(a,_) -> not (a==makerNote)) $ Map.toList parsed+ cleanedParsed = Map.fromList $ filter (\(a,_) -> (a/=makerNote)) $ Map.toList parsed makerNoteV = (\(Just (ExifUndefined x)) -> x) $ Map.lookup makerNote parsed testDate :: Map ExifTag ExifValue -> Spec@@ -113,9 +114,13 @@ testReadGpsLatLong :: Map ExifTag ExifValue -> Spec testReadGpsLatLong exifData = it "reads gps latitude longitude" $ do- let (Just (lat,long)) = readGpsLatitudeLongitude exifData+ let (Just (lat,long)) = getGpsLatitudeLongitude exifData assertBool' $ 50.2179 < lat && 50.2180 > lat assertBool' $ -5.031 > long && -5.032 < long ++testReadGpsLatLongNoData :: Map ExifTag ExifValue -> Spec+testReadGpsLatLongNoData exifData = it "reads gps latitude longitude" $+ assertEqual' Nothing $ getGpsLatitudeLongitude exifData assertEqual' :: (Show a, Eq a) => a -> a -> Assertion assertEqual' = assertEqual "doesn't match"