That Doesn’t Look Right: Fixing Broken Compressed SVGs in Canvg

Pam-Marie Guzzo
IBM Data Science in Practice
4 min readApr 30, 2021

This is the story of a bug hunt. One of those bugs that comes seemingly out of nowhere and ruins your day. One of those bugs that confuses and confounds as you chase through dependencies and projects to find the answers. And it looks like this:

an icon that looks like part of a smiley face but half of the icon is just white with a little of black.
A very broken SVG

What you’re seeing is an SVG that works perfectly fine in HTML breaking inexplicably when rendered on a canvas. The specific case I’m using is from the Carbon Icons Library, however it wouldn’t be surprising to see the issue in other compressed SVGs. For the example, I’ve used svgomg to compress the icon and used the canvg jsfiddle to produce the results.

Fiddle with the corrupted SVG

Whenever I’m looking at a bug, especially in an area of code I’m unfamiliar with, I usually go through these steps:

  1. See what changed. If something was working before, my first step is to look through the commit history or git blame in the relevant files. In this case, none of the code in charge of getting the code onto the canvas had been updated, although some of the dependencies (like canvg) had new version numbers.
  2. Ctrl+Z. It’s rare for only one thing to have changed in a commit — if something looks like it might be causing an issue, I will often undo that specific change to make sure it isn’t really the problem (or to confirm it is). In this case, I tried rolling back the canvg version just in case they had updated something that was causing problems.
  3. Reduce noise. If at all possible, I’ll rip things apart until I can isolate the part that’s actually broken. If I’m lucky there will be unit tests set up that make this very easy and I can use an added broken test case to troubleshoot. This type of thinking is what led me to putting the SVG directly into the canvg jsfiddle.
  4. Check the issues. Since I’d narrowed the issue down to canvg, I looked through their issue backlog to see if anyone had run into anything similar. I found some reports of broken SVGs, but they all related to size — with our tiny icon, this wasn’t a concern.
  5. Break it differently. To isolate problems further, I often try similar but different enough things to see if the same problem happens. Since I’d already figured it was something with canvg, I decided to try the original SVG from carbon in their fiddle, which looked fine. This confirmed that some modification to the SVG was the problem, but I still couldn’t see why.
a person sitting at a computer looking at code
Photo by Kelly Sikkema on Unsplash

After all this experimentation, I isolated the svg html:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<path d="M16 2a14 14 0 1014 14A14 14 0 0016 2zm0 26a12 12 0 1112-12 12 12 0 01-12 12z"/>
<path d="M11.5 11a2.5 2.5 0 102.5 2.5 2.48 2.48 0 00-2.5-2.5zM20.5 11a2.5 2.5 0 102.5 2.5 2.48 2.48 0 00-2.5-2.5zM16 24a8 8 0 006.85-3.89l-1.71-1a6 6 0 01-10.28 0l-1.71 1A8 8 0 0016 24z"/>
</svg>

Since the original file worked in canvg, I wanted to compare it:

<svg id="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<path d="M16,2A14,14,0,1,0,30,16,14,14,0,0,0,16,2Zm0,26A12,12,0,1,1,28,16,12,12,0,0,1,16,28Z"/>
<path d="M11.5,11A2.5,2.5,0,1,0,14,13.5,2.48,2.48,0,0,0,11.5,11Z"/>
<path d="M20.5,11A2.5,2.5,0,1,0,23,13.5,2.48,2.48,0,0,0,20.5,11Z"/>
<path d="M16,24a8,8,0,0,0,6.85-3.89l-1.71-1a6,6,0,0,1-10.28,0l-1.71,1A8,8,0,0,0,16,24Z"/>
</svg>

I’ll be honest, it was a colleague who saw the problem, not me. They had recently been working on a feature that required a lot of work with SVGs, which led them to point out a difference in the path elements I hadn’t noticed.

You can see the problem if you look at the last 7 digits of the compressed version’s second path (0016 24z) and compare it to the last 7 of the non-compressed version (excluding commas — 0 0 16 24z). The compression tool we were using has two settings that apply here: “Merge paths” and “Round/rewrite paths”. When both of these are selected, the commas aren’t reliably replaced with spaces. As mentioned earlier, this works fine in HTML renders, but canvg seems to take things a little more literally.

We found other compression tools we used had similar options set. Disabling them gave us this:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<path d="M16,2A14,14,0,1,0,30,16,14,14,0,0,0,16,2Zm0,26A12,12,0,1,1,28,16,12,12,0,0,1,16,28Z"/>
<path d="M11.5,11A2.5,2.5,0,1,0,14,13.5,2.48,2.48,0,0,0,11.5,11Z"/>
<path d="M20.5,11A2.5,2.5,0,1,0,23,13.5,2.48,2.48,0,0,0,20.5,11Z"/>
<path d="M16,24a8,8,0,0,0,6.85-3.89l-1.71-1a6,6,0,0,1-10.28,0l-1.71,1A8,8,0,0,0,16,24Z"/>
</svg>

Alternatively, you could add some spaces in the compressed version:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<path d="M16,2A14,14,0,1,0,30,16,14,14,0,0,0,16,2Zm0,26A12,12,0,1,1,28,16,12,12,0,0,1,16,28Z"/>
<path d="M11.5 11A2.5 2.5 0 1 0 14 13.5 2.48 2.48 0 0 0 11.5 11zM20.5 11A2.5 2.5 0 1 0 23 13.5 2.48 2.48 0 0 0 20.5 11zM16 24a8 8 0 0 0 6.85-3.89l-1.71-1a6 6 0 01-10.28 0l-1.71 1A8 8 0 0 0 16 24z"/></svg>

Either option works well with canvg and renders perfectly. Hopefully this saves someone some future grief. We were definitely all smiles once this was fixed.

a happy face icon
So relieved!

Pam-Marie Guzzo (she/they) is a Software Developer at IBM who is interested in continuous improvement — both personally and in the code base. Pam-Marie is also a Dungeon Master, mother, and occasional writer of questionable quality.

--

--

Pam-Marie Guzzo
IBM Data Science in Practice

Software Developer | Sometime Writer | Occasional Anthropologist | She/They