diff --git a/scour/scour.py b/scour/scour.py index 7e538b1..34c6b05 100644 --- a/scour/scour.py +++ b/scour/scour.py @@ -70,7 +70,7 @@ except ImportError: pass APP = 'scour' -VER = '0.26' +VER = '0.27' COPYRIGHT = 'Copyright Jeff Schiller, Louis Simard, 2010' NS = { 'SVG': 'http://www.w3.org/2000/svg', @@ -2099,6 +2099,8 @@ def cleanPath(element, options) : numBytesSavedInPathData += ( len(oldPathStr) - len(newPathStr) ) element.setAttribute('d', newPathStr) + + def parseListOfPoints(s): """ Parse string into a list of points. @@ -2155,6 +2157,8 @@ def parseListOfPoints(s): return nums + + def cleanPolygon(elem, options): """ Remove unnecessary closing point of polygon points attribute @@ -2171,6 +2175,8 @@ def cleanPolygon(elem, options): numPointsRemovedFromPolygon += 1 elem.setAttribute('points', scourCoordinates(pts, options, True)) + + def cleanPolyline(elem, options): """ Scour the polyline points attribute @@ -2178,6 +2184,8 @@ def cleanPolyline(elem, options): pts = parseListOfPoints(elem.getAttribute('points')) elem.setAttribute('points', scourCoordinates(pts, options, True)) + + def serializePath(pathObj, options): """ Reserializes the path data with some cleanups. @@ -2186,6 +2194,8 @@ def serializePath(pathObj, options): # this fixes an issue outlined in Fix https://bugs.launchpad.net/scour/+bug/412754 return ''.join([cmd + scourCoordinates(data, options, (cmd == 'a')) for cmd, data in pathObj]) + + def serializeTransform(transformObj): """ Reserializes the transform data with some cleanups. @@ -2197,6 +2207,8 @@ def serializeTransform(transformObj): for command, numbers in transformObj] ) + + def scourCoordinates(data, options, forceCommaWsp = False): """ Serializes coordinate data with some cleanups: @@ -2246,6 +2258,8 @@ def scourCoordinates(data, options, forceCommaWsp = False): return '' + + def scourLength(length): """ Scours a length. Accepts units. @@ -2254,6 +2268,8 @@ def scourLength(length): return scourUnitlessLength(length.value) + Unit.str(length.units) + + def scourUnitlessLength(length, needsRendererWorkaround=False): # length is of a numeric type """ Scours the numeric part of a length only. Does not accept units. @@ -2288,6 +2304,8 @@ def scourUnitlessLength(length, needsRendererWorkaround=False): # length is of a else: return nonsci else: return nonsci + + def reducePrecision(element) : """ Because opacities, letter spacings, stroke widths and all that don't need @@ -2333,6 +2351,8 @@ def reducePrecision(element) : return num + + def optimizeAngle(angle): """ Because any rotation can be expressed within 360 degrees @@ -2355,6 +2375,7 @@ def optimizeAngle(angle): return angle + def optimizeTransform(transform): """ Optimises a series of transformations parsed from a single @@ -2514,6 +2535,8 @@ def optimizeTransform(transform): else: i += 1 + + def optimizeTransforms(element, options) : """ Attempts to optimise transform specifications on the given node and its children. @@ -2544,6 +2567,8 @@ def optimizeTransforms(element, options) : return num + + def removeComments(element) : """ Removes comments from the element and its children. @@ -2566,6 +2591,8 @@ def removeComments(element) : for subelement in element.childNodes: removeComments(subelement) + + def embedRasters(element, options) : import base64 import urllib @@ -2623,6 +2650,8 @@ def embedRasters(element, options) : numRastersEmbedded += 1 del b64eRaster + + def properlySizeDoc(docElement, options): # get doc width and height w = SVGLength(docElement.getAttribute('width')) @@ -2663,6 +2692,8 @@ def properlySizeDoc(docElement, options): docElement.removeAttribute('width') docElement.removeAttribute('height') + + def remapNamespacePrefix(node, oldprefix, newprefix): if node == None or node.nodeType != 1: return @@ -2698,6 +2729,8 @@ def remapNamespacePrefix(node, oldprefix, newprefix): for child in node.childNodes : remapNamespacePrefix(child, oldprefix, newprefix) + + def makeWellFormed(str): xml_ents = { '<':'<', '>':'>', '&':'&', "'":''', '"':'"'} @@ -2711,6 +2744,8 @@ def makeWellFormed(str): # this list comprehension is short-form for the above for-loop: 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: # - pretty printing # - somewhat judicious use of whitespace @@ -2809,6 +2844,8 @@ def serializeXML(element, options, ind = 0, preserveWhitespace = False): return "".join(outParts) + + # this is the main method # input is a string representation of the input XML # returns a string representation of the output XML @@ -3020,6 +3057,8 @@ def scourString(in_string, options=None): return total_output + + # used mostly by unit tests # input is a filename # returns the minidom doc representation of the SVG @@ -3028,6 +3067,8 @@ def scourXmlFile(filename, options=None): out_string = scourString(in_string, options) return xml.dom.minidom.parseString(out_string.encode('utf-8')) + + # GZ: Seems most other commandline tools don't do this, is it really wanted? class HeaderedFormatter(optparse.IndentedHelpFormatter): """ @@ -3038,6 +3079,8 @@ class HeaderedFormatter(optparse.IndentedHelpFormatter): return "%s %s\n%s\n%s" % (APP, VER, COPYRIGHT, optparse.IndentedHelpFormatter.format_usage(self, usage)) + + # GZ: would prefer this to be in a function or class scope, but tests etc need # access to the defaults anyway _options_parser = optparse.OptionParser( @@ -3117,12 +3160,16 @@ _options_parser.add_option("--protect-ids-prefix", action="store", type="string", dest="protect_ids_prefix", default=None, help="Don't change IDs starting with the given prefix") + + 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) + + def parse_args(args=None): options, rargs = _options_parser.parse_args(args) @@ -3148,6 +3195,8 @@ def parse_args(args=None): return options, [infile, outfile] + + def getReport(): return ' Number of elements removed: ' + str(numElemsRemoved) + os.linesep + \ ' Number of attributes removed: ' + str(numAttrsRemoved) + os.linesep + \ @@ -3164,7 +3213,20 @@ def getReport(): ' Number of bytes saved in transformations: ' + str(numBytesSavedInTransforms) -def run(): + +def generateDefaultOptions(): + ## FIXME: clean up this mess/hack and refactor arg parsing to argparse + class Struct: + def __init__(self, **entries): + self.__dict__.update(entries) + + d = parse_args()[0].__dict__.copy() + + return Struct(**d) + + + +def start(options, input, output): if sys.platform == "win32": from time import clock as get_tick else: @@ -3174,8 +3236,6 @@ def run(): start = get_tick() - options, (input, output) = parse_args() - if not options.quiet: print >>sys.stderr, "%s %s\n%s" % (APP, VER, COPYRIGHT) @@ -3203,6 +3263,12 @@ def run(): 'new file size:', newsize, 'bytes (' + str(sizediff)[:5] + '%)' + +def run(): + options, (input, output) = parse_args() + start(options, input, output) + + + if __name__ == '__main__': run() - diff --git a/setup.py b/setup.py index 30b9080..73a7134 100644 --- a/setup.py +++ b/setup.py @@ -18,15 +18,28 @@ from setuptools import setup, find_packages +LONGDESC = """ +Scour is a SVG optimizer/sanitizer that can be used to produce SVGs for Web deployment. + +Website + - http://www.codedread.com/scour/ (original website) + - https://github.com/oberstet/scour (today) + +Authors: + - Jeff Schiller, Louis Simard (original authors) + - Tobias Oberstein (maintainer) +""" + setup ( name = 'scour', version = '0.27', description = 'Scour SVG Optimizer', - long_description = open("README.md").read(), +# long_description = open("README.md").read(), + long_description = LONGDESC, license = 'Apache License 2.0', author = 'Jeff Schiller', author_email = 'codedread@gmail.com', - url = 'http://blog.codedread.com/', + url = 'https://github.com/oberstet/scour', platforms = ('Any'), install_requires = [], packages = find_packages(),