#! /bin/sh
# pstoany --- convert PostScript images to gif, jpeg, etc.

# Copyright (C) 1994, 1995, 1998 Noah S. Friedman

# Author: Noah Friedman <friedman@splode.com>
# Created: 1994-10-17

# $Id: pstoany,v 1.8 1998/05/19 15:58:18 friedman Exp $

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, you can either send email to this
# program's maintainer or write to: The Free Software Foundation,
# Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA.

# Commentary:

# This program uses Ghostscript to convert postscript files to gif, jpeg,
# png, etc.  Because of patent problems, modern versions of Ghostscript do
# not have support for writing gif-format files directly.  If support is
# missing, ghostscript is used to convert the image to ppm format and the
# external `ppmtogif' program is used to finish the conversion.  The
# ppmtogif program can be found in the `pbmplus' or `netpbm' packages.

# Code:

# This string is expanded by RCS automatically when this file is checked out.
rcs_revision='$Revision: 1.8 $'

# Name by which this script was invoked.
progname=`echo "$0" | sed -e 's/[^\/]*\///g'`

# To prevent hairy quoting and escaping later.
bq='`'
eq="'"

# normal font resolution in dots per inch
defres=72
xres=
yres=

# Magnification step
xmag=1
ymag=1

# Image size
xsize=
ysize=

ppmtogifp=
format=gif8
aliasbits=0

GS="${GS-gs}"

revision=`set fnord $rcs_revision; echo $3`

usage="Usage: $progname {options} file1.ps {file2.ps {...}}
(Version $revision)

Diagnostic options:
-D, --debug                  Turn on shell debugging ($bq${bq}set -x$eq$eq).
                             Implies --verbose.
-h, --help                   You're looking at it.
-q, --quiet                  Do not report progress during conversion.
-v, --verbose                Report progress.  This is the default.
-V, --version                Show version number of this program & exit.

File options:
-F, --force                  Overwrite existing .gif or other intermediary
                             format files without notification.  By
                             default, this program refuses to overwrite any
                             existing files.
-f, --format          FMT    Specify FMT as the ${bq}device$eq (i.e. output
                             format) for Ghostscript.  Default is $bq$format$eq.
-o, --output-file     FILE   Use FILE as the output file name.
                             This is only appropriate when used with a
                             single input file to convert.

Image scaling and dithering options:
-a, --antialias       BITS   Use BITS bits of alpha antialiasing.
                             Valid values are 1 (no aliasing), 2, or 4.
                             Argument is optional and defaults to 4.
                             See ${bq}language.txt$eq in ghostscript sources.

-m, --magnify         STEP   Magnify x and y sizes by a factor of STEP.
                             Default is xmag=$xmag, ymag=$ymag
-x, --x-magnification XSTEP  Like $bq--magnify$eq, but only change x factor.
-y, --y-magnification YSTEP  Like $bq--magnify$eq, but only change y factor.

-s, --size            WxH    Resize image to W by H pixels.
                             To preserve aspect ratio automatically, use
                             $bq--x-size$eq or $bq--y-size$eq to specify
                             the size of just one axis.
-X, --x-size          XSIZE  Resize image to XSIZE pixels horizontal.
                             Aspect ratio is preserved unless $bq--y-size$eq
                             is also specified.
-Y, --y-size          YSIZE  Resize image to YSIZE pixels veritcal.
                             Aspect ratio is preserved unless $bq--x-size$eq
                             is also specified.

-r, --resolution      RES    Input image resolution for both x and y
                             coordinates, in dots per inch.  You probably
                             never need to adjust this manually.
                             Default is xres=$defres, yres=$defres
    --x-resolution    XRES   Like $bq--resolution$eq, but only change x value.
    --y-resolution    YRES   Like $bq--resolution$eq, but only change y value.
"

# Usage: eval "$getopt"; value=$optarg
# or     optarg_optional=t; eval "$getopt"; value=$optarg
#
# This function automatically shifts the positional args as appropriate.
# The argument to an option is optional if the variable `optarg_optional'
# is non-empty.  Otherwise, the argument is required and getopt will cause
# the program to exit on an error.  optarg_optional is reset to be empty
# after every call to getopt.  The argument (if any) is stored in the
# variable `optarg'.
#
# Long option syntax is `--foo=bar' or `--foo bar'.
# For optional args, you must use the `--foo=bar' long option syntax
# if the argument starts with `-', otherwise the argument will be ignored
# and treated as the next option.
#
# Note: because of broken bourne shells, using --foo=bar syntax can
# actually screw the quoting of args that end with trailing newlines.
# Specifically, most shells strip trailing newlines from substituted
# output, regardless of quoting.
getopt='
  {
    optarg=
    case "$1" in
      --*=* ) optarg=`echo "$1" | sed -e "1s/^[^=]*=//"` ; shift ;;
      -* )
        case "${2+set}:$optarg_optional" in
          set: ) optarg="$2" ; shift ; shift ;;
          set:?* )
            case "$2" in
              -* ) shift ;;
              * )  optarg="$2"; shift; shift ;;
            esac
           ;;
          : )
            option="$1"
            case "$option" in
              --*=* ) option=`echo "$option" | sed -e "1s/=.*//;q"` ;;
            esac
            echo "$progname: option $bq$option$eq requires argument." 1>&2
            echo "$progname: use $bq--help$eq to list option syntax." 1>&2
            exit 1
           ;;
          * ) shift ;;
        esac
       ;;
    esac
    optarg_optional=
  }'

# Initialize variables.
# Don't use `unset' since old bourne shells don't have this command.
# Instead, assign them an empty value.
debug=
force=
verbose=t
gs_quiet=
outfile=

# Parse command line arguments.
# Make sure that all wildcarded options are long enough to be unambiguous.
# It's a good idea to document the full long option name in each case.
# Long options which take arguments will need a `*' appended to the
# canonical name to match the value appended after the `=' character.
while : ; do
  case $# in 0) break ;; esac
  case "$1" in
    -a | --antialias* | --a* )
      optarg_optional=t
      eval "$getopt"
      aliasbits="$optarg"
      case "$aliasbits" in
        '' ) aliasbits=4 ;;
      esac
     ;;
    -D | --debug | --d* )
      debug=t
      verbose=t
      gs_quiet=
      shift
     ;;
    -F | --force | --forc )
      force=t
      shift
     ;;
    -f | --format* | --form* )
      eval "$getopt"
      format="$optarg"
     ;;
    -h | --help | --h* )
      echo "$usage" 1>&2
      exit 0
     ;;
    -m | --magnification* | --magnify* | --magstep* | --m* )
      eval "$getopt"
      xmag="$optarg"
      ymag="$optarg"
     ;;
    -o | --output-file* | --o* )
      eval "$getopt"
      outfile="$optarg"
     ;;
    -q | --quiet | --q* )
      verbose=
      gs_quiet=-q
      shift
     ;;
    -r | --resolution* | --re* )
      eval "$getopt"
      xres="$optarg"
      yres="$optarg"
     ;;
    -s | --size* | --s* )
      eval "$getopt"
      case "$optarg" in
        *x* )
          xsize=`echo "$optarg" | sed 's/x.*//'`
          ysize=`echo "$optarg" | sed 's/.*x//'`
         ;;
        * ) xsize="$optarg" ysize="$optarg" ;;
      esac
     ;;
    -V | --version | --vers* )
      echo "$progname version $revision"
      exit 0
     ;;
    -v | --verbose | --verb* )
      verbose=t
      gs_quiet=
      shift
     ;;
    --x-resolution* | --x-r* )
      eval "$getopt"
      xres="$optarg"
     ;;
    --y-resolution* | --y-r* )
      eval "$getopt"
      yres="$optarg"
     ;;
    -X | --x-size* | --x-s* )
      eval "$getopt"
      xsize="$optarg"
     ;;
    -Y | --y-size* | --y-s* )
      eval "$getopt"
      ysize="$optarg"
     ;;
    -x | --x-magnification* | --x-magnify* | --x-magstep* | --x-m* )
      eval "$getopt"
      xmag="$optarg"
     ;;
    -y | --y-magnification* | --y-magnify* | --y-magstep* | --y-m* )
      eval "$getopt"
      ymag="$optarg"
     ;;
    -- )     # Stop option processing
      shift
      break
     ;;
    -? | --* )
      case "$1" in
        --*=* ) arg=`echo "$1" | sed -e 's/=.*//'` ;;
        * )     arg="$1" ;;
      esac
      exec 1>&2
      echo "$progname: unknown or ambiguous option $bq$arg$eq"
      echo "$progname: Use $bq--help$eq for a list of options."
      exit 1
     ;;
    -??* )
      # Split grouped single options into separate args and try again
      optarg="$1"
      shift
      set fnord `echo "x$optarg" | sed -e 's/^x-//;s/\(.\)/-\1 /g'` ${1+"$@"}
      shift
     ;;
    * )
      break
     ;;
  esac
done

case "$debug" in t ) set -x ;; esac
case "$verbose" in '' ) exec 1> /dev/null ;; esac

case "$#:$outfile" in
 [01]:* ) : ;;
 *:?* )
   echo "$progname: Can't specify output file name when multiple input files given." 1>&2
   exit 1
  ;;
esac

# Build a list of supported output formats.
gs_devices_raw=`$GS -h | sed -ne '
                  /^Available devices:/{
                    :l
                    n
                    /^[^ 	]/q
                    p
                    b l
                  }'`
gs_devices=" "`echo $gs_devices_raw`" "

# Determine whether requested output format is supported.
case "$gs_devices" in
  *" $format "* ) : ;;
  * )
    case "$format" in
      jpg ) format=jpeg ;;
      # If desired image format is gif, see if ppmraw or ppm (in that order)
      # is supported by gs, and use that instead.
      gif | gif8 | gifmono )
        case "$gs_devices" in
          *" ppmraw "* ) ppmtogifp=t; format=ppmraw ;;
          *" ppm "* )    ppmtogifp=t; format=ppm ;;
          * )
            echo "$progname: $GS does not have gif or ppm drivers installed." 1>&2
           ;;
        esac
       ;;
    esac

    # Try again to see if modified format name is supported as a gs device.
    case "$gs_devices" in
      *" $format "* ) : ;;
      * )
        exec 1>&2
        echo "$progname: $format: unsupported format."
        echo "Supported formats include:"
        echo "$gs_devices_raw"
        exit 1
       ;;
    esac
   ;;
esac

file_exists_hint_given_p=
for file in ${1+"$@"}; do

  case "$file" in
    - ) tmpfile="/tmp/$progname$$.ps"
        trap 'rm -f "$tmpfile"' 0 1 2 3 15
        cat > "$tmpfile"
        file="$tmpfile"
     ;;
  esac

  case "$outfile" in
    '' ) outputfile= ;;
    * )  outputfile="$outfile" ;;
  esac

  basename=`echo "$file" \
             | sed -e 's/\/*$//
                       s/.*\///
                       s/\..*//'`

  case "$force" in
    t ) : ;;
    * )
      set fnord
      case "$ppmtogifp" in
        t ) set ${1+"$@"} `ls -1d "$basename"*.gif "" 2> /dev/null` ;;
      esac
      set ${1+"$@"} `ls -1d "$basename"*".$format" "$outputfile" 2> /dev/null`
      shift
      case $# in
        0 ) : ;;
        * )
          echo "$progname: skipping conversion of $bq$file$eq" 1>&2

          case "$file_exists_hint_given_p" in
            t ) : ;;
            * )
              file_exists_hint_given_p=t
              sed -e "s/^/$progname: /" 1>&2 <<__EOF__
The following files in the current directory may get overwritten
as a result of running this program.  If desired, rename or remove
them, or use the $bq-f$eq ($bq--force$eq) option.
__EOF__
             ;;
          esac

          echo
          ls -lsd ${1+"$@"} 2> /dev/null
          echo
          continue
         ;;
      esac
     ;;
  esac

  echo "$progname: converting $file"

  # Get the bounding box coordinates.
  # If the first bounding box instructions say "(atend)", then search to
  # the end of the file for the last occurrence of a bounding box.
  set fnord `sed -ne '
               /^%%BoundingBox:/{
                 /(atend)/!{
                   p
                   q
                 }
                 b l
               }
               d
               :l
               /^%%BoundingBox:/h
               $b e
               n
               b l
               :e
               x
               p
               q
             ' "$file"`

  bcinit='scale=4
  '
  case $# in
    1 )
      echo "$progname: warning: no BoundingBox found in $bq$arg$eq" 1>&2
      # Guess.  Assume image area fits in a 8.5x11 sheet.
      llx=0
      lly=0
      urx=`echo "$bcinit ($defres * 8.5)" | bc`
      ury=`echo "$bcinit ($defres * 11)"  | bc`
      echo "$progname: warning: guessing %%BoundingBox $llx $lly $urx $ury" 1>&2
     ;;
    * )
      # Args hopefully now: fnord %%BoundingBox llx lly urx ury
      llx=$3; urx=$5
      lly=$4; ury=$6
     ;;
  esac

  case "$xsize:$ysize" in
    : )
      # Dividing by 1 when scale=0 insures bc result is an integer
      xsize=`echo "($urx - $llx) * $xmag / 1" | bc`
      ysize=`echo "($ury - $lly) * $ymag / 1" | bc`
     ;;
    [0-9]*: )
      xmag=`echo "$bcinit $xsize / ($urx - $llx)" | bc`
      ymag=$xmag
      ysize=`echo "($ury - $lly) * $ymag / 1" | bc`
     ;;
    :[0-9]* )
      ymag=`echo "$bcinit $ysize / ($ury - $lly)" | bc`
      xmag=$ymag
      xsize=`echo "($urx - $llx) * $ymag / 1" | bc`
     ;;
    ?*:?* )
      xmag=`echo "$bcinit $xsize / ($urx - $llx)" | bc`
      ymag=`echo "$bcinit $ysize / ($ury - $lly)" | bc`
     ;;
  esac

  case "$xres" in
    '' ) xres=`echo "$bcinit ($defres * $xmag)" | bc` ;;
  esac

  case "$yres" in
    '' ) yres=`echo "$bcinit ($defres * $ymag)" | bc` ;;
  esac

  case "$aliasbits" in
    0 ) alias= ;;
    * ) alias="-dGraphicsAlphaBits=$aliasbits -dTextAlphaBits=$aliasbits" ;;
  esac

  set fnord "$GS" -sDEVICE=$format \
                  -sOutputFile="$basename%03d.$format" \
                  -g${xsize}x${ysize} \
                  -r${xres}x${yres} \
                  $alias $gs_quiet - "$file"
  shift

  echo $progname: ${1+"$@"}
  echo "$llx neg $lly neg translate" | ${1+"$@"}
  exitstat=$?
  case $exitstat in
    0 ) : ;;
    * ) exit $exitstat ;;
  esac

  case "$ppmtogifp" in
    t )
      for f in `ls -1d "$basename"*".$format" 2>/dev/null` ; do
        g=`echo "$f" | sed -e "s/\.$format\$/.gif/"`
        echo "$progname: running ppmtogif on $bq$f$eq"
        ppmtogif "$f" > "$g" \
          && rm -f "$f" \
          && echo "$progname: created $g"
      done
      format=gif
     ;;
  esac

  set fnord `ls -1d "$basename"[0-9]*".$format" 2> /dev/null`
  shift

  case $# in
    1 )
      case "$outputfile" in
        '' ) newname=`echo "$1" | sed -e "s/001\.$format\$/.$format/"` ;;
        *  ) newname=$outputfile ;;
      esac
      echo $progname: mv "$1" "$newname"
      mv "$1" "$newname"
     ;;
  esac
done

# pstoany ends here
