@@ -1,4 +1,4 @@
1−{-# LANGUAGE PatternGuards, CPP, ScopedTypeVariables #-}
1+{-# LANGUAGE PatternGuards, CPP, ScopedTypeVariables, ViewPatterns #-}
3Copyright (C) 2010-2014 John MacFarlane <jgm@berkeley.edu>
3Copyright (C) 2010-2014 John MacFarlane <jgm@berkeley.edu>
@@ -29,7 +29,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29Conversion of 'Pandoc' documents to EPUB.
29Conversion of 'Pandoc' documents to EPUB.
31module Text.Pandoc.Writers.EPUB ( writeEPUB ) where
31module Text.Pandoc.Writers.EPUB ( writeEPUB ) where
32−import Data.IORef
32+import Data.IORef ( IORef, newIORef, readIORef, modifyIORef )
33import qualified Data.Map as M
33import qualified Data.Map as M
34import Data.Maybe ( fromMaybe )
34import Data.Maybe ( fromMaybe )
35import Data.List ( isPrefixOf, isInfixOf, intercalate )
35import Data.List ( isPrefixOf, isInfixOf, intercalate )
@@ -40,27 +40,35 @@ import qualified Data.ByteString.Lazy as B
40import qualified Data.ByteString.Lazy.Char8 as B8
40import qualified Data.ByteString.Lazy.Char8 as B8
41import qualified Text.Pandoc.UTF8 as UTF8
41import qualified Text.Pandoc.UTF8 as UTF8
42import Text.Pandoc.SelfContained ( makeSelfContained )
42import Text.Pandoc.SelfContained ( makeSelfContained )
43−import Codec.Archive.Zip
43+import Codec.Archive.Zip ( emptyArchive, addEntryToArchive, eRelativePath, fromEntry , Entry, toEntry, fromArchive)
44import Control.Applicative ((<$>))
44import Control.Applicative ((<$>))
45−import Data.Time.Clock.POSIX
45+import Data.Time.Clock.POSIX ( getPOSIXTime )
46−import Data.Time
46+import Data.Time (getCurrentTime,UTCTime, formatTime)
47−import System.Locale
47+import System.Locale ( defaultTimeLocale )
48−import Text.Pandoc.Shared hiding ( Element )
48+import Text.Pandoc.Shared ( trimr, renderTags', safeRead, uniqueIdent, trim
49+ , normalizeDate, readDataFile, stringify, warn
50+ , hierarchicalize, fetchItem' )
49−import qualified Text.Pandoc.Shared as Shared
51+import qualified Text.Pandoc.Shared as S (Element(..))
50import Text.Pandoc.Builder (fromList, setMeta)
52import Text.Pandoc.Builder (fromList, setMeta)
51−import Text.Pandoc.Options
53+import Text.Pandoc.Options ( WriterOptions(..)
56+ , ObfuscationMethod(NoObfuscation) )
52import Text.Pandoc.Definition
57import Text.Pandoc.Definition
53−import Text.Pandoc.Walk
58+import Text.Pandoc.Walk (walk, walkM)
54−import Control.Monad.State
59+import Control.Monad.State (modify, get, execState, State, put, evalState)
60+import Control.Monad (foldM, when, mplus, liftM)
55−import Text.XML.Light hiding (ppTopElement)
61+import Text.XML.Light ( unode, Element(..), unqual, Attr(..), add_attrs
62+ , strContent, lookupAttr, Node(..), QName(..), parseXML
63+ , onlyElems, node, ppElement)
56−import Text.Pandoc.UUID
64+import Text.Pandoc.UUID (getRandomUUID)
57−import Text.Pandoc.Writers.HTML
65+import Text.Pandoc.Writers.HTML (writeHtmlString, writeHtml)
58import Data.Char ( toLower, isDigit, isAlphaNum )
66import Data.Char ( toLower, isDigit, isAlphaNum )
59import Network.URI ( unEscapeString )
67import Network.URI ( unEscapeString )
60import Text.Pandoc.MIME (getMimeType)
68import Text.Pandoc.MIME (getMimeType)
61import qualified Control.Exception as E
69import qualified Control.Exception as E
62import Text.Blaze.Html.Renderer.Utf8 (renderHtml)
70import Text.Blaze.Html.Renderer.Utf8 (renderHtml)
63−import Text.HTML.TagSoup
71+import Text.HTML.TagSoup (Tag(TagOpen), fromAttrib, parseTags)
65-- A Chapter includes a list of blocks and maybe a section
73-- A Chapter includes a list of blocks and maybe a section
66-- number offset. Note, some chapters are unnumbered. The section
74-- number offset. Note, some chapters are unnumbered. The section
@@ -86,6 +94,7 @@ data EPUBMetadata = EPUBMetadata{
86 , epubRights :: Maybe String
94 , epubRights :: Maybe String
87 , epubCoverImage :: Maybe String
95 , epubCoverImage :: Maybe String
88 , epubStylesheet :: Maybe Stylesheet
96 , epubStylesheet :: Maybe Stylesheet
97+ , epubPageDirection :: ProgressionDirection
89 } deriving Show
98 } deriving Show
91data Stylesheet = StylesheetPath FilePath
100data Stylesheet = StylesheetPath FilePath
@@ -114,6 +123,8 @@ data Title = Title{
114 , titleType :: Maybe String
123 , titleType :: Maybe String
115 } deriving Show
124 } deriving Show
126+data ProgressionDirection = LTR | RTL | Default deriving Show
117dcName :: String -> QName
128dcName :: String -> QName
118dcName n = QName n Nothing (Just "dc")
129dcName n = QName n Nothing (Just "dc")
@@ -288,6 +299,7 @@ metadataFromMeta opts meta = EPUBMetadata{
288 , epubRights = rights
299 , epubRights = rights
289 , epubCoverImage = coverImage
300 , epubCoverImage = coverImage
290 , epubStylesheet = stylesheet
301 , epubStylesheet = stylesheet
302+ , epubPageDirection = pageDirection
292 where identifiers = getIdentifier meta
304 where identifiers = getIdentifier meta
293 titles = getTitle meta
305 titles = getTitle meta
@@ -310,6 +322,14 @@ metadataFromMeta opts meta = EPUBMetadata{
310 stylesheet = (StylesheetContents <$> writerEpubStylesheet opts) `mplus`
322 stylesheet = (StylesheetContents <$> writerEpubStylesheet opts) `mplus`
311 ((StylesheetPath . metaValueToString) <$>
323 ((StylesheetPath . metaValueToString) <$>
312 lookupMeta "stylesheet" meta)
324 lookupMeta "stylesheet" meta)
325+ pageDirection = maybe Default stringToPageDirection
326+ (lookupMeta "page-progression-direction" meta)
327+ stringToPageDirection (metaValueToString -> s) =
314-- | Produce an EPUB file from a Pandoc document.
334-- | Produce an EPUB file from a Pandoc document.
315writeEPUB :: WriterOptions -- ^ Writer options
335writeEPUB :: WriterOptions -- ^ Writer options
@@ -374,6 +394,12 @@ writeEPUB opts doc@(Pandoc meta _) = do
374 let mkFontEntry f = mkEntry (takeFileName f) `fmap` B.readFile f
394 let mkFontEntry f = mkEntry (takeFileName f) `fmap` B.readFile f
375 fontEntries <- mapM mkFontEntry $ writerEpubFonts opts'
395 fontEntries <- mapM mkFontEntry $ writerEpubFonts opts'
397+ -- set page progression direction
398+ let progressionDirection = case epubPageDirection metadata of
401+ Default -> "default"
377 -- body pages
403 -- body pages
379 -- add level 1 header to beginning if none there
405 -- add level 1 header to beginning if none there
@@ -493,7 +519,8 @@ writeEPUB opts doc@(Pandoc meta _) = do
493 (pictureNode x)]) ++
519 (pictureNode x)]) ++
494 map pictureNode picEntries ++
520 map pictureNode picEntries ++
495 map fontNode fontEntries
521 map fontNode fontEntries
496− , unode "spine" ! [("toc","ncx")] $
522+ , unode "spine" ! [("toc","ncx")
523+ ,("page-progression-direction", progressionDirection)] $
497 case epubCoverImage metadata of
524 case epubCoverImage metadata of
498 Nothing -> []
525 Nothing -> []
499 Just _ -> [ unode "itemref" !
526 Just _ -> [ unode "itemref" !
@@ -524,8 +551,8 @@ writeEPUB opts doc@(Pandoc meta _) = do
524 let tocLevel = writerTOCDepth opts
551 let tocLevel = writerTOCDepth opts
526 let navPointNode :: (Int -> String -> String -> [Element] -> Element)
553 let navPointNode :: (Int -> String -> String -> [Element] -> Element)
527− -> Shared.Element -> State Int Element
554+ -> S.Element -> State Int Element
528− navPointNode formatter (Sec _ nums (ident,_,_) ils children) = do
555+ navPointNode formatter (S.Sec _ nums (ident,_,_) ils children) = do
530 modify (+1)
557 modify (+1)
531 let showNums :: [Int] -> String
558 let showNums :: [Int] -> String
@@ -537,12 +564,12 @@ writeEPUB opts doc@(Pandoc meta _) = do
537 let src = case lookup ident reftable of
564 let src = case lookup ident reftable of
538 Just x -> x
565 Just x -> x
539 Nothing -> error (ident ++ " not found in reftable")
566 Nothing -> error (ident ++ " not found in reftable")
540− let isSec (Sec lev _ _ _ _) = lev <= tocLevel
567+ let isSec (S.Sec lev _ _ _ _) = lev <= tocLevel
541 isSec _ = False
568 isSec _ = False
542 let subsecs = filter isSec children
569 let subsecs = filter isSec children
543 subs <- mapM (navPointNode formatter) subsecs
570 subs <- mapM (navPointNode formatter) subsecs
544 return $ formatter n tit src subs
571 return $ formatter n tit src subs
545− navPointNode _ (Blk _) = error "navPointNode encountered Blk"
572+ navPointNode _ (S.Blk _) = error "navPointNode encountered Blk"
547 let navMapFormatter :: Int -> String -> String -> [Element] -> Element
574 let navMapFormatter :: Int -> String -> String -> [Element] -> Element
548 navMapFormatter n tit src subs = unode "navPoint" !
575 navMapFormatter n tit src subs = unode "navPoint" !