Skip to content

Commit d6538d2

Browse files
authored
implement handling svg
1 parent 015e32a commit d6538d2

File tree

1 file changed

+97
-27
lines changed

1 file changed

+97
-27
lines changed

NonFreeImageResizer/littleimage.py

Lines changed: 97 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,58 @@
22
from PIL import Image, UnidentifiedImageError
33
import pyexiv2
44
import uuid
5+
import xml.dom.minidom
56
import sys
7+
import subprocess
68
import math
79
import os
10+
import re
811

912
sys.path.append("/data/project/datbot/Tasks/NonFreeImageResizer")
13+
svgMatch = re.compile(r"^\s*(-?\d+(?:\.\d+)?)\s*(px|in|cm|mm|pt|pc|%)?")
1014

1115
# CC-BY-SA Theopolisme, DatGuy
1216
# Task 3 on DatBot
1317

1418

19+
def parseLength(value):
20+
# Adapted from https://github.com/Zverik/svg-resize/blob/master/svg_resize.py
21+
if not value:
22+
return 0.0
23+
parts = svgMatch.match(value)
24+
if not parts:
25+
raise Exception('Unknown length format: "{}"'.format(value))
26+
num = float(parts.group(1))
27+
units = parts.group(2) or "px"
28+
if units == "px":
29+
return num
30+
elif units == "pt":
31+
return num * 1.25
32+
elif units == "pc":
33+
return num * 15.0
34+
elif units == "in":
35+
return num * 90.0
36+
elif units == "mm":
37+
return num * 3.543307
38+
elif units == "cm":
39+
return num * 35.43307
40+
elif units == "%":
41+
return -num / 100.0
42+
else:
43+
raise Exception("Unknown length units: {}".format(units))
44+
45+
46+
def calculateNewSize(origWidth, origHeight):
47+
newWidth = int(math.sqrt((100000.0 * origWidth) / origHeight))
48+
widthPercent = newWidth / origWidth
49+
newHeight = int(origHeight * widthPercent)
50+
51+
originalPixels = origWidth * origHeight
52+
modifiedPixels = newWidth * newHeight
53+
percentChange = 100.0 * (originalPixels - modifiedPixels) / float(originalPixels)
54+
return newWidth, newHeight, percentChange
55+
56+
1557
def updateMetadata(sourcePath, destPath, image):
1658
"""This function moves the metadata
1759
from the old image to the new, reduced
@@ -34,53 +76,81 @@ def downloadImage(randomName, origName, site):
3476
"""
3577
extension = os.path.splitext(origName)[1]
3678
extensionLower = extension[1:].lower()
79+
fullName = randomName + extension
3780

38-
if extensionLower == "jpg":
39-
extensionLower = "jpeg"
40-
elif extensionLower == "gif":
81+
if extensionLower == "gif":
4182
return "SKIP"
42-
4383
mwImage = site.Images[origName]
4484

4585
tempFile = str(uuid.uuid4()) + extension
4686
with open(tempFile, "wb") as f:
4787
mwImage.download(f)
48-
4988
try:
50-
fullName = randomName + extension
51-
img = Image.open(tempFile)
52-
imgWidth = img.size[0]
53-
imgHeight = img.size[1]
54-
if (imgWidth * imgHeight) > 80000000:
55-
img.close()
56-
return "BOMB"
57-
58-
baseWidth = int(math.sqrt((100000.0 * float(imgWidth)) / (imgHeight)))
59-
widthPercent = baseWidth / float(imgWidth)
60-
heightSize = int((float(imgHeight) * float(widthPercent)))
61-
62-
originalPixels = imgWidth * imgHeight
63-
modifiedPixels = baseWidth * heightSize
64-
percentChange = 100.0 * (originalPixels - modifiedPixels) / float(originalPixels)
65-
if percentChange > 5:
89+
# Maybe move this all to seperate functions?
90+
if extensionLower == "svg":
91+
subprocess.check_call(
92+
"scour -i {} --enable-viewboxing --enable-id-stripping "
93+
"--shorten-ids --indent=none".format(tempFile)
94+
)
95+
docElement = xml.dom.minidom.parse(tempFile).documentElement
96+
oldWidth = parseLength(docElement.getAttribute("width"))
97+
oldHeight = parseLength(docElement.getAttribute("height"))
98+
99+
viewboxArray = re.split("[ ,\t]+", docElement.getAttribute("viewBox"))
100+
viewboxOffsetX, viewboxOffsetY = 0, 0
101+
102+
if oldHeight == 0.0 and oldWidth == 0.0:
103+
viewboxOffsetX = parseLength(viewboxArray[0])
104+
viewboxOffsetY = parseLength(viewboxArray[1])
105+
oldHeight = parseLength(viewboxArray[2])
106+
oldWidth = parseLength(viewboxArray[3])
107+
108+
newWidth, newHeight, percentChange = calculateNewSize(oldWidth, oldHeight)
109+
if percentChange < 5:
110+
print("Looks like we'd have a less than 5% change "
111+
"in pixel counts. Skipping.")
112+
return "PIXEL"
113+
114+
docElement.setAttribute("width", str(newWidth))
115+
docElement.setAttribute("height", str(newHeight))
116+
docElement.setAttribute(
117+
"viewBox",
118+
"{} {} {} {}".format(
119+
viewboxOffsetX, viewboxOffsetY, newWidth, newHeight
120+
),
121+
)
122+
123+
with open(fullName, "wb") as f:
124+
docElement.writexml(f, encoding="utf-8")
125+
126+
else:
127+
img = Image.open(tempFile)
128+
imgWidth = img.size[0]
129+
imgHeight = img.size[1]
130+
if (imgWidth * imgHeight) > 80000000:
131+
img.close()
132+
return "BOMB"
133+
134+
newWidth, newHeight = calculateNewSize(imgWidth, imgHeight)
135+
if percentChange < 5:
136+
img.close()
137+
print("Looks like we'd have a less than 5% change in pixel counts. Skipping.")
138+
return "PIXEL"
139+
66140
originalMode = img.mode
67141
if originalMode in ["1", "L", "P"]:
68142
img = img.convert("RGBA")
69143

70-
img = img.resize((int(baseWidth), int(heightSize)), Image.ANTIALIAS)
144+
img = img.resize((int(newWidth), int(newHeight)), Image.ANTIALIAS)
71145
if originalMode in ["1", "L", "P"]:
72146
img = img.convert(originalMode, palette=Image.ADAPTIVE)
73147

74148
img.save(fullName, **img.info, quality=95)
75-
else:
76-
img.close()
77-
print("Looks like we'd have a less than 5% change in pixel counts. Skipping.")
78-
return "PIXEL"
149+
79150
except UnidentifiedImageError as e:
80151
print("Unable to open image {0} - aborting ({1})".format(origName, e))
81152
return "ERROR"
82153
except IOError as e:
83-
img.close()
84154
print("Unable to open image {0} - aborting ({1})".format(origName, e))
85155
return "ERROR"
86156

0 commit comments

Comments
 (0)