{-
Copyright (C) 2010 John MacFarlane <jgm@berkeley.edu>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-}{- |
Module : Text.Pandoc.Writers.EPUB
Copyright : Copyright (C) 2010 John MacFarlane
License : GNU GPL, version 2 or above
Maintainer : John MacFarlane <jgm@berkeley.edu>
Stability : alpha
Portability : portable
Conversion of 'Pandoc' documents to EPUB.
-}moduleText.Pandoc.Writers.EPUB(writeEPUB)whereimportData.IORefimportData.Maybe(fromMaybe,isNothing)importData.List(findIndices,isPrefixOf)importSystem.Environment(getEnv)importSystem.FilePath((</>),takeBaseName,takeExtension)importqualifiedData.ByteString.LazyasBimportData.ByteString.Lazy.UTF8(fromString)importCodec.Archive.ZipimportSystem.TimeimportText.Pandoc.Sharedhiding(Element)importText.Pandoc.DefinitionimportText.Pandoc.GenericimportControl.Monad(liftM)importText.XML.Lighthiding(ppTopElement)importText.Pandoc.UUIDimportText.Pandoc.Writers.HTMLimportText.Pandoc.Writers.Markdown(writePlain)importData.Char(toLower)importSystem.Directory(copyFile)importNetwork.URI(unEscapeString)-- | Produce an EPUB file from a Pandoc document.writeEPUB::MaybeString-- ^ EPUB stylesheet specified at command line->WriterOptions-- ^ Writer options->Pandoc-- ^ Document to convert->IOB.ByteStringwriteEPUBmbStylesheetoptsdoc@(Pandocmeta_)=do(TODepochtime_)<-getClockTimeletmkEntrypathcontent=toEntrypathepochtimecontentletopts'=opts{writerEmailObfuscation=NoObfuscation,writerStandalone=True,writerWrapText=False}letsourceDir=writerSourceDirectoryopts'letvars=writerVariablesopts'letmbCoverImage=lookup"epub-cover-image"vars-- cover page(cpgEntry,cpicEntry)<-casembCoverImageofNothing->return([],[])Justimg->doletcoverImage="cover-image"++takeExtensionimgcopyFileimgcoverImageletcpContent=fromString$writeHtmlStringopts'{writerTemplate=pageTemplate,writerVariables=("coverimage",coverImage):vars}(Pandocmeta[])imgContent<-B.readFileimgreturn([mkEntry"cover.xhtml"cpContent],[mkEntrycoverImageimgContent])-- title pagelettpContent=fromString$writeHtmlStringopts'{writerTemplate=pageTemplate,writerVariables=("titlepage","yes"):vars}(Pandocmeta[])lettpEntry=mkEntry"title_page.xhtml"tpContent-- handle picturespicsRef<-newIORef[]Pandoc_blocks<-liftM(bottomUptransformBlock)$bottomUpM(transformInlines(writerHTMLMathMethodopts)sourceDirpicsRef)docpics<-readIORefpicsRefletreadPicEntry(oldsrc,newsrc)=readEntry[]oldsrc>>=\e->returne{eRelativePath=newsrc}picEntries<-mapMreadPicEntrypics-- body pagesletisH1(Header1_)=TrueisH1_=Falseleth1Indices=dropWhile(==0)$findIndicesisH1blocksletchunks=splitByIndicesh1Indicesblockslettitleize(Header1xs:ys)=Pandocmeta{docTitle=xs}ystitleizexs=PandocmetaxsletchapToHtml=writeHtmlStringopts'{writerTemplate=pageTemplate}letchapters=maptitleizechunksletchapterToEntry::Int->Pandoc->EntrychapterToEntrynumchap=mkEntry("ch"++shownum++".xhtml")$fromString$chapToHtmlchapletchapterEntries=zipWithchapterToEntry[1..]chapters-- contents.opflang<-catch(liftM(takeWhile(/='.'))$getEnv"lang")(\_->return"en-US")uuid<-getRandomUUIDletchapterNodeent=unode"item"![("id",takeBaseName$eRelativePathent),("href",eRelativePathent),("media-type","application/xhtml+xml")]$()letchapterRefNodeent=unode"itemref"![("idref",takeBaseName$eRelativePathent)]$()letpictureNodeent=unode"item"![("id",takeBaseName$eRelativePathent),("href",eRelativePathent),("media-type",fromMaybe"application/octet-stream"$imageTypeOf$eRelativePathent)]$()letplainifyt=removeTrailingSpace$writePlainopts'{writerStandalone=False}$Pandocmeta[Plaint]letplainTitle=plainify$docTitlemetaletplainAuthors=mapplainify$docAuthorsmetaletcontentsData=fromString$ppTopElement$unode"package"![("version","2.0"),("xmlns","http://www.idpf.org/2007/opf"),("unique-identifier","BookId")]$[metadataElement(writerEPUBMetadataopts')uuidlangplainTitleplainAuthorsmbCoverImage,unode"manifest"$[unode"item"![("id","ncx"),("href","toc.ncx"),("media-type","application/x-dtbncx+xml")]$(),unode"item"![("id","style"),("href","stylesheet.css"),("media-type","text/css")]$()]++mapchapterNode(cpgEntry++(tpEntry:chapterEntries))++mappictureNode(cpicEntry++picEntries),unode"spine"![("toc","ncx")]$casembCoverImageofNothing->[]Just_->[unode"itemref"![("idref","cover"),("linear","no")]$()]++mapchapterRefNode(tpEntry:chapterEntries)]letcontentsEntry=mkEntry"content.opf"contentsData-- toc.ncxletnavPointNodeentntit=unode"navPoint"![("id","navPoint-"++shown),("playOrder",shown)]$[unode"navLabel"$unode"text"tit,unode"content"![("src",eRelativePathent)]$()]lettocData=fromString$ppTopElement$unode"ncx"![("version","2005-1"),("xmlns","http://www.daisy.org/z3986/2005/ncx/")]$[unode"head"$[unode"meta"![("name","dtb:uid"),("content",showuuid)]$(),unode"meta"![("name","dtb:depth"),("content","1")]$(),unode"meta"![("name","dtb:totalPageCount"),("content","0")]$(),unode"meta"![("name","dtb:maxPageNumber"),("content","0")]$()]++casembCoverImageofNothing->[]Just_->[unode"meta"![("name","cover"),("content","cover-image")]$()],unode"docTitle"$unode"text"$plainTitle,unode"navMap"$zipWith3navPointNode(tpEntry:chapterEntries)[1..(lengthchapterEntries+1)]("Title Page":map(\(Pandocm_)->plainify$docTitlem)chapters)]lettocEntry=mkEntry"toc.ncx"tocData-- mimetypeletmimetypeEntry=mkEntry"mimetype"$fromString"application/epub+zip"-- container.xmlletcontainerData=fromString$ppTopElement$unode"container"![("version","1.0"),("xmlns","urn:oasis:names:tc:opendocument:xmlns:container")]$unode"rootfiles"$unode"rootfile"![("full-path","content.opf"),("media-type","application/oebps-package+xml")]$()letcontainerEntry=mkEntry"META-INF/container.xml"containerData-- stylesheetstylesheet<-casembStylesheetofJusts->returnsNothing->readDataFile(writerUserDataDiropts)"epub.css"letstylesheetEntry=mkEntry"stylesheet.css"$fromStringstylesheet-- construct archiveletarchive=foldraddEntryToArchiveemptyArchive(mimetypeEntry:containerEntry:stylesheetEntry:tpEntry:contentsEntry:tocEntry:(picEntries++cpicEntry++cpgEntry++chapterEntries))return$fromArchivearchivemetadataElement::String->UUID->String->String->[String]->Maybea->ElementmetadataElementmetadataXMLuuidlangtitleauthorsmbCoverImage=letuserNodes=parseXMLmetadataXMLelt=unode"metadata"![("xmlns:dc","http://purl.org/dc/elements/1.1/"),("xmlns:opf","http://www.idpf.org/2007/opf")]$filterisDublinCoreElement$onlyElemsuserNodesdublinElements=["contributor","coverage","creator","date","description","format","identifier","language","publisher","relation","rights","source","subject","title","type"]isDublinCoreElemente=qPrefix(elNamee)==Just"dc"&&qName(elNamee)`elem`dublinElementscontainsen=not(null(findElements(QNamenNothing(Just"dc"))e))newNodes=[unode"dc:title"title|not(elt`contains`"title")]++[unode"dc:language"lang|not(elt`contains`"language")]++[unode"dc:identifier"![("id","BookId")]$showuuid|not(elt`contains`"identifier")]++[unode"dc:creator"![("opf:role","aut")]$a|a<-authors]++[unode"meta"![("name","cover"),("content","cover-image")]$()|not(isNothingmbCoverImage)]inelt{elContent=elContentelt++mapElemnewNodes}transformInlines::HTMLMathMethod->FilePath->IORef[(FilePath,FilePath)]-- ^ (oldpath, newpath) images->[Inline]->IO[Inline]transformInlines___(Imagelab(src,_):xs)|isNothing(imageTypeOfsrc)=return$Emphlab:xstransformInlines_sourceDirpicsRef(Imagelab(src,tit):xs)=doletsrc'=unEscapeStringsrcpics<-readIORefpicsRefletoldsrc=sourceDir</>src'letext=takeExtensionsrc'newsrc<-caselookupoldsrcpicsofJustn->returnnNothing->doletnew="images/img"++show(lengthpics)++extmodifyIORefpicsRef((oldsrc,new):)returnnewreturn$Imagelab(newsrc,tit):xstransformInlines(MathML_)__(x@(Math__):xs)=doletwriteHtmlInlineoptsz=removeTrailingSpace$writeHtmlStringopts$Pandoc(Meta[][][])[Plain[z]]mathml=writeHtmlInlinedefaultWriterOptions{writerHTMLMathMethod=MathMLNothing}xfallback=writeHtmlInlinedefaultWriterOptions{writerHTMLMathMethod=PlainMath}xinOps="<ops:switch xmlns:ops=\"http://www.idpf.org/2007/ops\">"++"<ops:case required-namespace=\"http://www.w3.org/1998/Math/MathML\">"++mathml++"</ops:case><ops:default>"++fallback++"</ops:default>"++"</ops:switch>"result=if"<math"`isPrefixOf`mathmltheninOpselsemathmlreturn$RawInline"html"result:xstransformInlines___(RawInline__:xs)=return$Str"":xstransformInlines___(Linklab(_,_):xs)=return$lab++xstransformInlines___xs=returnxstransformBlock::Block->BlocktransformBlock(RawBlock__)=NulltransformBlockx=x(!)::Nodet=>(t->Element)->[(String,String)]->t->Element(!)fattrsn=add_attrs(map(\(k,v)->Attr(unqualk)v)attrs)(fn)-- | Version of 'ppTopElement' that specifies UTF-8 encoding.ppTopElement::Element->StringppTopElement=("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"++).ppElementimageTypeOf::FilePath->MaybeStringimageTypeOfx=casedrop1(maptoLower(takeExtensionx))of"jpg"->Just"image/jpeg""jpeg"->Just"image/jpeg""jfif"->Just"image/jpeg""png"->Just"image/png""gif"->Just"image/gif""svg"->Just"image/svg+xml"_->NothingpageTemplate::StringpageTemplate=unlines["<?xml version=\"1.0\" encoding=\"UTF-8\"?>","<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">","<html xmlns=\"http://www.w3.org/1999/xhtml\">","<head>","<title>$title$</title>","$if(coverimage)$","<style type=\"text/css\">img{ max-width: 100%; }</style>","$endif$","<link href=\"stylesheet.css\" type=\"text/css\" rel=\"stylesheet\" />","</head>","<body>","$if(coverimage)$","<div id=\"cover-image\">","<img src=\"$coverimage$\" alt=\"$title$\" />","</div>","$else$","$if(titlepage)$","<h1 class=\"title\">$title$</h1>","$for(author)$","<h2 class=\"author\">$author$</h2>","$endfor$","$else$","<h1>$title$</h1>","$if(toc)$","$toc$","$endif$","$endif$","$body$","$endif$","</body>","</html>"]