git.iko.soy/typeable/pandoc
on master help sign in
Text.Pandoc.ImageSize: Parse EXIF format JPGs.
3bf8012John MacFarlane
src/Text/Pandoc/ImageSize.hs+28−6
@@ -53,7 +53,8 @@ imageType :: ByteString -> Maybe ImageType
53imageType img = case B.take 4 img of
53imageType img = case B.take 4 img of
54 "\x89\x50\x4e\x47" -> return Png
54 "\x89\x50\x4e\x47" -> return Png
55 "\x47\x49\x46\x38" -> return Gif
55 "\x47\x49\x46\x38" -> return Gif
56− "\xff\xd8\xff\xe0" -> return Jpeg
56+ "\xff\xd8\xff\xe0" -> return Jpeg -- JFIF
57+ "\xff\xd8\xff\xe1" -> return Jpeg -- Exif
57 "%PDF" -> return Pdf
58 "%PDF" -> return Pdf
58 "%!PS"
59 "%!PS"
59 | (B.take 4 $ B.drop 1 $ B.dropWhile (/=' ') img) == "EPSF"
60 | (B.take 4 $ B.drop 1 $ B.dropWhile (/=' ') img) == "EPSF"
@@ -139,8 +140,14 @@ gifSize img = do
139jpegSize :: ByteString -> Maybe ImageSize
140jpegSize :: ByteString -> Maybe ImageSize
140jpegSize img = do
141jpegSize img = do
141 let (hdr, rest) = B.splitAt 4 img
142 let (hdr, rest) = B.splitAt 4 img
142− guard $ hdr == "\xff\xd8\xff\xe0"
143 guard $ B.length rest >= 14
143 guard $ B.length rest >= 14
144+ case hdr of
145+ "\xff\xd8\xff\xe0" -> jfifSize rest
146+ "\xff\xd8\xff\xe1" -> exifSize rest
147+ _ -> mzero
148+
149+jfifSize :: ByteString -> Maybe ImageSize
150+jfifSize rest = do
144 let [dpiDensity,dpix1,dpix2,dpiy1,dpiy2] = map fromIntegral
151 let [dpiDensity,dpix1,dpix2,dpiy1,dpiy2] = map fromIntegral
145 $ unpack $ B.take 5 $ B.drop 9 $ rest
152 $ unpack $ B.take 5 $ B.drop 9 $ rest
146 let factor = case dpiDensity of
153 let factor = case dpiDensity of
@@ -149,11 +156,11 @@ jpegSize img = do
149 _ -> const 72
156 _ -> const 72
150 let dpix = factor (shift dpix1 8 + dpix2)
157 let dpix = factor (shift dpix1 8 + dpix2)
151 let dpiy = factor (shift dpiy1 8 + dpiy2)
158 let dpiy = factor (shift dpiy1 8 + dpiy2)
152− (w,h) <- findJpegSize rest
159+ (w,h) <- findJfifSize rest
153 return $ ImageSize { pxX = w, pxY = h, dpiX = dpix, dpiY = dpiy }
160 return $ ImageSize { pxX = w, pxY = h, dpiX = dpix, dpiY = dpiy }
154
161
155−findJpegSize :: ByteString -> Maybe (Integer,Integer)
162+findJfifSize :: ByteString -> Maybe (Integer,Integer)
156−findJpegSize bs = do
163+findJfifSize bs = do
157 let bs' = B.dropWhile (=='\xff') $ B.dropWhile (/='\xff') bs
164 let bs' = B.dropWhile (=='\xff') $ B.dropWhile (/='\xff') bs
158 case B.uncons bs' of
165 case B.uncons bs' of
159 Just (c,bs'') | c >= '\xc0' && c <= '\xc3' -> do
166 Just (c,bs'') | c >= '\xc0' && c <= '\xc3' -> do
@@ -165,8 +172,23 @@ findJpegSize bs = do
165 [c1,c2] -> do
172 [c1,c2] -> do
166 let len = shift c1 8 + c2
173 let len = shift c1 8 + c2
167 -- skip variables
174 -- skip variables
168− findJpegSize $ B.drop len bs''
175+ findJfifSize $ B.drop len bs''
169 _ -> fail "JPEG parse error"
176 _ -> fail "JPEG parse error"
170 Nothing -> fail "Did not find length record"
177 Nothing -> fail "Did not find length record"
171
178
179+exifSize :: ByteString -> Maybe ImageSize
180+exifSize rest = do
181+ let bs' = B.takeWhile (/='\xff') $ B.drop 8 rest -- exif data
182+ let (_,bs'') = B.breakSubstring "\xa0\x02" bs' -- width
183+ let rawWidth = B.take 2 $ B.drop 10 bs''
184+ let (_,bs''') = B.breakSubstring "\xa0\x03" bs' -- height
185+ let rawHeight = B.take 2 $ B.drop 10 bs'''
186+ let tonum bs = case map fromIntegral $ unpack bs of
187+ [x,y] -> Just $ shift x 8 + y
188+ _ -> Nothing
189+ case (tonum rawWidth, tonum rawHeight) of
190+ (Just w, Just h) ->
191+ return $ ImageSize { pxX = w, pxY = h, dpiX = 72, dpiY = 72 }
192+ _ -> fail "Could not determine exif image size"
193+ -- some day, figure out how to parse dpi from exif
172
194