git.iko.soy/typeable/pandoc
on master help sign in
Merge pull request #5589 from blmage/fix-3992

Add support for EPUB2 covers (fix #3992)

commit7bc9eab8465e16a13768834e49f124a3efbf29f4 authorJohn MacFarlane <jgm@berkeley.edu> dateSat 13 Jul 2019, 16:48 (7 years ago) parent1e0d4f14b73544
6 files changed+48−9
src/Text/Pandoc/Readers/EPUB.hs+15−8 test/Tests/Readers/EPUB.hs+33−1 test/epub/epub2_cover.epub test/epub/epub2_no_cover.epub test/epub/epub2_picture.epub test/epub/img_no_cover.epub
src/Text/Pandoc/Readers/EPUB.hs+15−8
@@ -21,7 +21,7 @@ import Prelude
2121import Codec.Archive.Zip (Archive (..), Entry, findEntryByPath, fromEntry,
2222 toArchiveOrFail)
2323import Control.DeepSeq (NFData, deepseq)
24−import Control.Monad (guard, liftM)
24+import Control.Monad (guard, liftM, liftM2, mplus)
2525import Control.Monad.Except (throwError)
2626import qualified Data.ByteString.Lazy as BL (ByteString)
2727import Data.List (isInfixOf, isPrefixOf)
@@ -62,8 +62,8 @@ archiveToEPUB :: (PandocMonad m) => ReaderOptions -> Archive -> m Pandoc
6262archiveToEPUB os archive = do
6363 -- root is path to folder with manifest file in
6464 (root, content) <- getManifest archive
65− meta <- parseMeta content
66− (cover, items) <- parseManifest content
65+ (coverId, meta) <- parseMeta content
66+ (cover, items) <- parseManifest content coverId
6767 -- No need to collapse here as the image path is from the manifest file
6868 let coverDoc = fromMaybe mempty (imageToPandoc <$> cover)
6969 spine <- parseSpine items content
@@ -124,18 +124,22 @@ imageToPandoc s = B.doc . B.para $ B.image s "" mempty
124124imageMimes :: [MimeType]
125125imageMimes = ["image/gif", "image/jpeg", "image/png"]
126126
127+type CoverId = String
128+
127129type CoverImage = FilePath
128130
129−parseManifest :: (PandocMonad m) => Element -> m (Maybe CoverImage, Items)
130−parseManifest content = do
131+parseManifest :: (PandocMonad m) => Element -> Maybe CoverId -> m (Maybe CoverImage, Items)
132+parseManifest content coverId = do
131133 manifest <- findElementE (dfName "manifest") content
132134 let items = findChildren (dfName "item") manifest
133135 r <- mapM parseItem items
134136 let cover = findAttr (emptyName "href") =<< filterChild findCover manifest
135− return (cover, M.fromList r)
137+ return (cover `mplus` coverId, M.fromList r)
136138 where
137139 findCover e = maybe False (isInfixOf "cover-image")
138140 (findAttr (emptyName "properties") e)
141+ || fromMaybe False
142+ (liftM2 (==) coverId (findAttr (emptyName "id") e))
139143 parseItem e = do
140144 uid <- findAttrE (emptyName "id") e
141145 href <- findAttrE (emptyName "href") e
@@ -153,14 +157,17 @@ parseSpine is e = do
153157 guard linear
154158 findAttr (emptyName "idref") ref
155159
156−parseMeta :: PandocMonad m => Element -> m Meta
160+parseMeta :: PandocMonad m => Element -> m (Maybe CoverId, Meta)
157161parseMeta content = do
158162 meta <- findElementE (dfName "metadata") content
159163 let dcspace (QName _ (Just "http://purl.org/dc/elements/1.1/") (Just "dc")) = True
160164 dcspace _ = False
161165 let dcs = filterChildrenName dcspace meta
162166 let r = foldr parseMetaItem nullMeta dcs
163− return r
167+ let coverId = findAttr (emptyName "content") =<< filterChild findCover meta
168+ return (coverId, r)
169+ where
170+ findCover e = maybe False (== "cover") (findAttr (emptyName "name") e)
164171
165172-- http://www.idpf.org/epub/30/spec/epub30-publications.html#sec-metadata-elem
166173parseMetaItem :: Element -> Meta -> Meta
test/Tests/Readers/EPUB.hs+33−1
@@ -44,10 +44,42 @@ featuresBag = [("img/check.gif","image/gif",1340)
4444 ,("img/multiscripts_and_greek_alphabet.png","image/png",10060)
4545 ]
4646
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+
4769tests :: [TestTree]
4870tests =
4971 [ testGroup "EPUB Mediabag"
5072 [ 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)
5284 ]
5385 ]
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.