[comp.windows.x] X11 PS previewer: does one exist?!

emo@iuvax.cs.indiana.edu (07/19/88)

I am seeking information concerning a PostScript previewer that will
run under X version 11.  Any and all pointers would be very much
appreciated!

eric

dana@dino.bellcore.com (Dana A. Chee) (07/19/88)

Yes, one does exist.  It is called xps and is available from the
comp.sources.unix archives (vol 12 issues 50-68, 18 files in all).
The patches to make it work in X11 (originally done for X10) are
below.

This package was written by:
   Crispin Goswell @ Rutherford Appleton Laboratory caag@uk.ac.rl.vd
and updated to X11 by:
   Terry Weissman      weissman@decwrl.dec.com

It works OK for me on a Sun 3/75.

Remove my signature from the end, the shar file for the patches
follows.

Enjoy!!

==================== x11ps.SHAR  ====================
#!/bin/sh
#
# This is the source for my X11 interface to the postscript previewer.
# This source did get posted, but not to comp.sources.unix; it ended up
# in (I believe) comp.sources.bugs.  Anyway, here it is, along with the
# makefile I use (which you'll probably have to modify somewhat).
#
# - Terry Weissman	weissman@decwrl.dec.com		...!decwrl!weissman
#
#
# to extract, remove the header and type "sh filename"
if `test ! -s ./X11.c`
then
echo "writing ./X11.c"
cat > ./X11.c << '\Rogue\Monster\'
/*
 * Copyright (C) Rutherford Appleton Laboratory 1987
 * 
 * This source may be copied, distributed, altered or used, but not sold for profit
 * or incorporated into a product except under licence from the author.
 * It is not in the public domain.
 * This notice should remain in the source unaltered, and any changes to the source
 * made by persons other than the author should be marked as such.
 * 
 *	Crispin Goswell @ Rutherford Appleton Laboratory caag@uk.ac.rl.vd
 */
#include "main.h"
#include "graphics.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include "canon.h"

static void Punt(str)
char *str;
{
    fprintf(stderr, "%s\n", str);
    exit(1);
}



static Display *dpy;

typedef struct _HardwareRec {
    Drawable w;
} HardwareRec, *Hardware;

#ifdef CANON
struct hardware
{
    /*
     * Each driver is expected to provide its own definition of this
     * structure.  It is only ever used as a pointer and is never dereferenced
     * outside the driver.
     */
    int pad;
};
#endif CANON

/*
 * This file describes the interface that PostScript requires to the graphics
 * system at Version 1.4.
 * 	
 * ''Hardware'' in this context refers to a pointer to windows and/or bitmaps
 * and is the lowest level of access that PostScript is interested in. Any
 * Hardware parameter may be expected to be NULL.
 */

/********************* CREATION OF WINDOWS AND BITMAPS *******************/

#define SCREEN 0		/* What to use as our screen number. */
#define MIN(x, y)	(((x) < (y)) ? (x) : (y))

static GC fillgc[16];

struct hardware *InitHardware ()
{
    XGCValues values;
    int i;
    if ((dpy = XOpenDisplay(dpy)) == NULL)
	Punt("Could not open display");
    InitTransfer(DisplayHeight(dpy, SCREEN) / 11);
    /* This defines our screen as being 11 inches high, no matter what its */
    /* real size.  What a hack. */
    values.foreground = AllPlanes;
    for (i=0 ; i<16 ; i++) {
	values.function = i;
	fillgc[i] = XCreateGC(dpy, RootWindow(dpy, SCREEN),
			      GCFunction | GCForeground, &values);
    }
}
/*
 * InitHardware () returns a default device which PostScript may use
 * immediately (or NULL if not appropriate).  Its size and shape are not
 * defined. Most typically the user will want to start up another device
 * before it is used anyway. No attempt will be made by PostScript to Destroy
 * the resulting device.
 */

static struct hardware *NewHardware(width, height)
int width, height;
{
    struct hardware *to;
    Hardware hard;
    to = (struct hardware *) malloc(sizeof(struct hardware));
    hard = (Hardware) malloc(sizeof(HardwareRec));
    to->hard.addr = (char *) hard;
    to->flags = 0;
    to->aux = to->clip = NULL;
    to->extent = NewDevicePoint(width, height);
    hard->w = NULL;
    return to;
}


struct hardware *NewBitmapHardware (width, height)
int width, height;
{
    struct hardware *to = NewHardware(width, height);
    Hardware hard = (Hardware) to->hard.addr;
    to->flags = NULL;
    hard->w = XCreatePixmap(dpy, RootWindow(dpy, SCREEN), width, height,
			    DefaultDepth(dpy, SCREEN));
    XFillRectangle(dpy, hard->w, fillgc[GXclear], 0, 0, width, height);

/*    {
	static int y = 0;
	XSetWindowAttributes attributes;
	hard->w = XCreateSimpleWindow(dpy, RootWindow(dpy, SCREEN), 700, y,
				  width, height, 1, BlackPixel(dpy, SCREEN),
				  WhitePixel(dpy, SCREEN));
	attributes.override_redirect = TRUE;
	XChangeWindowAttributes(dpy, hard->w, CWOverrideRedirect, &attributes);
	XMapWindow(dpy, hard->w);
	y+=30;
    }*/
    return to;
}

struct hardware *NewWindowHardware (width, height)
int width, height;
{
    struct hardware *to = NewHardware(width, height);
    Hardware hard = (Hardware) to->hard.addr;
    XEvent event;
    to->flags = ISWIN;
    hard->w = XCreateSimpleWindow(dpy, RootWindow(dpy, SCREEN), 0, 0,
				  width, height, 1, BlackPixel(dpy, SCREEN),
				  0);
    XSelectInput(dpy, hard->w, ExposureMask);
    XMapWindow(dpy, hard->w);
    XNextEvent(dpy, &event);
    XSelectInput(dpy, hard->w, 0);
    return to;
}
/*
 * NewBitmapHardware () is expected to create a new bitmap. Only one plane
 * will be needed.
 * 	
 * NewWindowHardware () is expected to create a window on the screen. On a
 * colour system this will be expected to support full colour.
 */

#ifdef CANON
int IsWindowHardware (h)
struct hardware *h;
{}
#endif CANON
/*
 * IsWindowHardware () should return TRUE if the hardware is a window, FALSE
 * otherwise.  NULL is a window.
 */

void DestroyHardware (h)
struct hardware *h;
{
    if (h) {
	Hardware hard = (Hardware) h->hard.addr;
	if (IsWindowHardware(h))
	    XDestroyWindow(dpy, hard->w);
	else
	    XFreePixmap(dpy, hard->w);
    }
}
/*
 * 	
 * DestroyHardware () should release the resources required by the hardware,
 * bitmap or window.  This should cause a window device to vanish. NULL is not
 * an error (does nothing).
 */


#ifdef CANON
Matrix DeviceMatrix (width, height)
int width, height;
{}
#endif CANON

/*
 *
 * DeviceMatrix () should return a matrix appropriate to a device of the given
 * height and width.  For a typical display with a graphics origin at the top
 * left of a window, an appropriate definition would be:
 * 	
 * Matrix DeviceMatrix (width, height)
 * int width, height;
 * {
 *     return NewMatrix (PIXELS_PER_INCH / 72.0, 0.0, 0.0,
 * 		         -PIXELS_PER_INCH / 72.0, 0.0, (float) height);
 * }
 */

#ifdef CANON
DevicePoint HardwareExtent (h)
struct hardware *h;
{}
#endif
/*
 * HardwareExtent () returns a DevicePoint describing the width and height of
 * the argument.  NULL has extent NewDevicePoint (0, 0).
 */

/*************************** OUTPUT PRIMITIVES ******************************/	

void BitBlt (from, to, fromPoint, toPoint, extent, rop)
struct hardware *from, *to;
DevicePoint toPoint, fromPoint, extent;
int rop;
{
    Hardware fromhard, tohard;
    static int count = 0;
    if (to == NULL) return;
    tohard = (Hardware) to->hard.addr;
    if (from == NULL) {
	XFillRectangle(dpy, tohard->w, fillgc[rop], toPoint.dx, toPoint.dy,
		       extent.dx, extent.dy);
    } else {
	fromhard = (Hardware) from->hard.addr;
	XCopyArea(dpy, fromhard->w, tohard->w, fillgc[rop], fromPoint.dx,
		  fromPoint.dy, extent.dx, extent.dy, toPoint.dx, toPoint.dy);
    }
    if (count++ % 50 == 0) XSync(dpy, 0);
}

#ifdef CANON
void Paint (from, to, fromPoint, toPoint, extent, colour)
struct hardware *from, *to;
DevicePoint toPoint, fromPoint, extent;
Colour colour;
{}
#endif

/*
 * 	
 * BitBlt () is a full function RasterOp. The 'rop' argument will have values
 * as described in the header file hard.h. If the from argument is NULL it is
 * taken to be a bitmap full of ones the shape of the fromPoint and extent. If
 * the to argument is NULL, this is a no-op.
 *
 * Paint () is an addition to BitBlt. Bits that are set in the source are
 * Painted into the destination in the given colour with a copying rasterop so
 * that they replace pixels previously there. If the machine does not support
 * colour windows, half-toning should be performed.  Colour values have hue,
 * saturation and brightness components. on a black and white or greyscale
 * system the brightness value will be a FP value between 0.0 (black) and 1.1
 * (white), which can be used as a grey level.
 * 	
 * Paint is expected to mask with the clip mask. BitBlt is not,
 */

#ifdef CANON
void BitBltTrapezoid(to, lefttop, leftbottom, righttop, rightbottom,
		     top, bottom, rop)
struct hardware *to;
DevicePoint lefttop, leftbottom, righttop, rightbottom;
int top, bottom, rop;
{}
#endif CANON

#ifdef CANON
void PaintTrapezoid (to, lefttop, leftbottom, righttop, rightbottom,
		     top, bottom, colour)
struct hardware *to;
DevicePoint lefttop, leftbottom, righttop, rightbottom;
int top, bottom;
Colour colour;
{}
#endif CANON

/*
 * BitBltTrapezoid () and PaintTrapezoid () render a complete trapezoidal
 * shape.  The corners of the trapezoid may lie far outside the range of
 * interesting scan-lines, but the slope of the line should be clipped by the
 * top and bottom. The coordinates are half-open.
 */

void BitBltLine (h, fromPoint, toPoint, rop)
struct hardware *h;
DevicePoint fromPoint, toPoint;
int rop;
{
    if (h) {
	Hardware hard = (Hardware) h->hard.addr;
	XDrawLine(dpy, hard->w, fillgc[rop], fromPoint.dx, fromPoint.dy,
		  toPoint.dx, toPoint.dy);
    }
}

#ifdef CANON
void PaintLine (h, fromPoint, toPoint, colour)
struct hardware *h;
DevicePoint fromPoint, toPoint;
Colour colour;
{}
#endif CANON

/*
 * 	
 * 	BitBltLine () is expected to draw a line between the given points
 * 	with the given RasterOp and colour masking.
 * 	The line should be one pixel wide and half-open.
 * 	[Thicker lines are done with BitBlt.]
 * 	
 * 	PaintLine () is expected to Paint a line by analogy with Paint
 * 	and BitBlt.
 */

void BitBltBlob (to, top, height, left, right, rop)
struct hardware *to;
int top, height, *left, *right, rop;
{
    int i;
    DevicePoint p1, p2;
    for (i=0 ; i<height ; i++) {
	p1.dx = left[i];
	p2.dx = right[i];
	p1.dy = p2.dy = top + i;
	BitBltLine(to, p1, p2, rop);
    }
}

 /*
  * BitBltBlob () takes a set of pixel coordinates and fills the trapezon
  * figure half open.
  */

#ifdef SLOWANDWRONG
void RasterTile (from, to, toPoint, extent, rop)
struct hardware *from, *to;
DevicePoint toPoint, extent;
int rop;
{
    Hardware fromhard, tohard;
    DevicePoint p1, p2, p3;
    int x, y;
    if (to == NULL) return;
    if (from == NULL)
	Punt("Can only RasterTile from Hardware.");
    fromhard = (Hardware) from->hard.addr;
    tohard = (Hardware) to->hard.addr;
    p1.dx = p1.dy = 0;
    for (x=toPoint.dx ; x < toPoint.dx + extent.dx ; x+=from->extent.dx) {
	for (y=toPoint.dy ; y < toPoint.dy + extent.dy ; y+=from->extent.dy) {
	    p2.dx = x;
	    p2.dy = y;
	    p3.dx = MIN(toPoint.dx + extent.dx - x, from->extent.dx);
	    p3.dy = MIN(toPoint.dy + extent.dy - y, from->extent.dy);
	    BitBlt(from, to, p1, p2, p3, rop);
	}
    }
}
#endif SLOWANDWRONG


void RasterTile (from, to, toPoint, extent, rop)
struct hardware *from, *to;
DevicePoint toPoint, extent;
int rop;
{
    Hardware fromhard, tohard;
    static GC gc = NULL;
    XGCValues values;
    int valuemask;
    if (to == NULL) return;
    if (from == NULL || IsWindowHardware(from))
	Punt("Can only RasterTile from Bitmap.");
    fromhard = (Hardware) from->hard.addr;
    tohard = (Hardware) to->hard.addr;
    values.tile = fromhard->w;
    values.fill_style = FillTiled;
    values.function = rop;
    valuemask = GCFunction | GCTile | GCFillStyle;
    if (gc == NULL)
	gc = XCreateGC(dpy, RootWindow(dpy, SCREEN), valuemask, &values);
    else
	XChangeGC(dpy, gc, valuemask, &values);
    XFillRectangle(dpy, tohard->w, gc, toPoint.dx, toPoint.dy,
		   extent.dx, extent.dy);
}

/*
 * RasterTile () replicates the whole of ``from'' over ``to'', but clipped by
 * the rectangle bounded by ``toPoint'' and ``extent''.
 */

/******************* BRIGHTNESS TRANSFER FUNCTION ************************/

#ifdef CANON
int TransferSize ()
{}
#endif CANON

#ifdef CANON
void SetTransfer (vec)
float *vec;
{}
#endif CANON
/*
 * 	
 * TransferSize () and SetTransfer () control the mapping function between
 * user brightnesses and device brightnesses. The interface is expected to
 * perform this mapping of brightnesses to a sufficient resolution.
 * SetTransfer takes a table of floating point numbers between 0 and 1. User
 * brightnesses are scaled to the size of this table and mapped through it.
 * The argument table given to SetTransfer () will be deleted after use.
 * TransferSize () simply enquires the required size of the table.
 * 	
 * It may be appropriate to half-tone on a grayscale or colour device to
 * improve rendering if it is not too expensive. TransferSize () returns the
 * size of the pattern table.
 */

/********************** BITMAP CONVERSION ********************************/

char *StringFromHardware (h)
struct hardware *h;
{
    XImage *image;
    Hardware hard;
    unsigned char *result, *ptr, c;
    int x, y, i;
    if (h == NULL) return NULL;
    hard = (Hardware) h->hard.addr;
    image = XGetImage(dpy, hard->w, 0, 0, h->extent.dx, h->extent.dy,
		      AllPlanes, ZPixmap);
    result = (unsigned char *) malloc(((h->extent.dx + 7) / 8) * h->extent.dy);
    ptr = result;
    for (y=0 ; y<h->extent.dy ; y++) {
	for (x=0 ; x<h->extent.dx ; x+=8) {
	    c = 0;
	    for (i=0 ; i<8 ; i++) {
		c = c << 1;
		if (x+i < h->extent.dx)
		    c |= XGetPixel(image, x+i, y);
	    }
	}
	*ptr++ = c;
    }
    free((char *) image);
    return (char *) result;
}

struct hardware *HardwareFromString (s, width, height)
char *s;
int width, height;
{
    struct hardware *h = NewBitmapHardware(width, height);
    Hardware hard = (Hardware) h->hard.addr;
    XImage *image;
    if (s == NULL) Punt("HardwareFromString called with NULL string!");
    image = XCreateImage(dpy, DefaultVisual(dpy, SCREEN),
			 DefaultDepth(dpy, SCREEN), ZPixmap, 0, s,
			 width, height, 8, 0);
    image->bitmap_bit_order = MSBFirst;
    XPutImage(dpy, hard->w, fillgc[GXcopy], image, 0, 0, 0, 0, width, height);
    free((char *) image);
    return h;
}
/*
 * 	
 * StringFromHardware () produces a string from its argument which describes
 * the bitmap.  The bitmap is returned in row-major order with the leftmost
 * bit of each byte in the most significant position. Rows are padded to byte
 * boundaries. Only single plane bitmaps are used.
 * 	
 * HardwareFromString () performs the inverse mapping, generating a bitmap
 * from a set of bits, given a width and height. Only single plane bitmaps are
 * used.
 */

/************************* HALF-TONE SCREEN *******************************/

#ifdef CANON
int ScreenSize (freq, rotation)
float freq, rotation;
{}
#endif CANON

#ifdef CANON
void BuildScreen (freq, rotation, x, y)
float freq, rotation, *x, *y;
{}
#endif CANON

#ifdef CANON
void SetScreen (freq, rotation, thresh)
float freq, rotation, *thresh;
{}
#endif CANON
/*
 * ScreenSize () allows PostScript to determine how large an array of sample
 * points to expect.  It should return the length of the side of the sample
 * square.
 * 	
 * BuildScreen () returns a set of sampling coordinates to PostScript to hand
 * to the users spot-function
 * 	
 * SetScreen () allows PostScript to set the thresholds for each sample point
 * so that half-tone bitmaps can be made.
 */

/************************* CLIPPING ******************************************/

#ifdef CANON
void SetClipHardware (h, clip)
struct hardware *h, *clip;
{}
#endif
/*
 * 	
 * SetClipHardware sets hardware which is a clip mask for BitBlt. This mask
 * should be ANDed with any output operation. If clip is NULL, masking will
 * not be needed.
 */

/************************ UPDATE CONTROLS **********************************/

void HardUpdate ()
{
    XFlush(dpy, 0);
}
/*
 * HardUpdate is a hook to allow devices which do output buffering to flush
 * that buffering when appropriate.  This allows an interactive user to see
 * completed graphics between prompts (it is called as a side-effect of the
 * PostScript flush operator). Typically is is a no-op.
 */

void UpdateControl (h, on)
struct hardware *h;
int on;
{}
/*
 * This call can be used to enable batching of output operations.
 * UpdateControl (h, FALSE) means ``start of batching'' UpdateControl (h,
 * TRUE) means ``end of batching''. It is used to improve performance on
 * machines where screen updates have a high locking overhead. It may be a
 * no-op.  The operation should nest if batching is already in progress: FALSE
 * increments a counter, TRUE decrements a counter. Display changes are
 * allowed when the counter is non-zero.
 */

/********************************** CANONICAL IMPLEMENTATION LIBRARY ******************************/

/*
 * Some parts of the above interface can be supported by a canonical library.
 * This library contains:

SetClipHardware
HardUpdate
IsWindowHardware
HardwareExtent

PaintTrapezoid
BitBltTrapezoid

Paint
PaintLine

DeviceMatrix
InitTransfer
TransferSize
SetTransfer
ScreenSize
BuildScreen
SetScreen

 *
 * As the driver matures, the user may provide his own versions of the
 * canonical routines.  This leaves the following for implementation by 
 * the user.
 *

InitHardware
NewBitmapHardware
NewWindowHardware
DestroyHardware
HardwareFromString
StringFromHardware
UpdateControl
RasterTile
BitBlt
BitBltLine
BitBltBlob

 * There is a pedagogical implementation in null.c
 *	
 * There are a number of interface issues concerning the canonical driver.
 * Firstly, a canonical struct hardware is defined, which contains a union of
 * a char * and an int handle. The remainder are expected to use this to store
 * device specific information.
 *
 * InitTransfer() should be called during InitHardware with the number of 
 * pixels per inch on the display as an argument.
 */
\Rogue\Monster\
else
  echo "will not over write ./X11.c"
fi
if `test ! -s ./makefile`
then
echo "writing ./makefile"
cat > ./makefile << '\Rogue\Monster\'
OBJECTS=array.o boolean.o config.o control.o dictionary.o file.o\
	integer.o main.o math.o misc.o name.o operator.o\
	poly.o property.o real.o save.o stack.o string.o unix.o
LIBS=libww.a -lsuntool -lsunwindow -lpixrect -g
GRAPHICS=cache.o colour.o device.o fill.o font.o gsave.o image.o mat.o matrix.o\
	pat.o path.o state.o stroke.o
CFLAGS=-g -DX11

XLIB= /usr/src/X11/lib/X/libX11.a
XLIBINCLUDES = -I/usr/src/X11/X11 -I/usr/src/X11

xps: $(OBJECTS) $(GRAPHICS) X11.o canon.a $(XLIB) makefile
	rm -f xps
	cc -o xps $(OBJECTS) $(GRAPHICS) X11.o canon.a -lm $(XLIB)

OLDXLIB= /usr/src/x11/Xlib/libX11.a
OLDXLIBINCLUDES = -I/usr/src/X11/X11 -I/usr/src/X11
xps.old: $(OBJECTS) $(GRAPHICS) X11.o canon.a $(OLDXLIB) makefile
	rm -f xps.old
	cc -o xps.old $(OBJECTS) $(GRAPHICS) X11.o canon.a -lm $(OLDXLIB)

X11.o: X11.c
	cc -c $(CFLAGS) $(XLIBINCLUDES) X11.c

PS:	$(OBJECTS) $(GRAPHICS) hard.o canon.a
	cc $(CFLAGS)  $(OBJECTS) $(GRAPHICS) hard.o canon.a -lm `libs` -o PS

sunPS:	$(OBJECTS) $(GRAPHICS) hard.o canon.a pixrect
	cc $(CFLAGS)  $(OBJECTS) $(GRAPHICS) hard.o canon.a -lm -lpixrect -o sunPS

CPS:	$(OBJECTS) $(GRAPHICS) colour-ww.o trapezoid.o canon.o
	cc $(CFLAGS)  $(OBJECTS) $(GRAPHICS) colour-ww.o canon.o trapezoid.o -lm `libs` -o CPS

postscript:	$(OBJECTS) $(GRAPHICS) adapter.o protocol.o
	cc $(CFLAGS) $(OBJECTS) $(GRAPHICS) adapter.o protocol.o -lm -o postscript

XPS:	$(OBJECTS) $(GRAPHICS) X.o
	cc $(CFLAGS)  $(OBJECTS) $(GRAPHICS) X.o -lm libX.a -o XPS

canon.a:	canon.o screen.o trapezoid.o paint.o
	ar ruv canon.a canon.o screen.o trapezoid.o paint.o
	ranlib canon.a

viewer:	protocol.o viewer.o hard.o canon.a
	cc protocol.o viewer.o hard.o canon.a `libs` -o viewer

all:	PS postscript viewer

ww:	ww.o wwlib installww

pixrect:	pixrect.o
	cp pixrect.o hard.o

sun:	ww wwsun

orion:	orion.o installorion orionlib

X.o:	
	cc -c X.c

wwlib:
	if [ -f libww.a ]; \
	then \
		echo 'echo libww.a' >lww; \
	else \
		echo 'echo -lww' >lww; \
	fi; \
	chmod +x lww
	echo "echo `lww`" >libs; chmod +x libs

wwsun:	
	echo "echo `lww` -lsuntool -lsunwindow -lpixrect" >libs; chmod +x libs

orionlib:
	echo 'echo -lG' >libs; chmod +x libs

installww:
	cp ww.o hard.o

installorion:
	cp orion.o hard.o
\Rogue\Monster\
else
  echo "will not over write ./makefile"
fi
echo "Finished archive 1 of 1"
exit
--
+*************************************************************************+
*  Dana Chee				(201) 829-4488			  *
*  Bellcore								  *
*  Room 2Q-250								  *
*  445 South Street			ARPA: dana@bellcore.com		  *
*  Morristown,  NJ  07960-1910		UUCP: {gateways}!bellcore!dana	  *
+*************************************************************************+

devon@hcx1.SSD.HARRIS.COM (07/19/88)

Contact:

Pipeline Associates, Inc.
239 W. Main St.
West Orange, NJ  07052
(201) 731-7860

They advertise PSVIEW, a PostScript previewer for X.