Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 324 lines (264 sloc) 8.64 KB
#!/bin/sh
###############################################################################
# Option variables
opt_verbose=no
opt_recursive=no
opt_backup=no
opt_keepgoing=no
opt_optipng_args="-o7 -strip all -clobber -quiet"
opt_optipng_args_nostrip="-o7 -clobber -quiet"
opt_advpng_args="-z -4 -q"
opt_help=no
opt_paths=""
###############################################################################
# Other globals
# Temporary directory and log file used through the script
tmpdir=`mktemp -d -t tmp.XXXXXXXXXX`
if [ $? -ne 0 ]; then
printf "error: Cannot create a temporary directory"
exit 1
fi
tmplog=`mktemp -t tmp.XXXXXXXXXX`
if [ $? -ne 0 ]; then
printf "error: Cannot create a temporary log file"
exit 1
fi
trap 'rm -rf "$tmpdir $tmplog"' EXIT INT TERM HUP
size_cmd="printf 1" # Command used to obtain the size of a file
total_src_size="0" # Total size of the source files
total_comp_size="0" # Total size of the compressed files
processed_files="0" # Number of files having been processed
###############################################################################
# Print the help message
print_help() {
printf "Usage: `basename $0` [OPTION...] [PATH...]
Recompresses PNG files using the 'optipng' and 'advpng' tools.
Application options:
-v, --verbose Print information on the files being processed
-r, --recursive Recurse into subdirectories and process all files
-b, --backup Keep a backup of the modified files
-k, --keepgoing Do not stop in case of errors
Compression options:
--optipng-args Arguments passed to optipng (default: $opt_optipng_args)
--advpng-args Arguments passed to advpng (default: $opt_advpng_args)
Help options:
-h, --help Show this help message
"
}
suggest_help() {
printf "Try \``basename $0` --help' for more information\n"
}
###############################################################################
# Parse the program arguments
parse_args() {
# Set the options variables depending on the passed arguments
while [ $# -gt 0 ]; do
if [ `expr "$1" : "^-"` -eq 1 ]; then
if [ $1 = "-v" ] || [ $1 = "--verbose" ]; then
shift
opt_verbose=yes
elif [ $1 = "-r" ] || [ $1 = "--recursive" ]; then
shift
opt_recursive=yes
elif [ $1 = "-b" ] || [ $1 = "--backup" ]; then
shift
opt_backup=yes
elif [ $1 = "-k" ] || [ $1 = "--keepgoing" ]; then
shift
opt_keepgoing=yes
elif [ $1 = "--optipng-args" ]; then
if [ $# -le 1 ]; then
printf "error: No argument specified for --optipng-args\n"
suggest_help
exit 1
else
opt_optipng_args="$2"
opt_optipng_args_nostrip=`echo $2 | sed -e 's/-strip all//'`
shift 2
fi
elif [ $1 = "--advpng-args" ]; then
if [ $# -le 1 ]; then
printf "error: No argument specified for --advpng-args\n"
suggest_help
exit 1
else
opt_advpng_args="$2"
shift 2
fi
elif [ $1 = "-h" ] || [ $1 = "--help" ]; then
opt_help=yes
shift 1
else
printf "error: Unknown option $1\n"
exit 1
fi
else
# No more arguments, the following parameters will be paths
break
fi
done
if [ $# -eq 0 ] && [ $opt_help != yes ]; then
printf "error: no files specified\n"
suggest_help
exit 1
fi
# Gather all the paths
while [ $# -gt 0 ]; do
opt_paths="$opt_paths$1\n"
shift
done
# Adjust the arguments
if [ $opt_backup = yes ]; then
opt_optipng_args="$opt_optipng_args -backup"
fi
}
###############################################################################
# Check for programs
check_optipng_version() {
version=$(optipng --version | head -n 1)
version="${version%%: Advanced PNG optimizer.}"
version="${version##OptiPNG version }"
version="${version##OptiPNG }"
if [ ${#version} -le 3 ]; then
version="$version.0"
fi
if [ "$version" \< 0.7.0 ]; then
printf "error: the version of optipng you have installed ($version) is"\
" too old, you need at least version 0.7.0 for this script to work correctly\n"
exit 1
fi
}
check_programs() {
which optipng 1>/dev/null 2>/dev/null
if [ $? -ne 0 ]; then
printf "error: optipng not found\n"
exit 1;
fi
check_optipng_version
which advpng 1>/dev/null 2>/dev/null
if [ $? -ne 0 ]; then
printf "error: advpng not found\n"
exit 1;
fi
if stat --printf %s . 1>/dev/null 2>/dev/null; then
size_cmd="stat --printf %s"
elif stat -f %z . 1>/dev/null 2>/dev/null; then
size_cmd="stat -f %z"
fi
}
###############################################################################
# Recompress all files in a directory, if recursive behavior was requested will
# recurse in all subdirectories
recompress_dir() {
local files
if [ $opt_recursive = yes ]; then
files=`find "$1" -iname "*.png" -type f`
else
files=`find "$1" -maxdepth 1 -iname "*.png" -type f`
fi
mkfifo "$tmpdir/files_pipe"
printf -- "$files\n" > "$tmpdir/files_pipe" &
while read -r fd; do
recompress_file "$fd"
done < "$tmpdir/files_pipe"
}
###############################################################################
# Recompress a single file using the specified options
recompress_file() {
local rv=0
local apng=0
local src_size=`$size_cmd "$1"`
if [ $opt_backup = yes ]; then
if [ -e "$1.bak" ] ; then
printf "error: backup file $1.bak already exists\n"
if [ $opt_keepgoing = no ]; then
exit 1
fi
else
cp -p "$1" "$1.bak"
fi
fi
optipng -o0 -simulate "$1" 1>"$tmplog" 2>"$tmplog"
grep -q APNG "$tmplog"
apng=$?
if [ $apng -eq 0 ]; then
# Do not strip animated PNG files otherwise the frames will be gone
optipng $opt_optipng_args_nostrip "$1"
rv=$?
else
optipng $opt_optipng_args "$1"
rv=$?
fi
if [ $rv -ne 0 ]; then
printf "error: optipng $opt_optipng_args \"$1\" returned $rv\n"
if [ $opt_keepgoing = no ]; then
exit 1
fi
fi
if [ $apng -ne 0 ]; then
# Do not invoke advpng on animated PNG files as it will strip the frames
advpng $opt_advpng_args "$1"
rv=$?
if [ $rv -ne 0 ]; then
printf "error: advpng $opt_optipng_args \"$1\" returned $rv\n"
if [ $opt_keepgoing = no ]; then
exit 1
fi
fi
fi
local comp_size=`$size_cmd "$1"`
local ratio=`compression_ratio $src_size $comp_size`
if [ $opt_verbose = yes ]; then
printf "$1 $src_size $comp_size $ratio\n"
fi
total_src_size=$((total_src_size + src_size))
total_comp_size=$((total_comp_size + comp_size))
processed_files=$((processed_files + 1))
}
###############################################################################
# Prints out the compression ratio, $1 is the source file size, $2 is the
# compressed file size
compression_ratio() {
echo "scale=3;
result = $2.0 / $1.0;
if (result >= 0 && result < 1) {
print \"0\";
}
if (result == 0) {
print \".\";
}
print result;" | bc
}
###############################################################################
# Main script
parse_args "$@"
check_programs
if [ $opt_help = yes ] || [ $# -eq 0 ]; then
print_help
exit 0
fi
# Process all the specified paths
mkfifo "$tmpdir/paths_pipe"
printf -- "$opt_paths" > "$tmpdir/paths_pipe" &
while read -r fd; do
if [ -f "$fd" ]; then
recompress_file "$fd"
elif [ -d "$fd" ]; then
recompress_dir "$fd"
elif ! [ -e "$fd" ]; then
printf "error: $fd no such file or directory\n"
if [ $opt_keepgoing = no ]; then
exit 1
fi
else
printf "warning: skipping $fd, as it is neither a file nor a directory\n"
fi
done < "$tmpdir/paths_pipe"
if [ $opt_verbose = yes ]; then
printf "
Number of files processed: $processed_files
Total size of the files prior to recompression: $total_src_size
Total size of the files after recompression: $total_comp_size
Compression ratio: `compression_ratio $total_src_size $total_comp_size`
"
fi
You can’t perform that action at this time.