Warning: count(): Parameter must be an array or an object that implements Countable in /usr/local/www/wordpress/wp-includes/post-template.php on line 284

Batch PNG Optimization

When designing web sites, squeezing your design down to as few kilobytes as possible is a critical operation to keep your site responsive to visitors with low-bandwidth internet connections.

In a well designed web site, markup is almost insignificant in size relative to the image files that make up a visually rich presentation. In order to create image files that are as tiny as possible, I use Ken Silverman‘s PNGOUT utility.

While PNGOUT serves my purposes very well, it has two flaws:

  1. No batch operation
  2. No support for FreeBSD

Fortunately, I can do something to address the first issue.

Below is a perl script I just finished pounding out. Given a base directory and optionally, custom pngout arguments, this script will recursively traverse through all subdirectories optimizing the PNG images via the pngout utility.

#!/usr/bin/perl

use strict;
use warnings;

use Cwd;

my $pngoutpath = "/usr/local/bin/pngout";

if (@ARGV == 2) {
	recurseDir ($ARGV[0], $ARGV[1]);
} elsif (@ARGV == 1) {
	recurseDir ($ARGV[0], 0);
} else {
	print "Usage: recursive-pngout dir [args]\n";
}


sub recurseDir
{
	my ($curpath, $args) = @_;

	if (opendir (CURDIR, $curpath)) {
		chdir ($curpath);

		my @files = readdir(CURDIR);
		closedir (CURDIR);

		foreach my $file (@files)
		{
			next if $file eq ".";
			next if $file eq "..";

			recurseDir ($file, $args);
		}

		chdir ('..');
	} else {
		if ($curpath =~ /.png$/) {
			my $dir = getcwd;

			if ($args) {
				system ("$pngoutpath $args $dir/$curpath");
			} else {
				system ("$pngoutpath $dir/$curpath");
			}
		}

		return;
	}
}

I know this could have been accomplished by a shell one-liner, but my abilities with the shell are a little rusty. I also tend to think in perl when I’m tackling a quick solution to a problem.

2 Responses to “Batch PNG Optimization”

  1. Mike Bobbitt Says:

    Thanks for the script! For interest’s sake, here’s a one-liner that works as well:

    for a in $(find /var/www/html -name ‘*.png’); do /path/to/pngout -r -k0 $a; done

    It’d be interesting to see if we could capture the number of bytes reduced and present a grand total at the end. I suppose a du -ks before and after would do. Let me see what I can come up with…

  2. Mike Bobbitt Says:

    A bit of a cheat because it’s really a script piled onto one line, but here it is:

    dir=”/var/www/html” ; presize=`du -bs $dir | gawk ‘{ print $1 }’` ; for a in $(find $dir -name ‘*.png’); do /home/bobbitt/bin/pngout $a; done; postsize=`du -bs $dir | gawk ‘{ print $1 }’` ; diff=`expr $presize – $postsize` ; echo -e “Size before: $presize\nSize After: $postsize\nDifference: $diff”

Leave a Reply