Python 3 Updates
Ran python-modernizer, this fixed most of the Python 3 incompatibilities. Had to manually rework the iteration in svg_regex and svg_transform; .next doesn't exist in Python 3. Wrapped the builtin next function in a partial for equivalent functionality.
This commit is contained in:
parent
85dff51cc9
commit
d1c66cc75b
5 changed files with 158 additions and 150 deletions
|
|
@ -15,8 +15,3 @@
|
|||
## limitations under the License.
|
||||
##
|
||||
###############################################################################
|
||||
|
||||
import scour
|
||||
import svg_regex
|
||||
import svg_transform
|
||||
import yocto_css
|
||||
|
|
|
|||
160
scour/scour.py
160
scour/scour.py
|
|
@ -47,21 +47,27 @@
|
|||
# necessary to get true division
|
||||
from __future__ import division
|
||||
|
||||
# Needed for Python 2/3 compatible print function.
|
||||
from __future__ import print_function
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import sys
|
||||
import xml.dom.minidom
|
||||
import re
|
||||
import math
|
||||
from svg_regex import svg_parser
|
||||
from svg_transform import svg_transform_parser
|
||||
from .svg_regex import svg_parser
|
||||
from .svg_transform import svg_transform_parser
|
||||
import optparse
|
||||
from yocto_css import parseCssString
|
||||
from .yocto_css import parseCssString
|
||||
import six
|
||||
from six.moves import range
|
||||
|
||||
# Python 2.3- did not have Decimal
|
||||
try:
|
||||
from decimal import *
|
||||
from decimal import Decimal, InvalidOperation, getcontext
|
||||
except ImportError:
|
||||
print >>sys.stderr, "Scour requires Python 2.4."
|
||||
print("Scour requires Python 2.4.", file=sys.stderr)
|
||||
|
||||
# Import Psyco if available
|
||||
try:
|
||||
|
|
@ -531,7 +537,7 @@ def findReferencingProperty(node, prop, val, ids):
|
|||
if prop in referencingProps and val != '' :
|
||||
if len(val) >= 7 and val[0:5] == 'url(#' :
|
||||
id = val[5:val.find(')')]
|
||||
if ids.has_key(id) :
|
||||
if id in ids :
|
||||
ids[id][0] += 1
|
||||
ids[id][1].append(node)
|
||||
else:
|
||||
|
|
@ -546,7 +552,7 @@ def findReferencingProperty(node, prop, val, ids):
|
|||
elif val[0:6] == "url('#" :
|
||||
id = val[6:val.find("')")]
|
||||
if id != None:
|
||||
if ids.has_key(id) :
|
||||
if id in ids :
|
||||
ids[id][0] += 1
|
||||
ids[id][1].append(node)
|
||||
else:
|
||||
|
|
@ -762,7 +768,7 @@ def unprotected_ids(doc, options):
|
|||
protect_ids_list = options.protect_ids_list.split(",")
|
||||
if options.protect_ids_prefix:
|
||||
protect_ids_prefixes = options.protect_ids_prefix.split(",")
|
||||
for id in identifiedElements.keys():
|
||||
for id in list(identifiedElements.keys()):
|
||||
protected = False
|
||||
if options.protect_ids_noninkscape and not id[-1].isdigit():
|
||||
protected = True
|
||||
|
|
@ -785,9 +791,9 @@ def removeUnreferencedIDs(referencedIDs, identifiedElements):
|
|||
global numIDsRemoved
|
||||
keepTags = ['font']
|
||||
num = 0;
|
||||
for id in identifiedElements.keys():
|
||||
for id in list(identifiedElements.keys()):
|
||||
node = identifiedElements[id]
|
||||
if referencedIDs.has_key(id) == False and not node.nodeName in keepTags:
|
||||
if (id in referencedIDs) == False and not node.nodeName in keepTags:
|
||||
node.removeAttribute('id')
|
||||
numIDsRemoved += 1
|
||||
num += 1
|
||||
|
|
@ -800,7 +806,7 @@ def removeNamespacedAttributes(node, namespaces):
|
|||
# remove all namespace'd attributes from this element
|
||||
attrList = node.attributes
|
||||
attrsToRemove = []
|
||||
for attrNum in xrange(attrList.length):
|
||||
for attrNum in range(attrList.length):
|
||||
attr = attrList.item(attrNum)
|
||||
if attr != None and attr.namespaceURI in namespaces:
|
||||
attrsToRemove.append(attr.nodeName)
|
||||
|
|
@ -915,7 +921,7 @@ def moveCommonAttributesToParentGroup(elem, referencedElements):
|
|||
# its fill attribute is not what we want to look at, we should look for the first
|
||||
# non-animate/set element
|
||||
attrList = childElements[0].attributes
|
||||
for num in xrange(attrList.length):
|
||||
for num in range(attrList.length):
|
||||
attr = attrList.item(num)
|
||||
# this is most of the inheritable properties from http://www.w3.org/TR/SVG11/propidx.html
|
||||
# and http://www.w3.org/TR/SVGTiny12/attributeTable.html
|
||||
|
|
@ -934,7 +940,7 @@ def moveCommonAttributesToParentGroup(elem, referencedElements):
|
|||
commonAttrs[attr.nodeName] = attr.nodeValue
|
||||
|
||||
# for each subsequent child element
|
||||
for childNum in xrange(len(childElements)):
|
||||
for childNum in range(len(childElements)):
|
||||
# skip first child
|
||||
if childNum == 0:
|
||||
continue
|
||||
|
|
@ -946,7 +952,7 @@ def moveCommonAttributesToParentGroup(elem, referencedElements):
|
|||
|
||||
distinctAttrs = []
|
||||
# loop through all current 'common' attributes
|
||||
for name in commonAttrs.keys():
|
||||
for name in list(commonAttrs.keys()):
|
||||
# if this child doesn't match that attribute, schedule it for removal
|
||||
if child.getAttribute(name) != commonAttrs[name]:
|
||||
distinctAttrs.append(name)
|
||||
|
|
@ -955,7 +961,7 @@ def moveCommonAttributesToParentGroup(elem, referencedElements):
|
|||
del commonAttrs[name]
|
||||
|
||||
# commonAttrs now has all the inheritable attributes which are common among all child elements
|
||||
for name in commonAttrs.keys():
|
||||
for name in list(commonAttrs.keys()):
|
||||
for child in childElements:
|
||||
child.removeAttribute(name)
|
||||
elem.setAttribute(name, commonAttrs[name])
|
||||
|
|
@ -1088,7 +1094,7 @@ def removeUnusedAttributesOnParent(elem):
|
|||
# get all attribute values on this parent
|
||||
attrList = elem.attributes
|
||||
unusedAttrs = {}
|
||||
for num in xrange(attrList.length):
|
||||
for num in range(attrList.length):
|
||||
attr = attrList.item(num)
|
||||
if attr.nodeName in ['clip-rule',
|
||||
'display-align',
|
||||
|
|
@ -1104,10 +1110,10 @@ def removeUnusedAttributesOnParent(elem):
|
|||
unusedAttrs[attr.nodeName] = attr.nodeValue
|
||||
|
||||
# for each child, if at least one child inherits the parent's attribute, then remove
|
||||
for childNum in xrange(len(childElements)):
|
||||
for childNum in range(len(childElements)):
|
||||
child = childElements[childNum]
|
||||
inheritedAttrs = []
|
||||
for name in unusedAttrs.keys():
|
||||
for name in list(unusedAttrs.keys()):
|
||||
val = child.getAttribute(name)
|
||||
if val == '' or val == None or val == 'inherit':
|
||||
inheritedAttrs.append(name)
|
||||
|
|
@ -1115,7 +1121,7 @@ def removeUnusedAttributesOnParent(elem):
|
|||
del unusedAttrs[a]
|
||||
|
||||
# unusedAttrs now has all the parent attributes that are unused
|
||||
for name in unusedAttrs.keys():
|
||||
for name in list(unusedAttrs.keys()):
|
||||
elem.removeAttribute(name)
|
||||
num += 1
|
||||
|
||||
|
|
@ -1145,7 +1151,7 @@ def removeDuplicateGradientStops(doc):
|
|||
color = stop.getAttribute('stop-color')
|
||||
opacity = stop.getAttribute('stop-opacity')
|
||||
style = stop.getAttribute('style')
|
||||
if stops.has_key(offset) :
|
||||
if offset in stops :
|
||||
oldStop = stops[offset]
|
||||
if oldStop[0] == color and oldStop[1] == opacity and oldStop[2] == style:
|
||||
stopsToRemove.append(stop)
|
||||
|
|
@ -1166,7 +1172,7 @@ def collapseSinglyReferencedGradients(doc):
|
|||
identifiedElements = findElementsWithId(doc.documentElement)
|
||||
|
||||
# make sure to reset the ref'ed ids for when we are running this in testscour
|
||||
for rid,nodeCount in findReferencedElements(doc.documentElement).iteritems():
|
||||
for rid,nodeCount in six.iteritems(findReferencedElements(doc.documentElement)):
|
||||
count = nodeCount[0]
|
||||
nodes = nodeCount[1]
|
||||
# Make sure that there's actually a defining element for the current ID name.
|
||||
|
|
@ -1253,7 +1259,7 @@ def removeDuplicateGradients(doc):
|
|||
|
||||
# now compare stops
|
||||
stopsNotEqual = False
|
||||
for i in xrange(stops.length):
|
||||
for i in range(stops.length):
|
||||
if stopsNotEqual: break
|
||||
stop = stops.item(i)
|
||||
ostop = ostops.item(i)
|
||||
|
|
@ -1265,16 +1271,16 @@ def removeDuplicateGradients(doc):
|
|||
|
||||
# ograd is a duplicate of grad, we schedule it to be removed UNLESS
|
||||
# ograd is ALREADY considered a 'master' element
|
||||
if not gradientsToRemove.has_key(ograd):
|
||||
if not duplicateToMaster.has_key(ograd):
|
||||
if not gradientsToRemove.has_key(grad):
|
||||
if ograd not in gradientsToRemove:
|
||||
if ograd not in duplicateToMaster:
|
||||
if grad not in gradientsToRemove:
|
||||
gradientsToRemove[grad] = []
|
||||
gradientsToRemove[grad].append( ograd )
|
||||
duplicateToMaster[ograd] = grad
|
||||
|
||||
# get a collection of all elements that are referenced and their referencing elements
|
||||
referencedIDs = findReferencedElements(doc.documentElement)
|
||||
for masterGrad in gradientsToRemove.keys():
|
||||
for masterGrad in list(gradientsToRemove.keys()):
|
||||
master_id = masterGrad.getAttribute('id')
|
||||
# print 'master='+master_id
|
||||
for dupGrad in gradientsToRemove[masterGrad]:
|
||||
|
|
@ -1322,7 +1328,7 @@ def _getStyle(node):
|
|||
|
||||
def _setStyle(node, styleMap):
|
||||
u"""Sets the style attribute of a node to the dictionary ``styleMap``."""
|
||||
fixedStyle = ';'.join([prop + ':' + styleMap[prop] for prop in styleMap.keys()])
|
||||
fixedStyle = ';'.join([prop + ':' + styleMap[prop] for prop in list(styleMap.keys())])
|
||||
if fixedStyle != '' :
|
||||
node.setAttribute('style', fixedStyle)
|
||||
elif node.getAttribute('style'):
|
||||
|
|
@ -1337,7 +1343,7 @@ def repairStyle(node, options):
|
|||
# I've seen this enough to know that I need to correct it:
|
||||
# fill: url(#linearGradient4918) rgb(0, 0, 0);
|
||||
for prop in ['fill', 'stroke'] :
|
||||
if styleMap.has_key(prop) :
|
||||
if prop in styleMap :
|
||||
chunk = styleMap[prop].split(') ')
|
||||
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] + ')'
|
||||
|
|
@ -1345,23 +1351,23 @@ def repairStyle(node, options):
|
|||
|
||||
# Here is where we can weed out unnecessary styles like:
|
||||
# opacity:1
|
||||
if styleMap.has_key('opacity') :
|
||||
if 'opacity' in styleMap :
|
||||
opacity = float(styleMap['opacity'])
|
||||
# if opacity='0' then all fill and stroke properties are useless, remove them
|
||||
if opacity == 0.0 :
|
||||
for uselessStyle in ['fill', 'fill-opacity', 'fill-rule', 'stroke', 'stroke-linejoin',
|
||||
'stroke-opacity', 'stroke-miterlimit', 'stroke-linecap', 'stroke-dasharray',
|
||||
'stroke-dashoffset', 'stroke-opacity'] :
|
||||
if styleMap.has_key(uselessStyle):
|
||||
if uselessStyle in styleMap:
|
||||
del styleMap[uselessStyle]
|
||||
num += 1
|
||||
|
||||
# if stroke:none, then remove all stroke-related properties (stroke-width, etc)
|
||||
# TODO: should also detect if the computed value of this element is stroke="none"
|
||||
if styleMap.has_key('stroke') and styleMap['stroke'] == 'none' :
|
||||
if 'stroke' in styleMap and styleMap['stroke'] == 'none' :
|
||||
for strokestyle in [ 'stroke-width', 'stroke-linejoin', 'stroke-miterlimit',
|
||||
'stroke-linecap', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-opacity'] :
|
||||
if styleMap.has_key(strokestyle) :
|
||||
if strokestyle in styleMap :
|
||||
del styleMap[strokestyle]
|
||||
num += 1
|
||||
# TODO: This is actually a problem if a parent element has a specified stroke
|
||||
|
|
@ -1369,38 +1375,38 @@ def repairStyle(node, options):
|
|||
del styleMap['stroke']
|
||||
|
||||
# if fill:none, then remove all fill-related properties (fill-rule, etc)
|
||||
if styleMap.has_key('fill') and styleMap['fill'] == 'none' :
|
||||
if 'fill' in styleMap and styleMap['fill'] == 'none' :
|
||||
for fillstyle in [ 'fill-rule', 'fill-opacity' ] :
|
||||
if styleMap.has_key(fillstyle) :
|
||||
if fillstyle in styleMap :
|
||||
del styleMap[fillstyle]
|
||||
num += 1
|
||||
|
||||
# fill-opacity: 0
|
||||
if styleMap.has_key('fill-opacity') :
|
||||
if 'fill-opacity' in styleMap :
|
||||
fillOpacity = float(styleMap['fill-opacity'])
|
||||
if fillOpacity == 0.0 :
|
||||
for uselessFillStyle in [ 'fill', 'fill-rule' ] :
|
||||
if styleMap.has_key(uselessFillStyle):
|
||||
if uselessFillStyle in styleMap:
|
||||
del styleMap[uselessFillStyle]
|
||||
num += 1
|
||||
|
||||
# stroke-opacity: 0
|
||||
if styleMap.has_key('stroke-opacity') :
|
||||
if 'stroke-opacity' in styleMap :
|
||||
strokeOpacity = float(styleMap['stroke-opacity'])
|
||||
if strokeOpacity == 0.0 :
|
||||
for uselessStrokeStyle in [ 'stroke', 'stroke-width', 'stroke-linejoin', 'stroke-linecap',
|
||||
'stroke-dasharray', 'stroke-dashoffset' ] :
|
||||
if styleMap.has_key(uselessStrokeStyle):
|
||||
if uselessStrokeStyle in styleMap:
|
||||
del styleMap[uselessStrokeStyle]
|
||||
num += 1
|
||||
|
||||
# stroke-width: 0
|
||||
if styleMap.has_key('stroke-width') :
|
||||
if 'stroke-width' in styleMap :
|
||||
strokeWidth = SVGLength(styleMap['stroke-width'])
|
||||
if strokeWidth.value == 0.0 :
|
||||
for uselessStrokeStyle in [ 'stroke', 'stroke-linejoin', 'stroke-linecap',
|
||||
'stroke-dasharray', 'stroke-dashoffset', 'stroke-opacity' ] :
|
||||
if styleMap.has_key(uselessStrokeStyle):
|
||||
if uselessStrokeStyle in styleMap:
|
||||
del styleMap[uselessStrokeStyle]
|
||||
num += 1
|
||||
|
||||
|
|
@ -1413,18 +1419,18 @@ def repairStyle(node, options):
|
|||
'text-align', 'text-anchor', 'text-decoration',
|
||||
'text-rendering', 'unicode-bidi',
|
||||
'word-spacing', 'writing-mode'] :
|
||||
if styleMap.has_key(fontstyle) :
|
||||
if fontstyle in styleMap :
|
||||
del styleMap[fontstyle]
|
||||
num += 1
|
||||
|
||||
# remove inkscape-specific styles
|
||||
# TODO: need to get a full list of these
|
||||
for inkscapeStyle in ['-inkscape-font-specification']:
|
||||
if styleMap.has_key(inkscapeStyle):
|
||||
if inkscapeStyle in styleMap:
|
||||
del styleMap[inkscapeStyle]
|
||||
num += 1
|
||||
|
||||
if styleMap.has_key('overflow') :
|
||||
if 'overflow' in styleMap :
|
||||
# overflow specified on element other than svg, marker, pattern
|
||||
if not node.nodeName in ['svg','marker','pattern']:
|
||||
del styleMap['overflow']
|
||||
|
|
@ -1445,7 +1451,7 @@ def repairStyle(node, options):
|
|||
# now if any of the properties match known SVG attributes we prefer attributes
|
||||
# over style so emit them and remove them from the style map
|
||||
if options.style_to_xml:
|
||||
for propName in styleMap.keys() :
|
||||
for propName in list(styleMap.keys()) :
|
||||
if propName in svgAttributes :
|
||||
node.setAttribute(propName, styleMap[propName])
|
||||
del styleMap[propName]
|
||||
|
|
@ -1594,7 +1600,7 @@ def removeDefaultAttributeValues(node, options, tainted=set()):
|
|||
for i in range(node.attributes.length)]
|
||||
for attribute in attributes:
|
||||
if attribute not in tainted:
|
||||
if attribute in default_attributes.keys():
|
||||
if attribute in list(default_attributes.keys()):
|
||||
if node.getAttribute(attribute) == default_attributes[attribute]:
|
||||
node.removeAttribute(attribute)
|
||||
num += 1
|
||||
|
|
@ -1602,9 +1608,9 @@ def removeDefaultAttributeValues(node, options, tainted=set()):
|
|||
tainted = taint(tainted, attribute)
|
||||
# These attributes might also occur as styles
|
||||
styles = _getStyle(node)
|
||||
for attribute in styles.keys():
|
||||
for attribute in list(styles.keys()):
|
||||
if attribute not in tainted:
|
||||
if attribute in default_attributes.keys():
|
||||
if attribute in list(default_attributes.keys()):
|
||||
if styles[attribute] == default_attributes[attribute]:
|
||||
del styles[attribute]
|
||||
num += 1
|
||||
|
|
@ -1626,7 +1632,7 @@ def convertColor(value):
|
|||
"""
|
||||
s = value
|
||||
|
||||
if s in colors.keys():
|
||||
if s in list(colors.keys()):
|
||||
s = colors[s]
|
||||
|
||||
rgbpMatch = rgbp.match(s)
|
||||
|
|
@ -1680,7 +1686,7 @@ def convertColors(element) :
|
|||
element.setAttribute(attr, newColorValue)
|
||||
numBytes += (oldBytes - len(element.getAttribute(attr)))
|
||||
# colors might also hide in styles
|
||||
if attr in styles.keys():
|
||||
if attr in list(styles.keys()):
|
||||
oldColorValue = styles[attr]
|
||||
newColorValue = convertColor(oldColorValue)
|
||||
oldBytes = len(oldColorValue)
|
||||
|
|
@ -1722,13 +1728,13 @@ def cleanPath(element, options) :
|
|||
# convert absolute coordinates into relative ones.
|
||||
# Reuse the data structure 'path', since we're not adding or removing subcommands.
|
||||
# Also reuse the coordinate lists since we're not adding or removing any.
|
||||
for pathIndex in xrange(0, len(path)):
|
||||
for pathIndex in range(0, len(path)):
|
||||
cmd, data = path[pathIndex] # Changes to cmd don't get through to the data structure
|
||||
i = 0
|
||||
# adjust abs to rel
|
||||
# only the A command has some values that we don't want to adjust (radii, rotation, flags)
|
||||
if cmd == 'A':
|
||||
for i in xrange(i, len(data), 7):
|
||||
for i in range(i, len(data), 7):
|
||||
data[i+5] -= x
|
||||
data[i+6] -= y
|
||||
x += data[i+5]
|
||||
|
|
@ -1738,14 +1744,14 @@ def cleanPath(element, options) :
|
|||
x += sum(data[5::7])
|
||||
y += sum(data[6::7])
|
||||
elif cmd == 'H':
|
||||
for i in xrange(i, len(data)):
|
||||
for i in range(i, len(data)):
|
||||
data[i] -= x
|
||||
x += data[i]
|
||||
path[pathIndex] = ('h', data)
|
||||
elif cmd == 'h':
|
||||
x += sum(data)
|
||||
elif cmd == 'V':
|
||||
for i in xrange(i, len(data)):
|
||||
for i in range(i, len(data)):
|
||||
data[i] -= y
|
||||
y += data[i]
|
||||
path[pathIndex] = ('v', data)
|
||||
|
|
@ -1761,14 +1767,14 @@ def cleanPath(element, options) :
|
|||
|
||||
x, y = startx, starty
|
||||
i = 2
|
||||
for i in xrange(i, len(data), 2):
|
||||
for i in range(i, len(data), 2):
|
||||
data[i] -= x
|
||||
data[i+1] -= y
|
||||
x += data[i]
|
||||
y += data[i+1]
|
||||
path[pathIndex] = ('m', data)
|
||||
elif cmd in ['L','T']:
|
||||
for i in xrange(i, len(data), 2):
|
||||
for i in range(i, len(data), 2):
|
||||
data[i] -= x
|
||||
data[i+1] -= y
|
||||
x += data[i]
|
||||
|
|
@ -1784,14 +1790,14 @@ def cleanPath(element, options) :
|
|||
else:
|
||||
startx = x + data[0]
|
||||
starty = y + data[1]
|
||||
for i in xrange(i, len(data), 2):
|
||||
for i in range(i, len(data), 2):
|
||||
x += data[i]
|
||||
y += data[i+1]
|
||||
elif cmd in ['l','t']:
|
||||
x += sum(data[0::2])
|
||||
y += sum(data[1::2])
|
||||
elif cmd in ['S','Q']:
|
||||
for i in xrange(i, len(data), 4):
|
||||
for i in range(i, len(data), 4):
|
||||
data[i] -= x
|
||||
data[i+1] -= y
|
||||
data[i+2] -= x
|
||||
|
|
@ -1803,7 +1809,7 @@ def cleanPath(element, options) :
|
|||
x += sum(data[2::4])
|
||||
y += sum(data[3::4])
|
||||
elif cmd == 'C':
|
||||
for i in xrange(i, len(data), 6):
|
||||
for i in range(i, len(data), 6):
|
||||
data[i] -= x
|
||||
data[i+1] -= y
|
||||
data[i+2] -= x
|
||||
|
|
@ -1824,7 +1830,7 @@ def cleanPath(element, options) :
|
|||
# Reuse the data structure 'path' and the coordinate lists, even if we're
|
||||
# deleting items, because these deletions are relatively cheap.
|
||||
if not withRoundLineCaps:
|
||||
for pathIndex in xrange(0, len(path)):
|
||||
for pathIndex in range(0, len(path)):
|
||||
cmd, data = path[pathIndex]
|
||||
i = 0
|
||||
if cmd in ['m','l','t']:
|
||||
|
|
@ -2059,7 +2065,7 @@ def cleanPath(element, options) :
|
|||
# Reuse the data structure 'path', since we're not adding or removing subcommands.
|
||||
# Also reuse the coordinate lists, even if we're deleting items, because these
|
||||
# deletions are relatively cheap.
|
||||
for pathIndex in xrange(1, len(path)):
|
||||
for pathIndex in range(1, len(path)):
|
||||
cmd, data = path[pathIndex]
|
||||
if cmd in ['h','v'] and len(data) > 1:
|
||||
coordIndex = 1
|
||||
|
|
@ -2120,7 +2126,7 @@ def parseListOfPoints(s):
|
|||
|
||||
# also, if 100-100 is found, split it into two also
|
||||
# <polygon points="100,-100,100-100,100-100-100,-100-100" />
|
||||
for i in xrange(len(ws_nums)):
|
||||
for i in range(len(ws_nums)):
|
||||
negcoords = ws_nums[i].split("-")
|
||||
|
||||
# this string didn't have any negative coordinates
|
||||
|
|
@ -2128,7 +2134,7 @@ def parseListOfPoints(s):
|
|||
nums.append(negcoords[0])
|
||||
# we got negative coords
|
||||
else:
|
||||
for j in xrange(len(negcoords)):
|
||||
for j in range(len(negcoords)):
|
||||
# first number could be positive
|
||||
if j == 0:
|
||||
if negcoords[0] != '':
|
||||
|
|
@ -2152,7 +2158,7 @@ def parseListOfPoints(s):
|
|||
try:
|
||||
nums[i] = getcontext().create_decimal(nums[i])
|
||||
nums[i + 1] = getcontext().create_decimal(nums[i + 1])
|
||||
except decimal.InvalidOperation: # one of the lengths had a unit or is an invalid number
|
||||
except InvalidOperation: # one of the lengths had a unit or is an invalid number
|
||||
return []
|
||||
|
||||
i += 2
|
||||
|
|
@ -2251,7 +2257,7 @@ def scourCoordinates(data, options, forceCommaWsp = False):
|
|||
# separate from the next number.
|
||||
if options.renderer_workaround:
|
||||
if len(newData) > 0:
|
||||
for i in xrange(1, len(newData)):
|
||||
for i in range(1, len(newData)):
|
||||
if newData[i][0] == '-' and 'e' in newData[i - 1]:
|
||||
newData[i - 1] += ' '
|
||||
return ''.join(newData)
|
||||
|
|
@ -2290,7 +2296,7 @@ def scourUnitlessLength(length, needsRendererWorkaround=False): # length is of a
|
|||
# gather the non-scientific notation version of the coordinate.
|
||||
# this may actually be in scientific notation if the value is
|
||||
# sufficiently large or small, so this is a misnomer.
|
||||
nonsci = unicode(length).lower().replace("e+", "e")
|
||||
nonsci = six.text_type(length).lower().replace("e+", "e")
|
||||
if not needsRendererWorkaround:
|
||||
if len(nonsci) > 2 and nonsci[:2] == '0.':
|
||||
nonsci = nonsci[1:] # remove the 0, leave the dot
|
||||
|
|
@ -2300,7 +2306,7 @@ def scourUnitlessLength(length, needsRendererWorkaround=False): # length is of a
|
|||
if len(nonsci) > 3: # avoid calling normalize unless strictly necessary
|
||||
# and then the scientific notation version, with E+NUMBER replaced with
|
||||
# just eNUMBER, since SVG accepts this.
|
||||
sci = unicode(length.normalize()).lower().replace("e+", "e")
|
||||
sci = six.text_type(length.normalize()).lower().replace("e+", "e")
|
||||
|
||||
if len(sci) < len(nonsci): return sci
|
||||
else: return nonsci
|
||||
|
|
@ -2337,7 +2343,7 @@ def reducePrecision(element) :
|
|||
num += len(val) - len(newVal)
|
||||
element.setAttribute(lengthAttr, newVal)
|
||||
# repeat for attributes hidden in styles
|
||||
if lengthAttr in styles.keys():
|
||||
if lengthAttr in list(styles.keys()):
|
||||
val = styles[lengthAttr]
|
||||
valLen = SVGLength(val)
|
||||
if valLen.units != Unit.INVALID:
|
||||
|
|
@ -2714,7 +2720,7 @@ def remapNamespacePrefix(node, oldprefix, newprefix):
|
|||
|
||||
# add all the attributes
|
||||
attrList = node.attributes
|
||||
for i in xrange(attrList.length):
|
||||
for i in range(attrList.length):
|
||||
attr = attrList.item(i)
|
||||
newNode.setAttributeNS( attr.namespaceURI, attr.localName, attr.nodeValue)
|
||||
|
||||
|
|
@ -2778,7 +2784,7 @@ def serializeXML(element, options, ind = 0, preserveWhitespace = False):
|
|||
|
||||
# now serialize the other attributes
|
||||
attrList = element.attributes
|
||||
for num in xrange(attrList.length) :
|
||||
for num in range(attrList.length) :
|
||||
attr = attrList.item(num)
|
||||
if attr.nodeName == 'id' or attr.nodeName == 'xml:id': continue
|
||||
# if the attribute value contains a double-quote, use single-quotes
|
||||
|
|
@ -2877,7 +2883,7 @@ def scourString(in_string, options=None):
|
|||
# remove the xmlns: declarations now
|
||||
xmlnsDeclsToRemove = []
|
||||
attrList = doc.documentElement.attributes
|
||||
for num in xrange(attrList.length) :
|
||||
for num in range(attrList.length) :
|
||||
if attrList.item(num).nodeValue in unwanted_ns :
|
||||
xmlnsDeclsToRemove.append(attrList.item(num).nodeName)
|
||||
|
||||
|
|
@ -2895,7 +2901,7 @@ def scourString(in_string, options=None):
|
|||
attrList = doc.documentElement.attributes
|
||||
xmlnsDeclsToRemove = []
|
||||
redundantPrefixes = []
|
||||
for i in xrange(attrList.length):
|
||||
for i in range(attrList.length):
|
||||
attr = attrList.item(i)
|
||||
name = attr.nodeName
|
||||
val = attr.nodeValue
|
||||
|
|
@ -3174,7 +3180,7 @@ def maybe_gziped_file(filename, mode="r"):
|
|||
if os.path.splitext(filename)[1].lower() in (".svgz", ".gz"):
|
||||
import gzip
|
||||
return gzip.GzipFile(filename, mode)
|
||||
return file(filename, mode)
|
||||
return open(filename, mode)
|
||||
|
||||
|
||||
|
||||
|
|
@ -3245,7 +3251,7 @@ def start(options, input, output):
|
|||
start = get_tick()
|
||||
|
||||
if not options.quiet:
|
||||
print >>sys.stderr, "%s %s\n%s" % (APP, VER, COPYRIGHT)
|
||||
print("%s %s\n%s" % (APP, VER, COPYRIGHT), file=sys.stderr)
|
||||
|
||||
# do the work
|
||||
in_string = input.read()
|
||||
|
|
@ -3260,15 +3266,15 @@ def start(options, input, output):
|
|||
|
||||
# GZ: not using globals would be good too
|
||||
if not options.quiet:
|
||||
print >>sys.stderr, ' File:', input.name, \
|
||||
print(' File:', input.name, \
|
||||
os.linesep + ' Time taken:', str(end-start) + 's' + os.linesep, \
|
||||
getReport()
|
||||
getReport(), file=sys.stderr)
|
||||
|
||||
oldsize = len(in_string)
|
||||
newsize = len(out_string)
|
||||
sizediff = (newsize / oldsize) * 100
|
||||
print >>sys.stderr, ' Original file size:', oldsize, 'bytes;', \
|
||||
'new file size:', newsize, 'bytes (' + str(sizediff)[:5] + '%)'
|
||||
print(' Original file size:', oldsize, 'bytes;', \
|
||||
'new file size:', newsize, 'bytes (' + str(sizediff)[:5] + '%)', file=sys.stderr)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -41,10 +41,11 @@ Out[4]: [('M', [(0.60509999999999997, 0.5)])]
|
|||
In [5]: svg_parser.parse('M 100-200') # Another edge case
|
||||
Out[5]: [('M', [(100.0, -200.0)])]
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import re
|
||||
from decimal import *
|
||||
|
||||
from functools import partial
|
||||
|
||||
# Sentinel.
|
||||
class _EOF(object):
|
||||
|
|
@ -145,140 +146,141 @@ class SVGPathParser(object):
|
|||
def parse(self, text):
|
||||
""" Parse a string of SVG <path> data.
|
||||
"""
|
||||
next = self.lexer.lex(text).next
|
||||
token = next()
|
||||
return self.rule_svg_path(next, token)
|
||||
gen = self.lexer.lex(text)
|
||||
next_val_fn = partial(next, *(gen,))
|
||||
token = next_val_fn()
|
||||
return self.rule_svg_path(next_val_fn, token)
|
||||
|
||||
def rule_svg_path(self, next, token):
|
||||
def rule_svg_path(self, next_val_fn, token):
|
||||
commands = []
|
||||
while token[0] is not EOF:
|
||||
if token[0] != 'command':
|
||||
raise SyntaxError("expecting a command; got %r" % (token,))
|
||||
rule = self.command_dispatch[token[1]]
|
||||
command_group, token = rule(next, token)
|
||||
command_group, token = rule(next_val_fn, token)
|
||||
commands.append(command_group)
|
||||
return commands
|
||||
|
||||
def rule_closepath(self, next, token):
|
||||
def rule_closepath(self, next_val_fn, token):
|
||||
command = token[1]
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
return (command, []), token
|
||||
|
||||
def rule_moveto_or_lineto(self, next, token):
|
||||
def rule_moveto_or_lineto(self, next_val_fn, token):
|
||||
command = token[1]
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
coordinates = []
|
||||
while token[0] in self.number_tokens:
|
||||
pair, token = self.rule_coordinate_pair(next, token)
|
||||
pair, token = self.rule_coordinate_pair(next_val_fn, token)
|
||||
coordinates.extend(pair)
|
||||
return (command, coordinates), token
|
||||
|
||||
def rule_orthogonal_lineto(self, next, token):
|
||||
def rule_orthogonal_lineto(self, next_val_fn, token):
|
||||
command = token[1]
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
coordinates = []
|
||||
while token[0] in self.number_tokens:
|
||||
coord, token = self.rule_coordinate(next, token)
|
||||
coord, token = self.rule_coordinate(next_val_fn, token)
|
||||
coordinates.append(coord)
|
||||
return (command, coordinates), token
|
||||
|
||||
def rule_curveto3(self, next, token):
|
||||
def rule_curveto3(self, next_val_fn, token):
|
||||
command = token[1]
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
coordinates = []
|
||||
while token[0] in self.number_tokens:
|
||||
pair1, token = self.rule_coordinate_pair(next, token)
|
||||
pair2, token = self.rule_coordinate_pair(next, token)
|
||||
pair3, token = self.rule_coordinate_pair(next, token)
|
||||
pair1, token = self.rule_coordinate_pair(next_val_fn, token)
|
||||
pair2, token = self.rule_coordinate_pair(next_val_fn, token)
|
||||
pair3, token = self.rule_coordinate_pair(next_val_fn, token)
|
||||
coordinates.extend(pair1)
|
||||
coordinates.extend(pair2)
|
||||
coordinates.extend(pair3)
|
||||
return (command, coordinates), token
|
||||
|
||||
def rule_curveto2(self, next, token):
|
||||
def rule_curveto2(self, next_val_fn, token):
|
||||
command = token[1]
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
coordinates = []
|
||||
while token[0] in self.number_tokens:
|
||||
pair1, token = self.rule_coordinate_pair(next, token)
|
||||
pair2, token = self.rule_coordinate_pair(next, token)
|
||||
pair1, token = self.rule_coordinate_pair(next_val_fn, token)
|
||||
pair2, token = self.rule_coordinate_pair(next_val_fn, token)
|
||||
coordinates.extend(pair1)
|
||||
coordinates.extend(pair2)
|
||||
return (command, coordinates), token
|
||||
|
||||
def rule_curveto1(self, next, token):
|
||||
def rule_curveto1(self, next_val_fn, token):
|
||||
command = token[1]
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
coordinates = []
|
||||
while token[0] in self.number_tokens:
|
||||
pair1, token = self.rule_coordinate_pair(next, token)
|
||||
pair1, token = self.rule_coordinate_pair(next_val_fn, token)
|
||||
coordinates.extend(pair1)
|
||||
return (command, coordinates), token
|
||||
|
||||
def rule_elliptical_arc(self, next, token):
|
||||
def rule_elliptical_arc(self, next_val_fn, token):
|
||||
command = token[1]
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
arguments = []
|
||||
while token[0] in self.number_tokens:
|
||||
rx = Decimal(token[1]) * 1
|
||||
if rx < Decimal("0.0"):
|
||||
raise SyntaxError("expecting a nonnegative number; got %r" % (token,))
|
||||
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
if token[0] not in self.number_tokens:
|
||||
raise SyntaxError("expecting a number; got %r" % (token,))
|
||||
ry = Decimal(token[1]) * 1
|
||||
if ry < Decimal("0.0"):
|
||||
raise SyntaxError("expecting a nonnegative number; got %r" % (token,))
|
||||
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
if token[0] not in self.number_tokens:
|
||||
raise SyntaxError("expecting a number; got %r" % (token,))
|
||||
axis_rotation = Decimal(token[1]) * 1
|
||||
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
if token[1] not in ('0', '1'):
|
||||
raise SyntaxError("expecting a boolean flag; got %r" % (token,))
|
||||
large_arc_flag = Decimal(token[1]) * 1
|
||||
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
if token[1] not in ('0', '1'):
|
||||
raise SyntaxError("expecting a boolean flag; got %r" % (token,))
|
||||
sweep_flag = Decimal(token[1]) * 1
|
||||
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
if token[0] not in self.number_tokens:
|
||||
raise SyntaxError("expecting a number; got %r" % (token,))
|
||||
x = Decimal(token[1]) * 1
|
||||
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
if token[0] not in self.number_tokens:
|
||||
raise SyntaxError("expecting a number; got %r" % (token,))
|
||||
y = Decimal(token[1]) * 1
|
||||
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
arguments.extend([rx, ry, axis_rotation, large_arc_flag, sweep_flag, x, y])
|
||||
|
||||
return (command, arguments), token
|
||||
|
||||
def rule_coordinate(self, next, token):
|
||||
def rule_coordinate(self, next_val_fn, token):
|
||||
if token[0] not in self.number_tokens:
|
||||
raise SyntaxError("expecting a number; got %r" % (token,))
|
||||
x = getcontext().create_decimal(token[1])
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
return x, token
|
||||
|
||||
|
||||
def rule_coordinate_pair(self, next, token):
|
||||
def rule_coordinate_pair(self, next_val_fn, token):
|
||||
# Inline these since this rule is so common.
|
||||
if token[0] not in self.number_tokens:
|
||||
raise SyntaxError("expecting a number; got %r" % (token,))
|
||||
x = getcontext().create_decimal(token[1])
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
if token[0] not in self.number_tokens:
|
||||
raise SyntaxError("expecting a number; got %r" % (token,))
|
||||
y = getcontext().create_decimal(token[1])
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
return [x, y], token
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -56,9 +56,12 @@ Multiple transformations are supported:
|
|||
In [12]: svg_transform_parser.parse('translate(30 -30) rotate(36)')
|
||||
Out[12]: [('translate', [30.0, -30.0]), ('rotate', [36.0])]
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import re
|
||||
from decimal import *
|
||||
from six.moves import range
|
||||
from functools import partial
|
||||
|
||||
|
||||
# Sentinel.
|
||||
|
|
@ -145,88 +148,90 @@ class SVGTransformationParser(object):
|
|||
def parse(self, text):
|
||||
""" Parse a string of SVG transform="" data.
|
||||
"""
|
||||
next = self.lexer.lex(text).next
|
||||
gen = self.lexer.lex(text)
|
||||
next_val_fn = partial(next, *(gen,))
|
||||
|
||||
commands = []
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
while token[0] is not EOF:
|
||||
command, token = self.rule_svg_transform(next, token)
|
||||
command, token = self.rule_svg_transform(next_val_fn, token)
|
||||
commands.append(command)
|
||||
return commands
|
||||
|
||||
def rule_svg_transform(self, next, token):
|
||||
def rule_svg_transform(self, next_val_fn, token):
|
||||
if token[0] != 'command':
|
||||
raise SyntaxError("expecting a transformation type; got %r" % (token,))
|
||||
command = token[1]
|
||||
rule = self.command_dispatch[command]
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
if token[0] != 'coordstart':
|
||||
raise SyntaxError("expecting '('; got %r" % (token,))
|
||||
numbers, token = rule(next, token)
|
||||
numbers, token = rule(next_val_fn, token)
|
||||
if token[0] != 'coordend':
|
||||
raise SyntaxError("expecting ')'; got %r" % (token,))
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
return (command, numbers), token
|
||||
|
||||
def rule_1or2numbers(self, next, token):
|
||||
def rule_1or2numbers(self, next_val_fn, token):
|
||||
numbers = []
|
||||
# 1st number is mandatory
|
||||
token = next()
|
||||
number, token = self.rule_number(next, token)
|
||||
token = next_val_fn()
|
||||
number, token = self.rule_number(next_val_fn, token)
|
||||
numbers.append(number)
|
||||
# 2nd number is optional
|
||||
number, token = self.rule_optional_number(next, token)
|
||||
number, token = self.rule_optional_number(next_val_fn, token)
|
||||
if number is not None:
|
||||
numbers.append(number)
|
||||
|
||||
return numbers, token
|
||||
|
||||
def rule_1number(self, next, token):
|
||||
def rule_1number(self, next_val_fn, token):
|
||||
# this number is mandatory
|
||||
token = next()
|
||||
number, token = self.rule_number(next, token)
|
||||
token = next_val_fn()
|
||||
number, token = self.rule_number(next_val_fn, token)
|
||||
numbers = [number]
|
||||
return numbers, token
|
||||
|
||||
def rule_1or3numbers(self, next, token):
|
||||
def rule_1or3numbers(self, next_val_fn, token):
|
||||
numbers = []
|
||||
# 1st number is mandatory
|
||||
token = next()
|
||||
number, token = self.rule_number(next, token)
|
||||
token = next_val_fn()
|
||||
number, token = self.rule_number(next_val_fn, token)
|
||||
numbers.append(number)
|
||||
# 2nd number is optional
|
||||
number, token = self.rule_optional_number(next, token)
|
||||
number, token = self.rule_optional_number(next_val_fn, token)
|
||||
if number is not None:
|
||||
# but, if the 2nd number is provided, the 3rd is mandatory.
|
||||
# we can't have just 2.
|
||||
numbers.append(number)
|
||||
|
||||
number, token = self.rule_number(next, token)
|
||||
number, token = self.rule_number(next_val_fn, token)
|
||||
numbers.append(number)
|
||||
|
||||
return numbers, token
|
||||
|
||||
def rule_6numbers(self, next, token):
|
||||
def rule_6numbers(self, next_val_fn, token):
|
||||
numbers = []
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
# all numbers are mandatory
|
||||
for i in xrange(6):
|
||||
number, token = self.rule_number(next, token)
|
||||
for i in range(6):
|
||||
number, token = self.rule_number(next_val_fn, token)
|
||||
numbers.append(number)
|
||||
return numbers, token
|
||||
|
||||
def rule_number(self, next, token):
|
||||
def rule_number(self, next_val_fn, token):
|
||||
if token[0] not in self.number_tokens:
|
||||
raise SyntaxError("expecting a number; got %r" % (token,))
|
||||
x = Decimal(token[1]) * 1
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
return x, token
|
||||
|
||||
def rule_optional_number(self, next, token):
|
||||
def rule_optional_number(self, next_val_fn, token):
|
||||
if token[0] not in self.number_tokens:
|
||||
return None, token
|
||||
else:
|
||||
x = Decimal(token[1]) * 1
|
||||
token = next()
|
||||
token = next_val_fn()
|
||||
return x, token
|
||||
|
||||
|
||||
|
|
|
|||
2
setup.py
2
setup.py
|
|
@ -41,7 +41,7 @@ setup (
|
|||
author_email = 'codedread@gmail.com',
|
||||
url = 'https://github.com/oberstet/scour',
|
||||
platforms = ('Any'),
|
||||
install_requires = [],
|
||||
install_requires = ['six>=1.9.0'],
|
||||
packages = find_packages(),
|
||||
zip_safe = True,
|
||||
entry_points = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue