Lets say you have a set of images that you want to add to your website, but you want to make sure that each one has a drop shadow. But you want it to be consistent, and not have to pull each one into Gimp or Photoshop in order to add that drop shadow. Here's what you do.

The Script
These instructions require a knowledge of Bash and Unix.
A bash shell script has been prepared to help you with the creation of the drop shadow.
Prerequirements:
- Unix: Use of a POSIX compatible Unix is required. Linux, OSX, FreeBSD, etc... are all good. If you are windows user, go install a copy of Cygwin, it's a POSIX compatibility layer on top of windows.
- Bash Shell: If you have Unix, you most likely have Bash already.
- Netpbm: Install a copy of the netpbm tools, using your favorite installer (yum, apt, rpm, deb, tarball, source, etc...)
This pnmdropshadow.sh bash shell script.
#!/bin/bash
TEMPDIR=".pnmdropshadow"
if [ ! -d "$TEMPDIR" ] ; then
mkdir -p $TEMPDIR
fi
function pnmWidth() {
PNM=$1
pamfile $PNM | \
sed -e "s/^.* \([0-9][0-9]*\) by [0-9][0-9]* .*$/\1/g"
}
function pnmHeight() {
PNM=$1
pamfile $PNM | \
sed -e "s/^.* [0-9][0-9]* by \([0-9][0-9]*\) .*$/\1/g"
}
function pnmBorder() {
PNM=$1
COLOR="#555555"
# Add Border to PNM
pnmmargin -color "$COLOR" 1 $PNM > $TEMPDIR/with_border.pnm
cp $TEMPDIR/with_border.pnm $PNM
}
function createConvolMap() {
let blurSize=$1;
if [ `expr $blurSize % 2` -eq 0 ] ; then
let blurSize=$blurSize+1;
fi
let offset=$blurSize*$blurSize;
let rowvalue=$offset+1;
let offset=$offset*2;
echo "P2"
echo "$blurSize $blurSize"
echo "$offset"
for (( y=0; y<$blurSize; y++))
do
for (( x=0; x<$blurSize; x++))
do
echo -n "$rowvalue"
if [ $x -ne $blurSize ]
then
echo -n " "
fi
done
echo ""
done
}
function pnmDropShadow() {
PNM=$1
BGCOLOR="#ffffff"
SHADOWCOLOR="#888888"
let shadowOffset=4;
let shadowBlur=5;
let leftPad=$shadowBlur-$shadowOffset;
if [ $leftPad -lt 0 ] ; then
let leftPad=0;
fi
let rightPad=$shadowBlur+$shadowOffset;
if [ $rightPad -lt 0 ] ; then
let rightPad=0;
fi
let topPad=$shadowBlur-$shadowOffset;
if [ $topPad -lt 0 ] ; then
let topPad=0;
fi
let bottomPad=$shadowBlur+$shadowOffset;
if [ $bottomPad -lt 0 ] ; then
let bottomPad=0;
fi
let pnmWidth=`pnmWidth "$PNM"`
let pnmHeight=`pnmHeight "$PNM"`
let shadowWidth=$pnmWidth+$leftPad+$rightPad;
let shadowHeight=$pnmHeight+$topPad+$bottomPad;
# Create the shadow background
ppmmake "$BGCOLOR" $shadowWidth $shadowHeight > \
$TEMPDIR/shadow_background.pnm
# Create the mask block
ppmmake "$SHADOWCOLOR" $pnmWidth $pnmHeight > \
$TEMPDIR/shadow_mask.pnm
# Insert mask block into the background block
pnmpaste -replace \
$TEMPDIR/shadow_mask.pnm \
$shadowOffset $shadowOffset \
$TEMPDIR/shadow_background.pnm > \
$TEMPDIR/shadow_paste.pnm
# Create Convolution Matrix File
createConvolMap "$shadowBlur" > $TEMPDIR/shadow.cnv
# Blur the shadow
pnmconvol \
$TEMPDIR/shadow.cnv \
$TEMPDIR/shadow_paste.pnm > \
$TEMPDIR/shadow_blur.pnm
# Put original image into blur
pnmpaste -replace $PNM \
$leftPad $topPad $TEMPDIR/shadow_blur.pnm > \
$TEMPDIR/with_shadow.pnm
cp $TEMPDIR/with_shadow.pnm $PNM
}
PNMT="$TEMPDIR/temp.pnm"
if [ -z "$1" ]; then
pnmtopnm > $PNMT
else
cp $1 $PNMT
fi
pnmBorder "$PNMT"
pnmDropShadow "$PNMT"
pnmtopnm "$PNMT"
rm -rf $TEMPDIR
Executing the Script
To execute the pnmdropshadow.sh script you have to do it the netpbm way, which is to first convert the original image into the PNM format (netpbm internal image format) so it can be piped thru the other tools and scripts before eventually being converted back into the output format you desire.
How to take a PNG file and add a dropshadow to it.
$ pngtopnm flower-pink.png | ./pnmdropshadow.sh | \
pnmtopng -compression=9 > flower-pink-with-shadow.png
How to take a directory full of PNG files and add a dropshadow to each.
for PNG in *.png
do
echo Adding drop shadow to $PNG
pngtopnm $PNG | ./pnmdropshadow.sh | \
pnmtopng -compression=9 > ${PNG//.png}-with-shadow.png
done
Examples of output



What's going on
original input image. flower-ping.png (433x189)

Create a simple 1 pixel border around the image.
$ pngtoppm flower-pink.png | pnmmargin -color "#555555" 1 > with_border.ppm
with_border.ppm (433x189)

Create a new, larger image with the background color desired.
$ ppmmake "#FFFFFF" 445 201 > shadow_background.ppm
shadow_background.pnm (445x201)

Create a new image of the desired shadow color (same size as with_border.ppm).
$ ppmmake "#FFFFFF" 435 191 > shadow_mask.ppm
shadow_mask.ppm (435x191)

Paste mask into background with offset applied.
$ ppmpaste -replace shadow_mask.ppm 4 4 shadow_background.pnm > shadow_paste.ppm
shadow_paste.pnm (445x201)

Create a Convolution Map text file.
$ cat shadow.cnv P2 5 5 50 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26
Apply convolution map to blur the shadow_paste.pnm
$ pnmconvol shadow.cnv shadow_paste.pnm > shadow_blur.pnm
shadow_blur.pnm (445x201)

Paste image with border into blured shadow image.
$ pnmpaste -replace with_border.pnm 1 1 shadow_blur.pnm > with_shadow.pnm
with_shadow.pnm (445x201)

Convert PNM image to PNG.
$ pnmtopng -compression=9 with_shadow.pnm > flower-pink-with-shadow.png
flower-ping-with-shadow.png (445x201)

