SVG optimiser

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.

[A very rough test app is available at http://petercollingridge.appspot.com/svg-optimiser. I offer no guarentees as to whether it will work with any particular file.]

Functions

The latest version of the program can be found on here (requires 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 exisiting transform. This makes a lot of sense as it's a lot simplier, but it can 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

Below 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, 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 actually 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.

AttachmentSize
translations.svg1.25 KB
translations_optimised.svg868 bytes

Comments

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

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.

Hi Peter.
That was a really 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.

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.

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

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 useable though...

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

Hi Peter

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

Hi,

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

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

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 :-).

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.

 

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

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.

SVG optimiser is a great post.

Post new comment

The content of this field is kept private and will not be shown publicly.