diff --git a/README.md b/README.md index c585575..7ed6c2c 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,4 @@ * [Rusty Checker](./rusty-checker.sh): quickly check if a public FOSS project is written in Rust (or another language that you pass to the script) * [Image Transfer](./it.sh): a more convenient wrapper around dd for writing and reading removable media images * [Mount Manager](./mm.sh): a simple script to mount/unmount removable media partitions +* [PNGizer](./pngizer.sh): an utility (and a shell library) to convert any file or directory to/from a PNG image diff --git a/pngizer.sh b/pngizer.sh new file mode 100755 index 0000000..007255b --- /dev/null +++ b/pngizer.sh @@ -0,0 +1,72 @@ +#!/bin/sh +# PNGizer: convert any file into PNG image and back +# Depends on mktemp, tar, ImageMagick and pngcrush +# Created by Luxferre in 2026, released into public domain + +# Convert any raw data file to PNG +# Usage: pngize_raw $infile $pngfile +pngize_raw() { + temp_ppm="$(mktemp)" + # get raw input size + fsize=$(wc -c < "$1") + # calculate image side length to fit everything + sidelen=$(echo "scale=0;1 + sqrt($fsize / 3 + 1)" | bc -l) + # how many trailing zero bytes we need + remlen=$((sidelen * sidelen * 3 - fsize)) + # shape the PPM file: + # 1. header (PPM binary) + printf 'P6\n%s %s\n255\n' $sidelen $sidelen > $temp_ppm + # 2. body (raw file data) + cat "$1" >> $temp_ppm + # 3. trailing zero bytes + for i in $(seq 1 $remlen); do printf '\x00' >> $temp_ppm; done + # convert PPM to PNG and remove the temporary PPM + magick ppm:$temp_ppm png:$ofile + rm -f $temp_ppm +} + +# extract any raw data file from PNG +# Usage: unpngize_raw $pngfile $outfile +unpngize_raw() { + temp_ppm="$(mktemp)" + # convert PNG to PPM (should be P6) + magick png:$1 ppm:$temp_ppm + # extract the raw body + tail -n +4 $temp_ppm > $2 + # remove the temporary PPM + rm -f $temp_ppm +} + +# Convert any file or directory to PNG +pngize() { + ofile="$2" + [ -z "$2" ] && ofile="$1.png" + temp_arch="$(mktemp)" + # containerize the input + tar cf $temp_arch "$1" + # run the conversion + pngize_raw $temp_arch $ofile + rm -f $temp_arch + # apply compression + pngcrush -ow $ofile +} + +# Extract the file or directory from PNG +unpngize() { + temp_arch="$(mktemp)" + unpngize_raw "$1" $temp_arch + tar xf $temp_arch + rm -f $temp_arch +} + +# entry point + +[ -z "$2" ] && echo 'No input file specified! Exiting...' && exit 1 +if [ "$1" = "e" ]; then + [ -z "$3" ] && echo "No output file specified! Using $2.png as output file" + pngize "$2" "$3" +elif [ "$1" = "d" ]; then + unpngize "$2" +else + echo 'Invalid mode! Use e for encoding or d for decoding' +fi