-- | Parse content stream
module Pdf.Content.Parser
(
readNextOperator,
parseContent,
)
where
import Pdf.Core.Exception
import Pdf.Core.Parsers.Object
import Pdf.Content.Ops
import Data.Attoparsec.ByteString.Char8 (Parser)
import qualified Data.Attoparsec.ByteString.Char8 as Parser
import Control.Applicative
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
-- | Read the next operator if any
readNextOperator :: InputStream Expr -> IO (Maybe Operator)
readNextOperator is = message "readNextOperator" $ go []
where
go args = do
expr <- Streams.read is
-- XXX: it should be handled by stream creator
`catch` \(Streams.ParseException msg) -> throwIO (Corrupted msg [])
case expr of
Nothing -> case args of
[] -> return Nothing
_ -> throwIO $ Corrupted ("Args without op: " ++ show args) []
Just (Obj o) -> go (o : args)
Just (Op o) -> return $ Just (o, reverse args)
-- | Parser expression in a content stream
parseContent :: Parser (Maybe Expr)
parseContent = do
skipSpace
(Parser.endOfInput >> return Nothing) <|>
fmap Just (fmap Obj parseObject <|>
fmap (Op . toOp) (Parser.takeWhile1 isRegularChar))
-- Treat comments as spaces
skipSpace :: Parser ()
skipSpace = do
Parser.skipSpace
void $ many $ do
_ <- Parser.char '%'
Parser.skipWhile $ \c -> c /= '\n' && c /= '\r'
Parser.skipSpace