Error in rule_elliptical_arc crashes scour

Bug #638764 reported by Vladimir Degtyar on 2010-09-15
This bug affects 1 person
Affects Status Importance Assigned to Milestone
scour (Ubuntu)

Bug Description


I've tried to process two svg files (check attachment), each of them raised an error :

  File "d:\666\", line 225, in rule_elliptical_arc
    raise SyntaxError("expecting a nonnegative number; got %r" % (token,
SyntaxError: expecting a nonnegative number; got ('float', u'169.364')

Vladimir Degtyar (degot) wrote :
Vladimir Degtyar (degot) wrote :

To fix, need to replace "< 0.0" with "< 0" , because Decimal('1.5') < 0.0 = True

codedread (codedread) wrote :


Can you tell me which version of scour you are using and what command (options) you used? I used the latest scour script from the bzr repo and ran scour with default options and did not see any crashes.


Please add to the above information your version of Python, which is output by 'python -V'.

Like codedread, I see nothing wrong with default options using revision 193 from Bazaar. I'm running Python 2.6.5.

Walther (walther-md) wrote :

I have the same issue.

./ -i 2.svg -o out.svg
scour 0.25
Copyright Jeff Schiller, Louis Simard, 2010
Traceback (most recent call last):
  File "./", line 2926, in <module>
    out_string = scourString(in_string, options).encode("UTF-8")
  File "./", line 2718, in scourString
    cleanPath(elem, options)
  File "./", line 1556, in cleanPath
    path = svg_parser.parse(oldPathStr)
  File "/home/wallex/scripts/scour/", line 150, in parse
    return self.rule_svg_path(next, token)
  File "/home/wallex/scripts/scour/", line 158, in rule_svg_path
    command_group, token = rule(next, token)
  File "/home/wallex/scripts/scour/", line 225, in rule_elliptical_arc
    raise SyntaxError("expecting a nonnegative number; got %r" % (token,))
SyntaxError: expecting a nonnegative number; got ('float', u'2.53433')

> python -V
Python 2.6.5

Could this be some kind of python configuration issue?
See the attached python test script, in my machine it triggers the same kind of error (Decimal("0.234") < 0.0 true).

Traceback (most recent call last):
  File "./", line 8, in <module>
    raise SyntaxError("%r expecting a nonnegative number; got %r" % (rx,token,))
SyntaxError: Decimal('0.234') expecting a nonnegative number; got '0.234'

Walther (walther-md) wrote :

Hmm, I found this bug report about it:

If I switch to Python 3.1.2, running the test file gives me :

Traceback (most recent call last):
  File "./", line 7, in <module>
    if rx <0.0:
TypeError: unorderable types: Decimal() < float()

I suggest the fix should be

ry = Decimal(token[1]) * 1
if ry < Decimal("0.0"):

This makes the test file work correctly for me again.

Thanks for investigating this bug.

Despite being unable to reproduce it myself (still), I have applied your proposed fix to the trunk in order to fix it.

Changed in scour:
status: New → Fix Committed
Walther (walther-md) wrote :

That is strange. Maybe the configuration has to do with it?

I wonder if the error is really that the *1 returns a Decimal object in my system (and that of the bug reporter), but it returns a Float in your system (hence why the bug doesn't trigger)? If so, what are the implications down the line? Perhaps the proper bug-fix isn't changing the check, but ensuring that rx and ry are Floats rather than Decimal objects.

If you want is to use Floats around the code, then the conversion code perhaps shouldn't be "rx = Decimal(token[i]) * 1" but a "rx = float(token[i])"

Checking around, scour uses the Decimal()*1 line in nine places: rx = Decimal(token[1]) * 1 ry = Decimal(token[1]) * 1 axis_rotation = Decimal(token[1]) * 1 large_arc_flag = Decimal(token[1]) * 1 sweep_flag = Decimal(token[1]) * 1 x = Decimal(token[1]) * 1 y = Decimal(token[1]) * 1 x = Decimal(token[1]) * 1 x = Decimal(token[1]) * 1

But these other lines make me wonder if you really wanted Decimal objects? Maybe they just are a different use-case. Python 2.3- did not have Decimal if not isinstance(length, Decimal): and reduces their precision to the current Decimal context's precision. Decimal(math.degrees(math.asin(transform[0][1][4])))

Anyway, I don't think using "*1" is intuitive in any way regarding it's purpose....

We do want to use Decimal, for the --precision option.

From the Python docs [1]:

> The solution [to round the inputs to an arithmetic operation]
> is either to increase precision or to force rounding of inputs
> using the unary plus operation:

> >>> getcontext().prec = 3
> >>> +Decimal('1.23456789') # unary plus triggers rounding
> Decimal('1.23')

Decimal * 1 does like +Decimal, making sure that all coordinates are rounded to the precision specified on the command line before any operation is carried out on them. Otherwise, coordinates that have no operations carried out on them would be kept in their full precisions in the resulting file, despite the --precision option. This is a way to force at least one operation to be carried out on the numbers.

Also, if we were to use floats, coordinates could become monstrosities such as 3.240000001, which are definitely not an optimisation :)

As for * 1, there's another thing I used in to create Decimal objects: x = getcontext().create_decimal(token[1]) y = getcontext().create_decimal(token[1])

Maybe that would be more intuitive?

Walther (walther-md) wrote :

Hmm, even if more intuitive, it looks a little uglier to read. Though I wonder why "Decimal()*1" was picked over "+Decimal()". The unary plus looks mysterious, but (IMHO) less confusion-prone than *1

Anyway, I am just not an experienced Python developer, so that may in part explain why it looks strange to me.

Another possibility to check for negativity (which should be faster than rx < Decimal("0.0"):) could be:

if rx.is_signed():

The only difference between a signed check and a < 0.0 check is that Decimal("-0.0").is_signed() returns true (but then again -0 is mathematically different from +0, no?)

Decimal.is_signed() is new in Python 2.6. This code is intended to run on Python 2.4 and later.

jazzynico (jazzynico) on 2011-02-28
Changed in inkscape:
assignee: nobody → JazzyNico (jazzynico)
importance: Undecided → Low
milestone: none → 0.49
status: New → Triaged
Launchpad Janitor (janitor) wrote :

This bug was fixed in the package scour - 0.25+bzr207-1

scour (0.25+bzr207-1) unstable; urgency=low

  * New upstream bzr snapshot:
    - Add the option --no-renderer-workaround.
    - Fix gradient corruption. (LP: #702423)
    - Add option to only remove autogenerated id's. (LP: #492277)
    - Fix Windows line ending handling. (LP: #717826)
    - Fix occasional production of empty defs. (LP: #717254)
    - Remove more attributes with default values. (LP: #714731)
    - Fix wrong handling of file:// references. (LP: #708515)
    - Delete text attributes from groups with only non-text elements.
      (LP: #714727)
    - Remove unnecessary text-align properties from non-text elements.
      (LP: #714720)
    - Fix wrong optimization of 0-length Bézier curves. (LP: #714717)
    - Fix decimal number processing in rule_elliptical_arc(). (LP: #638764)
    - Fix transform matrix order. (LP: #722544)
  * Drop 01-bzr195.patch, upstream now.
  * debian/cmpsvg: Fix computation of difference to add up the absolute
    difference of each pixel.
  * debian/cmpsvg: Show percentages with three digits after comma.
  * debian/cmpsvg: Lower default treshold to 0.05%.
 -- Martin Pitt <email address hidden> Tue, 15 Mar 2011 09:28:45 +0100

Changed in scour (Ubuntu):
status: New → Fix Released
jazzynico (jazzynico) wrote :

Fix committed in the trunk, revision 10180.

Changed in inkscape:
status: Triaged → Fix Committed

This bug is fixed in release 0.26 of Scour.

Changed in scour:
status: Fix Committed → Fix Released
jazzynico (jazzynico) on 2015-02-14
Changed in inkscape:
status: Fix Committed → Fix Released
To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.