git.iko.soy/typeable/pandoc
on master help sign in
Merge pull request #5589 from blmage/fix-3992
7bc9eabJohn MacFarlane
src/Text/Pandoc/Readers/EPUB.hs+15−8
@@ -21,7 +21,7 @@ import Prelude
21import Codec.Archive.Zip (Archive (..), Entry, findEntryByPath, fromEntry,
21import Codec.Archive.Zip (Archive (..), Entry, findEntryByPath, fromEntry,
22 toArchiveOrFail)
22 toArchiveOrFail)
23import Control.DeepSeq (NFData, deepseq)
23import Control.DeepSeq (NFData, deepseq)
24−import Control.Monad (guard, liftM)
24+import Control.Monad (guard, liftM, liftM2, mplus)
25import Control.Monad.Except (throwError)
25import Control.Monad.Except (throwError)
26import qualified Data.ByteString.Lazy as BL (ByteString)
26import qualified Data.ByteString.Lazy as BL (ByteString)
27import Data.List (isInfixOf, isPrefixOf)
27import Data.List (isInfixOf, isPrefixOf)
@@ -62,8 +62,8 @@ archiveToEPUB :: (PandocMonad m) => ReaderOptions -> Archive -> m Pandoc
62archiveToEPUB os archive = do
62archiveToEPUB os archive = do
63 -- root is path to folder with manifest file in
63 -- root is path to folder with manifest file in
64 (root, content) <- getManifest archive
64 (root, content) <- getManifest archive
65− meta <- parseMeta content
65+ (coverId, meta) <- parseMeta content
66− (cover, items) <- parseManifest content
66+ (cover, items) <- parseManifest content coverId
67 -- No need to collapse here as the image path is from the manifest file
67 -- No need to collapse here as the image path is from the manifest file
68 let coverDoc = fromMaybe mempty (imageToPandoc <$> cover)
68 let coverDoc = fromMaybe mempty (imageToPandoc <$> cover)
69 spine <- parseSpine items content
69 spine <- parseSpine items content
@@ -124,18 +124,22 @@ imageToPandoc s = B.doc . B.para $ B.image s "" mempty
124imageMimes :: [MimeType]
124imageMimes :: [MimeType]
125imageMimes = ["image/gif", "image/jpeg", "image/png"]
125imageMimes = ["image/gif", "image/jpeg", "image/png"]
126
126
127+type CoverId = String
128+
127type CoverImage = FilePath
129type CoverImage = FilePath
128
130
129−parseManifest :: (PandocMonad m) => Element -> m (Maybe CoverImage, Items)
131+parseManifest :: (PandocMonad m) => Element -> Maybe CoverId -> m (Maybe CoverImage, Items)
130−parseManifest content = do
132+parseManifest content coverId = do
131 manifest <- findElementE (dfName "manifest") content
133 manifest <- findElementE (dfName "manifest") content
132 let items = findChildren (dfName "item") manifest
134 let items = findChildren (dfName "item") manifest
133 r <- mapM parseItem items
135 r <- mapM parseItem items
134 let cover = findAttr (emptyName "href") =<< filterChild findCover manifest
136 let cover = findAttr (emptyName "href") =<< filterChild findCover manifest
135− return (cover, M.fromList r)
137+ return (cover `mplus` coverId, M.fromList r)
136 where
138 where
137 findCover e = maybe False (isInfixOf "cover-image")
139 findCover e = maybe False (isInfixOf "cover-image")
138 (findAttr (emptyName "properties") e)
140 (findAttr (emptyName "properties") e)
141+ || fromMaybe False
142+ (liftM2 (==) coverId (findAttr (emptyName "id") e))
139 parseItem e = do
143 parseItem e = do
140 uid <- findAttrE (emptyName "id") e
144 uid <- findAttrE (emptyName "id") e
141 href <- findAttrE (emptyName "href") e
145 href <- findAttrE (emptyName "href") e
@@ -153,14 +157,17 @@ parseSpine is e = do
153 guard linear
157 guard linear
154 findAttr (emptyName "idref") ref
158 findAttr (emptyName "idref") ref
155
159
156−parseMeta :: PandocMonad m => Element -> m Meta
160+parseMeta :: PandocMonad m => Element -> m (Maybe CoverId, Meta)
157parseMeta content = do
161parseMeta content = do
158 meta <- findElementE (dfName "metadata") content
162 meta <- findElementE (dfName "metadata") content
159 let dcspace (QName _ (Just "http://purl.org/dc/elements/1.1/") (Just "dc")) = True
163 let dcspace (QName _ (Just "http://purl.org/dc/elements/1.1/") (Just "dc")) = True
160 dcspace _ = False
164 dcspace _ = False
161 let dcs = filterChildrenName dcspace meta
165 let dcs = filterChildrenName dcspace meta
162 let r = foldr parseMetaItem nullMeta dcs
166 let r = foldr parseMetaItem nullMeta dcs
167+ let coverId = findAttr (emptyName "content") =<< filterChild findCover meta
163− return r
168+ return (coverId, r)
169+ where
170+ findCover e = maybe False (== "cover") (findAttr (emptyName "name") e)
164
171
165-- http://www.idpf.org/epub/30/spec/epub30-publications.html#sec-metadata-elem
172-- http://www.idpf.org/epub/30/spec/epub30-publications.html#sec-metadata-elem
166parseMetaItem :: Element -> Meta -> Meta
173parseMetaItem :: Element -> Meta -> Meta
test/Tests/Readers/EPUB.hs+33−1
@@ -44,10 +44,42 @@ featuresBag = [("img/check.gif","image/gif",1340)
44 ,("img/multiscripts_and_greek_alphabet.png","image/png",10060)
44 ,("img/multiscripts_and_greek_alphabet.png","image/png",10060)
45 ]
45 ]
46
46
47+-- with additional meta tag for cover in EPUB2 format
48+epub3CoverBag :: [(String, String, Int)]
49+epub3CoverBag = [("wasteland-cover.jpg","image/jpeg",103477)]
50+
51+epub3NoCoverBag :: [(String, String, Int)]
52+epub3NoCoverBag = [("img/check.gif","image/gif",1340)
53+ ,("img/check.jpg","image/jpeg",2661)
54+ ,("img/check.png","image/png",2815)
55+ ]
56+
57+-- content.opf uses the word `picture` to refer to the cover as much as validly possible
58+-- to check if references are resolved correctly
59+epub2PictureBag :: [(String, String, Int)]
60+epub2PictureBag = [("image/image.jpg","image/jpeg",9713)]
61+
62+-- content.opf contains the word `cover` as much as possible, to check if possible multiple matches cause errors
63+epub2CoverBag :: [(String, String, Int)]
64+epub2CoverBag = [("image/cover.jpg","image/jpeg",9713)]
65+
66+epub2NoCoverBag :: [(String, String, Int)]
67+epub2NoCoverBag = []
68+
47tests :: [TestTree]
69tests :: [TestTree]
48tests =
70tests =
49 [ testGroup "EPUB Mediabag"
71 [ testGroup "EPUB Mediabag"
50 [ testCase "features bag"
72 [ testCase "features bag"
51− (testMediaBag "epub/img.epub" featuresBag)
73+ (testMediaBag "epub/img.epub" featuresBag),
74+ testCase "EPUB3 cover bag"
75+ (testMediaBag "epub/wasteland.epub" epub3CoverBag),
76+ testCase "EPUB3 no cover bag"
77+ (testMediaBag "epub/img_no_cover.epub" epub3NoCoverBag),
78+ testCase "EPUB2 picture bag"
79+ (testMediaBag "epub/epub2_picture.epub" epub2PictureBag),
80+ testCase "EPUB2 cover bag"
81+ (testMediaBag "epub/epub2_cover.epub" epub2CoverBag),
82+ testCase "EPUB2 no cover bag"
83+ (testMediaBag "epub/epub2_no_cover.epub" epub2NoCoverBag)
52 ]
84 ]
53 ]
85 ]
test/epub/epub2_cover.epubnew file

Binary file not shown.

test/epub/epub2_no_cover.epubnew file

Binary file not shown.

test/epub/epub2_picture.epubnew file

Binary file not shown.

test/epub/img_no_cover.epubnew file

Binary file not shown.