Search code examples
pythonsvgpython-imaging-library

Filling color in svg image


I have an svg file in this format:

<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48">
  <path d="M11.45 43.25Q9.5 43.25 8.125 41.875Q6.75 40.5 6.75 38.55V19.7Q6.75 18.6 7.25 17.6Q7.75 16.6 8.65 15.95L21.15 6.55Q21.8 6.1 22.55 5.85Q23.3 5.6 24 5.6Q24.75 5.6 25.475 5.85Q26.2 6.1 26.85 6.55L39.35 15.95Q40.25 16.65 40.775 17.625Q41.3 18.6 41.3 19.7V38.55Q41.3 40.5 39.9 41.875Q38.5 43.25 36.55 43.25H28.3V27.7H19.7V43.25Z"/>
</svg>

This vector image looks like this:
an svg image of a "home" icon

To fill the SVG svg's all "black" places (path) with solid color of specified hex code I used this:

Example: 'svg42.svg' to be filled with solid color '#00bbaa'

import io

from PIL import Image
from cairosvg import svg2png
from lxml import etree


def svg2image(file: str, color: str, width: int, height: int):
        with open(f'{path}', 'r') as f:
        svg_data = f.read().encode('utf-8')

    root = etree.fromstring(svg_data)
    tree = etree.ElementTree(root)

    root.attrib["fill"] = color

    for path in root.iter('path'):
        path.attrib["fill"] = color

    imgdata = io.BytesIO()
    tree.write(imgdata)

    img = Image.open(io.BytesIO(svg2png(bytestring=imgdata.getvalue(), output_width=width, output_height=height)))

    return img

Problem:

when trying to fill the color in a more complex svg, it shows no effect on the output:

<svg width="989" height="875" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     xml:space="preserve" overflow="hidden"><defs><clipPath id="clip0"><rect x="902" y="586" width="989" height="875"/></clipPath></defs>
    <g clip-path="url(#clip0)" transform="translate(-902 -586)"><rect x="902" y="586" width="639" height="652" fill="#ED7D31"/>
        <rect x="1252" y="810" width="639" height="651" fill="#4472C4"/></g></svg>

What am I missing here?


Solution

  • You have hardcoded the names of the structure of the XML file. In the first file, the data is in an element called path, so you use root.iter('path').

    However, in the second file, there is no element path in the XML, so the loop over it is over an empty iterator. If you really want to color all elements in the given color, you should not give an optional filter in the loop.

    for path in root.iter():
        path.attrib['fill'] = color
    

    enter image description here