Scour length values for most attributes. Fix removal of duplicate gradients again. Two more unittests
This commit is contained in:
parent
3371177b80
commit
6fce13b84f
4 changed files with 102 additions and 34 deletions
99
scour.py
99
scour.py
|
|
@ -63,7 +63,7 @@
|
||||||
# + remove duplicate gradients
|
# + remove duplicate gradients
|
||||||
# + remove all empty path segments
|
# + remove all empty path segments
|
||||||
# + scour polyline coordinates just like path coordinates
|
# + scour polyline coordinates just like path coordinates
|
||||||
# - enable the precision argument to affect all numbers: polygon points, lengths, coordinates
|
# - enable the precision argument to affect all numbers: lengths, coordinates
|
||||||
# - remove id if it matches the Inkscape-style of IDs (also provide a switch to disable this)
|
# - remove id if it matches the Inkscape-style of IDs (also provide a switch to disable this)
|
||||||
# - prevent elements from being stripped if they are referenced in a <style> element
|
# - prevent elements from being stripped if they are referenced in a <style> element
|
||||||
# (for instance, filter, marker, pattern) - need a crude CSS parser
|
# (for instance, filter, marker, pattern) - need a crude CSS parser
|
||||||
|
|
@ -331,7 +331,22 @@ class Unit(object):
|
||||||
elif str == 'in': return Unit.IN
|
elif str == 'in': return Unit.IN
|
||||||
return Unit.INVALID
|
return Unit.INVALID
|
||||||
|
|
||||||
|
# @staticmethod
|
||||||
|
def str(u):
|
||||||
|
if u == Unit.NONE: return ''
|
||||||
|
elif u == Unit.PCT: return '%'
|
||||||
|
elif u == Unit.PX: return 'px'
|
||||||
|
elif u == Unit.PT: return 'pt'
|
||||||
|
elif u == Unit.PC: return 'pc'
|
||||||
|
elif u == Unit.EM: return 'em'
|
||||||
|
elif u == Unit.EX: return 'ex'
|
||||||
|
elif u == Unit.CM: return 'cm'
|
||||||
|
elif u == Unit.MM: return 'mm'
|
||||||
|
elif u == Unit.IN: return 'in'
|
||||||
|
return 'INVALID'
|
||||||
|
|
||||||
get = staticmethod(get)
|
get = staticmethod(get)
|
||||||
|
str = staticmethod(str)
|
||||||
|
|
||||||
class SVGLength(object):
|
class SVGLength(object):
|
||||||
def __init__(self, str):
|
def __init__(self, str):
|
||||||
|
|
@ -723,6 +738,7 @@ def removeDuplicateGradients(doc):
|
||||||
num = 0
|
num = 0
|
||||||
|
|
||||||
gradientsToRemove = {}
|
gradientsToRemove = {}
|
||||||
|
duplicateToMaster = {}
|
||||||
|
|
||||||
for gradType in ['linearGradient', 'radialGradient']:
|
for gradType in ['linearGradient', 'radialGradient']:
|
||||||
grads = doc.getElementsByTagNameNS(NS['SVG'], gradType)
|
grads = doc.getElementsByTagNameNS(NS['SVG'], gradType)
|
||||||
|
|
@ -734,9 +750,13 @@ def removeDuplicateGradients(doc):
|
||||||
|
|
||||||
# compare grad to ograd (all properties, then all stops)
|
# compare grad to ograd (all properties, then all stops)
|
||||||
# if attributes do not match, go to next gradient
|
# if attributes do not match, go to next gradient
|
||||||
|
someGradAttrsDoNotMatch = False
|
||||||
for attr in ['gradientUnits','spreadMethod','gradientTransform','x1','y1','x2','y2','cx','cy','fx','fy','r']:
|
for attr in ['gradientUnits','spreadMethod','gradientTransform','x1','y1','x2','y2','cx','cy','fx','fy','r']:
|
||||||
if grad.getAttribute(attr) != ograd.getAttribute(attr):
|
if grad.getAttribute(attr) != ograd.getAttribute(attr):
|
||||||
continue
|
someGradAttrsDoNotMatch = True
|
||||||
|
break;
|
||||||
|
|
||||||
|
if someGradAttrsDoNotMatch: continue
|
||||||
|
|
||||||
# compare xlink:href values too
|
# compare xlink:href values too
|
||||||
if grad.getAttributeNS(NS['XLINK'], 'href') != ograd.getAttributeNS(NS['XLINK'], 'href'):
|
if grad.getAttributeNS(NS['XLINK'], 'href') != ograd.getAttributeNS(NS['XLINK'], 'href'):
|
||||||
|
|
@ -763,9 +783,11 @@ def removeDuplicateGradients(doc):
|
||||||
# ograd is a duplicate of grad, we schedule it to be removed UNLESS
|
# ograd is a duplicate of grad, we schedule it to be removed UNLESS
|
||||||
# ograd is ALREADY considered a 'master' element
|
# ograd is ALREADY considered a 'master' element
|
||||||
if not gradientsToRemove.has_key(ograd):
|
if not gradientsToRemove.has_key(ograd):
|
||||||
if not gradientsToRemove.has_key(grad):
|
if not duplicateToMaster.has_key(ograd):
|
||||||
gradientsToRemove[grad] = []
|
if not gradientsToRemove.has_key(grad):
|
||||||
gradientsToRemove[grad].append( ograd )
|
gradientsToRemove[grad] = []
|
||||||
|
gradientsToRemove[grad].append( ograd )
|
||||||
|
duplicateToMaster[ograd] = grad
|
||||||
|
|
||||||
# get a collection of all elements that are referenced and their referencing elements
|
# get a collection of all elements that are referenced and their referencing elements
|
||||||
referencedIDs = findReferencedElements(doc.documentElement)
|
referencedIDs = findReferencedElements(doc.documentElement)
|
||||||
|
|
@ -809,7 +831,7 @@ def repairStyle(node, options):
|
||||||
for prop in ['fill', 'stroke'] :
|
for prop in ['fill', 'stroke'] :
|
||||||
if styleMap.has_key(prop) :
|
if styleMap.has_key(prop) :
|
||||||
chunk = styleMap[prop].split(') ')
|
chunk = styleMap[prop].split(') ')
|
||||||
if len(chunk) == 2 and chunk[0][:5] == 'url(#' and chunk[1] == 'rgb(0, 0, 0)' :
|
if len(chunk) == 2 and (chunk[0][:5] == 'url(#' or chunk[0][:6] == 'url("#' or chunk[0][:6] == "url('#") and chunk[1] == 'rgb(0, 0, 0)' :
|
||||||
styleMap[prop] = chunk[0] + ')'
|
styleMap[prop] = chunk[0] + ')'
|
||||||
num += 1
|
num += 1
|
||||||
|
|
||||||
|
|
@ -1529,32 +1551,8 @@ def scourCoordinates(data):
|
||||||
if data != None:
|
if data != None:
|
||||||
c = 0
|
c = 0
|
||||||
for coord in data:
|
for coord in data:
|
||||||
# reduce to the proper number of digits
|
# add the scoured coordinate to the path string
|
||||||
coord = Decimal(coord) * Decimal(1)
|
coordsStr += scourLength(coord)
|
||||||
|
|
||||||
# integerize if we can
|
|
||||||
if int(coord) == coord: coord = Decimal(str(int(coord)))
|
|
||||||
|
|
||||||
# Decimal.trim() is available in Python 2.6+ to trim trailing zeros
|
|
||||||
try:
|
|
||||||
coord = coord.trim()
|
|
||||||
except AttributeError:
|
|
||||||
# trim it ourselves
|
|
||||||
s = str(coord)
|
|
||||||
dec = s.find('.')
|
|
||||||
if dec != -1:
|
|
||||||
while s[-1] == '0':
|
|
||||||
s = s[:-1]
|
|
||||||
coord = Decimal(s)
|
|
||||||
|
|
||||||
# Decimal.normalize() will uses scientific notation - if that
|
|
||||||
# string is smaller, then use it
|
|
||||||
normd = coord.normalize()
|
|
||||||
if len(str(normd)) < len(str(coord)):
|
|
||||||
coord = normd
|
|
||||||
|
|
||||||
# finally add the coordinate to the path string
|
|
||||||
coordsStr += str(coord)
|
|
||||||
|
|
||||||
# only need the comma if the next number is non-negative
|
# only need the comma if the next number is non-negative
|
||||||
if c < len(data)-1 and Decimal(data[c+1]) >= 0:
|
if c < len(data)-1 and Decimal(data[c+1]) >= 0:
|
||||||
|
|
@ -1562,6 +1560,36 @@ def scourCoordinates(data):
|
||||||
c += 1
|
c += 1
|
||||||
return coordsStr
|
return coordsStr
|
||||||
|
|
||||||
|
def scourLength(str):
|
||||||
|
length = SVGLength(str)
|
||||||
|
coord = length.value
|
||||||
|
|
||||||
|
# reduce to the proper number of digits
|
||||||
|
coord = Decimal(unicode(coord)) * Decimal(1)
|
||||||
|
|
||||||
|
# integerize if we can
|
||||||
|
if int(coord) == coord: coord = Decimal(unicode(int(coord)))
|
||||||
|
|
||||||
|
# Decimal.trim() is available in Python 2.6+ to trim trailing zeros
|
||||||
|
try:
|
||||||
|
coord = coord.trim()
|
||||||
|
except AttributeError:
|
||||||
|
# trim it ourselves
|
||||||
|
s = unicode(coord)
|
||||||
|
dec = s.find('.')
|
||||||
|
if dec != -1:
|
||||||
|
while s[-1] == '0':
|
||||||
|
s = s[:-1]
|
||||||
|
coord = Decimal(s)
|
||||||
|
|
||||||
|
# Decimal.normalize() will uses scientific notation - if that
|
||||||
|
# string is smaller, then use it
|
||||||
|
normd = coord.normalize()
|
||||||
|
if len(unicode(normd)) < len(unicode(coord)):
|
||||||
|
coord = normd
|
||||||
|
|
||||||
|
return unicode(coord)+Unit.str(length.units)
|
||||||
|
|
||||||
def embedRasters(element, options) :
|
def embedRasters(element, options) :
|
||||||
"""
|
"""
|
||||||
Converts raster references to inline images.
|
Converts raster references to inline images.
|
||||||
|
|
@ -1752,6 +1780,13 @@ def scourString(in_string, options=None):
|
||||||
for polyline in doc.documentElement.getElementsByTagNameNS(NS['SVG'], 'polyline') :
|
for polyline in doc.documentElement.getElementsByTagNameNS(NS['SVG'], 'polyline') :
|
||||||
cleanPolygon(polyline)
|
cleanPolygon(polyline)
|
||||||
|
|
||||||
|
# scour lengths (including coordinates)
|
||||||
|
for type in ['svg', 'image', 'rect', 'circle', 'ellipse', 'line', 'linearGradient', 'radialGradient', 'stop']:
|
||||||
|
for elem in doc.documentElement.getElementsByTagNameNS(NS['SVG'], type):
|
||||||
|
for attr in ['x', 'y', 'width', 'height', 'cx', 'cy', 'r', 'rx', 'ry', 'x1', 'y1', 'x2', 'y2', 'fx', 'fy', 'offset']:
|
||||||
|
if elem.getAttribute(attr) != '':
|
||||||
|
elem.setAttribute(attr, scourLength(elem.getAttribute(attr)))
|
||||||
|
|
||||||
# convert rasters references to base64-encoded strings
|
# convert rasters references to base64-encoded strings
|
||||||
if options.embed_rasters:
|
if options.embed_rasters:
|
||||||
for elem in doc.documentElement.getElementsByTagNameNS(NS['SVG'], 'image') :
|
for elem in doc.documentElement.getElementsByTagNameNS(NS['SVG'], 'image') :
|
||||||
|
|
|
||||||
24
testscour.py
24
testscour.py
|
|
@ -706,6 +706,30 @@ class CollapseSamePathPoints(unittest.TestCase):
|
||||||
self.assertEquals(p.getAttribute('d'), "M100,100l100.12,100.12z",
|
self.assertEquals(p.getAttribute('d'), "M100,100l100.12,100.12z",
|
||||||
'Did not collapse same path points')
|
'Did not collapse same path points')
|
||||||
|
|
||||||
|
class ScourUnitlessLengths(unittest.TestCase):
|
||||||
|
def runTest(self):
|
||||||
|
r = scour.scourXmlFile('unittests/scour-lengths.svg').getElementsByTagNameNS(SVGNS, 'rect')[0];
|
||||||
|
self.assertEquals(r.getAttribute('x'), '123.46',
|
||||||
|
'Did not scour x attribute unitless number')
|
||||||
|
self.assertEquals(r.getAttribute('y'), '123',
|
||||||
|
'Did not scour y attribute unitless number')
|
||||||
|
self.assertEquals(r.getAttribute('width'), '300',
|
||||||
|
'Did not scour width attribute unitless number')
|
||||||
|
self.assertEquals(r.getAttribute('height'), '100',
|
||||||
|
'Did not scour height attribute unitless number')
|
||||||
|
|
||||||
|
class ScourLengthsWithUnits(unittest.TestCase):
|
||||||
|
def runTest(self):
|
||||||
|
r = scour.scourXmlFile('unittests/scour-lengths.svg').getElementsByTagNameNS(SVGNS, 'rect')[1];
|
||||||
|
self.assertEquals(r.getAttribute('x'), '123.46px',
|
||||||
|
'Did not scour x attribute with unit')
|
||||||
|
self.assertEquals(r.getAttribute('y'), '35ex',
|
||||||
|
'Did not scour y attribute with unit')
|
||||||
|
self.assertEquals(r.getAttribute('width'), '300pt',
|
||||||
|
'Did not scour width attribute with unit')
|
||||||
|
self.assertEquals(r.getAttribute('height'), '50%',
|
||||||
|
'Did not scour height attribute with unit')
|
||||||
|
|
||||||
# TODO; write a test for embedding rasters
|
# TODO; write a test for embedding rasters
|
||||||
# TODO: write a test for --disable-embed-rasters
|
# TODO: write a test for --disable-embed-rasters
|
||||||
# TODO: write tests for --keep-editor-data
|
# TODO: write tests for --keep-editor-data
|
||||||
|
|
|
||||||
4
unittests/collapse-same-path-points.svg
Normal file
4
unittests/collapse-same-path-points.svg
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<path fill="green" d="M100,100 L200.12345,200.12345 C210,210 190,190 200.12,200.12 Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 235 B |
5
unittests/scour-lengths.svg
Normal file
5
unittests/scour-lengths.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="123.4567" y="123.00" width="300.00001" height="1E+02" fill="lime" />
|
||||||
|
<rect x="123.4567px" y="35.000ex" width="300.00001pt" height="5E+01%" fill="blue" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 267 B |
Loading…
Add table
Add a link
Reference in a new issue