# SVG Gradient Simplifier
#
# This script cleans up the linear gradients in an SVG file by applying the matrix transforms that may be present in the gradients.
# For example:
# <linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="-320.3149" y1="694.9482" x2="-319.3149" y2="694.9482" gradientTransform="matrix(-46.5269 252.9134 252.9134 46.5269 -190547.0313 48709.5078)">
# is changed into
# <linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="117.940" y1="31.363" x2="71.413" y2="284.276">
# The rest of the file is not modified in any way.
#
# Author: Quibik <http://commons.wikimedia.org/wiki/User:Quibik>
# Last modified: 2010-11-25
# Licensing: released into the public domain (attribution is still welcome, of course :)
from sys import argv
import re
def usage():
print("This script cleans up the linear gradients in an SVG file by applying the matrix transforms that may be present in the gradients.\n\n"
"Parameters:\n"
"\t"+argv[0].split('\\')[-1]+" <input file> [<output file>]\n"
"If no output file is specified, the input file will be overwritten.")
return
def simplify_gradient(p1, p2, matrix):
# apply an affine transformation to a point in 2D space
def transform(pos, matrix):
return [matrix[4] + matrix[0]*pos[0] + matrix[2]*pos[1], matrix[5] + matrix[1]*pos[0] + matrix[3]*pos[1]]
return [transform(p1, matrix), transform(p2, matrix)]
def main():
# get the input and output filenames from the command line
if len(argv) == 2:
input_file_name = output_file_name = argv[1]
elif len(argv) == 3:
input_file_name = argv[1]
output_file_name = argv[2]
else:
usage()
exit()
# read in the whole input file
data = ""
with open(input_file_name, 'r') as f:
data = f.read()
# an iterator of all linearGradient nodes
matchiter = re.finditer(r'<linearGradient[^<>]+>', data)
# output file content will be stored here
newdata = ""
# the end position of the previous match in the input file
lastend = 0
modifycount = 0
for m in matchiter:
# add data outside the linearGradient node to output
newdata += data[lastend:m.start()]
lastend = m.end()
node = m.group(0)
# read in data fraom the attributes inside the node
matrix = re.search('gradientTransform\s*=\s*["\']\s*matrix\(([^)]+)\)\s*["\']', node)
x1 = re.search('x1\s*=\s*["\']([^"\']+)["\']', node)
y1 = re.search('y1\s*=\s*["\']([^"\']+)["\']', node)
x2 = re.search('x2\s*=\s*["\']([^"\']+)["\']', node)
y2 = re.search('y2\s*=\s*["\']([^"\']+)["\']', node)
if not (matrix and x1 and y1 and x2 and y2):
newdata += node
continue
# convert strings to numbers
matrix = re.split('[,\s]+', matrix.group(1))
matrix = [float(x) for x in matrix]
x1 = float(x1.group(1).replace('px', ''))
y1 = float(y1.group(1).replace('px', ''))
x2 = float(x2.group(1).replace('px', ''))
y2 = float(y2.group(1).replace('px', ''))
# the important part: applying the transformation matrix to the coordinates
newp1, newp2 = simplify_gradient([x1, y1], [x2, y2], matrix)
# update the attributes
node = re.sub('\s+gradientTransform\s*=\s*["\']\s*matrix\([^)]+\)\s*["\']', '', node)
node = re.sub('x1\s*=\s*["\'][^"\']+["\']', 'x1="' + "{0:0.3f}".format(newp1[0])+'"', node)
node = re.sub('y1\s*=\s*["\'][^"\']+["\']', 'y1="' + "{0:0.3f}".format(newp1[1])+'"', node)
node = re.sub('x2\s*=\s*["\'][^"\']+["\']', 'x2="' + "{0:0.3f}".format(newp2[0])+'"', node)
node = re.sub('y2\s*=\s*["\'][^"\']+["\']', 'y2="' + "{0:0.3f}".format(newp2[1])+'"', node)
# write the node to output
newdata += node
modifycount += 1
newdata += data[lastend:]
# write the new data to the output file
with open(output_file_name, 'w') as f:
f.write(str(newdata))
print("Done! Modified "+str(modifycount)+" gradient definitions.")
if __name__ == "__main__":
main()