mathandy / svgpathtools

A collection of tools for manipulating and analyzing SVG Path objects and Bezier curves.
MIT License
558 stars 142 forks source link

Exceptions parsing paths #129

Open mondeja opened 4 years ago

mondeja commented 4 years ago

This SVG path:

M15.65 16.105v-.24a3.543 3.543 0 00-1.267-2.471c-.724-.663-1.69-.965-2.655-.904-1.87.12-3.378 1.747-3.378 3.615 0 .784.12 1.567.422 2.471H4.55V6.946l7.42-5.123 7.482 5.122v11.692h-4.223c.18-.663.422-1.688.422-2.532m5.068-10.244L12.452.136c-.301-.181-.663-.181-.905 0L3.222 5.86c-.242.12-.362.361-.362.663V19.48c0 .482.362.844.844.844H9.92a.824.824 0 00.844-.844c0-.06 0-.18-.06-.24l-.06-.182c-.302-.723-.664-1.627-.664-2.53v-.182c-.06-.542.12-1.084.482-1.446a2.095 2.095 0 011.388-.723c.543-.06 1.026.12 1.448.482.422.362.664.844.724 1.386v.18c.06 1.206-.724 2.954-.845 3.135l-1.146 2.17-.18-.362c-.122-.181-.302-.362-.483-.422-.182-.06-.423-.06-.604.06-.18.12-.362.301-.422.482s-.06.422.06.603l.905 1.687c.121.241.423.422.724.422.302 0 .604-.18.724-.422l1.81-3.375h5.732a.824.824 0 00.844-.843V6.524c-.06-.302-.18-.543-.422-.663

...which you can see rendered here raises next exception trying to parse it:

  File "/usr/local/lib/python3.8/dist-packages/svgpathtools/parser.py", line 15, in parse_path
    return Path(pathdef, current_pos=current_pos, tree_element=tree_element)
  File "/usr/local/lib/python3.8/dist-packages/svgpathtools/path.py", line 2274, in __init__
    self._parse_path(segments[0], current_pos)
  File "/usr/local/lib/python3.8/dist-packages/svgpathtools/path.py", line 3076, in _parse_path
    end = float(elements.pop()) + float(elements.pop()) * 1j
ValueError: could not convert string to float: 'c'

Minimal reproducible example

path = "M15.65 16.105v-.24a3.543 3.543 0 00-1.267-2.471c-.724-.663-1.69-.965-2.655-.904-1.87.12-3.378 1.747-3.378 3.615 0 .784.12 1.567.422 2.471H4.55V6.946l7.42-5.123 7.482 5.122v11.692h-4.223c.18-.663.422-1.688.422-2.532m5.068-10.244L12.452.136c-.301-.181-.663-.181-.905 0L3.222 5.86c-.242.12-.362.361-.362.663V19.48c0 .482.362.844.844.844H9.92a.824.824 0 00.844-.844c0-.06 0-.18-.06-.24l-.06-.182c-.302-.723-.664-1.627-.664-2.53v-.182c-.06-.542.12-1.084.482-1.446a2.095 2.095 0 011.388-.723c.543-.06 1.026.12 1.448.482.422.362.664.844.724 1.386v.18c.06 1.206-.724 2.954-.845 3.135l-1.146 2.17-.18-.362c-.122-.181-.302-.362-.483-.422-.182-.06-.423-.06-.604.06-.18.12-.362.301-.422.482s-.06.422.06.603l.905 1.687c.121.241.423.422.724.422.302 0 .604-.18.724-.422l1.81-3.375h5.732a.824.824 0 00.844-.843V6.524c-.06-.302-.18-.543-.422-.663"
print(parse_path(path))

This SVG path:

M20.7 15.404l-1.894-4.967h1.411l1.39 3.582 1.379-3.582h.96l-1.92 4.967zM16.298 9.6V8.48h1.34V9.6zm0 5.808v-4.971h1.34v4.967zm-6.965-.003l2.146-3.3L9.43 8.707h1.627l1.364 2.254L13.9 8.707h1.12l-2.046 3.156 2.126 3.537h-1.622l-1.45-2.4-1.557 2.4H9.333zm-3.346 0v-4.968h1.338v.937c.344-.7.875-1.051 1.585-1.051a1.401 1.401 0 01.248.026v1.194a1.6 1.6 0 00-.53-.102c-.537 0-.968.267-1.303.8v3.164zm-3.028-.536q-.664.65-1.437.65a1.473 1.473 0 01-1.06-.398 1.376 1.376 0 01-.406-1.03 1.45 1.45 0 01.659-1.271q.657-.447 1.884-.448h.355v-.453q0-.772-.88-.772a3.305 3.305 0 00-1.587.443v-.922a5.016 5.016 0 011.808-.345q1.953 0 1.951 1.55v2.206c0 .39.123.58.376.58a.8.8 0 00.174-.02l.032.751a2.745 2.745 0 01-.751.13c-.552 0-.902-.216-1.06-.65h-.054zm0-.72v-1.01h-.32c-.866 0-1.297.274-1.297.815a.64.64 0 00.64.648c.329.004.647-.15.977-.453z

...which you can see rendered here raises next exception trying to parse it:

  File "/usr/local/lib/python3.8/dist-packages/svgpathtools/parser.py", line 15, in parse_path
    return Path(pathdef, current_pos=current_pos, tree_element=tree_element)
  File "/usr/local/lib/python3.8/dist-packages/svgpathtools/path.py", line 2274, in __init__
    self._parse_path(segments[0], current_pos)
  File "/usr/local/lib/python3.8/dist-packages/svgpathtools/path.py", line 3076, in _parse_path
    end = float(elements.pop()) + float(elements.pop()) * 1j
ValueError: could not convert string to float: 'v'

Minimal reproducible example

path = "M20.7 15.404l-1.894-4.967h1.411l1.39 3.582 1.379-3.582h.96l-1.92 4.967zM16.298 9.6V8.48h1.34V9.6zm0 5.808v-4.971h1.34v4.967zm-6.965-.003l2.146-3.3L9.43 8.707h1.627l1.364 2.254L13.9 8.707h1.12l-2.046 3.156 2.126 3.537h-1.622l-1.45-2.4-1.557 2.4H9.333zm-3.346 0v-4.968h1.338v.937c.344-.7.875-1.051 1.585-1.051a1.401 1.401 0 01.248.026v1.194a1.6 1.6 0 00-.53-.102c-.537 0-.968.267-1.303.8v3.164zm-3.028-.536q-.664.65-1.437.65a1.473 1.473 0 01-1.06-.398 1.376 1.376 0 01-.406-1.03 1.45 1.45 0 01.659-1.271q.657-.447 1.884-.448h.355v-.453q0-.772-.88-.772a3.305 3.305 0 00-1.587.443v-.922a5.016 5.016 0 011.808-.345q1.953 0 1.951 1.55v2.206c0 .39.123.58.376.58a.8.8 0 00.174-.02l.032.751a2.745 2.745 0 01-.751.13c-.552 0-.902-.216-1.06-.65h-.054zm0-.72v-1.01h-.32c-.866 0-1.297.274-1.297.815a.64.64 0 00.64.648c.329.004.647-.15.977-.453z"
print(parse_path(path))
tatarize commented 4 years ago

129 and #128 are the same bug the minimum method of duplicating this bug is:

Path("M0,0a1 1 0 00-1-1z")

The issue here is that the a command actually has XBNF that says the large arc flag and the sweep flag are literally flags. Their value can be 0 or 1and those values only. But, since they can't have additional digits it's legal to combined them. Here the program issues those flags as "00" in a single object. However, due to the parsing of the SVG catching this requires a rewrite of the parser. I did that in svgelements for a different reasons. Mostly it was really hard for me to parse the z suffixing code in svg 2.0 spec.

See regebro/svg.path#53 for some more specifics on this. This bug also causes the library to fail a couple W3C tests.

The solution would be to switch out the parser completely for my rewrite. Not sure @mathandy 's position on that but it's easy to prove it correctly works. Though I usually limit myself to one pull at a time and I still have #130 open. The immediate solution would be if you don't need any of the more advanced math stuff and you want more robust svg parsing with strong adherence to the spec you could switch to svgelements.

https://github.com/meerk40t/svgelements

I use my project in a somewhat popular lasercutting project so it gets hit with a bunch of different SVG files all the time and should work fairly robustly. I come back here for ideas and to steal math.

emlakjet

mondeja commented 4 years ago

I've mixed the other issue with this.