git.iko.soy/typeable/pandoc
on 67282fe help sign in
7 files changed+484−123
since splitsnapshots
unifiedsplit
pandoc.cabal+3−2 src/Text/Pandoc/Free.hs+310 src/Text/Pandoc/UUID.hs+1−1 src/Text/Pandoc/Writers/Docx.hs+37−28 src/Text/Pandoc/Writers/EPUB.hs+77−61 src/Text/Pandoc/Writers/ICML.hs+11−6 src/Text/Pandoc/Writers/ODT.hs+45−25
pandoc.cabal+3−2
@@ -291,10 +291,10 @@ Library
291291 old-time,
292292 deepseq >= 1.3 && < 1.5,
293293 JuicyPixels >= 3.1.6.1 && < 3.3,
294− filemanip >= 0.3 && < 0.4,
294+ Glob >= 0.7 && < 0.8,
295295 cmark >= 0.5 && < 0.6,
296296 doctemplates >= 0.1 && < 0.2,
297− ghc-prim >= 0.2
297+ free >= 4
298298 if flag(old-locale)
299299 Build-Depends: old-locale >= 1 && < 1.1,
300300 time >= 1.2 && < 1.5
@@ -385,6 +385,7 @@ Library
385385 Text.Pandoc.SelfContained,
386386 Text.Pandoc.Process,
387387 Text.Pandoc.CSS
388+ Text.Pandoc.Free
388389 Other-Modules: Text.Pandoc.Readers.Docx.Lists,
389390 Text.Pandoc.Readers.Docx.Combine,
390391 Text.Pandoc.Readers.Docx.Parse,
src/Text/Pandoc/Free.hsnew file+310
@@ -0,0 +1,310 @@
1+{-# LANGUAGE DeriveFunctor #-}
2+
3+{-
4+Copyright (C) 2016 Jesse Rosenthal <jrosenthal@jhu.edu>
5+
6+This program is free software; you can redistribute it and/or modify
7+it under the terms of the GNU General Public License as published by
8+the Free Software Foundation; either version 2 of the License, or
9+(at your option) any later version.
10+
11+This program is distributed in the hope that it will be useful,
12+but WITHOUT ANY WARRANTY; without even the implied warranty of
13+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+GNU General Public License for more details.
15+
16+You should have received a copy of the GNU General Public License
17+along with this program; if not, write to the Free Software
18+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19+-}
20+
21+{- |
22+ Module : Text.Pandoc.Free
23+ Copyright : Copyright (C) 2016 Jesse Rosenthal
24+ License : GNU GPL, version 2 or above
25+
26+ Maintainer : Jesse Rosenthal <jrosenthal@jhu.edu>
27+ Stability : alpha
28+ Portability : portable
29+
30+Pure implementations of the IO monads used in Pandoc's readers and writers.
31+-}
32+
33+module Text.Pandoc.Free ( PandocActionF(..)
34+ , PandocAction
35+ , runIO
36+ , runTest
37+ , liftF
38+ --
39+ , lookupEnv
40+ , getCurrentTime
41+ , getPOSIXTime
42+ , getDefaultReferenceDocx
43+ , getDefaultReferenceODT
44+ , newStdGen
45+ , newUniqueHash
46+ , newUUID
47+ , readFileStrict
48+ , readFileLazy
49+ , readFileUTF8
50+ , readDataFile
51+ , fetchItem
52+ , fetchItem'
53+ , warn
54+ , fail
55+ , glob
56+ ) where
57+
58+import Prelude hiding (readFile, fail)
59+import qualified Control.Monad as M (fail)
60+import System.Random (StdGen, next)
61+import qualified System.Random as IO (newStdGen)
62+import Codec.Archive.Zip (Archive, fromArchive)
63+import Data.Unique (Unique, hashUnique, newUnique)
64+import qualified Data.Unique as IO (newUnique)
65+import qualified Text.Pandoc.Shared as IO ( fetchItem
66+ , fetchItem'
67+ , getDefaultReferenceDocx
68+ , getDefaultReferenceODT
69+ , warn
70+ , readDataFile)
71+import Text.Pandoc.MediaBag (MediaBag, lookupMedia)
72+import Data.Time.Clock.POSIX (POSIXTime, utcTimeToPOSIXSeconds)
73+import qualified Data.Time.Clock.POSIX as IO (getPOSIXTime)
74+import Text.Pandoc.Compat.Time (UTCTime)
75+import qualified Text.Pandoc.Compat.Time as IO (getCurrentTime)
76+import Text.Pandoc.MIME (MimeType, getMimeType)
77+import qualified Data.ByteString as B
78+import qualified Data.ByteString.Lazy as BL
79+import Control.Monad.Free
80+import qualified Control.Exception as E
81+import qualified System.Environment as IO (lookupEnv)
82+import Text.Pandoc.UUID
83+import qualified Text.Pandoc.UUID as IO (getRandomUUID)
84+import qualified Text.Pandoc.UTF8 as UTF8 (readFile, toString)
85+import System.FilePath.Glob (match, compile)
86+import System.FilePath ((</>))
87+import qualified System.FilePath.Glob as IO (glob)
88+import Control.Monad.State hiding (fail)
89+import Control.Monad.Reader hiding (fail)
90+import Data.Word (Word8)
91+
92+data PandocActionF nxt =
93+ LookupEnv String (Maybe String -> nxt)
94+ | GetCurrentTime (UTCTime -> nxt)
95+ | GetPOSIXTime (POSIXTime -> nxt)
96+ | GetDefaultReferenceDocx (Maybe FilePath) (Archive -> nxt)
97+ | GetDefaultReferenceODT (Maybe FilePath) (Archive -> nxt)
98+ | NewStdGen (StdGen -> nxt)
99+ | NewUniqueHash (Int -> nxt)
100+ | NewUUID (UUID -> nxt)
101+ | ReadFileStrict FilePath (B.ByteString -> nxt)
102+ | ReadFileLazy FilePath (BL.ByteString -> nxt)
103+ | ReadFileUTF8 FilePath (String -> nxt)
104+ | ReadDataFile (Maybe FilePath) FilePath (B.ByteString -> nxt)
105+ | FetchItem (Maybe String) (String)
106+ (Either E.SomeException (B.ByteString, Maybe MimeType) -> nxt)
107+ | FetchItem' MediaBag (Maybe String) (String)
108+ (Either E.SomeException (B.ByteString, Maybe MimeType) -> nxt)
109+ | Glob String ([FilePath] -> nxt)
110+ | Warn String nxt
111+ | Fail String
112+ deriving Functor
113+
114+type PandocAction = Free PandocActionF
115+
116+lookupEnv :: String -> PandocAction (Maybe String)
117+lookupEnv s = liftF $ LookupEnv s id
118+
119+getCurrentTime :: PandocAction UTCTime
120+getCurrentTime = liftF $ GetCurrentTime id
121+
122+getPOSIXTime :: PandocAction POSIXTime
123+getPOSIXTime = liftF $ GetPOSIXTime id
124+
125+getDefaultReferenceDocx :: Maybe FilePath -> PandocAction Archive
126+getDefaultReferenceDocx fp = liftF $ GetDefaultReferenceDocx fp id
127+
128+getDefaultReferenceODT :: Maybe FilePath -> PandocAction Archive
129+getDefaultReferenceODT fp = liftF $ GetDefaultReferenceODT fp id
130+
131+newStdGen :: PandocAction StdGen
132+newStdGen = liftF $ NewStdGen id
133+
134+newUniqueHash :: PandocAction Int
135+newUniqueHash = liftF $ NewUniqueHash id
136+
137+newUUID :: PandocAction UUID
138+newUUID = liftF $ NewUUID id
139+
140+readFileStrict :: FilePath -> PandocAction B.ByteString
141+readFileStrict fp = liftF $ ReadFileStrict fp id
142+
143+readFileLazy :: FilePath -> PandocAction BL.ByteString
144+readFileLazy fp = liftF $ ReadFileLazy fp id
145+
146+readFileUTF8 :: FilePath -> PandocAction String
147+readFileUTF8 fp = liftF $ ReadFileUTF8 fp id
148+
149+readDataFile :: Maybe FilePath -> FilePath -> PandocAction B.ByteString
150+readDataFile mfp fp = liftF $ ReadDataFile mfp fp id
151+
152+fetchItem :: Maybe String ->
153+ String ->
154+ PandocAction (Either E.SomeException (B.ByteString, Maybe MimeType))
155+fetchItem ms s = liftF $ FetchItem ms s id
156+
157+
158+fetchItem' :: MediaBag ->
159+ Maybe String ->
160+ String ->
161+ PandocAction (Either E.SomeException (B.ByteString, Maybe MimeType))
162+fetchItem' mb ms s = liftF $ FetchItem' mb ms s id
163+
164+warn :: String -> PandocAction ()
165+warn s = liftF $ Warn s ()
166+
167+fail :: String -> PandocAction b
168+fail s = liftF $ Fail s
169+
170+glob :: String -> PandocAction [FilePath]
171+glob s = liftF $ Glob s id
172+
173+runIO :: PandocAction nxt -> IO nxt
174+runIO (Free (LookupEnv s f)) = IO.lookupEnv s >>= runIO . f
175+runIO (Free (GetCurrentTime f)) = IO.getCurrentTime >>= runIO . f
176+runIO (Free (GetPOSIXTime f)) = IO.getPOSIXTime >>= runIO . f
177+runIO (Free (GetDefaultReferenceDocx mfp f)) =
178+ IO.getDefaultReferenceDocx mfp >>= runIO . f
179+runIO (Free (GetDefaultReferenceODT mfp f)) =
180+ IO.getDefaultReferenceODT mfp >>= runIO . f
181+runIO (Free (NewStdGen f)) = IO.newStdGen >>= runIO . f
182+runIO (Free (NewUniqueHash f)) = hashUnique <$> IO.newUnique >>= runIO . f
183+runIO (Free (NewUUID f)) = IO.getRandomUUID >>= runIO . f
184+runIO (Free (ReadFileStrict fp f)) = B.readFile fp >>= runIO . f
185+runIO (Free (ReadFileLazy fp f)) = BL.readFile fp >>= runIO . f
186+runIO (Free (ReadFileUTF8 fp f)) = UTF8.readFile fp >>= runIO . f
187+runIO (Free (ReadDataFile mfp fp f)) = IO.readDataFile mfp fp >>= runIO . f
188+runIO (Free (Fail s)) = M.fail s
189+runIO (Free (FetchItem sourceUrl nm f)) =
190+ IO.fetchItem sourceUrl nm >>= runIO . f
191+runIO (Free (FetchItem' media sourceUrl nm f)) =
192+ IO.fetchItem' media sourceUrl nm >>= runIO . f
193+runIO (Free (Warn s nxt)) = IO.warn s >> runIO nxt
194+runIO (Free (Glob s f)) = IO.glob s >>= runIO . f
195+runIO (Pure r) = return r
196+
197+data TestState = TestState { stStdGen :: StdGen
198+ , stWord8Store :: [Word8] -- should be
199+ -- inifinite,
200+ -- i.e. [1..]
201+ , stWarnings :: [String]
202+ , stUniqStore :: [Int] -- should be
203+ -- inifinite and
204+ -- contain every
205+ -- element at most
206+ -- once, e.g. [1..]
207+ }
208+
209+data TestEnv = TestEnv { envEnv :: [(String, String)]
210+ , envTime :: UTCTime
211+ , envReferenceDocx :: Archive
212+ , envReferenceODT :: Archive
213+ , envFiles :: [(FilePath, B.ByteString)]
214+ , envUserDataDir :: [(FilePath, B.ByteString)]
215+ , envCabalDataDir :: [(FilePath, B.ByteString)]
216+ , envFontFiles :: [FilePath]
217+ }
218+
219+data TestException = TestException
220+ deriving (Show)
221+
222+instance E.Exception TestException
223+
224+type Testing = ReaderT TestEnv (State TestState)
225+
226+runTest :: PandocAction nxt -> Testing nxt
227+runTest (Free (LookupEnv s f)) = do
228+ env <- asks envEnv
229+ return (lookup s env) >>= runTest . f
230+runTest (Free (GetCurrentTime f)) =
231+ asks envTime >>= runTest . f
232+runTest (Free (GetPOSIXTime f)) =
233+ (utcTimeToPOSIXSeconds <$> asks envTime) >>= runTest . f
234+runTest (Free (GetDefaultReferenceDocx _ f)) =
235+ asks envReferenceDocx >>= runTest . f
236+runTest (Free (GetDefaultReferenceODT _ f)) =
237+ asks envReferenceODT >>= runTest . f
238+runTest (Free (NewStdGen f)) = do
239+ g <- gets stStdGen
240+ let (_, nxtGen) = next g
241+ modify $ \st -> st { stStdGen = nxtGen }
242+ return g >>= runTest . f
243+runTest (Free (NewUniqueHash f)) = do
244+ uniqs <- gets stUniqStore
245+ case uniqs of
246+ u : us -> do
247+ modify $ \st -> st { stUniqStore = us }
248+ return u >>= runTest . f
249+ _ -> M.fail "uniq store ran out of elements"
250+runTest (Free (NewUUID f)) = do
251+ word8s <- gets stWord8Store
252+ case word8s of
253+ -- note we use f' because f is a param of the function
254+ a:b:c:d:e:f':g:h:i:j:k:l:m:n:o:p:remaining -> do
255+ modify $ \st -> st { stWord8Store = remaining }
256+ return (UUID a b c d e f' g h i j k l m n o p) >>= runTest . f
257+ _ -> M.fail "word8 supply was not infinite"
258+runTest (Free (ReadFileStrict fp f)) = do
259+ fps <- asks envFiles
260+ case lookup fp fps of
261+ Just bs -> return bs >>= runTest . f
262+ Nothing -> error "openFile: does not exist"
263+runTest (Free (ReadFileLazy fp f)) = do
264+ fps <- asks envFiles
265+ case lookup fp fps of
266+ Just bs -> return (BL.fromStrict bs) >>= runTest . f
267+ Nothing -> error "openFile: does not exist"
268+runTest (Free (ReadFileUTF8 fp f)) = do
269+ fps <- asks envFiles
270+ case lookup fp fps of
271+ Just bs -> return (UTF8.toString bs) >>= runTest . f
272+ Nothing -> error "openFile: does not exist"
273+-- A few different cases of readDataFile to reimplement, for when
274+-- there is no filepath and it falls through to readDefaultDataFile
275+runTest (Free (ReadDataFile Nothing "reference.docx" f)) = do
276+ (B.concat . BL.toChunks . fromArchive) <$>
277+ (runTest $ getDefaultReferenceDocx Nothing) >>=
278+ runTest . f
279+runTest (Free (ReadDataFile Nothing "reference.odt" f)) = do
280+ (B.concat . BL.toChunks . fromArchive) <$>
281+ (runTest $ getDefaultReferenceODT Nothing) >>=
282+ runTest . f
283+runTest (Free (ReadDataFile Nothing fname f)) = do
284+ let fname' = if fname == "MANUAL.txt" then fname else "data" </> fname
285+ runTest (readFileStrict fname') >>= runTest . f
286+runTest (Free (ReadDataFile (Just userDir) fname f)) = do
287+ userDirFiles <- asks envUserDataDir
288+ case lookup (userDir </> fname) userDirFiles of
289+ Just bs -> return bs >>= runTest . f
290+ Nothing -> runTest (readDataFile Nothing fname) >>= runTest . f
291+runTest (Free (Fail s)) = M.fail s
292+runTest (Free (FetchItem _ fp f)) = do
293+ fps <- asks envFiles
294+ case lookup fp fps of
295+ Just bs -> return (Right (bs, getMimeType fp)) >>= runTest . f
296+ Nothing -> return (Left $ E.toException TestException) >>= runTest . f
297+runTest (Free (FetchItem' media sourceUrl nm f)) = do
298+ case lookupMedia nm media of
299+ Nothing -> runTest (fetchItem sourceUrl nm) >>= runTest . f
300+ Just (mime, bs) -> return (Right (B.concat $ BL.toChunks bs, Just mime)) >>= runTest . f
301+runTest (Free (Warn s nxt)) = do
302+ modify $ \st -> st { stWarnings = s : stWarnings st }
303+ runTest nxt
304+runTest (Free (Glob s f)) = do
305+ fontFiles <- asks envFontFiles
306+ return (filter (match (compile s)) fontFiles) >>= runTest . f
307+runTest (Pure r) = return r
308+
309+
310+
src/Text/Pandoc/UUID.hs+1−1
@@ -29,7 +29,7 @@ UUID generation using Version 4 (random method) described
2929in RFC4122. See http://tools.ietf.org/html/rfc4122
3030-}
3131
32−module Text.Pandoc.UUID ( UUID, getRandomUUID ) where
32+module Text.Pandoc.UUID ( UUID(..), getRandomUUID ) where
3333
3434import Text.Printf ( printf )
3535import System.Random ( randomIO )
src/Text/Pandoc/Writers/Docx.hs+37−28
@@ -1,4 +1,4 @@
1−{-# LANGUAGE ScopedTypeVariables, PatternGuards, ViewPatterns #-}
1+{-# LANGUAGE ScopedTypeVariables, PatternGuards, ViewPatterns, DeriveFunctor #-}
22{-
33Copyright (C) 2012-2015 John MacFarlane <jgm@berkeley.edu>
44
@@ -28,7 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2828
2929Conversion of 'Pandoc' documents to docx.
3030-}
31−module Text.Pandoc.Writers.Docx ( writeDocx ) where
31+module Text.Pandoc.Writers.Docx ( writeDocx, writeDocxPure ) where
3232import Data.List ( intercalate, isPrefixOf, isSuffixOf )
3333import qualified Data.ByteString as B
3434import qualified Data.ByteString.Lazy as BL
@@ -38,7 +38,6 @@ import qualified Data.Set as Set
3838import qualified Text.Pandoc.UTF8 as UTF8
3939import Codec.Archive.Zip
4040import Data.Time.Clock.POSIX
41−import System.Environment
4241import Text.Pandoc.Compat.Time
4342import Text.Pandoc.Definition
4443import Text.Pandoc.Generic
@@ -57,8 +56,7 @@ import Text.Pandoc.Readers.Docx.Util (elemName)
5756import Control.Monad.Reader
5857import Control.Monad.State
5958import Text.Highlighting.Kate
60−import Data.Unique (hashUnique, newUnique)
61−import System.Random (randomRIO)
59+import System.Random (randomR)
6260import Text.Printf (printf)
6361import qualified Control.Exception as E
6462import Data.Monoid ((<>))
@@ -67,6 +65,8 @@ import Text.Pandoc.MIME (MimeType, getMimeType, getMimeTypeDef,
6765import Control.Applicative ((<|>))
6866import Data.Maybe (fromMaybe, mapMaybe, maybeToList, isNothing)
6967import Data.Char (ord, isSpace, toLower)
68+import Text.Pandoc.Free (PandocAction, runIO)
69+import qualified Text.Pandoc.Free as P
7070
7171data ListMarker = NoMarker
7272 | BulletMarker
@@ -146,7 +146,7 @@ defaultWriterState = WriterState{
146146 , stDynamicTextProps = []
147147 }
148148
149−type WS = ReaderT WriterEnv (StateT WriterState IO)
149+type WS = ReaderT WriterEnv (StateT WriterState (PandocAction))
150150
151151mknode :: Node t => String -> [(String,String)] -> t -> Element
152152mknode s attrs =
@@ -213,19 +213,27 @@ metaValueToInlines (MetaBlocks bs) = query return bs
213213metaValueToInlines (MetaBool b) = [Str $ show b]
214214metaValueToInlines _ = []
215215
216−-- | Produce an Docx file from a Pandoc document.
216+
217+
217218writeDocx :: WriterOptions -- ^ Writer options
218219 -> Pandoc -- ^ Document to convert
219220 -> IO BL.ByteString
220−writeDocx opts doc@(Pandoc meta _) = do
221+writeDocx opts doc = runIO $ writeDocxPure opts doc
222+
223+
224+-- | Produce an Docx file from a Pandoc document.
225+writeDocxPure :: WriterOptions -- ^ Writer options
226+ -> Pandoc -- ^ Document to convert
227+ -> PandocAction BL.ByteString
228+writeDocxPure opts doc@(Pandoc meta _) = do
221229 let datadir = writerUserDataDir opts
222230 let doc' = walk fixDisplayMath $ doc
223− username <- lookup "USERNAME" <$> getEnvironment
224− utctime <- getCurrentTime
225− distArchive <- getDefaultReferenceDocx datadir
231+ username <- P.lookupEnv "USERNAME"
232+ utctime <- P.getCurrentTime
233+ distArchive <- P.getDefaultReferenceDocx datadir
226234 refArchive <- case writerReferenceDocx opts of
227− Just f -> liftM (toArchive . toLazy) $ B.readFile f
228− Nothing -> getDefaultReferenceDocx datadir
235+ Just f -> liftM (toArchive . toLazy) $ P.readFileStrict f
236+ Nothing -> P.getDefaultReferenceDocx datadir
229237
230238 parsedDoc <- parseXml refArchive distArchive "word/document.xml"
231239 let wname f qn = qPrefix qn == Just "w" && f (qName qn)
@@ -603,7 +611,7 @@ styleToOpenXml sm style =
603611 $ backgroundColor style )
604612 ]
605613
606−copyChildren :: Archive -> Archive -> String -> Integer -> [String] -> IO Entry
614+copyChildren :: Archive -> Archive -> String -> Integer -> [String] -> PandocAction Entry
607615copyChildren refArchive distArchive path timestamp elNames = do
608616 ref <- parseXml refArchive distArchive path
609617 dist <- parseXml distArchive distArchive path
@@ -622,7 +630,7 @@ copyChildren refArchive distArchive path timestamp elNames = do
622630baseListId :: Int
623631baseListId = 1000
624632
625−mkNumbering :: [ListMarker] -> IO [Element]
633+mkNumbering :: [ListMarker] -> PandocAction [Element]
626634mkNumbering lists = do
627635 elts <- mapM mkAbstractNum (ordNub lists)
628636 return $ elts ++ zipWith mkNum lists [baseListId..(baseListId + length lists - 1)]
@@ -638,9 +646,10 @@ mkNum marker numid =
638646 map (\lvl -> mknode "w:lvlOverride" [("w:ilvl",show (lvl :: Int))]
639647 $ mknode "w:startOverride" [("w:val",show start)] ()) [0..6]
640648
641−mkAbstractNum :: ListMarker -> IO Element
649+mkAbstractNum :: ListMarker -> PandocAction Element
642650mkAbstractNum marker = do
643− nsid <- randomRIO (0x10000000 :: Integer, 0xFFFFFFFF :: Integer)
651+ gen <- P.newStdGen
652+ let (nsid, _) = randomR (0x10000000 :: Integer, 0xFFFFFFFF :: Integer) gen
644653 return $ mknode "w:abstractNum" [("w:abstractNumId",listMarkerToId marker)]
645654 $ mknode "w:nsid" [("w:val", printf "%8x" nsid)] ()
646655 : mknode "w:multiLevelType" [("w:val","multilevel")] ()
@@ -695,6 +704,7 @@ mkLvl marker lvl =
695704getNumId :: WS Int
696705getNumId = (((baseListId - 1) +) . length) `fmap` gets stLists
697706
707+
698708makeTOC :: WriterOptions -> WS [Element]
699709makeTOC opts | writerTableOfContents opts = do
700710 let depth = "1-"++(show (writerTOCDepth opts))
@@ -781,10 +791,10 @@ rStyleM styleName = do
781791 let sty' = getStyleId styleName $ sCharStyleMap styleMaps
782792 return $ mknode "w:rStyle" [("w:val",sty')] ()
783793
784−getUniqueId :: MonadIO m => m String
794+getUniqueId :: PandocAction String
785795-- the + 20 is to ensure that there are no clashes with the rIds
786796-- already in word/document.xml.rel
787−getUniqueId = liftIO $ (show . (+ 20) . hashUnique) `fmap` newUnique
797+getUniqueId = (show . (+ 20)) <$> P.newUniqueHash
788798
789799-- | Key for specifying user-defined docx styles.
790800dynamicStyleKey :: String
@@ -825,7 +835,7 @@ blockToOpenXML' opts (Header lev (ident,_,_) lst) = do
825835 then uniqueIdent lst usedIdents
826836 else ident
827837 modify $ \s -> s{ stSectionIds = Set.insert bookmarkName $ stSectionIds s }
828− id' <- getUniqueId
838+ id' <- (lift . lift) getUniqueId
829839 let bookmarkStart = mknode "w:bookmarkStart" [("w:id", id')
830840 ,("w:name",bookmarkName)] ()
831841 let bookmarkEnd = mknode "w:bookmarkEnd" [("w:id", id')] ()
@@ -1133,7 +1143,7 @@ inlineToOpenXML' opts (Code attrs str) = do
11331143 else unhighlighted
11341144inlineToOpenXML' opts (Note bs) = do
11351145 notes <- gets stFootnotes
1136− notenum <- getUniqueId
1146+ notenum <- (lift . lift) getUniqueId
11371147 footnoteStyle <- rStyleM "Footnote Reference"
11381148 let notemarker = mknode "w:r" []
11391149 [ mknode "w:rPr" [] footnoteStyle
@@ -1164,7 +1174,7 @@ inlineToOpenXML' opts (Link _ txt (src,_)) = do
11641174 id' <- case M.lookup src extlinks of
11651175 Just i -> return i
11661176 Nothing -> do
1167− i <- ("rId"++) `fmap` getUniqueId
1177+ i <- ("rId"++) `fmap` ((lift . lift) getUniqueId)
11681178 modify $ \st -> st{ stExternalLinks =
11691179 M.insert src i extlinks }
11701180 return i
@@ -1176,15 +1186,14 @@ inlineToOpenXML' opts (Image attr alt (src, title)) = do
11761186 case M.lookup src imgs of
11771187 Just (_,_,_,elt,_) -> return [elt]
11781188 Nothing -> do
1179− res <- liftIO $
1180− fetchItem' (writerMediaBag opts) (writerSourceURL opts) src
1189+ res <- (lift . lift) $ P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) src
11811190 case res of
11821191 Left (_ :: E.SomeException) -> do
1183− liftIO $ warn $ "Could not find image `" ++ src ++ "', skipping..."
1192+ (lift . lift) $ P.warn ("Could not find image `" ++ src ++ "', skipping...")
11841193 -- emit alt text
11851194 inlinesToOpenXML opts alt
11861195 Right (img, mt) -> do
1187− ident <- ("rId"++) `fmap` getUniqueId
1196+ ident <- ("rId"++) `fmap` ((lift . lift) getUniqueId)
11881197 let (xpt,ypt) = desiredSizeInPoints opts attr
11891198 (either (const def) id (imageSize img))
11901199 -- 12700 emu = 1 pt
@@ -1262,13 +1271,13 @@ defaultFootnotes = [ mknode "w:footnote"
12621271 [ mknode "w:r" [] $
12631272 [ mknode "w:continuationSeparator" [] ()]]]]
12641273
1265−parseXml :: Archive -> Archive -> String -> IO Element
1274+parseXml :: Archive -> Archive -> String -> PandocAction Element
12661275parseXml refArchive distArchive relpath =
12671276 case findEntryByPath relpath refArchive `mplus`
12681277 findEntryByPath relpath distArchive of
12691278 Nothing -> fail $ relpath ++ " missing in reference docx"
12701279 Just e -> case parseXMLDoc . UTF8.toStringLazy . fromEntry $ e of
1271− Nothing -> fail $ relpath ++ " corrupt in reference docx"
1280+ Nothing -> P.fail $ relpath ++ " corrupt in reference docx"
12721281 Just d -> return d
12731282
12741283-- | Scales the image to fit the page
src/Text/Pandoc/Writers/EPUB.hs+77−61
@@ -28,26 +28,22 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2828
2929Conversion of 'Pandoc' documents to EPUB.
3030-}
31−module Text.Pandoc.Writers.EPUB ( writeEPUB ) where
32−import Data.IORef ( IORef, newIORef, readIORef, modifyIORef )
31+module Text.Pandoc.Writers.EPUB ( writeEPUB, writeEPUBPure ) where
3332import qualified Data.Map as M
3433import qualified Data.Set as Set
3534import Data.Maybe ( fromMaybe, catMaybes )
3635import Data.List ( isPrefixOf, isInfixOf, intercalate )
37−import System.Environment ( getEnv )
3836import Text.Printf (printf)
3937import System.FilePath ( takeExtension, takeFileName )
40−import System.FilePath.Glob ( namesMatching )
4138import Network.HTTP ( urlEncode )
4239import qualified Data.ByteString.Lazy as B
4340import qualified Data.ByteString.Lazy.Char8 as B8
4441import qualified Text.Pandoc.UTF8 as UTF8
4542import Codec.Archive.Zip ( emptyArchive, addEntryToArchive, eRelativePath, fromEntry , Entry, toEntry, fromArchive)
46−import Data.Time.Clock.POSIX ( getPOSIXTime )
4743import Text.Pandoc.Compat.Time
4844import Text.Pandoc.Shared ( renderTags', safeRead, uniqueIdent, trim
49− , normalizeDate, readDataFile, stringify, warn
50− , hierarchicalize, fetchItem' )
45+ , normalizeDate, stringify
46+ , hierarchicalize )
5147import qualified Text.Pandoc.Shared as S (Element(..))
5248import Text.Pandoc.Builder (fromList, setMeta)
5349import Text.Pandoc.Options ( WriterOptions(..)
@@ -57,18 +53,18 @@ import Text.Pandoc.Options ( WriterOptions(..)
5753 , ObfuscationMethod(NoObfuscation) )
5854import Text.Pandoc.Definition
5955import Text.Pandoc.Walk (walk, walkM, query)
60−import Control.Monad.State (modify, get, State, put, evalState)
61−import Control.Monad (mplus, liftM, when)
56+import Control.Monad.State (modify, get, gets, State, StateT, put, evalState, evalStateT, lift)
57+import Control.Monad (mplus, when)
6258import Text.XML.Light ( unode, Element(..), unqual, Attr(..), add_attrs
6359 , strContent, lookupAttr, Node(..), QName(..), parseXML
6460 , onlyElems, node, ppElement)
65−import Text.Pandoc.UUID (getRandomUUID)
6661import Text.Pandoc.Writers.HTML ( writeHtml )
6762import Data.Char ( toLower, isDigit, isAlphaNum )
6863import Text.Pandoc.MIME (MimeType, getMimeType, extensionFromMimeType)
69−import qualified Control.Exception as E
7064import Text.Blaze.Html.Renderer.Utf8 (renderHtml)
7165import Text.HTML.TagSoup (Tag(TagOpen), fromAttrib, parseTags)
66+import Text.Pandoc.Free (PandocAction, runIO)
67+import qualified Text.Pandoc.Free as P
7268
7369-- A Chapter includes a list of blocks and maybe a section
7470-- number offset. Note, some chapters are unnumbered. The section
@@ -76,6 +72,11 @@ import Text.HTML.TagSoup (Tag(TagOpen), fromAttrib, parseTags)
7672-- in filenames, chapter0003.xhtml.
7773data Chapter = Chapter (Maybe [Int]) [Block]
7874
75+data EPUBState = EPUBState { stMediaPaths :: [(FilePath, (FilePath, Maybe Entry))]
76+ }
77+
78+type E = StateT EPUBState PandocAction
79+
7980data EPUBMetadata = EPUBMetadata{
8081 epubIdentifier :: [Identifier]
8182 , epubTitle :: [Title]
@@ -143,7 +144,7 @@ removeNote :: Inline -> Inline
143144removeNote (Note _) = Str ""
144145removeNote x = x
145146
146−getEPUBMetadata :: WriterOptions -> Meta -> IO EPUBMetadata
147+getEPUBMetadata :: WriterOptions -> Meta -> E EPUBMetadata
147148getEPUBMetadata opts meta = do
148149 let md = metadataFromMeta opts meta
149150 let elts = onlyElems $ parseXML $ writerEpubMetadata opts
@@ -151,7 +152,7 @@ getEPUBMetadata opts meta = do
151152 let addIdentifier m =
152153 if null (epubIdentifier m)
153154 then do
154− randomId <- fmap show getRandomUUID
155+ randomId <- fmap show (lift P.newUUID)
155156 return $ m{ epubIdentifier = [Identifier randomId Nothing] }
156157 else return m
157158 let addLanguage m =
@@ -159,16 +160,19 @@ getEPUBMetadata opts meta = do
159160 then case lookup "lang" (writerVariables opts) of
160161 Just x -> return m{ epubLanguage = x }
161162 Nothing -> do
162− localeLang <- E.catch (liftM
163− (map (\c -> if c == '_' then '-' else c) .
164− takeWhile (/='.')) $ getEnv "LANG")
165− (\e -> let _ = (e :: E.SomeException) in return "en-US")
163+ mLang <- lift $ P.lookupEnv "LANG"
164+ let localeLang =
165+ case mLang of
166+ Just lang ->
167+ map (\c -> if c == '_' then '-' else c) $
168+ takeWhile (/='.') lang
169+ Nothing -> "en-US"
166170 return m{ epubLanguage = localeLang }
167171 else return m
168172 let fixDate m =
169173 if null (epubDate m)
170174 then do
171− currentTime <- getCurrentTime
175+ currentTime <- lift P.getCurrentTime
172176 return $ m{ epubDate = [ Date{
173177 dateText = showDateTimeISO8601 currentTime
174178 , dateEvent = Nothing } ] }
@@ -333,10 +337,24 @@ metadataFromMeta opts meta = EPUBMetadata{
333337writeEPUB :: WriterOptions -- ^ Writer options
334338 -> Pandoc -- ^ Document to convert
335339 -> IO B.ByteString
336−writeEPUB opts doc@(Pandoc meta _) = do
340+writeEPUB opts doc = runIO $ writeEPUBPure opts doc
341+
342+writeEPUBPure :: WriterOptions -- ^ Writer options
343+ -> Pandoc -- ^ Document to convert
344+ -> PandocAction B.ByteString
345+writeEPUBPure opts doc =
346+ let initState = EPUBState { stMediaPaths = []
347+ }
348+ in
349+ evalStateT (pandocToEPUB opts doc) initState
350+
351+pandocToEPUB :: WriterOptions
352+ -> Pandoc
353+ -> E B.ByteString
354+pandocToEPUB opts doc@(Pandoc meta _) = do
337355 let version = fromMaybe EPUB2 (writerEpubVersion opts)
338356 let epub3 = version == EPUB3
339− epochtime <- floor `fmap` getPOSIXTime
357+ epochtime <- floor <$> lift P.getPOSIXTime
340358 let mkEntry path content = toEntry path epochtime content
341359 let vars = ("epub3", if epub3 then "true" else "false")
342360 : ("css", "stylesheet.css")
@@ -362,7 +380,7 @@ writeEPUB opts doc@(Pandoc meta _) = do
362380 let cpContent = renderHtml $ writeHtml
363381 opts'{ writerVariables = ("coverpage","true"):vars }
364382 (Pandoc meta [RawBlock (Format "html") $ "<div id=\"cover-image\">\n<img src=\"" ++ coverImage ++ "\" alt=\"cover image\" />\n</div>"])
365− imgContent <- B.readFile img
383+ imgContent <- lift $ P.readFileLazy img
366384 return ( [mkEntry "cover.xhtml" cpContent]
367385 , [mkEntry coverImage imgContent] )
368386
@@ -373,18 +391,17 @@ writeEPUB opts doc@(Pandoc meta _) = do
373391 let tpEntry = mkEntry "title_page.xhtml" tpContent
374392
375393 -- handle pictures
376− mediaRef <- newIORef []
377− Pandoc _ blocks <- walkM (transformInline opts' mediaRef) doc >>=
378− walkM (transformBlock opts' mediaRef)
379− picEntries <- (catMaybes . map (snd . snd)) <$> readIORef mediaRef
380−
394+ -- mediaRef <- P.newIORef []
395+ Pandoc _ blocks <- walkM (transformInline opts') doc >>=
396+ walkM (transformBlock opts')
397+ picEntries <- (catMaybes . map (snd . snd)) <$> (gets stMediaPaths)
381398 -- handle fonts
382399 let matchingGlob f = do
383− xs <- namesMatching f
400+ xs <- lift $ P.glob f
384401 when (null xs) $
385− warn $ f ++ " did not match any font files."
402+ lift $ P.warn $ f ++ " did not match any font files."
386403 return xs
387− let mkFontEntry f = mkEntry (takeFileName f) `fmap` B.readFile f
404+ let mkFontEntry f = mkEntry (takeFileName f) `fmap` (lift $ P.readFileLazy f)
388405 fontFiles <- concat <$> mapM matchingGlob (writerEpubFonts opts')
389406 fontEntries <- mapM mkFontEntry fontFiles
390407
@@ -521,7 +538,7 @@ writeEPUB opts doc@(Pandoc meta _) = do
521538 let uuid = case epubIdentifier metadata of
522539 (x:_) -> identifierText x -- use first identifier as UUID
523540 [] -> error "epubIdentifier is null" -- shouldn't happen
524− currentTime <- getCurrentTime
541+ currentTime <- lift $ P.getCurrentTime
525542 let contentsData = UTF8.fromStringLazy $ ppTopElement $
526543 unode "package" ! [("version", case version of
527544 EPUB2 -> "2.0"
@@ -693,10 +710,10 @@ writeEPUB opts doc@(Pandoc meta _) = do
693710
694711 -- stylesheet
695712 stylesheet <- case epubStylesheet metadata of
696− Just (StylesheetPath fp) -> UTF8.readFile fp
713+ Just (StylesheetPath fp) -> lift $ P.readFileUTF8 fp
697714 Just (StylesheetContents s) -> return s
698715 Nothing -> UTF8.toString `fmap`
699− readDataFile (writerUserDataDir opts) "epub.css"
716+ (lift $ P.readDataFile (writerUserDataDir opts) "epub.css")
700717 let stylesheetEntry = mkEntry "stylesheet.css" $ UTF8.fromStringLazy stylesheet
701718
702719 -- construct archive
@@ -813,78 +830,77 @@ showDateTimeISO8601 :: UTCTime -> String
813830showDateTimeISO8601 = formatTime defaultTimeLocale "%FT%TZ"
814831
815832transformTag :: WriterOptions
816− -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media
833+ -- -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media
817834 -> Tag String
818− -> IO (Tag String)
819−transformTag opts mediaRef tag@(TagOpen name attr)
835+ -> E (Tag String)
836+transformTag opts tag@(TagOpen name attr)
820837 | name `elem` ["video", "source", "img", "audio"] &&
821838 lookup "data-external" attr == Nothing = do
822839 let src = fromAttrib "src" tag
823840 let poster = fromAttrib "poster" tag
824− newsrc <- modifyMediaRef opts mediaRef src
825− newposter <- modifyMediaRef opts mediaRef poster
841+ newsrc <- modifyMediaRef opts src
842+ newposter <- modifyMediaRef opts poster
826843 let attr' = filter (\(x,_) -> x /= "src" && x /= "poster") attr ++
827844 [("src", newsrc) | not (null newsrc)] ++
828845 [("poster", newposter) | not (null newposter)]
829846 return $ TagOpen name attr'
830−transformTag _ _ tag = return tag
847+transformTag _ tag = return tag
831848
832849modifyMediaRef :: WriterOptions
833− -> IORef [(FilePath, (FilePath, Maybe Entry))]
834850 -> FilePath
835− -> IO FilePath
836−modifyMediaRef _ _ "" = return ""
837−modifyMediaRef opts mediaRef oldsrc = do
838− media <- readIORef mediaRef
851+ -> E FilePath
852+modifyMediaRef _ "" = return ""
853+modifyMediaRef opts oldsrc = do
854+ media <- gets stMediaPaths
839855 case lookup oldsrc media of
840856 Just (n,_) -> return n
841857 Nothing -> do
842− res <- fetchItem' (writerMediaBag opts)
858+ res <- lift $ P.fetchItem' (writerMediaBag opts)
843859 (writerSourceURL opts) oldsrc
844860 (new, mbEntry) <-
845861 case res of
846862 Left _ -> do
847− warn $ "Could not find media `" ++ oldsrc ++ "', skipping..."
863+ lift $ P.warn $ "Could not find media `" ++ oldsrc ++ "', skipping..."
848864 return (oldsrc, Nothing)
849865 Right (img,mbMime) -> do
850866 let new = "media/file" ++ show (length media) ++
851867 fromMaybe (takeExtension (takeWhile (/='?') oldsrc))
852868 (('.':) <$> (mbMime >>= extensionFromMimeType))
853− epochtime <- floor `fmap` getPOSIXTime
869+ epochtime <- floor `fmap` lift P.getPOSIXTime
854870 let entry = toEntry new epochtime $ B.fromChunks . (:[]) $ img
855871 return (new, Just entry)
856− modifyIORef mediaRef ( (oldsrc, (new, mbEntry)): )
872+ modify $ \st -> st{ stMediaPaths = (oldsrc, (new, mbEntry)):media}
857873 return new
858874
859875transformBlock :: WriterOptions
860− -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media
876+ -- -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath, entry) media
861877 -> Block
862− -> IO Block
863−transformBlock opts mediaRef (RawBlock fmt raw)
878+ -> E Block
879+transformBlock opts (RawBlock fmt raw)
864880 | fmt == Format "html" = do
865881 let tags = parseTags raw
866− tags' <- mapM (transformTag opts mediaRef) tags
882+ tags' <- mapM (transformTag opts) tags
867883 return $ RawBlock fmt (renderTags' tags')
868−transformBlock _ _ b = return b
884+transformBlock _ b = return b
869885
870886transformInline :: WriterOptions
871− -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath) media
887+ -- -> IORef [(FilePath, (FilePath, Maybe Entry))] -- ^ (oldpath, newpath) media
872888 -> Inline
873− -> IO Inline
874−transformInline opts mediaRef (Image attr lab (src,tit)) = do
875− newsrc <- modifyMediaRef opts mediaRef src
889+ -> E Inline
890+transformInline opts (Image attr lab (src,tit)) = do
891+ newsrc <- modifyMediaRef opts src
876892 return $ Image attr lab (newsrc, tit)
877−transformInline opts mediaRef (x@(Math t m))
893+transformInline opts (x@(Math t m))
878894 | WebTeX url <- writerHTMLMathMethod opts = do
879− newsrc <- modifyMediaRef opts mediaRef (url ++ urlEncode m)
895+ newsrc <- modifyMediaRef opts (url ++ urlEncode m)
880896 let mathclass = if t == DisplayMath then "display" else "inline"
881897 return $ Span ("",["math",mathclass],[]) [Image nullAttr [x] (newsrc, "")]
882−transformInline opts mediaRef (RawInline fmt raw)
898+transformInline opts (RawInline fmt raw)
883899 | fmt == Format "html" = do
884900 let tags = parseTags raw
885− tags' <- mapM (transformTag opts mediaRef) tags
901+ tags' <- mapM (transformTag opts) tags
886902 return $ RawInline fmt (renderTags' tags')
887−transformInline _ _ x = return x
903+transformInline _ x = return x
888904
889905(!) :: (t -> Element) -> [(String, String)] -> t -> Element
890906(!) f attrs n = add_attrs (map (\(k,v) -> Attr (unqual k) v) attrs) (f n)
src/Text/Pandoc/Writers/ICML.hs+11−6
@@ -18,7 +18,7 @@ import Text.Pandoc.Definition
1818import Text.Pandoc.XML
1919import Text.Pandoc.Readers.TeXMath (texMathToInlines)
2020import Text.Pandoc.Writers.Shared
21−import Text.Pandoc.Shared (linesToPara, splitBy, fetchItem, warn)
21+import Text.Pandoc.Shared (linesToPara, splitBy, warn)
2222import Text.Pandoc.Options
2323import Text.Pandoc.Templates (renderTemplate')
2424import Text.Pandoc.Pretty
@@ -28,6 +28,8 @@ import Data.Text as Text (breakOnAll, pack)
2828import Control.Monad.State
2929import Network.URI (isURI)
3030import qualified Data.Set as Set
31+import Text.Pandoc.Free (runIO, PandocAction)
32+import qualified Text.Pandoc.Free as P
3133
3234type Style = [String]
3335type Hyperlink = [(Int, String)]
@@ -40,7 +42,7 @@ data WriterState = WriterState{
4042 , maxListDepth :: Int
4143 }
4244
43−type WS a = StateT WriterState IO a
45+type WS a = StateT WriterState PandocAction a
4446
4547defaultWriterState :: WriterState
4648defaultWriterState = WriterState{
@@ -121,10 +123,13 @@ subListParName = "subParagraph"
121123footnoteName = "Footnote"
122124citeName = "Cite"
123125
124−
125126-- | Convert Pandoc document to string in ICML format.
126127writeICML :: WriterOptions -> Pandoc -> IO String
127−writeICML opts (Pandoc meta blocks) = do
128+writeICML opts doc = runIO $ writeICMLPure opts doc
129+
130+-- | Convert Pandoc document to string in ICML format.
131+writeICMLPure :: WriterOptions -> Pandoc -> PandocAction String
132+writeICMLPure opts (Pandoc meta blocks) = do
128133 let colwidth = if writerWrapText opts == WrapAuto
129134 then Just $ writerColumns opts
130135 else Nothing
@@ -531,10 +536,10 @@ styleToStrAttr style =
531536-- | Assemble an ICML Image.
532537imageICML :: WriterOptions -> Style -> Attr -> Target -> WS Doc
533538imageICML opts style attr (src, _) = do
534− res <- liftIO $ fetchItem (writerSourceURL opts) src
539+ res <- lift $ P.fetchItem (writerSourceURL opts) src
535540 imgS <- case res of
536541 Left (_) -> do
537− liftIO $ warn $ "Could not find image `" ++ src ++ "', skipping..."
542+ lift $ P.warn $ "Could not find image `" ++ src ++ "', skipping..."
538543 return def
539544 Right (img, _) -> do
540545 case imageSize img of
src/Text/Pandoc/Writers/ODT.hs+45−25
@@ -28,8 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2828
2929Conversion of 'Pandoc' documents to ODT.
3030-}
31−module Text.Pandoc.Writers.ODT ( writeODT ) where
32−import Data.IORef
31+module Text.Pandoc.Writers.ODT ( writeODTPure, writeODT ) where
3332import Data.List ( isPrefixOf )
3433import Data.Maybe ( fromMaybe )
3534import Text.XML.Light.Output
@@ -38,8 +37,7 @@ import qualified Data.ByteString.Lazy as B
3837import Text.Pandoc.UTF8 ( fromStringLazy )
3938import Codec.Archive.Zip
4039import Text.Pandoc.Options ( WriterOptions(..), WrapOption(..) )
41−import Text.Pandoc.Shared ( stringify, fetchItem', warn,
42− getDefaultReferenceODT )
40+import Text.Pandoc.Shared ( stringify )
4341import Text.Pandoc.ImageSize
4442import Text.Pandoc.MIME ( getMimeType, extensionFromMimeType )
4543import Text.Pandoc.Definition
@@ -47,31 +45,53 @@ import Text.Pandoc.Walk
4745import Text.Pandoc.Writers.Shared ( fixDisplayMath )
4846import Text.Pandoc.Writers.OpenDocument ( writeOpenDocument )
4947import Control.Monad (liftM)
48+import Control.Monad.State
5049import Text.Pandoc.XML
5150import Text.Pandoc.Pretty
5251import qualified Control.Exception as E
53−import Data.Time.Clock.POSIX ( getPOSIXTime )
5452import System.FilePath ( takeExtension, takeDirectory, (<.>))
53+import Text.Pandoc.Free ( PandocAction, runIO )
54+import qualified Text.Pandoc.Free as P
55+
56+data ODTState = ODTState { stEntries :: [Entry]
57+ }
58+
59+type O = StateT ODTState PandocAction
5560
5661-- | Produce an ODT file from a Pandoc document.
5762writeODT :: WriterOptions -- ^ Writer options
5863 -> Pandoc -- ^ Document to convert
5964 -> IO B.ByteString
60−writeODT opts doc@(Pandoc meta _) = do
65+writeODT opts doc = runIO $ writeODTPure opts doc
66+
67+writeODTPure :: WriterOptions
68+ -> Pandoc
69+ -> PandocAction B.ByteString
70+writeODTPure opts doc =
71+ let initState = ODTState{ stEntries = []
72+ }
73+ in
74+ evalStateT (pandocToODT opts doc) initState
75+
76+-- | Produce an ODT file from a Pandoc document.
77+pandocToODT :: WriterOptions -- ^ Writer options
78+ -> Pandoc -- ^ Document to convert
79+ -> O B.ByteString
80+pandocToODT opts doc@(Pandoc meta _) = do
6181 let datadir = writerUserDataDir opts
6282 let title = docTitle meta
6383 refArchive <-
6484 case writerReferenceODT opts of
65− Just f -> liftM toArchive $ B.readFile f
66− Nothing -> getDefaultReferenceODT datadir
85+ Just f -> liftM toArchive $ lift $ P.readFileLazy f
86+ Nothing -> lift $ P.getDefaultReferenceODT datadir
6787 -- handle formulas and pictures
68− picEntriesRef <- newIORef ([] :: [Entry])
69− doc' <- walkM (transformPicMath opts picEntriesRef) $ walk fixDisplayMath doc
88+ -- picEntriesRef <- P.newIORef ([] :: [Entry])
89+ doc' <- walkM (transformPicMath opts) $ walk fixDisplayMath doc
7090 let newContents = writeOpenDocument opts{writerWrapText = WrapNone} doc'
71− epochtime <- floor `fmap` getPOSIXTime
91+ epochtime <- floor `fmap` (lift P.getPOSIXTime)
7292 let contentEntry = toEntry "content.xml" epochtime
7393 $ fromStringLazy newContents
74− picEntries <- readIORef picEntriesRef
94+ picEntries <- gets stEntries
7595 let archive = foldr addEntryToArchive refArchive
7696 $ contentEntry : picEntries
7797 -- construct META-INF/manifest.xml based on archive
@@ -126,18 +146,18 @@ writeODT opts doc@(Pandoc meta _) = do
126146 return $ fromArchive archive''
127147
128148-- | transform both Image and Math elements
129−transformPicMath :: WriterOptions -> IORef [Entry] -> Inline -> IO Inline
130−transformPicMath opts entriesRef (Image attr@(id', cls, _) lab (src,t)) = do
131− res <- fetchItem' (writerMediaBag opts) (writerSourceURL opts) src
149+transformPicMath :: WriterOptions ->Inline -> O Inline
150+transformPicMath opts (Image attr@(id', cls, _) lab (src,t)) = do
151+ res <- lift $ P.fetchItem' (writerMediaBag opts) (writerSourceURL opts) src
132152 case res of
133153 Left (_ :: E.SomeException) -> do
134− warn $ "Could not find image `" ++ src ++ "', skipping..."
154+ lift $ P.warn $ "Could not find image `" ++ src ++ "', skipping..."
135155 return $ Emph lab
136156 Right (img, mbMimeType) -> do
137157 (ptX, ptY) <- case imageSize img of
138158 Right s -> return $ sizeInPoints s
139159 Left msg -> do
140− warn $ "Could not determine image size in `" ++
160+ lift $ P.warn $ "Could not determine image size in `" ++
141161 src ++ "': " ++ msg
142162 return (100, 100)
143163 let dims =
@@ -155,28 +175,28 @@ transformPicMath opts entriesRef (Image attr@(id', cls, _) lab (src,t)) = do
155175 Just dim -> Just $ Inch $ inInch opts dim
156176 Nothing -> Nothing
157177 let newattr = (id', cls, dims)
158− entries <- readIORef entriesRef
178+ entries <- gets stEntries
159179 let extension = fromMaybe (takeExtension $ takeWhile (/='?') src)
160180 (mbMimeType >>= extensionFromMimeType)
161181 let newsrc = "Pictures/" ++ show (length entries) <.> extension
162182 let toLazy = B.fromChunks . (:[])
163− epochtime <- floor `fmap` getPOSIXTime
183+ epochtime <- floor `fmap` (lift P.getPOSIXTime)
164184 let entry = toEntry newsrc epochtime $ toLazy img
165− modifyIORef entriesRef (entry:)
185+ modify $ \st -> st{ stEntries = entry : entries }
166186 return $ Image newattr lab (newsrc, t)
167−transformPicMath _ entriesRef (Math t math) = do
168− entries <- readIORef entriesRef
187+transformPicMath _ (Math t math) = do
188+ entries <- gets stEntries
169189 let dt = if t == InlineMath then DisplayInline else DisplayBlock
170190 case writeMathML dt <$> readTeX math of
171191 Left _ -> return $ Math t math
172192 Right r -> do
173193 let conf = useShortEmptyTags (const False) defaultConfigPP
174194 let mathml = ppcTopElement conf r
175− epochtime <- floor `fmap` getPOSIXTime
195+ epochtime <- floor `fmap` (lift $ P.getPOSIXTime)
176196 let dirname = "Formula-" ++ show (length entries) ++ "/"
177197 let fname = dirname ++ "content.xml"
178198 let entry = toEntry fname epochtime (fromStringLazy mathml)
179− modifyIORef entriesRef (entry:)
199+ modify $ \st -> st{ stEntries = entry : entries }
180200 return $ RawInline (Format "opendocument") $ render Nothing $
181201 inTags False "draw:frame" [("text:anchor-type",
182202 if t == DisplayMath
@@ -189,4 +209,4 @@ transformPicMath _ entriesRef (Math t math) = do
189209 , ("xlink:show", "embed")
190210 , ("xlink:actuate", "onLoad")]
191211
192−transformPicMath _ _ x = return x
212+transformPicMath _ x = return x