1#!@BASH@ 2 3# Driver for rastertogutenprint tester. 4# 5# Copyright 2007-2017 Robert Krawitz (rlk@alum.mit.edu) 6# 7# This program is free software; you can redistribute it and/or modify it 8# under the terms of the GNU General Public License as published by the Free 9# Software Foundation; either version 2 of the License, or (at your option) 10# any later version. 11# 12# This program is distributed in the hope that it will be useful, but 13# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15# for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program. If not, see <https://www.gnu.org/licenses/>. 19 20retval=0 21 22if [[ -z $srcdir || $srcdir = . ]] ; then 23 sdir=$(pwd) 24elif [[ $srcdir =~ ^/ ]] ; then 25 sdir="$srcdir" 26else 27 sdir="$(pwd)/$srcdir" 28fi 29 30export STP_DATA_PATH=${STP_DATA_PATH:-"$sdir/../xml"} 31export STP_MODULE_PATH=${STP_MODULE_PATH:-"$sdir/../main:$sdir/../main/.libs"} 32 33declare single=0 34declare verbose=0 35declare valgrind=0 36declare make_ppds=1 37declare skip_simplified=0 38declare postscript=0 39declare use_explicit_quality=0 40declare extra_genppd_opts="-Z" 41declare use_smallest_pagesize=0 42declare cupsargs='' 43declare -a printers_to_test 44declare dontrun=0 45 46if [ -n "$STP_TEST_DEBUG" ] ; then 47 echo "Would run with single=$single skip_simplified=$skip_simplified extra_genppd_opts=$extra_genppd_opts cupsargs=$cupsargs use_explicit_quality=$use_explicit_quality valopts=$valopts valgrind=$valgrind" 48 exit 0 49fi 50 51declare family=$STP_TEST_FAMILY 52declare all_models='' 53declare md5dir='' 54declare outdir='' 55declare qualarg='' 56declare npages=3 57declare jobs=${STP_PARALLEL:-1} 58declare -A all_models= 59 60if [[ -r $sdir/../../src/cups/gutenprint-users-manual.pdf ]] ; then 61 testfile="$sdir/../../src/cups/gutenprint-users-manual.pdf" 62else 63 testfile="$sdir/../../doc/gutenprint-users-manual.pdf" 64fi 65 66quality_presets=(FastEconomy Economy Draft Standard High Photo HighPhoto UltraPhoto Best) 67 68usage() { 69 cat <<'EOF' 70Usage: test-rastertogutenprint [options] [PPD files...] 71 Options: 72 -s Run only one PPD file with a given model ID/family 73 -v Use valgrind 74 -c Use cachegrind 75 -g Use GDB attach in valgrind 76 -V Verbose output 77 -n Don't build PPD files prior to run 78 -O dir Save output in specified directory 79 -o opt Set option on CUPS command line 80 -m dir Save MD5 checksums in specified directory 81 -p pages Specify page range of input document to use 82 -P Use PostScript rather than PDF input 83 -t jobs Run jobs in parallel (alternatively, use STP_PARALLEL) 84 -f family Run printers only in the particular family 85 -S Skip simplified PPD files 86 -l Use lowest available quality setting 87 -L Use highest available quality setting 88 -X Don't use explicit quality setting 89 -N Use the smallest available page size 90 -d Don't actually run the command 91EOF 92exit 0; 93} 94 95while getopts "hvcgsVnO:m:o:p:PSt:lLXf:Nd" opt ; do 96 case "$opt" in 97 h*) usage ;; 98 v) valgrind=$((valgrind + 1)) ;; 99 c) valgrind=4 ;; 100 g) valopts='--vgdb=yes --error-exitcode=1' ;; 101 s) single=1 ;; 102 V) verbose=$((verbose+1)) ;; 103 n) make_ppds=0 ;; 104 O) outdir="$OPTARG"; mkdir -p "$outdir" ;; 105 o) cupsargs="$cupsargs $OPTARG" ;; 106 m) md5dir="$OPTARG"; mkdir -p "$md5dir" ;; 107 p) npages="$OPTARG" ;; 108 P) postscript=1 ;; 109 t) jobs="$OPTARG" ;; 110 f) family="$OPTARG" ;; 111 S) skip_simplified=1 ;; 112 X) use_explicit_quality=0 ;; 113 l) use_explicit_quality=1 ;; 114 L) use_explicit_quality=2 ;; 115 N) use_smallest_pagesize=1 ;; 116 d) dontrun=1 ;; 117 \?) usage ;; 118 *) echo "Unknown argument $opt"; usage ;; 119 esac 120done 121 122STP_TEST_ROTOR=${STP_TEST_ROTOR:-0} 123STP_TEST_ROTOR_CIRCUMFERENCE=${STP_TEST_ROTOR_CIRCUMFERENCE:-1} 124 125case "$valgrind" in 126 4) 127 valopts='--tool=callgrind --dump-instr=yes --trace-jump=yes --error-exitcode=1' 128 ;; 129 '') 130 ;; 131 *) 132 valopts='--tool=memcheck --error-exitcode=1' 133 ;; 134esac 135 136shift $((OPTIND - 1)) 137 138if (( $# > 0 )) ; then 139 single=0 140 for i in $(seq 1 $#) ; do 141 (( (i - 1) % $STP_TEST_ROTOR_CIRCUMFERENCE == $STP_TEST_ROTOR )) && printers_to_test+=($1) 142 shift 143 done 144 (( ${#printers_to_test[@]} == 0 )) && exit 77 145fi 146 147(( single > 0 )) && extra_genppd_opts="$extra_genppd_opts -S" 148 149if (( STP_TEST_ROTOR_CIRCUMFERENCE > 1 && 150 STP_TEST_ROTOR >= 0 && 151 STP_TEST_ROTOR < STP_TEST_ROTOR_CIRCUMFERENCE )) ; then 152 extra_genppd_opts="$extra_genppd_opts -R $STP_TEST_ROTOR_CIRCUMFERENCE -r $STP_TEST_ROTOR" 153 STP_TEST_ROTOR=0 154 STP_TEST_ROTOR_CIRCUMFERENCE=1 155fi 156 157version="@GUTENPRINT_RELEASE_VERSION@"; 158rgp="./rastertogutenprint.$version" 159cupsdir="$(cups-config --serverbin)/filter" 160cgpdftoraster="$cupsdir/cgpdftoraster" 161gstoraster="$cupsdir/gstoraster" 162imagetoraster="$cupsdir/imagetoraster" 163pdftops="$cupsdir/pdftops" 164pstops="$cupsdir/pstops" 165pstoraster="$cupsdir/pstoraster" 166 167if [[ ! -x $cgpdftoraster && ! -x $pdftops && ! -x $gstoraster ]] ; then 168 echo 'CUPS does not appear to be installed, skipping test' 169 exit 0 170fi 171 172if [[ -x $pstoraster || -x $gstoraster || -x $cgpdftoraster ]] ; then 173 pages="24-$((24 + npages - 1))" 174 (( postscript > 0 )) && pages="page-ranges=$pages" 175else 176 pages='' 177fi 178 179cleanup() { 180 [[ -n $tfile ]] && rm -f "$tfile" 181 exit 1 182} 183 184pdfjam=$(type -p pdfjam) 185[[ -z $pdfjam ]] && postscript=1 186 187if (( postscript > 0 )) ; then 188 pdftops=$(type -p pdftops) 189 190 if [[ -n $pdftops && ! -x $cgpdftoraster ]] ; then 191 tfile=$(mktemp) 192 trap cleanup 1 2 3 6 14 15 30 193 "$pdftops" -f 24 -l $((24 + npages - 1)) "$testfile" "$tfile" 194 fi 195else 196 tfile=$(mktemp) 197 trap cleanup 1 2 3 6 14 15 30 198 "$pdfjam" -q "$testfile" "$pages" -o "$tfile" 199fi 200 201case "$verbose" in 202 1) 203 export STP_SUPPRESS_VERBOSE_MESSAGES=1 204 ;; 205 0|'') 206 export STP_SUPPRESS_MESSAGES=1 207 export STP_SUPPRESS_VERBOSE_MESSAGES=1 208 ;; 209 *) 210 ;; 211esac 212 213# Note that using CUPS arguments may trigger valgrind memory leaks in 214# CUPS. 215#cupsargs='PageSize=Custom.400.00x500.00' 216#cupsargs='PageSize=Custom.324x495 Resolution=180dpi' 217#cupsargs='PageSize=w324h495 Resolution=180dpi' 218#cupsargs='PageSize=A8' 219 220get_ppds() { 221 if [[ -n $* ]] ; then 222 for f in "$@" ; do 223 if [[ -r $f ]] ; then 224 echo "$f" 225 elif [[ -r ppd/C/$f ]] ; then 226 echo "ppd/C/$f" 227 elif [[ -f ppd/C/${f}.ppd ]] ; then 228 echo "ppd/C/${f}.ppd" 229 elif [[ -f ppd/C/${f}.ppd.gz ]] ; then 230 echo "ppd/C/${f}.ppd.gz" 231 elif [[ -f ppd/C/${f}.ppd.GZ ]] ; then 232 echo "ppd/C/${f}.ppd.GZ" 233 elif [[ -f ppd/C/${f}.ppd.bz2 ]] ; then 234 echo "ppd/C/${f}.ppd.bz2" 235 elif [[ -f ppd/C/${f}.ppd.BZ2 ]] ; then 236 echo "ppd/C/${f}.ppd.BZ2" 237 elif [[ -f ppd/C/${f}.ppd.z ]] ; then 238 echo "ppd/C/${f}.ppd.z" 239 elif [[ -f ppd/C/${f}.ppd.Z ]] ; then 240 echo "ppd/C/${f}.ppd.Z" 241 elif [[ -f ppd/C/stp-${f}.ppd ]] ; then 242 echo "ppd/C/stp-${f}.ppd" 243 elif [[ -f ppd/C/stp-${f}.ppd.gz ]] ; then 244 echo "ppd/C/stp-${f}.ppd.gz" 245 elif [[ -f ppd/C/stp-${f}.ppd.GZ ]] ; then 246 echo "ppd/C/stp-${f}.ppd.GZ" 247 elif [[ -f ppd/C/stp-${f}.ppd.bz2 ]] ; then 248 echo "ppd/C/stp-${f}.ppd.bz2" 249 elif [[ -f ppd/C/stp-${f}.ppd.BZ2 ]] ; then 250 echo "ppd/C/stp-${f}.ppd.BZ2" 251 elif [[ -f ppd/C/stp-${f}.ppd.z ]] ; then 252 echo "ppd/C/stp-${f}.ppd.z" 253 elif [[ -f ppd/C/stp-${f}.ppd.Z ]] ; then 254 echo "ppd/C/stp-${f}.ppd.Z" 255 elif [[ -f ppd/C/stp-${f}.${version}.ppd ]] ; then 256 echo "ppd/C/stp-${f}.${version}.ppd" 257 elif [[ -f ppd/C/stp-${f}.${version}.ppd.gz ]] ; then 258 echo "ppd/C/stp-${f}.${version}.ppd.gz" 259 elif [[ -f ppd/C/stp-${f}.${version}.ppd.GZ ]] ; then 260 echo "ppd/C/stp-${f}.${version}.ppd.GZ" 261 elif [[ -f ppd/C/stp-${f}.${version}.ppd.bz2 ]] ; then 262 echo "ppd/C/stp-${f}.${version}.ppd.bz2" 263 elif [[ -f ppd/C/stp-${f}.${version}.ppd.BZ2 ]] ; then 264 echo "ppd/C/stp-${f}.${version}.ppd.BZ2" 265 elif [[ -f ppd/C/stp-${f}.${version}.ppd.z ]] ; then 266 echo "ppd/C/stp-${f}.${version}.ppd.z" 267 elif [[ -f ppd/C/stp-${f}.${version}.ppd.Z ]] ; then 268 echo "ppd/C/stp-${f}.${version}.ppd.Z" 269 fi 270 done 271 else 272 echo ppd/C/*.ppd* 273 fi 274} 275 276if [[ $make_ppds -gt 0 || ! -d ppd/C ]] ; then 277 rm -rf ppd/C 278## not all systems can work with gzipped PPDs 279 extra_genppd_opts="$extra_genppd_opts ${printers_to_test[*]}" 280 if [[ $skip_simplified == 1 ]] ; then 281 echo make ppd-nonls EXTRA_GENPPD_OPTS="$extra_genppd_opts" 282 make ppd-nonls EXTRA_GENPPD_OPTS="$extra_genppd_opts" 283 else 284 make ppd-nonls-a EXTRA_GENPPD_OPTS="$extra_genppd_opts" 285 fi 286fi 287 288find_page_size() { 289 ppd=$1 290 (( use_smallest_pagesize == 0 )) && return; 291 driver=$(grep '^\*StpDriverName' "$ppd" | sed -e 's/^[^"]*"//' -e 's/"//g') 292 pagesize=$(./min-pagesize "$driver") 293 [[ -n "$pagesize" ]] && echo "PageSize=$pagesize" 294} 295 296find_resolution() { 297 ppd=$1 298 resolutions=$(grep "^\\*Resolution " "$ppd" |sed -e 's,/.*,,' -e 's/.* //') 299 [[ -z "$resolutions" ]] && return 300 low_resolution=9999999999 301 low_resolution_name='' 302 high_resolution=0 303 high_resolution_name='' 304 for r in $resolutions ; do 305 res=$(sed -e 's/dpi//' -e 's/x/ \\\* /' -e 's/^\([0-9]*\)$/\1 \\\* \1/' <<< "$r") 306 resnum=$(eval "expr $res") 307 if (( resnum > high_resolution )) ; then 308 high_resolution=$resnum 309 high_resolution_name=$r 310 fi 311 if (( resnum < low_resolution )) ; then 312 low_resolution=$resnum 313 low_resolution_name=$r 314 fi 315 done 316 if (( use_explicit_quality == 1 )) ; then 317 echo "Resolution=$low_resolution_name" 318 elif (( use_explicit_quality == 2 )) ; then 319 echo "Resolution=$high_resolution_name" 320 fi 321 322} 323 324find_quality_preset() { 325 ppd=$1 326 if (( use_explicit_quality == 1 )) ; then 327 for q in "${quality_presets[@]}" ; do 328 if grep -q "^\\*StpQuality $q" "$ppd" ; then 329 echo "StpQuality=$q" 330 return 331 fi 332 done 333 elif (( use_explicit_quality == 2 )) ; then 334 best_quality='' 335 for q in "${quality_presets[@]}" ; do 336 grep -q "^\\*StpQuality $q" "$ppd" && best_quality=$q 337 done 338 [[ -n $best_quality ]] && echo "StpQuality=$best_quality" 339 fi 340} 341 342find_quality() { 343 ppd=$1 344 if [[ ! -r $ppd ]] ; then 345 echo "Can't find $ppd!" 1>&2 346 exit 1; 347 fi 348 (( use_explicit_quality == 0 )) && return 349 if grep -q '\*Resolution' "$ppd" ; then 350 find_resolution "$ppd" 351 else 352 find_quality_preset "$ppd" 353 fi 354} 355 356xgrep() { 357 pat=$1 358 file=$2 359 if [[ $file == *.gz ]] ; then 360 grep -E -m1 "$pat" "$file" 361 else 362 zgrep "$pat" "$file" 363 fi 364} 365 366runcmd() { 367 qualarg=$(find_quality "$PPD") 368 sizearg=$(find_page_size "$PPD") 369 a=(1 1 1 1) 370 qarg="$qualarg $sizearg $cupsargs" 371 if [[ -x $cgpdftoraster ]] ; then 372 # cgpdftoraster doesn't like arguments. How rude. 373 $cgpdftoraster "${a[@]}" "" < "$tfile" 374 elif [[ -f $tfile && -x $gstoraster ]] ; then 375 $gstoraster "${a[@]}" "$qarg" < "$tfile" 376 elif [[ -f $tfile ]] ; then 377 $pstops "${a[@]}" $"qarg" < "$tfile" 378 elif [[ -x $pstoraster ]] ; then 379 $pdftops "${a[@]}" "$qarg" < "$tfile" | $pstops "${a[@]}" "$pages$qarg" | $pstoraster 380 elif [[ -x $gstoraster ]] ; then 381 $pdftops "${a[@]}" "$qarg" < "$tfile" | $gstoraster "${a[@]}" "$pages$qarg" 382 else 383 $imagetoraster "${a[@]}" "$qarg" < calibrate.ppm 384 fi 385} 386 387do_output() { 388 driver=$(xgrep '^\*StpDriverName:' "$PPD" |awk '{print $2}' | sed 's/"//g') 389 if [[ -n $outdir ]] ; then 390 cat > "$outdir/$driver.prn" 391 if [[ -n $md5dir ]] ; then 392 md5sum < "$outdir/$driver.prn" | sed "s/-/\*$driver/" > "$md5dir/$driver.md5" 393 fi 394 elif [[ -n $md5dir ]] ; then 395 cat | md5sum | sed "s/-/\*$driver/" > "$md5dir/$driver.md5" 396 else 397 cat >/dev/null 398 fi 399} 400 401run_rastertogp() { 402 qualarg=$(find_quality "$PPD") 403 sizearg=$(find_page_size "$PPD") 404 vg="libtool --mode=execute valgrind $valopts --log-fd=3" 405 vg1="$vg --num-callers=50 --leak-check=yes --error-limit=no --error-exitcode=1" 406 rgpc="$rgp 1 1 1 1" 407 qarg="$qualarg $sizearg $cupsargs" 408 case "$valgrind" in 409 1) $vg1 -q $rgpc ;; 410 2) $vg1 --leak-resolution=high $rgpc "$qarg" ;; 411 3) $vg1 --leak-resolution=high --show-reachable=yes $rgpc "$qarg";; 412 4) $vg $rgpc "$qarg" ;; 413 5) cat ;; 414 6) cat > /dev/null ;; 415 *) $rgpc "$qarg" 416 esac 417} 418 419runme() { 420 f="$1" 421 m=${all_models[$f]} 422 p=${f#*stp-} 423 p=${p/${version}./} 424 export PPD=$f 425 if (( dontrun > 0 )) ; then 426 output="${p%.ppd*} ($m)" 427 elif [[ -n $outdir || -n $md5dir ]] ; then 428 output="${p%.ppd*} ($m)...$( (runcmd 2>/dev/null | run_rastertogp | do_output) 2>&1 3>&2)" 429 else 430 output="${p%.ppd*} ($m)...$( (runcmd 2>/dev/null | run_rastertogp >/dev/null) 2>&1 3>&2)" 431 fi 432 return $? 433} 434 435runall() { 436 jobs="${1:-1}" 437 rotor="${2:-0}" 438 shift 2 439 retval=0 440 jobno=0 441 for f in "$@" ; do 442 if (( jobno == rotor )) ; then 443 runme "$f" || retval=1 444 echo "$output" 445 grep -q 'ERROR:' <<< "$output" && retval=1 446 fi 447 jobno=$(((jobno+1) % jobs)) 448 done 449 return $retval 450} 451 452get_models() { 453 re='\*StpDriverModelFamily: ' 454 if (( ${#all_models[*]} <= 1 )) ; then 455 declare -a models=($(xargs grep -m1 -H "^$re" <<< "$*" | sed "s/:$re/=/")) 456 for m in "${models[@]}" ; do 457 model=${m#*=} 458 file=${m%%=*} 459 all_models[$file]=$model 460 done 461 fi 462} 463 464retval=0 465if [[ -d ppd/C ]] ; then 466 declare -a files=($(get_ppds)) 467 declare -A models 468 declare -a nfiles 469 if (( skip_simplified > 0 )) ; then 470 for f in "${files[@]}" ; do 471 [[ $f != *.sim.ppd* ]] && nfiles+=("$f") 472 done 473 files=("${nfiles[@]}") 474 fi 475 get_models "${files[@]}" 476 if [[ -n $family ]] ; then 477 nfiles=() 478 for f in "${files[@]}" ; do 479 [[ ${all_models[$f]} =~ $family ]] && nfiles+=("$f") 480 done 481 files=("${nfiles[@]}") 482 fi 483 if (( single != 0 )) ; then 484 declare -A seen_models 485 nfiles=() 486 get_models "${files[@]}" 487 for f in "${files[@]}" ; do 488 model=${all_models[$f]} 489 [[ $f == *.sim.ppd ]] && model="${model}_sim" 490 if [[ -z ${seen_models[$model]} ]] ; then 491 nfiles+=("$f") 492 seen_models[$model]=1 493 fi 494 done 495 files=("${nfiles[@]}") 496 fi 497 (( ${#files[@]} < jobs )) && jobs=${#files[@]} 498 # Fire 'em off in the background... 499 for i in $(seq 0 $((jobs-1))) ; do 500 runall "$jobs" "$i" "${files[@]}" & 501 done 502 # And wait for them to complete. 503 for i in $(seq 0 $((jobs-1))) ; do 504 wait -n || retval=1 505 done 506fi 507 508[[ -n $tfile ]] && rm -f "$tfile" 509(( retval == 0 && make_ppds > 0 )) && rm -rf ppd/C && rmdir ppd 510exit "$retval" 511