diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bc2dca4..30172d7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,3 +31,12 @@ jobs: chmod +x shrinkpdf.sh ./shrinkpdf.sh -g -r 72 -o new.pdf orig.pdf ls -al orig.pdf new.pdf + + # Shrink the PDF in place. + - name: Shrink the PDF in place + run: | + chmod +x shrinkpdf.sh + cp orig.pdf inplace.pdf + ls -al inplace.pdf + ./shrinkpdf.sh -g -r 72 -i inplace.pdf + ls -al inplace.pdf orig.pdf new.pdf diff --git a/shrinkpdf.sh b/shrinkpdf.sh index add6690..bdd9137 100755 --- a/shrinkpdf.sh +++ b/shrinkpdf.sh @@ -29,6 +29,40 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +create_tempdir () +{ + if command -v mktemp >/dev/null 2>&1; then + # Possibly unportable, but far more secure than the fallback below + mktemp -d + else + # Fallback method + temp_base="${TMPDIR:-/tmp}" + # $RANDOM might be undefined, but including a possibly empty string doesn't hurt + # shellcheck disable=SC3028 + temp_dir="$temp_base/shrinkpdf.sh.$$.$RANDOM.$(date +%s)" + + if mkdir "$temp_dir" 2>/dev/null; then + chmod 700 "$temp_dir" + echo "$temp_dir" + else + echo "could not create temporary directory $temp_dir." >&2 + exit 1 + fi + fi +} + +cleanup_tempdir () +{ + if [ -n "$1" ] ; then + rm -f "$1/output.pdf" + # ShellCheck complains about ls and weird filenames, but we only care about the number of newlines + # shellcheck disable=SC2012 + if [ "$( ls -A "$1" 2>/dev/null | wc -l )" -eq 0 ] ; then + # only rm -fr if the directory is empty to avoid recursive deletion catastrophe bugs + rm -fr "$1" + fi + fi +} shrink () { @@ -86,15 +120,17 @@ check_input_file () check_smaller () { # If $1 and $2 are regular files, we can compare file sizes to - # see if we succeeded in shrinking. If not, we copy $1 over $2: + # see if we succeeded in shrinking. if [ ! -f "$1" ] || [ ! -f "$2" ]; then - return 0; + echo 0; + return; fi ISIZE="$(wc -c "$1" | awk '{ print $1 }')" OSIZE="$(wc -c "$2" | awk '{ print $1 }')" if [ "$ISIZE" -lt "$OSIZE" ]; then - echo "Input smaller than output, doing straight copy" >&2 - cp "$1" "$2" + echo 1; + else + echo 0; fi } @@ -105,7 +141,7 @@ check_overwrite () # Unfortunately the stronger `-ef` test is not in POSIX. if [ "$1" = "$2" ]; then echo "The output file is the same as the input file. This would truncate the file." >&2 - echo "Use a temporary file as an intermediate step." >&2 + echo "Use a temporary file as an intermediate step, or use the -i flag." >&2 return 1 fi } @@ -123,6 +159,7 @@ usage () echo " -o Output file, default is standard output." echo " -r Resolution in DPI, default is 72." echo " -t Threshold multiplier for an image to qualify for downsampling, default is 1.5" + echo " -i Inplace operation: Overwrite the input file with the output if smaller. Use with caution." } # Set default option values. @@ -130,9 +167,10 @@ grayscale="" ofile="-" res="72" threshold="1.5" +inplace=0 # Parse command line options. -while getopts ':hgo:r:t:' flag; do +while getopts ':hgo:ir:t:' flag; do case $flag in h) usage "$0" @@ -142,7 +180,25 @@ while getopts ':hgo:r:t:' flag; do grayscale="YES" ;; o) - ofile="${OPTARG}" + if [ "-" = "$ofile" ] ; then + ofile="${OPTARG}" + else + echo "invalid combination of options -o and -i." + exit 1 + fi + ;; + i) + if [ "-" = "$ofile" ] ; then + temp_dir=$(create_tempdir) + # I actually want this string expanded right here. + # shellcheck disable=SC2064 + trap "cleanup_tempdir \"$temp_dir\"" EXIT INT TERM + ofile="$temp_dir/output.pdf" + inplace=1 + else + echo "invalid combination of options -o and -i." + exit 1 + fi ;; r) res="${OPTARG}" @@ -179,4 +235,18 @@ get_pdf_version "$ifile" || pdf_version="1.5" shrink "$ifile" "$ofile" "$res" "$pdf_version" "$threshold" || exit $? # Check that the output is actually smaller. -check_smaller "$ifile" "$ofile" +if [ 1 -eq "$(check_smaller "$ifile" "$ofile")" ] ; then + # file got bigger, just overwrite with the smaller original + echo "Input smaller than output, doing straight copy" >&2 + cp "$ifile" "$ofile" +else + # file has shrunk + if [ 1 -eq "$inplace" ] ; then + # user requested inplace operation, so overwrite the original + # input file with the temporary and smaller output + cp "$ofile" "$ifile" + + # if not inplace: + # noting to do, output has already been written to the right place + fi +fi