使用CSS3 matrix3d转换支持将SVG转换为PNG

时间:2015-07-04 11:09:19

标签: python css3 svg png image-conversion

我需要从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,尽管它确实适用于常规元素......

1 个答案:

答案 0 :(得分:0)

万岁,我找到了解决方案 - 但它有巨大的开销,需要大量的东西:

基本思想是将SVG作为一个网站提供,然后使用SlimerJS(一个可编写脚本的Firefox版本)进行渲染。与上面提到的其他方法不同,它使用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;删除任何网站周围的默认空间。

我使用Thread将服务器作为deamon=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)

Bonus:使用Promise进行批处理,与为每个要渲染的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

你已经完成了。

这很容易,快速而直接,不是吗?

如果您无法真正关注代码段,请查看the source code of the project I used it for