git.iko.soy/typeable/pandoc
on master help sign in
ODT writer: Use mathml for proper rendering of formulas.
33955fdJohn MacFarlane
src/Text/Pandoc/Writers/ODT.hs+42−8
@@ -30,8 +30,10 @@ Conversion of 'Pandoc' documents to ODT.
30-}
30-}
31module Text.Pandoc.Writers.ODT ( writeODT ) where
31module Text.Pandoc.Writers.ODT ( writeODT ) where
32import Data.IORef
32import Data.IORef
33−import Data.List ( isPrefixOf )
33+import Data.List ( isPrefixOf, isSuffixOf )
34import Data.Maybe ( fromMaybe )
34import Data.Maybe ( fromMaybe )
35+import Text.XML.Light.Output
36+import Text.TeXMath
35import qualified Data.ByteString.Lazy as B
37import qualified Data.ByteString.Lazy as B
36import Text.Pandoc.UTF8 ( fromStringLazy )
38import Text.Pandoc.UTF8 ( fromStringLazy )
37import Codec.Archive.Zip
39import Codec.Archive.Zip
@@ -41,13 +43,14 @@ import Text.Pandoc.ImageSize ( imageSize, sizeInPoints )
41import Text.Pandoc.MIME ( getMimeType )
43import Text.Pandoc.MIME ( getMimeType )
42import Text.Pandoc.Definition
44import Text.Pandoc.Definition
43import Text.Pandoc.Walk
45import Text.Pandoc.Walk
46+import Text.Pandoc.Writers.Shared ( fixDisplayMath )
44import Text.Pandoc.Writers.OpenDocument ( writeOpenDocument )
47import Text.Pandoc.Writers.OpenDocument ( writeOpenDocument )
45import Control.Monad (liftM)
48import Control.Monad (liftM)
46import Text.Pandoc.XML
49import Text.Pandoc.XML
47import Text.Pandoc.Pretty
50import Text.Pandoc.Pretty
48import qualified Control.Exception as E
51import qualified Control.Exception as E
49import Data.Time.Clock.POSIX ( getPOSIXTime )
52import Data.Time.Clock.POSIX ( getPOSIXTime )
50−import System.FilePath ( takeExtension )
53+import System.FilePath ( takeExtension, takeDirectory )
51
54
52-- | Produce an ODT file from a Pandoc document.
55-- | Produce an ODT file from a Pandoc document.
53writeODT :: WriterOptions -- ^ Writer options
56writeODT :: WriterOptions -- ^ Writer options
@@ -61,9 +64,9 @@ writeODT opts doc@(Pandoc meta _) = do
61 Just f -> B.readFile f
64 Just f -> B.readFile f
62 Nothing -> (B.fromChunks . (:[])) `fmap`
65 Nothing -> (B.fromChunks . (:[])) `fmap`
63 readDataFile datadir "reference.odt"
66 readDataFile datadir "reference.odt"
64− -- handle pictures
67+ -- handle formulas and pictures
65 picEntriesRef <- newIORef ([] :: [Entry])
68 picEntriesRef <- newIORef ([] :: [Entry])
66− doc' <- walkM (transformPic opts picEntriesRef) doc
69+ doc' <- walkM (transformPicMath opts picEntriesRef) $ walk fixDisplayMath doc
67 let newContents = writeOpenDocument opts{writerWrapText = False} doc'
70 let newContents = writeOpenDocument opts{writerWrapText = False} doc'
68 epochtime <- floor `fmap` getPOSIXTime
71 epochtime <- floor `fmap` getPOSIXTime
69 let contentEntry = toEntry "content.xml" epochtime
72 let contentEntry = toEntry "content.xml" epochtime
@@ -73,7 +76,11 @@ writeODT opts doc@(Pandoc meta _) = do
73 $ contentEntry : picEntries
76 $ contentEntry : picEntries
74 -- construct META-INF/manifest.xml based on archive
77 -- construct META-INF/manifest.xml based on archive
75 let toFileEntry fp = case getMimeType fp of
78 let toFileEntry fp = case getMimeType fp of
79+ Nothing -> if "Formula-" `isPrefixOf` fp && "/" `isSuffixOf` fp
80+ then selfClosingTag "manifest:file-entry"
81+ [("manifest:media-type","application/vnd.oasis.opendocument.formula")
82+ ,("manifest:full-path",fp)]
76− Nothing -> empty
83+ else empty
77 Just m -> selfClosingTag "manifest:file-entry"
84 Just m -> selfClosingTag "manifest:file-entry"
78 [("manifest:media-type", m)
85 [("manifest:media-type", m)
79 ,("manifest:full-path", fp)
86 ,("manifest:full-path", fp)
@@ -81,6 +88,8 @@ writeODT opts doc@(Pandoc meta _) = do
81 ]
88 ]
82 let files = [ ent | ent <- filesInArchive archive,
89 let files = [ ent | ent <- filesInArchive archive,
83 not ("META-INF" `isPrefixOf` ent) ]
90 not ("META-INF" `isPrefixOf` ent) ]
91+ let formulas = [ takeDirectory ent ++ "/" | ent <- filesInArchive archive,
92+ "Formula-" `isPrefixOf` ent, takeExtension ent == ".xml" ]
84 let manifestEntry = toEntry "META-INF/manifest.xml" epochtime
93 let manifestEntry = toEntry "META-INF/manifest.xml" epochtime
85 $ fromStringLazy $ render Nothing
94 $ fromStringLazy $ render Nothing
86 $ text "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
95 $ text "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
@@ -92,6 +101,7 @@ writeODT opts doc@(Pandoc meta _) = do
92 [("manifest:media-type","application/vnd.oasis.opendocument.text")
101 [("manifest:media-type","application/vnd.oasis.opendocument.text")
93 ,("manifest:full-path","/")]
102 ,("manifest:full-path","/")]
94 $$ vcat ( map toFileEntry $ files )
103 $$ vcat ( map toFileEntry $ files )
104+ $$ vcat ( map toFileEntry $ formulas )
95 )
105 )
96 )
106 )
97 let archive' = addEntryToArchive manifestEntry archive
107 let archive' = addEntryToArchive manifestEntry archive
@@ -119,8 +129,8 @@ writeODT opts doc@(Pandoc meta _) = do
119 $ addEntryToArchive metaEntry archive'
129 $ addEntryToArchive metaEntry archive'
120 return $ fromArchive archive''
130 return $ fromArchive archive''
121
131
122−transformPic :: WriterOptions -> IORef [Entry] -> Inline -> IO Inline
132+transformPicMath :: WriterOptions -> IORef [Entry] -> Inline -> IO Inline
123−transformPic opts entriesRef (Image lab (src,_)) = do
133+transformPicMath opts entriesRef (Image lab (src,_)) = do
124 res <- fetchItem (writerSourceURL opts) src
134 res <- fetchItem (writerSourceURL opts) src
125 case res of
135 case res of
126 Left (_ :: E.SomeException) -> do
136 Left (_ :: E.SomeException) -> do
@@ -137,5 +147,29 @@ transformPic opts entriesRef (Image lab (src,_)) = do
137 let entry = toEntry newsrc epochtime $ toLazy img
147 let entry = toEntry newsrc epochtime $ toLazy img
138 modifyIORef entriesRef (entry:)
148 modifyIORef entriesRef (entry:)
139 return $ Image lab (newsrc, tit')
149 return $ Image lab (newsrc, tit')
140−transformPic _ _ x = return x
150+transformPicMath _ entriesRef (Math t math) = do
151+ entries <- readIORef entriesRef
152+ let dt = if t == InlineMath then DisplayInline else DisplayBlock
153+ case texMathToMathML dt math of
154+ Left _ -> return $ Math t math
155+ Right r -> do
156+ let conf = useShortEmptyTags (const False) defaultConfigPP
157+ let mathml = ppcTopElement conf r
158+ epochtime <- floor `fmap` getPOSIXTime
159+ let dirname = "Formula-" ++ show (length entries) ++ "/"
160+ let fname = dirname ++ "content.xml"
161+ let entry = toEntry fname epochtime (fromStringLazy mathml)
162+ modifyIORef entriesRef (entry:)
163+ return $ RawInline (Format "opendocument") $ render Nothing $
164+ inTags False "draw:frame" [("text:anchor-type",
165+ if t == DisplayMath
166+ then "paragraph"
167+ else "as-char")
168+ ,("style:vertical-pos", "middle")
169+ ,("style:vertical-rel", "text")] $
170+ selfClosingTag "draw:object" [("xlink:href", dirname)
171+ , ("xlink:type", "simple")
172+ , ("xlink:show", "embed")
173+ , ("xlink:actuate", "onLoad")]
141
174
175+transformPicMath _ _ x = return x