module PDF.Output ( OBuilder(..) , Output(..) , byteString , char , join , lift , newLine , nextLine , string , render ) where import Data.ByteString.Builder (Builder, char8, lazyByteString, string8, toLazyByteString) import Data.ByteString.Lazy.Char8 (ByteString) import Data.String (IsString(..)) import Control.Monad.Reader (MonadReader(..), Reader, runReader) import qualified PDF.EOL as EOL (Style(..)) newtype OBuilder = OBuilder (Reader EOL.Style Builder) lift :: (a -> Builder) -> a -> OBuilder lift f a = OBuilder $ return (f a) instance Monoid OBuilder where mempty = OBuilder (return mempty) mappend (OBuilder a) (OBuilder b) = OBuilder (mappend <$> a <*> b) instance IsString OBuilder where fromString = string class Output a where output :: a -> OBuilder instance Output OBuilder where output = id instance Output Bool where output False = string "false" output True = string "true" instance Output Float where output = string . show instance Output a => Output [a] where output = foldl mappend mempty . fmap output join :: Output a => String -> [a] -> OBuilder join _ [] = mempty join _ [a] = output a join separator (a:as) = output a `mappend` string separator `mappend` (join separator as) newLine :: OBuilder newLine = OBuilder $ buildEOL <$> ask where buildEOL EOL.CR = char8 '\r' buildEOL EOL.LF = char8 '\n' buildEOL EOL.CRLF = string8 "\r\n" nextLine :: OBuilder -> OBuilder -> OBuilder nextLine a b = a `mappend` newLine `mappend` b char :: Char -> OBuilder char = lift char8 string :: String -> OBuilder string = lift string8 byteString :: ByteString -> OBuilder byteString = lift lazyByteString render :: Output a => EOL.Style -> a -> ByteString render eolStyle a = let OBuilder r = output a in toLazyByteString $ runReader r eolStyle