-- | Unclassified tools module Pdf.Core.Util ( notice , readObjectAtOffset , readCompressedObject ) where import Pdf.Core.IO.Buffer (Buffer) import qualified Pdf.Core.IO.Buffer as Buffer import Pdf.Core.Exception import Pdf.Core.Object import Pdf.Core.Parsers.Object import Data.Int import Data.ByteString (ByteString) import Data.Attoparsec.ByteString.Char8 (Parser) import qualified Data.Attoparsec.ByteString.Char8 as Parser import Control.Monad import Control.Exception hiding (throw) import System.IO.Streams (InputStream) import qualified System.IO.Streams as Streams import qualified System.IO.Streams.Attoparsec as Streams -- | Add a message to 'Maybe' notice :: Maybe a -> String -> Either String a notice Nothing = Left notice (Just a) = const (Right a) -- | Read indirect object at the specified offset -- -- Returns the object and the 'Ref'. The payload for stream -- will be an offset of stream content readObjectAtOffset :: Buffer -> Int64 -- ^ object offset -> IO (Ref, Object) readObjectAtOffset buf off = message "readObjectAtOffset" $ do Buffer.seek buf off (ref, o) <- Streams.parseFromStream parseIndirectObject (Buffer.toInputStream buf) `catch` \(Streams.ParseException msg) -> throwIO (Corrupted msg []) case o of Stream (S dict _) -> do pos <- Buffer.tell buf return (ref, Stream (S dict pos)) Ref _ -> throwIO $ Corrupted "Indirect object can't be a Ref" [] _ -> return (ref, o) -- | Read object from object stream -- -- Never returns 'Stream' readCompressedObject :: InputStream ByteString -- ^ decoded object stream -> Int64 -- ^ an offset of the first object -- (\"First\" key in dictionary) -> Int -- ^ object number to read -> IO Object readCompressedObject is first num = do (is', counter) <- Streams.countInput is off <- do res <- Streams.parseFromStream (replicateM (num + 1) headerP) is' `catch` \(Streams.ParseException msg) -> throwIO $ Corrupted "Object stream" [msg] when (null res) $ error "readCompressedObject: imposible" case last res of (_, off) -> return off pos <- counter Buffer.dropExactly (fromIntegral $ first + off - pos) is Streams.parseFromStream parseObject is `catch` \(Streams.ParseException msg) -> throwIO $ Corrupted "Object in object stream" [msg] where headerP :: Parser (Int, Int64) headerP = do n <- Parser.decimal Parser.skipSpace off <- Parser.decimal Parser.skipSpace return (n, off)