mirror of
https://github.com/jimeh/dotfiles.git
synced 2026-02-19 11:46:40 +00:00
178 lines
4.0 KiB
Bash
Executable File
178 lines
4.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
echo "Usage: $(basename "$0") <source.png> [output.icns]" >&2
|
|
}
|
|
|
|
gen-png() {
|
|
local src="$1"
|
|
local px="$2"
|
|
local dpi="$3"
|
|
local out="$4"
|
|
|
|
# Resize longest side to <px>, then pad to a square <px>x<px> with transparency.
|
|
local tmp_resized
|
|
tmp_resized="${out}.tmp.png"
|
|
|
|
# Scale preserving aspect ratio; longest side becomes <px>.
|
|
sips -Z "$px" "$src" --out "$tmp_resized" > /dev/null
|
|
|
|
# Pad to exact square dimensions centered on transparent background.
|
|
sips --padToHeightWidth "$px" "$px" \
|
|
"$tmp_resized" --out "$out" > /dev/null
|
|
|
|
rm -f "$tmp_resized" || true
|
|
|
|
# Set DPI metadata.
|
|
sips -s dpiHeight "$dpi" -s dpiWidth "$dpi" "$out" > /dev/null
|
|
}
|
|
|
|
get-image-max-dim() {
|
|
local src="$1"
|
|
local w h
|
|
|
|
# Capture width/height; avoid exiting on failure due to set -e/pipefail.
|
|
w=$(sips -g pixelWidth "$src" 2> /dev/null |
|
|
awk '/pixelWidth/ {print $2}') || true
|
|
h=$(sips -g pixelHeight "$src" 2> /dev/null |
|
|
awk '/pixelHeight/ {print $2}') || true
|
|
|
|
if [[ -z "$w" || -z "$h" ]]; then
|
|
echo "Failed to read image dimensions with sips: $src" >&2
|
|
return 1
|
|
fi
|
|
|
|
if ((w > h)); then
|
|
echo "$w"
|
|
else
|
|
echo "$h"
|
|
fi
|
|
}
|
|
|
|
verify-png() {
|
|
local src="$1"
|
|
local fmt
|
|
fmt=$(sips -g format "$src" 2> /dev/null |
|
|
awk '/format/ {print tolower($2)}') || true
|
|
if [[ -z "$fmt" ]]; then
|
|
echo "Unable to determine image format with sips: $src" >&2
|
|
return 1
|
|
fi
|
|
if [[ "$fmt" != "png" ]]; then
|
|
echo "Input must be a PNG image: $src" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
make-iconset() {
|
|
local src="$1"
|
|
local dest_dir="$2"
|
|
|
|
mkdir -p "$dest_dir"
|
|
|
|
# Sizes to generate (@1x). @2x files are double the pixels.
|
|
local sizes=(16 32 128 256 512)
|
|
|
|
# Determine maximum source dimension to avoid upscaling.
|
|
local src_max
|
|
if ! src_max=$(get-image-max-dim "$src"); then
|
|
return 1
|
|
fi
|
|
|
|
local generated
|
|
generated=0
|
|
|
|
for sz in "${sizes[@]}"; do
|
|
# @1x
|
|
local out1="${dest_dir}/icon_${sz}x${sz}.png"
|
|
if ((sz <= src_max)); then
|
|
gen-png "$src" "$sz" 72 "$out1"
|
|
generated=$((generated + 1))
|
|
fi
|
|
|
|
# @2x (double pixels, higher DPI)
|
|
local dsz=$((sz * 2))
|
|
local out2="${dest_dir}/icon_${sz}x${sz}@2x.png"
|
|
if ((dsz <= src_max)); then
|
|
gen-png "$src" "$dsz" 144 "$out2"
|
|
generated=$((generated + 1))
|
|
fi
|
|
done
|
|
|
|
if ((generated == 0)); then
|
|
echo "Source image too small; no icon sizes generated: $src" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
cleanup() {
|
|
local path="$1"
|
|
|
|
if [[ -z "$path" || ! -d "$path" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
# Remove generated PNGs inside the temp directory
|
|
find "$path" -type f -name '*.png' -delete 2> /dev/null || true
|
|
|
|
# Remove generated `*.iconset` inside the temp directory
|
|
find "$path" -type d -name '*.iconset' -delete 2> /dev/null || true
|
|
|
|
# Attempt to remove the temp directory if now empty
|
|
rmdir "$path" 2> /dev/null || true
|
|
}
|
|
|
|
main() {
|
|
if [[ $# -lt 1 || $# -gt 2 ]]; then
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
local src="$1"
|
|
local out="${2:-${src%.*}.icns}"
|
|
|
|
if [[ ! -f "$src" ]]; then
|
|
echo "Source file not found: $src" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v sips > /dev/null 2>&1; then
|
|
echo "This script requires 'sips' (macOS) but it wasn't found." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v iconutil > /dev/null 2>&1; then
|
|
echo "This script requires 'iconutil' (macOS) but it wasn't found." >&2
|
|
exit 1
|
|
fi
|
|
|
|
local outdir
|
|
outdir="$(dirname "$out")"
|
|
mkdir -p "$outdir"
|
|
|
|
# Create a temporary working directory
|
|
tmp_root="$(mktemp -d 2> /dev/null || mktemp -d -t png2icns)"
|
|
trap 'cleanup "$tmp_root"' EXIT
|
|
|
|
# Verify input is PNG; we do not accept other formats.
|
|
if ! verify-png "$src"; then
|
|
exit 1
|
|
fi
|
|
|
|
local iconset_dir
|
|
iconset_dir="${tmp_root}/icon.iconset"
|
|
|
|
make-iconset "$src" "$iconset_dir"
|
|
|
|
# Build `.icns` from the generated iconset
|
|
if ! iconutil -c icns -o "$out" "$iconset_dir" 2> /dev/null; then
|
|
echo "Failed to create .icns with iconutil" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "Done. ICNS written to: $out"
|
|
}
|
|
|
|
main "$@"
|