aboutsummaryrefslogtreecommitdiff
path: root/Text
diff options
context:
space:
mode:
authorfiddlosopher <fiddlosopher@788f1e2b-df1e-0410-8736-df70ead52e1b>2008-07-31 23:16:02 +0000
committerfiddlosopher <fiddlosopher@788f1e2b-df1e-0410-8736-df70ead52e1b>2008-07-31 23:16:02 +0000
commitd072ad4b66b5cdf5a8f811ae1f78460e34270d58 (patch)
tree6f77dde12c4d5824434e0961f71c9f38f19be4cd /Text
parent504a61a97b9dbd0b55b2b06fd2c1f547d71a1fa1 (diff)
downloadpandoc-d072ad4b66b5cdf5a8f811ae1f78460e34270d58.tar.gz
Added 'odt' output option to pandoc:
Not a writer, but a module that inserts the output of the OpenDocument writer into an ODT archive. This replaces markdown2odt. + Added odt output option to Main.hs. + Added default for .odt output file. + Changed defaults so that .xml and .sgml aren't automatically DocBook. + Added odt writer to Text.Pandoc exports. + Added Text.Pandoc.ODT and included in pandoc.cabal. + Added reference.odt as data-file in pandoc.cabal. + Handle picture links in OpenDocument files using xml library. + Removed markdown2odt and references from Makefile, README, man. git-svn-id: https://pandoc.googlecode.com/svn/trunk@1345 788f1e2b-df1e-0410-8736-df70ead52e1b
Diffstat (limited to 'Text')
-rw-r--r--Text/Pandoc/ODT.hs141
-rw-r--r--Text/Pandoc/Shared.hs26
2 files changed, 166 insertions, 1 deletions
diff --git a/Text/Pandoc/ODT.hs b/Text/Pandoc/ODT.hs
new file mode 100644
index 000000000..f388515fb
--- /dev/null
+++ b/Text/Pandoc/ODT.hs
@@ -0,0 +1,141 @@
+{-
+Copyright (C) 2008 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.ODT
+ Copyright : Copyright (C) 2006-7 John MacFarlane
+ License : GNU GPL, version 2 or above
+
+ Maintainer : John MacFarlane <jgm@berkeley.edu>
+ Stability : alpha
+ Portability : portable
+
+Functions for producing an ODT file from OpenDocument XML.
+-}
+module Text.Pandoc.ODT ( saveOpenDocumentAsODT ) where
+import Data.Maybe ( fromJust )
+import Data.List ( partition, intersperse )
+import Prelude hiding ( writeFile, readFile )
+import System.IO.UTF8
+import System.IO ( stderr )
+import System.Directory
+import System.FilePath ( (</>), takeDirectory, takeFileName, splitDirectories )
+import System.Process ( runCommand, waitForProcess )
+import System.Exit
+import Text.XML.Light
+import Text.XML.Light.Cursor
+import Text.Pandoc.Shared ( withTempDir )
+import Network.URI ( isURI )
+import Paths_pandoc
+
+-- | Produce an ODT file from OpenDocument XML.
+saveOpenDocumentAsODT :: FilePath -- ^ Pathname of ODT file to be produced.
+ -> FilePath -- ^ Relative directory of source file.
+ -> String -- ^ OpenDocument XML contents.
+ -> IO ()
+saveOpenDocumentAsODT destinationODTPath sourceDirRelative xml = do
+ let zipCmd = "zip"
+ -- check for zip in path:
+ maybeZipPath <- findExecutable zipCmd
+ let zipPath = case maybeZipPath of
+ Just p -> p
+ Nothing -> error $ "The '" ++ zipCmd ++ "' command, which is needed to build an ODT file, " ++
+ "was not found.\n" ++
+ "It can be obtained from http://www.info-zip.org/Zip.html\n" ++
+ "Debian (and Debian-based) linux: apt-get install zip\n" ++
+ "Windows: See http://gnuwin32.sourceforge.net/packages/zip.htm"
+ referenceODTPath <- getDataFileName "reference.odt"
+ withTempDir "pandoc-odt" $ \tempDir -> do
+ let tempODT = tempDir </> "reference.odt"
+ copyFile referenceODTPath tempODT
+ createDirectory $ tempDir </> "Pictures"
+ xml' <- handlePictures tempODT sourceDirRelative xml
+ writeFile (tempDir </> "content.xml") xml'
+ oldDir <- getCurrentDirectory
+ setCurrentDirectory tempDir
+ let zipCmdLine = zipPath ++ " -9 -q -r " ++ tempODT ++ " " ++ "content.xml Pictures"
+ ec <- runCommand zipCmdLine >>= waitForProcess -- this requires compilation with -threaded
+ setCurrentDirectory oldDir
+ case ec of
+ ExitSuccess -> copyFile tempODT destinationODTPath
+ _ -> error "Error creating ODT." >> exitWith ec
+
+
+-- | Find <draw:image ... /> elements and copy the file (xlink:href attribute) into Pictures/ in
+-- the zip file. If filename is a URL, attempt to download it. Modify xlink:href attributes
+-- to point to the new locations in Pictures/. Return modified XML.
+handlePictures :: FilePath -- ^ Path of ODT file in temp directory
+ -> FilePath -- ^ Directory (relative) containing source file
+ -> String -- ^ OpenDocument XML string
+ -> IO String -- ^ Modified XML
+handlePictures tempODT sourceDirRelative xml = do
+ let parsed = case parseXMLDoc xml of
+ Nothing -> error "Could not parse OpenDocument XML."
+ Just x -> x
+ let cursor = case (fromForest $ elContent parsed) of
+ Nothing -> error "ODT appears empty"
+ Just x -> x
+ cursor' <- scanPictures tempODT sourceDirRelative cursor
+ let modified = parsed { elContent = toForest $ root cursor' }
+ putStrLn $ showTopElement modified
+ return $ showTopElement modified
+
+scanPictures :: FilePath -> FilePath -> Cursor -> IO Cursor
+scanPictures tempODT sourceDirRelative cursor = do
+ cursor' <- handleTree tempODT sourceDirRelative cursor
+ case right cursor' of
+ Just n -> scanPictures tempODT sourceDirRelative n
+ Nothing -> return cursor'
+
+handleTree :: FilePath -> FilePath -> Cursor -> IO Cursor
+handleTree tempODT sourceDirRelative cursor = do
+ case firstChild cursor of
+ Nothing -> modifyContentM (handleContent tempODT sourceDirRelative) cursor
+ Just n -> scanPictures tempODT sourceDirRelative n >>= return . fromJust . parent
+
+-- | If content is an image link, handle it appropriately.
+-- Otherwise, handle children if any.
+handleContent :: FilePath -> FilePath -> Content -> IO Content
+handleContent tempODT sourceDirRelative content@(Elem el) = do
+ if qName (elName el) == "image"
+ then do
+ let (hrefs, rest) = partition (\a -> qName (attrKey a) == "href") $ elAttribs el
+ let href = case hrefs of
+ [] -> error $ "No href found in " ++ show el
+ [x] -> x
+ _ -> error $ "Multiple hrefs found in " ++ show el
+ if isURI $ attrVal href
+ then return content
+ else do -- treat as filename
+ let oldLoc = sourceDirRelative </> attrVal href
+ fileExists <- doesFileExist oldLoc
+ if fileExists
+ then do
+ let pref = take 230 $ concat $ intersperse "_" $
+ splitDirectories $ takeDirectory $ attrVal href
+ let newLoc = "Pictures" </> pref ++ "_" ++ (takeFileName $ attrVal href)
+ let tempDir = takeDirectory tempODT
+ copyFile oldLoc $ tempDir </> newLoc
+ let newAttrs = (href { attrVal = newLoc }) : rest
+ return $ Elem (el { elAttribs = newAttrs })
+ else do
+ hPutStrLn stderr $ "Warning: Unable to find image at " ++ oldLoc ++ " - ignoring."
+ return content
+ else return content
+
+handleContent _ _ c = return c -- not Element
diff --git a/Text/Pandoc/Shared.hs b/Text/Pandoc/Shared.hs
index 05289d6a6..3c202db0f 100644
--- a/Text/Pandoc/Shared.hs
+++ b/Text/Pandoc/Shared.hs
@@ -98,7 +98,9 @@ module Text.Pandoc.Shared (
-- * Writer options
HTMLMathMethod (..),
WriterOptions (..),
- defaultWriterOptions
+ defaultWriterOptions,
+ -- * File handling
+ withTempDir
) where
import Text.Pandoc.Definition
@@ -110,6 +112,9 @@ import Data.Char ( toLower, toUpper, ord, isLower, isUpper )
import Data.List ( find, isPrefixOf )
import Control.Monad ( join )
import Network.URI ( parseURI, URI (..), isAllowedInURI )
+import System.FilePath ( (</>), (<.>) )
+import System.IO.Error ( catch, ioError, isAlreadyExistsError )
+import System.Directory
--
-- List processing
@@ -900,3 +905,22 @@ defaultWriterOptions =
, writerReferenceLinks = False
, writerWrapText = True
}
+
+-- | Perform a function in a temporary directory and clean up.
+withTempDir :: FilePath -> (FilePath -> IO a) -> IO a
+withTempDir baseName func = do
+ tempDir <- createTempDir 0 baseName
+ result <- catch (func tempDir) $ \e -> removeDirectoryRecursive tempDir >> ioError e
+ removeDirectoryRecursive tempDir
+ return result
+
+-- | Create a temporary directory with a unique name.
+createTempDir :: Integer -> FilePath -> IO FilePath
+createTempDir num baseName = do
+ sysTempDir <- getTemporaryDirectory
+ let dirName = sysTempDir </> baseName <.> show num
+ catch (createDirectory dirName >> return dirName) $
+ \e -> if isAlreadyExistsError e
+ then createTempDir (num + 1) baseName
+ else ioError e
+