View Single Post
Old 06-28-2015, 05:52 AM   #229
ManDay
E-Reader
ManDay ought to be getting tired of karma fortunes by now.ManDay ought to be getting tired of karma fortunes by now.ManDay ought to be getting tired of karma fortunes by now.ManDay ought to be getting tired of karma fortunes by now.ManDay ought to be getting tired of karma fortunes by now.ManDay ought to be getting tired of karma fortunes by now.ManDay ought to be getting tired of karma fortunes by now.ManDay ought to be getting tired of karma fortunes by now.ManDay ought to be getting tired of karma fortunes by now.ManDay ought to be getting tired of karma fortunes by now.ManDay ought to be getting tired of karma fortunes by now.
 
Posts: 274
Karma: 1606616
Join Date: Oct 2012
Device: DPT-S1
I just figured someone could be interested in the script I had lying around my computer for quite some time now. I use it to crop PDFs for the reader. Example invocation

:/croppdf -o outfile.pdf infile.pdf 10 20. 500. 700 20 30. 500. 700

If you just run it without arguments, it gives a command summary for all features. Runs on Linux/Bash with ghostscript (command gs) installed.

Code:
#!/bin/bash

page_dimension=2
testmode=0
viewer=xdgopen
first_page=0
last_page=0
usage=0

for (( i = 0; i<page_dimension; i++ ))
do
	ratio[ i ]=1
done

ratio[ 0 ]=596
ratio[ 1 ]=842

while getopts :tf:l:v:a:o:d: name
do
	case "$name" in
	t)
		testmode=1
		;;
	f)
		first_page=$(( OPTARG*1 ))
		;;
	l)
		last_page=$(( OPTARG*1 ))
		;;
	o)
		outfile="$OPTARG"
		;;
	v)
		viewer="$OPTARG"
		;;
	d)
		page_dimension=$((OPTARG))
		;;
	a)
		remainder="${OPTARG}"
		i=0
		while echo "$remainder" | grep -oq '^[[:digit:]]*'
		do
			value=$(echo "$remainder" | grep -o '^[[:digit:]]*')
			ratio[ i ]=$value
			remainder=${remainder#${value},}
			(( i++ ))
		done
		;;
	?)
		usage=1
		;;
	esac
done

shift $((OPTIND - 1))

edge_count=$((2*page_dimension))

if (( usage || $#<5 || ( $#-1 )%edge_count ))
then
	echo "Usage $0 \\"
	echo " (-t)? \\"
	echo " (-a <Width>,<Height>)? \\"
	echo " (-f <First Page>)? (-l <Last Page>)? \\"
	echo " (-o <Out File>)? \\"
	echo " (-d <Dimension>)? \\"
	echo " (-v <Viewer>)? \\"
	echo " <In File> (<Left> <Bottom> ... <Right> <Top> ... )+"
	echo ""
	printf "Crop pages of PDF document <In File> to remove margins. If <First Page> is specified, will only output pages after that page. If <Last Page> is specified, will only output pages before that page.\nThe first group of <Left>, <Bottom>, <Right>, and <Top> specify the desired area to be cropped to on the first page of <In File>. The desired area for the n-th page after that page is obtained from the according group, with the groups being cyclically continued.\n\nTESTMODE\nIf -t is given, all outputted pages are cropped to their respective, desired areas and the resulting pdf is shown with the command specified by <Viewer> or \`xdgopen\`, if <Viewer> is not specified. This allows to tweak the areas to the exact desired amount.\n\nNORMAL MODE\nIf -t is not given, the smallest possible area with an optimal aspect ratio of width and height that contains the desired area is calculated. The outputted pages are then cropped to that optimal area with the optimal aspect ratio. If any of the values of <Left>, <Bottom>, <Right> or <Bottom> were suffixed by a period sign (.), the desired area will be aligned with the according edge of the optimal area. Otherwise, the desired area is centered within the optimal area. If no <Out File> is given, a filename is guessed based upon the <In File>. Otherwise, the output will be written to <Out File>.\nThe optimal aspect ratio can be specified by passing <Width> and <Height> and defaults to portrait oriented DIN A4. The dimensionality of pages can be specified by <Dimension>. Currently, the Ghostscript backend only supports 2 dimensional pages." | fmt -s
	exit 1
fi

infile=$1
shift 1

loop_count=$(($#/edge_count))

if [[ "$outfile" == "" ]]
then
	if echo "$infile" | grep -q '\.[^\/]*$'
	then
		outfile="${infile%.*}_cropped.pdf"
	else
		outfile="${infile}_cropped"
	fi
fi


optimal_ratio=$(echo "scale=3;${ratio[ 1 ]}/${ratio[ 0 ]}" | bc)
echo "Optimal ratio set to $optimal_ratio"

if (( testmode ))
then
	outfile="$(mktemp)"
else
	echo "Cropping '$infile' to '$outfile'"

	if [[ -f "$outfile" ]]
	then
		echo "'$outfile' already exists. Overwrite? y/[n]"
		read confirm
		if [[ "$confirm" == "y" ]]
		then
			rm "$outfile"
		else
			exit 1
		fi
	fi
fi

# GHOST SCRIPT DATA
declare -a lefts bottoms rights tops transx transy

for (( i = 0; i < loop_count; i++ ))
do
	echo "CropBox #$i:"
	
	declare -a desired
	aligns=(0 0)

	for (( edge = 0; edge<edge_count; edge++ ))
	do
		index=$((1+edge+i*edge_count))
		desired_value=${!index}

		if [[ "$desired_value" == *'.' ]]
		then
			desired_value=${desired_value:0:-1}
			(( aligns[ edge%page_dimension ]+=2*( edge/page_dimension )-1 ))
		fi
		desired[ edge ]=$((desired_value))
	done

	echo " Desired box: ${desired[ 0 ]} ${desired[ 1 ]} ${desired[ 2 ]} ${desired[ 3 ]}"

	echo " H-Alignment: ${aligns[ 0 ]}"
	echo " V-Alignment: ${aligns[ 1 ]}"

	if ! (( testmode ))
	then
		# Find constraining dimension, expand page in all other dimensions
		normalization=1
		for (( axis = 0; axis<page_dimension; axis++ ))
		do
			(( normalization*=ratio[ axis ] ))
		done

		constraining_axis=0
		constraining_factor=0

		for (( axis = 0; axis<page_dimension; axis++ ))
		do
			desired_distance=$((desired[ axis+page_dimension ]-desired[ axis ]))
			testfactor=$((( desired_distance*normalization )/ratio[ axis ]))

			if (( testfactor>constraining_factor ))
			then
				constraining_axis=$axis
				constraining_factor=$testfactor
			fi
		done

		constraining_distance=$((desired[ constraining_axis+page_dimension ]-desired[ constraining_axis ]))

		echo " Fit axis: $constraining_axis"

		# Now expand all other directions
		for (( align_dir = 0; align_dir<page_dimension; align_dir++ ))
		do
			if (( align_dir != constraining_axis ))
			then
				desired_distance=$((desired[ align_dir+page_dimension ]-desired[ align_dir ]))
				needed_distance=$((( constraining_distance*ratio[ align_dir ] )/ratio[ constraining_axis ]))

				margin=$((needed_distance-desired_distance))
				echo " Margin along axis $align_dir: $margin"
				
				for side in {0..1}
				do
					factor=$((( (-1)+2*side )-aligns[ align_dir ] ))
					(( desired[ align_dir+2*side ]+=( margin*factor )/2 ))
				done
			fi
		done
	fi

	echo " Effective box: ${desired[ 0 ]} ${desired[ 1 ]} ${desired[ 2 ]} ${desired[ 3 ]}"

	# BEGIN GHOSTSCRIPT SPECIFIC DATA PREPARATION
	if (( desired[ 0 ]>=0 ))
	then
		lefts[ i ]=${desired[ 0 ]}
		rights[ i ]=${desired[ 2 ]}
		transx[ i ]=0
	else
		lefts[ i ]=0
		rights[ i ]=$((desired[ 2 ]-desired[ 0 ]))
		transx[ i ]=$((-desired[ 0 ]))
	fi

	if (( desired[ 1 ]>=0 ))
	then	
		bottoms[ i ]=${desired[ 1 ]}
		tops[ i ]=${desired[ 3 ]}
		transy[ i ]=0
	else
		bottoms[ i ]=0
		tops[ i ]=$((desired[ 3 ]-desired[ 1 ]))
		transy[ i ]=$((-desired[ 1 ]))
	fi
	# END GHOSTSCRIPT SPECIFIC DATA PREPARATION
done

pages_string=''
if (( $first_page ))
then
	pages_string=" -dFirstPage=$first_page"
fi

if (( $last_page ))
then
	pages_string="$pages_string -dLastPage=$last_page"
fi

if (( first_page ))
then
	page_offset=$((first_page-1))
else
	page_offset=0
fi

read -d '' code <<endcode
	<<
		/BeginPage
		{
			${page_offset} add
			${loop_count} mod
				[ ${transx[@]} ]
					1 index
				get
					[ ${transy[@]} ]
						2 index
					get
			translate
		}
		/EndPage
		{
			0 eq
			{
				${page_offset} add
				${loop_count} mod
				[
					/CropBox
						[
							[ ${lefts[@]} ]
								4 index
							get
								[ ${bottoms[@]} ]
									5 index
								get
									[ ${rights[@]} ]
										6 index
									get
										[ ${tops[@]} ]
											7 index
									get
						]
							/PAGE
				pdfmark
				[
					/MediaBox
						[
							0
								0
									[ ${rights[@]} ]
										6 index
									get
										[ ${tops[@]} ]
											7 index
									get
						]
							/PAGE
				pdfmark
				true
			}
			{false}
			ifelse
		}
	>>
setpagedevice
endcode

gs -o "$outfile"$pages_string -sDEVICE=pdfwrite -c "$code" -f "$infile"

if (( testmode ))
then
	$viewer "$outfile"
	rm "$outfile"
fi
ManDay is offline   Reply With Quote