4

I have an SVG graphic represented in a string

svg_string='<svg height="100" width="500"><ellipse cx="240" cy="50" rx="220" ry="30" style="fill:yellow" /><ellipse cx="220" cy="50" rx="190" ry="20" style="fill:white" /></svg>'

I want to display the graphics represented by svg_string, which is 2 ellipses, on a window initiated from Python.

The pseudo code of which should be something like

import pygame


def display_svg_figure(screen, svg_string):
    #   Code that draws the rendered SVG string on
    #   the screen
    pass

background_colour = (255, 255, 255)
(width, height) = (900, 900)

screen = pygame.display.set_mode((width, height))
pygame.display.set_caption('Display SVG')
screen.fill(background_colour)

pygame.display.flip()

svg_string='<svg height="100" width="500"><ellipse cx="240" cy="50" rx="220" ry="30" style="fill:yellow" /><ellipse cx="220" cy="50" rx="190" ry="20" style="fill:white" /></svg>'
display_svg_figure(screen, svg_string)


running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

I am basically looking at an implementation of the method display_svg_figure(screen,svg_string)

I am open to a using non pygame libraries too as long as it follows the simple interfaces of the following and is possible to include it in pygame

from SOME_LIBRARY import SOME_COMPONENT

svg_string="......"
SOME_COMPONENT.display(svg_string)

I have already looked at pynanosvg but that's no longer supported and is failing so does not suffice my need.

The solution given in Display SVG file in Python does not seem to fit the bill as it is not working in PyGame. If someone can make it work kindly let me know.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Raj Oberoi
  • 654
  • 1
  • 7
  • 18
  • To be clear, do you want to display the _text_ of the SVG string, and not the graphic it represents? If so, the solution wouldn't care if the string was in SVG format or not. – Peter O. Jan 10 '21 at 03:45
  • @PeterO. To clarify it should render the graphics it represents. For instance I need the output to be a window showing 2 ellipses as mentioned in the svg_string. I have edited the initial query to bring my ask out more clearly. – Raj Oberoi Jan 10 '21 at 04:25

1 Answers1

6

How to read an Scalable Vector Graphics (SVG) file is answered at SVG rendering in a PyGame application.


SVG files are supported with Pygame version 2.0. Since Version 2.0.2, SDL Image supports SVG (Scalable Vector Graphics) files (see SDL_image 2.0). Therefore, with pygame version 2.0.1, SVG files can be loaded into a pygame.Surface object with pygame.image.load() from a stream (io.BytesIO):

svg_string = '<svg height="100" width="500"><ellipse cx="240" cy="50" rx="220" ry="30" style="fill:yellow" /><ellipse cx="220" cy="50" rx="190" ry="20" style="fill:white" /></svg>'
pygame_surface = pygame.image.load(io.BytesIO(svg_string.encode()))

Minimal example:

import pygame
import io

pygame.init()
window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()

svg_string = '<svg height="100" width="500"><ellipse cx="240" cy="50" rx="220" ry="30" style="fill:yellow" /><ellipse cx="220" cy="50" rx="190" ry="20" style="fill:white" /></svg>'
pygame_surface = pygame.image.load(io.BytesIO(svg_string.encode()))

run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    window.fill((255, 255, 255))
    window.blit(pygame_surface, pygame_surface.get_rect(center = window.get_rect().center))
    pygame.display.flip()

pygame.quit()
exit()

Before Pygame 2, you had to implement Scalable Vector Graphics loading with other libraries. There are multiple possibilities to render a SVG string.

A simple possibility is to use svglib. Install svglib:

pip install svglib

Write a function that parses and rasterizes an SVG string and and creates a pygame.Surface object:

from svglib.svglib import svg2rlg
import io

def load_svg(svg_string):
    svg_io = io.StringIO(svg_string)
    drawing = svg2rlg(svg_io)
    str = drawing.asString("png")
    byte_io = io.BytesIO(str)
    return pygame.image.load(byte_io)

However, there seems to be a problem with transparent backgrounds. There is an issue about this topic How to make the png background transparent? #171.

An alternative solution (which is apparently slower) is to use CairoSVG. With the function cairosvg.svg2png, an Vector Graphics (SVG) files can be directly converted to an [Portable Network Graphics (PNG)] file

Install CairoSVG.

pip install CairoSVG

Write a function that converts a SVF file to a PNG (ByteIO) and creates a pygame.Surface object may look as follows:

import cairosvg
import io

def load_svg(filename):
    new_bites = cairosvg.svg2png(url = filename)
    byte_io = io.BytesIO(new_bites)
    return pygame.image.load(byte_io)

See also svgsurf.py.


Minimal example:

from svglib.svglib import svg2rlg
import pygame
import io

def load_svg_svglib(svg_string):
    svg_io = io.StringIO(svg_string)
    drawing = svg2rlg(svg_io)
    str = drawing.asString("png")
    byte_io = io.BytesIO(str)
    svg_surf = pygame.image.load(byte_io)
    return svg_surf

def display_svg_figure(screen, svg_string):
    surf = load_svg_svglib(svg_string)
    screen.blit(surf, (0, 0))

background_colour = (255, 255, 255)
(width, height) = (900, 900)

screen = pygame.display.set_mode((width, height))
pygame.display.set_caption('Display SVG')
screen.fill(background_colour)

svg_string='<svg height="100" width="500"><ellipse cx="240" cy="50" rx="220" ry="30" style="fill:yellow" /><ellipse cx="220" cy="50" rx="190" ry="20" style="fill:white" /></svg>'
display_svg_figure(screen, svg_string)

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    pygame.display.flip()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174