Created
August 18, 2010 00:10
-
-
Save jwalsh/532750 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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