haskellprintinggtk2hs

How do I render a Cairo drawing to a printer in Haskell gtk2hs


I'm using the GTK3 build of gtk2hs. I have a Cairo drawing in the Render monad and I want to send it to a printer.

I see that there is a Surface type which encapsulates the Cairo back end. For example it is possible to create an SVG surface using withSVGSurface, and there are similar functions for PDF, Postscript and PNG. Once you have a Surface you can apply a Render action (i.e. actually draw on it) using renderWith. This is perfectly straightforward, and I can see how to use these functions to export drawings as files.

However printing does not seem to work this way. The printOptDrawPage signal provides its callback with a printContext value. This has a function printContextGetCairoContext which returns a Cairo Context. However the library documentation doesn't have an entry for this Context type, and I can't find any functions that use it.

It looks like there ought to be printContextGetSurface function, or else a way to convert a Context into a Surface. Am I missing something?


Solution

  • Hah, whoops, that's embarrassing! It seems the bindings are just a little incomplete in this department.

    Luckily it should be pretty easy to update them. Taking a look at the definition of the Render monad:

    newtype Render m = Render { runRender :: ReaderT Cairo IO m }
    

    We can see that the Cairo object you get from printContextGetCairoContext is just what you need to do something useful with a Render action. The implementation of the renderWith function gives you a clue about what cleanup actions you should take:

    renderWith surface (Render m) = liftIO $
      bracket (Internal.create surface)
              (\context -> do status <- Internal.status context
                              Internal.destroy context
                              unless (status == StatusSuccess) $
                                fail =<< Internal.statusToString status)
              (\context -> runReaderT m context)
    

    I think one of two patches would be sensible here:

    1. Expose a renderWith-alike that consumes a Cairo. Let the user connect up printContextGetCairoContext with the new renderWith-alike.
    2. Don't expose printContextGetCairoContext at all; replace it with

      printContextRender :: PrintContextClass self => self -> Render a -> IO a
      

      and have printContextRender merge the call to printContextGetCairoContext with the renderWith-style cleanup.

    I like option (1) for its clean backwards-compatibility story; but I like (2) a lot better from the API-design side of things. Since this module probably hasn't seen much use for the reasons you're describing, I'd lean towards patch (2).

    I'd also note that you may want to look into the gtk documentation a bit to check whether the Cairo context cleanup is somebody else's responsibility (the PrintOperation's, for example).

    Happy hacking!