svglib icon indicating copy to clipboard operation
svglib copied to clipboard

How to make the png background transparent?

Open gotounix opened this issue 6 years ago • 14 comments

gotounix avatar Jan 25 '19 09:01 gotounix

I usually appreciate an illustrating code snippet, but I would hope @replabrobin might want to comment about the transparency features available for PNGs created with reportlab, before anybody does some code digging.

deeplook avatar Jan 25 '19 10:01 deeplook

@deeplook So there are no ways to generate transparent pngs?

gotounix avatar Jan 25 '19 11:01 gotounix

@gotounix Have you tried renderPM.drawToFile(drawing, filename, fmt='GIF', configPIL={....})? See https://github.com/eduardocereto/reportlab/blob/master/src/reportlab/graphics/renderPM.py (old reportlab mirror on GitHub...)

deeplook avatar Jan 25 '19 15:01 deeplook

At the moment the renderPM canvas is three colour only. I looked at various backends to replace, but have little time to really get into that. Libart lgpl is pretty much dead so unless anyone has suggestions for a replacement it's not likely to change soon.

MrBitBucket avatar Jan 25 '19 15:01 MrBitBucket

Is there some way to specify the color that the transparency is replaced with? It seems to replace everything transparent with white. I'd like to use a different color instead to make that less noticable.

Akuli avatar Feb 27 '19 17:02 Akuli

The renderPM canvas has a background option as does renderPM.drawToFile; bg=0xff00ff would make the background red.

replabrobin avatar Feb 28 '19 11:02 replabrobin

Not sure whether this change is related or not,

https://bitbucket.org/rptlab/reportlab/commits/7df61e325601580bc36db042c6d6a8a776f62eef

liangqi avatar Oct 30 '19 12:10 liangqi

This is still present with svglib 1.1.0 and reportlab 3.5.67 / Pillow 7.0.0

I require support for transparent SVGs -- does anyone know of a good alternative library?

James-E-A avatar May 23 '21 19:05 James-E-A

A hack I can think of: call svglib twice with different background colors, and if a pixel comes out with two different colors, make it transparent instead.

Akuli avatar May 23 '21 20:05 Akuli

Hi, because of problems claudep found in the existing graphics state I added an alternate gstate based on py cairo (package rlPyCairo). It supports 32 bit colour and theoretically I believe you could get a transparent background to work. However, the defaults are set towards the existing three colour mechanisms so extension of the rlPyCairo code might be needed.

replabrobin avatar May 24 '21 09:05 replabrobin

For now, it looks like CairoSVG works for people who must have transparency and can't wait for this project to support it

import cairosvg.surface
from PIL import Image
from io import BytesIO

def svg2pil(*args, dpi=96, **kwargs):
    t = cairosvg.surface.Tree(*args, **kwargs)
    f = BytesIO()
    cairosvg.surface.PNGSurface(t, f, dpi).finish()
    return Image.open(f)

# svg2pil(url='input.svg').show()

James-E-A avatar Jun 05 '21 23:06 James-E-A

Following up on @Akuli 's answer:

from reportlab.graphics import renderPM
from reportlab.lib.colors import red, green
from PIL import ImageChops, Image

def drawing2png(drawing: Drawing, fp: Union[str, bytes]) -> None:
    red_img = renderPM.drawToPIL(drawing, bg=red, configPIL={'transparent': red})
    green_img = renderPM.drawToPIL(drawing, bg=green, configPIL={'transparent': green})
    bg_mask = ImageChops.difference(red_img, green_img).convert('1', dither=Image.NONE)
    bg_mask = ImageChops.invert(bg_mask)
    red_img.putalpha(bg_mask)
    red_img.save(fp, fmt='png')

tsoos99dev avatar Jan 26 '22 09:01 tsoos99dev

Here's one line equivalent to @JamesTheAwesomeDude's CairoSVG example:

from cairosvg import svg2png

svg2png(file_obj=StringIO(svg_text),
        write_to=png_file_name,
        background_color='transparent')

donkirkby avatar Feb 19 '22 07:02 donkirkby

Unfortunately, @Akuli's/@tsoos99dev's suggestion didn't work for me due to antialiasing. There would've probably been a way to prevent the ghosting by inverting the blending with the background, but I gave up on that since that still suffers from the scaling issue

A workaround I posted on a related issue also supports transparency. It uses pyMuPdf that comes with precompiled wheels for Windows/Linux/MacOS so no fiddling with external dependencies is needed.

Install packages:

pip install pymupdf svglib

Run the code:

import fitz
from svglib import svglib
from reportlab.graphics import renderPDF

# Convert svg to pdf in memory with svglib+reportlab
# directly rendering to png does not support transparency nor scaling
drawing = svglib.svg2rlg(path="input.svg")
pdf = renderPDF.drawToString(drawing)

# Open pdf with fitz (pyMuPdf) to convert to PNG
doc = fitz.Document(stream=pdf)
pix = doc.load_page(0).get_pixmap(alpha=True, dpi=300)
pix.save("output.png")

M4a1x avatar Oct 22 '22 15:10 M4a1x