Optimising PNG files

I have written about using lossless optimisations techniques to reduce the size of images before, but I recently learned of a few other tools to further reduce the size of PNG images.

Basic optimisation

While you could use Smush.it to manually optimise your images, if you want a single Open Source tool you can use in your scripts, optipng is the most effective one:

optipng -o9 image.png

Removing unnecessary chunks

While not as effective as optipng in its basic optimisation mode, pngcrush can be used remove unnecessary chunks from PNG files:

pngcrush -q -rem gAMA -rem alla -rem text image.png image.crushed.png

Depending on the software used to produce the original PNG file, this can yield significant savings so I usually start with this.

Reducing the colour palette

When optimising images uploaded by users, it's not possible to know whether or not the palette size can be reduced without too much quality degradation. On the other hand, if you are optimising your own images, it might be worth trying this lossy optimisation technique.

For example, this image went from 7.2 kB to 5.2 kB after running it through pngnq:

pngnq -f -n 32 -s 3 image.png

Re-compressing final image

Most PNG writers use zlib to compress the final output but it turns out that there are better algorithms to do this.

Using AdvanceCOMP I was able to bring the same image as above from 5.1kB to 4.6kB:

advpng -z -4 image.png

When the source image is an SVG

Another thing I noticed while optimising PNG files is that rendering a PNG of the right size straight from an SVG file produces a smaller result than exporting a large PNG from that same SVG and then resizing the PNG to smaller sizes.

Here's how you can use Inkscape to generate an 80x80 PNG:

inkscape --without-gui --export-width=80 --export-height=80 --export-png=80.png image.svg
Installing Etherpad on Debian/Ubuntu

Etherpad is an excellent Open Source web application for collaborative text editing. Like Google Docs, it allows you to share documents with others through a secret URL or to set up private documents for which people need a login.

It's a little tricky to install so here's how I did it.

Build a Debian package

Because the official repository is not kept up to date, you must build the package yourself:

  1. Grab the master branch from the official git repository:

    git clone git://github.com/ether/pad.git etherpad

  2. Build the package:

    dpkg-buildpackage -us -uc

Now, install some of its dependencies:

apt-get install --no-install-recommends dbconfig-common python-uno mysql-server

before installing the .deb you built:

dpkg -i etherpad_1.1.deb  
apt-get install --no-install-recommends -f

Application configuration

You will likely need to change a few minor things in the default configuration at /etc/etherpad/etherpad.local.properties:

useHttpsUrls = true  
customBrandingName = ExamplePad  
customEmailAddress = [email protected]  
topdomains = etherpad.example.com,your.external.ip.address,127.0.0.1,localhost,localhost.localdomain

Nginx configuration

If you use Nginx as your web server of choice, create a vhost file in /etc/nginx/sites-available/etherpad:

server {  
  listen 443;  
  server_name etherpad.example.com *.etherpad.example.com;  
  add_header Strict-Transport-Security max-age=15768000;  

  ssl on;  
  ssl_certificate  /etc/ssl/certs/etherpad.example.com.crt;  
  ssl_certificate_key  /etc/ssl/certs/etherpad.example.com.pem;  

  ssl_session_timeout  5m;  
  ssl_session_cache shared:SSL:1m;  

  ssl_protocols  TLSv1;  
  ssl_ciphers  RC4-SHA:HIGH:!kEDH;  
  ssl_prefer_server_ciphers   on;  

  access_log /var/log/nginx/etherpad.access.log;  
  error_log /var/log/nginx/etherpad.error.log;  

  location / {  
    proxy_pass http://localhost:9000/;  
    proxy_set_header Host $host;  
  }  
}

and then enable it and restart Nginx:

/etc/init.d/nginx restart

Apache configuration

If you prefer to use Apache instead, make sure that the required modules are enabled:

a2enmod proxy  
a2enmod proxy_http

and then create a vhost file in /etc/apache2/sites-available/etherpad:

<VirtualHost *:443>  
   ServerName etherpad.example.com  
   ServerAlias *.etherpad.example.com  

   SSLEngine on  
   SSLCertificateFile /etc/apache2/ssl/etherpad.example.com.crt  
   SSLCertificateKeyFile /etc/apache2/ssl/etherpad.example.com.pem  
   SSLCertificateChainFile /etc/apache2/ssl/etherpad.example.com-chain.pem  

   SSLProtocol TLSv1  
   SSLHonorCipherOrder On  
   SSLCipherSuite RC4-SHA:HIGH:!kEDH  
   Header add Strict-Transport-Security: "max-age=15768000"  

   <Proxy>  
       Order deny,allow  
       Allow from all  
   </Proxy>  

   Alias /sitemap.xml /ep/tag/\?format=sitemap  
   Alias /static /usr/share/etherpad/etherpad/src/static  

   ProxyPreserveHost On  
   SetEnv proxy-sendchunked 1  
   ProxyRequests Off  
   ProxyPass / http://localhost:9000/  
   ProxyPassReverse / http://localhost:9000/  
</VirtualHost>

before enabling that new vhost and restarting Apache:

a2ensite etherpad  
apache2ctl configtest  
apache2ctl graceful

DNS setup

The final step is to create these two DNS entries to point to your web server:

  • *.etherpad.example.com
  • etherpad.example.com

Also, as a precaution against an OpenOffice/LibreOffice-related bug, I suggest that you add the following entry to your web server's /etc/hosts file to avoid flooding your DNS resolver with bogus queries:

127.0.0.1 localhost.(none) localhost.(none).fulldomain.example.com

where fulldomain.example.com is the search base defined in /etc/resolv.conf.

Other useful instructions

Here are the most useful pages I used while setting this up: