Skip to content

Instantly share code, notes, and snippets.

@jwalsh
Created August 18, 2010 00:10
Show Gist options
  • Save jwalsh/532750 to your computer and use it in GitHub Desktop.
Save jwalsh/532750 to your computer and use it in GitHub Desktop.
#!/bin/sh
# POC of a CLI sprite generator
# This should mirror the behavior (i.e., default generation) of online scripts
# http://spritegen.website-performance.org/
# Usage: Run from the directory of images that should be sprited
# TODO: Just use files on the command line
# TODO: Convert to Python
# Setup: Populate ~/.spritesrc to change any of default values noted below
# Dependencies:
# - file (file-4.17)
# - montage (ImageMagick 6.4.8 2009-01-09 Q16)
# - pngcrush (1.6.12, uses libpng 1.2.34 and zlib 1.2.3.3)
# - uuencode
# * Sprite Output Options
# Build Direction: horizontal|vertical (default: horizontal)
SPRITE_BUILD_DIRECTION=vertical
# SPRITE_HORIZONTAL_OFFSET=0
# TODO: Creating the vertical offset still needs to be done
SPRITE_VERTICAL_OFFSET=0
# SPRITE_WRAP_COLUMNS_TO_FIX_OPERA_BUG=false
# The default background color: transparent
SPRITE_BACKGROUND_COLOUR=transparent
# SPRITE_NUMBER_OF_COLOURS=16-bit/color RGB
# SPRITE_IMAGE_QUALITY=TBD#
# Use PNG compression: true|false (default: false)
SPRITE_COMPRESS_IMAGE=true
# Ensure that a new image can be requested
SPRITE_CACHE_BUST_TOKEN=1
# * CSS Output Options
# CSS_PREFIX=""
CSS_CLASS_PREFIX=
# User either camel casing or dash delimiters (default: false)
CSS_CLASS_USE_CAMEL=true
# CSS_SUFFIX=""
# Element wrapper around the content requiring two classes instead of one
# This contrasts .sprite with using .img1, .img2
if [ -z "$CSS_USE_WRAPPER" ]
then
CSS_USE_WRAPPER=false
fi
# Place the data inline in the CSS
if [ -z "$CSS_USE_DATA_URI" ]
then
CSS_USE_DATA_URI=false
fi
# Should we even sprite for the individual selectors
if [ -z "$CSS_DATA_URI_SELECTOR" ]
then
CSS_DATA_URI_SELECTOR=false
fi
# TODO: The following isn't implemented since this is still a POC
# PARENT_CONTAINER # if a parent container is used when rendering the examples
# * Save options
# Assume the output should be based on the project name and sanitize the name
PWD=`pwd`
PROJ=`basename $PWD | sed -e s/[._]/-/g`
OUTPUT=output
SPRITE_CSS=css/ct/$PROJ.css
SPRITE_PNG=${PROJ}${SPRITE_CACHE_BUST_TOKEN}.png
SPRITE_GIF=${PROJ}${SPRITE_CACHE_BUST_TOKEN}.gif
SPRITE_ARCHIVE=${PROJ}${SPRITE_CACHE_BUST_TOKEN}.tar.gz
SPRITE_HTML=$PROJ.html
WEIGHT_ALL=0
WEIGHT_SPRITE=0
if [ -f ~/.spriterc ]
then
. ~/.spriterc
fi
mkdir -p $OUTPUT/{images,css/ct}
# TODO: Convert to a setting
if [ ! -f /tmp/spacer.gif ]
then
wget -O /tmp/spacer.gif http://graphics.classmates.com/graphics/spacer.gif
mogrify -resize 4000% /tmp/spacer.gif
fi
echo Creating a sprite for $PROJ in ${OUTPUT}/${SPRITE_HTML}
# Clear the CSS
rm -f ${OUTPUT}/$SPRITE_CSS
# Set up the test html page
echo "<html><head><title>$PROJ</title>" > ${OUTPUT}/$SPRITE_HTML
echo '<link rel="stylesheet" type="text/css" href="http://a.cmcdn.com/home/static/hmmm/bundles/canvasCSS3_gzip-cab29ee3f00684e3a0443ceaaea2ec9a.css"/> ' >> ${OUTPUT}/$SPRITE_HTML
echo "<link rel='stylesheet' type='text/css' href='$SPRITE_CSS'/>" >> ${OUTPUT}/$SPRITE_HTML
echo "<style>.stripe { background: url(http://wal.sh/stripe.png); } </style>" >> ${OUTPUT}/$SPRITE_HTML
echo "</head><body>" >> ${OUTPUT}/$SPRITE_HTML
# Pull the supported images from args if available
if [ $# -gt 0 ]
then
Processing $@
IMAGES=$@
elif [ -f images.csv ]
then
for I in $(cat images.csv)
do
# Get in the order of the original; or just strip everything after the ,
IMAGES="$IMAGES $(echo $I | cut -d , -f 1)"
done
else
# TODO: Handle all types of files without casing restriction
# TODO: Shove the exclusing rules for processed files back to find rather than another shell
# IMAGES=`echo *.gif *.png`
IMAGES=`find . -name "*.gif" -o -name "*.jpg" -o -name "*.png" | grep -v $OUTPUT/ | grep -v perf/`
fi
# TODO: Add 20px spacing for each image
if [ "$SPRITE_BUILD_DIRECTION" = "vertical" ]
then
TILE="1x"
else
TILE="x1"
fi
# TODO: Refactor to generate the space
IMAGES_SPACED=$(echo $IMAGES | sed -e "s# # /tmp/spacer.gif #g")
# Build the horizontal sprite
# TODO: Determine if this should generate SASS
if [ ! -f $OUTPUT/images/$SPRITE_PNG ]
then
# -geometry +0+$SPRITE_VERTICAL_OFFSET
montage -background $SPRITE_BACKGROUND_COLOUR -depth 8 -mode Concatenate -tile $TILE $IMAGES_SPACED $OUTPUT/images/$SPRITE_PNG
fi
# TODO: Convert to process for incorporating IE updates
if [ ! -f $OUTPUT/images/${SPRITE_GIF} ]
then
convert -background transparent $OUTPUT/images/$SPRITE_PNG $OUTPUT/images/${SPRITE_GIF}
fi
# Crush the file
if [ "$SPRITE_COMPRESS_IMAGE" = "true" ]
then
# Crush but preserve the original for comparison
pngcrush -q $OUTPUT/images/$SPRITE_PNG $OUTPUT/images/crushed-$SPRITE_PNG
cp $OUTPUT/images/$SPRITE_PNG $OUTPUT/images/original-$SPRITE_PNG
mv $OUTPUT/images/crushed-$SPRITE_PNG $OUTPUT/images/$SPRITE_PNG
fi
# Encode the image per http://tools.ietf.org/html/rfc2397
# See also MIME::Base64 or http://danielrichman.com/stsoftware/dataurlmaker/
# TODO: convert to function
DATA_IMAGE=`uuencode -m $OUTPUT/images/$SPRITE_PNG processed | grep -v begin-base64 | grep -v "====" | tr -d "\n"`
echo "<div id='styleHeader' class='space10padL space6marB'>
<h2>Beta Icons</h2>
<span class='text1'>last updated: $(date +'%B %d, %Y')</span>
</div>
<ul id='styleTable' class='border1 color21 space4marB'>" >> ${OUTPUT}/$SPRITE_HTML
# The counter of the ls current width or height offset offset
# This is currently broken for horizontal layouts
export COUNTER=$SPRITE_VERTICAL_OFFSET
# SPRITE_VERTICAL_OFFSET=31
# Use a comma separated selectors list to simplify the markup for the background URLs
SELECTORS=""
COUNT=0
for F in $IMAGES
do
COUNT=$(echo 1 + $COUNT | bc)
# echo $COUNTER
identify ${F}
# identify -verbose ${F}
WEIGHT=$(ls -l $F | awk '{print $5}')
# Use the image path to build a reasonable class name
# The following become dashes: directories, underscores, spaces(?)
# TODO: downcase class name
# TODO: Split camel-cased image names before creating the class name
CLASS=`echo $F | sed -e "s#[\/\\._]#-#g" -e 's#^-*##g' -e 's#-gif$##' -e 's#-png$##' -e 's#-jpg$##'| tr [:upper:] [:lower:] `
CLASS_ORIG=$CLASS
# Pass over anything not an image since the selector isn't particularly restrictive
if [ ! -f "$F" ]
then
continue
fi
# Images of different types will require different processing
# TODO: Watch for high bit characters in comments
TYPE=`identify $F | awk '{print $2}'`
DIMENSIONS=`identify $F | awk '{print $3}'`
if [ -z "${DIMENSIONS}" ]
then
echo $F not a supported file type and lacks geometry
continue
fi
# Use a prefix for numbered images
# Use consistent dashes as a separator
# TODO: This needs to be revisited at some point in the future
CLASS=`echo $CLASS | sed -e 's/[. _]/-/g'`
# Switch from dash delimiters to camel
if [ "$CSS_CLASS_USE_CAMEL" = "true" ]
then
CLASS=`echo $CLASS | ssed -e 's/-\(\w\)/\U\1/g'`
fi
# Do this after all processing so there isn't secondary delimiter processing
CLASS=$CSS_CLASS_PREFIX$CLASS
WIDTH=`echo $DIMENSIONS | sed -e 's/[ ]//g' | cut -d x -f 1`;
HEIGHT=`echo $DIMENSIONS | sed -e 's/[ ]//g' | cut -d x -f 2`;
# Some size difference was noted in selection of vertical vs. horizontal but
# cannot find the source
if [ "$SPRITE_BUILD_DIRECTION" = "vertical" ]
then
CSS_BACKGROUND_POSITION="0px -${COUNTER}px"
# TODO: Generalize for horizontal display
# $SPRITE_VERTICAL_OFFSET
COUNTER=`echo $HEIGHT + $COUNTER + 40 | bc`
else
CSS_BACKGROUND_POSITION="-${COUNTER}px 0px"
COUNTER=`echo $WIDTH + $COUNTER | bc`
fi
# This was the original implementation; testing simplification
# If we want inline presentation of data
if [ $CSS_DATA_URI_SELECTOR = "true" ]
then
DATA_IMAGE=`uuencode -m ${F} processed | grep -v begin-base64 | grep -v "====" | tr -d "\n"`
echo ".${CLASS} { background: url(data:image/png;base64,${DATA_IMAGE}); _background: url(../${F}); width: ${WIDTH}px; height: ${HEIGHT}px; background-repeat: no-repeat; }" >> ${OUTPUT}/$SPRITE_CSS
else
# Only provide the positioning when we have a sprite
echo ".${CLASS} { background-position: $CSS_BACKGROUND_POSITION; width: ${WIDTH}px; height: ${HEIGHT}px; }" >> ${OUTPUT}/$SPRITE_CSS
fi
SELECTORS=".${CLASS}, ${SELECTORS}"
# <img alt='$CLASS' align='right' width='${WIDTH}' height='${HEIGHT}' src='../${F}'>
echo "<li class='border3'>
<div class='txtCenter alpha omega grid_3'>
<h3>${CLASS}${ADDCLASS}</h3>
</div>
<div class='stripe grid_6 alpha omega space8 color12'>
<div class='floatLeft space4marR space10marL space2marT ${CLASS}${ADDCLASS}'></div>
<span class='space1padT floatRight width14'></span>
<span class='clear'></span>
</div>
<span class='clear'></span>
</li>" >> ${OUTPUT}/$SPRITE_HTML
# TODO: This needs to be reapplied to the markup
if [ $CSS_USE_WRAPPER = "true" ]
then
ADDCLASS=" $PROJ"
fi
WEIGHT_ALL=$(echo $WEIGHT_ALL + $WEIGHT | bc)
done
# Trim the trailing comma
if [ $CSS_USE_WRAPPER = "true" ]
then
SELECTORS=".$PROJ"
else
SELECTORS=$(echo $SELECTORS | sed -e 's#,$##')
fi
# Clear the CSS file and don't require the use of an external file request
# TODO: Ensure that without the sprite selector nothing is displayed
# A placeholder is needed to ensure that the positions are provided last
# TODO: Need to ensure that we're not providng the image data per selector
if [ $CSS_DATA_URI_SELECTOR != "true" ]
then
mv ${OUTPUT}/$SPRITE_CSS ${OUTPUT}/$SPRITE_CSS.tmp
if [ "$CSS_USE_DATA_URI" = "true" ]
then
echo "${SELECTORS} { background: url(data:image/png;base64,${DATA_IMAGE}); background-repeat: no-repeat; }" > ${OUTPUT}/$SPRITE_CSS
else
# Apply a hack for MSIE6 to allow for rendering in that system
echo "${SELECTORS} { background: url(../../images/$SPRITE_PNG); background-repeat: no-repeat; }" > ${OUTPUT}/$SPRITE_CSS
echo "${SELECTORS} { background: url(../../images/${SPRITE_GIF}); background-repeat: no-repeat; }" > $OUTPUT/css/ct/css2$PROJ.css
fi
cat ${OUTPUT}/$SPRITE_CSS.tmp >> ${OUTPUT}/$SPRITE_CSS
cat ${OUTPUT}/$SPRITE_CSS.tmp >> $OUTPUT/css/ct/css2$PROJ.css
rm ${OUTPUT}/$SPRITE_CSS.tmp
fi
echo "</ul>" >> ${OUTPUT}/$SPRITE_HTML
echo Images: $COUNT
echo Sizes:
echo Aggregated: $WEIGHT_ALL
echo PNG Sprite: $(ls -l $OUTPUT/images/$SPRITE_PNG | awk '{print $5}')
echo GIF Sprite: $(ls -l $OUTPUT/images/${SPRITE_GIF} | awk '{print $5}')
echo CSS: $(ls -l $OUTPUT/css/ct/$PROJ.css | awk '{print $5}')
gzip $OUTPUT/css/ct/$PROJ.css
echo CSS gzip: $(ls -l $OUTPUT/css/ct/$PROJ.css.gz | awk '{print $5}')
gunzip $OUTPUT/css/ct/$PROJ.css.gz
pushd $OUTPUT
tar czvf $SPRITE_ARCHIVE css/ct/$PROJ.css css/ct/css2$PROJ.css images/$SPRITE_PNG images/$SPRITE_GIF
popd
echo CSS_USE_WRAPPER $CSS_USE_WRAPPER
echo CSS_DATA_URI_SELECTOR $CSS_DATA_URI_SELECTOR
echo CSS_USE_DATA_URI $CSS_USE_DATA_URI
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment