arch-1.12/dev/png_recompress.sh

285 lines
8.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# $Id: png_recompress.sh 5270 2008-01-08 21:23:59Z anmaster $
# Copyright (C) 2007-2008 Arvid Norlander <anmaster AT tele2 DOT se>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This script recompressess .png files using optipng and
# advpng to get the smallest images. All recompression is
# looseless.
#
# This script needs at least bash 3.1, bash 3.x or 2.x will not work.
# (Note that it was only tested on bash 3.2.17 or later though.)
# Also this need the programs mentioned above (optipng and advpng).
#
# TODO:
# * Better docs
# * Check that input really are *.png
# * If advpng doesn't make a file smaller, don't run the second
# optipng pass on that file (if the first optipng pass was enabled).
# Check for new enough bash version
fail_old_bash() {
echo "Sorry your bash version is too old!"
echo "You need at least version 3.1 of bash."
echo "Please install a newer version:"
echo " * Either use your distro's packages."
echo " * Or see http://www.gnu.org/software/bash/"
exit 2
}
# Check bash version. We need at least 3.1
# Lets not use anything like =~ here because
# that may not work on old bash versions.
if [[ "${BASH_VERSINFO[0]}${BASH_VERSINFO[1]}" -lt 31 ]]; then
fail_old_bash
fi
# Version
declare -r png_recompress_version='0.0.1'
# Useful for debugging
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]} : '
print_cmd_help() {
echo 'png_recompress is a script to automate compressing of png images.'
echo ''
echo 'Usage: png_recompress [OPTION] files...'
echo ''
echo 'Options:'
echo ' -h, --help Display this help and exit.'
echo ' -V, --version Output version information and exit.'
echo ' -r, --recursive Run recursively on directories specified on command line. Will not follow symlinks.'
echo ' --skip pass Make png_recompress skip this pass (1, 2 or 3).'
echo ''
echo "Note that png_recompress can't handle short versions of options being written together like"
echo "-vv currently."
echo ''
echo 'Exit status is 0 if OK, 1 if minor problems, 2 if serious trouble.'
echo ''
echo 'Examples:'
echo ' png_recompress *.png Runs png_recompress on all *.png files in the current directory.'
exit $1
}
print_version() {
echo "png_recompress $png_recompress_version - Script to automate compressing of png images."
echo ''
echo 'Copyright (C) 2007-2008 Arvid Norlander'
echo 'This is free software; see the source for copying conditions. There is NO'
echo 'warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.'
echo ''
echo 'Written by Arvid Norlander.'
envbot_quit 0
}
#---------------------------------------------------------------------
## Checks if a space separated list contains a value.
## @param List to check.
## @param Value to check for.
## @return 0 If found.
## @return 1 If not found.
#---------------------------------------------------------------------
list_contains() {
[[ " ${!1} " = *" $2 "* ]]
}
die() {
if [[ $2 ]]; then
echo -e "\e[1mERROR\e[0m: $2"
fi
exit $1
}
# Array of paths (images or directories) to process
PATHS=()
# Option: Skip passes
SKIP=""
# Recursive?
RECURSIVE=''
if [[ $# -gt 0 ]]; then
while [[ $# -gt 0 ]]; do
case "$1" in
'--help'|'-help'|'--usage'|'-usage'|'-h')
print_cmd_help 0
;;
'--version'|'-V')
print_version
;;
'--recursive'|'-r')
RECURSIVE=1
shift 1
;;
'--skip')
if [[ -z "$2" ]]; then
die 1 "Missing parameter to --skip. Maybe try --help"
else
case $2 in
1|2|3)
SKIP+=" $2"
shift 2
;;
*)
die 1 "Parameter to skip must be 1, 2 or 3 (see --help)"
;;
esac
fi
;;
'-'*)
die 1 "Unknown option, try --help"
;;
*)
PATHS+=( "$1" )
shift 1
;;
esac
done
else
print_cmd_help 1
fi
if ! hash optipng > /dev/null 2>&1; then
echo "Can't find optipng!"
echo "This script depends on the optipng tool to be in PATH."
echo "Please install it or, if it is already installed add the"
echo "directory it is in to PATH and try again."
echo "Homepage of this tool is: http://optipng.sourceforge.net/"
die 2
fi
if ! hash advpng > /dev/null 2>&1; then
echo "Can't find advpng!"
echo "This script depends on the advpng tool to be in PATH."
echo "Please install it or, if it is already installed add the"
echo "directory it is in to PATH and try again."
echo "Homepage of this tool is: http://advancemame.sourceforge.net/comp-readme.html"
echo "Hint: For package name in your distro, try looking for \"advancecomp\" if"
echo "there is no \"advpng\"."
die 2
fi
echo -e "Please wait, this can take a \e[1mlong\e[0m time."
# Array of image files
IMAGES=()
# ${PATHS[@]} paths to process
# Sets IMAGES
collect_images_recursive() {
local image directory results
local images=()
for directory in "${PATHS[@]}"; do
if ! [[ -d $directory && -r "$directory" && -w "$directory" ]]; then
echo "$directory skipped: directory not found, not readable and/or writable, or not a directory."
continue
fi
export IFS=$'\n'
results=( $(find "$directory" -iname '*.png' -type f -print ) )
unset IFS
images+=( "${results[@]}" )
done
PATHS=( "${images[@]}" )
}
# This will check that the images in question is a png, otherwise remove it from the list
checkimages() {
local image
local failcount=0
for image in "${PATHS[@]}"; do
# First check it is a file that exists:
if ! [[ -f "$image" && -r "$image" && -w "$image" ]]; then
echo "$image skipped: file not found, not readable and/or writable, or not a normal file"
(( failcount++ ))
continue
elif ! [[ $(file -i "$image" ) == *image/png* ]]; then
echo "$image skipped: not a valid png (according to file)"
(( failcount++ ))
continue
else
IMAGES+=( "$image" )
fi
done
if [[ $failcount -ne 0 ]]; then
echo "Of the images given as parameters $failcount could not be processed (see above for reasons)"
else
echo "All images ok"
fi
return 0
}
# Run optipng
dooptipng() {
optipng -i 0 -o 7 "$@" | \
awk '
/^\*\* Processing:/ { print "File: " $3 }
/^Input file size/ { print "Input: " $5,$6 }
/^Output file size/ { print "Output: " $5,$6,$7,$8,$9,$10,$11,"\n" }
/is already optimized/ { print "Output: No change\n" }
'
}
# Run advpng
doadvpng() {
# I hope this matches up with the columns in all cases...
echo " In Out % Filename"
advpng -z -4 "$@"
}
# Check if pass should run, if yes print out the string as description and
# returns true. If no: returns false.
# $1 = pass number
# $2 = string to print
pass_check() {
if ! list_contains SKIP "$1"; then
echo -e "\n\e[1mPass $1: $2\e[0m\n"
return 0
else
return 1
fi
}
if [[ $RECURSIVE ]]; then
echo "Collecting images from directories"
collect_images_recursive
fi
pass_check 0 "verifying images (can not be skipped, checks that all images exists and are png)" && {
checkimages || die 2 "Pass 0 (check images) failed with return code $?"
}
# Hacks around issue with one more newline on optipng
echo
pass_check 1 "optipng" && {
dooptipng "${IMAGES[@]}" || die 2 "Pass 1 (optipng) failed with return code $?"
}
pass_check 2 "advpng" && {
doadvpng "${IMAGES[@]}" || die 2 "Pass 2 (advpng) failed with return code $?"
}
# Hacks around issue with one more newline on optipng
echo
pass_check 3 "optipng again (as advpng often makes optipng more effective)" && {
dooptipng "${IMAGES[@]}" || die 2 "Pass 3 (optipng) failed with return code $?"
}
# Hacks around issue with one more newline on optipng
echo
exit 0