Consistent whitespace across source files according to PEP 8
(mostly automated by using autopep8, fixes #69)
This commit is contained in:
parent
943319b710
commit
0f1974c1e2
8 changed files with 4787 additions and 4276 deletions
|
|
@ -1,19 +1,19 @@
|
||||||
###############################################################################
|
###############################################################################
|
||||||
##
|
#
|
||||||
## Copyright (C) 2010 Jeff Schiller, 2010 Louis Simard, 2013-2015 Tavendo GmbH
|
# Copyright (C) 2010 Jeff Schiller, 2010 Louis Simard, 2013-2015 Tavendo GmbH
|
||||||
##
|
#
|
||||||
## Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
## you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
## You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
##
|
#
|
||||||
## http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
##
|
#
|
||||||
## Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
## distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
## See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
## limitations under the License.
|
# limitations under the License.
|
||||||
##
|
#
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
__version__ = u'0.35'
|
__version__ = u'0.35'
|
||||||
|
|
|
||||||
175
scour/scour.py
175
scour/scour.py
|
|
@ -400,6 +400,7 @@ default_properties = { # excluded all properties with 'auto' as default
|
||||||
'viewport-fill-opacity': '1',
|
'viewport-fill-opacity': '1',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def isSameSign(a, b): return (a <= 0 and b <= 0) or (a >= 0 and b >= 0)
|
def isSameSign(a, b): return (a <= 0 and b <= 0) or (a >= 0 and b >= 0)
|
||||||
|
|
||||||
scinumber = re.compile(r"[-+]?(\d*\.?)?\d+[eE][-+]?\d+")
|
scinumber = re.compile(r"[-+]?(\d*\.?)?\d+[eE][-+]?\d+")
|
||||||
|
|
@ -407,6 +408,7 @@ number = re.compile(r"[-+]?(\d*\.?)?\d+")
|
||||||
sciExponent = re.compile(r"[eE]([-+]?\d+)")
|
sciExponent = re.compile(r"[eE]([-+]?\d+)")
|
||||||
unit = re.compile("(em|ex|px|pt|pc|cm|mm|in|%){1,1}$")
|
unit = re.compile("(em|ex|px|pt|pc|cm|mm|in|%){1,1}$")
|
||||||
|
|
||||||
|
|
||||||
class Unit(object):
|
class Unit(object):
|
||||||
# Integer constants for units.
|
# Integer constants for units.
|
||||||
INVALID = -1
|
INVALID = -1
|
||||||
|
|
@ -451,7 +453,8 @@ class Unit(object):
|
||||||
|
|
||||||
# @staticmethod
|
# @staticmethod
|
||||||
def get(unitstr):
|
def get(unitstr):
|
||||||
if unitstr is None: return Unit.NONE
|
if unitstr is None:
|
||||||
|
return Unit.NONE
|
||||||
try:
|
try:
|
||||||
return Unit.s2u[unitstr]
|
return Unit.s2u[unitstr]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|
@ -467,7 +470,9 @@ class Unit(object):
|
||||||
get = staticmethod(get)
|
get = staticmethod(get)
|
||||||
str = staticmethod(str)
|
str = staticmethod(str)
|
||||||
|
|
||||||
|
|
||||||
class SVGLength(object):
|
class SVGLength(object):
|
||||||
|
|
||||||
def __init__(self, str):
|
def __init__(self, str):
|
||||||
try: # simple unitless and no scientific notation
|
try: # simple unitless and no scientific notation
|
||||||
self.value = float(str)
|
self.value = float(str)
|
||||||
|
|
@ -509,6 +514,7 @@ class SVGLength(object):
|
||||||
self.value = 0
|
self.value = 0
|
||||||
self.units = Unit.INVALID
|
self.units = Unit.INVALID
|
||||||
|
|
||||||
|
|
||||||
def findElementsWithId(node, elems=None):
|
def findElementsWithId(node, elems=None):
|
||||||
"""
|
"""
|
||||||
Returns all elements with id attributes
|
Returns all elements with id attributes
|
||||||
|
|
@ -529,6 +535,7 @@ def findElementsWithId(node, elems=None):
|
||||||
referencingProps = ['fill', 'stroke', 'filter', 'clip-path', 'mask', 'marker-start',
|
referencingProps = ['fill', 'stroke', 'filter', 'clip-path', 'mask', 'marker-start',
|
||||||
'marker-end', 'marker-mid']
|
'marker-end', 'marker-mid']
|
||||||
|
|
||||||
|
|
||||||
def findReferencedElements(node, ids=None):
|
def findReferencedElements(node, ids=None):
|
||||||
"""
|
"""
|
||||||
Returns the number of times an ID is referenced as well as all elements
|
Returns the number of times an ID is referenced as well as all elements
|
||||||
|
|
@ -589,6 +596,7 @@ def findReferencedElements(node, ids=None):
|
||||||
findReferencedElements(child, ids)
|
findReferencedElements(child, ids)
|
||||||
return ids
|
return ids
|
||||||
|
|
||||||
|
|
||||||
def findReferencingProperty(node, prop, val, ids):
|
def findReferencingProperty(node, prop, val, ids):
|
||||||
global referencingProps
|
global referencingProps
|
||||||
if prop in referencingProps and val != '':
|
if prop in referencingProps and val != '':
|
||||||
|
|
@ -629,6 +637,7 @@ numBytesSavedInTransforms = 0
|
||||||
numPointsRemovedFromPolygon = 0
|
numPointsRemovedFromPolygon = 0
|
||||||
numCommentBytes = 0
|
numCommentBytes = 0
|
||||||
|
|
||||||
|
|
||||||
def removeUnusedDefs(doc, defElem, elemsToRemove=None):
|
def removeUnusedDefs(doc, defElem, elemsToRemove=None):
|
||||||
if elemsToRemove is None:
|
if elemsToRemove is None:
|
||||||
elemsToRemove = []
|
elemsToRemove = []
|
||||||
|
|
@ -639,7 +648,7 @@ def removeUnusedDefs(doc, defElem, elemsToRemove=None):
|
||||||
keepTags = ['font', 'style', 'metadata', 'script', 'title', 'desc']
|
keepTags = ['font', 'style', 'metadata', 'script', 'title', 'desc']
|
||||||
for elem in defElem.childNodes:
|
for elem in defElem.childNodes:
|
||||||
# only look at it if an element and not referenced anywhere else
|
# only look at it if an element and not referenced anywhere else
|
||||||
if elem.nodeType == 1 and (elem.getAttribute('id') == '' or \
|
if elem.nodeType == 1 and (elem.getAttribute('id') == '' or
|
||||||
(not elem.getAttribute('id') in referencedIDs)):
|
(not elem.getAttribute('id') in referencedIDs)):
|
||||||
|
|
||||||
# we only inspect the children of a group in a defs if the group
|
# we only inspect the children of a group in a defs if the group
|
||||||
|
|
@ -651,6 +660,7 @@ def removeUnusedDefs(doc, defElem, elemsToRemove=None):
|
||||||
elemsToRemove.append(elem)
|
elemsToRemove.append(elem)
|
||||||
return elemsToRemove
|
return elemsToRemove
|
||||||
|
|
||||||
|
|
||||||
def removeUnreferencedElements(doc, keepDefs):
|
def removeUnreferencedElements(doc, keepDefs):
|
||||||
"""
|
"""
|
||||||
Removes all unreferenced elements except for <svg>, <font>, <metadata>, <title>, and <desc>.
|
Removes all unreferenced elements except for <svg>, <font>, <metadata>, <title>, and <desc>.
|
||||||
|
|
@ -687,6 +697,7 @@ def removeUnreferencedElements(doc, keepDefs):
|
||||||
num += 1
|
num += 1
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
def shortenIDs(doc, prefix, unprotectedElements=None):
|
def shortenIDs(doc, prefix, unprotectedElements=None):
|
||||||
"""
|
"""
|
||||||
Shortens ID names used in the document. ID names referenced the most often are assigned the
|
Shortens ID names used in the document. ID names referenced the most often are assigned the
|
||||||
|
|
@ -731,6 +742,7 @@ def shortenIDs(doc, prefix, unprotectedElements=None):
|
||||||
|
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
def intToID(idnum, prefix):
|
def intToID(idnum, prefix):
|
||||||
"""
|
"""
|
||||||
Returns the ID name for the given ID number, spreadsheet-style, i.e. from a to z,
|
Returns the ID name for the given ID number, spreadsheet-style, i.e. from a to z,
|
||||||
|
|
@ -745,6 +757,7 @@ def intToID(idnum, prefix):
|
||||||
|
|
||||||
return prefix + rid
|
return prefix + rid
|
||||||
|
|
||||||
|
|
||||||
def renameID(doc, idFrom, idTo, identifiedElements, referencedIDs):
|
def renameID(doc, idFrom, idTo, identifiedElements, referencedIDs):
|
||||||
"""
|
"""
|
||||||
Changes the ID name from idFrom to idTo, on the declaring element
|
Changes the ID name from idFrom to idTo, on the declaring element
|
||||||
|
|
@ -822,6 +835,7 @@ def renameID(doc, idFrom, idTo, identifiedElements, referencedIDs):
|
||||||
|
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
def unprotected_ids(doc, options):
|
def unprotected_ids(doc, options):
|
||||||
u"""Returns a list of unprotected IDs within the document doc."""
|
u"""Returns a list of unprotected IDs within the document doc."""
|
||||||
identifiedElements = findElementsWithId(doc.documentElement)
|
identifiedElements = findElementsWithId(doc.documentElement)
|
||||||
|
|
@ -847,6 +861,7 @@ def unprotected_ids(doc, options):
|
||||||
del identifiedElements[id]
|
del identifiedElements[id]
|
||||||
return identifiedElements
|
return identifiedElements
|
||||||
|
|
||||||
|
|
||||||
def removeUnreferencedIDs(referencedIDs, identifiedElements):
|
def removeUnreferencedIDs(referencedIDs, identifiedElements):
|
||||||
"""
|
"""
|
||||||
Removes the unreferenced ID attributes.
|
Removes the unreferenced ID attributes.
|
||||||
|
|
@ -855,7 +870,7 @@ def removeUnreferencedIDs(referencedIDs, identifiedElements):
|
||||||
"""
|
"""
|
||||||
global numIDsRemoved
|
global numIDsRemoved
|
||||||
keepTags = ['font']
|
keepTags = ['font']
|
||||||
num = 0;
|
num = 0
|
||||||
for id in list(identifiedElements.keys()):
|
for id in list(identifiedElements.keys()):
|
||||||
node = identifiedElements[id]
|
node = identifiedElements[id]
|
||||||
if (id in referencedIDs) == False and not node.nodeName in keepTags:
|
if (id in referencedIDs) == False and not node.nodeName in keepTags:
|
||||||
|
|
@ -864,6 +879,7 @@ def removeUnreferencedIDs(referencedIDs, identifiedElements):
|
||||||
num += 1
|
num += 1
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
def removeNamespacedAttributes(node, namespaces):
|
def removeNamespacedAttributes(node, namespaces):
|
||||||
global numAttrsRemoved
|
global numAttrsRemoved
|
||||||
num = 0
|
num = 0
|
||||||
|
|
@ -885,6 +901,7 @@ def removeNamespacedAttributes(node, namespaces):
|
||||||
num += removeNamespacedAttributes(child, namespaces)
|
num += removeNamespacedAttributes(child, namespaces)
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
def removeNamespacedElements(node, namespaces):
|
def removeNamespacedElements(node, namespaces):
|
||||||
global numElemsRemoved
|
global numElemsRemoved
|
||||||
num = 0
|
num = 0
|
||||||
|
|
@ -905,6 +922,7 @@ def removeNamespacedElements(node, namespaces):
|
||||||
num += removeNamespacedElements(child, namespaces)
|
num += removeNamespacedElements(child, namespaces)
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
def removeDescriptiveElements(doc, options):
|
def removeDescriptiveElements(doc, options):
|
||||||
elementTypes = []
|
elementTypes = []
|
||||||
if options.remove_descriptive_elements:
|
if options.remove_descriptive_elements:
|
||||||
|
|
@ -932,6 +950,7 @@ def removeDescriptiveElements(doc, options):
|
||||||
|
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
def removeNestedGroups(node):
|
def removeNestedGroups(node):
|
||||||
"""
|
"""
|
||||||
This walks further and further down the tree, removing groups
|
This walks further and further down the tree, removing groups
|
||||||
|
|
@ -968,6 +987,7 @@ def removeNestedGroups(node):
|
||||||
num += removeNestedGroups(child)
|
num += removeNestedGroups(child)
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
def moveCommonAttributesToParentGroup(elem, referencedElements):
|
def moveCommonAttributesToParentGroup(elem, referencedElements):
|
||||||
"""
|
"""
|
||||||
This recursively calls this function on all children of the passed in element
|
This recursively calls this function on all children of the passed in element
|
||||||
|
|
@ -992,7 +1012,8 @@ def moveCommonAttributesToParentGroup(elem, referencedElements):
|
||||||
return num
|
return num
|
||||||
|
|
||||||
# only process the children if there are more than one element
|
# only process the children if there are more than one element
|
||||||
if len(childElements) <= 1: return num
|
if len(childElements) <= 1:
|
||||||
|
return num
|
||||||
|
|
||||||
commonAttrs = {}
|
commonAttrs = {}
|
||||||
# add all inheritable properties of the first child element
|
# add all inheritable properties of the first child element
|
||||||
|
|
@ -1049,6 +1070,7 @@ def moveCommonAttributesToParentGroup(elem, referencedElements):
|
||||||
num += (len(childElements) - 1) * len(commonAttrs)
|
num += (len(childElements) - 1) * len(commonAttrs)
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
def createGroupsForCommonAttributes(elem):
|
def createGroupsForCommonAttributes(elem):
|
||||||
"""
|
"""
|
||||||
Creates <g> elements to contain runs of 3 or more
|
Creates <g> elements to contain runs of 3 or more
|
||||||
|
|
@ -1114,17 +1136,21 @@ def createGroupsForCommonAttributes(elem):
|
||||||
while runStart > 0:
|
while runStart > 0:
|
||||||
nextNode = elem.childNodes.item(runStart - 1)
|
nextNode = elem.childNodes.item(runStart - 1)
|
||||||
if nextNode.nodeType == 1:
|
if nextNode.nodeType == 1:
|
||||||
if nextNode.getAttribute(curAttr) != value: break
|
if nextNode.getAttribute(curAttr) != value:
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
runElements += 1
|
runElements += 1
|
||||||
runStart -= 1
|
runStart -= 1
|
||||||
else: runStart -= 1
|
else:
|
||||||
|
runStart -= 1
|
||||||
|
|
||||||
if runElements >= 3:
|
if runElements >= 3:
|
||||||
# Include whitespace/comment/etc. nodes in the run.
|
# Include whitespace/comment/etc. nodes in the run.
|
||||||
while runEnd < elem.childNodes.length - 1:
|
while runEnd < elem.childNodes.length - 1:
|
||||||
if elem.childNodes.item(runEnd + 1).nodeType == 1: break
|
if elem.childNodes.item(runEnd + 1).nodeType == 1:
|
||||||
else: runEnd += 1
|
break
|
||||||
|
else:
|
||||||
|
runEnd += 1
|
||||||
|
|
||||||
runLength = runEnd - runStart + 1
|
runLength = runEnd - runStart + 1
|
||||||
if runLength == elem.childNodes.length: # Every child has this
|
if runLength == elem.childNodes.length: # Every child has this
|
||||||
|
|
@ -1170,6 +1196,7 @@ def createGroupsForCommonAttributes(elem):
|
||||||
|
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
def removeUnusedAttributesOnParent(elem):
|
def removeUnusedAttributesOnParent(elem):
|
||||||
"""
|
"""
|
||||||
This recursively calls this function on all children of the element passed in,
|
This recursively calls this function on all children of the element passed in,
|
||||||
|
|
@ -1185,7 +1212,8 @@ def removeUnusedAttributesOnParent(elem):
|
||||||
num += removeUnusedAttributesOnParent(child)
|
num += removeUnusedAttributesOnParent(child)
|
||||||
|
|
||||||
# only process the children if there are more than one element
|
# only process the children if there are more than one element
|
||||||
if len(childElements) <= 1: return num
|
if len(childElements) <= 1:
|
||||||
|
return num
|
||||||
|
|
||||||
# get all attribute values on this parent
|
# get all attribute values on this parent
|
||||||
attrList = elem.attributes
|
attrList = elem.attributes
|
||||||
|
|
@ -1223,6 +1251,7 @@ def removeUnusedAttributesOnParent(elem):
|
||||||
|
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
def removeDuplicateGradientStops(doc):
|
def removeDuplicateGradientStops(doc):
|
||||||
global numElemsRemoved
|
global numElemsRemoved
|
||||||
num = 0
|
num = 0
|
||||||
|
|
@ -1241,8 +1270,10 @@ def removeDuplicateGradientStops(doc):
|
||||||
else:
|
else:
|
||||||
offset = 0
|
offset = 0
|
||||||
# set the stop offset value to the integer or floating point equivalent
|
# set the stop offset value to the integer or floating point equivalent
|
||||||
if int(offset) == offset: stop.setAttribute('offset', str(int(offset)))
|
if int(offset) == offset:
|
||||||
else: stop.setAttribute('offset', str(offset))
|
stop.setAttribute('offset', str(int(offset)))
|
||||||
|
else:
|
||||||
|
stop.setAttribute('offset', str(offset))
|
||||||
|
|
||||||
color = stop.getAttribute('stop-color')
|
color = stop.getAttribute('stop-color')
|
||||||
opacity = stop.getAttribute('stop-opacity')
|
opacity = stop.getAttribute('stop-opacity')
|
||||||
|
|
@ -1261,6 +1292,7 @@ def removeDuplicateGradientStops(doc):
|
||||||
# linear gradients
|
# linear gradients
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
def collapseSinglyReferencedGradients(doc):
|
def collapseSinglyReferencedGradients(doc):
|
||||||
global numElemsRemoved
|
global numElemsRemoved
|
||||||
num = 0
|
num = 0
|
||||||
|
|
@ -1318,6 +1350,7 @@ def collapseSinglyReferencedGradients(doc):
|
||||||
num += 1
|
num += 1
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
def removeDuplicateGradients(doc):
|
def removeDuplicateGradients(doc):
|
||||||
global numElemsRemoved
|
global numElemsRemoved
|
||||||
num = 0
|
num = 0
|
||||||
|
|
@ -1331,7 +1364,8 @@ def removeDuplicateGradients(doc):
|
||||||
# TODO: should slice grads from 'grad' here to optimize
|
# TODO: should slice grads from 'grad' here to optimize
|
||||||
for ograd in grads:
|
for ograd in grads:
|
||||||
# do not compare gradient to itself
|
# do not compare gradient to itself
|
||||||
if grad == ograd: continue
|
if grad == ograd:
|
||||||
|
continue
|
||||||
|
|
||||||
# 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
|
||||||
|
|
@ -1339,9 +1373,10 @@ def removeDuplicateGradients(doc):
|
||||||
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):
|
||||||
someGradAttrsDoNotMatch = True
|
someGradAttrsDoNotMatch = True
|
||||||
break;
|
break
|
||||||
|
|
||||||
if someGradAttrsDoNotMatch: continue
|
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'):
|
||||||
|
|
@ -1351,19 +1386,22 @@ def removeDuplicateGradients(doc):
|
||||||
stops = grad.getElementsByTagName('stop')
|
stops = grad.getElementsByTagName('stop')
|
||||||
ostops = ograd.getElementsByTagName('stop')
|
ostops = ograd.getElementsByTagName('stop')
|
||||||
|
|
||||||
if stops.length != ostops.length: continue
|
if stops.length != ostops.length:
|
||||||
|
continue
|
||||||
|
|
||||||
# now compare stops
|
# now compare stops
|
||||||
stopsNotEqual = False
|
stopsNotEqual = False
|
||||||
for i in range(stops.length):
|
for i in range(stops.length):
|
||||||
if stopsNotEqual: break
|
if stopsNotEqual:
|
||||||
|
break
|
||||||
stop = stops.item(i)
|
stop = stops.item(i)
|
||||||
ostop = ostops.item(i)
|
ostop = ostops.item(i)
|
||||||
for attr in ['offset', 'stop-color', 'stop-opacity', 'style']:
|
for attr in ['offset', 'stop-color', 'stop-opacity', 'style']:
|
||||||
if stop.getAttribute(attr) != ostop.getAttribute(attr):
|
if stop.getAttribute(attr) != ostop.getAttribute(attr):
|
||||||
stopsNotEqual = True
|
stopsNotEqual = True
|
||||||
break
|
break
|
||||||
if stopsNotEqual: continue
|
if stopsNotEqual:
|
||||||
|
continue
|
||||||
|
|
||||||
# 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
|
||||||
|
|
@ -1411,6 +1449,7 @@ def removeDuplicateGradients(doc):
|
||||||
num += 1
|
num += 1
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
def _getStyle(node):
|
def _getStyle(node):
|
||||||
u"""Returns the style attribute of a node as a dictionary."""
|
u"""Returns the style attribute of a node as a dictionary."""
|
||||||
if node.nodeType == 1 and len(node.getAttribute('style')) > 0:
|
if node.nodeType == 1 and len(node.getAttribute('style')) > 0:
|
||||||
|
|
@ -1424,6 +1463,7 @@ def _getStyle(node):
|
||||||
else:
|
else:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def _setStyle(node, styleMap):
|
def _setStyle(node, styleMap):
|
||||||
u"""Sets the style attribute of a node to the dictionary ``styleMap``."""
|
u"""Sets the style attribute of a node to the dictionary ``styleMap``."""
|
||||||
fixedStyle = ';'.join([prop + ':' + styleMap[prop] for prop in list(styleMap.keys())])
|
fixedStyle = ';'.join([prop + ':' + styleMap[prop] for prop in list(styleMap.keys())])
|
||||||
|
|
@ -1433,6 +1473,7 @@ def _setStyle(node, styleMap):
|
||||||
node.removeAttribute('style')
|
node.removeAttribute('style')
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
def repairStyle(node, options):
|
def repairStyle(node, options):
|
||||||
num = 0
|
num = 0
|
||||||
styleMap = _getStyle(node)
|
styleMap = _getStyle(node)
|
||||||
|
|
@ -1564,6 +1605,7 @@ def repairStyle(node, options):
|
||||||
|
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
def styleInheritedFromParent(node, style):
|
def styleInheritedFromParent(node, style):
|
||||||
"""
|
"""
|
||||||
Returns the value of 'style' that is inherited from the parents of the passed-in node
|
Returns the value of 'style' that is inherited from the parents of the passed-in node
|
||||||
|
|
@ -1571,7 +1613,7 @@ def styleInheritedFromParent(node, style):
|
||||||
Warning: This method only considers presentation attributes and inline styles,
|
Warning: This method only considers presentation attributes and inline styles,
|
||||||
any style sheets are ignored!
|
any style sheets are ignored!
|
||||||
"""
|
"""
|
||||||
parentNode = node.parentNode;
|
parentNode = node.parentNode
|
||||||
|
|
||||||
# return None if we reached the Document element
|
# return None if we reached the Document element
|
||||||
if parentNode.nodeType == 9:
|
if parentNode.nodeType == 9:
|
||||||
|
|
@ -1592,6 +1634,7 @@ def styleInheritedFromParent(node, style):
|
||||||
# check the next parent recursively if we did not find a value yet
|
# check the next parent recursively if we did not find a value yet
|
||||||
return styleInheritedFromParent(parentNode, style)
|
return styleInheritedFromParent(parentNode, style)
|
||||||
|
|
||||||
|
|
||||||
def styleInheritedByChild(node, style, nodeIsChild=False):
|
def styleInheritedByChild(node, style, nodeIsChild=False):
|
||||||
"""
|
"""
|
||||||
Returns whether 'style' is inherited by any children of the passed-in node
|
Returns whether 'style' is inherited by any children of the passed-in node
|
||||||
|
|
@ -1609,7 +1652,6 @@ def styleInheritedByChild(node, style, nodeIsChild=False):
|
||||||
if node.nodeType != 1:
|
if node.nodeType != 1:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
if nodeIsChild:
|
if nodeIsChild:
|
||||||
# if the current child node sets a new value for 'style'
|
# if the current child node sets a new value for 'style'
|
||||||
# we can stop the search in the current branch of the DOM tree
|
# we can stop the search in the current branch of the DOM tree
|
||||||
|
|
@ -1641,6 +1683,7 @@ def styleInheritedByChild(node, style, nodeIsChild=False):
|
||||||
# (e.g nodes without children at the end of the DOM tree, text nodes, ...)
|
# (e.g nodes without children at the end of the DOM tree, text nodes, ...)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def mayContainTextNodes(node):
|
def mayContainTextNodes(node):
|
||||||
"""
|
"""
|
||||||
Returns True if the passed-in node is probably a text element, or at least
|
Returns True if the passed-in node is probably a text element, or at least
|
||||||
|
|
@ -1814,6 +1857,7 @@ default_attributes = [
|
||||||
DefaultAttribute('yChannelSelector', 'A', elements='feDisplacementMap')
|
DefaultAttribute('yChannelSelector', 'A', elements='feDisplacementMap')
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
|
@ -1825,6 +1869,7 @@ def taint(taintedSet, taintedAttribute):
|
||||||
taintedSet.add('marker')
|
taintedSet.add('marker')
|
||||||
return taintedSet
|
return taintedSet
|
||||||
|
|
||||||
|
|
||||||
def removeDefaultAttributeValue(node, attribute):
|
def removeDefaultAttributeValue(node, attribute):
|
||||||
"""
|
"""
|
||||||
Removes the DefaultAttribute 'attribute' from 'node' if specified conditions are fulfilled
|
Removes the DefaultAttribute 'attribute' from 'node' if specified conditions are fulfilled
|
||||||
|
|
@ -1851,12 +1896,14 @@ def removeDefaultAttributeValue(node, attribute):
|
||||||
|
|
||||||
return 0
|
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.
|
||||||
|
|
||||||
For such attributes, we don't delete attributes with default values."""
|
For such attributes, we don't delete attributes with default values."""
|
||||||
num = 0
|
num = 0
|
||||||
if node.nodeType != 1: return 0
|
if node.nodeType != 1:
|
||||||
|
return 0
|
||||||
|
|
||||||
# Conditionally remove all default attributes defined in 'default_attributes' (a list of 'DefaultAttribute's)
|
# Conditionally remove all default attributes defined in 'default_attributes' (a list of 'DefaultAttribute's)
|
||||||
for attribute in default_attributes:
|
for attribute in default_attributes:
|
||||||
|
|
@ -1892,6 +1939,8 @@ def removeDefaultAttributeValues(node, options, tainted=set()):
|
||||||
|
|
||||||
rgb = re.compile(r"\s*rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*")
|
rgb = re.compile(r"\s*rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*")
|
||||||
rgbp = re.compile(r"\s*rgb\(\s*(\d*\.?\d+)%\s*,\s*(\d*\.?\d+)%\s*,\s*(\d*\.?\d+)%\s*\)\s*")
|
rgbp = re.compile(r"\s*rgb\(\s*(\d*\.?\d+)%\s*,\s*(\d*\.?\d+)%\s*,\s*(\d*\.?\d+)%\s*\)\s*")
|
||||||
|
|
||||||
|
|
||||||
def convertColor(value):
|
def convertColor(value):
|
||||||
"""
|
"""
|
||||||
Converts the input color string and returns a #RRGGBB (or #RGB if possible) string
|
Converts the input color string and returns a #RRGGBB (or #RGB if possible) string
|
||||||
|
|
@ -1922,17 +1971,19 @@ def convertColor(value):
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def convertColors(element):
|
def convertColors(element):
|
||||||
"""
|
"""
|
||||||
Recursively converts all color properties into #RRGGBB format if shorter
|
Recursively converts all color properties into #RRGGBB format if shorter
|
||||||
"""
|
"""
|
||||||
numBytes = 0
|
numBytes = 0
|
||||||
|
|
||||||
if element.nodeType != 1: return 0
|
if element.nodeType != 1:
|
||||||
|
return 0
|
||||||
|
|
||||||
# set up list of color attributes for each element type
|
# set up list of color attributes for each element type
|
||||||
attrsToConvert = []
|
attrsToConvert = []
|
||||||
if element.nodeName in ['rect', 'circle', 'ellipse', 'polygon', \
|
if element.nodeName in ['rect', 'circle', 'ellipse', 'polygon',
|
||||||
'line', 'polyline', 'path', 'g', 'a']:
|
'line', 'polyline', 'path', 'g', 'a']:
|
||||||
attrsToConvert = ['fill', 'stroke']
|
attrsToConvert = ['fill', 'stroke']
|
||||||
elif element.nodeName in ['stop']:
|
elif element.nodeName in ['stop']:
|
||||||
|
|
@ -1971,6 +2022,8 @@ def convertColors(element):
|
||||||
# TODO: go over what this method does and see if there is a way to optimize it
|
# TODO: go over what this method does and see if there is a way to optimize it
|
||||||
# TODO: go over the performance of this method and see if I can save memory/speed by
|
# TODO: go over the performance of this method and see if I can save memory/speed by
|
||||||
# reusing data structures, etc
|
# reusing data structures, etc
|
||||||
|
|
||||||
|
|
||||||
def cleanPath(element, options):
|
def cleanPath(element, options):
|
||||||
"""
|
"""
|
||||||
Cleans the path string (d attribute) of the element
|
Cleans the path string (d attribute) of the element
|
||||||
|
|
@ -2318,7 +2371,7 @@ def cleanPath(element, options):
|
||||||
newPath.append(('t', [data[i + 2], data[i + 3]]))
|
newPath.append(('t', [data[i + 2], data[i + 3]]))
|
||||||
numPathSegmentsReduced += 1
|
numPathSegmentsReduced += 1
|
||||||
else:
|
else:
|
||||||
j = 0;
|
j = 0
|
||||||
while j <= 3:
|
while j <= 3:
|
||||||
curveTuples.append(data[i + j])
|
curveTuples.append(data[i + j])
|
||||||
j += 1
|
j += 1
|
||||||
|
|
@ -2384,7 +2437,6 @@ def cleanPath(element, options):
|
||||||
element.setAttribute('d', newPathStr)
|
element.setAttribute('d', newPathStr)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def parseListOfPoints(s):
|
def parseListOfPoints(s):
|
||||||
"""
|
"""
|
||||||
Parse string into a list of points.
|
Parse string into a list of points.
|
||||||
|
|
@ -2419,7 +2471,7 @@ def parseListOfPoints(s):
|
||||||
else:
|
else:
|
||||||
# unless we accidentally split a number that was in scientific notation
|
# unless we accidentally split a number that was in scientific notation
|
||||||
# and had a negative exponent (500.00e-1)
|
# and had a negative exponent (500.00e-1)
|
||||||
prev = "";
|
prev = ""
|
||||||
if len(nums):
|
if len(nums):
|
||||||
prev = nums[len(nums) - 1]
|
prev = nums[len(nums) - 1]
|
||||||
if prev and prev[len(prev) - 1] in ['e', 'E']:
|
if prev and prev[len(prev) - 1] in ['e', 'E']:
|
||||||
|
|
@ -2428,7 +2480,8 @@ def parseListOfPoints(s):
|
||||||
nums.append('-' + negcoords[j])
|
nums.append('-' + negcoords[j])
|
||||||
|
|
||||||
# if we have an odd number of points, return empty
|
# if we have an odd number of points, return empty
|
||||||
if len(nums) % 2 != 0: return []
|
if len(nums) % 2 != 0:
|
||||||
|
return []
|
||||||
|
|
||||||
# now resolve into Decimal values
|
# now resolve into Decimal values
|
||||||
i = 0
|
i = 0
|
||||||
|
|
@ -2444,7 +2497,6 @@ def parseListOfPoints(s):
|
||||||
return nums
|
return nums
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def cleanPolygon(elem, options):
|
def cleanPolygon(elem, options):
|
||||||
"""
|
"""
|
||||||
Remove unnecessary closing point of polygon points attribute
|
Remove unnecessary closing point of polygon points attribute
|
||||||
|
|
@ -2462,7 +2514,6 @@ def cleanPolygon(elem, options):
|
||||||
elem.setAttribute('points', scourCoordinates(pts, options, True))
|
elem.setAttribute('points', scourCoordinates(pts, options, True))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def cleanPolyline(elem, options):
|
def cleanPolyline(elem, options):
|
||||||
"""
|
"""
|
||||||
Scour the polyline points attribute
|
Scour the polyline points attribute
|
||||||
|
|
@ -2471,7 +2522,6 @@ def cleanPolyline(elem, options):
|
||||||
elem.setAttribute('points', scourCoordinates(pts, options, True))
|
elem.setAttribute('points', scourCoordinates(pts, options, True))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def serializePath(pathObj, options):
|
def serializePath(pathObj, options):
|
||||||
"""
|
"""
|
||||||
Reserializes the path data with some cleanups.
|
Reserializes the path data with some cleanups.
|
||||||
|
|
@ -2481,7 +2531,6 @@ def serializePath(pathObj, options):
|
||||||
return ''.join([cmd + scourCoordinates(data, options, (cmd == 'a')) for cmd, data in pathObj])
|
return ''.join([cmd + scourCoordinates(data, options, (cmd == 'a')) for cmd, data in pathObj])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def serializeTransform(transformObj):
|
def serializeTransform(transformObj):
|
||||||
"""
|
"""
|
||||||
Reserializes the transform data with some cleanups.
|
Reserializes the transform data with some cleanups.
|
||||||
|
|
@ -2494,7 +2543,6 @@ def serializeTransform(transformObj):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def scourCoordinates(data, options, forceCommaWsp=False):
|
def scourCoordinates(data, options, forceCommaWsp=False):
|
||||||
"""
|
"""
|
||||||
Serializes coordinate data with some cleanups:
|
Serializes coordinate data with some cleanups:
|
||||||
|
|
@ -2545,7 +2593,6 @@ def scourCoordinates(data, options, forceCommaWsp = False):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def scourLength(length):
|
def scourLength(length):
|
||||||
"""
|
"""
|
||||||
Scours a length. Accepts units.
|
Scours a length. Accepts units.
|
||||||
|
|
@ -2555,7 +2602,6 @@ def scourLength(length):
|
||||||
return scourUnitlessLength(length.value) + Unit.str(length.units)
|
return scourUnitlessLength(length.value) + Unit.str(length.units)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def scourUnitlessLength(length, needsRendererWorkaround=False): # length is of a numeric type
|
def scourUnitlessLength(length, needsRendererWorkaround=False): # length is of a numeric type
|
||||||
"""
|
"""
|
||||||
Scours the numeric part of a length only. Does not accept units.
|
Scours the numeric part of a length only. Does not accept units.
|
||||||
|
|
@ -2605,7 +2651,6 @@ def scourUnitlessLength(length, needsRendererWorkaround=False): # length is of a
|
||||||
return nonsci
|
return nonsci
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def reducePrecision(element):
|
def reducePrecision(element):
|
||||||
"""
|
"""
|
||||||
Because opacities, letter spacings, stroke widths and all that don't need
|
Because opacities, letter spacings, stroke widths and all that don't need
|
||||||
|
|
@ -2652,7 +2697,6 @@ def reducePrecision(element):
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def optimizeAngle(angle):
|
def optimizeAngle(angle):
|
||||||
"""
|
"""
|
||||||
Because any rotation can be expressed within 360 degrees
|
Because any rotation can be expressed within 360 degrees
|
||||||
|
|
@ -2664,18 +2708,21 @@ def optimizeAngle(angle):
|
||||||
# The modulo operator yields results with the sign of the
|
# The modulo operator yields results with the sign of the
|
||||||
# divisor, so for negative dividends, we preserve the sign
|
# divisor, so for negative dividends, we preserve the sign
|
||||||
# of the angle.
|
# of the angle.
|
||||||
if angle < 0: angle %= -360
|
if angle < 0:
|
||||||
else: angle %= 360
|
angle %= -360
|
||||||
|
else:
|
||||||
|
angle %= 360
|
||||||
# 720 degrees is unneccessary, as 360 covers all angles.
|
# 720 degrees is unneccessary, as 360 covers all angles.
|
||||||
# As "-x" is shorter than "35x" and "-xxx" one character
|
# As "-x" is shorter than "35x" and "-xxx" one character
|
||||||
# longer than positive angles <= 260, we constrain angle
|
# longer than positive angles <= 260, we constrain angle
|
||||||
# range to [-90, 270[ (or, equally valid: ]-100, 260]).
|
# range to [-90, 270[ (or, equally valid: ]-100, 260]).
|
||||||
if angle >= 270: angle -= 360
|
if angle >= 270:
|
||||||
elif angle < -90: angle += 360
|
angle -= 360
|
||||||
|
elif angle < -90:
|
||||||
|
angle += 360
|
||||||
return angle
|
return angle
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def optimizeTransform(transform):
|
def optimizeTransform(transform):
|
||||||
"""
|
"""
|
||||||
Optimises a series of transformations parsed from a single
|
Optimises a series of transformations parsed from a single
|
||||||
|
|
@ -2836,7 +2883,6 @@ def optimizeTransform(transform):
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def optimizeTransforms(element, options):
|
def optimizeTransforms(element, options):
|
||||||
"""
|
"""
|
||||||
Attempts to optimise transform specifications on the given node and its children.
|
Attempts to optimise transform specifications on the given node and its children.
|
||||||
|
|
@ -2868,7 +2914,6 @@ def optimizeTransforms(element, options):
|
||||||
return num
|
return num
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def removeComments(element):
|
def removeComments(element):
|
||||||
"""
|
"""
|
||||||
Removes comments from the element and its children.
|
Removes comments from the element and its children.
|
||||||
|
|
@ -2883,7 +2928,6 @@ def removeComments(element):
|
||||||
removeComments(subelement)
|
removeComments(subelement)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def embedRasters(element, options):
|
def embedRasters(element, options):
|
||||||
import base64
|
import base64
|
||||||
import urllib
|
import urllib
|
||||||
|
|
@ -2911,7 +2955,8 @@ def embedRasters(element, options):
|
||||||
# if this is not an absolute path, set path relative
|
# if this is not an absolute path, set path relative
|
||||||
# to script file based on input arg
|
# to script file based on input arg
|
||||||
infilename = '.'
|
infilename = '.'
|
||||||
if options.infilename: infilename = options.infilename
|
if options.infilename:
|
||||||
|
infilename = options.infilename
|
||||||
href = os.path.join(os.path.dirname(infilename), href)
|
href = os.path.join(os.path.dirname(infilename), href)
|
||||||
|
|
||||||
rasterdata = ''
|
rasterdata = ''
|
||||||
|
|
@ -2942,7 +2987,6 @@ def embedRasters(element, options):
|
||||||
del b64eRaster
|
del b64eRaster
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def properlySizeDoc(docElement, options):
|
def properlySizeDoc(docElement, options):
|
||||||
# get doc width and height
|
# get doc width and height
|
||||||
w = SVGLength(docElement.getAttribute('width'))
|
w = SVGLength(docElement.getAttribute('width'))
|
||||||
|
|
@ -2984,9 +3028,9 @@ def properlySizeDoc(docElement, options):
|
||||||
docElement.removeAttribute('height')
|
docElement.removeAttribute('height')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def remapNamespacePrefix(node, oldprefix, newprefix):
|
def remapNamespacePrefix(node, oldprefix, newprefix):
|
||||||
if node == None or node.nodeType != 1: return
|
if node == None or node.nodeType != 1:
|
||||||
|
return
|
||||||
|
|
||||||
if node.prefix == oldprefix:
|
if node.prefix == oldprefix:
|
||||||
localName = node.localName
|
localName = node.localName
|
||||||
|
|
@ -2999,7 +3043,7 @@ def remapNamespacePrefix(node, oldprefix, newprefix):
|
||||||
if newprefix != '':
|
if newprefix != '':
|
||||||
newNode = doc.createElementNS(namespace, newprefix + ":" + localName)
|
newNode = doc.createElementNS(namespace, newprefix + ":" + localName)
|
||||||
else:
|
else:
|
||||||
newNode = doc.createElement(localName);
|
newNode = doc.createElement(localName)
|
||||||
|
|
||||||
# add all the attributes
|
# add all the attributes
|
||||||
attrList = node.attributes
|
attrList = node.attributes
|
||||||
|
|
@ -3021,7 +3065,6 @@ def remapNamespacePrefix(node, oldprefix, newprefix):
|
||||||
remapNamespacePrefix(child, oldprefix, newprefix)
|
remapNamespacePrefix(child, oldprefix, newprefix)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def makeWellFormed(str):
|
def makeWellFormed(str):
|
||||||
# Don't escape quotation marks for now as they are fine in text nodes
|
# Don't escape quotation marks for now as they are fine in text nodes
|
||||||
# as well as in attributes if used reciprocally
|
# as well as in attributes if used reciprocally
|
||||||
|
|
@ -3039,7 +3082,6 @@ def makeWellFormed(str):
|
||||||
return ''.join([xml_ents[c] if c in xml_ents else c for c in str])
|
return ''.join([xml_ents[c] if c in xml_ents else c for c in str])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# hand-rolled serialization function that has the following benefits:
|
# hand-rolled serialization function that has the following benefits:
|
||||||
# - pretty printing
|
# - pretty printing
|
||||||
# - somewhat judicious use of whitespace
|
# - somewhat judicious use of whitespace
|
||||||
|
|
@ -3051,8 +3093,10 @@ def serializeXML(element, options, ind = 0, preserveWhitespace = False):
|
||||||
I = ''
|
I = ''
|
||||||
newline = ''
|
newline = ''
|
||||||
if options.newlines:
|
if options.newlines:
|
||||||
if options.indent_type == 'tab': I='\t'
|
if options.indent_type == 'tab':
|
||||||
elif options.indent_type == 'space': I=' '
|
I = '\t'
|
||||||
|
elif options.indent_type == 'space':
|
||||||
|
I = ' '
|
||||||
I *= options.indent_depth
|
I *= options.indent_depth
|
||||||
newline = '\n'
|
newline = '\n'
|
||||||
|
|
||||||
|
|
@ -3096,7 +3140,8 @@ def serializeXML(element, options, ind = 0, preserveWhitespace = False):
|
||||||
attrIndices += [attrName2Index[name] for name in sorted(attrName2Index.keys())]
|
attrIndices += [attrName2Index[name] for name in sorted(attrName2Index.keys())]
|
||||||
for index in attrIndices:
|
for index in attrIndices:
|
||||||
attr = attrList.item(index)
|
attr = attrList.item(index)
|
||||||
if attr.nodeName == 'id' or attr.nodeName == 'xml:id': continue
|
if attr.nodeName == 'id' or attr.nodeName == 'xml:id':
|
||||||
|
continue
|
||||||
# if the attribute value contains a double-quote, use single-quotes
|
# if the attribute value contains a double-quote, use single-quotes
|
||||||
quot = '"'
|
quot = '"'
|
||||||
if attr.nodeValue.find('"') != -1:
|
if attr.nodeValue.find('"') != -1:
|
||||||
|
|
@ -3156,17 +3201,19 @@ def serializeXML(element, options, ind = 0, preserveWhitespace = False):
|
||||||
else: # ignore the rest
|
else: # ignore the rest
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if onNewLine: outParts.append(I * ind)
|
if onNewLine:
|
||||||
|
outParts.append(I * ind)
|
||||||
outParts.extend(['</', element.nodeName, '>'])
|
outParts.extend(['</', element.nodeName, '>'])
|
||||||
if indent > 0: outParts.append(newline)
|
if indent > 0:
|
||||||
|
outParts.append(newline)
|
||||||
else:
|
else:
|
||||||
outParts.append('/>')
|
outParts.append('/>')
|
||||||
if indent > 0: outParts.append(newline)
|
if indent > 0:
|
||||||
|
outParts.append(newline)
|
||||||
|
|
||||||
return "".join(outParts)
|
return "".join(outParts)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# this is the main method
|
# this is the main method
|
||||||
# input is a string representation of the input XML
|
# input is a string representation of the input XML
|
||||||
# returns a string representation of the output XML
|
# returns a string representation of the output XML
|
||||||
|
|
@ -3414,7 +3461,6 @@ def scourString(in_string, options=None):
|
||||||
return total_output
|
return total_output
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# used mostly by unit tests
|
# used mostly by unit tests
|
||||||
# input is a filename
|
# input is a filename
|
||||||
# returns the minidom doc representation of the SVG
|
# returns the minidom doc representation of the SVG
|
||||||
|
|
@ -3438,19 +3484,18 @@ def scourXmlFile(filename, options=None):
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# GZ: Seems most other commandline tools don't do this, is it really wanted?
|
# GZ: Seems most other commandline tools don't do this, is it really wanted?
|
||||||
class HeaderedFormatter(optparse.IndentedHelpFormatter):
|
class HeaderedFormatter(optparse.IndentedHelpFormatter):
|
||||||
"""
|
"""
|
||||||
Show application name, version number, and copyright statement
|
Show application name, version number, and copyright statement
|
||||||
above usage information.
|
above usage information.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def format_usage(self, usage):
|
def format_usage(self, usage):
|
||||||
return "%s %s\n%s\n%s" % (APP, VER, COPYRIGHT,
|
return "%s %s\n%s\n%s" % (APP, VER, COPYRIGHT,
|
||||||
optparse.IndentedHelpFormatter.format_usage(self, usage))
|
optparse.IndentedHelpFormatter.format_usage(self, usage))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# GZ: would prefer this to be in a function or class scope, but tests etc need
|
# GZ: would prefer this to be in a function or class scope, but tests etc need
|
||||||
# access to the defaults anyway
|
# access to the defaults anyway
|
||||||
_options_parser = optparse.OptionParser(
|
_options_parser = optparse.OptionParser(
|
||||||
|
|
@ -3575,7 +3620,6 @@ _option_group_compatibility.add_option("--error-on-flowtext",
|
||||||
_options_parser.add_option_group(_option_group_compatibility)
|
_options_parser.add_option_group(_option_group_compatibility)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def parse_args(args=None, ignore_additional_args=False):
|
def parse_args(args=None, ignore_additional_args=False):
|
||||||
options, rargs = _options_parser.parse_args(args)
|
options, rargs = _options_parser.parse_args(args)
|
||||||
|
|
||||||
|
|
@ -3598,10 +3642,10 @@ def parse_args(args=None, ignore_additional_args=False):
|
||||||
return options
|
return options
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def generateDefaultOptions():
|
def generateDefaultOptions():
|
||||||
## FIXME: clean up this mess/hack and refactor arg parsing to argparse
|
# FIXME: clean up this mess/hack and refactor arg parsing to argparse
|
||||||
class Struct:
|
class Struct:
|
||||||
|
|
||||||
def __init__(self, **entries):
|
def __init__(self, **entries):
|
||||||
self.__dict__.update(entries)
|
self.__dict__.update(entries)
|
||||||
|
|
||||||
|
|
@ -3610,7 +3654,6 @@ def generateDefaultOptions():
|
||||||
return Struct(**d)
|
return Struct(**d)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# sanitizes options by updating attributes in a set of defaults options while discarding unknown attributes
|
# sanitizes options by updating attributes in a set of defaults options while discarding unknown attributes
|
||||||
def sanitizeOptions(options):
|
def sanitizeOptions(options):
|
||||||
optionsDict = dict((key, getattr(options, key)) for key in dir(options) if not key.startswith('__'))
|
optionsDict = dict((key, getattr(options, key)) for key in dir(options) if not key.startswith('__'))
|
||||||
|
|
@ -3621,7 +3664,6 @@ def sanitizeOptions(options):
|
||||||
return sanitizedOptions
|
return sanitizedOptions
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def maybe_gziped_file(filename, mode="r"):
|
def maybe_gziped_file(filename, mode="r"):
|
||||||
if os.path.splitext(filename)[1].lower() in (".svgz", ".gz"):
|
if os.path.splitext(filename)[1].lower() in (".svgz", ".gz"):
|
||||||
import gzip
|
import gzip
|
||||||
|
|
@ -3629,7 +3671,6 @@ def maybe_gziped_file(filename, mode="r"):
|
||||||
return open(filename, mode)
|
return open(filename, mode)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def getInOut(options):
|
def getInOut(options):
|
||||||
if options.infilename:
|
if options.infilename:
|
||||||
infile = maybe_gziped_file(options.infilename, "rb")
|
infile = maybe_gziped_file(options.infilename, "rb")
|
||||||
|
|
@ -3660,7 +3701,6 @@ def getInOut(options):
|
||||||
return [infile, outfile]
|
return [infile, outfile]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def getReport():
|
def getReport():
|
||||||
return ' Number of elements removed: ' + str(numElemsRemoved) + os.linesep + \
|
return ' Number of elements removed: ' + str(numElemsRemoved) + os.linesep + \
|
||||||
' Number of attributes removed: ' + str(numAttrsRemoved) + os.linesep + \
|
' Number of attributes removed: ' + str(numAttrsRemoved) + os.linesep + \
|
||||||
|
|
@ -3677,7 +3717,6 @@ def getReport():
|
||||||
' Number of bytes saved in transformations: ' + str(numBytesSavedInTransforms)
|
' Number of bytes saved in transformations: ' + str(numBytesSavedInTransforms)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def start(options, input, output):
|
def start(options, input, output):
|
||||||
|
|
||||||
start = walltime()
|
start = walltime()
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,10 @@ from decimal import *
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
# Sentinel.
|
# Sentinel.
|
||||||
|
|
||||||
|
|
||||||
class _EOF(object):
|
class _EOF(object):
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'EOF'
|
return 'EOF'
|
||||||
EOF = _EOF()
|
EOF = _EOF()
|
||||||
|
|
@ -70,6 +73,7 @@ class Lexer(object):
|
||||||
|
|
||||||
http://www.gooli.org/blog/a-simple-lexer-in-python/
|
http://www.gooli.org/blog/a-simple-lexer-in-python/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, lexicon):
|
def __init__(self, lexicon):
|
||||||
self.lexicon = lexicon
|
self.lexicon = lexicon
|
||||||
parts = []
|
parts = []
|
||||||
|
|
@ -270,7 +274,6 @@ class SVGPathParser(object):
|
||||||
token = next_val_fn()
|
token = next_val_fn()
|
||||||
return x, token
|
return x, token
|
||||||
|
|
||||||
|
|
||||||
def rule_coordinate_pair(self, next_val_fn, token):
|
def rule_coordinate_pair(self, next_val_fn, token):
|
||||||
# Inline these since this rule is so common.
|
# Inline these since this rule is so common.
|
||||||
if token[0] not in self.number_tokens:
|
if token[0] not in self.number_tokens:
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ from functools import partial
|
||||||
|
|
||||||
# Sentinel.
|
# Sentinel.
|
||||||
class _EOF(object):
|
class _EOF(object):
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'EOF'
|
return 'EOF'
|
||||||
EOF = _EOF()
|
EOF = _EOF()
|
||||||
|
|
@ -89,6 +90,7 @@ class Lexer(object):
|
||||||
|
|
||||||
http://www.gooli.org/blog/a-simple-lexer-in-python/
|
http://www.gooli.org/blog/a-simple-lexer-in-python/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, lexicon):
|
def __init__(self, lexicon):
|
||||||
self.lexicon = lexicon
|
self.lexicon = lexicon
|
||||||
parts = []
|
parts = []
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@
|
||||||
# | DASHMATCH | FUNCTION S* any* ')'
|
# | DASHMATCH | FUNCTION S* any* ')'
|
||||||
# | '(' S* any* ')' | '[' S* any* ']' ] S*;
|
# | '(' S* any* ')' | '[' S* any* ']' ] S*;
|
||||||
|
|
||||||
|
|
||||||
def parseCssString(str):
|
def parseCssString(str):
|
||||||
rules = []
|
rules = []
|
||||||
# first, split on } to get the rule chunks
|
# first, split on } to get the rule chunks
|
||||||
|
|
@ -55,17 +56,20 @@ def parseCssString(str):
|
||||||
for chunk in chunks:
|
for chunk in chunks:
|
||||||
# second, split on { to get the selector and the list of properties
|
# second, split on { to get the selector and the list of properties
|
||||||
bits = chunk.split('{')
|
bits = chunk.split('{')
|
||||||
if len(bits) != 2: continue
|
if len(bits) != 2:
|
||||||
|
continue
|
||||||
rule = {}
|
rule = {}
|
||||||
rule['selector'] = bits[0].strip()
|
rule['selector'] = bits[0].strip()
|
||||||
# third, split on ; to get the property declarations
|
# third, split on ; to get the property declarations
|
||||||
bites = bits[1].strip().split(';')
|
bites = bits[1].strip().split(';')
|
||||||
if len(bites) < 1: continue
|
if len(bites) < 1:
|
||||||
|
continue
|
||||||
props = {}
|
props = {}
|
||||||
for bite in bites:
|
for bite in bites:
|
||||||
# fourth, split on : to get the property name and value
|
# fourth, split on : to get the property name and value
|
||||||
nibbles = bite.strip().split(':')
|
nibbles = bite.strip().split(':')
|
||||||
if len(nibbles) != 2: continue
|
if len(nibbles) != 2:
|
||||||
|
continue
|
||||||
props[nibbles[0].strip()] = nibbles[1].strip()
|
props[nibbles[0].strip()] = nibbles[1].strip()
|
||||||
rule['properties'] = props
|
rule['properties'] = props
|
||||||
rules.append(rule)
|
rules.append(rule)
|
||||||
|
|
|
||||||
30
setup.py
30
setup.py
|
|
@ -1,19 +1,19 @@
|
||||||
###############################################################################
|
###############################################################################
|
||||||
##
|
#
|
||||||
## Copyright (C) 2013-2014 Tavendo GmbH
|
# Copyright (C) 2013-2014 Tavendo GmbH
|
||||||
##
|
#
|
||||||
## Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
## you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
## You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
##
|
#
|
||||||
## http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
##
|
#
|
||||||
## Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
## distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
## See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
## limitations under the License.
|
# limitations under the License.
|
||||||
##
|
#
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
|
||||||
|
|
@ -27,19 +27,24 @@ from scour.yocto_css import parseCssString
|
||||||
|
|
||||||
|
|
||||||
class Blank(unittest.TestCase):
|
class Blank(unittest.TestCase):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
r = parseCssString('')
|
r = parseCssString('')
|
||||||
self.assertEqual(len(r), 0, 'Blank string returned non-empty list')
|
self.assertEqual(len(r), 0, 'Blank string returned non-empty list')
|
||||||
self.assertEqual(type(r), type([]), 'Blank string returned non list')
|
self.assertEqual(type(r), type([]), 'Blank string returned non list')
|
||||||
|
|
||||||
|
|
||||||
class ElementSelector(unittest.TestCase):
|
class ElementSelector(unittest.TestCase):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
r = parseCssString('foo {}')
|
r = parseCssString('foo {}')
|
||||||
self.assertEqual(len(r), 1, 'Element selector not returned')
|
self.assertEqual(len(r), 1, 'Element selector not returned')
|
||||||
self.assertEqual(r[0]['selector'], 'foo', 'Selector for foo not returned')
|
self.assertEqual(r[0]['selector'], 'foo', 'Selector for foo not returned')
|
||||||
self.assertEqual(len(r[0]['properties']), 0, 'Property list for foo not empty')
|
self.assertEqual(len(r[0]['properties']), 0, 'Property list for foo not empty')
|
||||||
|
|
||||||
|
|
||||||
class ElementSelectorWithProperty(unittest.TestCase):
|
class ElementSelectorWithProperty(unittest.TestCase):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
r = parseCssString('foo { bar: baz}')
|
r = parseCssString('foo { bar: baz}')
|
||||||
self.assertEqual(len(r), 1, 'Element selector not returned')
|
self.assertEqual(len(r), 1, 'Element selector not returned')
|
||||||
|
|
|
||||||
476
testscour.py
476
testscour.py
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue