pytroll / aggdraw

Python package wrapping AGG2 drawing functionality

Home Page:https://aggdraw.readthedocs.io/en/latest/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add svg support from agg-svg

dov opened this issue · comments

A few years back I extended the svg support of agg to be a bit more usable, see http://github/dov/agg-svg . It should be trivial to add a wrapper to aggdraw for this. Note that this adds one more dependency, expat. The question is whether this should be included in aggdraw, or perhaps in another module.

How would the cpp/h files need to be compiled? The installation section says it gets dropped in to the source tree. So this links against an existing agg build and provides a new SVG interface or adds/modifies the existing SVG interface? Without fully understanding the interface I see two options:

  1. Add these to aggdraw, compile them if expat can be found.
  2. Create a separate package (that could be added to conda-forge for easier installation: https://conda-forge.org/) and compile the relevant parts of aggdraw.cxx based on a MACRO set from setup.py (if the library can be found or not).

I think I prefer number 1, but would want to do it where it was clear that these files are not part of the base AGG project. This may be easier too if we turned aggdraw in to a "full" python package where the aggdraw.cxx is just one submodule/subpackage. Would be easier to include tests in the released distribution, but would increase the size of the project too. Would make it much more flexible for future changes though.

The svg parser returns an agg::path_renderer object that really simply is a path container. If you wrap this object into python it really has the same semantics as the current Symbol. So draw.symbol() could be overloaded to recognize path renderer objects. This makes it possible e.g. to create an object in inkscape and then load it and copy and paste it multiple times in the "canvas". We still need to figure out how to transform it though, but perhaps draw.symbol can simply take a transformation object...

expat is public domain if I'm mistaken and quite small, so just dropping it into the sources is really not a problem. It is probably an overkill to put it in a different module.

This would be a great addition. I’ve been looking for a good simple SVG-to-Pillow solution for a while now (I want to be able to load vector images at a given arbitrary size and then draw them to the screen with PyOpenGL), and having the functionality rolled into aggdraw would make everything very easy. Would this be able to correctly render most SVG files, or are there any common SVG features that aren’t supported yet?

Please see the https://github.com/dov/aggdraw/tree/svg for a first implementation of svg support.

Regarding the coverage of the SVG support, it is indeed quite limited, as dictated by the https://github.com/dov/agg-svg project. If there is some particular file that doesn't render, feel free to open a bug report about it in agg-svg. The major feature missing though is text. There is no text support whatsoever.

Here is an example of what you can do with it. (Note that I'm also making use of https://github.com/dov/pyeuclid for matrix calculations.)

import math
import euclid
import aggdraw
from aggdraw import Draw,Brush,Svg
from PIL import Image

def euclid2agg(t):
    '''Extract the euclid affine coordinates in the proper order for agg'''
    return (t[0],t[1],t[3],t[4],t[6],t[7])

im = Image.new("RGB", (1024, 1024))
draw = Draw(im)
brush = Brush('#44c')
draw.rectangle((0, 0, im.size[0], im.size[1]), brush)

svg = Svg(open('tux.svg').read())
br = svg.bounding_rect()
center = euclid.Point2(0.5*(br[0]+br[2]),0.5*(br[1]+br[3]))
brush = Brush('red')
draw.svg((500-center[0],500-center[1]),svg)
s = 0.3
for i in range(16):
    angle = math.radians(360./16*i)
    t = (euclid.Matrix3.new_identity()
        .scale(s,s)
        .rotate_around(angle, center[0], center[1])
        .translate(0, -1300)
        .pre_translate(-center[0]*s+500, -center[1]*s+500)
        )
    draw.svg(euclid2agg(t),svg)
draw.flush()
im.save('tuxes.png')

print('ok')

And here is the result:

image