SVG optimiser


28 Jan 2012 Code on Github

I created an online program to optimise SVG files. It's currently hosted here (with a slightly different, more interactive version here). There are now other programs online that do very similar things and are probably better. Below is a post I wrote about a Python version I started even earlier, though the principles are the same for all versions.

Introduction

I've started work on a program I've been meaning to make for a while: an SVG optimiser. I've often found myself spending a lot of time tidying, simplifying and compressing SVGs created by Inkscape or Illustrator. Sometimes, changes are merely aesthetic, e.g. reducing numbers from an unnecessary six decimal places to one, or removing unused attributes. These changes make it easier to read the file, and can reduce its size noticeably. Other changes are more practical, such as removing transforms which otherwise make it difficult to see where paths and shapes are actually placed. This is particularly important if you want to add animations or interactive elements.

The latest version of the program can uses Python 2.7. At the time of writing, the functionality is limited and the program quite buggy. Below a brief description of what it can and can't (yet) do.

Simplify decimals

One of the annoying aspect of Inkscape and Illustrator is that they often specific coordinates to six decimal places which is completely unnecessary unless you're planning to view your SVG at a million times magnification. My program can now fix numbers to n decimal places with svg.setDemicalPlaces(n). The part I'm most pleased about is that it also removes trailing decimal zeros. So if you want two decimal places, it won't convert "12" to "12.00" and it will convert "12.02" to "12". Unfortunately, at present, it doesn't work for the <path> 'd' attribute, which is probably the most frequently used, but also the hardest to parse. [Update 29/01/12 - it does now, although it strips out all commas too, which works fine, but isn't ideal.]

Remove attributes

Inkscape also gives all shapes an id, which makes sense, but I prefer to remove it as it makes the file slightly harder to read and because I like minimal files. Now I can call svg.removeAttribute('id') and away they go. Inkscape also generates lots of attributes beginning with sodipodi, which can be removed if you don't want to open the file in Inkscape again (even if you do, I don't think it makes much difference). To remove these efficiently, I intend to create a function that removes an entire namespace. [Update 29/01/12 - I now have this function, which can strip out all the sodipodi tags that Inkscape adds.]

Apply transformations

Whenever you move a shape in Inkscape, rather than change its actual coordinates, it adds a transform or changes an existing transform. This makes a lot of sense as it's a lot simpler, but it can make working with an SVG manually a pain. Eventually I hope to be able to remove all the transform elements, but for now I can only remove translations from all shapes other than paths.

Move styles to CSS

Another inefficiency of Inkscape- and Illustrator-derived SVGs is that they tend to assign a lengthy style attribute to each element. Often the same style is applied to all or many elements. I'm hoping to be able to remove these and replace them with a class attribute which can be styled using CSS. So far, I've not attempted this, but it should be relatively easy. [Update 29/01/12 - I've made a start on this and it works relatively well, but will cause problems on some SVGs.]

Example

Here is an example of an SVG before and after optimisation. Hopefully you can't see any difference in the images. In the first, nearly all the shapes have various a translation attribute, and so of the values have many decimal places.

In the second file, the translation have been applied to the coordinates, the numbers have been rounded to one decimal place and all the id attributes have been removed. As a result, the second file is 68% the size of the first - not a massive difference, but the original file was hand scripted so is relatively concise already.

Issues

[Update 29/01/12 - both these issues are fixed since I switched to using lxml.] The biggest issue at the moment is the weird way that the XML parser it uses (which is part of the built-in ElementTree module) treats namespaces. As a result, it strips out the xmlns:xlink, which doesn't matter in the above example, but causes problems with Inkscape files. I'm going to have to spend some time working through the parser to see if I can work out where the problem is. It also strips out comments, which is potentially annoying.

Comments (19)

Geoff on 18 Feb 2012, 2:57 p.m.

Hi Peter.

This is a great idea. I am trying to reduce the file size for a detailed world map.
Currently it is 27mb. My goal is to get it below 2-3 mb.

Can you maybe suggest a link, where I can find out how to run Python?
Basically I have no idea :o)

By the way, your website is super, and is really helping me to better understand SVG

BR
Geoff

Peter on 18 Feb 2012, 11:08 p.m.

Hi Geoff,

Thanks for your kind comment. If you're not familiar with Python, I suspect my program will be very difficult to use. However, you've spurred me into attempting to make an online version. It is available now at:

http://petercollingridge.appspot.com

It's very basic and may well break at a moment's notice. If you have any problems then send me an email via the Contact Me button on the left side of this page and I'll see if I can help. It may be that you have to run the program on your computer.

A ten-fold reduction in size seems like a challenge, but it should be possible if the original file has a lot of redundancy, which is often the case with files produced by Illustrator or Inkscape. How was it created? I'm very interested in helping as it will give me a good idea what sort of things are needed for a real-world example.

Geoff on 19 Feb 2012, 9:53 p.m.

Hi Peter.
That was a really quick response - thanks.
I did a quick trial on a smaller file and it worked , but the country borders get a bit distorted.

In the original file the lat/long are roughly 10 digits before & after the decimal.

It would be fantastic, if you could make it optional for a user to:

1. First, scale the coordinates (eg. divided by 1000000)
2. Then, specify the number of digits after the decimal (eg.
with 4 decimal places)

So, coordinate 123456789.123456789 would become=123.4568

The original map is much too "accurate" for me (and my screen resolution).
But with 4 or 5 decimal places even a highly zoomed map will still be accurate.

The (public domain) map data is from the NaturalEarth dataset as an SHP file.
It was converted to SVG using Indiemapper (both sites are excellent).

Many thanks in advance.

peter on 20 Feb 2012, 1:20 a.m.

Hi Geoff,

As it happens, I'm currently working on an option to allow users to select the number of decimal places. For the moment, I've changed it from 1 to 2, which should be sufficient for your image. Twenty signficant figures is just ridiculous - you'd probably have to blow the image up to the size of the real world before you noticed a difference.

I think I can see the file you uploaded as my program to delete files doesn't seem to be working. My latest version improves the reduction in size further, but does something weird with the clipping path. The upshot is that I noticed, the background of the image is a red grid. Is that necessary? It looks like a huge number of path coordinate to draw something that's hidden. If you can delete all the paths with a stroke of ff0000 (or style0 in my optimised version), then you can pretty much half the file size.

As for dividing by a large number to start with,I've added a function to apply the scale transform which works for your image at least.

Geoff on 20 Feb 2012, 10:53 a.m.

Hi Peter.
Tried to load the big file, but the server gave an error message after 3-4 mins.
I will have another attempt with the smaller file tomorrow..

BR

peter on 21 Feb 2012, 12:55 a.m.

I'm slowly improving the site. I noticed someone had uploaded some invalid SVGs, so there's now an additional step to separate uploading from optimising. This should make detecting errors better at the very least. I also improve the error-handling at bit so it deals with unexpected parameters bit better.

There's still a long way to go before it's really usable though...

Jelle on 16 Apr 2012, 5:45 a.m.

Hello Peter,

Pretty cool project. Inkscape now has an export function as well to do many of these things, but it will be great if yours works with any odd SVG files slung at it.

I'll give it a try later and see if I can get anything processed.

Cheers,

Jelle

Anonymous on 1 Jun 2012, 3:30 p.m.

Hi Peter

Great information of SVG use, thanks for sharing with us newbs

Slavko on 3 Nov 2012, 12:53 p.m.

Hi,

nice piece of the software, but there are license informations missing. Please, can you to add it?

Peter on 3 Nov 2012, 9:40 p.m.

Good point, Slavko - I've added a Creative Commons Attribution-ShareAlike 3.0 license.

Johan Sundström on 9 Dec 2012, 4:23 p.m.

While I applaud all SVG optimization utilities, and your chosen semantics for setting a number of decimals to keep seems at first blush a bit smarter than Scour's – have you considered improving http://www.codedread.com/scour/ (public repo: https://launchpad.net/scour) instead of starting from scratch, reimplementing a few of its features? Considering it also is a Python module, and fairly easy to contribute to (I have submitted some myself, despite not really being a python guy, and found it pretty painless; Jeff cares about this as much as we do :-).

Anonymous on 19 Dec 2012, 12:41 a.m.

One idea that you might find easy to implement, is to merge transforms :

Sometimes an element has multiple transforms, that could be composited,

e.g.:

<g transform="matrix(0.9935814,0,0,0.9942195,106.8535,157.8948)"><text transform="matrix(1,0,0,-1,12.45322,-27)">foo</text></g>

could be shortened to

<g transform="matrix(0.9935814,0,0,-0.9942195,119.227,131.05)"><text>foo</text></g>

by doing the matrix maths.

Craig Patik on 20 Nov 2013, 9:53 p.m.

The tool works great. Any chance of turning it into a Grunt module?

Timothy Austen on 23 Apr 2014, 12:46 p.m.

This looks really good. :) I have been looking for something like this for a while. Do you have a way to remove an unnecessary translation, while keeping the element in place? I mean coordinates and size, but not necessarily rotation and scew.

Avinash R on 29 Mar 2015, 5:18 p.m.

I found the online app quite helpful and as I found myself coming back to the same, I have decided to create a chrome app out of it as an excercise (and perhaps more). I have kept the name same (I hope you don't mind).

The source can be found at: https://github.com/Avinash-Bhat/SVG-Optimizer.

Hope to see some of your contributions.

PS: The app is originally inspired by http://petercollingridge.appspot.com/svg-editor rather than the one you mentioned in this post.

VVS on 17 Nov 2015, 12:25 p.m.

Hello.

Optimizer is not working :(

I upload file, click Optimize file and error:

Unable to create file Status: 200 OK Content-Type: text/html; charset=utf-8 Content-Length: 0 Cache-Control: no-cache

Iain on 1 Oct 2016, 7:34 p.m.

Hello,

Great work on this... at the moment there is an issue with rounding paths since commas are removed "killing" the code...

probs easy to fix?

2017commingthrough on 24 Mar 2017, 2:57 p.m.

I just tried your link. The idea was to remove a lot of unused styles from svg (reduces size by 50%) but your app doesn`t handle unused classes at all

any way on 4 Apr 2017, 11:13 a.m.

Great work. Sadly it does not handle elements that already have a class attribute, and adds another one, which leads to an error. Would be a nice feature :)