【问题标题】:Convert SVG to PNG with CSS3 matrix3d transformation support使用 CSS3 matrix3d 转换支持将 SVG 转换为 PNG
【发布时间】:2015-09-22 01:38:48
【问题描述】:

我需要从 Python 脚本将 SVG 转换为 PNG。有很多工具可以做到这一点,我尝试过的那些(我在 Ubuntu 上):

  • inkscape(Inkscape 命令行)
  • rsvg-convert 来自librsvg2-bin
  • convert(即 ImageMagick)

但这些都不支持 CSS3 transform: matrix3d(...)。到目前为止,我发现的唯一支持此功能的软件是 Firefox/Chrom[ium]/etc,但我似乎不允许命令行渲染为 PNG。

我是否可以将任何特殊选项传递给上述选项之一以获得完全支持 CSS3 的渲染? 或者是否还有其他我目前不知道的转换选项?


编辑 我现在尝试了更多工具,包括:
  • wkhtmltoimage 部分wkhtmltopdf
  • nodeshot(在本地服务器上提供 SVG 并下载最终图像需要大量管道)

可能,虽然我无法测试,因为它只是 OS X

  • webkit2png(感谢@Mark Setchell 的建议)

以上所有都不符合我的要求,因为它们是基于 WebKit 和 WebKit just doesn't support matrix3d in SVG 的,尽管它确实可以完美地应用于常规元素...

【问题讨论】:

  • 只是一个想法,因此是评论而不是答案,但是您可能会发现,如果将文件放在本地 Web 服务器下,则可以使用 webkit2png 正确呈现它。未经测试!
  • 好建议!不幸的是,它是OS X only。但这让我想到了基于 WebKit 的方法,所以我遇到了nodeshot——目前是broke。如果/当它得到修复,我会在这里发布更新。
  • webkit2png 在 OS X 上可用 - 使用 homebrew 轻松安装...就像这样 brew install webkit2png
  • 不,正如我在 OS X 上的问题中指出的那样,我 not。我最新的猜测是 wkhtmltopdf,尽管有这个名字,但它会以 wkhtmltoimage 导出到 PNG .结果和直接在 Chromium 中打开 SVG 一样好,但还是错了。

标签: python css svg png image-conversion


【解决方案1】:

万岁,我找到了一个解决方案 - 但它的开销很大并且需要大量的东西:

基本思想是将您的 SVG 用作网站,然后使用 Firefox 的脚本版本 SlimerJS 呈现它。与提到的其他方法不同,它使用Gecko 来渲染 SVG,并且如上所述,Gecko (unlike WebKit) 在 SVG 中正确渲染 CSS3 3D 旋转。

您可能也想使用xvfb,因此您不必在渲染时看到 SlimerJS 窗口(它本身还不支持无头)。

在本地服务器上提供 SVG/HTML

首先,您需要将 SVG 作为 HTML 页面中的图像提供。内联 SVG 或直接 SVG 对我不起作用。我推荐http.server.BaseHTTPRequestHandler,同时提供 HTML 和纯 SVG(将在第二个请求中请求)。

html = """
<!DOCTYPE HTML>
<html>
    <head>
        <style>
            body {
                margin: 0;
            }
        </style>
    </head>
    <body>
        <img src="http://localhost:8000/svg/%s" />
    </body>
</html>
""" % svg_name

margin: 0; 删除任何网站周围的默认空间。

我以Threaddeamon=True 启动服务器,因此一旦我的脚本完成,它将关闭。

class SvgServer:
    def __init__(self):
        self.server = http.server.HTTPServer(('', PORT), SvgRequestHandler)
        self.server_thread = threading.Thread(target=self.server.serve_forever, daemon=True).start()

SvgRequestHandler 应该是您的 BaseHTTPRequestHandler 实例

(我认为有 - 或将会有 - 一种直接从 SlimerJS 访问文件的方法,因为 Firefox 可以使用 file:// 执行此操作,但我无法让它工作。那么这一步将变得过时。)

用 SlimerJS 渲染它

现在浏览器可以访问 SVG,我们可以调用 SlimerJS。 SlimerJS 只接受 JavaScript 文件作为输入,所以我们最好生成一些 JavaScript:

slimer_commands = """
var webpage = require('webpage').create();
webpage
  .open('%s')
  .then(function () {
    webpage.viewportSize = { width: 1920, height: 1080 };
    webpage.render('%s', { onlyViewport: true });
    slimer.exit()
  });
""" % (url_for_html_embedding_svg, output_file_name)

奖励:使用 Promises 进行批处理,与为我们要渲染的每个 SVG 启动单独的 SlimerJS 相比,这要快得多。我个人使用索引 SVG,根据需要进行更改。

slimer_command_head = "const { defer } = require('sdk/core/promise');" \
                      "var webpage = require('webpage').create();" \
                      "webpage.viewportSize = { width: 1920, height: 1080 };" \
                      "var deferred = defer();" \
                      "deferred.resolve();" \
                      "deferred.promise.then(function () {"

commands = [slimer_command_head]

for frame_index in range(frame_count):
    command = "return webpage.open('%s'); }).then(function () { webpage.render('%s', { onlyViewport: true });" % (
        'http://localhost:8000/html/%d' % frame_index,
        FileManagement.png_file_path_for_frame(frame_index)
    )

    commands.append(command)

commands.append("slimer.exit(); });")

slimer_commands = ''.join(commands)

现在我们已经准备好脚本,将其保存到临时文件并执行它:

with tempfile.NamedTemporaryFile(suffix='.js') as slimer_file:
    slimer_file.write(bytes(slimer_commands, 'UTF-8'))
    slimer_file.flush()

    command = [
        SLIMER_EXECUTABLE,
        os.path.abspath(slimer_file.name)
    ]

    if run_headless:
        command.insert(0, 'xvfb-run')

    os.system(' '.join(command))

run_headless 选项将 XVFB 命令前置到 headless

你已经完成了。

这很简单、快速、直接,不是吗?

如果您无法真正遵循代码 sn-ps,请查看the source code of the project I used it for

【讨论】:

  • 请注意安全隐患——如果您的 SVG 来自不受信任的来源,这样做可能非常危险。
  • 您能否概述一下您在那里看到的具体危害?但正如示例所示,所有 SVG 都来自 localhost。
  • 重点是,例如从 localhost 提供的 Javascript 可能比从其他地方提供的 javascript 具有更高的权限;此外,跨来自同一域(在您的情况下为 localhost)的多个同时 SVG 服务,应用不太严格的跨脚本攻击缓解规则。这有点理论上的风险,但如果有人要在你的 SVG 中嵌入东西,请确保这些东西不会做有害的事情,比如从你的机器上读取其他 SVG 并将它们渲染成同一个 PNG!
猜你喜欢
  • 1970-01-01
  • 2015-10-04
  • 2015-04-06
  • 1970-01-01
  • 2020-12-03
  • 2018-09-07
  • 1970-01-01
  • 2019-02-01
  • 2011-09-09
相关资源
最近更新 更多