{-# LANGUAGE CPP #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
module Graphics.PDF.LowLevel.Types where
import qualified Data.Map.Strict as M
import Data.List(intersperse)
import Data.Int
import Control.Monad.State
import Control.Monad.Writer
import Data.Binary.Builder(Builder,fromByteString)
import Graphics.PDF.LowLevel.Serializer
import Data.Complex
import qualified Data.ByteString as S
import qualified Data.ByteString.Lazy.Internal as L(ByteString(..))
import Data.Text.Encoding
import qualified Data.Text as T
import qualified Data.ByteString.Char8 as C
import Data.Word
import Data.Char(ord)
import Text.Printf(printf)
data SpecialChar = NormalChar !Char
| BreakingHyphen
| BiggerSpace
| NormalSpace
class PdfObject a where
toPDF :: a -> Builder
class PdfLengthInfo a where
pdfLengthInfo :: a -> Maybe (Int64 , PDFReference MaybeLength)
pdfLengthInfo _ = Nothing
data AnyPdfObject = forall a . (PdfObject a, PdfLengthInfo a) => AnyPdfObject !a
instance PdfObject AnyPdfObject where
toPDF (AnyPdfObject a) = toPDF a
instance PdfLengthInfo AnyPdfObject where
pdfLengthInfo (AnyPdfObject a) = pdfLengthInfo a
newtype PDFInteger = PDFInteger Int deriving(Eq,Show,Ord,Num)
newtype PDFLength = PDFLength Int64 deriving(Eq,Show,Ord,Num)
data MaybeLength = UnknownLength
| KnownLength !PDFLength
instance PdfObject MaybeLength where
toPDF (KnownLength a) = toPDF a
toPDF (UnknownLength) = error "Trying to process an unknown length during PDF generation"
instance PdfLengthInfo MaybeLength where
type PDFFloat = Double
instance PdfObject PDFInteger where
toPDF (PDFInteger a) = serialize a
instance PdfLengthInfo PDFInteger where
instance PdfObject Int where
toPDF a = serialize a
instance PdfLengthInfo Int where
instance PdfObject PDFLength where
toPDF (PDFLength a) = serialize (show a)
instance PdfLengthInfo PDFLength where
instance PdfObject PDFFloat where
toPDF a = serialize a
instance PdfLengthInfo PDFFloat where
instance PdfObject (Complex PDFFloat) where
toPDF (x :+ y) = mconcat [ serialize x
, serialize ' '
, serialize y
]
instance PdfLengthInfo (Complex PDFFloat) where
instance PdfObject Bool where
toPDF (True) = serialize ("true" :: String)
toPDF (False) = serialize ("false" :: String)
instance PdfLengthInfo Bool where
newtype PDFString = PDFString S.ByteString deriving(Eq,Ord,Show)
newtype PDFGlyph = PDFGlyph S.ByteString deriving(Eq,Ord,Show)
newtype EscapedPDFGlyph = EscapedPDFGlyph S.ByteString deriving(Eq,Ord,Show)
newtype AsciiString = AsciiString S.ByteString deriving(Eq,Ord,Show)
newtype EscapedAsciiString = EscapedAsciiString S.ByteString deriving(Eq,Ord,Show)
escapeText :: Char -> T.Text
escapeText '(' = "\\("
escapeText ')' = "\\)"
escapeText '\\' = "\\\\"
escapeText a = T.singleton a
escapeByteString :: Char -> S.ByteString
escapeByteString '(' = C.pack "\\("
escapeByteString ')' = C.pack "\\)"
escapeByteString '\\' = C.pack "\\\\"
escapeByteString a = C.singleton a
toPDFString :: T.Text -> PDFString
toPDFString = PDFString . encodeUtf16BE
toPDFGlyph :: S.ByteString -> PDFGlyph
toPDFGlyph = PDFGlyph
toAsciiString :: String -> AsciiString
toAsciiString s = AsciiString (C.pack s)
class HasHexaStream a where
toHexaStream :: a -> S.ByteString
instance HasHexaStream S.ByteString where
toHexaStream x =
let hexChar c = C.pack (printf "%02X" (ord c) :: String)
in
C.cons 'F' . C.cons 'E' . C.cons 'F' . C.cons 'F' . C.concatMap hexChar $ x
instance HasHexaStream PDFString where
toHexaStream (PDFString x) = toHexaStream x
instance HasHexaStream PDFGlyph where
toHexaStream (PDFGlyph x) =
let hexChar c = C.pack (printf "%02X" (ord c) :: String)
in
C.concatMap hexChar $ x
newtype GlyphCode = GlyphCode Word8 deriving(Eq,Ord,Show,Integral,Bounded,Enum,Real,Num)
instance SerializeValue L.ByteString PDFString where
serialize (PDFString t) = L.Chunk t L.Empty
instance SerializeValue Builder PDFString where
serialize (PDFString t) = fromByteString t
instance SerializeValue L.ByteString PDFGlyph where
serialize (PDFGlyph t) = L.Chunk t L.Empty
instance SerializeValue Builder EscapedPDFGlyph where
serialize (EscapedPDFGlyph t) = fromByteString t
instance SerializeValue L.ByteString AsciiString where
serialize (AsciiString t) = L.Chunk t L.Empty
instance SerializeValue Builder EscapedAsciiString where
serialize (EscapedAsciiString t) = fromByteString t
lparen :: SerializeValue s Char => s
lparen = serialize '('
rparen :: SerializeValue s Char => s
rparen = serialize ')'
lbracket :: SerializeValue s Char => s
lbracket = serialize '['
rbracket :: SerializeValue s Char => s
rbracket = serialize ']'
bspace :: SerializeValue s Char => s
bspace = serialize ' '
blt :: SerializeValue s Char => s
blt = serialize '<'
bgt :: SerializeValue s Char => s
bgt = serialize '>'
newline :: SerializeValue s Char => s
newline = serialize '\n'
noPdfObject :: Monoid s => s
noPdfObject = mempty
espacePDFGlyph :: PDFGlyph -> EscapedPDFGlyph
espacePDFGlyph (PDFGlyph t) = EscapedPDFGlyph . C.concatMap escapeByteString $ t
espaceAsciiString :: AsciiString -> EscapedAsciiString
espaceAsciiString (AsciiString t) = EscapedAsciiString . C.concatMap escapeByteString $ t
instance PdfObject PDFString where
toPDF a = mconcat [ blt
, fromByteString $ toHexaStream a
, bgt
]
instance PdfLengthInfo PDFString where
instance PdfObject PDFGlyph where
toPDF a = mconcat [ blt
, fromByteString $ toHexaStream a
, bgt
]
instance PdfLengthInfo PDFGlyph where
instance PdfLengthInfo AsciiString where
instance PdfObject AsciiString where
toPDF a = mconcat [ lparen
, serialize . espaceAsciiString $ a
, rparen
]
newtype PDFName = PDFName String deriving(Eq,Ord)
instance PdfObject PDFName where
toPDF (PDFName a) = serialize ("/" ++ a)
instance PdfLengthInfo PDFName where
type PDFArray = [AnyPdfObject]
instance PdfObject a => PdfObject [a] where
toPDF l = mconcat $ (lbracket:intersperse bspace (map toPDF l)) ++ [bspace] ++ [rbracket]
instance PdfObject a => PdfLengthInfo [a] where
newtype PDFDictionary = PDFDictionary (M.Map PDFName AnyPdfObject)
instance PdfObject PDFDictionary where
toPDF (PDFDictionary a) = mconcat $ [blt,blt,newline]
++ [convertLevel a]
++ [bgt,bgt]
where
convertLevel _ = let convertItem key value current = mconcat $ [ toPDF key
, bspace
, toPDF value
, newline
, current
]
in
M.foldrWithKey convertItem mempty a
instance PdfLengthInfo PDFDictionary where
emptyDictionary :: PDFDictionary
emptyDictionary = PDFDictionary M.empty
isEmptyDictionary :: PDFDictionary -> Bool
isEmptyDictionary (PDFDictionary d) = M.null d
insertInPdfDict :: PDFName -> AnyPdfObject -> PDFDictionary -> PDFDictionary
insertInPdfDict key obj (PDFDictionary d) = PDFDictionary $ M.insert key obj d
pdfDictUnion :: PDFDictionary -> PDFDictionary -> PDFDictionary
pdfDictUnion (PDFDictionary a) (PDFDictionary b) = PDFDictionary $ M.union a b
data PDFRect = PDFRect !Double !Double !Double !Double
instance PdfObject PDFRect where
toPDF (PDFRect a b c d) = toPDF . map AnyPdfObject $ [a,b,c,d]
instance PdfLengthInfo PDFRect where
data PDFReferencedObject a = PDFReferencedObject !Int !a
instance PdfObject a => PdfObject (PDFReferencedObject a) where
toPDF (PDFReferencedObject referenceId obj) =
mconcat $ [ serialize . show $ referenceId
, serialize (" 0 obj" :: String)
, newline
, toPDF obj
, newline
, serialize ("endobj" :: String)
, newline , newline
]
instance PdfObject a => PdfLengthInfo (PDFReferencedObject a) where
data PDFReference s = PDFReference !Int deriving(Eq,Ord,Show)
referenceValue :: PDFReference s -> Int
referenceValue (PDFReference i) = i
instance PdfObject s => Num (PDFReference s) where
(+) (PDFReference a) (PDFReference b) = PDFReference (a+b)
(*) (PDFReference a) (PDFReference b) = PDFReference (a*b)
negate (PDFReference a) = PDFReference (negate a)
abs (PDFReference a) = PDFReference (abs a)
signum (PDFReference a) = PDFReference (signum a)
fromInteger a = PDFReference (fromInteger a)
instance PdfObject s => PdfObject (PDFReference s) where
toPDF (PDFReference i) = mconcat $ [ serialize . show $ i
, serialize (" 0 R" :: String)]
instance PdfObject s => PdfLengthInfo (PDFReference s) where
instance (PdfObject a,PdfObject b) => PdfObject (Either a b) where
toPDF (Left a) = toPDF a
toPDF (Right a) = toPDF a
instance (PdfObject a, PdfObject b) => PdfLengthInfo (Either a b) where
modifyStrict :: (MonadState s m) => (s -> s) -> m ()
modifyStrict f = do
s <- get
put $! (f s)
class MonadWriter Builder m => MonadPath m
data EmbeddedFont
instance PdfObject EmbeddedFont where
toPDF _ = noPdfObject
instance PdfLengthInfo EmbeddedFont where