首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何将PIL和cairosvg结合起来创建svg模式?

如何将PIL和cairosvg结合起来创建svg模式?
EN

Stack Overflow用户
提问于 2022-07-17 16:18:16
回答 2查看 278关注 0票数 3

我想知道是否有可能加载svg格式,比如shape和with循环来重复for循环,以获得某种通用模式。我创建的大部分在线研究是从svg转换到png或其他格式,但我想知道在转换到某种格式(jpg)之前是否可以进行操作?

我试着把cairosvg和PIL结合起来,但我并没有走得太远。

代码语言:javascript
运行
复制
from cairosvg import svg2png
from PIL import Image, ImageDraw

white = (255,255,255)
img = Image.new('RGB', (300,300), white)
draw = ImageDraw.Draw(img)

svg_code = """
    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
        <circle cx="12" cy="12" r="10"/>
        <line x1="12" y1="8" x2="12" y2="12"/>
        <line x1="12" y1="16" x2="12" y2="16"/>
    </svg>
"""

通常我会用这样的东西..。

代码语言:javascript
运行
复制
for x in range(0, 300, 25):
    draw.svg_code??

但这行不通..。知道如何加载svg格式并与其他模块一起使用吗?

提前感谢!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-07-22 10:34:40

需求

  • 以svg格式加载形状
  • 创建背景模式
  • 在用PIL转换为jpg之前进行操作

Skia

Google,Android,Flutter等使用开源的2D图形库Skia,其中也有Python绑定。

Skia允许创建带有平铺模式规则的着色器来填充绘制区域。此外,可以提供允许例如旋转的矩阵。优点是,即使目标图像很大且构成模式的元素数量非常多,执行效率也非常高。

因此,可以很容易地创建这样的模式,例如使用以下代码:

代码语言:javascript
运行
复制
def pattern(canvas, image_element, rotation):
    matrix = skia.Matrix()
    matrix.preRotate(rotation)
    canvas.drawPaint({
        'Shader': image_element.makeShader(
            skia.TileMode.kRepeat,
            skia.TileMode.kRepeat,
            matrix,
        )
    })

因此,一个完整的方法可能如下所示:

  • 将SVG字符串(或文件)转换为大小接近的Skia图像
  • 创建模式作为背景
  • 通过绘制其他元素(如矩形/文本)来操作它
  • 将其转换为PIL映像并保存

许多用例的一个重要特性是SVG绘图是在透明的背景下完成的。

但是,JPEG不支持透明性。您可以使用RBGA将图像从具有透明度的类型( .convert('RGB') )转换为没有透明度的类型。但在这种情况下,黑色背景上将有黑色元素。转换时,可以在预置的背景下创建新图像,并将透明图像粘贴到图像中。

自带的Python示例

考虑到上述各点的独立示例可能类似于以下程序。

它只是在SVG示例的基础上创建一个模式,并在上面的灰色矩形上绘制一个红色文本。它输出两幅图像:一次是所需的JPEG,也是PNG,以便能够显示上述透明度选项。

代码语言:javascript
运行
复制
import io
import skia
from PIL import Image


def image_from_svg(svg, element_size):
    stream = skia.MemoryStream()
    stream.setMemory(bytes(svg, 'UTF-8'))
    svg = skia.SVGDOM.MakeFromStream(stream)
    width, height = svg.containerSize()
    surface = skia.Surface(element_size, element_size)
    with surface as canvas:
        canvas.scale(element_size / width, element_size / height)
        svg.render(canvas)
    return surface.makeImageSnapshot()


def pattern_image_with_title(image_element, width, height, rotation, title):
    surface = skia.Surface(width, height)
    with surface as canvas:
        pattern(canvas, image_element, rotation)
        rectangle(canvas, width)
        draw_title(canvas, title)
    return surface.makeImageSnapshot()


def draw_title(canvas, title):
    paint = skia.Paint(AntiAlias=True, Color=skia.ColorRED)
    canvas.drawString(title, 48, 76, skia.Font(None, 32), paint)


def rectangle(canvas, width):
    rect = skia.Rect(32, 32, width - 32, 96)
    paint = skia.Paint(
        Color=skia.ColorGRAY,
        Style=skia.Paint.kFill_Style)
    canvas.drawRect(rect, paint)


def pattern(canvas, image_element, rotation):
    matrix = skia.Matrix()
    matrix.preRotate(rotation)
    canvas.drawPaint({
        'Shader': image_element.makeShader(
            skia.TileMode.kRepeat,
            skia.TileMode.kRepeat,
            matrix,
        )
    })


def write_png(file_name, skia_image):
    with io.BytesIO(skia_image.encodeToData()) as f:
        pil_image = Image.open(f)
        pil_image.save(file_name, 'PNG')


def write_jpeg(file_name, skia_image, background):
    with io.BytesIO(skia_image.encodeToData()) as f:
        pil_image = Image.open(f)
        new_image = Image.new("RGBA", pil_image.size, background)
        new_image.paste(pil_image, (0, 0), pil_image)
        new_image = new_image.convert('RGB')
        new_image.save(file_name, 'JPEG')


svg_code = """
    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
        <circle cx="12" cy="12" r="10"/>
        <line x1="12" y1="8" x2="12" y2="12"/>
        <line x1="12" y1="16" x2="12" y2="16"/>
    </svg>
"""

if __name__ == "__main__":
    img = image_from_svg(svg_code, 50)
    img = pattern_image_with_title(img, 300, 300, 45, 'hello world!')
    write_png('result.png', img)
    write_jpeg('result.jpg', img, 'WHITE')

结果

作为上述程序的结果,您将得到两幅图像。左边是带有白色背景的JPEG图像,右边是具有透明度的PNG图像。

票数 2
EN

Stack Overflow用户

发布于 2022-07-22 10:59:03

我知道这个问题被标记为ImageMagick-based,但是我提供了一个python的答案,因为:

  • 您可能不知道这里不需要Python,而且有更简单的解决方案。
  • 您/我们可以使用轻松地将下面的代码移植到魔棒,如果您愿意的话,这是一个绑定到ImageMagick魔棒类型。

请注意,如果您希望看到一些示例,可以通过将wand放入[wand]搜索框中,找到标记为StackOverflow的问题和答案。

因此,如果将SVG保存在名为image.svg的文件中,则可以在终端(Linux/macOS)或命令提示符(Windows)中运行以下ImageMagick命令:

代码语言:javascript
运行
复制
magick -background none image.svg -write mpr:tile +delete \
       -size 400x200 tile:mpr:tile result.gif

或者:

代码语言:javascript
运行
复制
magick -background yellow image.svg -write mpr:tile +delete \
       -size 400x200 tile:mpr:tile result.gif

或者,如果你想要一个特定数量的拷贝,比如21张“toto”重复的每一行3张图像:

代码语言:javascript
运行
复制
magick -background magenta image.svg -duplicate 20 miff: | magick montage -tile 3x -geometry +0+0 miff:- result.gif

或20 "in toto",间隔稍小,排列成2排:

代码语言:javascript
运行
复制
magick -background cyan image.svg -duplicate 19 miff: | magick montage -background cyan -tile x2 -geometry +5+5 miff:- result.gif

如果在上面的命令中有一个旧的ImageMagick,,则需要:

  • magick montage替换为montage,并且
  • 将普通magick替换为convert

请注意,有很多有趣的材料关于瓷砖/重复背景安东尼蒂森这里

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73013439

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档