removeDefaultAttributeValues(): Refactor to improve code clarity and extensibility
This commit is contained in:
parent
cbda5dcd86
commit
002c54b5cc
1 changed files with 63 additions and 96 deletions
159
scour/scour.py
159
scour/scour.py
|
|
@ -57,6 +57,7 @@ import xml.dom.minidom
|
||||||
import re
|
import re
|
||||||
import math
|
import math
|
||||||
import time
|
import time
|
||||||
|
from collections import namedtuple
|
||||||
from scour.svg_regex import svg_parser
|
from scour.svg_regex import svg_parser
|
||||||
from scour.svg_transform import svg_transform_parser
|
from scour.svg_transform import svg_transform_parser
|
||||||
import optparse
|
import optparse
|
||||||
|
|
@ -1522,6 +1523,39 @@ def mayContainTextNodes(node):
|
||||||
node.mayContainTextNodes = result
|
node.mayContainTextNodes = result
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
# An extended list of default attributes that are safe to remove if all conditions are fulfilled
|
||||||
|
# Each default attribute is an object of type 'DefaultAttribute' with the following fields:
|
||||||
|
# name - name of the attribute to be matched
|
||||||
|
# value - default value of the attribute
|
||||||
|
# units - the unit(s) for which 'value' is valid (see 'Unit' class for possible specifications)
|
||||||
|
# elements - name(s) of SVG element(s) for which the attribute specification is valid
|
||||||
|
# conditions - additional conditions that have to be fulfilled for removal of the specified default attribute
|
||||||
|
# implemented as lambda functions with one argument (a xml.dom.minidom node) evaluating to True or False
|
||||||
|
# When not specifying a field value, it will be ignored (i.e. always matches)
|
||||||
|
DefaultAttribute = namedtuple('DefaultAttribute', ['name', 'value', 'units', 'elements', 'conditions'])
|
||||||
|
DefaultAttribute.__new__.__defaults__ = (None,) * len(DefaultAttribute._fields)
|
||||||
|
default_attributes_ex = [
|
||||||
|
DefaultAttribute('gradientUnits', 'objectBoundingBox'),
|
||||||
|
DefaultAttribute('spreadMethod' , 'pad'),
|
||||||
|
DefaultAttribute('x1', 0),
|
||||||
|
DefaultAttribute('y1', 0),
|
||||||
|
DefaultAttribute('x2', 0, elements = "line"),
|
||||||
|
DefaultAttribute('y2', 0),
|
||||||
|
DefaultAttribute('x2', 100 , Unit.PCT, "linearGradient"),
|
||||||
|
DefaultAttribute('x2', 1 , Unit.NONE, "linearGradient" , lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'),
|
||||||
|
DefaultAttribute('fx', conditions = lambda node: node.getAttribute('fx') == node.getAttribute('cx')),
|
||||||
|
DefaultAttribute('fy', conditions = lambda node: node.getAttribute('fy') == node.getAttribute('cy')),
|
||||||
|
DefaultAttribute('cx', 0 , elements = ["circle", "ellipse"]),
|
||||||
|
DefaultAttribute('cy', 0 , elements = ["circle", "ellipse"]),
|
||||||
|
DefaultAttribute('cx', 50 , Unit.PCT , "radialGradient"),
|
||||||
|
DefaultAttribute('cy', 50 , Unit.PCT , "radialGradient"),
|
||||||
|
DefaultAttribute('cx', 0.5 , Unit.NONE, "radialGradient" , lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'),
|
||||||
|
DefaultAttribute('cy', 0.5 , Unit.NONE, "radialGradient" , lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'),
|
||||||
|
DefaultAttribute('r' , 50 , Unit.PCT , "radialGradient"),
|
||||||
|
DefaultAttribute('r' , 0.5 , Unit.NONE, "radialGradient" , lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse')
|
||||||
|
]
|
||||||
|
|
||||||
def taint(taintedSet, taintedAttribute):
|
def taint(taintedSet, taintedAttribute):
|
||||||
u"""Adds an attribute to a set of attributes.
|
u"""Adds an attribute to a set of attributes.
|
||||||
|
|
||||||
|
|
@ -1533,6 +1567,32 @@ def taint(taintedSet, taintedAttribute):
|
||||||
taintedSet.add('marker')
|
taintedSet.add('marker')
|
||||||
return taintedSet
|
return taintedSet
|
||||||
|
|
||||||
|
def removeDefaultAttributeValue(node, attribute):
|
||||||
|
"""
|
||||||
|
Removes the DefaultAttribute 'attribute' from 'node' if specified conditions are fulfilled
|
||||||
|
"""
|
||||||
|
if not node.hasAttribute(attribute.name):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if (attribute.elements is not None) and (node.nodeName not in attribute.elements):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# differentiate between text and numeric values
|
||||||
|
if isinstance(attribute.value, str):
|
||||||
|
if node.getAttribute(attribute.name) == attribute.value:
|
||||||
|
if (attribute.conditions is None) or attribute.conditions(node):
|
||||||
|
node.removeAttribute(attribute.name)
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
nodeValue = SVGLength(node.getAttribute(attribute.name))
|
||||||
|
if (attribute.value is None) or (nodeValue.value == attribute.value):
|
||||||
|
if (attribute.units is None) or (nodeValue.units == attribute.units) or (isinstance(attribute.units, list) and nodeValue.units in attribute.units):
|
||||||
|
if (attribute.conditions is None) or attribute.conditions(node):
|
||||||
|
node.removeAttribute(attribute.name)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
def removeDefaultAttributeValues(node, options, tainted=set()):
|
def removeDefaultAttributeValues(node, options, tainted=set()):
|
||||||
u"""'tainted' keeps a set of attributes defined in parent nodes.
|
u"""'tainted' keeps a set of attributes defined in parent nodes.
|
||||||
|
|
||||||
|
|
@ -1540,102 +1600,9 @@ def removeDefaultAttributeValues(node, options, tainted=set()):
|
||||||
num = 0
|
num = 0
|
||||||
if node.nodeType != 1: return 0
|
if node.nodeType != 1: return 0
|
||||||
|
|
||||||
# gradientUnits: objectBoundingBox
|
# Conditionally remove all default attributes defined in the 'default_attributes_ex' (a list of 'DefaultAttribute's)
|
||||||
if node.getAttribute('gradientUnits') == 'objectBoundingBox':
|
for attribute in default_attributes_ex:
|
||||||
node.removeAttribute('gradientUnits')
|
num += removeDefaultAttributeValue(node, attribute)
|
||||||
num += 1
|
|
||||||
|
|
||||||
# spreadMethod: pad
|
|
||||||
if node.getAttribute('spreadMethod') == 'pad':
|
|
||||||
node.removeAttribute('spreadMethod')
|
|
||||||
num += 1
|
|
||||||
|
|
||||||
# x1 - line: 0
|
|
||||||
# linearGradient: 0%
|
|
||||||
if node.getAttribute('x1') != '':
|
|
||||||
x1 = SVGLength(node.getAttribute('x1'))
|
|
||||||
if x1.value == 0:
|
|
||||||
node.removeAttribute('x1')
|
|
||||||
num += 1
|
|
||||||
|
|
||||||
# y1 - line: 0
|
|
||||||
# linearGradient: 0%
|
|
||||||
if node.getAttribute('y1') != '':
|
|
||||||
y1 = SVGLength(node.getAttribute('y1'))
|
|
||||||
if y1.value == 0:
|
|
||||||
node.removeAttribute('y1')
|
|
||||||
num += 1
|
|
||||||
|
|
||||||
# x2 - line: 0
|
|
||||||
# linearGradient: 100% (x2="1" usually equals "1px" which only equals "100%" if gradientUnits="objectBoundingBox")
|
|
||||||
if node.getAttribute('x2') != '':
|
|
||||||
x2 = SVGLength(node.getAttribute('x2'))
|
|
||||||
if node.nodeName == 'line':
|
|
||||||
if x2.value == 0:
|
|
||||||
node.removeAttribute('x2')
|
|
||||||
num += 1
|
|
||||||
elif node.nodeName == 'linearGradient':
|
|
||||||
if ( (x2.value == 100 and x2.units == Unit.PCT) or
|
|
||||||
(x2.value == 1 and x2.units == Unit.NONE and not node.getAttribute('gradientUnits') == 'userSpaceOnUse') ):
|
|
||||||
node.removeAttribute('x2')
|
|
||||||
num += 1
|
|
||||||
|
|
||||||
# y2 - line: 0
|
|
||||||
# linearGradient: 0%
|
|
||||||
if node.getAttribute('y2') != '':
|
|
||||||
y2 = SVGLength(node.getAttribute('y2'))
|
|
||||||
if y2.value == 0:
|
|
||||||
node.removeAttribute('y2')
|
|
||||||
num += 1
|
|
||||||
|
|
||||||
# fx: equal to rx
|
|
||||||
if node.getAttribute('fx') != '':
|
|
||||||
if node.getAttribute('fx') == node.getAttribute('cx'):
|
|
||||||
node.removeAttribute('fx')
|
|
||||||
num += 1
|
|
||||||
|
|
||||||
# fy: equal to ry
|
|
||||||
if node.getAttribute('fy') != '':
|
|
||||||
if node.getAttribute('fy') == node.getAttribute('cy'):
|
|
||||||
node.removeAttribute('fy')
|
|
||||||
num += 1
|
|
||||||
|
|
||||||
# cx - circle / ellipse: 0
|
|
||||||
# radialGradient: 50% (cx="0.5" usually equals "0.5px" which only equals "50%" if gradientUnits="objectBoundingBox")
|
|
||||||
if node.getAttribute('cx') != '':
|
|
||||||
cx = SVGLength(node.getAttribute('cx'))
|
|
||||||
if node.nodeName in ['circle', 'ellipse']:
|
|
||||||
if cx.value == 0:
|
|
||||||
node.removeAttribute('cx')
|
|
||||||
num += 1
|
|
||||||
elif node.nodeName == "radialGradient":
|
|
||||||
if ( (cx.value == 50 and cx.units == Unit.PCT) or
|
|
||||||
(cx.value == 0.5 and cx.units == Unit.NONE and not node.getAttribute('gradientUnits') == 'userSpaceOnUse') ):
|
|
||||||
node.removeAttribute('cx')
|
|
||||||
num += 1
|
|
||||||
|
|
||||||
# cy - circle / ellipse: 0
|
|
||||||
# radialGradient: 50% (cy="0.5" usually equals "0.5px" which only equals "50%" if gradientUnits="objectBoundingBox")
|
|
||||||
if node.getAttribute('cy') != '':
|
|
||||||
cy = SVGLength(node.getAttribute('cy'))
|
|
||||||
if node.nodeName in ['circle', 'ellipse']:
|
|
||||||
if cy.value == 0:
|
|
||||||
node.removeAttribute('cy')
|
|
||||||
num += 1
|
|
||||||
elif node.nodeName == "radialGradient":
|
|
||||||
if ( (cy.value == 50 and cy.units == Unit.PCT) or
|
|
||||||
(cy.value == 0.5 and cy.units == Unit.NONE and not node.getAttribute('gradientUnits') == 'userSpaceOnUse') ):
|
|
||||||
node.removeAttribute('cy')
|
|
||||||
num += 1
|
|
||||||
|
|
||||||
# r - radialGradient: 50% (r="0.5" usually equals "0.5px" which only equals "50%" if gradientUnits="objectBoundingBox")
|
|
||||||
if node.getAttribute('r') != '':
|
|
||||||
if node.nodeName == 'radialGradient':
|
|
||||||
r = SVGLength(node.getAttribute('r'))
|
|
||||||
if ( (r.value == 50 and r.units == Unit.PCT) or
|
|
||||||
(r.value == 0.5 and r.units == Unit.NONE and not node.getAttribute('gradientUnits') == 'userSpaceOnUse') ):
|
|
||||||
node.removeAttribute('r')
|
|
||||||
num += 1
|
|
||||||
|
|
||||||
# Summarily get rid of some more attributes
|
# Summarily get rid of some more attributes
|
||||||
attributes = [node.attributes.item(i).nodeName
|
attributes = [node.attributes.item(i).nodeName
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue