Apply a modified patch submitted by Jan Thor in bug 717254 to fix a bug whereby Scour would keep the <defs/> element if it was there but had only whitespace or unreferenced children.

This commit is contained in:
Louis Simard 2011-02-11 12:29:20 -05:00
parent bdae2bafae
commit 4657cb7515
4 changed files with 61 additions and 20 deletions

View file

@ -596,8 +596,9 @@ def removeUnreferencedElements(doc):
""" """
global numElemsRemoved global numElemsRemoved
num = 0 num = 0
removeTags = ['linearGradient', 'radialGradient', 'pattern']
# Remove certain unreferenced elements outside of defs
removeTags = ['linearGradient', 'radialGradient', 'pattern']
identifiedElements = findElementsWithId(doc.documentElement) identifiedElements = findElementsWithId(doc.documentElement)
referencedIDs = findReferencedElements(doc.documentElement) referencedIDs = findReferencedElements(doc.documentElement)
@ -609,8 +610,7 @@ def removeUnreferencedElements(doc):
num += 1 num += 1
numElemsRemoved += 1 numElemsRemoved += 1
# TODO: should also go through defs and vacuum it # Remove most unreferenced elements inside defs
num = 0
defs = doc.documentElement.getElementsByTagName('defs') defs = doc.documentElement.getElementsByTagName('defs')
for aDef in defs: for aDef in defs:
elemsToRemove = removeUnusedDefs(doc, aDef) elemsToRemove = removeUnusedDefs(doc, aDef)
@ -2784,15 +2784,21 @@ def scourString(in_string, options=None):
if options.remove_metadata: if options.remove_metadata:
removeMetadataElements(doc) removeMetadataElements(doc)
# remove unreferenced gradients/patterns outside of defs
# and most unreferenced elements inside of defs
while removeUnreferencedElements(doc) > 0:
pass
# remove empty defs, metadata, g # remove empty defs, metadata, g
# NOTE: these elements will be removed even if they have (invalid) text nodes # NOTE: these elements will be removed if they just have whitespace-only text nodes
elemsToRemove = []
for tag in ['defs', 'metadata', 'g'] : for tag in ['defs', 'metadata', 'g'] :
for elem in doc.documentElement.getElementsByTagName(tag) : for elem in doc.documentElement.getElementsByTagName(tag) :
removeElem = not elem.hasChildNodes() removeElem = not elem.hasChildNodes()
if removeElem == False : if removeElem == False :
for child in elem.childNodes : for child in elem.childNodes :
if child.nodeType in [1, 3, 4, 8] : if child.nodeType in [1, 4, 8]:
break
elif child.nodeType == 3 and not child.nodeValue.isspace():
break break
else: else:
removeElem = True removeElem = True
@ -2800,10 +2806,6 @@ def scourString(in_string, options=None):
elem.parentNode.removeChild(elem) elem.parentNode.removeChild(elem)
numElemsRemoved += 1 numElemsRemoved += 1
# remove unreferenced gradients/patterns outside of defs
while removeUnreferencedElements(doc) > 0:
pass
if options.strip_ids: if options.strip_ids:
bContinueLooping = True bContinueLooping = True
while bContinueLooping: while bContinueLooping:

View file

@ -1148,20 +1148,20 @@ class StyleToAttr(unittest.TestCase):
class PathEmptyMove(unittest.TestCase): class PathEmptyMove(unittest.TestCase):
def runTest(self): def runTest(self):
doc = scour.scourXmlFile('unittests/path-empty-move.svg') doc = scour.scourXmlFile('unittests/path-empty-move.svg')
self.assertEquals(doc.getElementsByTagName('path')[0].getAttribute('d'), 'm100 100l200 100z'); self.assertEquals(doc.getElementsByTagName('path')[0].getAttribute('d'), 'm100 100l200 100z')
self.assertEquals(doc.getElementsByTagName('path')[1].getAttribute('d'), 'm100 100v200l100 100z'); self.assertEquals(doc.getElementsByTagName('path')[1].getAttribute('d'), 'm100 100v200l100 100z')
class DefaultsRemovalToplevel(unittest.TestCase): class DefaultsRemovalToplevel(unittest.TestCase):
def runTest(self): def runTest(self):
doc = scour.scourXmlFile('unittests/cascading-default-attribute-removal.svg') doc = scour.scourXmlFile('unittests/cascading-default-attribute-removal.svg')
self.assertEquals(doc.getElementsByTagName('path')[1].getAttribute('fill-rule'), '', self.assertEquals(doc.getElementsByTagName('path')[1].getAttribute('fill-rule'), '',
'Default attribute fill-rule:nonzero not removed'); 'Default attribute fill-rule:nonzero not removed')
class DefaultsRemovalToplevelInverse(unittest.TestCase): class DefaultsRemovalToplevelInverse(unittest.TestCase):
def runTest(self): def runTest(self):
doc = scour.scourXmlFile('unittests/cascading-default-attribute-removal.svg') doc = scour.scourXmlFile('unittests/cascading-default-attribute-removal.svg')
self.assertEquals(doc.getElementsByTagName('path')[0].getAttribute('fill-rule'), 'evenodd', self.assertEquals(doc.getElementsByTagName('path')[0].getAttribute('fill-rule'), 'evenodd',
'Non-Default attribute fill-rule:evenodd removed'); 'Non-Default attribute fill-rule:evenodd removed')
class DefaultsRemovalToplevelFormat(unittest.TestCase): class DefaultsRemovalToplevelFormat(unittest.TestCase):
def runTest(self): def runTest(self):
@ -1173,37 +1173,49 @@ class DefaultsRemovalInherited(unittest.TestCase):
def runTest(self): def runTest(self):
doc = scour.scourXmlFile('unittests/cascading-default-attribute-removal.svg') doc = scour.scourXmlFile('unittests/cascading-default-attribute-removal.svg')
self.assertEquals(doc.getElementsByTagName('path')[3].getAttribute('fill-rule'), '', self.assertEquals(doc.getElementsByTagName('path')[3].getAttribute('fill-rule'), '',
'Default attribute fill-rule:nonzero not removed in child'); 'Default attribute fill-rule:nonzero not removed in child')
class DefaultsRemovalInheritedInverse(unittest.TestCase): class DefaultsRemovalInheritedInverse(unittest.TestCase):
def runTest(self): def runTest(self):
doc = scour.scourXmlFile('unittests/cascading-default-attribute-removal.svg') doc = scour.scourXmlFile('unittests/cascading-default-attribute-removal.svg')
self.assertEquals(doc.getElementsByTagName('path')[2].getAttribute('fill-rule'), 'evenodd', self.assertEquals(doc.getElementsByTagName('path')[2].getAttribute('fill-rule'), 'evenodd',
'Non-Default attribute fill-rule:evenodd removed in child'); 'Non-Default attribute fill-rule:evenodd removed in child')
class DefaultsRemovalInheritedFormat(unittest.TestCase): class DefaultsRemovalInheritedFormat(unittest.TestCase):
def runTest(self): def runTest(self):
doc = scour.scourXmlFile('unittests/cascading-default-attribute-removal.svg') doc = scour.scourXmlFile('unittests/cascading-default-attribute-removal.svg')
self.assertEquals(doc.getElementsByTagName('path')[2].getAttribute('stroke-width'), '', self.assertEquals(doc.getElementsByTagName('path')[2].getAttribute('stroke-width'), '',
'Default attribute stroke-width:1.00 not removed in child'); 'Default attribute stroke-width:1.00 not removed in child')
class DefaultsRemovalOverwrite(unittest.TestCase): class DefaultsRemovalOverwrite(unittest.TestCase):
def runTest(self): def runTest(self):
doc = scour.scourXmlFile('unittests/cascading-default-attribute-removal.svg') doc = scour.scourXmlFile('unittests/cascading-default-attribute-removal.svg')
self.assertEquals(doc.getElementsByTagName('path')[5].getAttribute('fill-rule'), 'nonzero', self.assertEquals(doc.getElementsByTagName('path')[5].getAttribute('fill-rule'), 'nonzero',
'Default attribute removed, although it overwrites parent element'); 'Default attribute removed, although it overwrites parent element')
class DefaultsRemovalOverwriteMarker(unittest.TestCase): class DefaultsRemovalOverwriteMarker(unittest.TestCase):
def runTest(self): def runTest(self):
doc = scour.scourXmlFile('unittests/cascading-default-attribute-removal.svg') doc = scour.scourXmlFile('unittests/cascading-default-attribute-removal.svg')
self.assertEquals(doc.getElementsByTagName('path')[4].getAttribute('marker-start'), 'none', self.assertEquals(doc.getElementsByTagName('path')[4].getAttribute('marker-start'), 'none',
'Default marker attribute removed, although it overwrites parent element'); 'Default marker attribute removed, although it overwrites parent element')
class DefaultsRemovalNonOverwrite(unittest.TestCase): class DefaultsRemovalNonOverwrite(unittest.TestCase):
def runTest(self): def runTest(self):
doc = scour.scourXmlFile('unittests/cascading-default-attribute-removal.svg') doc = scour.scourXmlFile('unittests/cascading-default-attribute-removal.svg')
self.assertEquals(doc.getElementsByTagName('path')[10].getAttribute('fill-rule'), '', self.assertEquals(doc.getElementsByTagName('path')[10].getAttribute('fill-rule'), '',
'Default attribute not removed, although its parent used default'); 'Default attribute not removed, although its parent used default')
class RemoveDefsWithUnreferencedElements(unittest.TestCase):
def runTest(self):
doc = scour.scourXmlFile('unittests/useless-defs.svg')
self.assertEquals(doc.getElementsByTagName('defs').length, 0,
'Kept defs, although it contains only unreferenced elements')
class RemoveDefsWithWhitespace(unittest.TestCase):
def runTest(self):
doc = scour.scourXmlFile('unittests/whitespace-defs.svg')
self.assertEquals(doc.getElementsByTagName('defs').length, 0,
'Kept defs, although it contains only whitespace or is <defs/>')
# TODO: write tests for --enable-viewboxing # TODO: write tests for --enable-viewboxing
# TODO; write a test for embedding rasters # TODO; write a test for embedding rasters

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<defs id="defs_one">
<linearGradient id="testgradient">
<stop style="stop-color:#e4ff01" offset="0"/>
<stop style="stop-color:#befb00" offset=".36267"/>
<stop style="stop-color:#216908" offset=".78168"/>
<stop style="stop-color:#0a2002" offset="1"/>
</linearGradient>
<filter id="CDS" x="-0.2" y="0.2" width="1.4" height="1.4">
<feGaussianBlur stdDeviation="8"/>
<feColorMatrix values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.6 0"/>
</filter>
<pattern id="testpattern">
<rect x="0" y="0" width="10" height="10" style="fill:#f00;"/>
</pattern>
<symbol id="testsymbol">
<rect x="0" y="0" width="10" height="10" style="fill:#0f0;"/>
</symbol>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 837 B

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<defs id="defs_whitespace">
</defs>
<defs id="defs_empty"/>
</svg>

After

Width:  |  Height:  |  Size: 169 B