import Control.Monad.State (MonadState(..), evalStateT) import Control.Monad.Except.IOH (handle) import Control.Monad.Trans (lift) import qualified Data.ByteString.Char8 as BS (readFile) import qualified Data.ByteString.Lazy.Char8 as Lazy (writeFile) import Data.Map ((!), fromSet) import qualified Data.Set as Set (fromList) import PDF (Document, UnifiedLayers(..), parseDocument, render) import PDF.Box (at) import PDF.Pages (Pages(..)) import System.Environment (getArgs) import System.Exit (die) import System.FilePath (replaceBaseName, takeBaseName) import Text.Read (readEither) parseRange :: String -> Either String (Int, Int) parseRange = evalStateT $ do from <- lift . readEither =<< state (break (== '-')) get >>= makeRange from where makeRange from "" = return (from, from) makeRange from (_:to) = (,) from <$> lift (readEither to) cut :: (Int, Int) -> Document -> IO Document cut (p1, p2) doc = (( at UnifiedLayers .at Pages $ \pages -> return $ fromSet (pages!) $ Set.fromList [pMin .. pMax] ) doc) `handle` die where (pMin, pMax) = (min p1 p2, max p1 p2) main :: IO () main = do [inputPath, inputRange] <- getArgs range <- catchLeft $ parseRange inputRange doc <- catchLeft =<< parseDocument <$> BS.readFile inputPath Lazy.writeFile (outputPath inputPath inputRange) . render =<< cut range doc where catchLeft = either die return outputPath fileName range = replaceBaseName fileName (takeBaseName fileName ++ "_p" ++ range)