diff --git a/scour.py b/scour.py index 6ad1c58..3be9f80 100755 --- a/scour.py +++ b/scour.py @@ -1482,11 +1482,11 @@ def mayContainTextNodes(node): result = True # Blacklisted elements. Those are guaranteed not to be text elements. elif node.nodeName in ['rect', 'circle', 'ellipse', 'line', 'polygon', - 'polyline', 'path', 'image', 'stop']: + 'polyline', 'path', 'image', 'stop']: result = False # Group elements. If we're missing any here, the default of True is used. elif node.nodeName in ['g', 'clipPath', 'marker', 'mask', 'pattern', - 'linearGradient', 'radialGradient', 'symbol']: + 'linearGradient', 'radialGradient', 'symbol']: result = False for child in node.childNodes: if mayContainTextNodes(child): @@ -2110,7 +2110,7 @@ def parseListOfPoints(s): nums = [] # also, if 100-100 is found, split it into two also - # + # for i in xrange(len(ws_nums)): negcoords = ws_nums[i].split("-") @@ -2328,6 +2328,28 @@ def reducePrecision(element) : return num +def optimizeAngle(angle): + """ + Because any rotation can be expressed within 360 degrees + of any given number, and since negative angles sometimes + are one character longer than corresponding positive angle, + we shorten the number to one in the range to [-90, 270[. + """ + # First, we put the new angle in the range ]-360, 360[. + # The modulo operator yields results with the sign of the + # divisor, so for negative dividends, we preserve the sign + # of the angle. + if angle < 0: angle %= -360 + else: angle %= 360 + # 720 degrees is unneccessary, as 360 covers all angles. + # As "-x" is shorter than "35x" and "-xxx" one character + # longer than positive angles <= 260, we constrain angle + # range to [-90, 270[ (or, equally valid: ]-100, 260]). + if angle >= 270: angle -= 360 + elif angle < -90: angle += 360 + return angle + + def optimizeTransform(transform): """ Optimises a series of transformations parsed from a single @@ -2401,6 +2423,7 @@ def optimizeTransform(transform): if len(args) == 2 and args[1] == 0: del args[1] elif type == 'rotate': + args[0] = optimizeAngle(args[0]) # angle # Only the angle is required for rotations. # If the coordinates are unspecified, it's the origin (0, 0). if len(args) == 3 and args[1] == args[2] == 0: @@ -2447,14 +2470,7 @@ def optimizeTransform(transform): elif (currType == prevType == 'rotate' and len(prevArgs) == len(currArgs) == 1): # Only coalesce if both rotations are from the origin. - prevArgs[0] += currArgs[0] # angle - # Put the new angle in the range ]-360, 360[. - # The modulo operator yields results with the sign of the - # divisor, so for negative dividends, we preserve the sign - # of the angle. - if prevArgs[0] < 0: prevArgs[0] = prevArgs[0] % -360 - else: prevArgs[0] = prevArgs[0] % 360 - + prevArgs[0] = optimizeAngle(prevArgs[0] + currArgs[0]) del transform[i] elif currType == prevType == 'scale': prevArgs[0] *= currArgs[0] # x diff --git a/testscour.py b/testscour.py index f5084d8..8da5566 100755 --- a/testscour.py +++ b/testscour.py @@ -1243,9 +1243,9 @@ class TransformRotate90(unittest.TestCase): class TransformRotateCCW135(unittest.TestCase): def runTest(self): - doc = scour.scourXmlFile('unittests/transform-matrix-is-rotate-neg-135.svg') - self.assertEquals(doc.getElementsByTagName('line')[0].getAttribute('transform'), 'rotate(-135)', - 'Counter-clockwise rotation matrix not converted to rotate(-135)') + doc = scour.scourXmlFile('unittests/transform-matrix-is-rotate-225.svg') + self.assertEquals(doc.getElementsByTagName('line')[0].getAttribute('transform'), 'rotate(225)', + 'Counter-clockwise rotation matrix not converted to rotate(225)') class TransformRotateCCW45(unittest.TestCase): def runTest(self): @@ -1277,6 +1277,18 @@ class TransformTranslate(unittest.TestCase): self.assertEquals(doc.getElementsByTagName('line')[0].getAttribute('transform'), 'translate(2 3)', 'Translation matrix not converted to translate(2 3)') +class TransformRotationRange719_5(unittest.TestCase): + def runTest(self): + doc = scour.scourXmlFile('unittests/transform-rotate-trim-range-719.5.svg') + self.assertEquals(doc.getElementsByTagName('line')[0].getAttribute('transform'), 'rotate(-.5)', + 'Transform containing rotate(719.5) not shortened to rotate(-.5)') + +class TransformRotationRangeCCW540_0(unittest.TestCase): + def runTest(self): + doc = scour.scourXmlFile('unittests/transform-rotate-trim-range-neg-540.0.svg') + self.assertEquals(doc.getElementsByTagName('line')[0].getAttribute('transform'), 'rotate(180)', + 'Transform containing rotate(-540.0) not shortened to rotate(180)') + class TransformRotation3Args(unittest.TestCase): def runTest(self): doc = scour.scourXmlFile('unittests/transform-rotate-fold-3args.svg') diff --git a/unittests/transform-matrix-is-rotate-neg-135.svg b/unittests/transform-matrix-is-rotate-225.svg similarity index 100% rename from unittests/transform-matrix-is-rotate-neg-135.svg rename to unittests/transform-matrix-is-rotate-225.svg diff --git a/unittests/transform-rotate-trim-range-719.5.svg b/unittests/transform-rotate-trim-range-719.5.svg new file mode 100644 index 0000000..f0bb947 --- /dev/null +++ b/unittests/transform-rotate-trim-range-719.5.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/unittests/transform-rotate-trim-range-neg-540.0.svg b/unittests/transform-rotate-trim-range-neg-540.0.svg new file mode 100644 index 0000000..3e857f6 --- /dev/null +++ b/unittests/transform-rotate-trim-range-neg-540.0.svg @@ -0,0 +1,5 @@ + + + + +