epub-metadata 3.0 → 4.0
raw patch · 59 files changed
+2296/−1377 lines, 59 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Codec.Epub2.Archive: mkEpubArchive :: FilePath -> IO Archive
- Codec.Epub2.Archive: readArchive :: FilePath -> IO Archive
- Codec.Epub2.Archive: writeArchive :: FilePath -> Archive -> IO ()
- Codec.Epub2.IO: opfContentsFromBS :: (MonadError String m, MonadIO m) => ByteString -> m (FilePath, String)
- Codec.Epub2.IO: opfContentsFromDir :: (MonadError String m, MonadIO m) => FilePath -> m (FilePath, String)
- Codec.Epub2.IO: opfContentsFromZip :: (MonadError String m, MonadIO m) => FilePath -> m (FilePath, String)
- Codec.Epub2.IO: removeDoctype :: String -> String
- Codec.Epub2.IO: removeEncoding :: String -> String
- Codec.Epub2.IO: removeIllegalStartChars :: String -> String
- Codec.Epub2.Opf.Common: type MFItemId = String
- Codec.Epub2.Opf.Format.Guide: tellGuide :: MonadWriter (Seq Char) m => [GuideRef] -> m ()
- Codec.Epub2.Opf.Format.Guide: tellGuideRef :: MonadWriter (Seq Char) m => GuideRef -> m ()
- Codec.Epub2.Opf.Format.Manifest: tellManifest :: MonadWriter (Seq Char) m => [ManifestItem] -> m ()
- Codec.Epub2.Opf.Format.Manifest: tellManifestItem :: MonadWriter (Seq Char) m => ManifestItem -> m ()
- Codec.Epub2.Opf.Format.Metadata: tellContributor :: MonadWriter (Seq Char) m => Creator -> m ()
- Codec.Epub2.Opf.Format.Metadata: tellCreator :: MonadWriter (Seq Char) m => Creator -> m ()
- Codec.Epub2.Opf.Format.Metadata: tellDate :: MonadWriter (Seq Char) m => Date -> m ()
- Codec.Epub2.Opf.Format.Metadata: tellDescription :: MonadWriter (Seq Char) m => Description -> m ()
- Codec.Epub2.Opf.Format.Metadata: tellId :: MonadWriter (Seq Char) m => Identifier -> m ()
- Codec.Epub2.Opf.Format.Metadata: tellMetadata :: MonadWriter (Seq Char) m => Metadata -> m ()
- Codec.Epub2.Opf.Format.Metadata: tellSimpleString :: MonadWriter (Seq Char) m => String -> String -> m ()
- Codec.Epub2.Opf.Format.Metadata: tellTitle :: MonadWriter (Seq Char) m => Title -> m ()
- Codec.Epub2.Opf.Format.Package: formatPackage :: Bool -> Package -> String
- Codec.Epub2.Opf.Format.Spine: tellSpine :: MonadWriter (Seq Char) m => Spine -> m ()
- Codec.Epub2.Opf.Format.Spine: tellSpineItemref :: MonadWriter (Seq Char) m => SpineItemref -> m ()
- Codec.Epub2.Opf.Format.Util: data Seq a :: * -> *
- Codec.Epub2.Opf.Format.Util: formatSubline :: String -> Maybe String -> String
- Codec.Epub2.Opf.Format.Util: tellSeq :: MonadWriter (Seq a) m => [a] -> m ()
- Codec.Epub2.Opf.Package: Creator :: (Maybe String) -> (Maybe String) -> String -> Creator
- Codec.Epub2.Opf.Package: Date :: (Maybe String) -> String -> Date
- Codec.Epub2.Opf.Package: Description :: (Maybe String) -> String -> Description
- Codec.Epub2.Opf.Package: GuideRef :: String -> Maybe String -> String -> GuideRef
- Codec.Epub2.Opf.Package: Identifier :: String -> (Maybe String) -> String -> Identifier
- Codec.Epub2.Opf.Package: ManifestItem :: MFItemId -> MFItemHref -> MFItemMediaType -> ManifestItem
- Codec.Epub2.Opf.Package: Metadata :: [Title] -> [Creator] -> [Creator] -> [String] -> [Description] -> [String] -> [Date] -> [String] -> [String] -> [Identifier] -> [String] -> [String] -> [String] -> [String] -> [String] -> Metadata
- Codec.Epub2.Opf.Package: Package :: String -> String -> Metadata -> [ManifestItem] -> Spine -> [GuideRef] -> Package
- Codec.Epub2.Opf.Package: Spine :: MFItemId -> [SpineItemref] -> Spine
- Codec.Epub2.Opf.Package: SpineItemref :: MFItemId -> Maybe Bool -> SpineItemref
- Codec.Epub2.Opf.Package: Title :: (Maybe String) -> String -> Title
- Codec.Epub2.Opf.Package: data Creator
- Codec.Epub2.Opf.Package: data Date
- Codec.Epub2.Opf.Package: data Description
- Codec.Epub2.Opf.Package: data GuideRef
- Codec.Epub2.Opf.Package: data Identifier
- Codec.Epub2.Opf.Package: data ManifestItem
- Codec.Epub2.Opf.Package: data Metadata
- Codec.Epub2.Opf.Package: data Package
- Codec.Epub2.Opf.Package: data Spine
- Codec.Epub2.Opf.Package: data SpineItemref
- Codec.Epub2.Opf.Package: data Title
- Codec.Epub2.Opf.Package: emptyMetadata :: Metadata
- Codec.Epub2.Opf.Package: grHref :: GuideRef -> String
- Codec.Epub2.Opf.Package: grTitle :: GuideRef -> Maybe String
- Codec.Epub2.Opf.Package: grType :: GuideRef -> String
- Codec.Epub2.Opf.Package: instance Eq Package
- Codec.Epub2.Opf.Package: instance Show Package
- Codec.Epub2.Opf.Package: metaContributors :: Metadata -> [Creator]
- Codec.Epub2.Opf.Package: metaCoverages :: Metadata -> [String]
- Codec.Epub2.Opf.Package: metaCreators :: Metadata -> [Creator]
- Codec.Epub2.Opf.Package: metaDates :: Metadata -> [Date]
- Codec.Epub2.Opf.Package: metaDescriptions :: Metadata -> [Description]
- Codec.Epub2.Opf.Package: metaFormats :: Metadata -> [String]
- Codec.Epub2.Opf.Package: metaIds :: Metadata -> [Identifier]
- Codec.Epub2.Opf.Package: metaLangs :: Metadata -> [String]
- Codec.Epub2.Opf.Package: metaPublishers :: Metadata -> [String]
- Codec.Epub2.Opf.Package: metaRelations :: Metadata -> [String]
- Codec.Epub2.Opf.Package: metaRights :: Metadata -> [String]
- Codec.Epub2.Opf.Package: metaSources :: Metadata -> [String]
- Codec.Epub2.Opf.Package: metaSubjects :: Metadata -> [String]
- Codec.Epub2.Opf.Package: metaTitles :: Metadata -> [Title]
- Codec.Epub2.Opf.Package: metaTypes :: Metadata -> [String]
- Codec.Epub2.Opf.Package: mfiHref :: ManifestItem -> MFItemHref
- Codec.Epub2.Opf.Package: mfiId :: ManifestItem -> MFItemId
- Codec.Epub2.Opf.Package: mfiMediaType :: ManifestItem -> MFItemMediaType
- Codec.Epub2.Opf.Package: opGuide :: Package -> [GuideRef]
- Codec.Epub2.Opf.Package: opManifest :: Package -> [ManifestItem]
- Codec.Epub2.Opf.Package: opMeta :: Package -> Metadata
- Codec.Epub2.Opf.Package: opSpine :: Package -> Spine
- Codec.Epub2.Opf.Package: opUniqueId :: Package -> String
- Codec.Epub2.Opf.Package: opVersion :: Package -> String
- Codec.Epub2.Opf.Package: siIdRef :: SpineItemref -> MFItemId
- Codec.Epub2.Opf.Package: siLinear :: SpineItemref -> Maybe Bool
- Codec.Epub2.Opf.Package: spineItemrefs :: Spine -> [SpineItemref]
- Codec.Epub2.Opf.Package: spineToc :: Spine -> MFItemId
- Codec.Epub2.Opf.Package.Guide: GuideRef :: String -> Maybe String -> String -> GuideRef
- Codec.Epub2.Opf.Package.Guide: data GuideRef
- Codec.Epub2.Opf.Package.Guide: grHref :: GuideRef -> String
- Codec.Epub2.Opf.Package.Guide: grTitle :: GuideRef -> Maybe String
- Codec.Epub2.Opf.Package.Guide: grType :: GuideRef -> String
- Codec.Epub2.Opf.Package.Guide: instance Eq GuideRef
- Codec.Epub2.Opf.Package.Guide: instance Show GuideRef
- Codec.Epub2.Opf.Package.Manifest: ManifestItem :: MFItemId -> MFItemHref -> MFItemMediaType -> ManifestItem
- Codec.Epub2.Opf.Package.Manifest: data ManifestItem
- Codec.Epub2.Opf.Package.Manifest: instance Eq ManifestItem
- Codec.Epub2.Opf.Package.Manifest: instance Show ManifestItem
- Codec.Epub2.Opf.Package.Manifest: mfiHref :: ManifestItem -> MFItemHref
- Codec.Epub2.Opf.Package.Manifest: mfiId :: ManifestItem -> MFItemId
- Codec.Epub2.Opf.Package.Manifest: mfiMediaType :: ManifestItem -> MFItemMediaType
- Codec.Epub2.Opf.Package.Metadata: Creator :: (Maybe String) -> (Maybe String) -> String -> Creator
- Codec.Epub2.Opf.Package.Metadata: Date :: (Maybe String) -> String -> Date
- Codec.Epub2.Opf.Package.Metadata: Description :: (Maybe String) -> String -> Description
- Codec.Epub2.Opf.Package.Metadata: Identifier :: String -> (Maybe String) -> String -> Identifier
- Codec.Epub2.Opf.Package.Metadata: Metadata :: [Title] -> [Creator] -> [Creator] -> [String] -> [Description] -> [String] -> [Date] -> [String] -> [String] -> [Identifier] -> [String] -> [String] -> [String] -> [String] -> [String] -> Metadata
- Codec.Epub2.Opf.Package.Metadata: Title :: (Maybe String) -> String -> Title
- Codec.Epub2.Opf.Package.Metadata: data Creator
- Codec.Epub2.Opf.Package.Metadata: data Date
- Codec.Epub2.Opf.Package.Metadata: data Description
- Codec.Epub2.Opf.Package.Metadata: data Identifier
- Codec.Epub2.Opf.Package.Metadata: data Metadata
- Codec.Epub2.Opf.Package.Metadata: data Title
- Codec.Epub2.Opf.Package.Metadata: emptyMetadata :: Metadata
- Codec.Epub2.Opf.Package.Metadata: instance Eq Creator
- Codec.Epub2.Opf.Package.Metadata: instance Eq Date
- Codec.Epub2.Opf.Package.Metadata: instance Eq Description
- Codec.Epub2.Opf.Package.Metadata: instance Eq Identifier
- Codec.Epub2.Opf.Package.Metadata: instance Eq Metadata
- Codec.Epub2.Opf.Package.Metadata: instance Eq Title
- Codec.Epub2.Opf.Package.Metadata: instance Show Creator
- Codec.Epub2.Opf.Package.Metadata: instance Show Date
- Codec.Epub2.Opf.Package.Metadata: instance Show Description
- Codec.Epub2.Opf.Package.Metadata: instance Show Identifier
- Codec.Epub2.Opf.Package.Metadata: instance Show Metadata
- Codec.Epub2.Opf.Package.Metadata: instance Show Title
- Codec.Epub2.Opf.Package.Metadata: metaContributors :: Metadata -> [Creator]
- Codec.Epub2.Opf.Package.Metadata: metaCoverages :: Metadata -> [String]
- Codec.Epub2.Opf.Package.Metadata: metaCreators :: Metadata -> [Creator]
- Codec.Epub2.Opf.Package.Metadata: metaDates :: Metadata -> [Date]
- Codec.Epub2.Opf.Package.Metadata: metaDescriptions :: Metadata -> [Description]
- Codec.Epub2.Opf.Package.Metadata: metaFormats :: Metadata -> [String]
- Codec.Epub2.Opf.Package.Metadata: metaIds :: Metadata -> [Identifier]
- Codec.Epub2.Opf.Package.Metadata: metaLangs :: Metadata -> [String]
- Codec.Epub2.Opf.Package.Metadata: metaPublishers :: Metadata -> [String]
- Codec.Epub2.Opf.Package.Metadata: metaRelations :: Metadata -> [String]
- Codec.Epub2.Opf.Package.Metadata: metaRights :: Metadata -> [String]
- Codec.Epub2.Opf.Package.Metadata: metaSources :: Metadata -> [String]
- Codec.Epub2.Opf.Package.Metadata: metaSubjects :: Metadata -> [String]
- Codec.Epub2.Opf.Package.Metadata: metaTitles :: Metadata -> [Title]
- Codec.Epub2.Opf.Package.Metadata: metaTypes :: Metadata -> [String]
- Codec.Epub2.Opf.Package.Spine: Spine :: MFItemId -> [SpineItemref] -> Spine
- Codec.Epub2.Opf.Package.Spine: SpineItemref :: MFItemId -> Maybe Bool -> SpineItemref
- Codec.Epub2.Opf.Package.Spine: data Spine
- Codec.Epub2.Opf.Package.Spine: data SpineItemref
- Codec.Epub2.Opf.Package.Spine: instance Eq Spine
- Codec.Epub2.Opf.Package.Spine: instance Eq SpineItemref
- Codec.Epub2.Opf.Package.Spine: instance Show Spine
- Codec.Epub2.Opf.Package.Spine: instance Show SpineItemref
- Codec.Epub2.Opf.Package.Spine: siIdRef :: SpineItemref -> MFItemId
- Codec.Epub2.Opf.Package.Spine: siLinear :: SpineItemref -> Maybe Bool
- Codec.Epub2.Opf.Package.Spine: spineItemrefs :: Spine -> [SpineItemref]
- Codec.Epub2.Opf.Package.Spine: spineToc :: Spine -> MFItemId
- Codec.Epub2.Opf.Parse: parseEpub2Opf :: (MonadIO m, MonadError String m) => FilePath -> m Package
- Codec.Epub2.Opf.Parse: parseXmlToOpf :: (MonadIO m, MonadError String m) => String -> m Package
+ Codec.Epub.Data.Common: type MFItemId = String
+ Codec.Epub.Data.Guide: GuideRef :: String -> Maybe String -> MFItemId -> GuideRef
+ Codec.Epub.Data.Guide: data GuideRef
+ Codec.Epub.Data.Guide: grHref :: GuideRef -> MFItemId
+ Codec.Epub.Data.Guide: grTitle :: GuideRef -> Maybe String
+ Codec.Epub.Data.Guide: grType :: GuideRef -> String
+ Codec.Epub.Data.Guide: instance Eq GuideRef
+ Codec.Epub.Data.Guide: instance Show GuideRef
+ Codec.Epub.Data.Manifest: Manifest :: [ManifestItem] -> Manifest
+ Codec.Epub.Data.Manifest: ManifestItem :: MFItemId -> MFItemHref -> MFItemMediaType -> ManifestItem
+ Codec.Epub.Data.Manifest: data ManifestItem
+ Codec.Epub.Data.Manifest: instance Eq Manifest
+ Codec.Epub.Data.Manifest: instance Eq ManifestItem
+ Codec.Epub.Data.Manifest: instance Show Manifest
+ Codec.Epub.Data.Manifest: instance Show ManifestItem
+ Codec.Epub.Data.Manifest: mfiHref :: ManifestItem -> MFItemHref
+ Codec.Epub.Data.Manifest: mfiId :: ManifestItem -> MFItemId
+ Codec.Epub.Data.Manifest: mfiMediaType :: ManifestItem -> MFItemMediaType
+ Codec.Epub.Data.Manifest: newtype Manifest
+ Codec.Epub.Data.Metadata: Creator :: Maybe String -> Maybe String -> Maybe Int -> String -> Creator
+ Codec.Epub.Data.Metadata: Date :: (Maybe String) -> String -> Date
+ Codec.Epub.Data.Metadata: Description :: (Maybe String) -> String -> Description
+ Codec.Epub.Data.Metadata: Identifier :: Maybe String -> Maybe String -> Maybe String -> String -> Identifier
+ Codec.Epub.Data.Metadata: Metadata :: [Identifier] -> [Title] -> [String] -> [Creator] -> [Creator] -> [Date] -> Maybe String -> Maybe String -> Maybe String -> [String] -> [Description] -> [String] -> [String] -> [String] -> [String] -> [String] -> Metadata
+ Codec.Epub.Data.Metadata: Refinement :: String -> String -> String -> String -> Refinement
+ Codec.Epub.Data.Metadata: Title :: Maybe String -> Maybe String -> Maybe Int -> String -> Title
+ Codec.Epub.Data.Metadata: creatorFileAs :: Creator -> Maybe String
+ Codec.Epub.Data.Metadata: creatorRole :: Creator -> Maybe String
+ Codec.Epub.Data.Metadata: creatorSeq :: Creator -> Maybe Int
+ Codec.Epub.Data.Metadata: creatorText :: Creator -> String
+ Codec.Epub.Data.Metadata: data Creator
+ Codec.Epub.Data.Metadata: data Date
+ Codec.Epub.Data.Metadata: data Description
+ Codec.Epub.Data.Metadata: data Identifier
+ Codec.Epub.Data.Metadata: data Metadata
+ Codec.Epub.Data.Metadata: data Refinement
+ Codec.Epub.Data.Metadata: data Title
+ Codec.Epub.Data.Metadata: emptyMetadata :: Metadata
+ Codec.Epub.Data.Metadata: getModified :: [Refinement] -> Maybe String
+ Codec.Epub.Data.Metadata: idId :: Identifier -> Maybe String
+ Codec.Epub.Data.Metadata: idScheme :: Identifier -> Maybe String
+ Codec.Epub.Data.Metadata: idText :: Identifier -> String
+ Codec.Epub.Data.Metadata: idType :: Identifier -> Maybe String
+ Codec.Epub.Data.Metadata: instance Eq Creator
+ Codec.Epub.Data.Metadata: instance Eq Date
+ Codec.Epub.Data.Metadata: instance Eq Description
+ Codec.Epub.Data.Metadata: instance Eq Identifier
+ Codec.Epub.Data.Metadata: instance Eq Metadata
+ Codec.Epub.Data.Metadata: instance Eq Title
+ Codec.Epub.Data.Metadata: instance Show Creator
+ Codec.Epub.Data.Metadata: instance Show Date
+ Codec.Epub.Data.Metadata: instance Show Description
+ Codec.Epub.Data.Metadata: instance Show Identifier
+ Codec.Epub.Data.Metadata: instance Show Metadata
+ Codec.Epub.Data.Metadata: instance Show Title
+ Codec.Epub.Data.Metadata: metaContributors :: Metadata -> [Creator]
+ Codec.Epub.Data.Metadata: metaCoverages :: Metadata -> [String]
+ Codec.Epub.Data.Metadata: metaCreators :: Metadata -> [Creator]
+ Codec.Epub.Data.Metadata: metaDates :: Metadata -> [Date]
+ Codec.Epub.Data.Metadata: metaDescriptions :: Metadata -> [Description]
+ Codec.Epub.Data.Metadata: metaFormats :: Metadata -> [String]
+ Codec.Epub.Data.Metadata: metaIds :: Metadata -> [Identifier]
+ Codec.Epub.Data.Metadata: metaLangs :: Metadata -> [String]
+ Codec.Epub.Data.Metadata: metaModified :: Metadata -> Maybe String
+ Codec.Epub.Data.Metadata: metaPublishers :: Metadata -> [String]
+ Codec.Epub.Data.Metadata: metaRelations :: Metadata -> [String]
+ Codec.Epub.Data.Metadata: metaRights :: Metadata -> [String]
+ Codec.Epub.Data.Metadata: metaSource :: Metadata -> Maybe String
+ Codec.Epub.Data.Metadata: metaSubjects :: Metadata -> [String]
+ Codec.Epub.Data.Metadata: metaTitles :: Metadata -> [Title]
+ Codec.Epub.Data.Metadata: metaType :: Metadata -> Maybe String
+ Codec.Epub.Data.Metadata: refId :: Refinement -> String
+ Codec.Epub.Data.Metadata: refProp :: Refinement -> String
+ Codec.Epub.Data.Metadata: refScheme :: Refinement -> String
+ Codec.Epub.Data.Metadata: refText :: Refinement -> String
+ Codec.Epub.Data.Metadata: refineCreator :: [Refinement] -> (String, Creator) -> Creator
+ Codec.Epub.Data.Metadata: refineIdentifier :: [Refinement] -> Identifier -> Identifier
+ Codec.Epub.Data.Metadata: refineTitle :: [Refinement] -> (String, Title) -> Title
+ Codec.Epub.Data.Metadata: titleLang :: Title -> Maybe String
+ Codec.Epub.Data.Metadata: titleSeq :: Title -> Maybe Int
+ Codec.Epub.Data.Metadata: titleText :: Title -> String
+ Codec.Epub.Data.Metadata: titleType :: Title -> Maybe String
+ Codec.Epub.Data.Package: Package :: String -> String -> Package
+ Codec.Epub.Data.Package: data Package
+ Codec.Epub.Data.Package: instance Eq Package
+ Codec.Epub.Data.Package: instance Show Package
+ Codec.Epub.Data.Package: pkgUniqueId :: Package -> String
+ Codec.Epub.Data.Package: pkgVersion :: Package -> String
+ Codec.Epub.Data.Spine: Spine :: MFItemId -> [SpineItemref] -> Spine
+ Codec.Epub.Data.Spine: SpineItemref :: MFItemId -> Maybe Bool -> SpineItemref
+ Codec.Epub.Data.Spine: data Spine
+ Codec.Epub.Data.Spine: data SpineItemref
+ Codec.Epub.Data.Spine: instance Eq Spine
+ Codec.Epub.Data.Spine: instance Eq SpineItemref
+ Codec.Epub.Data.Spine: instance Show Spine
+ Codec.Epub.Data.Spine: instance Show SpineItemref
+ Codec.Epub.Data.Spine: siIdRef :: SpineItemref -> MFItemId
+ Codec.Epub.Data.Spine: siLinear :: SpineItemref -> Maybe Bool
+ Codec.Epub.Data.Spine: spineItemrefs :: Spine -> [SpineItemref]
+ Codec.Epub.Data.Spine: spineToc :: Spine -> MFItemId
+ Codec.Epub.Format: class Formattable a
+ Codec.Epub.Format: format :: Formattable a => a -> String
+ Codec.Epub.Format: instance Formattable Manifest
+ Codec.Epub.Format: instance Formattable Metadata
+ Codec.Epub.Format: instance Formattable Package
+ Codec.Epub.Format: instance Formattable Spine
+ Codec.Epub.Format: instance Formattable [GuideRef]
+ Codec.Epub.Format.Guide: formatGuide :: [GuideRef] -> String
+ Codec.Epub.Format.Manifest: formatManifest :: Manifest -> String
+ Codec.Epub.Format.Metadata: formatMetadata :: Metadata -> String
+ Codec.Epub.Format.Package: formatPackage :: Package -> String
+ Codec.Epub.Format.Spine: formatSpine :: Spine -> String
+ Codec.Epub.Format.Util: data Seq a :: * -> *
+ Codec.Epub.Format.Util: formatSubline :: String -> Maybe String -> String
+ Codec.Epub.Format.Util: tellSeq :: MonadWriter (Seq a) m => [a] -> m ()
+ Codec.Epub.IO: getPkgPathXmlFromBS :: (MonadError String m, MonadIO m) => ByteString -> m (FilePath, String)
+ Codec.Epub.IO: getPkgPathXmlFromDir :: (MonadError String m, MonadIO m) => FilePath -> m (FilePath, String)
+ Codec.Epub.IO: getPkgPathXmlFromZip :: (MonadError String m, MonadIO m) => FilePath -> m (FilePath, String)
+ Codec.Epub.IO: getPkgXmlFromZip :: (MonadError String m, MonadIO m) => FilePath -> m String
+ Codec.Epub.IO: mkEpubArchive :: FilePath -> IO Archive
+ Codec.Epub.IO: readArchive :: FilePath -> IO Archive
+ Codec.Epub.IO: writeArchive :: FilePath -> Archive -> IO ()
+ Codec.Epub.Parse: getGuide :: (MonadIO m, MonadError String m) => String -> m [GuideRef]
+ Codec.Epub.Parse: getManifest :: (MonadIO m, MonadError String m) => String -> m Manifest
+ Codec.Epub.Parse: getMetadata :: (MonadIO m, MonadError String m) => String -> m Metadata
+ Codec.Epub.Parse: getPackage :: (MonadIO m, MonadError String m) => String -> m Package
+ Codec.Epub.Parse: getSpine :: (MonadIO m, MonadError String m) => String -> m Spine
+ Codec.Epub.Parse.Guide: guideP :: ArrowXml a => a (NTree XNode) [GuideRef]
+ Codec.Epub.Parse.Manifest: manifestP :: ArrowXml a => a (NTree XNode) Manifest
+ Codec.Epub.Parse.Metadata: metadataP :: ArrowXml a => [Refinement] -> a (NTree XNode) Metadata
+ Codec.Epub.Parse.Package: packageP :: ArrowXml a => a (NTree XNode) Package
+ Codec.Epub.Parse.Refinements: refinementsP :: ArrowXml a => a (NTree XNode) [Refinement]
+ Codec.Epub.Parse.Spine: spineP :: ArrowXml a => a (NTree XNode) Spine
+ Codec.Epub.Parse.Util: atQTag :: ArrowXml a => QName -> a (NTree XNode) XmlTree
+ Codec.Epub.Parse.Util: dcName :: String -> QName
+ Codec.Epub.Parse.Util: mbGetAttrValue :: ArrowXml a => String -> a XmlTree (Maybe String)
+ Codec.Epub.Parse.Util: mbGetQAttrValue :: ArrowXml a => QName -> a XmlTree (Maybe String)
+ Codec.Epub.Parse.Util: mbQTagText :: ArrowXml a => QName -> a (NTree XNode) (Maybe String)
+ Codec.Epub.Parse.Util: notNullA :: ArrowList a => a [b] [b]
+ Codec.Epub.Parse.Util: opfName :: String -> QName
+ Codec.Epub.Parse.Util: text :: ArrowXml a => a (NTree XNode) String
+ Codec.Epub.Parse.Util: xmlName :: String -> QName
Files
- doc/examples/README +3/−0
- doc/examples/Setup.hs +6/−0
- doc/examples/epub-metadata-example.cabal +11/−0
- doc/examples/epub-metadata-example.hs +45/−0
- epub-metadata.cabal +56/−28
- src/Codec/Epub.hs +74/−0
- src/Codec/Epub/Data/Common.hs +14/−0
- src/Codec/Epub/Data/Guide.hs +23/−0
- src/Codec/Epub/Data/Manifest.hs +33/−0
- src/Codec/Epub/Data/Metadata.hs +212/−0
- src/Codec/Epub/Data/Package.hs +23/−0
- src/Codec/Epub/Data/Spine.hs +31/−0
- src/Codec/Epub/Format.hs +47/−0
- src/Codec/Epub/Format/Guide.hs +43/−0
- src/Codec/Epub/Format/Manifest.hs +40/−0
- src/Codec/Epub/Format/Metadata.hs +118/−0
- src/Codec/Epub/Format/Package.hs +34/−0
- src/Codec/Epub/Format/Spine.hs +46/−0
- src/Codec/Epub/Format/Util.hs +26/−0
- src/Codec/Epub/IO.hs +180/−0
- src/Codec/Epub/Parse.hs +134/−0
- src/Codec/Epub/Parse/Guide.hs +33/−0
- src/Codec/Epub/Parse/Manifest.hs +35/−0
- src/Codec/Epub/Parse/Metadata.hs +127/−0
- src/Codec/Epub/Parse/Package.hs +27/−0
- src/Codec/Epub/Parse/Refinements.hs +40/−0
- src/Codec/Epub/Parse/Spine.hs +36/−0
- src/Codec/Epub/Parse/Util.hs +96/−0
- src/Codec/Epub2/Archive.hs +0/−60
- src/Codec/Epub2/IO.hs +0/−141
- src/Codec/Epub2/Opf/Common.hs +0/−17
- src/Codec/Epub2/Opf/Format/Guide.hs +0/−31
- src/Codec/Epub2/Opf/Format/Manifest.hs +0/−27
- src/Codec/Epub2/Opf/Format/Metadata.hs +0/−93
- src/Codec/Epub2/Opf/Format/Package.hs +0/−40
- src/Codec/Epub2/Opf/Format/Spine.hs +0/−33
- src/Codec/Epub2/Opf/Format/Util.hs +0/−26
- src/Codec/Epub2/Opf/Package.hs +0/−44
- src/Codec/Epub2/Opf/Package/Guide.hs +0/−24
- src/Codec/Epub2/Opf/Package/Manifest.hs +0/−31
- src/Codec/Epub2/Opf/Package/Metadata.hs +0/−86
- src/Codec/Epub2/Opf/Package/Spine.hs +0/−34
- src/Codec/Epub2/Opf/Parse.hs +0/−287
- testsuite/Archive.hs +33/−1
- testsuite/Epub2/ParseGuide.hs +52/−0
- testsuite/Epub2/ParseMetadata.hs +149/−0
- testsuite/Epub3/ParseMetadata.hs +103/−0
- testsuite/OpfParse.hs +0/−264
- testsuite/ParseManifest.hs +58/−0
- testsuite/ParsePackage.hs +34/−0
- testsuite/ParseSpine.hs +59/−0
- testsuite/epub2-full.opf +67/−0
- testsuite/epub2-minimal.opf +24/−0
- testsuite/epub2-missingAll.opf +15/−0
- testsuite/epub3-full.opf +97/−0
- testsuite/test-main.hs +12/−2
- testsuite/testFull.opf +0/−69
- testsuite/testMinimal.opf +0/−24
- testsuite/testMissingAll.opf +0/−15
+ doc/examples/README view
@@ -0,0 +1,3 @@+This is a small project for maintaining the example code that goes into the aHaddock docs for Codec.Epub++To build it, you'll need to install epub-metadata as it's called for by this example code's cabal file.
+ doc/examples/Setup.hs view
@@ -0,0 +1,6 @@+#! /usr/bin/env runhaskell++import Distribution.Simple+++main = defaultMain
+ doc/examples/epub-metadata-example.cabal view
@@ -0,0 +1,11 @@+name: epub-metadata-example+version: 1.0+cabal-version: >= 1.8+build-type: Simple++executable epub-metadata-example+ main-is: epub-metadata-example.hs+ build-depends: base >= 3 && < 5,+ epub-metadata,+ mtl+ ghc-options: -Wall
+ doc/examples/epub-metadata-example.hs view
@@ -0,0 +1,45 @@+import Codec.Epub+import Codec.Epub.Data.Package+import Control.Monad.Error+++main :: IO ()+main = do+ -- epub-metadata actions are in MonadIO and MonadError, so we're+ -- using ErrorT here++ result <- runErrorT $ do++ -- Use the getPkgXmlFromZip action to extract the Package+ -- Document as an XML string. There are also other actions+ -- for reading from ByteStringS and directories.+ --+ -- See Codec.Epub.IO++ xmlString <- getPkgXmlFromZip "/path/to/book.epub"++ -- Now the sections of meta-information about the book can+ -- be extracted from that XML using functions like getPackage,+ -- getMetadata, etc.+ --+ -- See Codec.Epub.Parse++ pkg <- getPackage xmlString -- :: Codec.Epub.Data.Package+ meta <- getMetadata xmlString -- :: Codec.Epub.Data.Metadata++ -- Parts of these data structures can be used from here+ -- as needed+ --+ -- See Codec.Epub.Data.Package for pkgVersion below+ -- and the others in Codec.Epub.Data.*++ liftIO $ putStrLn $ pkgVersion pkg++ -- There is also pretty-print formatting of these data types+ -- in Codec.Epub.Data through the Formattable typeclass+ --+ -- See Codec.Epub.Format++ liftIO $ putStr $ format meta++ either putStrLn return result
epub-metadata.cabal view
@@ -1,5 +1,5 @@ name: epub-metadata-version: 3.0+version: 4.0 cabal-version: >= 1.8 build-type: Simple license: BSD3@@ -7,41 +7,62 @@ copyright: 2010-2013 Dino Morelli author: Dino Morelli maintainer: Dino Morelli <dino@ui3.info>-stability: stable+stability: experimental homepage: http://ui3.info/d/proj/epub-metadata.html-synopsis: Library for parsing and manipulating EPUB files and OPF package data-description: Library for parsing and manipulating EPUB files and OPF package data. An attempt has been made here to thoroughly implement the OPF Package Document specification.+synopsis: Library for parsing epub document metdata+description: Library for parsing and manipulating epub document metadata. Supports epub versions 2 and 3.++ This library was constructed by studying the IDPF specifications for epub documents found here <http://www.idpf.org/epub/20/spec/OPF_2.0.1_draft.htm> and here <http://www.idpf.org/epub/30/spec/epub30-publications.html> category: Codec, Text tested-with: GHC >= 7.6.2-extra-source-files: testsuite/*.epub,- testsuite/*.hs,- testsuite/*.opf,- testsuite/bookfiles/mimetype,- testsuite/bookfiles/foo,+extra-source-files: doc/examples/README+ doc/examples/*.cabal+ doc/examples/*.hs+ testsuite/*.epub+ testsuite/*.hs+ testsuite/*.opf+ testsuite/bookfiles/foo+ testsuite/bookfiles/mimetype testsuite/bookfiles/content/bar+ testsuite/Epub2/*.hs+ testsuite/Epub3/*.hs source-repository head type: darcs location: http://ui3.info/darcs/epub-metadata/ library- exposed-modules: Codec.Epub2.Archive- Codec.Epub2.IO,- Codec.Epub2.Opf.Common,- Codec.Epub2.Opf.Format.Guide,- Codec.Epub2.Opf.Format.Manifest,- Codec.Epub2.Opf.Format.Metadata,- Codec.Epub2.Opf.Format.Package,- Codec.Epub2.Opf.Format.Spine,- Codec.Epub2.Opf.Format.Util,- Codec.Epub2.Opf.Package,- Codec.Epub2.Opf.Package.Guide,- Codec.Epub2.Opf.Package.Manifest,- Codec.Epub2.Opf.Package.Metadata,- Codec.Epub2.Opf.Package.Spine,- Codec.Epub2.Opf.Parse- build-depends: base >= 3 && < 5, bytestring, containers,- directory, filepath, hxt >= 9, mtl, regex-compat,+ exposed-modules: Codec.Epub+ Codec.Epub.Data.Common+ Codec.Epub.Data.Guide+ Codec.Epub.Data.Manifest+ Codec.Epub.Data.Metadata+ Codec.Epub.Data.Package+ Codec.Epub.Data.Spine+ Codec.Epub.Format+ Codec.Epub.Format.Guide+ Codec.Epub.Format.Manifest+ Codec.Epub.Format.Metadata+ Codec.Epub.Format.Package+ Codec.Epub.Format.Spine+ Codec.Epub.Format.Util+ Codec.Epub.IO+ Codec.Epub.Parse+ Codec.Epub.Parse.Guide+ Codec.Epub.Parse.Manifest+ Codec.Epub.Parse.Metadata+ Codec.Epub.Parse.Package+ Codec.Epub.Parse.Refinements+ Codec.Epub.Parse.Spine+ Codec.Epub.Parse.Util+ build-depends: base >= 3 && < 5,+ bytestring,+ containers,+ directory,+ filepath,+ hxt >= 9,+ mtl,+ regex-compat, zip-archive hs-source-dirs: src ghc-options: -Wall@@ -49,7 +70,14 @@ test-suite test-epub-metadata type: exitcode-stdio-1.0 main-is: test-main.hs- build-depends: base >= 3 && < 5, bytestring, directory, filepath,- HUnit, hxt >= 9, mtl, regex-compat, zip-archive+ build-depends: base >= 3 && < 5,+ bytestring,+ directory,+ filepath,+ HUnit,+ hxt >= 9,+ mtl,+ regex-compat,+ zip-archive hs-source-dirs: src testsuite ghc-options: -Wall
+ src/Codec/Epub.hs view
@@ -0,0 +1,74 @@+-- Copyright: 2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{- |+ This is a library for parsing and manipulating epub document metadata. Almost all of the data stored in the epub Package Document can be worked with using this API. This includes the most useful block of data, the epub metadata. This library supports epub versions 2 and 3.++ This library was constructed by studying the IDPF specifications for epub documents found here <http://www.idpf.org/epub/20/spec/OPF_2.0.1_draft.htm> and here <http://www.idpf.org/epub/30/spec/epub30-publications.html>++ Consumers of epub-metadata will likely need this module, which re-exports the most useful parts concerning disk IO, parsing of the XML into Codec.Epub.Data.* data structures and formatting these data structures to be pretty-printed. Please also see Codec.Epub.Data.Metadata etc.+-}+module Codec.Epub (+ -- * Epub+ module Codec.Epub.Format,+ module Codec.Epub.IO,+ module Codec.Epub.Parse++ -- * Example+ -- $example1+ )+ where++import Codec.Epub.Format+import Codec.Epub.IO+import Codec.Epub.Parse+++{- $example1+> import Codec.Epub+> import Codec.Epub.Data.Package+> import Control.Monad.Error+> +> +> main :: IO ()+> main = do+> -- epub-metadata actions are in MonadIO and MonadError, so we're+> -- using ErrorT here+> +> result <- runErrorT $ do+> +> -- Use the getPkgXmlFromZip action to extract the Package+> -- Document as an XML string. There are also other actions+> -- for reading from ByteStringS and directories.+> --+> -- See Codec.Epub.IO+> +> xmlString <- getPkgXmlFromZip "/path/to/book.epub"+> +> -- Now the sections of meta-information about the book can+> -- be extracted from that XML using functions like getPackage,+> -- getMetadata, etc.+> --+> -- See Codec.Epub.Parse+> +> pkg <- getPackage xmlString -- :: Codec.Epub.Data.Package+> meta <- getMetadata xmlString -- :: Codec.Epub.Data.Metadata+> +> -- Parts of these data structures can be used from here+> -- as needed+> --+> -- See Codec.Epub.Data.Package for pkgVersion below+> -- and the others in Codec.Epub.Data.*+> +> liftIO $ putStrLn $ pkgVersion pkg+> +> -- There is also pretty-print formatting of these data types+> -- in Codec.Epub.Data through the Formattable typeclass+> --+> -- See Codec.Epub.Format+> +> liftIO $ putStr $ format meta+> +> either putStrLn return result+-}
+ src/Codec/Epub/Data/Common.hs view
@@ -0,0 +1,14 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{- | Data types for working with the metadata of epub documents++ This module helps to avoid circular imports for definitions needed by two or more other modules in this library.+-}+module Codec.Epub.Data.Common+ where+++-- | Attribute value used to relate things to each other in the OPF data+type MFItemId = String
+ src/Codec/Epub/Data/Guide.hs view
@@ -0,0 +1,23 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{- | Data types for working with the metadata of epub documents++ This contains the guide section in an epub2 OPF document. It has been deprecated in the epub3 spec and should not be present in epub3 documents.+-}+module Codec.Epub.Data.Guide+ ( GuideRef (..)+ )+ where++import Codec.Epub.Data.Common+++-- | package\/guide\/reference tag+data GuideRef = GuideRef+ { grType :: String -- ^ type attribute. Must follow 13th edition of the Chicago Manual of Style+ , grTitle :: Maybe String -- ^ title attribute+ , grHref :: MFItemId -- ^ href attribute. Must reference an item in the manifest+ }+ deriving (Eq, Show)
+ src/Codec/Epub/Data/Manifest.hs view
@@ -0,0 +1,33 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{- | Data types for working with the metadata of epub documents++ This contains the manifest information from an epub document.+-}+module Codec.Epub.Data.Manifest+ ( Manifest (..)+ , ManifestItem (..)+ )+ where++import Codec.Epub.Data.Common+++-- | manifest attribute values+type MFItemHref = String+type MFItemMediaType = String+++-- | package\/manifest\/item tag+data ManifestItem = ManifestItem+ { mfiId :: MFItemId -- ^ id attribute+ , mfiHref :: MFItemHref -- ^ href attribute+ , mfiMediaType :: MFItemMediaType -- ^ media-type attribute+ }+ deriving (Eq, Show)+++newtype Manifest = Manifest [ManifestItem]+ deriving (Eq, Show)
+ src/Codec/Epub/Data/Metadata.hs view
@@ -0,0 +1,212 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{- | Data types for working with the metadata of epub documents++ This module defines the Metadata structure which contains most of the taxonomic information about the literary work. Metadata is probably the most important data structure in this library.++ Both commonly-used versions of epub (2.x and 3.x) are supported by these types.+-}+module Codec.Epub.Data.Metadata+ ( Metadata (..)+ , Identifier (..)+ , Title (..)+ , Creator (..)+ , Date (..)+ , Description (..)+ , Refinement (..)+ , emptyMetadata+ , refineIdentifier+ , refineTitle+ , getModified+ , refineCreator+ )+ where++import Control.Monad ( mplus )+import Data.List ( find )+++{- | Refinements represent meta tags within the metadata section+ that refine other tags. These are used during the parsing phase+ and are discarded as their information is slotted into the data+ they refine (the types below like Creator, Title, etc..)++ This is specific to epub3+-}+data Refinement = Refinement+ { refId :: String -- ^ id attribute+ , refProp :: String -- ^ property attribute+ , refScheme :: String -- ^ scheme attribute+ , refText :: String -- ^ meta tag text+ }+++{- Used for locating specific meta information in a list of+ Refinements. The data from these meta tags is related to other+ tags by an XML id attribute.+-}+findByIdProp :: String -> String -> [Refinement] -> Maybe Refinement+findByIdProp i prop = find (\r -> refId r == i && refProp r == prop)+++-- | package\/metadata\/dc:identifier tag+data Identifier = Identifier+ { idId :: Maybe String -- ^ id attribute+ , idType :: Maybe String -- ^ identifier-type property from meta tag+ , idScheme :: Maybe String -- ^ scheme from attribute or meta tag+ , idText :: String -- ^ identifier tag text+ }+ deriving (Eq, Show)+++{- | Used internally by Codec.Epub.Parse.Metadata to merge epub3 meta+ tag info into the data gathered from an identifier tag+-}+refineIdentifier :: [Refinement] -> Identifier -> Identifier+refineIdentifier refinements ident = assignScheme . assignType $ ident+ where+ meta = findByIdProp (maybe "" id $ idId ident)+ "identifier-type" refinements++ assignType ident' = ident' { idType = refText `fmap` meta }++ assignScheme ident' =+ let existingScheme = idScheme ident'+ in ident' { idScheme = existingScheme `mplus`+ (refScheme `fmap` meta) }+++-- | package\/metadata\/dc:title tag+data Title = Title+ { titleLang :: Maybe String -- ^ lang attribute+ , titleType :: Maybe String -- ^ title-type property from meta tag+ , titleSeq :: Maybe Int -- ^ display-sequence property from meta+ , titleText :: String -- ^ title tag text+ }+ deriving (Eq, Show)+++{- | Used internally by Codec.Epub.Parse.Metadata to merge epub3 meta+ tag info into the data gathered from a title tag+-}+refineTitle :: [Refinement] -> (String, Title) -> Title+refineTitle refinements (elid, title) = assignSeq . assignType $ title+ where+ assignType title' =+ let newTy = refText `fmap`+ findByIdProp elid "title-type" refinements+ in title' { titleType = newTy }++ assignSeq title' =+ let sq = maybe Nothing (Just . read . refText) $+ findByIdProp elid "display-seq" refinements+ in title' { titleSeq = sq }+++{- | package\/metadata\/dc:creator or package\/metadata\/dc:contributor+ tags++ This structure is used for both contributor and creator as they are exactly the same.+-}+data Creator = Creator+ { creatorRole :: Maybe String -- ^ role from attribute or meta tag+ , creatorFileAs :: Maybe String -- ^ file-as from attribute or meta tag+ , creatorSeq :: Maybe Int -- ^ display-sequence property from meta+ , creatorText :: String -- ^ creator or contributor tag text+ }+ deriving (Eq, Show)+++{- | Used internally by Codec.Epub.Parse.Metadata to merge epub3 meta+ tag info into the data gathered from contributor and creator tags+-}+refineCreator :: [Refinement] -> (String, Creator) -> Creator+refineCreator refinements (elid, creator) =+ assignSeq . assignFileAs . assignRole $ creator++ where+ assignRole creator' =+ let existingRole = creatorRole creator'+ metaRole = maybe Nothing (Just . refText) $+ findByIdProp elid "role" refinements+ in creator' { creatorRole = existingRole `mplus` metaRole }++ assignFileAs creator' =+ let existingFileAs = creatorFileAs creator'+ metaFileAs = maybe Nothing (Just . refText) $+ findByIdProp elid "file-as" refinements+ in creator' { creatorFileAs = existingFileAs `mplus` metaFileAs }++ assignSeq creator' =+ let sq = maybe Nothing (Just . read . refText) $+ findByIdProp elid "display-seq" refinements+ in creator' { creatorSeq = sq }+++-- | package\/metadata\/dc:date tag, opf:event attribute, text+data Date = Date (Maybe String) String+ deriving (Eq, Show)+++{- | Used internally by Codec.Epub.Parse.Metadata to populate the+ metadata modified field with data extracted from the epub3 meta+ tag with property dcterms:modified+-}+getModified :: [Refinement] -> Maybe String+getModified refinements =+ refText `fmap` findByIdProp "" "dcterms:modified" refinements+++-- | package\/metadata\/dc:description tag, xml:lang attribute, text+data Description = Description (Maybe String) String+ deriving (Eq, Show)+++{- | package\/metadata tag++ This is perhaps the most useful data structure in this library. It+ contains most of the information tools will want to use to+ organize epub documents.+-}+data Metadata = Metadata+ { metaIds :: [Identifier] -- ^ at least one required+ , metaTitles :: [Title] -- ^ at least one required+ , metaLangs :: [String] -- ^ dc:language tags, at least one required+ , metaContributors :: [Creator]+ , metaCreators :: [Creator]+ , metaDates :: [Date]+ , metaModified :: Maybe String -- ^ meta tag with property dcterms:modified, present only in epub3 documents+ , metaSource :: Maybe String -- ^ dc:source tags+ , metaType :: Maybe String -- ^ dc:type tags+ , metaCoverages :: [String] -- ^ dc:coverage tags+ , metaDescriptions :: [Description]+ , metaFormats :: [String] -- ^ dc:format tags+ , metaPublishers :: [String] -- ^ dc:publisher tags+ , metaRelations :: [String] -- ^ dc:relation tags+ , metaRights :: [String] -- ^ dc:rights tags+ , metaSubjects :: [String] -- ^ dc:subject tags+ }+ deriving (Eq, Show)++-- | Note: This isn't valid as-is, some required values are empty lists!+emptyMetadata :: Metadata+emptyMetadata = Metadata+ { metaIds = [] -- one required+ , metaTitles = [] -- one required+ , metaLangs = [] -- one required+ , metaContributors = []+ , metaCreators = []+ , metaDates = []+ , metaModified = Nothing+ , metaSource = Nothing+ , metaType = Nothing+ , metaCoverages = []+ , metaDescriptions = []+ , metaFormats = []+ , metaPublishers = []+ , metaRelations = []+ , metaRights = []+ , metaSubjects = []+ }
+ src/Codec/Epub/Data/Package.hs view
@@ -0,0 +1,23 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{- | Data type for working with epub package info++ This contains the package section from an epub document. In the+ OPF XML document, this tag contains the other tags (metadata,+ manifest...)+-}+module Codec.Epub.Data.Package+ ( Package (..)+ )+ where+++{- | package tag+-}+data Package = Package+ { pkgVersion :: String -- ^ version attribute. This contains which epub specification version (2.x or 3.x) this document conforms to.+ , pkgUniqueId :: String -- ^ unique-identifier attribute. This should relate to one metaIdentifier in the Metadata and is intended to represent this book's unique identifier.+ }+ deriving (Eq, Show)
+ src/Codec/Epub/Data/Spine.hs view
@@ -0,0 +1,31 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{- | Data types for working with the metadata of epub documents++ The spine represents a default ordering of items in the manifest.+-}+module Codec.Epub.Data.Spine+ ( Spine (..)+ , SpineItemref (..)+ )+ where++import Codec.Epub.Data.Common+++-- | package\/spine\/itemref tag+data SpineItemref = SpineItemref+ { siIdRef :: MFItemId -- ^ idref attribute. Must reference an item in the manifest+ , siLinear :: Maybe Bool -- ^ linear attribute+ }+ deriving (Eq, Show)+++-- | package\/spine tag+data Spine = Spine+ { spineToc :: MFItemId -- ^ toc attribute. Must reference the NCX in the manifest+ , spineItemrefs :: [SpineItemref] -- ^ at least one required+ }+ deriving (Eq, Show)
+ src/Codec/Epub/Format.hs view
@@ -0,0 +1,47 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{-# LANGUAGE FlexibleInstances #-}++{- | Module for pretty-printing epub meta-information++ Defined here is the Formattable typeclass and instances for the+ top-level epub data structures. The intention is that consumers+ use the format function on any of these otherwise disparate types.+-}+module Codec.Epub.Format+ ( Formattable (..)+ )+ where++import Codec.Epub.Data.Guide+import Codec.Epub.Data.Manifest+import Codec.Epub.Data.Metadata+import Codec.Epub.Data.Package+import Codec.Epub.Data.Spine+import Codec.Epub.Format.Guide+import Codec.Epub.Format.Manifest+import Codec.Epub.Format.Metadata+import Codec.Epub.Format.Package+import Codec.Epub.Format.Spine+++class Formattable a where+ format :: a -> String+++instance Formattable [GuideRef] where+ format = formatGuide++instance Formattable Manifest where+ format = formatManifest++instance Formattable Metadata where+ format = formatMetadata++instance Formattable Package where+ format = formatPackage++instance Formattable Spine where+ format = formatSpine
+ src/Codec/Epub/Format/Guide.hs view
@@ -0,0 +1,43 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{-# LANGUAGE FlexibleContexts #-}++{- | Module for pretty-printing epub guide info++ Used internally by Codec.Epub.Format+-}+module Codec.Epub.Format.Guide+ ( formatGuide+ )+ where++import Control.Monad.Writer.Lazy+import Data.Foldable ( toList )+import Text.Printf++import Codec.Epub.Format.Util+import Codec.Epub.Data.Guide+++tellGuideRef :: MonadWriter (Seq Char) m => GuideRef -> m ()+tellGuideRef (GuideRef grty title href) =+ tellSeq $ printf " type: %s%s, href: %s\n"+ grty (titleToString title) href++ where+ titleToString = maybe "" (printf ", title: %s")+++tellGuide :: MonadWriter (Seq Char) m => [GuideRef] -> m ()+tellGuide [] = return ()+tellGuide grs = do+ tellSeq "guide items:\n"+ mapM_ tellGuideRef grs+++{- | Format an epub Guide structure for pretty printing+-}+formatGuide :: [GuideRef] -> String+formatGuide guideRefs = toList . execWriter $ tellGuide guideRefs
+ src/Codec/Epub/Format/Manifest.hs view
@@ -0,0 +1,40 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{-# LANGUAGE FlexibleContexts #-}++{- | Module for pretty-printing epub manifest info++ Used internally by Codec.Epub.Format+-}+module Codec.Epub.Format.Manifest+ ( formatManifest+ )+ where++import Control.Monad.Writer.Lazy+import Data.Foldable ( toList )+import Text.Printf++import Codec.Epub.Format.Util+import Codec.Epub.Data.Manifest+++tellManifestItem :: MonadWriter (Seq Char) m => ManifestItem -> m ()+tellManifestItem (ManifestItem mfId href mediaType) =+ tellSeq $ printf " id: %s, href: %s, media-type: %s\n"+ mfId href mediaType+++tellManifest :: MonadWriter (Seq Char) m => [ManifestItem] -> m ()+tellManifest mas = do+ tellSeq "manifest items:\n"+ mapM_ tellManifestItem mas+++{- | Format an epub Manifest structure for pretty printing+-}+formatManifest :: Manifest -> String+formatManifest (Manifest mis) = toList . execWriter+ $ tellManifest mis
+ src/Codec/Epub/Format/Metadata.hs view
@@ -0,0 +1,118 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{-# LANGUAGE FlexibleContexts #-}++{- | Module for pretty-printing epub metadata info++ Used internally by Codec.Epub.Format+-}+module Codec.Epub.Format.Metadata+ ( formatMetadata+ )+ where++import Control.Monad.Writer.Lazy+import Data.Foldable ( toList )+import Text.Printf++import Codec.Epub.Format.Util+import Codec.Epub.Data.Metadata+++tellTitle :: MonadWriter (Seq Char) m => Title -> m ()+tellTitle (Title Nothing Nothing Nothing text) =+ tellSeq $ printf "title: %s\n" text+tellTitle title =+ tellSeq $ printf "title\n%s%s%s%s"+ (formatSubline "text" (Just $ titleText title))+ (formatSubline "lang" (titleLang title))+ (formatSubline "title-type" (titleType title))+ (formatSubline "display-seq" (show `fmap` titleSeq title))+++tellCreator :: MonadWriter (Seq Char) m => Creator -> m ()+tellCreator (Creator Nothing Nothing Nothing creator) =+ tellSeq $ printf "creator: %s\n" creator+tellCreator (Creator role fileAs dseq creator) =+ tellSeq $ printf "creator\n%s%s%s%s"+ (formatSubline "text" (Just creator))+ (formatSubline "file-as" fileAs)+ (formatSubline "role" role)+ (formatSubline "display-seq" (show `fmap` dseq))+++tellContributor :: MonadWriter (Seq Char) m => Creator -> m ()+tellContributor (Creator Nothing Nothing Nothing contributor) =+ tellSeq $ printf "contributor: %s\n" contributor+tellContributor (Creator role fileAs dseq contributor) =+ tellSeq $ printf "contributor\n%s%s%s%s"+ (formatSubline "text" (Just contributor))+ (formatSubline "file-as" fileAs)+ (formatSubline "role" role)+ (formatSubline "display-seq" (show `fmap` dseq))+++tellDate :: MonadWriter (Seq Char) m => Date -> m ()+tellDate (Date Nothing date) =+ tellSeq $ printf "date: %s\n" date+tellDate (Date event date) =+ tellSeq $ printf "date\n%s%s"+ (formatSubline "event" event)+ (formatSubline "text" (Just date))+++tellId :: MonadWriter (Seq Char) m => Identifier -> m ()+tellId ident =+ tellSeq $ printf "identifier\n%s%s%s%s"+ (formatSubline "id" (idId ident))+ (formatSubline "identifier-type" (idType ident))+ (formatSubline "scheme" (idScheme ident))+ (formatSubline "text" (Just . idText $ ident))+++tellDescription :: MonadWriter (Seq Char) m => Description -> m ()+tellDescription (Description Nothing text) =+ tellSeq $ printf "description: %s\n" text+tellDescription (Description lang text) =+ tellSeq $ printf "description\n%s%s"+ (formatSubline "lang" lang)+ (formatSubline "text" (Just text))+++tellSimpleString :: MonadWriter (Seq Char) m => String -> String -> m ()+tellSimpleString label = tellSeq . (printf "%s: %s\n" label)+++tellSimpleMbString :: MonadWriter (Seq Char) m => String+ -> Maybe String -> m ()+tellSimpleMbString _ Nothing = return ()+tellSimpleMbString label (Just s) = tellSimpleString label s+++tellMetadata :: MonadWriter (Seq Char) m => Metadata -> m ()+tellMetadata (Metadata ids titles langs contributors creators dates modified source mType coverage desc format publisher relation rights subjects) = do+ mapM_ tellId ids+ mapM_ tellTitle titles+ mapM_ (tellSimpleString "language") langs+ mapM_ tellContributor contributors+ mapM_ tellCreator creators+ mapM_ tellDate dates+ tellSimpleMbString "modified" modified+ tellSimpleMbString "source" source+ tellSimpleMbString "type" mType+ mapM_ (tellSimpleString "coverage") coverage+ mapM_ tellDescription desc+ mapM_ (tellSimpleString "format") format+ mapM_ (tellSimpleString "publisher") publisher+ mapM_ (tellSimpleString "relation") relation+ mapM_ (tellSimpleString "rights") rights+ mapM_ (tellSimpleString "subject") subjects+++{- | Format an epub Metadata structure for pretty printing+-}+formatMetadata :: Metadata -> String+formatMetadata meta = toList . execWriter+ $ tellMetadata meta
+ src/Codec/Epub/Format/Package.hs view
@@ -0,0 +1,34 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{-# LANGUAGE FlexibleContexts #-}++{- | Module for pretty-printing epub package info++ Used internally by Codec.Epub.Format+-}+module Codec.Epub.Format.Package+ ( formatPackage+ )+ where++import Control.Monad.Writer.Lazy+import Data.Foldable ( toList )++import Codec.Epub.Data.Package+import Codec.Epub.Format.Util+++tellPackage :: MonadWriter (Seq Char) m => (String, String) -> m ()+tellPackage (version, uniqueId) = do+ tellSeq "package\n"+ tellSeq $ formatSubline "version" (Just version)+ tellSeq $ formatSubline "unique-identifier" (Just uniqueId)+++{- | Format an epub Package structure for pretty printing+-}+formatPackage :: Package -> String+formatPackage (Package v u) = toList . execWriter+ $ tellPackage (v, u)
+ src/Codec/Epub/Format/Spine.hs view
@@ -0,0 +1,46 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{-# LANGUAGE FlexibleContexts #-}++{- | Module for pretty-printing epub spine info++ Used internally by Codec.Epub.Format+-}+module Codec.Epub.Format.Spine+ ( formatSpine+ )+ where++import Control.Monad.Writer.Lazy+import Data.Foldable ( toList )+import Text.Printf++import Codec.Epub.Format.Util+import Codec.Epub.Data.Spine+++tellSpineItemref :: MonadWriter (Seq Char) m => SpineItemref -> m ()+tellSpineItemref (SpineItemref idref linear) =+ tellSeq $ printf " idref: %s%s\n" idref (linearToString linear)++ where+ boolToYn True = "yes"+ boolToYn False = "no"++ linearToString Nothing = ""+ linearToString (Just l) = printf ", linear: %s" (boolToYn l)+++tellSpine :: MonadWriter (Seq Char) m => Spine -> m ()+tellSpine (Spine toc itemRefs) = do+ tellSeq $ printf "spine toc: %s, itemrefs:\n" toc+ mapM_ tellSpineItemref itemRefs+++{- | Format an epub Spine structure for pretty printing+-}+formatSpine :: Spine -> String+formatSpine sp = toList . execWriter+ $ tellSpine sp
+ src/Codec/Epub/Format/Util.hs view
@@ -0,0 +1,26 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{-# LANGUAGE FlexibleContexts #-}++-- | Utility functions shared by several formatting modules+module Codec.Epub.Format.Util+ ( formatSubline+ , tellSeq+ , Seq+ )+ where++import Control.Monad.Writer.Lazy+import Data.Sequence ( Seq, fromList )+import Text.Printf+++formatSubline :: String -> Maybe String -> String+formatSubline _ Nothing = ""+formatSubline key (Just value) = printf " %s: %s\n" key value+++tellSeq :: MonadWriter (Seq a) m => [a] -> m ()+tellSeq = tell . fromList
+ src/Codec/Epub/IO.hs view
@@ -0,0 +1,180 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{-# LANGUAGE FlexibleContexts #-}++-- | Functions for performing some IO operations on epub files++module Codec.Epub.IO+ ( getPkgXmlFromZip+ , getPkgPathXmlFromZip+ , getPkgPathXmlFromBS+ , getPkgPathXmlFromDir+ , mkEpubArchive+ , readArchive+ , writeArchive+ )+ where++import Codec.Archive.Zip+import Control.Arrow.ListArrows ( (>>>), deep )+import Control.Exception+import Control.Monad.Error+import qualified Data.ByteString.Char8 as BS+import Data.ByteString.Lazy ( fromChunks )+import qualified Data.ByteString.Lazy as B+import qualified Data.ByteString.Lazy.Char8 as BL+import Data.List+import System.Directory+import System.FilePath+import Text.XML.HXT.Arrow.ReadDocument ( readString )+import Text.XML.HXT.Arrow.XmlArrow ( getAttrValue, hasName, isElem )+import Text.XML.HXT.Arrow.XmlState ( no, runX, withValidate )+++locateRootFile :: (MonadIO m, MonadError String m) =>+ FilePath -> String -> m FilePath+locateRootFile containerPath' containerDoc = do+ result <- liftIO $ runX (+ readString [withValidate no] containerDoc+ >>> deep (isElem >>> hasName "rootfile")+ >>> getAttrValue "full-path"+ )++ case result of+ (p : []) -> return p+ _ -> throwError $+ "ERROR: rootfile full-path missing from " ++ containerPath'+++-- | Extract a file from a zip archive throwing an error on failure+fileFromArchive :: MonadError String m =>+ FilePath -> Archive -> m String+fileFromArchive filePath archive = do+ let mbEntry = findEntryByPath filePath archive+ maybe+ (throwError $ "Unable to locate file " ++ filePath)+ (return . BL.unpack . fromEntry) mbEntry+++{- | The static location of the container.xml, as specified by the+ epub docs+-}+containerPath :: FilePath+containerPath = "META-INF/container.xml"+++{- | Get the path and contents of the epub Package Document from+ a ByteString representing an epub (zip) file+-}+getPkgPathXmlFromBS :: (MonadError String m, MonadIO m)+ => BS.ByteString -- ^ contents of the zip file+ -> m (FilePath, String) -- ^ path (within the epub archive) and contents of the epub Package Document+getPkgPathXmlFromBS strictBytes = do+ -- Need to turn this strict byte string into a lazy one+ let lazyBytes = fromChunks [strictBytes]+ result <- liftIO $ ( try $ evaluate+ (toArchive lazyBytes) :: IO (Either SomeException Archive) )+ archive <- either (throwError . show) return result++ {- We need to first extract the container.xml file+ It's required to have a certain path and name in the epub+ and contains the path to what we really want, the .opf file.+ -}+ containerDoc <- fileFromArchive containerPath archive++ rootPath <- locateRootFile containerPath containerDoc++ -- Now that we have the path to the .opf file, extract it+ rootContents <- fileFromArchive rootPath archive++ return (rootPath, rootContents)+++{- | Get the path and contents of the epub Package Document from+ an epub (zip) file+-}+getPkgPathXmlFromZip :: (MonadError String m, MonadIO m)+ => FilePath -- ^ path to epub zip file+ -> m (FilePath, String) -- ^ path (within the epub archive) and contents of the epub Package Document+getPkgPathXmlFromZip zipPath = do+ {- Strictly read this file into a ByteString, send to + getPkgPathXmlFromBS+ -}+ zipFileBytes <- liftIO $ BS.readFile zipPath+ getPkgPathXmlFromBS zipFileBytes+++-- | Get the contents of the epub Package Document from an epub (zip) file+getPkgXmlFromZip :: (MonadError String m, MonadIO m)+ => FilePath -- ^ path to epub zip file+ -> m String -- ^ contents of the epub Package Document+getPkgXmlFromZip zipPath = snd `liftM` getPkgPathXmlFromZip zipPath+++{- | Get the path and contents of the epub Package Document from+ a directory containing the files from an epub file (as in:+ it's been unzipped into a dir)+-}+getPkgPathXmlFromDir :: (MonadError String m, MonadIO m)+ => FilePath -- ^ directory path+ -> m (FilePath, String) -- ^ path (within the epub archive) and contents of the epub Package Document+getPkgPathXmlFromDir dir = do+ {- We need to first extract the container.xml file+ It's required to have a certain path and name in the epub+ and contains the path to what we really want, the .opf file.+ -}+ liftIO $ setCurrentDirectory dir++ containerDoc <- liftIO $ readFile containerPath++ rootPath <- locateRootFile (dir </> containerPath) containerDoc++ -- Now that we have the path to the .opf file, load it+ rootContents <- liftIO $ readFile rootPath++ return (rootPath, rootContents)+++{- Recursively get a list of all files starting with the supplied+ parent directory. Excluding the directories themselves and ANY + dotfiles.+-}+getRecursiveContents :: FilePath -> IO [FilePath]+getRecursiveContents parent = do+ fullContents <- getDirectoryContents parent+ let contents = filter (not . isPrefixOf ".") fullContents+ paths <- forM contents $ \name -> do+ let path = parent </> name+ isDirectory <- doesDirectoryExist path+ if isDirectory+ then getRecursiveContents path+ else return [path]+ return $ concat paths+++{- | Construct a zip Archive containing epub book data from the + specified directory+-}+mkEpubArchive :: FilePath -> IO Archive+mkEpubArchive rootDir = do+ setCurrentDirectory rootDir++ let mimetype = ["mimetype"]+ allFiles <- getRecursiveContents "."+ let restFiles = allFiles \\ mimetype++ flip (addFilesToArchive [OptRecursive]) restFiles >=>+ flip (addFilesToArchive []) ["mimetype"]+ $ emptyArchive+++-- | Read a zip Archive from disk+readArchive :: FilePath -> IO Archive+readArchive = fmap toArchive . B.readFile+++-- | Write a zip Archive to disk using the specified filename+writeArchive :: FilePath -> Archive -> IO ()+writeArchive zipPath = (B.writeFile zipPath) . fromArchive
+ src/Codec/Epub/Parse.hs view
@@ -0,0 +1,134 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{-# LANGUAGE Arrows, FlexibleContexts #-}++{- | The main parsing interface, these get* functions are intended+ to be used by consumers of this library++ This module is called Parse because it invokes the XML parsing+ machinery of this library, but consumers of the library do not+ have to interact with HXT, Arrows or XML directly.+-}+module Codec.Epub.Parse+ ( getGuide+ , getManifest+ , getMetadata+ , getPackage+ , getSpine+ )+ where++import Control.Arrow.ListArrows+import Control.Monad.Error+import Text.Regex+import Text.XML.HXT.Arrow.Namespace ( propagateNamespaces )+import Text.XML.HXT.Arrow.XmlState ( no, runX, withValidate )+import Text.XML.HXT.Arrow.XmlState.TypeDefs+import Text.XML.HXT.Arrow.ReadDocument ( readString )+import Text.XML.HXT.DOM.TypeDefs++import Codec.Epub.Data.Guide+import Codec.Epub.Data.Manifest+import Codec.Epub.Data.Metadata+import Codec.Epub.Data.Package+import Codec.Epub.Data.Spine+import Codec.Epub.Parse.Guide+import Codec.Epub.Parse.Manifest+import Codec.Epub.Parse.Metadata+import Codec.Epub.Parse.Package+import Codec.Epub.Parse.Refinements+import Codec.Epub.Parse.Spine+++{- An evil hack to remove *ILLEGAL* characters before the XML+ declaration. Why do people write software that does this?+ Can't they follow directions?+-}+removeIllegalStartChars :: String -> String+removeIllegalStartChars = dropWhile (/= '<')+++-- An evil hack to remove encoding from the document+removeEncoding :: String -> String+removeEncoding = flip (subRegex + (mkRegexWithOpts " +encoding=\"UTF-8\"" False False)) ""+++-- An evil hack to remove any \<!DOCTYPE ...\> from the document+removeDoctype :: String -> String+removeDoctype = flip (subRegex + (mkRegexWithOpts "<!DOCTYPE [^>]*>" False True)) ""+++{- Extract the epub OPF Package data contained in the supplied + XML string+-}+performParse :: (MonadIO m, MonadError String m) =>+ IOSLA (XIOState ()) XmlTree b -> String -> m b+performParse parser contents = do+ {- Improper encoding and schema declarations have been causing+ havok with this parse, cruelly strip them out. -}+ let cleanedContents = removeIllegalStartChars . removeEncoding+ . removeDoctype $ contents+ + result <- liftIO $ runX (+ readString [withValidate no] cleanedContents+ >>> propagateNamespaces+ >>> parser+ )++ case result of+ (r : []) -> return r+ _ -> throwError+ "ERROR: FIXME with a better message"+++{- | Parse epub guide items from a String representing the epub XML+ Package Document+-}+getGuide :: (MonadIO m, MonadError String m) =>+ String -> m [GuideRef]+getGuide = performParse guideP+++{- | Parse epub manifest data from a String representing the epub XML+ Package Document+-}+getManifest :: (MonadIO m, MonadError String m) =>+ String -> m Manifest+getManifest = performParse manifestP+++{- | Parse epub metadata from a String representing the epub XML+ Package Document+-}+{- Parsing the metadata is a two-pass process+ First we need to parse the meta tags only, referred to in this+ code as 'refinements.'+ Second we parse the metadata tags themselves, passing in the+ refinements so their info can be merged during parse+-}+getMetadata :: (MonadIO m, MonadError String m) =>+ String -> m Metadata+getMetadata opfContents = do+ refinements <- performParse refinementsP opfContents+ rawMd <- performParse (metadataP refinements) opfContents+ return rawMd+++{- | Parse epub package info from a String representing the epub XML+ Package Document+-}+getPackage :: (MonadIO m, MonadError String m) =>+ String -> m Package+getPackage = performParse packageP+++{- | Parse epub spine info from a String representing the epub XML+ Package Document+-}+getSpine :: (MonadIO m, MonadError String m) =>+ String -> m Spine+getSpine = performParse spineP
+ src/Codec/Epub/Parse/Guide.hs view
@@ -0,0 +1,33 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{-# LANGUAGE Arrows #-}++-- | Parsing for the guide section of the OPF Package XML Document+module Codec.Epub.Parse.Guide+ ( guideP+ )+ where++import Control.Arrow.ListArrows+import Data.Tree.NTree.TypeDefs ( NTree )+import Text.XML.HXT.Arrow.XmlArrow+import Text.XML.HXT.DOM.TypeDefs++import Codec.Epub.Data.Guide+import Codec.Epub.Parse.Util+++guideRefP :: (ArrowXml a) => a (NTree XNode) GuideRef+guideRefP = atQTag (opfName "reference") >>>+ proc x -> do+ t <- getAttrValue "type" -< x+ mt <- mbGetAttrValue "title" -< x+ h <- getAttrValue "href" -< x+ returnA -< GuideRef t mt h+++guideP :: (ArrowXml a) => a (NTree XNode) [GuideRef]+guideP = (atQTag (opfName "guide") >>> listA guideRefP)+ `orElse` constA []
+ src/Codec/Epub/Parse/Manifest.hs view
@@ -0,0 +1,35 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{-# LANGUAGE Arrows #-}++-- | Parsing for the manifest section of the OPF Package XML Document+module Codec.Epub.Parse.Manifest+ ( manifestP+ )+ where++import Control.Arrow.ListArrows+import Data.Tree.NTree.TypeDefs ( NTree )+import Text.XML.HXT.Arrow.XmlArrow+import Text.XML.HXT.DOM.TypeDefs++import Codec.Epub.Data.Manifest+import Codec.Epub.Parse.Util+++manifestItemP :: (ArrowXml a) => a (NTree XNode) ManifestItem+manifestItemP= atQTag (opfName "item") >>>+ proc x -> do+ i <- getAttrValue "id" -< x+ h <- getAttrValue "href" -< x+ m <- getAttrValue "media-type" -< x+ returnA -< ManifestItem i h m+++manifestP :: (ArrowXml a) => a (NTree XNode) Manifest+manifestP = atQTag (opfName "manifest") >>>+ proc x -> do + l <- listA manifestItemP -< x+ returnA -< Manifest l
+ src/Codec/Epub/Parse/Metadata.hs view
@@ -0,0 +1,127 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{-# LANGUAGE Arrows #-}++-- | Parsing for the metadata section of the OPF Package XML Document+module Codec.Epub.Parse.Metadata+ ( metadataP+ )+ where++import Control.Applicative+import Control.Arrow.ListArrows+import Data.Tree.NTree.TypeDefs ( NTree )+import Text.XML.HXT.Arrow.XmlArrow+import Text.XML.HXT.DOM.TypeDefs++import Codec.Epub.Data.Metadata+import Codec.Epub.Parse.Util+++idP :: (ArrowXml a) => a (NTree XNode) Identifier+idP = atQTag (dcName "identifier") >>> ( unwrapArrow $ Identifier+ <$> (WrapArrow $ mbGetAttrValue "id")+ <*> (WrapArrow $ constA Nothing)+ <*> (WrapArrow $ mbGetQAttrValue (opfName "scheme")) -- An attr in epub2+ <*> (WrapArrow $ text)+ )+++titleP :: (ArrowXml a) => a (NTree XNode) (String, Title)+titleP = atQTag (dcName "title") >>>+ proc x -> do+ i <- mbGetAttrValue "id" -< x+ l <- mbGetQAttrValue (xmlName "lang") -< x+ c <- text -< x+ returnA -< ((maybe "" id i), Title l Nothing Nothing c)+++langP :: (ArrowXml a) => a (NTree XNode) String+langP = atQTag (dcName "language") >>> text+++{- Since creators and contributors have the same exact XML structure,+ this arrow is used to get either of them+-}+creatorP :: (ArrowXml a) => String -> a (NTree XNode) (String, Creator)+creatorP tag = atQTag (dcName tag) >>>+ proc x -> do+ i <- mbGetAttrValue "id" -< x+ r <- mbGetQAttrValue (opfName "role") -< x+ f <- mbGetQAttrValue (opfName "file-as") -< x+ t <- text -< x+ returnA -< ((maybe "" id i), Creator r f Nothing t)+++dateP :: (ArrowXml a) => a (NTree XNode) Date+dateP = atQTag (dcName "date") >>>+ proc x -> do+ e <- mbGetQAttrValue (opfName "event") -< x+ c <- text -< x+ returnA -< Date e c+++sourceP :: (ArrowXml a) => a (NTree XNode) (Maybe String)+sourceP = mbQTagText $ dcName "source"+++typeP :: (ArrowXml a) => a (NTree XNode) (Maybe String)+typeP = mbQTagText $ dcName "type"+++coverageP :: (ArrowXml a) => a (NTree XNode) String+coverageP = atQTag (dcName "coverage") >>> text+++descriptionP :: (ArrowXml a) => a (NTree XNode) Description+descriptionP = atQTag (dcName "description") >>>+ proc x -> do+ l <- mbGetQAttrValue (xmlName "lang") -< x+ c <- text -< x+ returnA -< Description l c+++formatP :: (ArrowXml a) => a (NTree XNode) String+formatP = atQTag (dcName "format") >>> text+++publisherP :: (ArrowXml a) => a (NTree XNode) String+publisherP = atQTag (dcName "publisher") >>> text+++relationP :: (ArrowXml a) => a (NTree XNode) String+relationP = atQTag (dcName "relation") >>> text+++rightsP :: (ArrowXml a) => a (NTree XNode) String+rightsP = atQTag (dcName "rights") >>> text+++subjectP :: (ArrowXml a) => a (NTree XNode) String+subjectP = atQTag (dcName "subject") >>> text+++metadataP :: (ArrowXml a) => [Refinement] -> a (NTree XNode) Metadata+metadataP refinements =+ atQTag (opfName "metadata") >>> ( unwrapArrow $ Metadata+ <$> (WrapArrow $ listA $ idP >>. map (refineIdentifier refinements))+ <*> (WrapArrow $ listA $ titleP >>. map (refineTitle refinements))+ <*> (WrapArrow $ listA langP)+ <*> (WrapArrow $ listA $ creatorP "contributor" >>.+ map (refineCreator refinements))+ <*> (WrapArrow $ listA $ creatorP "creator" >>.+ map (refineCreator refinements))+ <*> (WrapArrow $ listA dateP)+ <*> (WrapArrow $ constA $ getModified refinements)+ <*> (WrapArrow sourceP)+ <*> (WrapArrow typeP)+ <*> (WrapArrow $ listA coverageP)+ <*> (WrapArrow $ listA descriptionP)+ <*> (WrapArrow $ listA formatP)+ <*> (WrapArrow $ listA publisherP)+ <*> (WrapArrow $ listA relationP)+ <*> (WrapArrow $ listA rightsP)+ <*> (WrapArrow $ listA subjectP)+ )
+ src/Codec/Epub/Parse/Package.hs view
@@ -0,0 +1,27 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{-# LANGUAGE Arrows #-}++-- | Parsing for the package section of the OPF Package XML Document+module Codec.Epub.Parse.Package+ ( packageP+ )+ where++import Control.Arrow.ListArrows+import Data.Tree.NTree.TypeDefs ( NTree )+import Text.XML.HXT.Arrow.XmlArrow+import Text.XML.HXT.DOM.TypeDefs++import Codec.Epub.Data.Package+import Codec.Epub.Parse.Util+++packageP :: (ArrowXml a) => a (NTree XNode) Package+packageP = atQTag (opfName "package") >>>+ proc x -> do+ v <- getAttrValue "version" -< x+ u <- getAttrValue "unique-identifier" -< x+ returnA -< (Package v u)
+ src/Codec/Epub/Parse/Refinements.hs view
@@ -0,0 +1,40 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{-# LANGUAGE Arrows #-}++{- | Parsing for meta tags in the metadata section of the OPF+ Package XML Document+-}+module Codec.Epub.Parse.Refinements+ ( refinementsP+ )+ where++import Control.Applicative+import Control.Arrow.ListArrows+import Data.Tree.NTree.TypeDefs ( NTree )+import Text.XML.HXT.Arrow.XmlArrow+import Text.XML.HXT.DOM.TypeDefs++import Codec.Epub.Data.Metadata+import Codec.Epub.Parse.Util+++removeHash :: String -> String+removeHash ('#' : cs) = cs+removeHash s = s+++refinementP :: (ArrowXml a) => a (NTree XNode) Refinement+refinementP = atQTag (opfName "meta") >>> ( unwrapArrow $ Refinement+ <$> (WrapArrow $ mbGetAttrValue "refines" >>^ maybe "" removeHash)+ <*> (WrapArrow $ mbGetAttrValue "property" >>^ maybe "" id)+ <*> (WrapArrow $ mbGetAttrValue "scheme" >>^ maybe "" id)+ <*> (WrapArrow $ text)+ )+++refinementsP :: (ArrowXml a) => a (NTree XNode) [Refinement]+refinementsP = atQTag (opfName "metadata") >>> listA refinementP
+ src/Codec/Epub/Parse/Spine.hs view
@@ -0,0 +1,36 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{-# LANGUAGE Arrows #-}++-- | Parsing for the spine section of the OPF Package XML Document+module Codec.Epub.Parse.Spine+ ( spineP+ )+ where++import Control.Arrow.ListArrows+import Data.Tree.NTree.TypeDefs ( NTree )+import Text.XML.HXT.Arrow.XmlArrow+import Text.XML.HXT.DOM.TypeDefs++import Codec.Epub.Data.Spine+import Codec.Epub.Parse.Util+++spineItemrefP :: (ArrowXml a) => a (NTree XNode) SpineItemref+spineItemrefP = atQTag (opfName "itemref") >>>+ proc x -> do+ i <- getAttrValue "idref" -< x+ ml <- mbGetAttrValue "linear" -< x+ let l = maybe Nothing (\v -> if v == "no" then Just False else Just True) ml + returnA -< SpineItemref i l+++spineP :: (ArrowXml a) => a (NTree XNode) Spine+spineP = atQTag (opfName "spine") >>>+ proc x -> do+ i <- getAttrValue "toc" -< x+ l <- listA spineItemrefP -< x+ returnA -< (Spine i l)
+ src/Codec/Epub/Parse/Util.hs view
@@ -0,0 +1,96 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++{-# LANGUAGE Arrows #-}++-- | Helper functions used by the other parsing modules+module Codec.Epub.Parse.Util+ ( atQTag+ , mbQTagText+ , mbGetAttrValue+ , mbGetQAttrValue+ , notNullA+ , text+ , dcName+ , opfName+ , xmlName+ )+ where++import Control.Arrow.ListArrows+import Data.Tree.NTree.TypeDefs ( NTree )+import Text.XML.HXT.Arrow.XmlArrow+import Text.XML.HXT.DOM.TypeDefs+++-- HXT helpers++{- Not used at this time. But may be used someday++atTag :: (ArrowXml a) => String -> a (NTree XNode) XmlTree+atTag tag = deep (isElem >>> hasName tag)+-}+++{- | Shortcut arrow to drill down to a specific namespaced child+ element+-}+atQTag :: (ArrowXml a) => QName -> a (NTree XNode) XmlTree+atQTag tag = deep (isElem >>> hasQName tag)+++-- | Shortcut arrow to gather up the text part of all child nodes+text :: (ArrowXml a) => a (NTree XNode) String+text = getChildren >>> getText+++-- | Arrow that succeeds if the input is not the empty list+notNullA :: (ArrowList a) => a [b] [b]+notNullA = isA $ not . null+++{- | Shortcut arrow to retrieve the contents of a namespaced element+ as a Maybe String+-}+mbQTagText :: (ArrowXml a) => QName -> a (NTree XNode) (Maybe String)+mbQTagText tag =+ ( atQTag tag >>>+ text >>> notNullA >>^ Just )+ `orElse`+ (constA Nothing)+++{- | Shortcut arrow to retrieve an attribute of an element as a+ Maybe String+-}+mbGetAttrValue :: (ArrowXml a) =>+ String -> a XmlTree (Maybe String)+mbGetAttrValue n =+ (getAttrValue n >>> notNullA >>^ Just)+ `orElse` (constA Nothing)+++{- | Shortcut arrow to retrieve an attribute of a namespaced element+ as a Maybe String+-}+mbGetQAttrValue :: (ArrowXml a) =>+ QName -> a XmlTree (Maybe String)+mbGetQAttrValue qn =+ (getQAttrValue qn >>> notNullA >>^ Just)+ `orElse` (constA Nothing)+++-- | Construct a qualified name in the Dublin Core namespace+dcName :: String -> QName+dcName local = mkQName "dc" local "http://purl.org/dc/elements/1.1/"+++-- | Construct a qualified name in the epub OPF namespace+opfName :: String -> QName+opfName local = mkQName "opf" local "http://www.idpf.org/2007/opf"+++-- | Construct a qualified name in the XML namespace+xmlName :: String -> QName+xmlName local = mkQName "xml" local "http://www.w3.org/XML/1998/namespace"
− src/Codec/Epub2/Archive.hs
@@ -1,60 +0,0 @@--- Copyright: 2011-2013 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module Codec.Epub2.Archive- ( mkEpubArchive- , readArchive- , writeArchive- )- where--import Codec.Archive.Zip-import Control.Monad-import qualified Data.ByteString.Lazy as B-import Data.List-import System.Directory-import System.FilePath---{- Recursively get a list of all files starting with the supplied- parent directory. Excluding the directories themselves and ANY - dotfiles.--}-getRecursiveContents :: FilePath -> IO [FilePath]-getRecursiveContents parent = do- fullContents <- getDirectoryContents parent- let contents = filter (not . isPrefixOf ".") fullContents- paths <- forM contents $ \name -> do- let path = parent </> name- isDirectory <- doesDirectoryExist path- if isDirectory- then getRecursiveContents path- else return [path]- return $ concat paths---{- | Construct a zip Archive containing epub book data from the - specified directory--}-mkEpubArchive :: FilePath -> IO Archive-mkEpubArchive rootDir = do- setCurrentDirectory rootDir-- let mimetype = ["mimetype"]- allFiles <- getRecursiveContents "."- let restFiles = allFiles \\ mimetype-- flip (addFilesToArchive [OptRecursive]) restFiles >=>- flip (addFilesToArchive []) ["mimetype"]- $ emptyArchive----- | Read a zip Archive from disk-readArchive :: FilePath -> IO Archive-readArchive = fmap toArchive . B.readFile----- | Write a zip Archive to disk using the specified filename-writeArchive :: FilePath -> Archive -> IO ()-writeArchive zipPath = (B.writeFile zipPath) . fromArchive
− src/Codec/Epub2/IO.hs
@@ -1,141 +0,0 @@--- Copyright: 2010-2013 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--{-# LANGUAGE FlexibleContexts #-}---- | Functions for doing some disk IO with ePub documents--module Codec.Epub2.IO- ( opfContentsFromZip- , opfContentsFromBS- , opfContentsFromDir- , removeIllegalStartChars- , removeEncoding- , removeDoctype- )- where--import Codec.Archive.Zip-import Control.Arrow.ListArrows ( (>>>), deep )-import Control.Exception-import Control.Monad.Error-import qualified Data.ByteString.Char8 as BS-import Data.ByteString.Lazy ( fromChunks )-import qualified Data.ByteString.Lazy.Char8 as BL-import System.Directory-import System.FilePath-import Text.Regex-import Text.XML.HXT.Arrow.XmlArrow ( getAttrValue, hasName, isElem )-import Text.XML.HXT.Arrow.XmlState ( no, runX, withValidate )-import Text.XML.HXT.Arrow.ReadDocument ( readString )---{- | An evil hack to remove *ILLEGAL* characters before the XML- declaration. Why do people write software that does this?- Can't they follow directions?--}-removeIllegalStartChars :: String -> String-removeIllegalStartChars = dropWhile (/= '<')----- | An evil hack to remove encoding from the document-removeEncoding :: String -> String-removeEncoding = flip (subRegex - (mkRegexWithOpts " +encoding=\"UTF-8\"" False False)) ""----- | An evil hack to remove any <!DOCTYPE ...> from the document-removeDoctype :: String -> String-removeDoctype = flip (subRegex - (mkRegexWithOpts "<!DOCTYPE [^>]*>" False True)) ""---locateRootFile :: (MonadIO m, MonadError String m) =>- FilePath -> String -> m FilePath-locateRootFile containerPath containerDoc = do- result <- liftIO $ runX (- readString [withValidate no] containerDoc- >>> deep (isElem >>> hasName "rootfile")- >>> getAttrValue "full-path"- )-- case result of- (p : []) -> return p- _ -> throwError $- "ERROR: rootfile full-path missing from " ++ containerPath----- | Extract a file from a zip archive throwing an error on failure-fileFromArchive :: MonadError String m =>- FilePath -> Archive -> m String-fileFromArchive filePath archive = do- let mbEntry = findEntryByPath filePath archive- maybe- (throwError $ "Unable to locate file " ++ filePath)- (return . BL.unpack . fromEntry) mbEntry---{- | Get the contents of the OPF Package Document from a ByteString- representing an EPUB (zip) file--}-opfContentsFromBS :: (MonadError String m, MonadIO m)- => BS.ByteString -- ^ contents of the zip file- -> m (FilePath, String) -- ^ path and contents of the OPF Package Document-opfContentsFromBS strictBytes = do- -- Need to turn this strict byte string into a lazy one- let lazyBytes = fromChunks [strictBytes]- result <- liftIO $ ( try $ evaluate- (toArchive lazyBytes) :: IO (Either SomeException Archive) )- archive <- either (throwError . show) return result-- {- We need to first extract the container.xml file- It's required to have a certain path and name in the epub- and contains the path to what we really want, the .opf file.- -}- let containerPath = "META-INF/container.xml"- containerDoc <- fileFromArchive containerPath archive-- rootPath <- locateRootFile containerPath containerDoc-- -- Now that we have the path to the .opf file, extract it- rootContents <- fileFromArchive rootPath archive-- return (rootPath, rootContents)----- | Get the contents of the OPF Package Document from an EPUB (zip) file-opfContentsFromZip :: (MonadError String m, MonadIO m)- => FilePath -- ^ path to ePub zip file- -> m (FilePath, String) -- ^ path and contents of the OPF Package Document-opfContentsFromZip zipPath = do- {- Strictly read this file into a ByteString, send to - opfContentsFromBS- -}- zipFileBytes <- liftIO $ BS.readFile zipPath- opfContentsFromBS zipFileBytes---{- | Get the contents of the OPF Package Document from a directory- containing the files from an EPUB file (as in: it's been- unzipped into a dir)--}-opfContentsFromDir :: (MonadError String m, MonadIO m)- => FilePath -- ^ directory path- -> m (FilePath, String) -- ^ path and contents of the OPF Package Document-opfContentsFromDir dir = do- {- We need to first extract the container.xml file- It's required to have a certain path and name in the epub- and contains the path to what we really want, the .opf file.- -}- liftIO $ setCurrentDirectory dir-- let containerPath = "META-INF/container.xml"- containerDoc <- liftIO $ readFile containerPath-- rootPath <- locateRootFile (dir </> containerPath) containerDoc-- -- Now that we have the path to the .opf file, load it- rootContents <- liftIO $ readFile rootPath-- return (rootPath, rootContents)
− src/Codec/Epub2/Opf/Common.hs
@@ -1,17 +0,0 @@--- Copyright: 2010-2013 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--{- | Data types for working with the metadata of ePub documents-- These data types were constructed by studying the IDPF OPF - specification for ePub documents found here:-- <http://www.idpf.org/epub/20/spec/OPF_2.0.1_draft.htm>--}-module Codec.Epub2.Opf.Common- where----- | Attribute value used to relate things to each other in the OPF data-type MFItemId = String
− src/Codec/Epub2/Opf/Format/Guide.hs
@@ -1,31 +0,0 @@--- Copyright: 2010-2013 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--{-# LANGUAGE FlexibleContexts #-}---- | Module for pretty-printing ePub metadata info-module Codec.Epub2.Opf.Format.Guide- where--import Control.Monad.Writer.Lazy-import Text.Printf--import Codec.Epub2.Opf.Format.Util-import Codec.Epub2.Opf.Package.Guide---tellGuideRef :: MonadWriter (Seq Char) m => GuideRef -> m ()-tellGuideRef (GuideRef grty title href) =- tellSeq $ printf " type: %s%s, href: %s\n"- grty (titleToString title) href-- where- titleToString = maybe "" (printf ", title: %s")---tellGuide :: MonadWriter (Seq Char) m => [GuideRef] -> m ()-tellGuide [] = return ()-tellGuide grs = do- tellSeq "guide items:\n"- mapM_ tellGuideRef grs
− src/Codec/Epub2/Opf/Format/Manifest.hs
@@ -1,27 +0,0 @@--- Copyright: 2010-2013 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--{-# LANGUAGE FlexibleContexts #-}---- | Module for pretty-printing ePub metadata info-module Codec.Epub2.Opf.Format.Manifest- where--import Control.Monad.Writer.Lazy-import Text.Printf--import Codec.Epub2.Opf.Format.Util-import Codec.Epub2.Opf.Package.Manifest---tellManifestItem :: MonadWriter (Seq Char) m => ManifestItem -> m ()-tellManifestItem (ManifestItem mfId href mediaType) =- tellSeq $ printf " id: %s, href: %s, media-type: %s\n"- mfId href mediaType---tellManifest :: MonadWriter (Seq Char) m => [ManifestItem] -> m ()-tellManifest mas = do- tellSeq "manifest items:\n"- mapM_ tellManifestItem mas
− src/Codec/Epub2/Opf/Format/Metadata.hs
@@ -1,93 +0,0 @@--- Copyright: 2010-2013 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--{-# LANGUAGE FlexibleContexts #-}---- | Module for pretty-printing ePub metadata info-module Codec.Epub2.Opf.Format.Metadata- where--import Control.Monad.Writer.Lazy-import Text.Printf--import Codec.Epub2.Opf.Format.Util-import Codec.Epub2.Opf.Package.Metadata---tellTitle :: MonadWriter (Seq Char) m => Title -> m ()-tellTitle (Title Nothing title) = tellSeq $ printf "title: %s\n" title-tellTitle (Title lang title) =- tellSeq $ printf "title\n%s%s" (formatSubline "lang" lang)- (formatSubline "text" (Just title))---tellCreator :: MonadWriter (Seq Char) m => Creator -> m ()-tellCreator (Creator Nothing Nothing creator) =- tellSeq $ printf "creator: %s\n" creator-tellCreator (Creator role fileAs creator) =- tellSeq $ printf "creator\n%s%s%s"- (formatSubline "role" role)- (formatSubline "file-as" fileAs)- (formatSubline "text" (Just creator))---tellContributor :: MonadWriter (Seq Char) m => Creator -> m ()-tellContributor (Creator Nothing Nothing contributor) =- tellSeq $ printf "contributor: %s\n" contributor-tellContributor (Creator role fileAs contributor) =- tellSeq $ printf "contributor\n%s%s%s"- (formatSubline "role" role)- (formatSubline "file-as" fileAs)- (formatSubline "text" (Just contributor))---tellDate :: MonadWriter (Seq Char) m => Date -> m ()-tellDate (Date Nothing date) =- tellSeq $ printf "date: %s\n" date-tellDate (Date event date) =- tellSeq $ printf "date\n%s%s"- (formatSubline "event" event)- (formatSubline "text" (Just date))---tellId :: MonadWriter (Seq Char) m => Identifier -> m ()-tellId (Identifier idVal scheme content) =- tellSeq $ printf "identifier\n%s%s%s"- (formatSubline "id" (Just idVal))- (formatSubline "scheme" scheme)- (formatSubline "text" (Just content))---tellDescription :: MonadWriter (Seq Char) m => Description -> m ()-tellDescription (Description Nothing text) =- tellSeq $ printf "description: %s\n" text-tellDescription (Description lang text) =- tellSeq $ printf "description\n%s%s"- (formatSubline "lang" lang)- (formatSubline "text" (Just text))---tellSimpleString :: MonadWriter (Seq Char) m => String -> String -> m ()-tellSimpleString label = tellSeq . (printf "%s: %s\n" label)---tellMetadata :: MonadWriter (Seq Char) m => Metadata -> m ()-tellMetadata (Metadata titles creators contributors subjects desc - publisher dates mType format ids source langs relation - coverage rights) = do- mapM_ tellTitle titles- mapM_ tellDescription desc- mapM_ tellDate dates- mapM_ tellCreator creators- mapM_ tellContributor contributors- mapM_ (tellSimpleString "publisher") publisher- mapM_ tellId ids- mapM_ (tellSimpleString "language") langs- mapM_ (tellSimpleString "subject") subjects- mapM_ (tellSimpleString "type") mType- mapM_ (tellSimpleString "format") format- mapM_ (tellSimpleString "source") source- mapM_ (tellSimpleString "relation") relation- mapM_ (tellSimpleString "coverage") coverage- mapM_ (tellSimpleString "rights") rights
− src/Codec/Epub2/Opf/Format/Package.hs
@@ -1,40 +0,0 @@--- Copyright: 2010-2013 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--{-# LANGUAGE FlexibleContexts #-}---- | Module for pretty-printing OPF package data-module Codec.Epub2.Opf.Format.Package- ( formatPackage- )- where--import Control.Monad.Writer.Lazy-import Data.Foldable ( toList )--import Codec.Epub2.Opf.Format.Guide-import Codec.Epub2.Opf.Format.Manifest-import Codec.Epub2.Opf.Format.Metadata-import Codec.Epub2.Opf.Format.Spine-import Codec.Epub2.Opf.Format.Util-import Codec.Epub2.Opf.Package---tellPackage :: MonadWriter (Seq Char) m => (String, String) -> m ()-tellPackage (version, uniqueId) = do- tellSeq "package\n"- tellSeq $ formatSubline "version" (Just version)- tellSeq $ formatSubline "unique-identifier" (Just uniqueId)---formatPackage :: Bool -> Package -> String-formatPackage showAll (Package v u meta ma sp gu) =- toList . execWriter $ do- tellPackage (v, u)- tellMetadata meta-- when showAll $ do- tellManifest ma- tellSpine sp- tellGuide gu
− src/Codec/Epub2/Opf/Format/Spine.hs
@@ -1,33 +0,0 @@--- Copyright: 2010-2013 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--{-# LANGUAGE FlexibleContexts #-}---- | Module for pretty-printing ePub metadata info-module Codec.Epub2.Opf.Format.Spine- where--import Control.Monad.Writer.Lazy-import Text.Printf--import Codec.Epub2.Opf.Format.Util-import Codec.Epub2.Opf.Package.Spine---tellSpineItemref :: MonadWriter (Seq Char) m => SpineItemref -> m ()-tellSpineItemref (SpineItemref idref linear) =- tellSeq $ printf " idref: %s%s\n" idref (linearToString linear)-- where- boolToYn True = "yes"- boolToYn False = "no"-- linearToString Nothing = ""- linearToString (Just l) = printf ", linear: %s" (boolToYn l)---tellSpine :: MonadWriter (Seq Char) m => Spine -> m ()-tellSpine (Spine toc itemRefs) = do- tellSeq $ printf "spine toc: %s, itemrefs:\n" toc- mapM_ tellSpineItemref itemRefs
− src/Codec/Epub2/Opf/Format/Util.hs
@@ -1,26 +0,0 @@--- Copyright: 2010-2013 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--{-# LANGUAGE FlexibleContexts #-}---- | Functions shared by several formatting modules-module Codec.Epub2.Opf.Format.Util- ( formatSubline- , tellSeq- , Seq- )- where--import Control.Monad.Writer.Lazy-import Data.Sequence ( Seq, fromList )-import Text.Printf---formatSubline :: String -> Maybe String -> String-formatSubline _ Nothing = ""-formatSubline key (Just value) = printf " %s: %s\n" key value---tellSeq :: MonadWriter (Seq a) m => [a] -> m ()-tellSeq = tell . fromList
− src/Codec/Epub2/Opf/Package.hs
@@ -1,44 +0,0 @@--- Copyright: 2010-2013 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--{- | Data types for working with the metadata of ePub documents-- These data types were constructed by studying the IDPF OPF - specification for ePub documents found here:-- <http://www.idpf.org/epub/20/spec/OPF_2.0.1_draft.htm>--}-module Codec.Epub2.Opf.Package- ( Package (..)- , Creator (..)- , Date (..)- , Description (..)- , Identifier (..)- , Metadata (..)- , Title (..)- , ManifestItem (..)- , SpineItemref (..)- , Spine (..)- , GuideRef (..)- , emptyMetadata- )- where--import Codec.Epub2.Opf.Package.Guide-import Codec.Epub2.Opf.Package.Manifest-import Codec.Epub2.Opf.Package.Metadata-import Codec.Epub2.Opf.Package.Spine---{- | package tag--}-data Package = Package- { opVersion :: String -- ^ version attr- , opUniqueId :: String -- ^ unique-identifier attr- , opMeta :: Metadata -- ^ metadata child element contents- , opManifest :: [ManifestItem] -- ^ manifest child element contents. One required- , opSpine :: Spine -- ^ spine child element contents- , opGuide :: [GuideRef] -- ^ guide child element contents- }- deriving (Eq, Show)
− src/Codec/Epub2/Opf/Package/Guide.hs
@@ -1,24 +0,0 @@--- Copyright: 2010-2013 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--{- | Data types for working with the metadata of ePub documents-- These data types were constructed by studying the IDPF OPF - specification for ePub documents found here:-- <http://www.idpf.org/epub/20/spec/OPF_2.0.1_draft.htm>--}-module Codec.Epub2.Opf.Package.Guide- ( GuideRef (..)- )- where----- | package\/guide\/reference tag-data GuideRef = GuideRef- { grType :: String -- ^ type attr. Must follow 13th edition of the Chicago Manual of Style- , grTitle :: Maybe String -- ^ title attr- , grHref :: String -- ^ href attr. Must reference item in manifest- }- deriving (Eq, Show)
− src/Codec/Epub2/Opf/Package/Manifest.hs
@@ -1,31 +0,0 @@--- Copyright: 2010-2013 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--{- | Data types for working with the metadata of ePub documents-- These data types were constructed by studying the IDPF OPF - specification for ePub documents found here:-- <http://www.idpf.org/epub/20/spec/OPF_2.0.1_draft.htm>--}-module Codec.Epub2.Opf.Package.Manifest- ( ManifestItem (..)- )- where--import Codec.Epub2.Opf.Common----- | manifest attribute values-type MFItemHref = String-type MFItemMediaType = String----- | package\/manifest\/item tag-data ManifestItem = ManifestItem- { mfiId :: MFItemId -- ^ id attr- , mfiHref :: MFItemHref -- ^ href attr- , mfiMediaType :: MFItemMediaType -- ^ media-type attr- }- deriving (Eq, Show)
− src/Codec/Epub2/Opf/Package/Metadata.hs
@@ -1,86 +0,0 @@--- Copyright: 2010-2013 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--{- | Data types for working with the metadata of ePub documents-- These data types were constructed by studying the IDPF OPF - specification for ePub documents found here:-- <http://www.idpf.org/epub/20/spec/OPF_2.0.1_draft.htm>--}-module Codec.Epub2.Opf.Package.Metadata- ( Creator (..)- , Date (..)- , Description (..)- , Identifier (..)- , Metadata (..)- , Title (..)- , emptyMetadata- )- where---{- | package\/metadata\/dc:creator tag, opf:role attr, opf:file-as attr,- content--}-data Creator = Creator (Maybe String) (Maybe String) String- deriving (Eq, Show)---- | package\/metadata\/dc:date tag, opf:event attr, content-data Date = Date (Maybe String) String- deriving (Eq, Show)---- | package\/metadata\/dc:description tag, xml:lang attr, content-data Description = Description (Maybe String) String- deriving (Eq, Show)--{- | package\/metadata\/dc:identifier tag, id attr, opf:scheme attr,- content--}-data Identifier = Identifier String (Maybe String) String- deriving (Eq, Show)---- | package\/metadata\/dc:title tag, xml:lang attr, content-data Title = Title (Maybe String) String- deriving (Eq, Show)---- | package\/metadata tag-data Metadata = Metadata- { metaTitles :: [Title] -- ^ at least one required- , metaCreators :: [Creator]- , metaContributors :: [Creator]- , metaSubjects :: [String] -- ^ dc:subject tags- , metaDescriptions :: [Description]- , metaPublishers :: [String] -- ^ dc:publisher tags- , metaDates :: [Date]- , metaTypes :: [String] -- ^ dc:type tags- , metaFormats :: [String] -- ^ dc:format tags- , metaIds :: [Identifier] -- ^ at least one required- , metaSources :: [String] -- ^ dc:source tags- , metaLangs :: [String] -- ^ dc:language tags, at least one required- , metaRelations :: [String] -- ^ dc:relation tags- , metaCoverages :: [String] -- ^ dc:coverage tags- , metaRights :: [String] -- ^ dc:rights tags- }- deriving (Eq, Show)---- | Note: This isn't valid as-is, some required values are empty lists!-emptyMetadata :: Metadata-emptyMetadata = Metadata- { metaTitles = [] -- one required- , metaCreators = []- , metaContributors = []- , metaSubjects = []- , metaDescriptions = []- , metaPublishers = []- , metaDates = []- , metaTypes = []- , metaFormats = []- , metaIds = [] -- one required- , metaSources = []- , metaLangs = [] -- one required- , metaRelations = []- , metaCoverages = []- , metaRights = []- }
− src/Codec/Epub2/Opf/Package/Spine.hs
@@ -1,34 +0,0 @@--- Copyright: 2010-2013 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--{- | Data types for working with the metadata of ePub documents-- These data types were constructed by studying the IDPF OPF - specification for ePub documents found here:-- <http://www.idpf.org/epub/20/spec/OPF_2.0.1_draft.htm>--}-module Codec.Epub2.Opf.Package.Spine- ( Spine (..)- , SpineItemref (..)- )- where--import Codec.Epub2.Opf.Common----- | package\/spine\/itemref tag-data SpineItemref = SpineItemref- { siIdRef :: MFItemId -- ^ idref attr. Must reference item in manifest- , siLinear :: Maybe Bool -- ^ linear attr- }- deriving (Eq, Show)----- | package\/spine tag-data Spine = Spine- { spineToc :: MFItemId -- ^ toc attr. Must reference the NCX in the manifest- , spineItemrefs :: [ SpineItemref ] -- one required- }- deriving (Eq, Show)
− src/Codec/Epub2/Opf/Parse.hs
@@ -1,287 +0,0 @@--- Copyright: 2010-2013 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--{-# LANGUAGE Arrows #-}-{-# LANGUAGE FlexibleContexts #-}---- | Module for extracting the metadata from an ePub file-module Codec.Epub2.Opf.Parse- ( parseXmlToOpf- , parseEpub2Opf- )- where--import Control.Applicative-import Control.Arrow.ListArrows-import Control.Monad.Error-import Data.Tree.NTree.TypeDefs ( NTree )-import Text.XML.HXT.Arrow.Namespace ( propagateNamespaces )-import Text.XML.HXT.Arrow.XmlArrow-import Text.XML.HXT.Arrow.XmlState ( no, runX, withValidate )-import Text.XML.HXT.Arrow.ReadDocument ( readString )-import Text.XML.HXT.DOM.TypeDefs--import Codec.Epub2.IO-import Codec.Epub2.Opf.Package----- HXT helpers--{- Not used at this time. But may be used someday--atTag :: (ArrowXml a) => String -> a (NTree XNode) XmlTree-atTag tag = deep (isElem >>> hasName tag)--}--atQTag :: (ArrowXml a) => QName -> a (NTree XNode) XmlTree-atQTag tag = deep (isElem >>> hasQName tag)--text :: (ArrowXml a) => a (NTree XNode) String-text = getChildren >>> getText--notNullA :: (ArrowList a) => a [b] [b]-notNullA = isA $ not . null---{- Not used at this time, we don't have any single but optional- tags any longer--mbQTagText :: (ArrowXml a) => QName -> a (NTree XNode) (Maybe String)-mbQTagText tag =- ( atQTag tag >>>- text >>> notNullA >>> arr Just )- `orElse`- (constA Nothing)--}---mbGetAttrValue :: (ArrowXml a) =>- String -> a XmlTree (Maybe String)-mbGetAttrValue n =- (getAttrValue n >>> notNullA >>> arr Just)- `orElse` (constA Nothing)--mbGetQAttrValue :: (ArrowXml a) =>- QName -> a XmlTree (Maybe String)-mbGetQAttrValue qn =- (getQAttrValue qn >>> notNullA >>> arr Just)- `orElse` (constA Nothing)---{- ePub parsing helpers-- Note that these URIs could conceivably change in the future- Is it ok that they're hardcoded like this?-- Well, ok, the xml namespace URI will probably never change.--}--dcName, opfName, xmlName :: String -> QName-dcName local = mkQName "dc" local "http://purl.org/dc/elements/1.1/"-opfName local = mkQName "opf" local "http://www.idpf.org/2007/opf"-xmlName local = mkQName "xml" local "http://www.w3.org/XML/1998/namespace"---getPackage :: (ArrowXml a) => a (NTree XNode) (String, String)-getPackage = atQTag (opfName "package") >>>- proc x -> do- v <- getAttrValue "version" -< x- u <- getAttrValue "unique-identifier" -< x- returnA -< (v, u)---getTitle :: (ArrowXml a) => a (NTree XNode) Title-getTitle = atQTag (dcName "title") >>>- proc x -> do- l <- mbGetQAttrValue (xmlName "lang") -< x- c <- text -< x- returnA -< Title l c---{- Since creators and contributors have the same exact XML structure,- this arrow is used to get either of them--}-getCreator :: (ArrowXml a) => String -> a (NTree XNode) Creator-getCreator tag = atQTag (dcName tag) >>> ( unwrapArrow $ Creator- <$> (WrapArrow $ mbGetQAttrValue (opfName "role"))- <*> (WrapArrow $ mbGetQAttrValue (opfName "file-as"))- <*> (WrapArrow $ text)- )---getSubject :: (ArrowXml a) => a (NTree XNode) String-getSubject = atQTag (dcName "subject") >>> text---getDescription :: (ArrowXml a) => a (NTree XNode) Description-getDescription = atQTag (dcName "description") >>>- proc x -> do- l <- mbGetQAttrValue (xmlName "lang") -< x- c <- text -< x- returnA -< Description l c---getPublisher :: (ArrowXml a) => a (NTree XNode) String-getPublisher = atQTag (dcName "publisher") >>> text---getDate :: (ArrowXml a) => a (NTree XNode) Date-getDate = atQTag (dcName "date") >>>- proc x -> do- e <- mbGetQAttrValue (opfName "event") -< x- c <- text -< x- returnA -< Date e c---getType :: (ArrowXml a) => a (NTree XNode) String-getType = atQTag (dcName "type") >>> text---getFormat :: (ArrowXml a) => a (NTree XNode) String-getFormat = atQTag (dcName "format") >>> text---getId :: (ArrowXml a) => a (NTree XNode) Identifier-getId = atQTag (dcName "identifier") >>>- proc x -> do- mbi <- mbGetAttrValue "id" -< x- s <- mbGetQAttrValue (opfName "scheme") -< x- c <- text -< x- let i = maybe "[WARNING: missing required id attribute]" id mbi- returnA -< Identifier i s c---getSource :: (ArrowXml a) => a (NTree XNode) String-getSource = atQTag (dcName "source") >>> text---getLang :: (ArrowXml a) => a (NTree XNode) String-getLang = atQTag (dcName "language") >>> text---getRelation :: (ArrowXml a) => a (NTree XNode) String-getRelation = atQTag (dcName "relation") >>> text---getCoverage :: (ArrowXml a) => a (NTree XNode) String-getCoverage = atQTag (dcName "coverage") >>> text---getRights :: (ArrowXml a) => a (NTree XNode) String-getRights = atQTag (dcName "rights") >>> text---getMeta :: (ArrowXml a) => a (NTree XNode) Metadata-getMeta = atQTag (opfName "metadata") >>> ( unwrapArrow $ Metadata- <$> (WrapArrow $ listA getTitle)- <*> (WrapArrow $ listA $ getCreator "creator")- <*> (WrapArrow $ listA $ getCreator "contributor")- <*> (WrapArrow $ listA getSubject)- <*> (WrapArrow $ listA getDescription)- <*> (WrapArrow $ listA getPublisher)- <*> (WrapArrow $ listA getDate)- <*> (WrapArrow $ listA getType)- <*> (WrapArrow $ listA getFormat)- <*> (WrapArrow $ listA getId)- <*> (WrapArrow $ listA getSource)- <*> (WrapArrow $ listA getLang)- <*> (WrapArrow $ listA getRelation)- <*> (WrapArrow $ listA getCoverage)- <*> (WrapArrow $ listA getRights)- )---getManifestItem :: (ArrowXml a) => a (NTree XNode) ManifestItem-getManifestItem = atQTag (opfName "item") >>>- proc x -> do- i <- getAttrValue "id" -< x- h <- getAttrValue "href" -< x- m <- getAttrValue "media-type" -< x- returnA -< ManifestItem i h m---getManifest :: (ArrowXml a) => a (NTree XNode) [ManifestItem]-getManifest = atQTag (opfName "manifest") >>>- proc x -> do- l <- listA getManifestItem -< x- returnA -< l---getSpineItemref :: (ArrowXml a) => a (NTree XNode) SpineItemref-getSpineItemref = atQTag (opfName "itemref") >>>- proc x -> do- i <- getAttrValue "idref" -< x- ml <- mbGetAttrValue "linear" -< x- let l = maybe Nothing (\v -> if v == "no" then Just False else Just True) ml- returnA -< SpineItemref i l---getSpine :: (ArrowXml a) => a (NTree XNode) Spine-getSpine = atQTag (opfName "spine") >>>- proc x -> do- i <- getAttrValue "toc" -< x- l <- listA getSpineItemref -< x- returnA -< (Spine i l)---getGuideRef :: (ArrowXml a) => a (NTree XNode) GuideRef-getGuideRef = atQTag (opfName "reference") >>>- proc x -> do- t <- getAttrValue "type" -< x- mt <- mbGetAttrValue "title" -< x- h <- getAttrValue "href" -< x- returnA -< GuideRef t mt h---getGuide :: (ArrowXml a) => a (NTree XNode) [GuideRef]-getGuide = atQTag (opfName "guide") >>>- proc x -> do- l <- listA getGuideRef -< x- returnA -< l---getBookData :: (ArrowXml a) => a (NTree XNode) Package-getBookData = - proc x -> do- (v, u) <- getPackage -< x- m <- getMeta -< x- mf <- getManifest -< x- sp <- getSpine -< x- gl <- listA getGuide -< x- let g = case gl of- [] -> []- [e] -> e- _ -> error "ERROR: more than one guide entries" - returnA -< (Package v u m mf sp g)---{- | Extract the ePub OPF Package data contained in the supplied - XML string--}-parseXmlToOpf :: (MonadIO m, MonadError String m) =>- String -> m Package-parseXmlToOpf contents = do- {- Improper encoding and schema declarations have been causing- havok with this parse, cruelly strip them out. -}- let cleanedContents = removeIllegalStartChars . removeEncoding- . removeDoctype $ contents- - result <- liftIO $ runX (- readString [withValidate no] cleanedContents- >>> propagateNamespaces- >>> getBookData- )-- case result of- (p : []) -> return p- _ -> throwError- "ERROR: Parse didn't result in a single document metadata"----- | Given the path to an ePub file, extract the OPF Package data-parseEpub2Opf :: (MonadIO m, MonadError String m) =>- FilePath -> m Package-parseEpub2Opf zipPath = do- (_, contents) <- opfContentsFromZip zipPath- parseXmlToOpf contents
testsuite/Archive.hs view
@@ -7,16 +7,21 @@ where import Codec.Archive.Zip+import Control.Monad.Error import System.Directory import System.FilePath import Test.HUnit -import Codec.Epub2.Archive+import Codec.Epub.Data.Package+import Codec.Epub.IO+import Codec.Epub.Parse tests :: Test tests = TestList [ testMkArchive+ , testDamagedZip+ , testIllegalCharsBeforeDecl ] @@ -30,3 +35,30 @@ let (firstFile : _) = filesInArchive a assertEqual "mimetype file is FIRST" "mimetype" firstFile+++{- Occasionally epub zip files come along that are damaged in this+ way. It's not fatal to the UNIX zip utility or to book readers, but had+ to be specially handled in the Haskell zip-archive library or it causes+ a fatal exception.+-}+testDamagedZip :: Test+testDamagedZip = TestLabel "damaged zip" $ TestCase $ do+ actual <- runErrorT $ getPkgXmlFromZip $ "testsuite"+ </> "damagedZipCentralDir.epub"+ actual @?= Left "Data.Binary.Get.runGet at position 138: Did not find end of central directory signature"+++{- Found books coming from Barnes & Noble (for their NOOK reader) to+ contain illegal characters before the XML declaration. This is+ strictly not allowed by the XML specification. I am very+ disappointed with Barnes & Noble for selling garbage like this.+-}+testIllegalCharsBeforeDecl :: Test+testIllegalCharsBeforeDecl = TestCase $ do+ xmlString <- readFile $+ "testsuite" </> "testIllegalCharsBeforeDecl.opf"+ actual <- runErrorT $ getPackage xmlString+ let expected =+ Right Package {pkgVersion = "2.0", pkgUniqueId = "uuid_id"}+ assertEqual "illegal chars before XML declaration" expected actual
+ testsuite/Epub2/ParseGuide.hs view
@@ -0,0 +1,52 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++module Epub2.ParseGuide+ ( tests )+ where++import Control.Monad.Error+import System.FilePath+import Test.HUnit++import Codec.Epub.Data.Guide+import Codec.Epub.Parse+++tests :: Test+tests = TestList+ [ testFull+ , testMinimal+ ]+++{- A test containing a couple of guide ref items+-}+testFull :: Test+testFull = TestCase $ do+ xmlString <- readFile $ "testsuite" </> "epub2-full.opf"+ actual <- runErrorT $ getGuide xmlString+ let expected =+ Right [ GuideRef+ { grType = "title-page"+ , grTitle = Just "Title page"+ , grHref = "content/titlePage.html"+ }+ , GuideRef+ { grType = "text"+ , grTitle = Just "Title Of This Book"+ , grHref = "content/someContent.html"+ }+ ]+ assertEqual "some guide items" expected actual+++{- Minimal guide+-}+testMinimal :: Test+testMinimal = TestCase $ do+ xmlString <- liftIO $ readFile $ "testsuite" </> "epub2-minimal.opf"+ actual <- runErrorT $ getGuide xmlString+ let expected = Right []+ assertEqual "minimal guide" expected actual
+ testsuite/Epub2/ParseMetadata.hs view
@@ -0,0 +1,149 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++module Epub2.ParseMetadata+ ( tests )+ where++import Control.Monad.Error+import System.FilePath+import Test.HUnit++import Codec.Epub.Data.Metadata+import Codec.Epub.Parse+++tests :: Test+tests = TestList+ [ testFull+ , testMinimal+ , testMissingAll+ ]+++{- A fairly comprehensive test containing all possible things+ Not complete at this time because the library can't parse it all yet!+-}+testFull :: Test+testFull = TestCase $ do+ xmlString <- readFile $ "testsuite" </> "epub2-full.opf"+ actual <- runErrorT $ getMetadata xmlString+ let expected =+ Right Metadata+ { metaTitles =+ [ Title Nothing Nothing Nothing "Title Of This Book"+ , Title (Just "fr") Nothing Nothing "Titre De Ce Livre"+ ]+ , metaCreators = + [ Creator+ (Just "aut")+ (Just "Wiggins, Josephine B.")+ Nothing+ "Josephine B. Wiggins"+ , Creator+ (Just "aut")+ Nothing+ Nothing+ "Horatio Cromwell"+ , Creator+ Nothing+ Nothing+ Nothing+ "Natalia Jenkins"+ ]+ , metaContributors = + [ Creator + (Just "ill") + (Just "Knickerbocker, Reginald Q.") + Nothing+ "Reginald Q. Knickerbocker"+ , Creator + (Just "edt") + Nothing+ Nothing+ "Beverly Abercrombie"+ ]+ , metaSubjects = ["Fiction","Science Fiction"]+ , metaDescriptions =+ [ Description Nothing "This document is a stub used for unit testing. It is missing the rest of the tags that normally occur after metadata."+ , Description Nothing "An additional description"+ ]+ , metaPublishers =+ [ "Fictional Books Ltd."+ , "An additional publisher"+ ]+ , metaDates = + [ Date (Just "published") "2010"+ , Date (Just "created") "2010-05-07"+ , Date (Just "modified") "2010-05-08T10:20:57"+ , Date Nothing "2009"+ ]+ , metaModified = Nothing+ , metaType = Just "test OPF Package Document"+ , metaFormats =+ [ "ePub publication"+ , "an additional format"+ ]+ , metaIds = + [ Identifier (Just "isbn") Nothing (Just "ISBN") "1-82057-821-9"+ , Identifier (Just "other") Nothing Nothing "1386506873266"+ ]+ , metaSource = Just "document source"+ , metaLangs = ["en-US", "en-UK"]+ , metaRelations =+ [ "document relation"+ , "an additional relation"+ ]+ , metaCoverages =+ [ "coverage information"+ , "an additional coverage"+ ]+ , metaRights =+ [ "Copyright: 2010 Dino Morelli, License: BSD3"+ , "an additional rights"+ ]+ }+ assertEqual "epub2 - very full" expected actual+++{- Test the absolute minimum set of fields allowed while remaining + compliant with the spec+-}+testMinimal :: Test+testMinimal = TestCase $ do+ xmlString <- liftIO $ readFile $ "testsuite" </> "epub2-minimal.opf"+ actual <- runErrorT $ getMetadata xmlString+ let expected = + Right Metadata + { metaTitles = [Title Nothing Nothing Nothing+ "Title Of This Book"]+ , metaCreators = []+ , metaContributors = []+ , metaSubjects = []+ , metaDescriptions = []+ , metaPublishers = []+ , metaDates = []+ , metaModified = Nothing+ , metaType = Nothing+ , metaFormats = []+ , metaIds = [Identifier (Just "isbn") Nothing+ (Just "ISBN") "1-82057-821-9"]+ , metaSource = Nothing+ , metaLangs = ["en-us"]+ , metaRelations = []+ , metaCoverages = []+ , metaRights = []+ }+ assertEqual "minimal" expected actual+++{- Test data missing everything important: package version and + unique-identifier attributes, title, identifier and language tags+-}+testMissingAll :: Test+testMissingAll = TestCase $ do+ xmlString <- readFile $ "testsuite" </> "epub2-missingAll.opf"+ actual <- runErrorT $ getMetadata xmlString+ let expected = Right emptyMetadata+ assertEqual "missing all" expected actual
+ testsuite/Epub3/ParseMetadata.hs view
@@ -0,0 +1,103 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++module Epub3.ParseMetadata+ ( tests )+ where++import Control.Monad.Error+import System.FilePath+import Test.HUnit++import Codec.Epub.Data.Metadata+import Codec.Epub.Parse+++tests :: Test+tests = TestList+ [ testFull+ ]+++{- A fairly comprehensive test containing all possible things+ Not complete at this time because the library can't parse it all yet!+-}+testFull :: Test+testFull = TestCase $ do+ xmlString <- readFile $ "testsuite" </> "epub3-full.opf"+ actual <- runErrorT $ getMetadata xmlString+ let expected =+ Right Metadata+ { metaIds =+ [ Identifier (Just "isbn") Nothing Nothing "1-82057-821-9"+ , Identifier (Just "other") (Just "some-type")+ (Just "some-scheme") "1386506873266"+ ]+ , metaTitles =+ [ Title Nothing (Just "main") (Just 1) "Title Of This Book"+ , Title Nothing (Just "subtitle") (Just 2) "The Subtitle"+ , Title (Just "fr") Nothing Nothing "Titre De Ce Livre"+ ]+ , metaLangs = ["en-US", "en-UK"]+ , metaContributors =+ [ Creator+ (Just "ill")+ (Just "Knickerbocker, Reginald Q.")+ Nothing+ "Reginald Q. Knickerbocker"+ , Creator+ (Just "edt")+ Nothing+ Nothing+ "Beverly Abercrombie"+ ]+ , metaCreators =+ [ Creator+ (Just "aut")+ (Just "Wiggins, Josephine B.")+ (Just 2)+ "Josephine B. Wiggins"+ , Creator+ (Just "aut")+ Nothing+ (Just 1)+ "Horatio Cromwell"+ , Creator+ Nothing+ Nothing+ (Just 3)+ "Natalia Jenkins"+ ]+ , metaDates = [ Date Nothing "2012" ]+ , metaModified = Just "2013-08-31T13:06:32Z"+ , metaSource = Just "document source"+ , metaType = Just "test OPF Package Document"+ , metaCoverages =+ [ "coverage information"+ , "an additional coverage"+ ]+ , metaDescriptions =+ [ Description Nothing "This document is a stub used for unit testing. It is missing the rest of the tags that normally occur after metadata."+ , Description Nothing "An additional description"+ ]+ , metaFormats =+ [ "ePub publication"+ , "an additional format"+ ]+ , metaPublishers =+ [ "Fictional Books Ltd."+ , "An additional publisher"+ ]+ , metaRelations =+ [ "document relation"+ , "an additional relation"+ ]+ , metaSubjects = ["Fiction","Science Fiction"]+ , metaRights =+ [ "Copyright: 2010 Dino Morelli, License: BSD3"+ , "an additional rights"+ ]+ }++ assertEqual "epub3 - very full" expected actual
− testsuite/OpfParse.hs
@@ -1,264 +0,0 @@--- Copyright: 2010-2013 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module OpfParse- ( tests )- where--import Control.Monad.Error-import System.FilePath-import Test.HUnit--import Codec.Epub2.IO-import Codec.Epub2.Opf.Package-import Codec.Epub2.Opf.Parse---tests :: Test-tests = TestList- [ testFull- , testMinimal- , testMissingAll- , testDamagedZip- , testIllegalCharsBeforeDecl- ]---{- A fairly comprehensive test containing all possible things- Not complete at this time because the library can't parse it all yet!--}-testFull :: Test-testFull = TestCase $ do- xmlString <- readFile $ "testsuite" </> "testFull.opf"- actual <- runErrorT $ parseXmlToOpf xmlString- let expected =- Right Package- { opVersion = "2.0"- , opUniqueId = "isbn"- , opMeta = Metadata- { metaTitles =- [ Title Nothing "Title Of This Book"- , Title (Just "fr") "Titre De Ce Livre"- ]- , metaCreators = - [ Creator- (Just "aut")- (Just "Wiggins, Josephine B.")- "Josephine B. Wiggins"- , Creator- (Just "aut")- Nothing- "Horatio Cromwell"- , Creator- Nothing- Nothing- "Natalia Jenkins"- ]- , metaContributors = - [ Creator - (Just "ill") - (Just "Knickerbocker, Reginald Q.") - "Reginald Q. Knickerbocker"- , Creator - (Just "edt") - Nothing "Beverly Abercrombie"- ]- , metaSubjects = ["Fiction","Science Fiction"]- , metaDescriptions =- [ Description Nothing "This document is a stub used for unit testing. It is missing the rest of the tags that normally occur after metadata."- , Description Nothing "An additional description"- ]- , metaPublishers =- [ "Fictional Books Ltd."- , "An additional publisher"- ]- , metaDates = - [ Date (Just "published") "2010"- , Date (Just "created") "2010-05-07"- , Date (Just "modified") "2010-05-08T10:20:57"- , Date Nothing "2009"- ]- , metaTypes =- [ "test OPF Package Document"- , "an additional type"- ]- , metaFormats =- [ "ePub publication"- , "an additional format"- ]- , metaIds = - [ Identifier "isbn" (Just "ISBN") "1-82057-821-9"- , Identifier "other" Nothing "1386506873266"- ]- , metaSources =- [ "document source"- , "an additional source"- ]- , metaLangs = ["en-US", "en-UK"]- , metaRelations =- [ "document relation"- , "an additional relation"- ]- , metaCoverages =- [ "coverage information"- , "an additional coverage"- ]- , metaRights =- [ "Copyright: 2010 Dino Morelli, License: BSD3"- , "an additional rights"- ]- }- , opManifest = - [ ManifestItem - { mfiId = "ncx"- , mfiHref = "toc.ncx"- , mfiMediaType = "application/x-dtbncx+xml"- }- , ManifestItem - { mfiId = "titlePage"- , mfiHref = "content/titlePage.html"- , mfiMediaType = "application/xhtml+xml"- }- , ManifestItem - { mfiId = "someContent"- , mfiHref = "content/someContent.html"- , mfiMediaType = "application/xhtml+xml"- }- ]- , opSpine = Spine- { spineToc = "ncx"- , spineItemrefs = - [ SpineItemref {siIdRef = "titlePage", siLinear = Nothing}- , SpineItemref {siIdRef = "someContent", siLinear = Nothing}- ]- }- , opGuide = - [ GuideRef - { grType = "title-page"- , grTitle = Just "Title page"- , grHref = "content/titlePage.html"- }- , GuideRef - { grType = "text"- , grTitle = Just "Title Of This Book"- , grHref = "content/someContent.html"- }- ]- }- assertEqual "very full" expected actual---{- Test the absolute minimum set of fields allowed while remaining - compliant with the spec--}-testMinimal :: Test-testMinimal = TestCase $ do- xmlString <- liftIO $ readFile $ "testsuite" </> "testMinimal.opf"- actual <- runErrorT $ parseXmlToOpf xmlString- let expected = - Right Package - { opVersion = "2.0"- , opUniqueId = "isbn"- , opMeta = Metadata - { metaTitles = [Title Nothing "Title Of This Book"]- , metaCreators = []- , metaContributors = []- , metaSubjects = []- , metaDescriptions = []- , metaPublishers = []- , metaDates = []- , metaTypes = []- , metaFormats = []- , metaIds = [Identifier "isbn" (Just "ISBN") "1-82057-821-9"]- , metaSources = []- , metaLangs = ["en-us"]- , metaRelations = []- , metaCoverages = []- , metaRights = []- }- , opManifest = - [ ManifestItem - { mfiId = "ncx"- , mfiHref = "toc.ncx"- , mfiMediaType = "application/x-dtbncx+xml"- }- ]- , opSpine = Spine {spineToc = "ncx", spineItemrefs = []}- , opGuide = []- }- assertEqual "minimal" expected actual---{- Test data missing everything important: package version and - unique-identifier attributes, title, identifier and language tags--}-testMissingAll :: Test-testMissingAll = TestCase $ do- xmlString <- readFile $ "testsuite" </> "testMissingAll.opf"- actual <- runErrorT $ parseXmlToOpf xmlString- let expected =- Right Package - { opVersion = ""- , opUniqueId = ""- , opMeta = emptyMetadata- , opManifest = []- , opSpine = Spine {spineToc = "", spineItemrefs = []}- , opGuide = []- }- assertEqual "missing all" expected actual---{- Occasionally epub zip files come along that are damaged in this- way. It's not fatal to the UNIX zip utility or to book readers, but had- to be specially handled in the Haskell zip-archive library or it causes- a fatal exception.--}-testDamagedZip :: Test-testDamagedZip = TestLabel "damaged zip" $ TestCase $ do- actual <- runErrorT $ opfContentsFromZip $ "testsuite"- </> "damagedZipCentralDir.epub"- actual @?= Left "Did not find end of central directory signature. Failed reading at byte position 138"---{- Found books coming from Barnes & Noble (for their NOOK reader) to- contain illegal characters before the XML declaration. This is- strictly not allowed by the XML specification. I am very- disappointed with Barnes & Noble for selling garbage like this.--}-testIllegalCharsBeforeDecl :: Test-testIllegalCharsBeforeDecl = TestCase $ do- xmlString <- readFile $- "testsuite" </> "testIllegalCharsBeforeDecl.opf"- actual <- runErrorT $ parseXmlToOpf xmlString- let expected =- Right Package- { opVersion = "2.0"- , opUniqueId = "uuid_id"- , opMeta = Metadata- { metaTitles = [Title Nothing "Foo Bar Baz"]- , metaCreators = []- , metaContributors = []- , metaSubjects = []- , metaDescriptions = []- , metaPublishers = []- , metaDates = []- , metaTypes = []- , metaFormats = []- , metaIds = [Identifier "uuid_id" (Just "uuid") "1122334455"]- , metaSources = []- , metaLangs = ["en"]- , metaRelations = []- , metaCoverages = []- , metaRights = []}- , opManifest =- [ ManifestItem- { mfiId = "ncx"- , mfiHref = "toc.ncx"- , mfiMediaType = "application/x-dtbncx+xml"- }- ]- , opSpine = Spine {spineToc = "ncx", spineItemrefs = []}- , opGuide = []- }- assertEqual "illegal chars before XML declaration" expected actual
+ testsuite/ParseManifest.hs view
@@ -0,0 +1,58 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++module ParseManifest+ ( tests )+ where++import Control.Monad.Error+import System.FilePath+import Test.HUnit++import Codec.Epub.Data.Manifest+import Codec.Epub.Parse+++tests :: Test+tests = TestList+ [ testSeveral+ , testMissing+ ]+++{- A fairly comprehensive test containing several manifest items+-}+testSeveral :: Test+testSeveral = TestCase $ do+ xmlString <- readFile $ "testsuite" </> "epub2-full.opf"+ actual <- runErrorT $ getManifest xmlString+ let expected =+ Right $ Manifest [ ManifestItem + { mfiId = "ncx"+ , mfiHref = "toc.ncx"+ , mfiMediaType = "application/x-dtbncx+xml"+ }+ , ManifestItem + { mfiId = "titlePage"+ , mfiHref = "content/titlePage.html"+ , mfiMediaType = "application/xhtml+xml"+ }+ , ManifestItem + { mfiId = "someContent"+ , mfiHref = "content/someContent.html"+ , mfiMediaType = "application/xhtml+xml"+ }+ ]+ assertEqual "several manifest items" expected actual+++{- Test data devoid of any manifest items+-}+testMissing :: Test+testMissing = TestCase $ do+ xmlString <- readFile $ "testsuite" </> "epub2-missingAll.opf"+ actual <- runErrorT $ getManifest xmlString+ let expected =+ Right $ Manifest []+ assertEqual "missing entirely" expected actual
+ testsuite/ParsePackage.hs view
@@ -0,0 +1,34 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++module ParsePackage+ ( tests )+ where++import Control.Monad.Error+import System.FilePath+import Test.HUnit++import Codec.Epub.Data.Package+import Codec.Epub.Parse+++tests :: Test+tests = TestList+ [ testEpub2+ ]+++{- Test reading the package info for an EPUB2 document+-}+testEpub2 :: Test+testEpub2 = TestCase $ do+ xmlString <- liftIO $ readFile $ "testsuite" </> "epub2-minimal.opf"+ actual <- runErrorT $ getPackage xmlString+ let expected = + Right Package + { pkgVersion = "2.0"+ , pkgUniqueId = "isbn"+ }+ assertEqual "epub2 package info" expected actual
+ testsuite/ParseSpine.hs view
@@ -0,0 +1,59 @@+-- Copyright: 2010-2013 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++module ParseSpine+ ( tests )+ where++import Control.Monad.Error+import System.FilePath+import Test.HUnit++import Codec.Epub.Data.Spine+import Codec.Epub.Parse+++tests :: Test+tests = TestList+ [ testFull+ , testMinimal+ , testMissingAll+ ]+++{- A test containing a couple of spine items+-}+testFull :: Test+testFull = TestCase $ do+ xmlString <- readFile $ "testsuite" </> "epub2-full.opf"+ actual <- runErrorT $ getSpine xmlString+ let expected =+ Right Spine+ { spineToc = "ncx"+ , spineItemrefs = + [ SpineItemref {siIdRef = "titlePage", siLinear = Nothing}+ , SpineItemref {siIdRef = "someContent", siLinear = Nothing}+ ]+ }+ assertEqual "some spine items" expected actual+++{- Minimal spine, only the spineToc is required+-}+testMinimal :: Test+testMinimal = TestCase $ do+ xmlString <- liftIO $ readFile $ "testsuite" </> "epub2-minimal.opf"+ actual <- runErrorT $ getSpine xmlString+ let expected = Right Spine {spineToc = "ncx", spineItemrefs = []}+ assertEqual "minimal" expected actual+++{- Missing any spine info at all+-}+testMissingAll :: Test+testMissingAll = TestCase $ do+ xmlString <- readFile $ "testsuite" </> "epub2-missingAll.opf"+ actual <- runErrorT $ getSpine xmlString+ let expected = Right Spine {spineToc = "", spineItemrefs = []}+ assertEqual "missing all" expected actual
+ testsuite/epub2-full.opf view
@@ -0,0 +1,67 @@+<?xml version="1.0" encoding="UTF-8"?>+<package+ xmlns="http://www.idpf.org/2007/opf"+ version="2.0"+ unique-identifier="isbn">+ <metadata+ xmlns:dc="http://purl.org/dc/elements/1.1/"+ xmlns:opf="http://www.idpf.org/2007/opf"+ xmlns:xml="http://www.w3.org/XML/1998/namespace">+ <dc:title>Title Of This Book</dc:title>+ <dc:title xml:lang="fr">Titre De Ce Livre</dc:title>+ <dc:creator opf:role="aut" opf:file-as="Wiggins, Josephine B.">Josephine B. Wiggins</dc:creator>+ <dc:creator opf:role="aut">Horatio Cromwell</dc:creator>+ <dc:creator>Natalia Jenkins</dc:creator>+ <dc:contributor opf:role="ill" opf:file-as="Knickerbocker, Reginald Q.">Reginald Q. Knickerbocker</dc:contributor>+ <dc:contributor opf:role="edt">Beverly Abercrombie</dc:contributor>+ <dc:description>This document is a stub used for unit testing. It is missing the rest of the tags that normally occur after metadata.</dc:description>+ <dc:description>An additional description</dc:description>+ <dc:identifier id="isbn" opf:scheme="ISBN">1-82057-821-9</dc:identifier>+ <dc:date opf:event="published">2010</dc:date>+ <dc:date opf:event="created">2010-05-07</dc:date>+ <dc:date opf:event="modified">2010-05-08T10:20:57</dc:date>+ <dc:date>2009</dc:date>+ <dc:language>en-US</dc:language>+ <dc:language>en-UK</dc:language>+ <dc:publisher>Fictional Books Ltd.</dc:publisher>+ <dc:publisher>An additional publisher</dc:publisher>+ <dc:identifier id="other">1386506873266</dc:identifier>+ <dc:subject>Fiction</dc:subject>+ <dc:subject>Science Fiction</dc:subject>+ <dc:type>test OPF Package Document</dc:type>+ <dc:format>ePub publication</dc:format>+ <dc:format>an additional format</dc:format>+ <dc:source>document source</dc:source>+ <dc:relation>document relation</dc:relation>+ <dc:relation>an additional relation</dc:relation>+ <dc:coverage>coverage information</dc:coverage>+ <dc:coverage>an additional coverage</dc:coverage>+ <dc:rights>Copyright: 2010 Dino Morelli, License: BSD3</dc:rights>+ <dc:rights>an additional rights</dc:rights>+ </metadata>++ <manifest>+ <item id="ncx"+ href="toc.ncx"+ media-type="application/x-dtbncx+xml" />+ <item id="titlePage"+ href="content/titlePage.html"+ media-type="application/xhtml+xml" />+ <item id="someContent"+ href="content/someContent.html"+ media-type="application/xhtml+xml" />+ </manifest>++ <spine toc="ncx">+ <itemref idref="titlePage" />+ <itemref idref="someContent" />+ </spine>++ <guide>+ <reference type="title-page" title="Title page"+ href="content/titlePage.html" />+ <reference type="text" title="Title Of This Book"+ href="content/someContent.html" />+ </guide>++</package>
+ testsuite/epub2-minimal.opf view
@@ -0,0 +1,24 @@+<?xml version="1.0" encoding="UTF-8"?>+<package+ xmlns="http://www.idpf.org/2007/opf"+ version="2.0"+ unique-identifier="isbn">+ <metadata+ xmlns:dc="http://purl.org/dc/elements/1.1/"+ xmlns:opf="http://www.idpf.org/2007/opf"+ xmlns:xml="http://www.w3.org/XML/1998/namespace">+ <dc:title>Title Of This Book</dc:title>+ <dc:identifier id="isbn" opf:scheme="ISBN">1-82057-821-9</dc:identifier>+ <dc:language>en-us</dc:language>+ </metadata>++ <manifest>+ <item id="ncx"+ href="toc.ncx"+ media-type="application/x-dtbncx+xml" />+ </manifest>++ <spine toc="ncx">+ </spine>++</package>
+ testsuite/epub2-missingAll.opf view
@@ -0,0 +1,15 @@+<?xml version="1.0" encoding="UTF-8"?>+<package xmlns="http://www.idpf.org/2007/opf">+ <metadata+ xmlns:dc="http://purl.org/dc/elements/1.1/"+ xmlns:opf="http://www.idpf.org/2007/opf"+ xmlns:xml="http://www.w3.org/XML/1998/namespace">+ </metadata>++ <manifest>+ </manifest>++ <spine>+ </spine>++</package>
+ testsuite/epub3-full.opf view
@@ -0,0 +1,97 @@+<?xml version="1.0" encoding="UTF-8"?>+<package+ xmlns="http://www.idpf.org/2007/opf"+ version="3.0"+ unique-identifier="isbn">++ <metadata+ xmlns:dc="http://purl.org/dc/elements/1.1/"+ xmlns:xml="http://www.w3.org/XML/1998/namespace">++ <dc:identifier id="isbn">1-82057-821-9</dc:identifier>+ <dc:identifier id="other">1386506873266</dc:identifier>+ <meta refines="#other" property="identifier-type"+ scheme="some-scheme">some-type</meta>++ <dc:title id="t1">Title Of This Book</dc:title>+ <meta refines="#t1" property="title-type">main</meta>+ <meta refines="#t1" property="display-seq">1</meta>+ <dc:title id="t2">The Subtitle</dc:title>+ <meta refines="#t2" property="title-type">subtitle</meta>+ <meta refines="#t2" property="display-seq">2</meta>+ <dc:title xml:lang="fr">Titre De Ce Livre</dc:title>++ <dc:language>en-US</dc:language>+ <dc:language>en-UK</dc:language>++ <dc:contributor id="contrib1">Reginald Q. Knickerbocker</dc:contributor>+ <meta refines="#contrib1" property="role">ill</meta>+ <meta refines="#contrib1" property="file-as">Knickerbocker, Reginald Q.</meta>+ <dc:contributor id="contrib2">Beverly Abercrombie</dc:contributor>+ <meta refines="#contrib2" property="role">edt</meta>++ <dc:creator id="creator1">Josephine B. Wiggins</dc:creator>+ <meta refines="#creator1" property="role">aut</meta>+ <meta refines="#creator1" property="file-as">Wiggins, Josephine B.</meta>+ <meta refines="#creator1" property="display-seq">2</meta>+ <dc:creator id="creator2">Horatio Cromwell</dc:creator>+ <meta refines="#creator2" property="role">aut</meta>+ <meta refines="#creator2" property="display-seq">1</meta>+ <dc:creator id="creator3">Natalia Jenkins</dc:creator>+ <meta refines="#creator3" property="display-seq">3</meta>++ <dc:date>2012</dc:date>++ <meta property="dcterms:modified">2013-08-31T13:06:32Z</meta>++ <dc:source>document source</dc:source>++ <dc:type>test OPF Package Document</dc:type>++ <dc:coverage>coverage information</dc:coverage>+ <dc:coverage>an additional coverage</dc:coverage>++ <dc:description>This document is a stub used for unit testing. It is missing the rest of the tags that normally occur after metadata.</dc:description>+ <dc:description>An additional description</dc:description>++ <dc:format>ePub publication</dc:format>+ <dc:format>an additional format</dc:format>++ <dc:publisher>Fictional Books Ltd.</dc:publisher>+ <dc:publisher>An additional publisher</dc:publisher>++ <dc:relation>document relation</dc:relation>+ <dc:relation>an additional relation</dc:relation>++ <dc:subject>Fiction</dc:subject>+ <dc:subject>Science Fiction</dc:subject>++ <dc:rights>Copyright: 2010 Dino Morelli, License: BSD3</dc:rights>+ <dc:rights>an additional rights</dc:rights>+ </metadata>++ <manifest>+ <item id="ncx"+ href="toc.ncx"+ media-type="application/x-dtbncx+xml" />+ <item id="titlePage"+ href="content/titlePage.html"+ media-type="application/xhtml+xml" />+ <item id="someContent"+ href="content/someContent.html"+ media-type="application/xhtml+xml" />+ </manifest>++ <spine toc="ncx">+ <itemref idref="titlePage" />+ <itemref idref="someContent" />+ </spine>++ <guide>+ <reference type="title-page" title="Title page"+ href="content/titlePage.html" />+ <reference type="text" title="Title Of This Book"+ href="content/someContent.html" />+ </guide>++</package>
testsuite/test-main.hs view
@@ -6,7 +6,12 @@ import Test.HUnit hiding ( counts ) import qualified Archive-import qualified OpfParse+import qualified Epub2.ParseGuide+import qualified Epub2.ParseMetadata+import qualified Epub3.ParseMetadata+import qualified ParsePackage+import qualified ParseManifest+import qualified ParseSpine main :: IO ()@@ -27,5 +32,10 @@ tests :: Test tests = TestList [ Archive.tests- , OpfParse.tests+ , Epub2.ParseGuide.tests+ , Epub2.ParseMetadata.tests+ , Epub3.ParseMetadata.tests+ , ParsePackage.tests+ , ParseManifest.tests+ , ParseSpine.tests ]
− testsuite/testFull.opf
@@ -1,69 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<package- xmlns="http://www.idpf.org/2007/opf"- version="2.0"- unique-identifier="isbn">- <metadata- xmlns:dc="http://purl.org/dc/elements/1.1/"- xmlns:opf="http://www.idpf.org/2007/opf"- xmlns:xml="http://www.w3.org/XML/1998/namespace">- <dc:title>Title Of This Book</dc:title>- <dc:title xml:lang="fr">Titre De Ce Livre</dc:title>- <dc:creator opf:role="aut" opf:file-as="Wiggins, Josephine B.">Josephine B. Wiggins</dc:creator>- <dc:creator opf:role="aut">Horatio Cromwell</dc:creator>- <dc:creator>Natalia Jenkins</dc:creator>- <dc:contributor opf:role="ill" opf:file-as="Knickerbocker, Reginald Q.">Reginald Q. Knickerbocker</dc:contributor>- <dc:contributor opf:role="edt">Beverly Abercrombie</dc:contributor>- <dc:description>This document is a stub used for unit testing. It is missing the rest of the tags that normally occur after metadata.</dc:description>- <dc:description>An additional description</dc:description>- <dc:identifier id="isbn" opf:scheme="ISBN">1-82057-821-9</dc:identifier>- <dc:date opf:event="published">2010</dc:date>- <dc:date opf:event="created">2010-05-07</dc:date>- <dc:date opf:event="modified">2010-05-08T10:20:57</dc:date>- <dc:date>2009</dc:date>- <dc:language>en-US</dc:language>- <dc:language>en-UK</dc:language>- <dc:publisher>Fictional Books Ltd.</dc:publisher>- <dc:publisher>An additional publisher</dc:publisher>- <dc:identifier id="other">1386506873266</dc:identifier>- <dc:subject>Fiction</dc:subject>- <dc:subject>Science Fiction</dc:subject>- <dc:type>test OPF Package Document</dc:type>- <dc:type>an additional type</dc:type>- <dc:format>ePub publication</dc:format>- <dc:format>an additional format</dc:format>- <dc:source>document source</dc:source>- <dc:source>an additional source</dc:source>- <dc:relation>document relation</dc:relation>- <dc:relation>an additional relation</dc:relation>- <dc:coverage>coverage information</dc:coverage>- <dc:coverage>an additional coverage</dc:coverage>- <dc:rights>Copyright: 2010 Dino Morelli, License: BSD3</dc:rights>- <dc:rights>an additional rights</dc:rights>- </metadata>-- <manifest>- <item id="ncx"- href="toc.ncx"- media-type="application/x-dtbncx+xml" />- <item id="titlePage"- href="content/titlePage.html"- media-type="application/xhtml+xml" />- <item id="someContent"- href="content/someContent.html"- media-type="application/xhtml+xml" />- </manifest>-- <spine toc="ncx">- <itemref idref="titlePage" />- <itemref idref="someContent" />- </spine>-- <guide>- <reference type="title-page" title="Title page"- href="content/titlePage.html" />- <reference type="text" title="Title Of This Book"- href="content/someContent.html" />- </guide>--</package>
− testsuite/testMinimal.opf
@@ -1,24 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<package- xmlns="http://www.idpf.org/2007/opf"- version="2.0"- unique-identifier="isbn">- <metadata- xmlns:dc="http://purl.org/dc/elements/1.1/"- xmlns:opf="http://www.idpf.org/2007/opf"- xmlns:xml="http://www.w3.org/XML/1998/namespace">- <dc:title>Title Of This Book</dc:title>- <dc:identifier id="isbn" opf:scheme="ISBN">1-82057-821-9</dc:identifier>- <dc:language>en-us</dc:language>- </metadata>-- <manifest>- <item id="ncx"- href="toc.ncx"- media-type="application/x-dtbncx+xml" />- </manifest>-- <spine toc="ncx">- </spine>--</package>
− testsuite/testMissingAll.opf
@@ -1,15 +0,0 @@-<?xml version="1.0" encoding="UTF-8"?>-<package xmlns="http://www.idpf.org/2007/opf">- <metadata- xmlns:dc="http://purl.org/dc/elements/1.1/"- xmlns:opf="http://www.idpf.org/2007/opf"- xmlns:xml="http://www.w3.org/XML/1998/namespace">- </metadata>-- <manifest>- </manifest>-- <spine>- </spine>--</package>