Hi Rohan,

I wanted to generate a small animation. First I tried cairo, which is
not quite easy to build. When I saw, that this much resembles PostScript
and also generates PostScript output, I thought I can also program
PostScript directly. To that end I found your package hps easy to use:
Simple installation, Haskell 98, nicely organized. See my code at

http://code.haskell.org/~thielema/firework/src/Credits.hs

Now I found it attractive to feed GhostScript directly with the output
of hps. This way I do no longer generate a temporary postscript file of
several megatons. See the attached patch.

My idea is to generate the whole PostScript string lazily and write it
with a single writeFile call. However in order to reduce restructuring
of your code I just have replaced the IO code by code on a Monoid. That
monoid can be either an Endo monoid that creates the whole string in the
DiffList way, or the monoid can be (IO ()), where mappend = (>>). I have
only added stringFromPS to the API. If you like it, you may add
stringFromEPS in the same way. The function stringFromPS also allows us
to generate GZipped postscript on the fly.


Regards,
Henning
diff -rN -u old-postscript/Graphics/PS/PS.hs new-postscript/Graphics/PS/PS.hs
--- old-postscript/Graphics/PS/PS.hs	2010-09-14 17:12:20.000000000 +0200
+++ new-postscript/Graphics/PS/PS.hs	2010-09-14 17:12:21.000000000 +0200
@@ -1,4 +1,4 @@
-module Graphics.PS.PS (ps, eps) where
+module Graphics.PS.PS (ps, eps, stringFromPS, ) where
 
 import Graphics.PS.Pt
 import qualified Graphics.PS.Matrix as M
@@ -10,6 +10,8 @@
 import qualified Graphics.PS.Image as I
 import Data.List
 import System.IO
+import Data.Monoid (Monoid, mappend, mempty, mconcat, Endo(Endo,appEndo), )
+
 
 data PS = Name String
         | LName String
@@ -181,29 +183,35 @@
 mlist :: M.Matrix -> [Double]
 mlist (M.Matrix a b c d e f) = [a,b,c,d,e,f]
 
-bracket :: (String -> IO ()) -> String -> String -> [a] -> (a -> IO ()) -> IO ()
+infixl 1 >+>
+
+(>+>) :: Monoid m => m -> m -> m
+(>+>) = mappend
+
+bracket :: (Monoid m) =>
+    (String -> m) -> String -> String -> [a] -> (a -> m) -> m
 bracket f o c p g =
-    let h a = f a >> f " "
-    in h o >> mapM_ g p >> h c
+    let h a = f a >+> f " "
+    in h o >+> mconcat (map g p) >+> h c
 
 escape :: String -> String
 escape = concatMap (\c -> if elem c "()" then ['\\', c] else [c])
 
-put :: (String -> IO ()) -> PS -> IO ()
-put f (Name n) = f n >> f " "
-put f (LName n) = f "/" >> f n >> f " "
-put f (Op o) = f o >> f "\n"
-put f (Comment o) = f "%" >> f o >> f "\n"
-put f (Int n) = f (show n) >> f " "
-put f (Double n) = f (show n) >> f " "
-put f (String s) = f "(" >> f (escape s) >> f ") "
+put :: (Monoid m) => (String -> m) -> PS -> m
+put f (Name n) = f n >+> f " "
+put f (LName n) = f "/" >+> f n >+> f " "
+put f (Op o) = f o >+> f "\n"
+put f (Comment o) = f "%" >+> f o >+> f "\n"
+put f (Int n) = f (show n) >+> f " "
+put f (Double n) = f (show n) >+> f " "
+put f (String s) = f "(" >+> f (escape s) >+> f ") "
 put f (Array a) = bracket f "[" "]" a (put f)
 put f (Proc p) = bracket f "{" "}" p (put f)
 put f (Matrix m) = put f (Array (map Double (mlist m)))
 put f (Dict d) =
     let g = concatMap (\(a,b) -> [a,b])
     in bracket f "<<" ">>" (g d) (put f)
-put f (Seq a) = mapM_ (put f) a
+put f (Seq a) = mconcat (map (put f) a)
 
 ps' :: (I.Image, Int) -> PS
 ps' (p, n) = Seq [page "Graphics.PS" n,  image p, showPage]
@@ -215,40 +223,51 @@
 
 -- | Write a postscript file.  The list of images are written
 --   one per page.
-ps :: String -> P.Paper -> [I.Image] -> IO ()
-ps f d p = do
-  h <- openFile f WriteMode
-  let g = put (hPutStr h)
+ps :: FilePath -> P.Paper -> [I.Image] -> IO ()
+ps f d p =
+  writeFile f (stringFromPS f d p)
+
+stringFromPS :: String -> P.Paper -> [I.Image] -> String
+stringFromPS t d p =
+  let g = put (\s -> Endo (s++))
       (P.Paper width height) = paper_ps d
-  mapM_ g [header
-          ,title ("Graphics.PS: " ++ f)
-          ,creator "Graphics.PS"
-          ,languageLevel 2
-          ,pages (length p)
-          ,documentMedia "Default" width height
-          ,endComments
-          ,prolog]
-  mapM_ g (map ps' (zip p [1..]))
-  mapM_ g [trailer
-          ,eof]
-  hClose h
-
+  in  flip appEndo "" $
+      g header >+>
+      g (title t) >+>
+      g (creator "Graphics.PS") >+>
+      g (languageLevel 2) >+>
+      g (pages (length p)) >+>
+      g (documentMedia "Default" width height) >+>
+      g endComments >+>
+      g prolog >+>
+      mconcat (map (g . ps') (zip p [1..])) >+>
+      g trailer >+>
+      g eof
+
+
+newtype MonadMonoid m = MonadMonoid {appMonadMonoid :: m ()}
+
+instance Monad m => Monoid (MonadMonoid m) where
+   mempty = MonadMonoid (return ())
+   mappend (MonadMonoid a) (MonadMonoid b) =
+      MonadMonoid (a >> b)
+   mconcat = MonadMonoid . mapM_ appMonadMonoid
 
 -- | Write an encapsulated postscript file.  The single image
 --   is written.
 eps :: String -> P.BBox -> I.Image -> IO ()
-eps f d p = do
-  h <- openFile f WriteMode
-  let g = put (hPutStr h)
-  mapM_ g [headerEps
-          ,title ("Graphics.PS: " ++ f)
-          ,creator "Graphics.PS"
-          ,languageLevel 2
-          ,bbox d
-          ,endComments
-          ,prolog
-          ,image p]
-  hClose h
+eps f d p =
+  withFile f WriteMode $ \h ->
+     let g = put (MonadMonoid . hPutStr h)
+     in  mapM_ (appMonadMonoid . g) $
+            [headerEps
+            ,title ("Graphics.PS: " ++ f)
+            ,creator "Graphics.PS"
+            ,languageLevel 2
+            ,bbox d
+            ,endComments
+            ,prolog
+            ,image p]
 
 {--
 

_______________________________________________
haskell-art mailing list
haskell-art@lurk.org
http://lists.lurk.org/mailman/listinfo/haskell-art

Reply via email to