summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Dönszelmann <jonabent@gmail.com>2022-03-29 17:40:20 +0200
committerGitHub <noreply@github.com>2022-03-29 08:40:20 -0700
commitcd931e55b685a0f3526781fda724bbd7dbd0a908 (patch)
tree870d47b2510d80531c6763abc0a962b56a1d8879
parent40dd8fd129449fb9db356f418afffa5ae71ebfd4 (diff)
Refactor Text.Pandoc.Writers.EPUB (#7991)
Refactor for readability. Co-authored-by: Ola Wolska <A.k.wolska@student.tudelft.nl@gmail.com> Co-authored-by: Ivar de Bruin <ivardb@gmail.com> Co-authored-by: Jaap de Jong <jaapdejong15@gmail.com>
-rw-r--r--src/Text/Pandoc/Writers/EPUB.hs534
1 files changed, 321 insertions, 213 deletions
diff --git a/src/Text/Pandoc/Writers/EPUB.hs b/src/Text/Pandoc/Writers/EPUB.hs
index 794a338c7..8eb70746d 100644
--- a/src/Text/Pandoc/Writers/EPUB.hs
+++ b/src/Text/Pandoc/Writers/EPUB.hs
@@ -254,7 +254,7 @@ addMetadataFromXML e@(Element (QName name _ (Just "dc")) attrs _ _) md
| name == "coverage" = md { epubCoverage = Just $ strContent e }
| name == "rights" = md { epubRights = Just $ strContent e }
| name == "belongs-to-collection" = md { epubBelongsToCollection = Just $ strContent e }
- | name == "group-position" = md { epubGroupPosition = Just $ strContent e }
+ | name == "group-position" = md { epubGroupPosition = Just $ strContent e }
| otherwise = md
where getAttr n = lookupAttr (opfName n) attrs
addMetadataFromXML e@(Element (QName "meta" _ _) attrs _ _) md =
@@ -444,11 +444,14 @@ pandocToEPUB version opts doc = do
epubSubdir <- gets stEpubSubdir
let epub3 = version == EPUB3
+
let writeHtml o = fmap (UTF8.fromTextLazy . TL.fromStrict) .
writeHtmlStringForEPUB version o
metadata <- getEPUBMetadata opts meta
- let plainTitle = case docTitle' meta of
+ -- retreive title of document
+ let plainTitle :: Text
+ plainTitle = case docTitle' meta of
[] -> case epubTitle metadata of
[] -> "UNTITLED"
(x:_) -> titleText x
@@ -463,14 +466,18 @@ pandocToEPUB version opts doc = do
(\bs n -> mkEntry ("styles/stylesheet" ++ show n ++ ".css") bs)
stylesheets [(1 :: Int)..]
- let vars = Context $
+ -- writer variables
+ let vars :: Context Text
+ vars = Context $
M.delete "css" .
M.insert "epub3"
(toVal' $ if epub3 then "true" else "false") .
M.insert "lang" (toVal' $ epubLanguage metadata)
$ unContext $ writerVariables opts
- let cssvars useprefix = Context $ M.insert "css"
+ -- If True create paths relative to parent folder
+ let cssvars :: Bool -> Context Text
+ cssvars useprefix = Context $ M.insert "css"
(ListVal $ map
(\e -> toVal' $
(if useprefix then "../" else "") <>
@@ -479,7 +486,9 @@ pandocToEPUB version opts doc = do
stylesheetEntries)
mempty
- let opts' = opts{ writerEmailObfuscation = NoObfuscation
+ -- Add additional options for the writer
+ let opts' :: WriterOptions
+ opts' = opts{ writerEmailObfuscation = NoObfuscation
, writerSectionDivs = True
, writerVariables = vars
, writerHTMLMathMethod =
@@ -489,41 +498,7 @@ pandocToEPUB version opts doc = do
, writerWrapText = WrapAuto }
-- cover page
- (cpgEntry, cpicEntry) <-
- case epubCoverImage metadata of
- Nothing -> return ([],[])
- Just img -> do
- let fp = takeFileName img
- mediaPaths <- gets (map (fst . snd) . stMediaPaths)
- coverImageName <- -- see #4206
- if ("media/" <> fp) `elem` mediaPaths
- then getMediaNextNewName (takeExtension fp)
- else return fp
- imgContent <- lift $ P.readFileLazy img
- (coverImageWidth, coverImageHeight) <-
- case imageSize opts' (B.toStrict imgContent) of
- Right sz -> return $ sizeInPixels sz
- Left err' -> (0, 0) <$ report
- (CouldNotDetermineImageSize (T.pack img) err')
- cpContent <- lift $ writeHtml
- opts'{ writerVariables =
- Context (M.fromList [
- ("coverpage", toVal' "true"),
- ("pagetitle", toVal $
- escapeStringForXML plainTitle),
- ("cover-image",
- toVal' $ T.pack coverImageName),
- ("cover-image-width", toVal' $
- tshow coverImageWidth),
- ("cover-image-height", toVal' $
- tshow coverImageHeight)]) <>
- cssvars True <> vars }
- (Pandoc meta [])
- coverEntry <- mkEntry "text/cover.xhtml" cpContent
- coverImageEntry <- mkEntry ("media/" ++ coverImageName)
- imgContent
- return ( [ coverEntry ]
- , [ coverImageEntry ] )
+ (cpgEntry, cpicEntry) <- createCoverPage meta metadata opts' vars cssvars writeHtml plainTitle
-- title page
tpContent <- lift $ writeHtml opts'{
@@ -537,45 +512,22 @@ pandocToEPUB version opts doc = do
(Pandoc meta [])
tpEntry <- mkEntry "text/title_page.xhtml" tpContent
+
-- handle fonts
let matchingGlob f = do
xs <- lift $ P.glob f
when (null xs) $
report $ CouldNotFetchResource (T.pack f) "glob did not match any font files"
return xs
- let mkFontEntry f = mkEntry ("fonts/" ++ takeFileName f) =<<
+
+ let mkFontEntry :: PandocMonad m => FilePath -> StateT EPUBState m Entry
+ mkFontEntry f = mkEntry ("fonts/" ++ takeFileName f) =<<
lift (P.readFileLazy f)
fontFiles <- concat <$> mapM matchingGlob (writerEpubFonts opts')
fontEntries <- mapM mkFontEntry fontFiles
- -- set page progression direction attribution
- let progressionDirection = case epubPageDirection metadata of
- Just LTR | epub3 ->
- [("page-progression-direction", "ltr")]
- Just RTL | epub3 ->
- [("page-progression-direction", "rtl")]
- _ -> []
-
-- body pages
- let chapterHeaderLevel = writerEpubChapterLevel opts
-
- let isChapterHeader (Div _ (Header n _ _:_)) = n <= chapterHeaderLevel
- isChapterHeader _ = False
-
- let secsToChapters :: [Block] -> [Chapter]
- secsToChapters [] = []
- secsToChapters (d@(Div attr (h@(Header lvl _ _) : bs)) : rest)
- | chapterHeaderLevel == lvl =
- Chapter [d] : secsToChapters rest
- | chapterHeaderLevel > lvl =
- Chapter [Div attr (h:xs)] :
- secsToChapters ys ++ secsToChapters rest
- where (xs, ys) = break isChapterHeader bs
- secsToChapters bs =
- (if null xs then id else (Chapter xs :)) $ secsToChapters ys
- where (xs, ys) = break isChapterHeader bs
-
-- add level 1 header to beginning if none there
let secs = makeSections True Nothing
$ addIdentifiers opts
@@ -586,98 +538,26 @@ pandocToEPUB version opts doc = do
_ -> Header 1 ("",["unnumbered"],[])
(docTitle' meta) : blocks
- let chapters' = secsToChapters secs
-
- let extractLinkURL' :: Int -> Inline -> [(T.Text, T.Text)]
- extractLinkURL' num (Span (ident, _, _) _)
- | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
- extractLinkURL' num (Link (ident, _, _) _ _)
- | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
- extractLinkURL' num (Image (ident, _, _) _ _)
- | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
- extractLinkURL' num (RawInline fmt raw)
- | isHtmlFormat fmt
- = foldr (\tag ->
- case tag of
- TagOpen{} ->
- case fromAttrib "id" tag of
- "" -> id
- x -> ((x, showChapter num <> "#" <> x):)
- _ -> id)
- [] (parseTags raw)
- extractLinkURL' _ _ = []
-
- let extractLinkURL :: Int -> Block -> [(T.Text, T.Text)]
- extractLinkURL num (Div (ident, _, _) _)
- | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
- extractLinkURL num (Header _ (ident, _, _) _)
- | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
- extractLinkURL num (Table (ident,_,_) _ _ _ _ _)
- | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
- extractLinkURL num (RawBlock fmt raw)
- | isHtmlFormat fmt
- = foldr (\tag ->
- case tag of
- TagOpen{} ->
- case fromAttrib "id" tag of
- "" -> id
- x -> ((x, showChapter num <> "#" <> x):)
- _ -> id)
- [] (parseTags raw)
- extractLinkURL num b = query (extractLinkURL' num) b
-
- let reftable = concat $ zipWith (\(Chapter bs) num ->
- query (extractLinkURL num) bs)
- chapters' [1..]
+ -- create the chapters and their reftable from the original options and the sections
+ let (chapters, reftable) = createChaptersAndReftable opts secs
- let fixInternalReferences :: Inline -> Inline
- fixInternalReferences (Link attr lab (src, tit))
- | Just ('#', xs) <- T.uncons src = case lookup xs reftable of
- Just ys -> Link attr lab (ys, tit)
- Nothing -> Link attr lab (src, tit)
- fixInternalReferences x = x
+ -- Create the chapter entries from the chapters.
+ -- Also requires access to the extended writer options and context
+ -- as well as the css Context and html writer
+ chapterEntries <- createChapterEntries opts' vars cssvars writeHtml chapters
- -- internal reference IDs change when we chunk the file,
- -- so that '#my-header-1' might turn into 'chap004.xhtml#my-header'.
- -- this fixes that:
- let chapters = map (\(Chapter bs) ->
- Chapter $ walk fixInternalReferences bs)
- chapters'
- let chapToEntry num (Chapter bs) =
- mkEntry ("text/" ++ T.unpack (showChapter num)) =<<
- writeHtml opts'{ writerVariables =
- Context (M.fromList
- [("body-type", toVal' bodyType),
- ("pagetitle", toVal' $
- showChapter num)])
- <> cssvars True <> vars } pdoc
- where (pdoc, bodyType) =
- case bs of
- (Div (_,"section":_,kvs)
- (Header _ _ xs : _) : _) ->
- -- remove notes or we get doubled footnotes
- (Pandoc (setMeta "title"
- (walk removeNote $ fromList xs) nullMeta) bs,
- case lookup "epub:type" kvs of
- Nothing -> "bodymatter"
- Just x
- | x `elem` frontMatterTypes -> "frontmatter"
- | x `elem` backMatterTypes -> "backmatter"
- | otherwise -> "bodymatter")
- _ -> (Pandoc nullMeta bs, "bodymatter")
- frontMatterTypes = ["prologue", "abstract", "acknowledgments",
- "copyright-page", "dedication",
- "credits", "keywords", "imprint",
- "contributors", "other-credits",
- "errata", "revision-history",
- "titlepage", "halftitlepage", "seriespage",
- "foreword", "preface", "frontispiece",
- "seriespage", "titlepage"]
- backMatterTypes = ["appendix", "colophon", "bibliography",
- "index"]
- chapterEntries <- zipWithM chapToEntry [1..] chapters
+ -- contents.opf
+
+ -- set page progression direction attribution
+ let progressionDirection :: [(Text, Text)]
+ progressionDirection = case epubPageDirection metadata of
+ Just LTR | epub3 ->
+ [("page-progression-direction", "ltr")]
+ Just RTL | epub3 ->
+ [("page-progression-direction", "rtl")]
+ _ -> []
-- incredibly inefficient (TODO):
let containsMathML ent = epub3 &&
@@ -688,7 +568,6 @@ pandocToEPUB version opts doc = do
B8.unpack (fromEntry ent)
let props ent = ["mathml" | containsMathML ent] ++ ["svg" | containsSVG ent]
- -- contents.opf
let chapterNode ent = unode "item" !
([("id", toId $ makeRelative epubSubdir
$ eRelativePath ent),
@@ -719,12 +598,12 @@ pandocToEPUB version opts doc = do
("media-type", fromMaybe "" $
getMimeType $ eRelativePath ent)] $ ()
+ -- The tocTitle is either the normal title or a specially configured title.
let tocTitle = maybe plainTitle
metaValueToString $ lookupMeta "toc-title" meta
- uuid <- case epubIdentifier metadata of
- (x:_) -> return $ identifierText x -- use first identifier as UUID
- [] -> throwError $ PandocShouldNeverHappenError "epubIdentifier is null" -- shouldn't happen
currentTime <- lift P.getTimestamp
+
+ -- Construct the contentsData
let contentsData = UTF8.fromTextLazy $ TL.fromStrict $ ppTopElement $
unode "package" !
([("version", case version of
@@ -783,11 +662,13 @@ pandocToEPUB version opts doc = do
| isJust (epubCoverImage metadata)
]
]
+ -- Content should be stored in content.opf
contentsEntry <- mkEntry "content.opf" contentsData
-- toc.ncx
let tocLevel = writerTOCDepth opts
+ -- Helper function for both the toc and anv Entries
let navPointNode :: PandocMonad m
=> (Int -> [Inline] -> T.Text -> [Element] -> Element)
-> Block -> StateT Int m [Element]
@@ -812,7 +693,244 @@ pandocToEPUB version opts doc = do
navPointNode formatter (Div _ bs) =
concat <$> mapM (navPointNode formatter) bs
navPointNode _ _ = return []
+
+ -- Create the tocEntry from the metadata together with the sections and title.
+ tocEntry <- createTocEntry meta metadata plainTitle secs navPointNode
+
+ -- Create the navEntry using the metadata, all of the various writer options,
+ -- the CSS and HTML helpers, the document and toc title as well as the epub version and all of the sections
+ navEntry <- createNavEntry meta metadata opts opts' vars cssvars writeHtml plainTitle tocTitle version secs navPointNode
+
+ -- mimetype
+ mimetypeEntry <- mkEntry "mimetype" $
+ UTF8.fromStringLazy "application/epub+zip"
+
+ -- container.xml
+ let containerData = B.fromStrict $ UTF8.fromText $ ppTopElement $
+ unode "container" ! [("version","1.0")
+ ,("xmlns","urn:oasis:names:tc:opendocument:xmlns:container")] $
+ unode "rootfiles" $
+ unode "rootfile" ! [("full-path",
+ (if null epubSubdir
+ then ""
+ else T.pack epubSubdir <> "/") <> "content.opf")
+ ,("media-type","application/oebps-package+xml")] $ ()
+ containerEntry <- mkEntry "META-INF/container.xml" containerData
+
+ -- com.apple.ibooks.display-options.xml
+ let apple = B.fromStrict $ UTF8.fromText $ ppTopElement $
+ unode "display_options" $
+ unode "platform" ! [("name","*")] $
+ unode "option" ! [("name","specified-fonts")] $ ("true" :: Text)
+ appleEntry <- mkEntry "META-INF/com.apple.ibooks.display-options.xml" apple
+
+ -- construct archive
+ let archive = foldr addEntryToArchive emptyArchive $
+ [mimetypeEntry, containerEntry, appleEntry,
+ contentsEntry, tocEntry, navEntry, tpEntry] ++
+ stylesheetEntries ++ picEntries ++ cpicEntry ++
+ cpgEntry ++ chapterEntries ++ fontEntries
+ return $ fromArchive archive
+
+-- | Function used during conversion from pandoc to EPUB to create the cover page.
+-- The first Entry list is for the cover while the second one is for the cover image.
+-- If no cover images are specified, empty lists will be returned.
+createCoverPage :: PandocMonad m =>
+ Meta
+ -> EPUBMetadata
+ -> WriterOptions
+ -> Context Text
+ -> (Bool -> Context Text)
+ -> (WriterOptions -> Pandoc -> m B8.ByteString)
+ -> Text
+ -> StateT EPUBState m ([Entry], [Entry])
+createCoverPage meta metadata opts' vars cssvars writeHtml plainTitle =
+ case epubCoverImage metadata of
+ Nothing -> return ([],[])
+ Just img -> do
+ let fp = takeFileName img
+ -- retrieve cover image file
+ mediaPaths <- gets (map (fst . snd) . stMediaPaths)
+ coverImageName <- -- see #4206
+ if ("media/" <> fp) `elem` mediaPaths
+ then getMediaNextNewName (takeExtension fp)
+ else return fp
+ -- image dimensions
+ imgContent <- lift $ P.readFileLazy img
+ (coverImageWidth, coverImageHeight) <-
+ case imageSize opts' (B.toStrict imgContent) of
+ Right sz -> return $ sizeInPixels sz
+ Left err' -> (0, 0) <$ report
+ (CouldNotDetermineImageSize (T.pack img) err')
+ -- write the HTML. Use the cssvars, vars and additional writer options.
+ cpContent <- lift $ writeHtml
+ opts'{ writerVariables =
+ Context (M.fromList [
+ ("coverpage", toVal' "true"),
+ ("pagetitle", toVal $
+ escapeStringForXML plainTitle),
+ ("cover-image",
+ toVal' $ T.pack coverImageName),
+ ("cover-image-width", toVal' $
+ tshow coverImageWidth),
+ ("cover-image-height", toVal' $
+ tshow coverImageHeight)]) <>
+ cssvars True <> vars }
+ (Pandoc meta [])
+
+ coverEntry <- mkEntry "text/cover.xhtml" cpContent
+ coverImageEntry <- mkEntry ("media/" ++ coverImageName)
+ imgContent
+
+ return ( [ coverEntry ], [ coverImageEntry ] )
+
+-- | Converts the given chapters to entries using the writeHtml function
+-- and the various provided options
+createChapterEntries :: PandocMonad m =>
+ WriterOptions
+ -> Context Text
+ -> (Bool -> Context Text)
+ -> (WriterOptions -> Pandoc -> StateT EPUBState m B8.ByteString)
+ -> [Chapter]
+ -> StateT EPUBState m [Entry]
+createChapterEntries opts' vars cssvars writeHtml chapters = do
+ -- Create an entry from the chapter with the provided number.
+ -- chapToEntry :: Int -> Chapter -> StateT EPUBState m Entry
+ let chapToEntry num (Chapter bs) =
+ mkEntry ("text/" ++ T.unpack (showChapter num)) =<<
+ -- Combine all provided options
+ writeHtml opts'{ writerVariables =
+ Context (M.fromList
+ [("body-type", toVal' bodyType),
+ ("pagetitle", toVal' $
+ showChapter num)])
+ <> cssvars True <> vars } pdoc
+ where (pdoc, bodyType) =
+ case bs of
+ (Div (_,"section":_,kvs) (Header _ _ xs : _) : _) ->
+ -- remove notes or we get doubled footnotes
+ (Pandoc (setMeta "title"
+ (walk removeNote $ fromList xs) nullMeta) bs,
+ -- Check if the chapters belongs to the frontmatter,
+ -- backmatter of bodymatter defaulting to the body
+ case lookup "epub:type" kvs of
+ Nothing -> "bodymatter"
+ Just x
+ | x `elem` frontMatterTypes -> "frontmatter"
+ | x `elem` backMatterTypes -> "backmatter"
+ | otherwise -> "bodymatter")
+ _ -> (Pandoc nullMeta bs, "bodymatter")
+ frontMatterTypes = ["prologue", "abstract", "acknowledgments",
+ "copyright-page", "dedication",
+ "credits", "keywords", "imprint",
+ "contributors", "other-credits",
+ "errata", "revision-history",
+ "titlepage", "halftitlepage", "seriespage",
+ "foreword", "preface", "frontispiece",
+ "seriespage", "titlepage"]
+ backMatterTypes = ["appendix", "colophon", "bibliography",
+ "index"]
+
+ zipWithM chapToEntry [1..] chapters
+
+-- | Splits the blocks into chapters and creates a corresponding reftable
+createChaptersAndReftable :: WriterOptions -> [Block] -> ([Chapter], [(Text, Text)])
+createChaptersAndReftable opts secs = (chapters, reftable)
+ where
+ chapterHeaderLevel = writerEpubChapterLevel opts
+
+ isChapterHeader :: Block -> Bool
+ isChapterHeader (Div _ (Header n _ _:_)) = n <= chapterHeaderLevel
+ isChapterHeader _ = False
+
+ secsToChapters :: [Block] -> [Chapter]
+ secsToChapters [] = []
+ secsToChapters (d@(Div attr (h@(Header lvl _ _) : bs)) : rest)
+ -- If the header is of the same level as chapters, create a chapter
+ | chapterHeaderLevel == lvl =
+ Chapter [d] : secsToChapters rest
+ -- If the header is a level higher than chapters,
+ -- create a chapter of everything until the next chapter header.
+ | chapterHeaderLevel > lvl =
+ Chapter [Div attr (h:xs)] :
+ secsToChapters ys ++ secsToChapters rest
+ where (xs, ys) = break isChapterHeader bs
+ secsToChapters bs =
+ -- If this is the last block, keep it as is,
+ -- otherwise create a chapter for everything until the next chapter header.
+ (if null xs then id else (Chapter xs :)) $ secsToChapters ys
+ where (xs, ys) = break isChapterHeader bs
+
+ -- Convert the sections to initial chapters
+ chapters' = secsToChapters secs
+
+ -- Extract references for the reftable from Inline elements
+ extractLinkURL' :: Int -> Inline -> [(T.Text, T.Text)]
+ extractLinkURL' num (Span (ident, _, _) _)
+ | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
+ extractLinkURL' num (Link (ident, _, _) _ _)
+ | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
+ extractLinkURL' num (Image (ident, _, _) _ _)
+ | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
+ extractLinkURL' num (RawInline fmt raw)
+ | isHtmlFormat fmt
+ = foldr (\tag ->
+ case tag of
+ TagOpen{} ->
+ case fromAttrib "id" tag of
+ "" -> id
+ x -> ((x, showChapter num <> "#" <> x):)
+ _ -> id)
+ [] (parseTags raw)
+ extractLinkURL' _ _ = []
+
+ -- Extract references for the reftable from Block elements
+ extractLinkURL :: Int -> Block -> [(T.Text, T.Text)]
+ extractLinkURL num (Div (ident, _, _) _)
+ | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
+ extractLinkURL num (Header _ (ident, _, _) _)
+ | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
+ extractLinkURL num (Table (ident,_,_) _ _ _ _ _)
+ | not (T.null ident) = [(ident, showChapter num <> "#" <> ident)]
+ extractLinkURL num (RawBlock fmt raw)
+ | isHtmlFormat fmt
+ = foldr (\tag ->
+ case tag of
+ TagOpen{} ->
+ case fromAttrib "id" tag of
+ "" -> id
+ x -> ((x, showChapter num <> "#" <> x):)
+ _ -> id)
+ [] (parseTags raw)
+ extractLinkURL num b = query (extractLinkURL' num) b
+
+ -- Create a reference table for the chapters with appropriate numbering
+ reftable = concat $ zipWith (\(Chapter bs) num ->
+ query (extractLinkURL num) bs)
+ chapters' [1..]
+ fixInternalReferences :: Inline -> Inline
+ fixInternalReferences (Link attr lab (src, tit))
+ | Just ('#', xs) <- T.uncons src = case lookup xs reftable of
+ Just ys -> Link attr lab (ys, tit)
+ Nothing -> Link attr lab (src, tit)
+ fixInternalReferences x = x
+
+ -- internal reference IDs change when we chunk the file,
+ -- so that '#my-header-1' might turn into 'chap004.xhtml#my-header'.
+ -- this fixes that:
+ chapters = map (\(Chapter bs) ->
+ Chapter $ walk fixInternalReferences bs)
+ chapters'
+
+createTocEntry :: PandocMonad m =>
+ Meta
+ -> EPUBMetadata
+ -> Text
+ -> [Block]
+ -> ((Int -> [Inline] -> T.Text -> [Element] -> Element) -> Block -> StateT Int m [Element])
+ -> StateT EPUBState m Entry
+createTocEntry meta metadata plainTitle secs navPointNode = do
let navMapFormatter :: Int -> [Inline] -> T.Text -> [Element] -> Element
navMapFormatter n tit src subs = unode "navPoint" !
[("id", "navPoint-" <> tshow n)] $
@@ -827,6 +945,10 @@ pandocToEPUB version opts doc = do
navMap <- lift $ evalStateT
(concat <$> mapM (navPointNode navMapFormatter) secs) 1
+
+ uuid <- case epubIdentifier metadata of
+ (x:_) -> return $ identifierText x -- use first identifier as UUID
+ [] -> throwError $ PandocShouldNeverHappenError "epubIdentifier is null" -- shouldn't happen
let tocData = B.fromStrict $ UTF8.fromText $ ppTopElement $
unode "ncx" ! [("version","2005-1")
,("xmlns","http://www.daisy.org/z3986/2005/ncx/")] $
@@ -847,29 +969,45 @@ pandocToEPUB version opts doc = do
, unode "navMap" $
tpNode : navMap
]
- tocEntry <- mkEntry "toc.ncx" tocData
-
+ mkEntry "toc.ncx" tocData
+
+
+createNavEntry :: PandocMonad m =>
+ Meta
+ -> EPUBMetadata
+ -> WriterOptions
+ -> WriterOptions
+ -> Context Text
+ -> (Bool -> Context Text)
+ -> (WriterOptions -> Pandoc -> m B8.ByteString)
+ -> Text
+ -> Text
+ -> EPUBVersion
+ -> [Block]
+ -> ((Int -> [Inline] -> T.Text -> [Element] -> Element) -> Block -> StateT Int m [Element])
+ -> StateT EPUBState m Entry
+createNavEntry meta metadata opts opts' vars cssvars writeHtml plainTitle tocTitle version secs navPointNode = do
let navXhtmlFormatter :: Int -> [Inline] -> T.Text -> [Element] -> Element
navXhtmlFormatter n tit src subs = unode "li" !
- [("id", "toc-li-" <> tshow n)] $
- (unode "a" !
- [("href", "text/" <> src)]
- $ titElements)
- : case subs of
- [] -> []
- (_:_) -> [unode "ol" ! [("class","toc")] $ subs]
+ [("id", "toc-li-" <> tshow n)] $
+ (unode "a" !
+ [("href", "text/" <> src)]
+ $ titElements)
+ : case subs of
+ [] -> []
+ (_:_) -> [unode "ol" ! [("class","toc")] $ subs]
where titElements = either (const []) id $
parseXMLContents (TL.fromStrict titRendered)
titRendered = case P.runPure
- (writeHtmlStringForEPUB version
- opts{ writerTemplate = Nothing
- , writerVariables =
- Context (M.fromList
- [("pagetitle", toVal $
- escapeStringForXML plainTitle)])
- <> writerVariables opts}
- (Pandoc nullMeta
- [Plain $ walk clean tit])) of
+ (writeHtmlStringForEPUB version
+ opts{ writerTemplate = Nothing
+ , writerVariables =
+ Context (M.fromList
+ [("pagetitle", toVal $
+ escapeStringForXML plainTitle)])
+ <> writerVariables opts}
+ (Pandoc nullMeta
+ [Plain $ walk clean tit])) of
Left _ -> stringify tit
Right x -> x
-- can't have <a> elements inside generated links...
@@ -877,16 +1015,16 @@ pandocToEPUB version opts doc = do
clean (Note _) = Str ""
clean x = x
- let navtag = if epub3 then "nav" else "div"
+ let navtag = if version == EPUB3 then "nav" else "div"
tocBlocks <- lift $ evalStateT
(concat <$> mapM (navPointNode navXhtmlFormatter) secs) 1
let navBlocks = [RawBlock (Format "html")
$ showElement $ -- prettyprinting introduces bad spaces
- unode navtag ! ([("epub:type","toc") | epub3] ++
+ unode navtag ! ([("epub:type","toc") | version == EPUB3] ++
[("id","toc")]) $
[ unode "h1" ! [("id","toc-title")] $ tocTitle
, unode "ol" ! [("class","toc")] $ tocBlocks ]]
- let landmarkItems = if epub3
+ let landmarkItems = if version == EPUB3
then unode "li"
[ unode "a" ! [("href",
"text/title_page.xhtml")
@@ -919,38 +1057,8 @@ pandocToEPUB version opts doc = do
(Pandoc (setMeta "title"
(walk removeNote $ fromList $ docTitle' meta) nullMeta)
(navBlocks ++ landmarks))
- navEntry <- mkEntry "nav.xhtml" navData
-
- -- mimetype
- mimetypeEntry <- mkEntry "mimetype" $
- UTF8.fromStringLazy "application/epub+zip"
-
- -- container.xml
- let containerData = B.fromStrict $ UTF8.fromText $ ppTopElement $
- unode "container" ! [("version","1.0")
- ,("xmlns","urn:oasis:names:tc:opendocument:xmlns:container")] $
- unode "rootfiles" $
- unode "rootfile" ! [("full-path",
- (if null epubSubdir
- then ""
- else T.pack epubSubdir <> "/") <> "content.opf")
- ,("media-type","application/oebps-package+xml")] $ ()
- containerEntry <- mkEntry "META-INF/container.xml" containerData
-
- -- com.apple.ibooks.display-options.xml
- let apple = B.fromStrict $ UTF8.fromText $ ppTopElement $
- unode "display_options" $
- unode "platform" ! [("name","*")] $
- unode "option" ! [("name","specified-fonts")] $ ("true" :: Text)
- appleEntry <- mkEntry "META-INF/com.apple.ibooks.display-options.xml" apple
-
- -- construct archive
- let archive = foldr addEntryToArchive emptyArchive $
- [mimetypeEntry, containerEntry, appleEntry,
- contentsEntry, tocEntry, navEntry, tpEntry] ++
- stylesheetEntries ++ picEntries ++ cpicEntry ++
- cpgEntry ++ chapterEntries ++ fontEntries
- return $ fromArchive archive
+ -- Return
+ mkEntry "nav.xhtml" navData
metadataElement :: EPUBVersion -> EPUBMetadata -> UTCTime -> Element
metadataElement version md currentTime =
@@ -1002,7 +1110,7 @@ metadataElement version md currentTime =
$ epubCoverImage md
modifiedNodes = [ unode "meta" ! [("property", "dcterms:modified")] $
showDateTimeISO8601 currentTime | version == EPUB3 ]
- belongsToCollectionNodes =
+ belongsToCollectionNodes =
maybe []
(\belongsToCollection -> (unode "meta" ! [("property", "belongs-to-collection"), ("id", "epub-collection-1")] $ belongsToCollection )
: