0

A little background: I wanted to use Python to dynamically generate some interesting random backgrounds, and I came across geopatterns:

https://github.com/bryanveloso/geopatterns

The library is a port of a Ruby version. I'm no expert at CSS or SVGs, but it seems like the library creates an SVG string that you can use in CSS and then CSS will magically tile/pattern the SVG onto the background of a DOM element to make big backgrounds like this: enter image description here

Now, I don't want to use this library for web development, I actually want to generate a static image, preferably with control over the height and width of the image. How can I do this?

If I just use the geopatterns example code:

import cairosvg # https://stackoverflow.com/a/60220855/11741232 for problems
from geopatterns import GeoPattern
pattern = GeoPattern('A string for your consideration.', generator='xes')
cairosvg.svg2png(bytestring=pattern.svg_string, write_to="output.png")

I get this small image: enter image description here

I found this similar question: https://stackoverflow.com/a/73078937/11741232, and modified their code a bit, but my svg string did not work with Skia, Code for that attempt:

from geopatterns import GeoPattern
from PIL import Image

import skia
import io

# Generate the pattern as an SVG string
pattern = GeoPattern('A string for your consideration.', generator='xes')
svg_string = pattern.svg_string
print(svg_string)

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(canvas, image_element, rotation):
    matrix = skia.Matrix()
    matrix.preRotate(rotation)
    canvas.drawPaint({
        'Shader': image_element.makeShader(
            skia.TileMode.kRepeat,
            skia.TileMode.kRepeat,
            matrix,
        )
    })


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


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')


img = image_from_svg(svg_string, 50)
img = pattern_image_with_title(img, 300, 300, 0)
write_png('result.png', img)

Error:

AttributeError: 'NoneType' object has no attribute 'containerSize'
kevinlinxc
  • 470
  • 5
  • 20
  • A general introduction to what the error message means can be found in [this StackOverflow question](https://stackoverflow.com/questions/8949252/why-do-i-get-attributeerror-nonetype-object-has-no-attribute-something). The actual causes in this case are probably hiding somewhere in the semantics of `MakeFromStream`. And next time, please show a full traceback, or at least indicate which line of code triggers the error. – Ture Pålsson Apr 03 '23 at 08:55

0 Answers0