module Graphics.PDF.File
(
writePdf
) where
import System.IO
import Control.Monad
import Control.Monad.State
import Graphics.PDF.LowLevel
import Text.Printf
import qualified Data.Map as Map
import Data.List
import System.Mem
writeText :: Handle -> String -> StateT (Int,String) IO ()
writeText f text = do (s,t) <- get
put (s + (length text),t)
lift $ hPutStr f (seq s text)
writeStream :: Handle -> [Cmd] -> StateT (Int,String) IO ()
writeStream f c = mapM_ (drawAction f) c
drawAction :: Handle -> Cmd -> StateT (Int,String) IO ()
drawAction f (PdfQ actions) = do writeText f "\nq"
writeStream f actions
writeText f "\nQ"
drawAction f action = writeText f (show action)
class PdfWrite a where
writePdfObject :: Handle -> a -> Int -> StateT (Int,String) IO Int
instance PdfWrite PdfDictionary where
writePdfObject f (PdfDictionary d) k = do writeText f "<<\n"
Map.foldWithKey item (return ()) d
writeText f ">>"
return 0
where
item key itm m = do m
writeText f ("/" ++ key ++ " " )
writePdfObject f itm k
writeText f "\n"
instance PdfWrite PdfObject where
writePdfObject f (PdfInt a) k = do writeText f (show a)
return 0
writePdfObject f (PdfFloat a) k = do writeText f (show a)
return 0
writePdfObject f (PdfUnknownPointer s) k = error ("The object " ++ s ++ " has not been found or defined")
writePdfObject f (PdfPointer a) k = do writeText f $ (show a) ++ " 0 R"
return 0
writePdfObject f (PdfString a) k = do writeText f $ "(" ++ a ++ ")"
return 0
writePdfObject f (PdfBool a) k = if a then do writeText f $ "true"
return 0
else do writeText f $ "false"
return 0
writePdfObject f (PdfName a) k = do writeText f $ "/" ++ a
return 0
writePdfObject f (PdfDict d) k | pdfDictionaryEmpty d = do writeText f "<< >>"
return 0
| otherwise = do writePdfObject f d k
return 0
writePdfObject f (PdfArray []) k = do writeText f "[]"
return 0
writePdfObject f (PdfArray l) k = do writeText f "["
mapM_ (\x -> writePdfObject f x k >> writeText f " ") $ l
writeText f " ]"
return 0
writePdfObject f (PdfStream c) k = do writeText f "<<\n"
writeText f ("/Length " ++ (show (k+1)) ++ " 0 R")
writeText f "\n>>\n"
writeText f "stream"
(before,_) <- get
writeStream f (stream c)
(after,_) <- get
writeText f "\nendstream"
return (afterbefore)
where
stream (Content(s)) = s
resources = pdfDictionary [("ExtGState",PdfUnknownPointer "ExtGState"),
("XObject",PdfUnknownPointer "XObject"),
("Font",PdfUnknownPointer "Font"),
("Pattern",PdfUnknownPointer "Pattern"),
("Shading",PdfUnknownPointer "Shading"),
("ProcSet",PdfUnknownPointer "ProcSet")
]
header = "%PDF-1.4\n"
obj1 = pdfDictionary [("Type", PdfName "Catalog"),
("Outlines",PdfUnknownPointer "Obj2"),
("Pages",PdfUnknownPointer "Obj3")
]
obj2 = pdfDictionary [("Type",PdfName "Outlines"),
("Count", PdfInt 0)
]
obj3 = pdfDictionary [("Type", PdfName "Pages"),
("Kids",PdfArray [PdfUnknownPointer "Obj4"]),
("Count",PdfInt 1)
]
obj4 pdf = pdfDictionary [("Type",PdfName "Page"),
("Parent",PdfUnknownPointer "Obj3"),
("MediaBox",PdfArray [PdfInt 0,PdfInt 0,PdfInt (round (x pdf)),PdfInt (round (y pdf))]),
("Contents",PdfUnknownPointer "Main"),
("Resources",resources)
]
obj6 = PdfArray [PdfName "PDF"]
beginxref nb = ("xref\n" ++ printf "0 %d\n" (nb+1))
trailer np = ("trailer\n" ++
(printf " << /Size %d\n" ((nbObjects np)+1)) ++
(printf " /Root %d 0 R\n" (objectIndex "Obj1" np) ) ++
" >>\n\
\startxref\n")
eof = "%%EOF"
standardAlpha = pdfDictionary [("Type",PdfName "ExtGState"),
("ca",PdfFloat 1.0),
("CA",PdfFloat 1.0)
]
writeObj f k o maxnb = do (s,t) <- get
put (s,t ++ printf "%010d 00000 n \n" (s::Int) )
writeText f (printf "%d 0 obj\n" k)
len <- writePdfObject f o maxnb
writeText f ("\nendobj\n\n")
return len
writeXref f p = do (_,t) <- get
writeText f (beginxref (nbObjects p))
writeText f t
writeText f ("\n")
writeObjects f thepdf = do len <- foldM write 0 (allObjects thepdf)
return len
where
write old (k,s) = do len <- writeObj f k s maxnb
return(len+old)
maxnb = nbObjects thepdf
writePdf :: String -> PDF -> IO ()
writePdf fileName pdf =
let newpdf = computeObjectPos .
addObject "Obj1" obj1 .
addObject "Obj2" obj2 .
addObject "Obj3" obj3 .
addObject "Obj4" (obj4 pdf) .
addObject "ProcSet" obj6 .
addState "standardAlpha" standardAlpha $ pdf
in
do f <- lift $ openBinaryFile fileName WriteMode
writeText f header
len <- writeObjects f newpdf
writeEndPdf f len (addObject "ZZZ" (PdfInt len) newpdf)
`evalStateT` (0,"0000000000 65535 f \n")
where
writeEndPdf f len epdf = do writeObj f (nbObjects epdf) (PdfInt len) (nbObjects epdf)
(xrefStart,_) <- get
writeXref f epdf
writeText f (trailer epdf)
writeText f (printf "%d\n" (xrefStart::Int))
writeText f eof
lift $ hClose f