使用 libcairo 和 Haskell 的多页 SVG
我正在编写的应用程序使用 libcairo 来输出矢量图形;对于支持多页(PDF、PostScript)的输出格式,一切正常,但我还想支持 SVG 和光栅图像格式。
我目前只是使用 showPage
推送页面,否则会溢出底部边距,并且我希望保持代码的这种结构。我提出了两个理论上可能的解决方案:
a) 一个辅助 monad,它包裹了 Cairo 的 Render monad,但提供了一个flushPage 操作,当链接到它时,将推动当前渲染操作到内部页面堆栈上,一个 liftRender
操作,通过将它链接到先前缓冲的操作上,将一个 Render
操作提升到 monad 中,以及一个用于提取 Render ()
操作列表的辅助函数,每个页面一个。因此,我只需调用我的主渲染函数,但它不会返回 Render () 操作,而是返回一个分页包装器操作,然后我将从中提取各个页面并处理它们 - 对于多个页面对于页面格式,我可以简单地将它们链接在一起,在它们之间插入 showPage
操作,而对于单页格式,我会单独渲染它们。例如,它的外观如下:
-- 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) cairo 表面类型,其外部行为类似于多页文档,但在外部生成一系列单页文档。这将是理想的,因为它根本不需要对渲染代码进行任何更改,但我不确定是否可以在不干扰源代码级别的 cairo 本身的情况下做到这一点。
那么,实际的问题是:上述解决方案是否已经存在?例如,是否有人编写过“分页包装器 monad”或“多页 SVG 表面”?并且,如果答案是“否”;其中哪一个更可取,您将如何实施它?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果有人感兴趣的话,我在 #haskell 的朋友们的友好帮助下找到了答案。
我的渲染函数返回 Render [Render ()],而不是编写自定义包装器 monad。我递归地渲染片段,传递一些状态,并且在每次迭代时,我检查当前操作是否会溢出当前页面。如果是,则递归调用会追加一个新页面并重试;否则,它将当前操作链接到首页。结果是一个
Render ()
操作列表,每个页面一个。然后,主函数从渲染函数的结果中获取这些
Render()
操作。然后它检查所需的输出格式;如果它是像 PostScript 或 PDF 这样的多页面格式,它只是将操作链接在一起,在它们之间插入showPage
操作。如果它是单页面格式,它会为每个页面创建一个新的渲染表面,并在其上渲染一个页面操作。第一页兼作初始渲染调用的上下文。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, insertingshowPage
actions 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.如果您不必打印“doc”,则可以将多层 svg“g”节点显示为页面。
要分页,可见性可以打开或关闭。
如果印刷过程有智慧理解这一点,那么印刷是可能的吗?
马克特
If you don't have to print the 'doc' then multiple layers of svg 'g' nodes could be made visible as pages.
To paginate, then the visibility could be on or off.
If a printing process has smarts to understand this, then printing is possible?
MarkT