#!/bin/bash
# Script to extract individual pages of a book from raw PNM scan images

set -e

OUTDIR=/home/achurch/public_html/tsj
DPI=600
WIDTH=$[DPI*7/2]
HEIGHT=$[DPI*11/2]
SHRINK=3

min () {
    if test $1 -lt $2; then
	echo $1
    else
	echo $2
    fi
}

max () {
    if test $1 -gt $2; then
	echo $1
    else
	echo $2
    fi
}

# Page number, 2 points to find slope and 4 edge points (outer, 2 sides, inner)
while read page c1 c2 e1 e2 e3 e4; do
    if test "x$page" = "x#"; then continue; fi
    echo $page
    # Determine the filename
    if test $[page%2] = 0; then
	file=`echo tsj-\`printf %03d $page\`-???.pnm`
    else
	file=`echo tsj-???-\`printf %03d $page\`.pnm`
    fi
    # Find image width and height
    imgsize=`head -10 "$file" | grep '^[^#]' | head -2 | tail -1`
    imgw=`echo "$imgsize" | awk '{print $1}' | sed 's/^0*//'`
    imgh=`echo "$imgsize" | awk '{print $2}' | sed 's/^0*//'`
    # Find page rotation angle
    x1=`echo $c1 | cut -d, -f1`
    y1=`echo $c1 | cut -d, -f2`
    x2=`echo $c2 | cut -d, -f1`
    y2=`echo $c2 | cut -d, -f2`
    dx=$[x2-x1]
    dy=$[y2-y1]
    adx=$dx
    ady=$dy
    if test 0 -gt $adx; then adx=$[-adx]; fi
    if test 0 -gt $ady; then ady=$[-ady]; fi
    if test $adx -gt $ady; then
	# Coordinates represent a horizontal line
	sy=$dy
	sx=$dx
    else
	# Coordinates represent a vertical line
	sy=$[-dx]
	sx=$dy
    fi
    # sx and sy represent the slope of a horizontal line on the image
    # (vertical line on the page).  We want the angle to rotate by, which
    # is the negative of the line's angle, but image coordinates are
    # flipped vertically compared to Cartesian coordinates, so the
    # negatives cancel out.
    angle=`perl -e "use Math::Trig; print atan($sy/$sx)*180/3.141592653589793"`
    # Figure out which edge point is which
    # Note that T, L, etc. refer to top/left/etc w.r.t. the page, not the image
    x1=`echo $e1 | cut -d, -f1`
    y1=`echo $e1 | cut -d, -f2`
    x2=`echo $e2 | cut -d, -f1`
    y2=`echo $e2 | cut -d, -f2`
    x3=`echo $e3 | cut -d, -f1`
    y3=`echo $e3 | cut -d, -f2`
    x4=`echo $e4 | cut -d, -f1`
    y4=`echo $e4 | cut -d, -f2`
    if test $[page%2] = 0; then
	# Even (left) page: first coordinate is left edge, last is right edge
	lx=$x1
	ly=$y1
	rx=$x4
	ry=$y4
    else
	# Odd (right) page: first coordinate is right edge, last is left edge
	rx=$x1
	ry=$y1
	lx=$x4
	ly=$y4
    fi
    if test $x2 -lt $x3; then
	bx=$x2
	by=$y2
	tx=$x3
	ty=$y3
    else
	bx=$x3
	by=$y3
	tx=$x2
	ty=$y2
    fi
    # Find the corners of the page in source coordinates
    if test 0 != $angle; then
	intersect='($x1,$y1,$x2,$y2,$p,$q) = @ARGV; $c = $y1-$x1*($p/$q); $d = $y2-$x2*(-$q/$p); $x3 = ($p*$q*($d-$c))/($p*$p+$q*$q); $y3 = $x3*($p/$q)+$c'
	intersect_x="$intersect"'; printf "%d",int($x3+0.5)'
	intersect_y="$intersect"'; printf "%d",int($y3+0.5)'
	tlx=`perl -e "$intersect_x" $lx $ly $tx $ty $sy $sx`
	tly=`perl -e "$intersect_y" $lx $ly $tx $ty $sy $sx`
	trx=`perl -e "$intersect_x" $rx $ry $tx $ty $sy $sx`
	try=`perl -e "$intersect_y" $rx $ry $tx $ty $sy $sx`
	blx=`perl -e "$intersect_x" $lx $ly $bx $by $sy $sx`
	bly=`perl -e "$intersect_y" $lx $ly $bx $by $sy $sx`
	brx=`perl -e "$intersect_x" $rx $ry $bx $by $sy $sx`
	bry=`perl -e "$intersect_y" $rx $ry $bx $by $sy $sx`
    else
	# Angle is zero, so just copy the coordinates
	tlx=$tx
	tly=$ly
	trx=$tx
	try=$ry
	blx=$bx
	bly=$ly
	brx=$bx
	bry=$ry
    fi
    # Find the smallest enclosing rectangle, to reduce the size of the data
    # we need to work with (fluff the borders a little to be safe)
    x1=`min \`min $tlx $trx\` \`min $blx $brx\``
    x2=`max \`max $tlx $trx\` \`max $blx $brx\``
    y1=`min \`min $tly $try\` \`min $bly $bry\``
    y2=`max \`max $tly $try\` \`max $bly $bry\``
    cutx=$[x1-5]; if test $cutx -lt 0; then cutx=0; fi
    cuty=$[y1-5]; if test $cuty -lt 0; then cuty=0; fi
    cutw=$[x2-x1+1+10]; if test $[cutx+cutw] -gt $imgw; then cutw=$[imgw-cutx]; fi
    cuth=$[y2-y1+1+10]; if test $[cuty+cuth] -gt $imgh; then cuth=$[imgh-cuty]; fi
    # Determine page corner coordinates in target image
    # Note that the page is still turned on its side, so the top-left corner
    # of the image is the bottom-left corner of the page.  Also note that
    # image coordinates are vertically flipped from Cartesian, so adjust for
    # that.
    rotate='use Math::Trig; ($x,$y,$da) = @ARGV; $r = sqrt($x*$x+$y*$y); $r *= -1 if $x < 0; $a = asin(-$y/$r); $da *= 3.141592653589793/180'
    rotate_x="$rotate"'; printf "%d", int($r*cos($a+$da)+0.5)'
    rotate_y="$rotate"'; printf "%d", int($r*-sin($a+$da)+0.5)'
    cx=$[cutx+cutw/2]  # center coordinates
    cy=$[cuty+cuth/2]
    cropx=$[`perl -e "$rotate_x" -- $[blx-cx] $[bly-cy] $angle`+cutw/2]
    cropy=$[`perl -e "$rotate_y" -- $[blx-cx] $[bly-cy] $angle`+cuth/2]
    cropw=$[`perl -e "$rotate_x" -- $[trx-cx] $[try-cy] $angle`+cutw/2-cropx+1]
    croph=$[`perl -e "$rotate_y" -- $[trx-cx] $[try-cy] $angle`+cuth/2-cropy+1]
    # Adjust for borders added by pnmrotate
    bx=$[`perl -e "$rotate_x" -- $[-cutw/2] $[-cuth/2] $angle`+cutw/2]
    by=$[`perl -e "$rotate_y" -- $[-cutw/2] $[-cuth/2] $angle`+cuth/2]
    if test 0 -gt $bx; then bx=$[-bx]; fi
    if test 0 -gt $by; then by=$[-by]; fi
    cropx=$[cropx+bx]
    cropy=$[cropy+by]
    # Reduce to a constant size (note $cropw vs. $HEIGHT: this is still the
    # rotated image)
    if test $cropw -gt $HEIGHT; then
	cropx=$[cropx+(cropw-HEIGHT)/2]
	cropw=$HEIGHT
    fi
    if test $croph -gt $WIDTH; then
	if test $[page%2] = 0; then
	    cropy=$[cropy+DPI/3]
	else
	    cropy=$[cropy+((croph-WIDTH)-DPI/3)]
	fi
	croph=$WIDTH
    fi
    echo "$page $imgw $imgh $angle $blx,$bly $trx,$try [$cutx $cuty $cutw $cuth] [$cropx $cropy $cropw $croph]"
    # Okay, now do all the image manipulation
    pamcut $cutx $cuty $cutw $cuth "$file" \
	| (if test 0 = $angle; then cat; else pnmrotate $angle; fi) \
	| pamcut $cropx $cropy $cropw $croph \
        | pamscale -xsize=$[cropw/SHRINK] \
	| pamflip -ccw \
	| cjpeg -quality 75 -dct float -optimize -xdpi $[DPI/SHRINK] -ydpi $[DPI/SHRINK] \
	> "$OUTDIR/`printf %03d $page`.jpg"
done
