git.iko.soy/typeable/pandoc
on master help sign in
Text.Pandoc.ImageSize: Parse EXIF format JPGs.

Note: For now we just assign them all 72 dpi. It wasn't clear to me how to extract the resolution information. At least the aspect ratio will be right, and 72 dpi is the most common setting. Closes #976.

commit3bf8012bf6e6965a68de76d5bb46782086393da7 authorJohn MacFarlane <fiddlosopher@gmail.com> dateWed 8 Jan 2014, 19:33 (13 years ago) parentaada7b4
1 file changed+28−6
src/Text/Pandoc/ImageSize.hs+28−6
src/Text/Pandoc/ImageSize.hs+28−6
@@ -53,7 +53,8 @@ imageType :: ByteString -> Maybe ImageType
5353imageType img = case B.take 4 img of
5454 "\x89\x50\x4e\x47" -> return Png
5555 "\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
5758 "%PDF" -> return Pdf
5859 "%!PS"
5960 | (B.take 4 $ B.drop 1 $ B.dropWhile (/=' ') img) == "EPSF"
@@ -139,8 +140,14 @@ gifSize img = do
139140jpegSize :: ByteString -> Maybe ImageSize
140141jpegSize img = do
141142 let (hdr, rest) = B.splitAt 4 img
142− guard $ hdr == "\xff\xd8\xff\xe0"
143143 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
144151 let [dpiDensity,dpix1,dpix2,dpiy1,dpiy2] = map fromIntegral
145152 $ unpack $ B.take 5 $ B.drop 9 $ rest
146153 let factor = case dpiDensity of
@@ -149,11 +156,11 @@ jpegSize img = do
149156 _ -> const 72
150157 let dpix = factor (shift dpix1 8 + dpix2)
151158 let dpiy = factor (shift dpiy1 8 + dpiy2)
152− (w,h) <- findJpegSize rest
159+ (w,h) <- findJfifSize rest
153160 return $ ImageSize { pxX = w, pxY = h, dpiX = dpix, dpiY = dpiy }
154161
155−findJpegSize :: ByteString -> Maybe (Integer,Integer)
156−findJpegSize bs = do
162+findJfifSize :: ByteString -> Maybe (Integer,Integer)
163+findJfifSize bs = do
157164 let bs' = B.dropWhile (=='\xff') $ B.dropWhile (/='\xff') bs
158165 case B.uncons bs' of
159166 Just (c,bs'') | c >= '\xc0' && c <= '\xc3' -> do
@@ -165,8 +172,23 @@ findJpegSize bs = do
165172 [c1,c2] -> do
166173 let len = shift c1 8 + c2
167174 -- skip variables
168− findJpegSize $ B.drop len bs''
175+ findJfifSize $ B.drop len bs''
169176 _ -> fail "JPEG parse error"
170177 Nothing -> fail "Did not find length record"
171178
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
172194