The application I’m writing uses libcairo to output vector graphics; everything works fine for output formats that support multiple pages (PDF, PostScript), however I would like to also support SVG and raster image formats.
I’m currently simply pushing pages using showPage whenever I would otherwise overflow the bottom margin, and I would like keeping the code structured this way. I have come up with two theroretically possible solutions:
a) A helper monad that wraps around Cairo’s Render monad, but provides a flushPage action which, when chained into it, pushes the current Render action onto an internal page stack, a liftRender action that would, well, lift a Render action into the monad by chaining it onto the previously buffered action, and a helper function to extract a list of Render () actions, one for each page. So I would simply call my main rendering function, but instead of a Render () action, it would return a pagination-wrapper action, from which I would then extract individual pages and process them – for multi-page formats, I could simply chain them together, inserting showPage actions between them, while for single-page formats, I would render them individually. As an example, here’s what it would look like:
-- original code
renderMe :: Render ()
renderMe = do
newPath
moveTo 10 10
lineTo 20 20
lineTo 10 30
lineTo 10 10
fill
showPage
newPath
moveTo 10 10
lineTo 20 20
lineTo 10 30
lineTo 10 10
fill
-- new code
renderPages :: PagedRender ()
renderPages = do
liftRender (do
newPath
moveTo 10 10
lineTo 20 20
lineTo 10 30
lineTo 10 10
fill)
flushPage
liftRender (do
newPath
moveTo 10 10
lineTo 20 20
lineTo 10 30
lineTo 10 10
fill)
flushPage
b) A cairo surface type that acts like a multi-page document on the outside, but produces a series of single-page documents on the outside. This would be ideal, since it wouldn’t require any changes to the rendering code at all, but I’m not sure if it is possible to do this without messing with cairo itself at the source level.
So, the actual question: Does any of the above solutions already exist? As in, has anyone written either a ‘pagination wrapper monad’ or a ‘multi-page SVG surface’? And, in case the answer is ‘no’; which one of these is preferable, and how would you go about implementing it?
In case anyone is interested, I figured it out thanks to some friendly help from the guys at #haskell.
Instead of writing a custom wrapper monad, my render function returns
Render [Render ()]. I render fragments recursively, passing some state along, and on each iteration, I check whether the current operation would overflow the current page. If it would, then the recursive call appends a new page and tries again; otherwise, it chains the current operation onto the top page. The result is a list ofRender ()actions, one for each page.The main function, then, takes these
Render()actions out of the result of the render function. Then it checks the desired output format; if it’s a multi-page format like PostScript or PDF, it simply chains the actions together, insertingshowPageactions between them. If it’s a single-page format, it creates a new render surface for each page, and renders one page action onto it. The first page doubles as the context for the initial render call.