在 python 中,将 SVG 转换为 PNG,同时调整大小并提高质量

Mar*_*ink 5 python svg reportlab

首先,我注意到有很多相关的问题,但是在尝试了 pyvip 和 cairo 以及其余的一天之后,即使在安装了它们似乎依赖的其他软件之后,它们都对我不起作用。例外是 svglib 和 reportlab,它很接近,但还没有完全达到目标!是我发现的最好的帖子,可能会对一些人有所帮助。

我的所有源图像都保存在 SVG 文件中。大多数应用商店要求您提供一组具有特定尺寸和质量的 PNG。所以我需要获取一个 SVG 并生成一个宽度为 w、高度为 h 以及特定 dpi 的 PNG。我想在 python 中以编程方式执行此操作。

我编写了一个几乎可以工作的函数,但是缩放和 dpi 以奇怪的方式相互作用。我使用 svglib 将 SVG 转换为 ReportLab 绘图,然后使用 reportlab 操作该绘图。与其他一些选项不同,Windows 上的安装过程很顺利。

pip install svglib
pip install reportlab
Run Code Online (Sandbox Code Playgroud)

代码如下。我检查了上面的库来获取参数,但添加了一些东西来获取特定的大小。

from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM

def svg_to_png(in_path,out_path,fmt="PNG",
    scale=None,size=None,scale_x=1,size_x=None,scale_y=1,size_y=None,
    dpi=72, bg=0xffffff):
    # Convert SVG to ReportLab drawing. 
    drawing = svg2rlg(in_path)
    # Work out scale factors
    # Scale over-rides scale_x|y, ditto size
    scale_x = scale if scale else scale_x
    scale_y = scale if scale else scale_y
    size_x = size if size else size_x
    size_y = size if size else size_y
    # Size over-rides scale
    scaling_x = size_x/drawing.width if size_x else scale_x
    scaling_y = size_y/drawing.height if size_y else scale_y
    # Scale the drawing
    drawing.width = drawing.minWidth() * scaling_x
    drawing.height = drawing.height * scaling_y
    drawing.scale(scaling_x, scaling_y)
    # Render ReportLab drawing as a PNG
    renderPM.drawToFile(drawing, out_path, fmt=fmt, dpi=dpi, bg=bg)

if __name__ == '__main__': 
    breakpoint()
    in_path = 'C:\\Users\\...\\A.svg'
    out_path = 'C:\\Users\\...\\A.png'
    svg_to_png(in_path,out_path,scale=2,dpi=72)
Run Code Online (Sandbox Code Playgroud)

只要我不考虑 dpi,该函数就可以调整大小。dpi=72 似乎是reportlab 中的通用默认值,也是问题的根源。我希望增加 dpi 能够提高 PNG 的质量(减少像素化),但它似乎所做的只是扩大画布。

输入的 SVG 当然是像素完美的。这是上述函数中的四个 PNG。

比例 1 dpi 72(暗淡 115x124 大小 8.13kb),使用所有默认值进行转换。

在此输入图像描述

比例 1 dpi 144(暗淡 230x249 大小 9.06kb),画布加倍,图片位于左下象限,忽略黑线错误。

在此输入图像描述

缩放 2 dpi 72(暗淡 230x249 大小 17.5kb),缩放正确但像素化(看眼睛)

在此输入图像描述

比例 2 dpi 144(暗淡 461x497 大小 19.8kb),画布四倍?图片翻倍了。

在此输入图像描述

有人可以建议如何正确使用reportlab将图片调整为给定的比例或尺寸,并在该固定尺寸内通过dpi提高质量。

Mar*_*ink 6

回答我自己的问题,在 svglib 中进行大量代码检查后,似乎不可能调整大小并将 dpi 增加到预定义值。这是因为 svglib 将 SVG 渲染到 ReportLab 绘图,但没有任何能力告诉它以所需的分辨率渲染 SVG,考虑到矢量的整个点是任意分辨率,这是一个遗憾。一旦进入 ReportLab,您就会被绘图的分辨率所困扰。

我切换到使用 libvips 的 pyvips。

安装起来有点麻烦,你不能只 pip install pyvips 。您必须下载libvips 软件包并将其解压缩到磁盘上保存程序的位置。然后你可以按照通常的方式 pip install pyvips 。然后,在您的 python 代码中,将 libvips /bin 添加到您的路径中,假设您不想从操作系统永久执行此操作。

然后可以使用以下函数将 SVG 转换为 PNG 并设置 PNG 的分辨率以及缩放或设置其水平宽度。

def svg_to_png(svg_path,png_path,dpi=72,scale=1,size=None):
    # Documentation
    # Ref: https://libvips.github.io/libvips/API/current/
    # Ref: https://libvips.github.io/pyvips/
    # Initialise
    debug=False
    import os
    os.environ['path'] += r';C:\programs\vips\vips-dev-8.11\bin'
    import pyvips
    # Get the image
    if size:
        image = pyvips.Image.new_from_file(svg_path,dpi=dpi,scale=1)
        if debug: print({field:image.get(field) for field in image.get_fields()})
        scale = size/image.get('width')
        image = image.resize(scale)
    else:
        image = pyvips.Image.new_from_file(svg_path,dpi=dpi,scale=scale)
    # Write the image
    if debug: print({field:image.get(field) for field in image.get_fields()})
    image.write_to_file(png_path)
Run Code Online (Sandbox Code Playgroud)

缩放时该功能可以正常工作。设置固定输出大小时,效果很好,但可能需要对输入 dpi 进行一些调整才能获得准确正确的输出 dpi。