packages feed

pdf-toolbox-content-0.1.1: lib/Pdf/Content/Parser.hs

-- | 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