/* libppm5.c - ppm utility library part 5
**
** This library module contains the ppmdraw routines.
**
** Copyright (C) 1989, 1991 by Jef Poskanzer.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation.  This software is provided "as is" without express or
** implied warranty.
** 
** The character drawing routines are by John Walker
** Copyright (C) 1994 by John Walker, kelvin@fourmilab.ch
*/

#include "ppm.h"
#include "ppmdraw.h"
#include "mallocvar.h"

#define DDA_SCALE 8192

void
ppmd_point_drawproc(pixel**     const pixels, 
                    int         const cols, 
                    int         const rows, 
                    pixval      const maxval, 
                    int         const x, 
                    int         const y, 
                    const void* const clientdata )
    {
    if ( x >= 0 && x < cols && y >= 0 && y < rows )
	pixels[y][x] = *( (pixel*) clientdata );
    }


/* Simple fill routine. */

void
ppmd_filledrectangle(pixel** const pixels, 
                     int const cols, 
                     int const rows, 
                     pixval const maxval, 
                     int const x, 
                     int const y, 
                     int const width, 
                     int const height, 
                     void (*drawprocP)(pixel**, int, int, pixval, 
                                       int, int, const void*), 
                     const void* const clientdata )
{
    register int cx, cy, cwidth, cheight, col, row;

    /* Clip. */
    cx = x;
    cy = y;
    cwidth = width;
    cheight = height;
    if ( cx < 0 )
	{
	cx = 0;
	cwidth += x;
	}
    if ( cy < 0 )
	{
	cy = 0;
	cheight += y;
	}
    if ( cx + cwidth > cols )
	cwidth = cols - cx;
    if ( cy + cheight > rows )
	cheight = rows - cy;

    /* Draw. */
    for ( row = cy; row < cy + cheight; ++row )
	for ( col = cx; col < cx + cwidth; ++col )
	    if ( drawprocP == PPMD_NULLDRAWPROC )
		pixels[row][col] = *( (pixel*) clientdata );
	    else
		(*drawprocP)(
		    pixels, cols, rows, maxval, col, row, clientdata );
    }


/* Outline drawing stuff. */

static int ppmd_linetype = PPMD_LINETYPE_NORMAL;

int
ppmd_setlinetype( type )
    int type;
    {
    int old;

    old = ppmd_linetype;
    ppmd_linetype = type;
    return old;
    }

static int ppmd_lineclip = 1;

int
ppmd_setlineclip( clip )
    int clip;
    {
    int old;

    old = ppmd_lineclip;
    ppmd_lineclip = clip;
    return old;
    }

void
ppmd_line(pixel**      const pixels, 
          int          const cols, 
          int          const rows, 
          pixval       const maxval, 
          int          const x0, 
          int          const y0, 
          int          const x1, 
          int          const y1, 
          void (*drawprocP)(pixel**, int, int, pixval, int, int, const void*),
          const void * const clientdata)
{
    register int cx0, cy0, cx1, cy1;

    /* Special case zero-length lines. */
    if ( x0 == x1 && y0 == y1 )
	{
	if ( ( ! ppmd_lineclip ) ||
	     ( x0 >= 0 && x0 < cols && y0 >= 0 && y0 < rows ) ) {
	    if ( drawprocP == PPMD_NULLDRAWPROC )
            ppmd_point_drawproc(pixels, cols, rows, maxval, x0, y0, 
                                clientdata );
	    else
            (*drawprocP)( pixels, cols, rows, maxval, x0, y0, clientdata );
    }
	return;
	}

    /* Clip. */
    cx0 = x0;
    cy0 = y0;
    cx1 = x1;
    cy1 = y1;
    if ( ppmd_lineclip )
	{
	if ( cx0 < 0 )
	    {
	    if ( cx1 < 0 ) return;
	    cy0 = cy0 + ( cy1 - cy0 ) * ( -cx0 ) / ( cx1 - cx0 );
	    cx0 = 0;
	    }
	else if ( cx0 >= cols )
	    {
	    if ( cx1 >= cols ) return;
	    cy0 = cy0 + ( cy1 - cy0 ) * ( cols - 1 - cx0 ) / ( cx1 - cx0 );
	    cx0 = cols - 1;
	    }
	if ( cy0 < 0 )
	    {
	    if ( cy1 < 0 ) return;
	    cx0 = cx0 + ( cx1 - cx0 ) * ( -cy0 ) / ( cy1 - cy0 );
	    cy0 = 0;
	    }
	else if ( cy0 >= rows )
	    {
	    if ( cy1 >= rows ) return;
	    cx0 = cx0 + ( cx1 - cx0 ) * ( rows - 1 - cy0 ) / ( cy1 - cy0 );
	    cy0 = rows - 1;
	    }
	if ( cx1 < 0 )
	    {
	    cy1 = cy1 + ( cy0 - cy1 ) * ( -cx1 ) / ( cx0 - cx1 );
	    cx1 = 0;
	    }
	else if ( cx1 >= cols )
	    {
	    cy1 = cy1 + ( cy0 - cy1 ) * ( cols - 1 - cx1 ) / ( cx0 - cx1 );
	    cx1 = cols - 1;
	    }
	if ( cy1 < 0 )
	    {
	    cx1 = cx1 + ( cx0 - cx1 ) * ( -cy1 ) / ( cy0 - cy1 );
	    cy1 = 0;
	    }
	else if ( cy1 >= rows )
	    {
	    cx1 = cx1 + ( cx0 - cx1 ) * ( rows - 1 - cy1 ) / ( cy0 - cy1 );
	    cy1 = rows - 1;
	    }

	/* Check again for zero-length lines. */
	if ( cx0 == cx1 && cy0 == cy1 )
	    {
	    if ( drawprocP == PPMD_NULLDRAWPROC )
		ppmd_point_drawproc(
		    pixels, cols, rows, maxval, cx0, cy0, clientdata );
	    else
		(*drawprocP)(
		    pixels, cols, rows, maxval, cx0, cy0, clientdata );
	    return;
	    }
	}

    /* Draw, using a simple DDA. */
    if ( abs( cx1 - cx0 ) > abs( cy1 - cy0 ) )
	{ /* Loop over X domain. */
	register long dy, srow;
	register int dx, col, row, prevrow;

	if ( cx1 > cx0 )
	    dx = 1;
	else
	    dx = -1;
	dy = ( cy1 - cy0 ) * DDA_SCALE / abs( cx1 - cx0 );
	prevrow = row = cy0;
	srow = row * DDA_SCALE + DDA_SCALE / 2;
	col = cx0;
	for ( ; ; )
	    {
	    if ( ppmd_linetype == PPMD_LINETYPE_NODIAGS && row != prevrow )
		{
		if ( drawprocP == PPMD_NULLDRAWPROC )
		    pixels[prevrow][col] = *( (pixel*) clientdata );
		else
		    (*drawprocP)(
		        pixels, cols, rows, maxval, col, prevrow, clientdata );
		prevrow = row;
		}
	    if ( drawprocP == PPMD_NULLDRAWPROC )
		pixels[row][col] = *( (pixel*) clientdata );
	    else
		(*drawprocP)(
		    pixels, cols, rows, maxval, col, row, clientdata );
	    if ( col == cx1 )
		break;
	    srow += dy;
	    row = srow / DDA_SCALE;
	    col += dx;
	    }
	}
    else
	{ /* Loop over Y domain. */
	register long dx, scol;
	register int dy, col, row, prevcol;

	if ( cy1 > cy0 )
	    dy = 1;
	else
	    dy = -1;
	dx = ( cx1 - cx0 ) * DDA_SCALE / abs( cy1 - cy0 );
	row = cy0;
	prevcol = col = cx0;
	scol = col * DDA_SCALE + DDA_SCALE / 2;
	for ( ; ; )
	    {
	    if ( ppmd_linetype == PPMD_LINETYPE_NODIAGS && col != prevcol )
		{
		if ( drawprocP == PPMD_NULLDRAWPROC )
		    pixels[row][prevcol] = *( (pixel*) clientdata );
		else
		    (*drawprocP)(
			pixels, cols, rows, maxval, prevcol, row, clientdata );
		prevcol = col;
		}
	    if ( drawprocP == PPMD_NULLDRAWPROC )
		pixels[row][col] = *( (pixel*) clientdata );
	    else
		(*drawprocP)(
		    pixels, cols, rows, maxval, col, row, clientdata );
	    if ( row == cy1 )
		break;
	    row += dy;
	    scol += dx;
	    col = scol / DDA_SCALE;
	    }
	}
    }

#define SPLINE_THRESH 3
void
ppmd_spline3(pixel** const pixels, 
             int     const cols, 
             int const rows, 
             pixval const maxval, 
             int const x0, 
             int const y0, 
             int const x1, 
             int const y1, 
             int const x2, 
             int const y2, 
             void (*drawprocP)(pixel**, int, int, pixval, 
                               int, int, const void*), 
             const void* const clientdata )
{
    register int xa, ya, xb, yb, xc, yc, xp, yp;

    xa = ( x0 + x1 ) / 2;
    ya = ( y0 + y1 ) / 2;
    xc = ( x1 + x2 ) / 2;
    yc = ( y1 + y2 ) / 2;
    xb = ( xa + xc ) / 2;
    yb = ( ya + yc ) / 2;

    xp = ( x0 + xb ) / 2;
    yp = ( y0 + yb ) / 2;
    if ( abs( xa - xp ) + abs( ya - yp ) > SPLINE_THRESH )
	ppmd_spline3(
	    pixels, cols, rows, maxval, x0, y0, xa, ya, xb, yb, drawprocP,
	    clientdata );
    else
	ppmd_line(
	    pixels, cols, rows, maxval, x0, y0, xb, yb, drawprocP, clientdata );

    xp = ( x2 + xb ) / 2;
    yp = ( y2 + yb ) / 2;
    if ( abs( xc - xp ) + abs( yc - yp ) > SPLINE_THRESH )
	ppmd_spline3(
	    pixels, cols, rows, maxval, xb, yb, xc, yc, x2, y2, drawprocP,
	    clientdata );
    else
	ppmd_line(
	    pixels, cols, rows, maxval, xb, yb, x2, y2, drawprocP, clientdata );
    }

void
ppmd_polyspline(pixel** const pixels, 
                int const cols, 
                int const rows, 
                pixval const maxval, 
                int const x0, 
                int const y0, 
                int const nc, 
                int* const xc, 
                int* const yc, 
                int const x1, 
                int const y1, 
                void (*drawprocP)(pixel**, int, int, pixval, 
                                  int, int, const void*), 
                const void* const clientdata )
    {
    register int i, x, y, xn, yn;

    x = x0;
    y = y0;
    for ( i = 0; i < nc - 1; ++i )
	{
	xn = ( xc[i] + xc[i + 1] ) / 2;
	yn = ( yc[i] + yc[i + 1] ) / 2;
	ppmd_spline3(
	    pixels, cols, rows, maxval, x, y, xc[i], yc[i], xn, yn, drawprocP,
	    clientdata );
	x = xn;
	y = yn;
	}
    ppmd_spline3(
	pixels, cols, rows, maxval, x, y, xc[nc - 1], yc[nc - 1], x1, y1,
	drawprocP, clientdata );
    }

void
ppmd_circle(pixel** const pixels, 
            int const cols, 
            int const rows, 
            pixval const maxval, 
            int const cx, 
            int const cy, 
            int const radius, 
            void (*drawprocP)(pixel**, int, int, pixval, 
                              int, int, const void*), 
            const void* const clientdata)
{
    register int x0, y0, x, y, prevx, prevy, nopointsyet;
    register long sx, sy, e;

    x0 = x = radius;
    y0 = y = 0;
    sx = x * DDA_SCALE + DDA_SCALE / 2;
    sy = y * DDA_SCALE + DDA_SCALE / 2;
    e = DDA_SCALE / radius;
    if ( drawprocP == PPMD_NULLDRAWPROC )
	pixels[y + cy][x + cx] = *( (pixel*) clientdata );
    else
	(*drawprocP)( pixels, cols, rows, maxval, x + cx, y + cy, clientdata );
    nopointsyet = 1;
    do
	{
	prevx = x;
	prevy = y;
	sx += e * sy / DDA_SCALE;
	sy -= e * sx / DDA_SCALE;
	x = sx / DDA_SCALE;
	y = sy / DDA_SCALE;
	if ( x != prevx || y != prevy )
	    {
	    nopointsyet = 0;
	    if ( drawprocP == PPMD_NULLDRAWPROC )
		pixels[y + cy][x + cx] = *( (pixel*) clientdata );
	    else
		(*drawprocP)(
		    pixels, cols, rows, maxval, x + cx, y + cy, clientdata );
	    }
	}
    while ( nopointsyet || x != x0 || y != y0 );
    }


/* Arbitrary fill stuff. */

typedef struct
    {
    short x;
    short y;
    short edge;
    } coord;
typedef struct
    {
    int n;
    int size;
    int curedge;
    int segstart;
    int ydir;
    int startydir;
    coord* coords;
    } fillobj;

#define SOME 1000

static int oldclip;

char*
ppmd_fill_init( ) {
    fillobj* fh;

    MALLOCVAR(fh);
    if (fh == NULL)
        pm_error("out of memory allocating a fillhandle");
    fh->n = 0;
    fh->size = SOME;
    MALLOCARRAY(fh->coords, fh->size);
    if (fh->coords == NULL)
        pm_error("out of memory allocating a fillhandle");
    fh->curedge = 0;
    
    /* Turn off line clipping. */
    oldclip = ppmd_setlineclip(0);
    
    return (char*) fh;
}



void
ppmd_fill_drawproc(pixel** const pixels, 
                   int const cols, 
                   int const rows, 
                   pixval const maxval, 
                   int const x, 
                   int const y, 
                   const void* const clientdata)
{
    register fillobj* fh;
    register coord* cp;
    register coord* ocp;

    fh = (fillobj*) clientdata;

    if ( fh->n > 0 )
	{
	/* If these are the same coords we saved last time, don't bother. */
	ocp = &(fh->coords[fh->n - 1]);
	if ( x == ocp->x && y == ocp->y )
	    return;
	}

    /* Ok, these are new; check if there's room for two more coords. */
    if ( fh->n + 1 >= fh->size )
	{
	fh->size += SOME;
	fh->coords = (coord*) realloc(
	    (char*) fh->coords, fh->size * sizeof(coord) );
	if ( fh->coords == 0 )
	    pm_error( "out of memory enlarging a fillhandle" );
	}

    /* Check for extremum and set the edge number. */
    if ( fh->n == 0 )
	{ /* Start first segment. */
	fh->segstart = fh->n;
	fh->ydir = 0;
	fh->startydir = 0;
	}
    else
	{
	register int dx, dy;

	dx = x - ocp->x;
	dy = y - ocp->y;
	if ( dx < -1 || dx > 1 || dy < -1 || dy > 1 )
	    { /* Segment break.  Close off old one. */
	    if ( fh->startydir != 0 && fh->ydir != 0 )
		if ( fh->startydir == fh->ydir )
		    { /* Oops, first edge and last edge are the same.
		      ** Renumber the first edge in the old segment. */
		    register coord* fcp;
		    int oldedge;

		    fcp = &(fh->coords[fh->segstart]);
		    oldedge = fcp->edge;
		    for ( ; fcp->edge == oldedge; ++fcp )
			fcp->edge = ocp->edge;
		    }
	    /* And start new segment. */
	    ++(fh->curedge);
	    fh->segstart = fh->n;
	    fh->ydir = 0;
	    fh->startydir = 0;
	    }
	else
	    { /* Segment continues. */
	    if ( dy != 0 )
		{
		if ( fh->ydir != 0 && fh->ydir != dy )
		    { /* Direction changed.  Insert a fake coord, old
		      ** position but new edge number. */
		    ++(fh->curedge);
		    cp = &(fh->coords[fh->n]);
		    cp->x = ocp->x;
		    cp->y = ocp->y;
		    cp->edge = fh->curedge;
		    ++(fh->n);
		    }
		fh->ydir = dy;
		if ( fh->startydir == 0 )
		    fh->startydir = dy;
		}
	    }
	}

    /* Save this coord. */
    cp = &(fh->coords[fh->n]);
    cp->x = x;
    cp->y = y;
    cp->edge = fh->curedge;
    ++(fh->n);
    }

static int yx_compare ARGS((const void* c1, const void* c2));
static int
yx_compare( const void *c1, const void *c2 )
    {
    if ( ((coord*)c1)->y > ((coord*)c2)->y )
	return 1;
    if ( ((coord*)c1)->y < ((coord*)c2)->y )
	return -1;
    if ( ((coord*)c1)->x > ((coord*)c2)->x )
	return 1;
    if ( ((coord*)c1)->x < ((coord*)c2)->x )
	return -1;
    return 0;
    }

void
ppmd_fill(pixel** const pixels, 
          int const cols, 
          int const rows, 
          pixval const maxval, 
          char* const fillhandle, 
          void (*drawprocP)(pixel**, int, int, pixval, 
                            int, int, const void*), 
          const void* const clientdata)
    {
    register fillobj* fh;
    int pedge, eq;
    register int i, leftside, edge, lx, rx, py;
    register coord* cp;

    fh = (fillobj*) fillhandle;

    /* Close off final segment. */
    if ( fh->n > 0 && fh->startydir != 0 && fh->ydir != 0 )
	if ( fh->startydir == fh->ydir )
	    { /* Oops, first edge and last edge are the same. */
	    register coord* fcp;
	    int lastedge, oldedge;

	    lastedge = fh->coords[fh->n - 1].edge;
	    fcp = &(fh->coords[fh->segstart]);
	    oldedge = fcp->edge;
	    for ( ; fcp->edge == oldedge; ++fcp )
		fcp->edge = lastedge;
	    }

    /* Restore clipping now. */
    (void) ppmd_setlineclip( oldclip );

    /* Sort the coords by Y, secondarily by X. */
    qsort( (char*) fh->coords, fh->n, sizeof(coord), yx_compare );

    /* Find equal coords with different edge numbers, and swap if necessary. */
    edge = -1;
    for ( i = 0; i < fh->n; ++i )
	{
	cp = &(fh->coords[i]);
	if ( i > 1 && eq && cp->edge != edge && cp->edge == pedge )
	    { /* Swap .-1 and .-2. */
	    coord t;

	    t = fh->coords[i-1];
	    fh->coords[i-1] = fh->coords[i-2];
	    fh->coords[i-2] = t;
	    }
	if ( i > 0 )
	    {
	    if ( cp->x == lx && cp->y == py )
		{
		eq = 1;
		if ( cp->edge != edge && cp->edge == pedge )
		    { /* Swap . and .-1. */
		    coord t;

		    t = *cp;
		    *cp = fh->coords[i-1];
		    fh->coords[i-1] = t;
		    }
		}
	    else
		eq = 0;
	    }
	lx = cp->x;
	py = cp->y;
	pedge = edge;
	edge = cp->edge;
	}

    /* Ok, now run through the coords filling spans. */
    for ( i = 0; i < fh->n; ++i )
	{
	cp = &(fh->coords[i]);
	if ( i == 0 )
	    {
	    lx = rx = cp->x;
	    py = cp->y;
	    edge = cp->edge;
	    leftside = 1;
	    }
	else
	    {
	    if ( cp->y != py )
		{ /* Row changed.  Emit old span and start a new one. */
		ppmd_filledrectangle(
		    pixels, cols, rows, maxval, lx, py, rx - lx + 1, 1,
		    drawprocP, clientdata);
		lx = rx = cp->x;
		py = cp->y;
		edge = cp->edge;
		leftside = 1;
		}
	    else
		{
		if ( cp->edge == edge )
		    { /* Continuation of side. */
		    rx = cp->x;
		    }
		else
		    { /* Edge changed.  Is it a span? */
		    if ( leftside )
			{
			rx = cp->x;
			leftside = 0;
			}
		    else
			{ /* Got a span to fill. */
			ppmd_filledrectangle(
			    pixels, cols, rows, maxval, lx, py, rx - lx + 1,
			    1, drawprocP, clientdata);
			lx = rx = cp->x;
			leftside = 1;
			}
		    edge = cp->edge;
		    }
		}
	    }
	}

    /* All done.  Free up the fillhandle and leave. */
    free( fh->coords );
    free( fh );
    }



/*		  Stroke character definitions

   The	following  character  definitions are derived from the (public
   domain) Hershey plotter  font  database,  using  the  single-stroke
   Roman font.

   Each  character  definition	begins	with 3 bytes which specify the
   number of X, Y plot pairs which follow, the negative  of  the  skip
   before  starting  to  draw  the  characters, and the skip after the
   character.  The first plot pair moves the pen to that location  and
   subsequent  pairs  draw  to	the  location given.  A pair of 192, 0
   raises the pen, moves to the location given by the following  pair,
   and resumes drawing with the pair after that.

   The  values  in  the  definition  tables are 8-bit two's complement
   signed numbers.  We  declare  the  table  as  "unsigned  char"  and
   manually  sign-extend  the  values because C compilers differ as to
   whether the type "char" is signed or unsigned, and  some  compilers
   don't  accept the qualifier "signed" which we would like to use for
   these items.  We specify negative numbers as their  unsigned  two's
   complements  to  avoid  complaints  from compilers which don't like
   initialising unsigned data with signed values.  Ahhh,  portability.
*/

static unsigned char
char32[] = { 0, 0, 21 },
char33[] = { 8, 251, 5, 0, 244, 0, 2, 192, 0, 0, 7, 255, 8, 0, 9, 1, 8, 0, 7 },
char34[] = { 17, 253, 15, 2, 244, 1, 245, 0, 244, 1, 243, 2, 244, 2, 246, 1,
	     248, 0, 249, 192, 0, 10, 244, 9, 245, 8, 244, 9, 243, 10, 244,
	     10, 246, 9, 248, 8, 249, },
char35[] = { 11, 246, 11, 1, 240, 250, 16, 192, 0, 7, 240, 0, 16, 192, 0, 250,
	     253, 8, 253, 192, 0, 249, 3, 7, 3 },
char36[] = { 26, 246, 10, 254, 240, 254, 13, 192, 0, 2, 240, 2, 13, 192, 0, 7,
	     247, 5, 245, 2, 244, 254, 244, 251, 245, 249, 247, 249, 249, 250,
	     251, 251, 252, 253, 253, 3, 255, 5, 0, 6, 1, 7, 3, 7, 6, 5, 8, 2,
	     9, 254, 9, 251, 8, 249, 6 },
char37[] = { 31, 244, 12, 9, 244, 247, 9, 192, 0, 252, 244, 254, 246, 254,
	     248, 253, 250, 251, 251, 249, 251, 247, 249, 247, 247, 248, 245,
	     250, 244, 252, 244, 254, 245, 1, 246, 4, 246, 7, 245, 9, 244,
	     192, 0, 5, 2, 3, 3, 2, 5, 2, 7, 4, 9, 6, 9, 8, 8, 9, 6, 9, 4, 7,
	     2, 5, 2 },
char38[] = { 34, 243, 13, 10, 253, 10, 252, 9, 251, 8, 251, 7, 252, 6, 254, 4,
	     3, 2, 6, 0, 8, 254, 9, 250, 9, 248, 8, 247, 7, 246, 5, 246, 3,
	     247, 1, 248, 0, 255, 252, 0, 251, 1, 249, 1, 247, 0, 245, 254,
	     244, 252, 245, 251, 247, 251, 249, 252, 252, 254, 255, 3, 6, 5,
	     8, 7, 9, 9, 9, 10, 8, 10, 7 },
char39[] = { 7, 251, 5, 0, 246, 255, 245, 0, 244, 1, 245, 1, 247, 0, 249, 255,
	     250 },
char40[] = { 10, 249, 7, 4, 240, 2, 242, 0, 245, 254, 249, 253, 254, 253, 2,
	     254, 7, 0, 11, 2, 14, 4, 16 },
char41[] = { 10, 249, 7, 252, 240, 254, 242, 0, 245, 2, 249, 3, 254, 3, 2, 2,
	     7, 0, 11, 254, 14, 252, 16 },
char42[] = { 8, 248, 8, 0, 250, 0, 6, 192, 0, 251, 253, 5, 3, 192, 0, 5, 253,
	     251, 3 },
char43[] = { 5, 243, 13, 0, 247, 0, 9, 192, 0, 247, 0, 9, 0 },
char44[] = { 8, 251, 5, 1, 8, 0, 9, 255, 8, 0, 7, 1, 8, 1, 10, 0, 12, 255, 13
	   },
char45[] = { 2, 243, 13, 247, 0, 9, 0 },
char46[] = { 5, 251, 5, 0, 7, 255, 8, 0, 9, 1, 8, 0, 7 },
char47[] = { 2, 245, 11, 9, 240, 247, 16 },
char48[] = { 17, 246, 10, 255, 244, 252, 245, 250, 248, 249, 253, 249, 0, 250,
	     5, 252, 8, 255, 9, 1, 9, 4, 8, 6, 5, 7, 0, 7, 253, 6, 248, 4,
	     245, 1, 244, 255, 244 },
char49[] = { 4, 246, 10, 252, 248, 254, 247, 1, 244, 1, 9 },
char50[] = { 14, 246, 10, 250, 249, 250, 248, 251, 246, 252, 245, 254, 244, 2,
	     244, 4, 245, 5, 246, 6, 248, 6, 250, 5, 252, 3, 255, 249, 9, 7, 9
	   },
char51[] = { 15, 246, 10, 251, 244, 6, 244, 0, 252, 3, 252, 5, 253, 6, 254, 7,
	     1, 7, 3, 6, 6, 4, 8, 1, 9, 254, 9, 251, 8, 250, 7, 249, 5 },
char52[] = { 6, 246, 10, 3, 244, 249, 2, 8, 2, 192, 0, 3, 244, 3, 9 },
char53[] = { 17, 246, 10, 5, 244, 251, 244, 250, 253, 251, 252, 254, 251, 1,
	     251, 4, 252, 6, 254, 7, 1, 7, 3, 6, 6, 4, 8, 1, 9, 254, 9, 251,
	     8, 250, 7, 249, 5 },
char54[] = { 23, 246, 10, 6, 247, 5, 245, 2, 244, 0, 244, 253, 245, 251, 248,
	     250, 253, 250, 2, 251, 6, 253, 8, 0, 9, 1, 9, 4, 8, 6, 6, 7, 3,
	     7, 2, 6, 255, 4, 253, 1, 252, 0, 252, 253, 253, 251, 255, 250, 2
	     },
char55[] = { 5, 246, 10, 7, 244, 253, 9, 192, 0, 249, 244, 7, 244 },
char56[] = { 29, 246, 10, 254, 244, 251, 245, 250, 247, 250, 249, 251, 251,
	     253, 252, 1, 253, 4, 254, 6, 0, 7, 2, 7, 5, 6, 7, 5, 8, 2, 9,
	     254, 9, 251, 8, 250, 7, 249, 5, 249, 2, 250, 0, 252, 254, 255,
	     253, 3, 252, 5, 251, 6, 249, 6, 247, 5, 245, 2, 244, 254, 244 },
char57[] = { 23, 246, 10, 6, 251, 5, 254, 3, 0, 0, 1, 255, 1, 252, 0, 250,
	     254, 249, 251, 249, 250, 250, 247, 252, 245, 255, 244, 0, 244, 3,
	     245, 5, 247, 6, 251, 6, 0, 5, 5, 3, 8, 0, 9, 254, 9, 251, 8, 250,
	     6 },
char58[] = { 11, 251, 5, 0, 251, 255, 252, 0, 253, 1, 252, 0, 251, 192, 0, 0,
	     7, 255, 8, 0, 9, 1, 8, 0, 7 },
char59[] = { 14, 251, 5, 0, 251, 255, 252, 0, 253, 1, 252, 0, 251, 192, 0, 1,
	     8, 0, 9, 255, 8, 0, 7, 1, 8, 1, 10, 0, 12, 255, 13 },
char60[] = { 3, 244, 12, 8, 247, 248, 0, 8, 9 },
char61[] = { 5, 243, 13, 247, 253, 9, 253, 192, 0, 247, 3, 9, 3 },
char62[] = { 3, 244, 12, 248, 247, 8, 0, 248, 9 },
char63[] = { 20, 247, 9, 250, 249, 250, 248, 251, 246, 252, 245, 254, 244, 2,
	     244, 4, 245, 5, 246, 6, 248, 6, 250, 5, 252, 4, 253, 0, 255, 0,
	     2, 192, 0, 0, 7, 255, 8, 0, 9, 1, 8, 0, 7 },
char64[] = { 55, 243, 14, 5, 252, 4, 250, 2, 249, 255, 249, 253, 250, 252,
	     251, 251, 254, 251, 1, 252, 3, 254, 4, 1, 4, 3, 3, 4, 1, 192, 0,
	     255, 249, 253, 251, 252, 254, 252, 1, 253, 3, 254, 4, 192, 0, 5,
	     249, 4, 1, 4, 3, 6, 4, 8, 4, 10, 2, 11, 255, 11, 253, 10, 250, 9,
	     248, 7, 246, 5, 245, 2, 244, 255, 244, 252, 245, 250, 246, 248,
	     248, 247, 250, 246, 253, 246, 0, 247, 3, 248, 5, 250, 7, 252, 8,
	     255, 9, 2, 9, 5, 8, 7, 7, 8, 6, 192, 0, 6, 249, 5, 1, 5, 3, 6, 4
	     },
char65[] = { 8, 247, 9, 0, 244, 248, 9, 192, 0, 0, 244, 8, 9, 192, 0, 251, 2,
	     5, 2 },
char66[] = { 23, 245, 10, 249, 244, 249, 9, 192, 0, 249, 244, 2, 244, 5, 245,
	     6, 246, 7, 248, 7, 250, 6, 252, 5, 253, 2, 254, 192, 0, 249, 254,
	     2, 254, 5, 255, 6, 0, 7, 2, 7, 5, 6, 7, 5, 8, 2, 9, 249, 9 },
char67[] = { 18, 246, 11, 8, 249, 7, 247, 5, 245, 3, 244, 255, 244, 253, 245,
	     251, 247, 250, 249, 249, 252, 249, 1, 250, 4, 251, 6, 253, 8,
	     255, 9, 3, 9, 5, 8, 7, 6, 8, 4 },
char68[] = { 15, 245, 10, 249, 244, 249, 9, 192, 0, 249, 244, 0, 244, 3, 245,
	     5, 247, 6, 249, 7, 252, 7, 1, 6, 4, 5, 6, 3, 8, 0, 9, 249, 9 },
char69[] = { 11, 246, 9, 250, 244, 250, 9, 192, 0, 250, 244, 7, 244, 192, 0,
	     250, 254, 2, 254, 192, 0, 250, 9, 7, 9 },
char70[] = { 8, 246, 8, 250, 244, 250, 9, 192, 0, 250, 244, 7, 244, 192, 0,
	     250, 254, 2, 254 },
char71[] = { 22, 246, 11, 8, 249, 7, 247, 5, 245, 3, 244, 255, 244, 253, 245,
	     251, 247, 250, 249, 249, 252, 249, 1, 250, 4, 251, 6, 253, 8,
	     255, 9, 3, 9, 5, 8, 7, 6, 8, 4, 8, 1, 192, 0, 3, 1, 8, 1 },
char72[] = { 8, 245, 11, 249, 244, 249, 9, 192, 0, 7, 244, 7, 9, 192, 0, 249,
	     254, 7, 254 },
char73[] = { 2, 252, 4, 0, 244, 0, 9 },
char74[] = { 10, 248, 8, 4, 244, 4, 4, 3, 7, 2, 8, 0, 9, 254, 9, 252, 8, 251,
	     7, 250, 4, 250, 2 },
char75[] = { 8, 245, 10, 249, 244, 249, 9, 192, 0, 7, 244, 249, 2, 192, 0,
	     254, 253, 7, 9 },
char76[] = { 5, 246, 7, 250, 244, 250, 9, 192, 0, 250, 9, 6, 9 },
char77[] = { 11, 244, 12, 248, 244, 248, 9, 192, 0, 248, 244, 0, 9, 192, 0, 8,
	     244, 0, 9, 192, 0, 8, 244, 8, 9 },
char78[] = { 8, 245, 11, 249, 244, 249, 9, 192, 0, 249, 244, 7, 9, 192, 0, 7,
	     244, 7, 9 },
char79[] = { 21, 245, 11, 254, 244, 252, 245, 250, 247, 249, 249, 248, 252,
	     248, 1, 249, 4, 250, 6, 252, 8, 254, 9, 2, 9, 4, 8, 6, 6, 7, 4,
	     8, 1, 8, 252, 7, 249, 6, 247, 4, 245, 2, 244, 254, 244 },
char80[] = { 13, 245, 10, 249, 244, 249, 9, 192, 0, 249, 244, 2, 244, 5, 245,
	     6, 246, 7, 248, 7, 251, 6, 253, 5, 254, 2, 255, 249, 255 },
char81[] = { 24, 245, 11, 254, 244, 252, 245, 250, 247, 249, 249, 248, 252,
	     248, 1, 249, 4, 250, 6, 252, 8, 254, 9, 2, 9, 4, 8, 6, 6, 7, 4,
	     8, 1, 8, 252, 7, 249, 6, 247, 4, 245, 2, 244, 254, 244, 192, 0,
	     1, 5, 7, 11 },
char82[] = { 16, 245, 10, 249, 244, 249, 9, 192, 0, 249, 244, 2, 244, 5, 245,
	     6, 246, 7, 248, 7, 250, 6, 252, 5, 253, 2, 254, 249, 254, 192, 0,
	     0, 254, 7, 9 },
char83[] = { 20, 246, 10, 7, 247, 5, 245, 2, 244, 254, 244, 251, 245, 249,
	     247, 249, 249, 250, 251, 251, 252, 253, 253, 3, 255, 5, 0, 6, 1,
	     7, 3, 7, 6, 5, 8, 2, 9, 254, 9, 251, 8, 249, 6 },
char84[] = { 5, 248, 8, 0, 244, 0, 9, 192, 0, 249, 244, 7, 244 },
char85[] = { 10, 245, 11, 249, 244, 249, 3, 250, 6, 252, 8, 255, 9, 1, 9, 4,
	     8, 6, 6, 7, 3, 7, 244 },
char86[] = { 5, 247, 9, 248, 244, 0, 9, 192, 0, 8, 244, 0, 9 },
char87[] = { 11, 244, 12, 246, 244, 251, 9, 192, 0, 0, 244, 251, 9, 192, 0, 0,
	     244, 5, 9, 192, 0, 10, 244, 5, 9 },
char88[] = { 5, 246, 10, 249, 244, 7, 9, 192, 0, 7, 244, 249, 9 },
char89[] = { 6, 247, 9, 248, 244, 0, 254, 0, 9, 192, 0, 8, 244, 0, 254 },
char90[] = { 8, 246, 10, 7, 244, 249, 9, 192, 0, 249, 244, 7, 244, 192, 0,
	    249, 9, 7, 9 },
char91[] = { 11, 249, 7, 253, 240, 253, 16, 192, 0, 254, 240, 254, 16, 192, 0,
	     253, 240, 4, 240, 192, 0, 253, 16, 4, 16 },
char92[] = { 2, 245, 11, 9, 16, 247, 240 },
char93[] = { 11, 249, 7, 2, 240, 2, 16, 192, 0, 3, 240, 3, 16, 192, 0, 252,
	     240, 3, 240, 192, 0, 252, 16, 3, 16 },
char94[] = { 7, 245, 11, 248, 2, 0, 253, 8, 2, 192, 0, 248, 2, 0, 254, 8, 2 },
char95[] = { 2, 253, 22, 0, 9, 20, 9 },
char96[] = { 7, 251, 5, 1, 244, 0, 245, 255, 247, 255, 249, 0, 250, 1, 249, 0,
	     248 },
char97[] = { 17, 247, 10, 6, 251, 6, 9, 192, 0, 6, 254, 4, 252, 2, 251, 255,
	     251, 253, 252, 251, 254, 250, 1, 250, 3, 251, 6, 253, 8, 255, 9,
	     2, 9, 4, 8, 6, 6 },
char98[] = { 17, 246, 9, 250, 244, 250, 9, 192, 0, 250, 254, 252, 252, 254,
	     251, 1, 251, 3, 252, 5, 254, 6, 1, 6, 3, 5, 6, 3, 8, 1, 9, 254,
	     9, 252, 8, 250, 6 },
char99[] = { 14, 247, 9, 6, 254, 4, 252, 2, 251, 255, 251, 253, 252, 251, 254,
	     250, 1, 250, 3, 251, 6, 253, 8, 255, 9, 2, 9, 4, 8, 6, 6 },
char100[] = { 17, 247, 10, 6, 244, 6, 9, 192, 0, 6, 254, 4, 252, 2, 251, 255,
	      251, 253, 252, 251, 254, 250, 1, 250, 3, 251, 6, 253, 8, 255, 9,
	      2, 9, 4, 8, 6, 6 },
char101[] = { 17, 247, 9, 250, 1, 6, 1, 6, 255, 5, 253, 4, 252, 2, 251, 255,
	      251, 253, 252, 251, 254, 250, 1, 250, 3, 251, 6, 253, 8, 255, 9,
	      2, 9, 4, 8, 6, 6 },
char102[] = { 8, 251, 7, 5, 244, 3, 244, 1, 245, 0, 248, 0, 9, 192, 0, 253,
	      251, 4, 251 },
char103[] = { 22, 247, 10, 6, 251, 6, 11, 5, 14, 4, 15, 2, 16, 255, 16, 253,
	      15, 192, 0, 6, 254, 4, 252, 2, 251, 255, 251, 253, 252, 251,
	      254, 250, 1, 250, 3, 251, 6, 253, 8, 255, 9, 2, 9, 4, 8, 6, 6 },
char104[] = { 10, 247, 10, 251, 244, 251, 9, 192, 0, 251, 255, 254, 252, 0,
	      251, 3, 251, 5, 252, 6, 255, 6, 9 },
char105[] = { 8, 252, 4, 255, 244, 0, 245, 1, 244, 0, 243, 255, 244, 192, 0,
	      0, 251, 0, 9 },
char106[] = { 11, 251, 5, 0, 244, 1, 245, 2, 244, 1, 243, 0, 244, 192, 0, 1,
	      251, 1, 12, 0, 15, 254, 16, 252, 16 },
char107[] = { 8, 247, 8, 251, 244, 251, 9, 192, 0, 5, 251, 251, 5, 192, 0,
	      255, 1, 6, 9 },
char108[] = { 2, 252, 4, 0, 244, 0, 9 },
char109[] = { 18, 241, 15, 245, 251, 245, 9, 192, 0, 245, 255, 248, 252, 250,
	      251, 253, 251, 255, 252, 0, 255, 0, 9, 192, 0, 0, 255, 3, 252,
	      5, 251, 8, 251, 10, 252, 11, 255, 11, 9 },
char110[] = { 10, 247, 10, 251, 251, 251, 9, 192, 0, 251, 255, 254, 252, 0,
	      251, 3, 251, 5, 252, 6, 255, 6, 9 },
char111[] = { 17, 247, 10, 255, 251, 253, 252, 251, 254, 250, 1, 250, 3, 251,
	      6, 253, 8, 255, 9, 2, 9, 4, 8, 6, 6, 7, 3, 7, 1, 6, 254, 4, 252,
	      2, 251, 255, 251 },
char112[] = { 17, 246, 9, 250, 251, 250, 16, 192, 0, 250, 254, 252, 252, 254,
	      251, 1, 251, 3, 252, 5, 254, 6, 1, 6, 3, 5, 6, 3, 8, 1, 9, 254,
	      9, 252, 8, 250, 6 },
char113[] = { 17, 247, 10, 6, 251, 6, 16, 192, 0, 6, 254, 4, 252, 2, 251, 255,
	      251, 253, 252, 251, 254, 250, 1, 250, 3, 251, 6, 253, 8, 255, 9,
	      2, 9, 4, 8, 6, 6 },
char114[] = { 8, 249, 6, 253, 251, 253, 9, 192, 0, 253, 1, 254, 254, 0, 252,
	      2, 251, 5, 251 },
char115[] = { 17, 248, 9, 6, 254, 5, 252, 2, 251, 255, 251, 252, 252, 251,
	      254, 252, 0, 254, 1, 3, 2, 5, 3, 6, 5, 6, 6, 5, 8, 2, 9, 255, 9,
	      252, 8, 251, 6 },
char116[] = { 8, 251, 7, 0, 244, 0, 5, 1, 8, 3, 9, 5, 9, 192, 0, 253, 251, 4,
	      251 },
char117[] = { 10, 247, 10, 251, 251, 251, 5, 252, 8, 254, 9, 1, 9, 3, 8, 6, 5,
	      192, 0, 6, 251, 6, 9 },
char118[] = { 5, 248, 8, 250, 251, 0, 9, 192, 0, 6, 251, 0, 9 },
char119[] = { 11, 245, 11, 248, 251, 252, 9, 192, 0, 0, 251, 252, 9, 192, 0,
	      0, 251, 4, 9, 192, 0, 8, 251, 4, 9 },
char120[] = { 5, 248, 9, 251, 251, 6, 9, 192, 0, 6, 251, 251, 9 },
char121[] = { 9, 248, 8, 250, 251, 0, 9, 192, 0, 6, 251, 0, 9, 254, 13, 252,
	      15, 250, 16, 249, 16 },
char122[] = { 8, 248, 9, 6, 251, 251, 9, 192, 0, 251, 251, 6, 251, 192, 0,
	      251, 9, 6, 9 },
char123[] = { 39, 249, 7, 2, 240, 0, 241, 255, 242, 254, 244, 254, 246, 255,
	      248, 0, 249, 1, 251, 1, 253, 255, 255, 192, 0, 0, 241, 255, 243,
	      255, 245, 0, 247, 1, 248, 2, 250, 2, 252, 1, 254, 253, 0, 1, 2,
	      2, 4, 2, 6, 1, 8, 0, 9, 255, 11, 255, 13, 0, 15, 192, 0, 255, 1,
	      1, 3, 1, 5, 0, 7, 255, 8, 254, 10, 254, 12, 255, 14, 0, 15, 2,
	      16 },
char124[] = { 2, 252, 4, 0, 240, 0, 16 },
char125[] = { 39, 249, 7, 254, 240, 0, 241, 1, 242, 2, 244, 2, 246, 1, 248, 0,
	      249, 255, 251, 255, 253, 1, 255, 192, 0, 0, 241, 1, 243, 1, 245,
	      0, 247, 255, 248, 254, 250, 254, 252, 255, 254, 3, 0, 255, 2,
	      254, 4, 254, 6, 255, 8, 0, 9, 1, 11, 1, 13, 0, 15, 192, 0, 1, 1,
	      255, 3, 255, 5, 0, 7, 1, 8, 2, 10, 2, 12, 1, 14, 0, 15, 254, 16,
	      },
char126[] = { 23, 255, 21, 2, 1, 0, 255, 1, 253, 3, 251, 5, 251, 7, 252,
	      11, 255, 13, 0, 15, 0, 17, 255, 18, 254, 192, 0, 2, 0, 1,
	      254, 3, 253, 5, 253, 7, 254, 11, 1, 13, 2, 15, 2, 17, 1, 18,
	      255, 18, 252 };

/* Pointers to character definition tables. */

static unsigned char *ctab[] = {
    char32, char33, char34, char35, char36, char37, char38, char39, char40,
    char41, char42, char43, char44, char45, char46, char47, char48, char49,
    char50, char51, char52, char53, char54, char55, char56, char57, char58,
    char59, char60, char61, char62, char63, char64, char65, char66, char67,
    char68, char69, char70, char71, char72, char73, char74, char75, char76,
    char77, char78, char79, char80, char81, char82, char83, char84, char85,
    char86, char87, char88, char89, char90, char91, char92, char93, char94,
    char95, char96, char97, char98, char99, char100, char101, char102,
    char103, char104, char105, char106, char107, char108, char109, char110,
    char111, char112, char113, char114, char115, char116, char117, char118,
    char119, char120, char121, char122, char123, char124, char125, char126
};

/* Table used to look up sine of angles from 0 through 90 degrees.
   The value returned is the sine * 65536.  Symmetry is used to
   obtain sine and cosine for arbitrary angles using this table. */

static long sintab[] = {
    0, 1143, 2287, 3429, 4571, 5711, 6850, 7986, 9120, 10252, 11380,
    12504, 13625, 14742, 15854, 16961, 18064, 19160, 20251, 21336,
    22414, 23486, 24550, 25606, 26655, 27696, 28729, 29752, 30767,
    31772, 32768, 33753, 34728, 35693, 36647, 37589, 38521, 39440,
    40347, 41243, 42125, 42995, 43852, 44695, 45525, 46340, 47142,
    47929, 48702, 49460, 50203, 50931, 51643, 52339, 53019, 53683,
    54331, 54963, 55577, 56175, 56755, 57319, 57864, 58393, 58903,
    59395, 59870, 60326, 60763, 61183, 61583, 61965, 62328, 62672,
    62997, 63302, 63589, 63856, 64103, 64331, 64540, 64729, 64898,
    65047, 65176, 65286, 65376, 65446, 65496, 65526, 65536
};

static int extleft, exttop, extright, extbottom;  /* To accumulate extents */

/* LINTLIBRARY */

/*  ISIN  --  Return sine of an angle in integral degrees.  The
	      value returned is 65536 times the sine.  */

#if __STDC__
static long isin(int deg)
#else
static long isin(deg)
  int deg;
#endif
{
    /* Domain reduce to 0 to 360 degrees. */

    if (deg < 0) {
	deg = (360 - ((- deg) % 360)) % 360;
    } else if (deg >= 360) {
	deg = deg % 360;
    }

    /* Now look up from table according to quadrant. */

    if (deg <= 90) {
	return sintab[deg];
    } else if (deg <= 180) {
	return sintab[180 - deg];
    } else if (deg <= 270) {
	return -sintab[deg - 180];
    }
    return -sintab[360 - deg];
}

/*  ICOS  --  Return cosine of an angle in integral degrees.  The
	      value returned is 65536 times the cosine.  */

#if __STDC__
static long icos(int deg)
#else
static long icos(deg)
  int deg;
#endif
{
    return isin(deg + 90);
}  

#define Schar(x) (u = (x), (((u) & 0x80) ? ((u) | (-1 ^ 0xFF)) : (u)))

#define Scalef 21		/* Font design size */
#define Descend 9		/* Descender offset */

/* PPMD_TEXT  --  Draw the zero-terminated  string  s,	with  its  baseline
		  starting  at	point  (x, y), inclined by angle degrees to
		  the X axis, with letters height pixels  high	(descenders
		  will	extend below the baseline).  The supplied drawprocP
		  and cliendata are passed to ppmd_line which performs	the
		  actual drawing. */

void
ppmd_text(pixel**      const pixels, 
          int          const cols, 
          int          const rows, 
          pixval       const maxval, 
          int          const xpos, 
          int          const ypos, 
          int          const height, 
          int          const angle, 
          const char * const sArg, 
          void (*drawprocP)(pixel**, int, int, pixval, int, int, const void*), 
          const void*  const clientdata)
{
    long rotsin, rotcos;
    int x, y;
    const char * s;

    x = y = 0;
    rotsin = isin(-angle);
    rotcos = icos(-angle);

    s = sArg;
    while (*s) {
        unsigned char ch = *s++;
        int pen = 1;
        int u;

        if (ch >= ' ' && ch < 127) {
            ch -= ' ';
	    if (ctab[ch] != NULL) {
		int cn = *ctab[ch];
		unsigned char *cst = ctab[ch] + 3;
		int lx, ly;

		x -= Schar(*(ctab[ch] + 1));
		lx = x + Schar(*cst++);
		ly = y + Schar(*cst++);
		while (--cn > 0) {
		    if (*cst == 192) {
			pen = 0;
			cst += 2;
		    } else {
			int nx = x + Schar(*cst++);
			int ny = y + Schar(*cst++);
			if (pen) {
			    int mx1, my1, mx2, my2;
			    int tx1, ty1, tx2, ty2;

                            /* Note that up until this  moment  we've  been
			       working	in  an	arbitrary model co-ordinate
			       system with  fixed  size  and  no  rotation.
			       Before  drawing	the  stroke,  transform  to
			       viewing co-ordinates to	honour	the  height
			       and angle specifications. */

			    mx1 = (lx * height) / Scalef;
			    my1 = ((ly - Descend) * height) / Scalef;
			    mx2 = (nx * height) / Scalef;
			    my2 = ((ny - Descend) * height) / Scalef;
			    tx1 = xpos + (mx1 * rotcos - my1 * rotsin) / 65536;
			    ty1 = ypos + (mx1 * rotsin + my1 * rotcos) / 65536;
			    tx2 = xpos + (mx2 * rotcos - my2 * rotsin) / 65536;
			    ty2 = ypos + (mx2 * rotsin + my2 * rotcos) / 65536;
			    ppmd_line(pixels, cols, rows, maxval,
                          tx1, ty1, tx2, ty2,
                          drawprocP, clientdata);
			}
			lx = nx;
			ly = ny;
			pen = 1;
		    }
		}
		x += *(ctab[ch] + 2); 
	    }
        } else if (ch == '\n') {
	    y += Scalef + Descend;
	    x = 0;
	}
    }
}

/* EXTENTS_DRAWPROC  --  Drawproc which just accumulates the extents
			 rectangle bounding the text. */

static void 
extents_drawproc (pixel**      const pixels, 
                  int          const cols, 
                  int          const rows,
                  pixval       const maxval, 
                  int          const x, 
                  int          const y, 
                  const void * const clientdata)
{
    extleft = MIN(extleft, x);
    exttop = MIN(exttop, y);
    extright = MAX(extright, x);
    extbottom = MAX(extbottom, y);
}


/* PPMD_TEXT_BOX  --  Calculate  extents  rectangle for a given piece of
		      text.  For most  applications  where  extents  are
		      needed,	angle  should  be  zero  to  obtain  the
		      unrotated extents.  If you need  the  extents  box
		      for post-rotation text, however, you can set angle
		      nonzero and it will be calculated correctly. */

void
ppmd_text_box(int const height, 
              int const angle, 
              const char * const s, 
              int * const left, 
              int * const top, 
              int * const right, 
              int * const bottom)
{
    extleft = 32767;
    exttop = 32767;
    extright = -32767;
    extbottom = -32767;
    ppmd_text(NULL, 32767, 32767, 255, 1000, 1000, height, angle, s, 
              extents_drawproc, NULL);
    *left = extleft - 1000; 
    *top = exttop - 1000;
    *right = extright - 1000;
    *bottom = extbottom - 1000;
}
