{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings #-}
module Pdf.Document.Pdf
(
Pdf,
withPdfFile,
fromFile,
fromHandle,
fromBytes,
document,
lookupObject,
streamContent,
rawStreamContent,
deref,
isEncrypted,
setUserPassword,
defaultUserPassword,
EncryptedError (..),
enableCache,
disableCache,
)
where
import Pdf.Core.Object
import Pdf.Core.Stream (knownFilters)
import Pdf.Core.File (File)
import qualified Pdf.Core.File as File
import Pdf.Core.Encryption (defaultUserPassword)
import Pdf.Document.Internal.Types
import Data.Typeable
import Data.IORef
import Data.ByteString (ByteString)
import Data.Text (Text)
import qualified Data.HashMap.Strict as HashMap
import Control.Monad
import Control.Exception hiding (throw)
import System.IO (Handle)
import System.IO.Streams (InputStream)
withPdfFile :: FilePath -> (Pdf -> IO a) -> IO a
withPdfFile path action = File.withPdfFile path $ \f -> do
pdf <- fromFile f
action pdf
-- | Make Pdf with interface to pdf file
fromFile :: File -> IO Pdf
fromFile f = Pdf f
<$> newIORef (False, HashMap.empty)
-- | Make Pdf with seekable handle
fromHandle :: Handle -> IO Pdf
fromHandle h = do
File.fromHandle knownFilters h >>= fromFile
-- | Make Pdf from a ByteString
fromBytes :: ByteString -> IO Pdf
fromBytes h = do
File.fromBytes knownFilters h >>= fromFile
file :: Pdf -> File
file (Pdf f _) = f
-- | Get PDF document
document :: Pdf -> IO Document
document pdf = do
status <- File.encryptionStatus (file pdf)
case status of
File.Encrypted -> throwIO $
EncryptedError "File is encrypted, use 'setUserPassword'"
File.Decrypted -> return ()
File.Plain -> return ()
Document pdf <$> File.lastTrailer (file pdf)
-- | Find object by it's reference
lookupObject :: Pdf -> Ref -> IO Object
lookupObject pdf ref = do
let Pdf _ cacheRef = pdf
(useCache, cache) <- readIORef cacheRef
case HashMap.lookup ref cache of
Just obj -> return obj
Nothing -> do
obj <- File.findObject (file pdf) ref
when useCache $
writeIORef cacheRef (useCache, HashMap.insert ref obj cache)
return obj
-- | Cache object for future lookups
enableCache :: Pdf -> IO ()
enableCache (Pdf _ cacheRef) = do
(_, cache) <- readIORef cacheRef
writeIORef cacheRef (True, cache)
-- | Don't cache object for future lookups
disableCache :: Pdf -> IO ()
disableCache (Pdf _ cacheRef) = do
(_, cache) <- readIORef cacheRef
writeIORef cacheRef (False, cache)
-- | Get stream content, decoded and decrypted
--
-- Note: length of the content may differ from the raw one
streamContent :: Pdf
-> Ref
-> Stream
-> IO (InputStream ByteString)
streamContent pdf ref s =
File.streamContent (file pdf) ref s
-- | Get stream content without decoding it
rawStreamContent
:: Pdf
-> Ref
-> Stream
-> IO (InputStream ByteString)
rawStreamContent pdf ref s =
File.rawStreamContent (file pdf) ref s
-- | Whether the PDF document it encrypted
isEncrypted :: Pdf -> IO Bool
isEncrypted pdf = do
status <- File.encryptionStatus (file pdf)
return $ case status of
File.Encrypted -> True
File.Decrypted -> True
File.Plain -> False
-- | Set the password to be user for decryption
--
-- Returns False when the password is wrong
setUserPassword :: Pdf -> ByteString -> IO Bool
setUserPassword pdf password =
File.setUserPassword (file pdf) password
deref :: Pdf -> Object -> IO Object
deref pdf (Ref r) = lookupObject pdf r
deref _ o = return o
-- | File is enctypted
data EncryptedError = EncryptedError Text
deriving (Show, Typeable)
instance Exception EncryptedError