git.iko.soy/typeable/pandoc
on master help sign in
EPUB: Correctly handle internal links.

Previously they were ignored. Now all links are preserved, but purely internal links are modified so that they point to the proper place in the EPUB. This is nontrivial, since the heading you refer to in your markdown source with 'my-section-1' might end up as 'ch16.xhtml#my-section' in the EPUB. Closes #76.

commit4fd9fb9ea24b5a30e0cfa50ec5317186af114df8 authorJohn MacFarlane <fiddlosopher@gmail.com> dateWed 28 Dec 2011, 19:40 (15 years ago) parentaf3e07f
1 file changed+55−6
src/Text/Pandoc/Writers/EPUB.hs+55−6
src/Text/Pandoc/Writers/EPUB.hs+55−6
@@ -40,13 +40,13 @@ import System.Time
4040import Text.Pandoc.Shared hiding ( Element )
4141import Text.Pandoc.Definition
4242import Text.Pandoc.Generic
43−import Control.Monad (liftM)
43+import Control.Monad.State
4444import Text.XML.Light hiding (ppTopElement)
4545import Text.Pandoc.UUID
4646import Text.Pandoc.Writers.HTML
4747import Text.Pandoc.Writers.Markdown ( writePlain )
4848import Data.Char ( toLower )
49−import Network.URI ( unEscapeString, isRelativeReference )
49+import Network.URI ( unEscapeString )
5050
5151-- | Produce an EPUB file from a Pandoc document.
5252writeEPUB :: Maybe String -- ^ EPUB stylesheet specified at command line
@@ -104,12 +104,16 @@ writeEPUB mbStylesheet opts doc@(Pandoc meta _) = do
104104 -- body pages
105105 let isH1 (Header 1 _) = True
106106 isH1 _ = False
107− let h1Indices = dropWhile (== 0) $ findIndices isH1 blocks
108− let chunks = splitByIndices h1Indices blocks
107+ -- internal reference IDs change when we chunk the file,
108+ -- so the next two lines fix that:
109+ let reftable = correlateRefs blocks
110+ let blocks' = replaceRefs reftable blocks
111+ let h1Indices = dropWhile (== 0) $ findIndices isH1 blocks'
112+ let chunks = splitByIndices h1Indices blocks'
109113 let titleize (Header 1 xs : ys) = Pandoc meta{docTitle = xs} ys
110114 titleize xs = Pandoc meta xs
111− let chapToHtml = writeHtmlString opts'{ writerTemplate = pageTemplate }
112115 let chapters = map titleize chunks
116+ let chapToHtml = writeHtmlString opts'{ writerTemplate = pageTemplate }
113117 let chapterToEntry :: Int -> Pandoc -> Entry
114118 chapterToEntry num chap = mkEntry ("ch" ++ show num ++ ".xhtml") $
115119 fromString $ chapToHtml chap
@@ -271,7 +275,6 @@ transformInlines (MathML _) _ _ (x@(Math _ _) : xs) = do
271275 result = if "<math" `isPrefixOf` mathml then inOps else mathml
272276 return $ RawInline "html" result : xs
273277transformInlines _ _ _ (RawInline _ _ : xs) = return $ Str "" : xs
274−transformInlines _ _ _ (Link lab (src,_) : xs) | isRelativeReference src = return $ lab ++ xs
275278transformInlines _ _ _ xs = return xs
276279
277280transformBlock :: Block -> Block
@@ -305,3 +308,49 @@ imageTypeOf x = case drop 1 (map toLower (takeExtension x)) of
305308 "svg" -> Just "image/svg+xml"
306309 _ -> Nothing
307310
311+
312+data IdentState = IdentState{
313+ chapterNumber :: Int,
314+ runningIdents :: [String],
315+ chapterIdents :: [String],
316+ identTable :: [(String,String)]
317+ } deriving (Read, Show)
318+
319+-- Go through a block list and construct a table
320+-- correlating the automatically constructed references
321+-- that would be used in a normal pandoc document with
322+-- new URLs to be used in the EPUB. For example, what
323+-- was "header-1" might turn into "ch6.xhtml#header".
324+correlateRefs :: [Block] -> [(String,String)]
325+correlateRefs bs = identTable $ execState (mapM_ go bs)
326+ IdentState{ chapterNumber = 0
327+ , runningIdents = []
328+ , chapterIdents = []
329+ , identTable = [] }
330+ where go :: Block -> State IdentState ()
331+ go (Header n ils) = do
332+ when (n == 1) $
333+ modify $ \s -> s{ chapterNumber = chapterNumber s + 1
334+ , chapterIdents = [] }
335+ st <- get
336+ let runningid = uniqueIdent ils (runningIdents st)
337+ let chapid = if n == 1
338+ then Nothing
339+ else Just $ uniqueIdent ils (chapterIdents st)
340+ modify $ \s -> s{ runningIdents = runningid : runningIdents st
341+ , chapterIdents = maybe (chapterIdents st)
342+ (: chapterIdents st) chapid
343+ , identTable = (runningid, "ch" ++ show (chapterNumber st) ++
344+ ".xhtml" ++ maybe "" ('#':) chapid) : identTable st
345+ }
346+ go _ = return ()
347+
348+-- Replace internal link references using the table produced
349+-- by correlateRefs.
350+replaceRefs :: [(String,String)] -> [Block] -> [Block]
351+replaceRefs refTable = bottomUp replaceOneRef
352+ where replaceOneRef x@(Link lab ('#':xs,tit)) =
353+ case lookup xs refTable of
354+ Just url -> Link lab (url,tit)
355+ Nothing -> x
356+ replaceOneRef x = x