使用 libcairo 和 Haskell 的多页 SVG

发布于 2024-12-05 09:33:21 字数 1419 浏览 2 评论 0原文

我正在编写的应用程序使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

等风也等你 2024-12-12 09:33:21

如果有人感兴趣的话,我在 #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 of Render () 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, inserting showPage 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.

给妤﹃绝世温柔 2024-12-12 09:33:21

如果您不必打印“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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文