From fc356815a275d558bb29f63dfd262c5885ae9b7f Mon Sep 17 00:00:00 2001 From: Eduard Braun Date: Thu, 15 Sep 2016 01:54:19 +0200 Subject: [PATCH] Some reformatting and manually break all long lines at column 119 --- scour/scour.py | 143 ++++++++++++++++++++++++++++++------------------- testscour.py | 140 +++++++++++++++++++++++++++++++---------------- 2 files changed, 182 insertions(+), 101 deletions(-) diff --git a/scour/scour.py b/scour/scour.py index 78bb66b..6b5da5d 100644 --- a/scour/scour.py +++ b/scour/scour.py @@ -32,8 +32,8 @@ # * Collapse all group based transformations # Even more ideas here: http://esw.w3.org/topic/SvgTidy -# * analysis of path elements to see if rect can be used instead? (must also need to look -# at rounded corners) +# * analysis of path elements to see if rect can be used instead? +# (must also need to look at rounded corners) # Next Up: # - why are marker-start, -end not removed from the style attribute? @@ -104,9 +104,9 @@ unwanted_ns = [NS['SODIPODI'], NS['INKSCAPE'], NS['ADOBE_ILLUSTRATOR'], # A list of all SVG presentation properties # # Sources for this list: -# https://www.w3.org/TR/SVG/propidx.html (implemented) -# https://www.w3.org/TR/SVGTiny12/attributeTable.html (implemented) -# https://www.w3.org/TR/SVG2/propidx.html (not yet implemented) +# https://www.w3.org/TR/SVG/propidx.html (implemented) +# https://www.w3.org/TR/SVGTiny12/attributeTable.html (implemented) +# https://www.w3.org/TR/SVG2/propidx.html (not yet implemented) # svgAttributes = [ # SVG 1.1 @@ -337,9 +337,9 @@ colors = { # A list of default poperties that are safe to remove # # Sources for this list: -# https://www.w3.org/TR/SVG/propidx.html (implemented) -# https://www.w3.org/TR/SVGTiny12/attributeTable.html (implemented) -# https://www.w3.org/TR/SVG2/propidx.html (not yet implemented) +# https://www.w3.org/TR/SVG/propidx.html (implemented) +# https://www.w3.org/TR/SVGTiny12/attributeTable.html (implemented) +# https://www.w3.org/TR/SVG2/propidx.html (not yet implemented) # default_properties = { # excluded all properties with 'auto' as default # SVG 1.1 presentation attributes @@ -1370,7 +1370,8 @@ def removeDuplicateGradients(doc): # compare grad to ograd (all properties, then all stops) # if attributes do not match, go to next gradient someGradAttrsDoNotMatch = False - 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): someGradAttrsDoNotMatch = True break @@ -1484,7 +1485,9 @@ def repairStyle(node, options): for prop in ['fill', 'stroke']: 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)': + 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] + ')' num += 1 @@ -1676,7 +1679,8 @@ def styleInheritedByChild(node, style, nodeIsChild=False): # If the current element is a container element the inherited style is meaningless # (since we made sure it's not inherited by any of its children) - if node.nodeName in ['a', 'defs', 'glyph', 'g', 'marker', 'mask', 'missing-glyph', 'pattern', 'svg', 'switch', 'symbol']: + if node.nodeName in ['a', 'defs', 'glyph', 'g', 'marker', 'mask', + 'missing-glyph', 'pattern', 'svg', 'switch', 'symbol']: return False # in all other cases we have to assume the inherited value of 'style' is meaningfull and has to be kept @@ -1730,18 +1734,19 @@ def mayContainTextNodes(node): # A list of default attributes that are safe to remove if all conditions are fulfilled # # Each default attribute is an object of type 'DefaultAttribute' with the following fields: -# name - name of the attribute to be matched -# value - default value of the attribute -# units - the unit(s) for which 'value' is valid (see 'Unit' class for possible specifications) -# elements - name(s) of SVG element(s) for which the attribute specification is valid -# conditions - additional conditions that have to be fulfilled for removal of the specified default attribute -# implemented as lambda functions with one argument (a xml.dom.minidom node) evaluating to True or False +# name - name of the attribute to be matched +# value - default value of the attribute +# units - the unit(s) for which 'value' is valid (see 'Unit' class for possible specifications) +# elements - name(s) of SVG element(s) for which the attribute specification is valid +# conditions - additional conditions that have to be fulfilled for removal of the specified default attribute +# implemented as lambda functions with one argument (an xml.dom.minidom node) +# evaluating to either True or False # When not specifying a field value, it will be ignored (i.e. always matches) # # Sources for this list: -# https://www.w3.org/TR/SVG/attindex.html (mostly implemented) -# https://www.w3.org/TR/SVGTiny12/attributeTable.html (not yet implemented) -# https://www.w3.org/TR/SVG2/attindex.html (not yet implemented) +# https://www.w3.org/TR/SVG/attindex.html (mostly implemented) +# https://www.w3.org/TR/SVGTiny12/attributeTable.html (not yet implemented) +# https://www.w3.org/TR/SVG2/attindex.html (not yet implemented) # DefaultAttribute = namedtuple('DefaultAttribute', ['name', 'value', 'units', 'elements', 'conditions']) DefaultAttribute.__new__.__defaults__ = (None,) * len(DefaultAttribute._fields) @@ -1756,21 +1761,26 @@ default_attributes = [ DefaultAttribute('patternContentUnits', 'userSpaceOnUse', elements='pattern'), DefaultAttribute('primitiveUnits', 'userSpaceOnUse', elements='filter'), - DefaultAttribute('externalResourcesRequired', 'false', elements=['a', 'altGlyph', 'animate', 'animateColor', - 'animateMotion', 'animateTransform', 'circle', 'clipPath', 'cursor', 'defs', 'ellipse', 'feImage', 'filter', - 'font', 'foreignObject', 'g', 'image', 'line', 'linearGradient', 'marker', 'mask', 'mpath', 'path', 'pattern', - 'polygon', 'polyline', 'radialGradient', 'rect', 'script', 'set', 'svg', 'switch', 'symbol', 'text', 'textPath', - 'tref', 'tspan', 'use', 'view']), + DefaultAttribute('externalResourcesRequired', 'false', + elements=['a', 'altGlyph', 'animate', 'animateColor', + 'animateMotion', 'animateTransform', 'circle', 'clipPath', 'cursor', 'defs', 'ellipse', + 'feImage', 'filter', 'font', 'foreignObject', 'g', 'image', 'line', 'linearGradient', + 'marker', 'mask', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialGradient', + 'rect', 'script', 'set', 'svg', 'switch', 'symbol', 'text', 'textPath', 'tref', 'tspan', + 'use', 'view']), # svg elements DefaultAttribute('width', 100, Unit.PCT, elements='svg'), DefaultAttribute('height', 100, Unit.PCT, elements='svg'), DefaultAttribute('baseProfile', 'none', elements='svg'), - DefaultAttribute('preserveAspectRatio', 'xMidYMid meet', elements=['feImage', 'image', 'marker', 'pattern', 'svg', 'symbol', 'view']), + DefaultAttribute('preserveAspectRatio', 'xMidYMid meet', + elements=['feImage', 'image', 'marker', 'pattern', 'svg', 'symbol', 'view']), # common attributes / basic types - DefaultAttribute('x', 0, elements=['cursor', 'fePointLight', 'feSpotLight', 'foreignObject', 'image', 'pattern', 'rect', 'svg', 'text', 'use']), - DefaultAttribute('y', 0, elements=['cursor', 'fePointLight', 'feSpotLight', 'foreignObject', 'image', 'pattern', 'rect', 'svg', 'text', 'use']), + DefaultAttribute('x', 0, elements=['cursor', 'fePointLight', 'feSpotLight', 'foreignObject', + 'image', 'pattern', 'rect', 'svg', 'text', 'use']), + DefaultAttribute('y', 0, elements=['cursor', 'fePointLight', 'feSpotLight', 'foreignObject', + 'image', 'pattern', 'rect', 'svg', 'text', 'use']), DefaultAttribute('z', 0, elements=['fePointLight', 'feSpotLight']), DefaultAttribute('x1', 0, elements='line'), DefaultAttribute('y1', 0, elements='line'), @@ -1795,29 +1805,39 @@ default_attributes = [ # filters and masks DefaultAttribute('x', -10, Unit.PCT, ['filter', 'mask']), - DefaultAttribute('x', -0.1, Unit.NONE, ['filter', 'mask'], lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'), + DefaultAttribute('x', -0.1, Unit.NONE, ['filter', 'mask'], + conditions=lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'), DefaultAttribute('y', -10, Unit.PCT, ['filter', 'mask']), - DefaultAttribute('y', -0.1, Unit.NONE, ['filter', 'mask'], lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'), + DefaultAttribute('y', -0.1, Unit.NONE, ['filter', 'mask'], + conditions=lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'), DefaultAttribute('width', 120, Unit.PCT, ['filter', 'mask']), - DefaultAttribute('width', 1.2, Unit.NONE, ['filter', 'mask'], lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'), + DefaultAttribute('width', 1.2, Unit.NONE, ['filter', 'mask'], + conditions=lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'), DefaultAttribute('height', 120, Unit.PCT, ['filter', 'mask']), - DefaultAttribute('height', 1.2, Unit.NONE, ['filter', 'mask'], lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'), + DefaultAttribute('height', 1.2, Unit.NONE, ['filter', 'mask'], + conditions=lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'), # gradients DefaultAttribute('x1', 0, elements='linearGradient'), DefaultAttribute('y1', 0, elements='linearGradient'), DefaultAttribute('y2', 0, elements='linearGradient'), DefaultAttribute('x2', 100, Unit.PCT, 'linearGradient'), - DefaultAttribute('x2', 1, Unit.NONE, 'linearGradient', lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'), + DefaultAttribute('x2', 1, Unit.NONE, 'linearGradient', + conditions=lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'), # remove fx/fy before cx/cy to catch the case where fx = cx = 50% or fy = cy = 50% respectively - DefaultAttribute('fx', elements='radialGradient', conditions=lambda node: node.getAttribute('fx') == node.getAttribute('cx')), - DefaultAttribute('fy', elements='radialGradient', conditions=lambda node: node.getAttribute('fy') == node.getAttribute('cy')), + DefaultAttribute('fx', elements='radialGradient', + conditions=lambda node: node.getAttribute('fx') == node.getAttribute('cx')), + DefaultAttribute('fy', elements='radialGradient', + conditions=lambda node: node.getAttribute('fy') == node.getAttribute('cy')), DefaultAttribute('r', 50, Unit.PCT, 'radialGradient'), - DefaultAttribute('r', 0.5, Unit.NONE, 'radialGradient', lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'), + DefaultAttribute('r', 0.5, Unit.NONE, 'radialGradient', + conditions=lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'), DefaultAttribute('cx', 50, Unit.PCT, 'radialGradient'), - DefaultAttribute('cx', 0.5, Unit.NONE, 'radialGradient', lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'), + DefaultAttribute('cx', 0.5, Unit.NONE, 'radialGradient', + conditions=lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'), DefaultAttribute('cy', 50, Unit.PCT, 'radialGradient'), - DefaultAttribute('cy', 0.5, Unit.NONE, 'radialGradient', lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'), + DefaultAttribute('cy', 0.5, Unit.NONE, 'radialGradient', + conditions=lambda node: node.getAttribute('gradientUnits') != 'userSpaceOnUse'), DefaultAttribute('spreadMethod', 'pad'), # filter effects @@ -1888,8 +1908,11 @@ def removeDefaultAttributeValue(node, attribute): return 1 else: nodeValue = SVGLength(node.getAttribute(attribute.name)) - if (attribute.value is None) or ((nodeValue.value == attribute.value) and not (nodeValue.units == Unit.INVALID)): - if (attribute.units is None) or (nodeValue.units == attribute.units) or (isinstance(attribute.units, list) and nodeValue.units in attribute.units): + if ((attribute.value is None) + or ((nodeValue.value == attribute.value) and not (nodeValue.units == Unit.INVALID))): + if ((attribute.units is None) + or (nodeValue.units == attribute.units) + or (isinstance(attribute.units, list) and nodeValue.units in attribute.units)): if (attribute.conditions is None) or attribute.conditions(node): node.removeAttribute(attribute.name) return 1 @@ -2325,9 +2348,11 @@ def cleanPath(element, options): newPath.append((cmd, lineTuples)) # convert Bézier curve segments into s where possible elif cmd == 'c': - # set up the assumed bezier control point as the current point, i.e. (0,0) since we're using relative coords + # set up the assumed bezier control point as the current point, + # i.e. (0,0) since we're using relative coords bez_ctl_pt = (0, 0) - # however if the previous command was 's' the assumed control point is a reflection of the previous control point at the current point + # however if the previous command was 's' + # the assumed control point is a reflection of the previous control point at the current point if len(newPath): (prevCmd, prevData) = newPath[-1] if prevCmd == 's': @@ -2732,9 +2757,9 @@ def optimizeTransform(transform): """ # FIXME: reordering these would optimize even more cases: # first: Fold consecutive runs of the same transformation - # extra: Attempt to cast between types to create sameness: - # "matrix(0 1 -1 0 0 0) rotate(180) scale(-1)" all - # are rotations (90, 180, 180) -- thus "rotate(90)" + # extra: Attempt to cast between types to create sameness: + # "matrix(0 1 -1 0 0 0) rotate(180) scale(-1)" all + # are rotations (90, 180, 180) -- thus "rotate(90)" # second: Simplify transforms where numbers are optional. # third: Attempt to simplify any single remaining matrix() # @@ -3068,15 +3093,15 @@ def remapNamespacePrefix(node, oldprefix, newprefix): def makeWellFormed(str): # Don't escape quotation marks for now as they are fine in text nodes # as well as in attributes if used reciprocally - # xml_ents = { '<':'<', '>':'>', '&':'&', "'":''', '"':'"'} + # xml_ents = { '<':'<', '>':'>', '&':'&', "'":''', '"':'"'} xml_ents = {'<': '<', '>': '>', '&': '&'} # starr = [] # for c in str: -# if c in xml_ents: -# starr.append(xml_ents[c]) -# else: -# starr.append(c) +# if c in xml_ents: +# starr.append(xml_ents[c]) +# else: +# starr.append(c) # 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]) @@ -3222,7 +3247,8 @@ def scourString(in_string, options=None): options = sanitizeOptions(options) # create decimal context with reduced precision for scouring numbers - # calculations should be done in the default context (precision defaults to 28 significant digits) to minimize errors + # calculations should be done in the default context (precision defaults to 28 significant digits) + # to minimize errors global scouringContext scouringContext = Context(prec=options.digits) @@ -3240,7 +3266,8 @@ def scourString(in_string, options=None): # flowRoot elements don't render at all on current browsers (04/2016) cnt_flowText_el = len(doc.getElementsByTagName('flowRoot')) if cnt_flowText_el: - errmsg = "SVG input document uses {} flow text elements, which won't render on browsers!".format(cnt_flowText_el) + errmsg = "SVG input document uses {} flow text elements, " \ + "which won't render on browsers!".format(cnt_flowText_el) if options.error_on_flowtext: raise Exception(errmsg) else: @@ -3404,7 +3431,8 @@ def scourString(in_string, options=None): numBytesSavedInIDs += shortenIDs(doc, options.shorten_ids_prefix, unprotected_ids(doc, options)) # scour lengths (including coordinates) - for type in ['svg', 'image', 'rect', 'circle', 'ellipse', 'line', 'linearGradient', 'radialGradient', 'stop', 'filter']: + for type in ['svg', 'image', 'rect', 'circle', 'ellipse', 'line', + 'linearGradient', 'radialGradient', 'stop', 'filter']: for elem in doc.getElementsByTagName(type): for attr in ['x', 'y', 'width', 'height', 'cx', 'cy', 'r', 'rx', 'ry', 'x1', 'y1', 'x2', 'y2', 'fx', 'fy', 'offset']: @@ -3537,7 +3565,8 @@ _option_group_optimization.add_option("--create-groups", help="create elements for runs of elements with identical attributes") _option_group_optimization.add_option("--keep-editor-data", action="store_true", dest="keep_editor_data", default=False, - help="won't remove Inkscape, Sodipodi, Adobe Illustrator or Sketch elements and attributes") + help="won't remove Inkscape, Sodipodi, Adobe Illustrator " + "or Sketch elements and attributes") _option_group_optimization.add_option("--keep-unreferenced-defs", action="store_true", dest="keep_defs", default=False, help="won't remove elements within the defs container that are unreferenced") @@ -3561,7 +3590,8 @@ _option_group_document.add_option("--remove-descriptions", help="remove elements") _option_group_document.add_option("--remove-metadata", action="store_true", dest="remove_metadata", default=False, - help="remove elements (which may contain license/author information etc.)") + help="remove elements " + "(which may contain license/author information etc.)") _option_group_document.add_option("--remove-descriptive-elements", action="store_true", dest="remove_descriptive_elements", default=False, help="remove , <desc> and <metadata> elements") @@ -3616,7 +3646,8 @@ _options_parser.add_option_group(_option_group_ids) _option_group_compatibility = optparse.OptionGroup(_options_parser, "SVG compatibility checks") _option_group_compatibility.add_option("--error-on-flowtext", action="store_true", dest="error_on_flowtext", default=False, - help="In case the input SVG uses flow text, bail out with error. Otherwise only warn. (default: False)") + help="If the input SVG uses non-standard flowing text exit with error. " + "Otherwise only warn.") _options_parser.add_option_group(_option_group_compatibility) diff --git a/testscour.py b/testscour.py index adc2021..5f9515c 100755 --- a/testscour.py +++ b/testscour.py @@ -63,7 +63,8 @@ class EmptyOptions(unittest.TestCase): fail = False except: fail = True - self.assertEqual(fail, False, 'Exception when calling Scour with empty options object') + self.assertEqual(fail, False, + 'Exception when calling Scour with empty options object') class InvalidOptions(unittest.TestCase): @@ -76,7 +77,8 @@ class InvalidOptions(unittest.TestCase): fail = False except: fail = True - self.assertEqual(fail, False, 'Exception when calling Scour with invalid options') + self.assertEqual(fail, False, + 'Exception when calling Scour with invalid options') class GetElementById(unittest.TestCase): @@ -94,7 +96,8 @@ class NoInkscapeElements(unittest.TestCase): def runTest(self): self.assertNotEqual(walkTree(scour.scourXmlFile('unittests/sodipodi.svg').documentElement, - lambda e: e.namespaceURI != 'http://www.inkscape.org/namespaces/inkscape'), False, + lambda e: e.namespaceURI != 'http://www.inkscape.org/namespaces/inkscape'), + False, 'Found Inkscape elements') @@ -102,7 +105,8 @@ class NoSodipodiElements(unittest.TestCase): def runTest(self): self.assertNotEqual(walkTree(scour.scourXmlFile('unittests/sodipodi.svg').documentElement, - lambda e: e.namespaceURI != 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd'), False, + lambda e: e.namespaceURI != 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd'), + False, 'Found Sodipodi elements') @@ -110,7 +114,8 @@ class NoAdobeIllustratorElements(unittest.TestCase): def runTest(self): self.assertNotEqual(walkTree(scour.scourXmlFile('unittests/adobe.svg').documentElement, - lambda e: e.namespaceURI != 'http://ns.adobe.com/AdobeIllustrator/10.0/'), False, + lambda e: e.namespaceURI != 'http://ns.adobe.com/AdobeIllustrator/10.0/'), + False, 'Found Adobe Illustrator elements') @@ -118,7 +123,8 @@ class NoAdobeGraphsElements(unittest.TestCase): def runTest(self): self.assertNotEqual(walkTree(scour.scourXmlFile('unittests/adobe.svg').documentElement, - lambda e: e.namespaceURI != 'http://ns.adobe.com/Graphs/1.0/'), False, + lambda e: e.namespaceURI != 'http://ns.adobe.com/Graphs/1.0/'), + False, 'Found Adobe Graphs elements') @@ -126,7 +132,8 @@ class NoAdobeSVGViewerElements(unittest.TestCase): def runTest(self): self.assertNotEqual(walkTree(scour.scourXmlFile('unittests/adobe.svg').documentElement, - lambda e: e.namespaceURI != 'http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/'), False, + lambda e: e.namespaceURI != 'http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/'), + False, 'Found Adobe SVG Viewer elements') @@ -134,7 +141,8 @@ class NoAdobeVariablesElements(unittest.TestCase): def runTest(self): self.assertNotEqual(walkTree(scour.scourXmlFile('unittests/adobe.svg').documentElement, - lambda e: e.namespaceURI != 'http://ns.adobe.com/Variables/1.0/'), False, + lambda e: e.namespaceURI != 'http://ns.adobe.com/Variables/1.0/'), + False, 'Found Adobe Variables elements') @@ -142,7 +150,8 @@ class NoAdobeSaveForWebElements(unittest.TestCase): def runTest(self): self.assertNotEqual(walkTree(scour.scourXmlFile('unittests/adobe.svg').documentElement, - lambda e: e.namespaceURI != 'http://ns.adobe.com/SaveForWeb/1.0/'), False, + lambda e: e.namespaceURI != 'http://ns.adobe.com/SaveForWeb/1.0/'), + False, 'Found Adobe Save For Web elements') @@ -150,7 +159,8 @@ class NoAdobeExtensibilityElements(unittest.TestCase): def runTest(self): self.assertNotEqual(walkTree(scour.scourXmlFile('unittests/adobe.svg').documentElement, - lambda e: e.namespaceURI != 'http://ns.adobe.com/Extensibility/1.0/'), False, + lambda e: e.namespaceURI != 'http://ns.adobe.com/Extensibility/1.0/'), + False, 'Found Adobe Extensibility elements') @@ -158,7 +168,8 @@ class NoAdobeFlowsElements(unittest.TestCase): def runTest(self): self.assertNotEqual(walkTree(scour.scourXmlFile('unittests/adobe.svg').documentElement, - lambda e: e.namespaceURI != 'http://ns.adobe.com/Flows/1.0/'), False, + lambda e: e.namespaceURI != 'http://ns.adobe.com/Flows/1.0/'), + False, 'Found Adobe Flows elements') @@ -166,7 +177,8 @@ class NoAdobeImageReplacementElements(unittest.TestCase): def runTest(self): self.assertNotEqual(walkTree(scour.scourXmlFile('unittests/adobe.svg').documentElement, - lambda e: e.namespaceURI != 'http://ns.adobe.com/ImageReplacement/1.0/'), False, + lambda e: e.namespaceURI != 'http://ns.adobe.com/ImageReplacement/1.0/'), + False, 'Found Adobe Image Replacement elements') @@ -174,7 +186,8 @@ class NoAdobeCustomElements(unittest.TestCase): def runTest(self): self.assertNotEqual(walkTree(scour.scourXmlFile('unittests/adobe.svg').documentElement, - lambda e: e.namespaceURI != 'http://ns.adobe.com/GenericCustomNamespace/1.0/'), False, + lambda e: e.namespaceURI != 'http://ns.adobe.com/GenericCustomNamespace/1.0/'), + False, 'Found Adobe Custom elements') @@ -182,7 +195,8 @@ class NoAdobeXPathElements(unittest.TestCase): def runTest(self): self.assertNotEqual(walkTree(scour.scourXmlFile('unittests/adobe.svg').documentElement, - lambda e: e.namespaceURI != 'http://ns.adobe.com/XPath/1.0/'), False, + lambda e: e.namespaceURI != 'http://ns.adobe.com/XPath/1.0/'), + False, 'Found Adobe XPath elements') @@ -464,8 +478,8 @@ class NoSodipodiAttributes(unittest.TestCase): if attrs.item(i).namespaceURI == 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd': return False return True - self.assertNotEqual(walkTree(scour.scourXmlFile('unittests/sodipodi.svg').documentElement, - findSodipodiAttr), False, + self.assertNotEqual(walkTree(scour.scourXmlFile('unittests/sodipodi.svg').documentElement, findSodipodiAttr), + False, 'Found Sodipodi attributes') @@ -480,8 +494,8 @@ class NoInkscapeAttributes(unittest.TestCase): if attrs.item(i).namespaceURI == 'http://www.inkscape.org/namespaces/inkscape': return False return True - self.assertNotEqual(walkTree(scour.scourXmlFile('unittests/inkscape.svg').documentElement, - findInkscapeAttr), False, + self.assertNotEqual(walkTree(scour.scourXmlFile('unittests/inkscape.svg').documentElement, findInkscapeAttr), + False, 'Found Inkscape attributes') @@ -900,9 +914,11 @@ class ChangeBezierToShorthandInPath(unittest.TestCase): self.assertEqual(doc.getElementById('path1').getAttribute('d'), 'm10 100c50-50 50 50 100 0s50 50 100 0', 'Did not change bezier curves into shorthand curve segments in path') self.assertEqual(doc.getElementById('path2a').getAttribute('d'), 'm200 200s200 100 200 0', - 'Did not change bezier curve into shorthand curve segment when first control point is the current point and previous command was not a bezier curve') + 'Did not change bezier curve into shorthand curve segment when first control point ' + 'is the current point and previous command was not a bezier curve') self.assertEqual(doc.getElementById('path2b').getAttribute('d'), 'm0 300s200-100 200 0c0 0 200 100 200 0', - 'Did change bezier curve into shorthand curve segment when first control point is the current point but previous command was a bezier curve with a different control point') + 'Did change bezier curve into shorthand curve segment when first control point ' + 'is the current point but previous command was a bezier curve with a different control point') class ChangeQuadToShorthandInPath(unittest.TestCase): @@ -917,25 +933,42 @@ class DoNotOptimzePathIfLarger(unittest.TestCase): def runTest(self): p = scour.scourXmlFile('unittests/path-no-optimize.svg').getElementsByTagNameNS(SVGNS, 'path')[0] - self.assertTrue(len(p.getAttribute('d')) <= len("M100,100 L200.12345,200.12345 C215,205 185,195 200.12,200.12 Z"), + self.assertTrue(len(p.getAttribute('d')) <= + # this was the scoured path data as of 2016-08-31 without the length check in cleanPath(): + # d="m100 100l100.12 100.12c14.877 4.8766-15.123-5.1234-0.00345-0.00345z" + len("M100,100 L200.12345,200.12345 C215,205 185,195 200.12,200.12 Z"), 'Made path data longer during optimization') - # this was the scoured path data as of 2016-08-31 without the length check in cleanPath(): - # d="m100 100l100.12 100.12c14.877 4.8766-15.123-5.1234-0.00345-0.00345z" class HandleEncodingUTF8(unittest.TestCase): def runTest(self): doc = scour.scourXmlFile('unittests/encoding-utf8.svg') - text = u'Hello in many languages:\nar: أهلا\nbn: হ্যালো\nel: Χαίρετε\nen: Hello\nhi: नमस्ते\niw: שלום\nja: こんにちは\nkm: ជំរាបសួរ\nml: ഹലോ\nru: Здравствуйте\nur: ہیلو\nzh: 您好' + text = u'Hello in many languages:\n' \ + u'ar: أهلا\n' \ + u'bn: হ্যালো\n' \ + u'el: Χαίρετε\n' \ + u'en: Hello\n' \ + u'hi: नमस्ते\n' \ + u'iw: שלום\n' \ + u'ja: こんにちは\n' \ + u'km: ជំរាបសួរ\n' \ + u'ml: ഹലോ\n' \ + u'ru: Здравствуйте\n' \ + u'ur: ہیلو\n' \ + u'zh: 您好' desc = six.text_type(doc.getElementsByTagNameNS(SVGNS, 'desc')[0].firstChild.wholeText).strip() - self.assertEqual(desc, text, 'Did not handle international UTF8 characters') + self.assertEqual(desc, text, + 'Did not handle international UTF8 characters') desc = six.text_type(doc.getElementsByTagNameNS(SVGNS, 'desc')[1].firstChild.wholeText).strip() - self.assertEqual(desc, u'“”‘’–—…‐‒°©®™•½¼¾⅓⅔†‡µ¢£€«»♠♣♥♦¿�', 'Did not handle common UTF8 characters') + self.assertEqual(desc, u'“”‘’–—…‐‒°©®™•½¼¾⅓⅔†‡µ¢£€«»♠♣♥♦¿�', + 'Did not handle common UTF8 characters') desc = six.text_type(doc.getElementsByTagNameNS(SVGNS, 'desc')[2].firstChild.wholeText).strip() - self.assertEqual(desc, u':-×÷±∞π∅≤≥≠≈∧∨∩∪∈∀∃∄∑∏←↑→↓↔↕↖↗↘↙↺↻⇒⇔', 'Did not handle mathematical UTF8 characters') + self.assertEqual(desc, u':-×÷±∞π∅≤≥≠≈∧∨∩∪∈∀∃∄∑∏←↑→↓↔↕↖↗↘↙↺↻⇒⇔', + 'Did not handle mathematical UTF8 characters') desc = six.text_type(doc.getElementsByTagNameNS(SVGNS, 'desc')[3].firstChild.wholeText).strip() - self.assertEqual(desc, u'⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁽⁾ⁿⁱ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎', 'Did not handle superscript/subscript UTF8 characters') + self.assertEqual(desc, u'⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁽⁾ⁿⁱ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎', + 'Did not handle superscript/subscript UTF8 characters') class HandleEncodingISO_8859_15(unittest.TestCase): @@ -997,7 +1030,8 @@ class TranslateLongHexColorIntoShortHex(unittest.TestCase): class DoNotConvertShortColorNames(unittest.TestCase): def runTest(self): - elem = scour.scourXmlFile('unittests/dont-convert-short-color-names.svg').getElementsByTagNameNS(SVGNS, 'rect')[0] + elem = scour.scourXmlFile('unittests/dont-convert-short-color-names.svg') \ + .getElementsByTagNameNS(SVGNS, 'rect')[0] self.assertEqual('red', elem.getAttribute('fill'), 'Converted short color name to longer hex string') @@ -1259,9 +1293,11 @@ class RemoveDefaultGradX2Value(unittest.TestCase): self.assertEqual(doc.getElementById('grad1').getAttribute('x2'), '', 'x2="100%" not removed') self.assertEqual(doc.getElementById('grad1b').getAttribute('x2'), '', - 'x2="1" not removed, which is equal to the default x2="100%" when gradientUnits="objectBoundingBox"') + 'x2="1" not removed, ' + 'which is equal to the default x2="100%" when gradientUnits="objectBoundingBox"') self.assertNotEqual(doc.getElementById('grad1c').getAttribute('x2'), '', - 'x2="1" removed, which is NOT equal to the default x2="100%" when gradientUnits="userSpaceOnUse"') + 'x2="1" removed, ' + 'which is NOT equal to the default x2="100%" when gradientUnits="userSpaceOnUse"') class RemoveDefaultGradY2Value(unittest.TestCase): @@ -1422,7 +1458,8 @@ class MoveSVGElementsToDefaultNamespace(unittest.TestCase): class MoveCommonAttributesToParent(unittest.TestCase): def runTest(self): - g = scour.scourXmlFile('unittests/move-common-attributes-to-parent.svg').getElementsByTagNameNS(SVGNS, 'g')[0] + g = scour.scourXmlFile('unittests/move-common-attributes-to-parent.svg') \ + .getElementsByTagNameNS(SVGNS, 'g')[0] self.assertEqual(g.getAttribute('fill'), '#0F0', 'Did not move common fill attribute to parent group') @@ -1430,7 +1467,8 @@ class MoveCommonAttributesToParent(unittest.TestCase): class RemoveCommonAttributesFromChild(unittest.TestCase): def runTest(self): - r = scour.scourXmlFile('unittests/move-common-attributes-to-parent.svg').getElementsByTagNameNS(SVGNS, 'rect')[0] + r = scour.scourXmlFile('unittests/move-common-attributes-to-parent.svg') \ + .getElementsByTagNameNS(SVGNS, 'rect')[0] self.assertNotEqual(r.getAttribute('fill'), '#0F0', 'Did not remove common fill attribute from child') @@ -1438,7 +1476,8 @@ class RemoveCommonAttributesFromChild(unittest.TestCase): class DontRemoveCommonAttributesIfParentHasTextNodes(unittest.TestCase): def runTest(self): - text = scour.scourXmlFile('unittests/move-common-attributes-to-parent.svg').getElementsByTagNameNS(SVGNS, 'text')[0] + text = scour.scourXmlFile('unittests/move-common-attributes-to-parent.svg') \ + .getElementsByTagNameNS(SVGNS, 'text')[0] self.assertNotEqual(text.getAttribute('font-style'), 'italic', 'Removed common attributes when parent contained text elements') @@ -1446,7 +1485,8 @@ class DontRemoveCommonAttributesIfParentHasTextNodes(unittest.TestCase): class PropagateCommonAttributesUp(unittest.TestCase): def runTest(self): - g = scour.scourXmlFile('unittests/move-common-attributes-to-grandparent.svg').getElementsByTagNameNS(SVGNS, 'g')[0] + g = scour.scourXmlFile('unittests/move-common-attributes-to-grandparent.svg') \ + .getElementsByTagNameNS(SVGNS, 'g')[0] self.assertEqual(g.getAttribute('fill'), '#0F0', 'Did not move common fill attribute to grandparent') @@ -1454,7 +1494,8 @@ class PropagateCommonAttributesUp(unittest.TestCase): class PathEllipticalArcParsingCommaWsp(unittest.TestCase): def runTest(self): - p = scour.scourXmlFile('unittests/path-elliptical-arc-parsing.svg').getElementsByTagNameNS(SVGNS, 'path')[0] + p = scour.scourXmlFile('unittests/path-elliptical-arc-parsing.svg') \ + .getElementsByTagNameNS(SVGNS, 'path')[0] self.assertEqual(p.getAttribute('d'), 'm100 100a100 100 0 1 1 -50 100z', 'Did not parse elliptical arc command properly') @@ -1462,7 +1503,8 @@ class PathEllipticalArcParsingCommaWsp(unittest.TestCase): class RemoveUnusedAttributesOnParent(unittest.TestCase): def runTest(self): - g = scour.scourXmlFile('unittests/remove-unused-attributes-on-parent.svg').getElementsByTagNameNS(SVGNS, 'g')[0] + g = scour.scourXmlFile('unittests/remove-unused-attributes-on-parent.svg') \ + .getElementsByTagNameNS(SVGNS, 'g')[0] self.assertNotEqual(g.getAttribute('stroke'), '#000', 'Unused attributes on group not removed') @@ -1470,7 +1512,8 @@ class RemoveUnusedAttributesOnParent(unittest.TestCase): class DoNotRemoveCommonAttributesOnParentIfAtLeastOneUsed(unittest.TestCase): def runTest(self): - g = scour.scourXmlFile('unittests/remove-unused-attributes-on-parent.svg').getElementsByTagNameNS(SVGNS, 'g')[0] + g = scour.scourXmlFile('unittests/remove-unused-attributes-on-parent.svg') \ + .getElementsByTagNameNS(SVGNS, 'g')[0] self.assertEqual(g.getAttribute('fill'), '#0F0', 'Used attributes on group were removed') @@ -1478,7 +1521,8 @@ class DoNotRemoveCommonAttributesOnParentIfAtLeastOneUsed(unittest.TestCase): class DoNotRemoveGradientsWhenReferencedInStyleCss(unittest.TestCase): def runTest(self): - grads = scour.scourXmlFile('unittests/css-reference.svg').getElementsByTagNameNS(SVGNS, 'linearGradient') + grads = scour.scourXmlFile('unittests/css-reference.svg') \ + .getElementsByTagNameNS(SVGNS, 'linearGradient') self.assertEqual(grads.length, 2, 'Gradients removed when referenced in CSS') @@ -1516,7 +1560,8 @@ class DoNotPrettyPrintWhenNestedWhitespacePreserved(unittest.TestCase): class GetAttrPrefixRight(unittest.TestCase): def runTest(self): - grad = scour.scourXmlFile('unittests/xml-namespace-attrs.svg').getElementsByTagNameNS(SVGNS, 'linearGradient')[1] + grad = scour.scourXmlFile('unittests/xml-namespace-attrs.svg') \ + .getElementsByTagNameNS(SVGNS, 'linearGradient')[1] self.assertEqual(grad.getAttributeNS('http://www.w3.org/1999/xlink', 'href'), '#linearGradient841', 'Did not get xlink:href prefix right') @@ -1709,7 +1754,8 @@ class GroupNoCreationForTspan(unittest.TestCase): doc = scour.scourXmlFile('unittests/group-no-creation-tspan.svg', scour.parse_args(['--create-groups'])) self.assertEqual(doc.getElementsByTagName('g').length, 0, - 'Created a <g> for a run of <tspan>s that are not allowed as children according to content model') + 'Created a <g> for a run of <tspan>s ' + 'that are not allowed as children according to content model') class DoNotCommonizeAttributesOnReferencedElements(unittest.TestCase): @@ -2022,11 +2068,14 @@ class DuplicateGradientsUpdateStyle(unittest.TestCase): gradient = doc.getElementsByTagName('linearGradient')[0] rects = doc.getElementsByTagName('rect') self.assertEqual('fill:url(#' + gradient.getAttribute('id') + ')', rects[0].getAttribute('style'), - 'Either of #duplicate-one or #duplicate-two was removed, but style="fill:" was not updated to reflect this') + 'Either of #duplicate-one or #duplicate-two was removed, ' + 'but style="fill:" was not updated to reflect this') self.assertEqual('fill:url(#' + gradient.getAttribute('id') + ')', rects[1].getAttribute('style'), - 'Either of #duplicate-one or #duplicate-two was removed, but style="fill:" was not updated to reflect this') + 'Either of #duplicate-one or #duplicate-two was removed, ' + 'but style="fill:" was not updated to reflect this') self.assertEqual('fill:url(#' + gradient.getAttribute('id') + ') #fff', rects[2].getAttribute('style'), - 'Either of #duplicate-one or #duplicate-two was removed, but style="fill:" (with fallback) was not updated to reflect this') + 'Either of #duplicate-one or #duplicate-two was removed, ' + 'but style="fill:" (with fallback) was not updated to reflect this') class DocWithFlowtext(unittest.TestCase): @@ -2051,7 +2100,8 @@ class ParseStyleAttribute(unittest.TestCase): def runTest(self): doc = scour.scourXmlFile('unittests/style.svg') - self.assertEqual(doc.documentElement.getAttribute('style'), 'property1:value1;property2:value2;property3:value3', + self.assertEqual(doc.documentElement.getAttribute('style'), + 'property1:value1;property2:value2;property3:value3', 'Style attribute not properly parsed and/or serialized') # TODO: write tests for --enable-viewboxing