#!/bin/bash
#
# Developed by Fred Weinhaus 8/15/2019 .......... 8/15/2019
#
# ------------------------------------------------------------------------------
# 
# Licensing:
# 
# Copyright © Fred Weinhaus
# 
# My scripts are available free of charge for non-commercial use, ONLY.
# 
# For use of my scripts in commercial (for-profit) environments or 
# non-free applications, please contact me (Fred Weinhaus) for 
# licensing arrangements. My email address is fmw at alink dot net.
# 
# If you: 1) redistribute, 2) incorporate any of these scripts into other 
# free applications or 3) reprogram them in another scripting language, 
# then you must contact me for permission, especially if the result might 
# be used in a commercial or for-profit environment.
# 
# My scripts are also subject, in a subordinate manner, to the ImageMagick 
# license, which can be found at: http://www.imagemagick.org/script/license.php
# 
# ------------------------------------------------------------------------------
# 
####
#
# USAGE: textetch -t text [-p pointsize] [-f font] [-c color] [-g gravity] [-o offset]
# [-a arc] [-r rotation] [-n noise] [-i imageoffset] [-s scale] [-b brighness] 
# [-C compose] infile [clipart] outfile
# 
# USAGE: textetch [-help or -h]
#
# OPTIONS:
#
# -t     text            text to apply to the input image. Required.
# -p     pointsize       pointsize for the text; integer>0; default=28
# -f     font            font name or path to font file; default=arial
# -c     color           color of font; any valid opaque IM color is allowed; 
#                        default="gray(128)"
# -g     gravity         gravity setting for offset placement of text; choices are: 
#                        center or northwest; default=center
# -o     offset          offset of text placement relative to gravity setting; format 
#                        is +-X+-Y; default="+0+0"
# -a     arc             arc curvature of the text in degrees; 0<=integer<=360; 
#                        default=0
# -r     rotation        rotation angle of text; 0<=integer<=360; default=0
# -n     noise           noise amount to use to texturize the text; float>=0; default=0.5
# -i     imageoffset     offset of clipart placement relative to gravity setting; format 
#                        is +-X+-Y; default="+0+0"
# -s     scale           scale in percent for clipart; integer>0; default=100 (no change)
# -b     brightness      brightness for clipart; integer>0; default=100 (no change)
# -C     compose         compose method for compositing text and clipart; choices are: 
#                        over or multiply; default=over
# 
###
#
# NAME: TEXTETCH 
# 
# PURPOSE: Adds text to an image to simulate etching on glass.
# 
# DESCRIPTION: TEXTETCH adds text to an image to simulate etching on glass. Text is 
# nominally added in one line. Multiple line format is controlled by adding new line 
# \n characters within the text. One optional clipart file may be added. It should 
# have a transparent background and will be converted to grayscale and optionally 
# colorized the same as the text.
# 
# 
# OPTIONS: 
# 
# -t text ... TEXT string to apply to the image. Required. Must be enclosed in quotes.
# 
# -p pointsize ... POINTSIZE for the text. Values are integer>0. The default=28.
# 
# -f font ... FONT name or path to font file. The default=arial.
# 
# -c color ... COLOR of the font. Any valid opaque IM color is allowed. The 
# default="gray(128)".
# 
# -g gravity ... GRAVITY setting for the offset placement of text. The choices are: 
# center (c) or northwest (n). The default=center.
# 
# -o offset ... OFFSET placement of the text relative to the gravity setting. The 
# format is +-X+-Y. The default="+0+0"
# 
# -a arc ... ARC downward curvature of the text in degrees. Values are 0<=integer<=360. 
# The default=0.
# 
# -r rotation ... ROTATION angle of text. Values are 0<=integer<=360. The default=0.
# 
# -n noise ... NOISE amount to use to texturize the text. Values are float>=0. 
# The default=0.5.
# 
# -i imageoffset ... IMAGE OFFSET of clipart placement relative to gravity setting. 
# The format is +-X+-Y; default="+0+0".
# 
# -s scale ... SCALE in percent for clipart. Values are integer>0. The default=100 
# (indicates no change).
# 
# -b brightness ... BRIGHTNESS adjustment for the clipart. Values are integer>0. 
# The default=100 (indicates no change)
# 
# -C compose ... COMPOSE method for compositing text and clipart; choices are: 
# over (o) or multiply (m). The default=over.
# 
# CAVEAT: No guarantee that this script will work on all platforms, 
# nor that trapping of inconsistent parameters is complete and 
# foolproof. Use At Your Own Risk. 
# 
######
#

# set default values
text=""						# text is required
pointsize=28				# font pointsize
font="arial"				# font name
color="gray(128)"			# font color
gravity="center"		    # gravity for offset: northwest or center
offset="+0+0"				# text offset relative to gravity
arc=0						# arc downward curvature
rotation=0					# rotation of text
noise=0.5					# noise attenuation
imageoffset="+0+0"			# image offset relative to gravity
scale=100					# resize percent for clipart
brightness=100				# brightness percent for clipart
compose="over"				# compose method


# set directory for temporary files
# tmpdir="/tmp"
tmpdir="."

# set up functions to report Usage and Usage with Description
PROGNAME=`type $0 | awk '{print $3}'`  # search for executable on path
PROGDIR=`dirname $PROGNAME`            # extract directory of program
PROGNAME=`basename $PROGNAME`          # base name of program
usage1() 
	{
	echo >&2 ""
	echo >&2 "$PROGNAME:" "$@"
	sed >&2 -e '1,/^####/d;  /^###/g;  /^#/!q;  s/^#//;  s/^ //;  4,$p' "$PROGDIR/$PROGNAME"
	}
usage2() 
	{
	echo >&2 ""
	echo >&2 "$PROGNAME:" "$@"
	sed >&2 -e '1,/^####/d;  /^######/g;  /^#/!q;  s/^#*//;  s/^ //;  4,$p' "$PROGDIR/$PROGNAME"
	}


# function to report error messages
errMsg()
	{
	echo ""
	echo $1
	echo ""
	usage1
	exit 1
	}


# function to test for minus at start of value of second part of option 1 or 2
checkMinus()
	{
	test=`echo "$1" | grep -c '^-.*$'`   # returns 1 if match; 0 otherwise
    [ $test -eq 1 ] && errMsg "$errorMsg"
	}

# test for correct number of arguments and get values
if [ $# -eq 0 ]
	then
	# help information
   echo ""
   usage2
   exit 0
elif [ $# -gt 29 ]
	then
	errMsg "--- TOO MANY ARGUMENTS WERE PROVIDED ---"
else
	while [ $# -gt 0 ]
		do
			# get parameter values
			case "$1" in
		   -help|h)    # help information
					   echo ""
					   usage2
					   exit 0
					   ;;
				-t)    # get text
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   text="$1"
		   			   ;;
				-p)    # get pointsize
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID POINTSIZE SPECIFICATION ---"
					   checkMinus "$1"
					   pointsize=`expr "$1" : '\([0-9]*\)'`
					   [ "$pointsize" = "" ] && errMsg "--- POINTSIZE=$pointsize MUST BE A NON-NEGATIVE INTEGER ---"
					   test=`echo "$pointsize <= 0" | bc`
					   [ $test -eq 1  ] && errMsg "--- POINTSIZE=$pointsize MUST BE A POSITIVE INTEGER ---"
		   			   ;;
				-f)    # get font
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID FONT SPECIFICATION ---"
					   checkMinus "$1"
					   font="$1"
		   			   ;;
				-c)    # get color
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID COLOR SPECIFICATION ---"
					   checkMinus "$1"
					   color="$1"
		   			   ;;
				-g)    # get gravity
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID GRAVITY SPECIFICATION ---"
					   checkMinus "$1"
					   gravity=`echo "$1" | tr "[:upper:]" "[:lower:]"`
					   case "$gravity" in 
					   		center|c) gravity="center" ;;
					   		northwest|n) gravity="northwest" ;;
					   		*) errMsg "--- GRAVITY=$gravity IS AN INVALID VALUE ---" 
					   	esac
					   ;;
				-o)    # get offset
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID OFFSET SPECIFICATION ---"
					   checkMinus "$1"
					   offset=`expr "$1" : '\([+-][0-9]*[+-][0-9]*\)'`
					   [ "$offset" = "" ] && errMsg "--- OFFSET=$offset MUST BE IN FORMAT +-X+-Y ---"
		   			   ;;
				-a)    # get arc
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID ARC SPECIFICATION ---"
					   checkMinus "$1"
					   arc=`expr "$1" : '\([0-9]*\)'`
					   [ "$arc" = "" ] && errMsg "--- ARC=$arc MUST BE A NON-NEGATIVE INTEGER ---"
					   test1=`echo "$arc < 0" | bc`
					   test2=`echo "$arc > 360" | bc`
					   [ $test1 -eq 1 -o $test2 -eq 1 ] && errMsg "--- ARC=$arc MUST BE A POSITIVE INTEGER BETWEEN 0 AND 360 ---"
		   			   ;;
				-r)    # get rotation
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID ROTATION SPECIFICATION ---"
					   checkMinus "$1"
					   rotation=`expr "$1" : '\([0-9]*\)'`
					   [ "$rotation" = "" ] && errMsg "--- ROTATION=$rotation MUST BE A NON-NEGATIVE INTEGER ---"
					   test1=`echo "$rotation < 0" | bc`
					   test2=`echo "$rotation > 360" | bc`
					   [ $test1 -eq 1 -o $test2 -eq 1 ] && errMsg "--- ROTATION=$rotation MUST BE A POSITIVE INTEGER BETWEEN 0 AND 360 ---"
		   			   ;;
				-n)    # get noise
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID NOISE SPECIFICATION ---"
					   checkMinus "$1"
					   noise=`expr "$1" : '\([.0-9]*\)'`
					   [ "$noise" = "" ] && errMsg "--- NOISE=$noise MUST BE A NON-NEGATIVE FLOAT ---"
					   ;;
				-i)    # get imageoffset
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID IMAGEOFFSET SPECIFICATION ---"
					   checkMinus "$1"
					   imageoffset=`expr "$1" : '\([+-][0-9]*[+-][0-9]*\)'`
					   [ "$imageoffset" = "" ] && errMsg "--- IMAGEOFFSET=$imageoffset MUST BE IN FORMAT +-X+-Y ---"
		   			   ;;
				-s)    # get scale
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID SCALE SPECIFICATION ---"
					   checkMinus "$1"
					   scale=`expr "$1" : '\([0-9]*\)'`
					   [ "$scale" = "" ] && errMsg "--- SCALE=$scale MUST BE A NON-NEGATIVE INTEGER ---"
					   test=`echo "$scale <= 0" | bc`
					   [ $test -eq 1  ] && errMsg "--- SCALE=$scale MUST BE A POSITIVE INTEGER ---"
		   			   ;;
				-b)    # get brightness
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID BRIGHTNESS SPECIFICATION ---"
					   checkMinus "$1"
					   brightness=`expr "$1" : '\([0-9]*\)'`
					   [ "$brightness" = "" ] && errMsg "--- BRIGHTNESS=$brightness MUST BE A NON-NEGATIVE INTEGER ---"
					   test=`echo "$brightness <= 0" | bc`
					   [ $test -eq 1  ] && errMsg "--- BRIGHTNESS=$brightness MUST BE A POSITIVE INTEGER ---"
		   			   ;;
				-C)    # get compose
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID COMPOSE SPECIFICATION ---"
					   checkMinus "$1"
					   compose=`echo "$1" | tr "[:upper:]" "[:lower:]"`
					   case "$compose" in 
					   		over|o) compose="over" ;;
					   		multiply|m) compose="multiply" ;;
					   		*) errMsg "--- COMPOSE=$compose IS AN INVALID VALUE ---" 
					   	esac
					   ;;
			 	 -)    # STDIN and end of arguments
					   break
					   ;;
				-*)    # any other - argument
					   errMsg "--- UNKNOWN OPTION ---"
					   ;;
		     	 *)    # end of arguments
					   break
					   ;;
			esac
			shift   # next option
	done
	#
	# get infile, clipart and outfile
	if [ $# -eq 3 ]; then
		infile="$1"
		clipart="$2"
		outfile="$3"
	elif [ $# -eq 2 ]; then
		infile="$1"
		outfile="$2"
	else
	errMsg "--- NO OUTPUT FILE SPECIFIED ---"
	fi
fi

# test that infile provided
[ "$infile" = "" ] && errMsg "NO INPUT FILE SPECIFIED"

# test that outfile provided
[ "$outfile" = "" ] && errMsg "NO OUTPUT FILE SPECIFIED"


dir="$tmpdir/TEXTETCH.$$"

mkdir "$dir" || errMsg "--- FAILED TO CREATE TEMPORARY FILE DIRECTORY ---"
trap "rm -rf $dir; exit 0" 0
trap "rm -rf $dir; exit 1" 1 2 3 15

# read the input image into the temporary cached image and test if valid
convert -quiet "$infile" +repage $dir/tmpI.mpc ||
	echo "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO size  ---"

if [ "$clipart" != "" ]; then
	# read the clipart image into the temporary cached image and test if valid
	convert -quiet "$clipart" +repage $dir/tmpC.mpc ||
		echo "--- FILE $clipart DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO size  ---"
fi

arcval=`convert xc: -format "%[fx:$arc+quantumscale]" info:`

if [ "$clipart" = "" ]; then
	convert $dir/tmpI.mpc \
		\( -background none -font $font -pointsize $pointsize -gravity center -fill "white" label:"$text" \
			-channel rgb -shade 270x45 -attenuate $noise +noise gaussian -colorspace gray \
			-fill "$color" -colorize 50 +channel -channel a -evaluate multiply 0.5 +channel \
			-rotate 180 -virtual-pixel none -distort arc "$arcval 180" \
			-rotate $rotation \) \
		-gravity $gravity -geometry "$offset" -compose over -composite \
		"$outfile"
else
	convert $dir/tmpI.mpc \
		\( -background none -font $font -pointsize $pointsize -gravity center -fill "white" label:"$text" \
			-channel rgb -shade 270x45 -attenuate $noise +noise gaussian -colorspace gray \
			-fill "$color" -colorize 50 +channel -channel a -evaluate multiply 0.5 +channel \
			-rotate 180 -virtual-pixel none -distort arc "$arcval 180" \
			-rotate $rotation \) \
		-gravity $gravity -geometry "$offset" -compose over -composite \
		\( $dir/tmpC.mpc -resize $scale% -fill none -rotate $rotation \
			-channel rgb -modulate $brightness,100,100 \
			-attenuate $noise +noise gaussian -colorspace gray \
			-fill "$color" -colorize 50 +channel -channel a -evaluate multiply 0.5 +channel \) \
		-gravity $gravity -geometry "$imageoffset" -compose over -composite \
		"$outfile"
fi

exit 0