how to resize svgs while constraining proportions using python 3.6.5 on Windows 10 (vscode)

2.4k Views Asked by At

I'm trying to downscale some SVGs that have a default of about 420 by 500 pixels, they are not all the same sizes, but I would like to maintain a width of 50px on the output files.

This is what I've managed so far (from this):

import os
import svgutils


path = 'path/to/files'
svg_files = [f for f in os.listdir(path) if f.endswith('.svg')]

for i in svg_files:
    svg_file = os.path.join(path, i)
    svg = svgutils.transform.fromfile(svg_file)
    original = svgutils.compose.SVG(svg_file)
    original.scale(.1)
    svg_out = os.path.splitext(svg_file)[0] + '_scale.svg'
    new_svg = svgutils.compose.Figure(float(svg.height) * .1,
                                      float(svg.width) * .1, original)
    new_svg.save(svg_out)

But it only adds the <g> with transform, and doesn't resize the original. Moreover, the end results won't open in inkscape.

Any idea what I am doing wrong here?

Edit:

I've had a deeper look around, so far I've managed to resize the svgs using librsvg-2.40.1-2-w32-bin.zip from this sourceforge project and adding the /bin folder to windows path, this allows me to do the following:

rsvg-convert -a ".\infile.svg" -w "30" -f svg -o ".\outfile.svg"

however, no color information is retained. So I'm thus far.

Edit 2:

The color issue happens during the conversion using rsvg-convert, it seems that it also converts hex to rgb tuple in the process. Which some viewers do not support (such as MapboxGL studio)

2

There are 2 best solutions below

0
On BEST ANSWER

After some fiddling around, I finally managed to get this right. While the solution is rather dirty, it gets the job done.

Here is the solution:

import os
import re
import subprocess
from webcolors import rgb_percent_to_hex

path = 'path/to/svgs'
svg_files = [f for f in os.listdir(path) if f.endswith('.svg')]

def cs(s): return rgb_percent_to_hex((s.group(1), s.group(2), s.group(3)))

# scaling the files
for i in svg_files:
    file_path = os.path.join(path, i)
    svg_path = os.path.splitext(file_path)[0] + '_scaled.svg'
    subprocess.call([
                    'rsvg-convert',
                    '-a',
                    file_path,
                    '-w', '30',
                    '-f', 'svg',
                    '-o', svg_path])

scaled_files = [f for f in os.listdir(path) if f.endswith('_scaled.svg')]
exp = r'rgb\((?:(?P<red>\d{1,3}.?(?:\d{1,50}\%)?)(?:\,?)(?P<green>\d{1,3}.?(?:\d{1,50}\%)?)(?:\,?)(?P<blue>\d{1,3}.?(?:\d{1,50}\%)?)(?:))\)'

for j in scaled_files:
    w = open(os.path.splitext(os.path.join(path, j))[0] + '_hexed.svg', 'w')
    r = open(os.path.join(path, j))
    for l in r:
        w.write(re.sub(exp, cs, l))
    w.close()
    r.close()
2
On

Here's something I've got so far:

# scale SVG images from files

import os
import re
import svgutils as su

SCALE = .1
path = 'E:/Slicke/Sitne'
svg_files = [f for f in os.listdir(path) if f.endswith('.svg')]

for i in svg_files:
    svg_file = os.path.join(path, i)

    # Get SVGFigure from file
    original = su.transform.fromfile(svg_file)

    # Original size is represetnted as string (examle: '600px'); convert to float
    original_width = float(re.sub('[^0-9]','', original.width))
    original_height = float(re.sub('[^0-9]','', original.width))

    scaled = su.transform.SVGFigure(original_width * SCALE, original_height * SCALE,)
    # Get the root element
    svg = original.getroot()

    # Scale the root element
    svg.scale_xy(SCALE, SCALE)

    # Add scaled svg element to figure
    scaled.append(svg)
    # Create the path and new file name
    svg_out = os.path.splitext(svg_file)[0] + '_scale.svg'
    print(svg_out)
    scaled.save(svg_out)