Repository: googlefonts/picosvg

Issue:
ZeroDivisionError when applying gradient translation
the infamous baseball emoji (U+26BE) produces a ZeroDivisionError when passed through the latest picosvg (following #128)

```
$ picosvg ../color-fonts/font-srcs/noto-emoji/svg/emoji_u26be.svg
Traceback (most recent call last):
  File "/Users/clupo/Github/nanoemoji/.venv/bin/picosvg", line 33, in <module>
    sys.exit(load_entry_point('picosvg', 'console_scripts', 'picosvg')())
  File "/Users/clupo/Github/picosvg/src/picosvg/picosvg.py", line 39, in main
    svg = SVG.parse(input_file).topicosvg()
  File "/Users/clupo/Github/picosvg/src/picosvg/svg.py", line 822, in topicosvg
    svg.topicosvg(inplace=True)
  File "/Users/clupo/Github/picosvg/src/picosvg/svg.py", line 841, in topicosvg
    self._apply_gradient_translation(inplace=True)
  File "/Users/clupo/Github/picosvg/src/picosvg/svg.py", line 761, in _apply_gradient_translation
    y_prime = (r2 - r1 * b / a) / (d - b * c / a)
ZeroDivisionError: float division by zero
```

Investigating

Comments:

Patch:
diff --git a/src/picosvg/svg.py b/src/picosvg/svg.py
index b82838a..d8bd22f 100644
--- a/src/picosvg/svg.py
+++ b/src/picosvg/svg.py
@@ -733,8 +733,9 @@ class SVG:
                 el, self.view_box()
             )
             affine = gradient.gradientTransform
-            a, b, c, d, dx, dy = affine
-            if (dx, dy) == (0, 0):
+            a, b, c, d, e, f = affine
+            #  no translate? nop!
+            if (e, f) == (0, 0):
                 continue
             affine_prime = affine._replace(e=0, f=0)
 
@@ -756,13 +757,24 @@ class SVG:
                 # 2) - 1)  bx` - bx` + dy` - (b/a)cy` = r2 - (b/a) * r1
                 #         y` = (r2 - (b/a) * r1) / (d - (b/a)c)
                 r1, r2 = affine.map_point((x, y))
-                assert r1 == a * x + c * y + dx
-                assert r2 == b * x + d * y + dy
-                y_prime = (r2 - r1 * b / a) / (d - b * c / a)
-
-                # Sub y` into 1)
-                # 1) x` = (r1 - cy`) / a
-                x_prime = (r1 - c * y_prime) / a
+                assert r1 == a * x + c * y + e
+                assert r2 == b * x + d * y + f
+
+                if a != 0:
+                    y_prime = (r2 - r1 * b / a) / (d - b * c / a)
+
+                    # Sub y` into 1)
+                    # 1) x` = (r1 - cy`) / a
+                    x_prime = (r1 - c * y_prime) / a
+                else:
+                    # if a == 0 then above gives div / 0. Take a simpler path.
+                    # 1) 0x` + cy` + 0 = 0x + cy + e
+                    #    y` = y + e/c
+                    y_prime = y + e / c
+                    # Sub y` into 2)
+                    # 2)  bx` + dy` + 0 = bx + dy + f
+                    #      x` = x + dy/b  + f/b - dy`/b
+                    x_prime = x + (d * y / b) + (f / b) - (d * y_prime / b)
 
                 # sanity check: a`(x`, y`) should be a(x, y)
                 # all our float brutality damages points; low tolerance sanity checks!


Test patch:
diff --git a/tests/svg_test.py b/tests/svg_test.py
index 81294a0..e89ed6a 100644
--- a/tests/svg_test.py
+++ b/tests/svg_test.py
@@ -436,6 +436,12 @@ def test_apply_style_attributes(actual, expected_result):
             '<radialGradient id="mbbox" cx="0.75" cy="0.75" r="0.40" gradientTransform="matrix(1 1 -0.7873 -0.001717 0.5 0)" gradientUnits="objectBoundingBox"/>',
             '<radialGradient id="mbbox" cx="0.748907" cy="0.11353" r="0.40" gradientTransform="matrix(1 1 -0.7873 -0.001717 0 0)" gradientUnits="objectBoundingBox"/>',
         ),
+        # Real example from emoji_u26BE
+        # https://github.com/googlefonts/picosvg/issues/129
+        (
+            '<radialGradient id="f" cx="-779.79" cy="3150" r="58.471" gradientTransform="matrix(0 1 -1 0 3082.5 1129.5)" gradientUnits="userSpaceOnUse"/>',
+            '<radialGradient id="f" cx="349.71" cy="67.5" r="58.471" gradientTransform="matrix(0 1 -1 0 0 0)" gradientUnits="userSpaceOnUse"/>',
+        ),
     ],
 )
 def test_apply_gradient_translation(gradient_string, expected_result):