How to convert SVG to PNG or JPEG in Python? - python

I'm using svgwrite and generating svg-files, how do I convert them to PNG or JPEG?

pyvips supports SVG load. It's free, fast, needs little memory, and works on macOS, Windows and Linux.
You can use it like this:
import pyvips
image = pyvips.Image.new_from_file("something.svg", dpi=300)
image.write_to_file("x.png")
The default DPI is 72, which might be a little low, but you can set any DPI you like. You can write to JPG instead in the obvious way.
You can also load by the pixel dimensions you want like this:
import pyvips
image = pyvips.Image.thumbnail("something.svg", 200, height=300)
image.write_to_file("x.png")
That will render the SVG to fit within a 200 x 300 pixel box. The docs introduce all the options.
The pyvips SVG loader has some nice properties:
It uses librsvg for the actual rendering, so the PNG will have high-quality anti-aliased edges.
It's much faster than systems like ImageMagick, which simply shell out to inkscape for rendering.
It supports progressive rendering. Large images (more than a few thousand pixels a side) are rendered in sections, keeping memory use under control, even for very large images.
It supports streaming, so you can render an SVG directly to a huge Deep Zoom pyramid (for example) without needing any intermediate storage.
It supports input from memory areas, strings and pipes as well as files.
Rendering from strings can be handy, eg.:
import pyvips
x = pyvips.Image.svgload_buffer(b"""
<svg viewBox="0 0 200 200">
<circle r="100" cx="100" cy="100" fill="#900"/>
</svg>
""")
x.write_to_file("x.png")

For converting svg to png, there are 2 ways I can think of:
1.
Here is lib which can do what you need: https://cairosvg.org/documentation/
$ pip3 install cairosvg
python3 code:
cairosvg.svg2png(url="/path/to/input.svg", write_to="/tmp/output.png")
Have used it on linux (debian 9+ and ubuntu 18+) and MacOS. It works as expect for large files about 1MB svg. Example: world map. Lib also allow to export pdf file.
Tip: cairosvg provide scaling up of png output image as default size looks blurry after working with vector graphics svg :) . I couldn't get DPI option working for me.
2.
There is another method to do same by using browser to open svg file and take screenshot using Selenium webdriver either with Firefox or other browser. You can save screenshot as png.
One can use Pillow to convert png to jpeg: Convert png to jpeg using Pillow

On Windows, you get errors like libgobject-2.0-0.dll, libvips-42.dll, etc. not found when trying to import pyvips. To get pyvips working on Windows, do the following:
Download the zip file for Windows from https://github.com/libvips/libvips/releases and unzip to a folder
pip install pyvips
In code do this:
import os
# The bin folder has the DLLs
os.environ['path'] += r';C:\Path\ToYour\VIPsFolder\bin'
import pyvips
image = pyvips.Image.thumbnail("test.svg", 200)
image.write_to_file("test.png")
I recommend using pyvips over cairosvg. From my tests it's much faster than cairosvg, especially for large SVGs. You need to something similar to the above to get cairosvg working on Windows anyway.

I looked several methods, including cairo (which I could not make it work on Windows), svglib+reportlab (dpi cannot be changed) and even inkscape (from command line).
At the end this is the best method I found. I tested it on python 3.7.
def convert(method, svg_file, png_file, resolution = 72):
from wand.api import library
import wand.color
import wand.image
with open(svg_file, "r") as svg_file:
with wand.image.Image() as image:
with wand.color.Color('transparent') as background_color:
library.MagickSetBackgroundColor(image.wand,
background_color.resource)
svg_blob = svg_file.read().encode('utf-8')
image.read(blob=svg_blob, resolution = resolution)
png_image = image.make_blob("png32")
with open(png_file, "wb") as out:
out.write(png_image)
I had to install the wand package (using pip) and then ImageMagick for Windows (http://docs.wand-py.org/en/latest/guide/install.html#install-imagemagick-on-windows).

Related

How can I programmatically resize SVG file with svgwrite in python?

My current workflow is using svgwrite to generate my svg file and import into inkscape to resize and align to export to gcode. I'm looking for a way to do this programmatically via cli. I've tried a couple svg-resizing repo's from github however they don't work well shrinking and svgwrite doesn't have a fixed output size option. I've thought of maybe creating an empty rectangle of the size I require with svgwrite's drawing class but I'm not sure how to add my svg file and have it fit to its container (the empty rectangle).
Any and all suggestions are greatly appreciated.

How to insert vector graphics (SVG) to a raster image such as JPG/TIF programmatically (Python/JS/C++)?

I need a programmatic way to embed a clipping-path (saved as SVG file) to another image such as JPG/TIF/PSD. Using a tool such as Photoshop, this can be done easily and the path will be inserted in the image 8BIM profile, but it seems there is no way to do it programmatically. ImageMagick allows you to get a vector image for example by using the following command:
identify -format "%[8BIM:1999,2998:#1]" test.jpg > test.svg
But it seems not possible to do the reverse operation and add a vector image. Can anyone suggest any libraries which allow this operation?
It's a bit more code than I feel like writing for the moment, but it should be possible to put an 8BIM into a JPEG using the following information.
The anatomy of a JPEG is described here and here.
You can use PIL or OpenCV to encode a JPEG into a memory buffer and then locate and modify/add segments (such as an 8BIM) using code like this. Or you could just read() in an existing JPEG that you want to modify. To insert a segment, just write the first few segments to disk, then write your new one followed by the remaining segments from the existing file that you read at the start.
You can construct an 8BIM segment to insert using this answer.
You can use exiftool -v -v -v to see where an 8BIM appears in a JPEG created by Photoshop and then put yours in a similar place. You can also, obviously, equally use exiftool to see where/how your own attempt has landed.

Opening Tif file with PIL "PIL.UnidentifiedImageError: cannot identify image file"

I've a huge TIF file of shape (39906, 30365, 4). I want to use it on PyQt5, however when I use PIL to open the image it gives the error "PIL.UnidentifiedImageError: cannot identify image file".
I've searched and it seems that PIL can open TIF/TIFF files, but it has to be 8bit and my image is 8bit.
What could I do fix it? Is the file to large to be open by PIL? Is there another option to open a huge TIF to be used with PyQt5?
It's not necessary to open the whole image. Actually, if I could open a 25% scaled version of the original would be better.
Pillow version = 7.2.0
If you are having issues handling very large images, consider using libvips, either in your Python code or in the Terminal. It is very fast and frugal with memory.
Here's an example for Terminal:
vipsthumbnail BIGBOY.TIF --size 10000x -o small.tif # reduce width to 10000px
And see usingVIPSandShrink() here.

Python PIL (Pillow) resizes my pictures after modifying exif data

i made a small script in Python, which can set the exif data of my old Whatsapp pictures based on their filename.
I use the piexif and the PIL (Pillow) package.
import piexif
from PIL import Image
from collections import defaultdict
img = Image.open(fname)
try:
exif_dict = piexif.load(img.info["exif"])
except KeyError:
exif_dict = defaultdict(dict)
exif_dict['Exif'][piexif.ExifIFD.DateTimeOriginal] = exiftime(date)
exif_dict['Exif'][piexif.ExifIFD.DateTimeDigitized] = exiftime(date)
exif_bytes = piexif.dump(exif_dict)
img.save('%s' % fname, "jpeg", exif=exif_bytes)
The exiftime() function is only for formatting the date.
However, the script is setting some exif fields, i don't modify compression or someting like that.
My problem is, that the pictures get much smaller, after running that script.
I tested this script with some sample images, e.g. a picture shot with a Nikon D5300 with an resolution of 6000x4000. The original file has about 12Mb, after the script it has only 4Mb.
Does the script cause a quality loss of the picture, or is it just a better compression?
Pillow's .save automatically compresses with a default of 75% quality according to the documentation. You can bump that up to 100% (add quality=100), which will minimize compression and looks like it will skip some compression components completely, but Pillow apparently doesn't have the ability to skip compression altogether. Very few packages do this, and I'm unaware of any in the form of a Python module. Note that the docs say not to raise quality over 95, and I can attest that doing so outputs a BIGGER file.. weird.
A bit of a late answer, but a similar post pointed out a solution here to only write exif information in the file (using piexif).
As a consequence the content of the image is not altered, since no compression (through the "save" command) is done.

Can you reduce memory consumption by ReportLab when embedding very large images, or is there a Python PDF toolkit that can?

Right now reportlab is making PDFs most of the time. However when one file gets several large images (125 files with a total on disk size of 7MB), we end up running out of memory and crashing trying to build a PDF that should ultimately be smaller than 39MB. The problem stems from:
elif mode not in ('L','RGB','CMYK'):
im = im.convert('RGB')
self.mode = 'RGB'
Where nice b&w (bitonal) images are converted to RGB and when you have images with sizes in the 2595x3000, they consume a lot of memory. (Not sure why they consume 2GB, but that point is moot. When we add them to reportlab our entire python memory footprint is about 50MB, when we call
doc.build(elements, canvasmaker=canvasmaker)
Memory usage skyrockets as we go from bitonal PNGs to RGB and then render them onto the page.
While I try to see if I can figure out how to inject bitonal images into reportlab PDFs, I thought I would see if anyone else had an idea of how to fix this problem either in reportlab or with another tool.
We have a working PDF maker using PODOFO in C++, one of my possible solutions is to write a script/outline for that tool that will simply generate the PDF in a subprocess and then return that via a file or stdout.
Short of redoing PIL you are out of luck. The Images are converted internally in PIL to 24 bit color TIFs. This is not something you can easily change.
We switched to Podofo and generate the PDF outside of python.

Categories