{-# LANGUAGE PatternGuards     #-}
{-# LANGUAGE OverloadedStrings #-}
{- |
   Module      : Text.Pandoc.Reader.Odt
   Copyright   : Copyright (C) 2015 Martin Linnemann
   License     : GNU GPL, version 2 or above

   Maintainer  : Martin Linnemann <theCodingMarlin@googlemail.com>
   Stability   : alpha
   Portability : portable

Entry point to the odt reader.
-}

module Text.Pandoc.Readers.Odt ( readOdt ) where

import Codec.Archive.Zip
import qualified Text.XML.Light as XML

import qualified Data.ByteString.Lazy as B

import System.FilePath

import Control.Monad.Except (throwError)

import Text.Pandoc.Class.PandocMonad (PandocMonad)
import qualified Text.Pandoc.Class.PandocMonad as P
import Text.Pandoc.Definition
import Text.Pandoc.Error
import Text.Pandoc.MediaBag
import Text.Pandoc.Options
import qualified Text.Pandoc.UTF8 as UTF8

import Text.Pandoc.Readers.Odt.ContentReader
import Text.Pandoc.Readers.Odt.StyleReader

import Text.Pandoc.Readers.Odt.Generic.Fallible
import Text.Pandoc.Readers.Odt.Generic.XMLConverter
import Text.Pandoc.Shared (filteredFilesFromArchive)

readOdt :: PandocMonad m
        => ReaderOptions
        -> B.ByteString
        -> m Pandoc
readOdt :: ReaderOptions -> ByteString -> m Pandoc
readOdt ReaderOptions
opts ByteString
bytes = case ReaderOptions
-> ByteString -> Either PandocError (Pandoc, MediaBag)
readOdt' ReaderOptions
opts ByteString
bytes of
  Right (Pandoc
doc, MediaBag
mb) -> do
    MediaBag -> m ()
forall (m :: * -> *). PandocMonad m => MediaBag -> m ()
P.setMediaBag MediaBag
mb
    Pandoc -> m Pandoc
forall (m :: * -> *) a. Monad m => a -> m a
return Pandoc
doc
  Left PandocError
e -> PandocError -> m Pandoc
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError PandocError
e

--
readOdt' :: ReaderOptions
         -> B.ByteString
         -> Either PandocError (Pandoc, MediaBag)
readOdt' :: ReaderOptions
-> ByteString -> Either PandocError (Pandoc, MediaBag)
readOdt' ReaderOptions
_ ByteString
bytes = ByteString -> Either PandocError (Pandoc, MediaBag)
bytesToOdt ByteString
bytes-- of
--                    Right (pandoc, mediaBag) -> Right (pandoc , mediaBag)
--                    Left  err                -> Left err

--
bytesToOdt :: B.ByteString -> Either PandocError (Pandoc, MediaBag)
bytesToOdt :: ByteString -> Either PandocError (Pandoc, MediaBag)
bytesToOdt ByteString
bytes = case ByteString -> Either String Archive
toArchiveOrFail ByteString
bytes of
  Right Archive
archive -> Archive -> Either PandocError (Pandoc, MediaBag)
archiveToOdt Archive
archive
  Left String
_        -> PandocError -> Either PandocError (Pandoc, MediaBag)
forall a b. a -> Either a b
Left (PandocError -> Either PandocError (Pandoc, MediaBag))
-> PandocError -> Either PandocError (Pandoc, MediaBag)
forall a b. (a -> b) -> a -> b
$ Text -> PandocError
PandocParseError Text
"Couldn't parse odt file."

--
archiveToOdt :: Archive -> Either PandocError (Pandoc, MediaBag)
archiveToOdt :: Archive -> Either PandocError (Pandoc, MediaBag)
archiveToOdt Archive
archive
  | Just Entry
contentEntry      <- String -> Archive -> Maybe Entry
findEntryByPath String
"content.xml" Archive
archive
  , Just Entry
stylesEntry       <- String -> Archive -> Maybe Entry
findEntryByPath String
"styles.xml"  Archive
archive
  , Just Element
contentElem       <- Entry -> Maybe Element
entryToXmlElem Entry
contentEntry
  , Just Element
stylesElem        <- Entry -> Maybe Element
entryToXmlElem Entry
stylesEntry
  , Right Styles
styles           <- Either () Styles -> Either () Styles -> Either () Styles
forall a b.
(Monoid a, Monoid b) =>
Either a b -> Either a b -> Either a b
chooseMax (Element -> Either () Styles
readStylesAt Element
stylesElem )
                                        (Element -> Either () Styles
readStylesAt Element
contentElem)
  , [(String, ByteString)]
media                  <- Archive -> (String -> Bool) -> [(String, ByteString)]
filteredFilesFromArchive Archive
archive String -> Bool
filePathIsOdtMedia
  , ReaderState
startState             <- Styles -> [(String, ByteString)] -> ReaderState
readerState Styles
styles [(String, ByteString)]
media
  , Right (Pandoc, MediaBag)
pandocWithMedia  <- FallibleXMLConverter Namespace ReaderState () (Pandoc, MediaBag)
-> ReaderState -> Element -> Either () (Pandoc, MediaBag)
forall nsID extraState success.
NameSpaceID nsID =>
FallibleXMLConverter nsID extraState () success
-> extraState -> Element -> Fallible success
runConverter' FallibleXMLConverter Namespace ReaderState () (Pandoc, MediaBag)
forall _x. OdtReader _x (Pandoc, MediaBag)
read_body
                                            ReaderState
startState
                                            Element
contentElem

  = (Pandoc, MediaBag) -> Either PandocError (Pandoc, MediaBag)
forall a b. b -> Either a b
Right (Pandoc, MediaBag)
pandocWithMedia

  | Bool
otherwise
    -- Not very detailed, but I don't think more information would be helpful
  = PandocError -> Either PandocError (Pandoc, MediaBag)
forall a b. a -> Either a b
Left (PandocError -> Either PandocError (Pandoc, MediaBag))
-> PandocError -> Either PandocError (Pandoc, MediaBag)
forall a b. (a -> b) -> a -> b
$ Text -> PandocError
PandocParseError Text
"Couldn't parse odt file."
    where
      filePathIsOdtMedia :: FilePath -> Bool
      filePathIsOdtMedia :: String -> Bool
filePathIsOdtMedia String
fp =
        let (String
dir, String
name) = String -> (String, String)
splitFileName String
fp
        in  (String
dir String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"Pictures/") Bool -> Bool -> Bool
|| (String
dir String -> String -> Bool
forall a. Eq a => a -> a -> Bool
/= String
"./" Bool -> Bool -> Bool
&& String
name String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"content.xml")


--
entryToXmlElem :: Entry -> Maybe XML.Element
entryToXmlElem :: Entry -> Maybe Element
entryToXmlElem = String -> Maybe Element
forall s. XmlSource s => s -> Maybe Element
XML.parseXMLDoc (String -> Maybe Element)
-> (Entry -> String) -> Entry -> Maybe Element
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> String
UTF8.toStringLazy (ByteString -> String) -> (Entry -> ByteString) -> Entry -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Entry -> ByteString
fromEntry