[comp.windows.x] X windows -> Postscript

sundar@HERMES.AI.MIT.EDU.UUCP (03/12/87)

Hi:
Since too many people requested this program and since it is
relatively small, I've decided to mail it out. Please read the
comments at the beginning of the file to see how to use it. 

As I mentioned before, I've tested it out only on Suns running 4.2BSD.
The program needs to be compiled with -DSTANDALONE. When executed, the
cursor changes to a cross, and the mouse is grabbed, if all the
options turn out to be ok. Clicking on a window will then produce a
screen dump of that window. The #define DEFAULTPRINTER tells the
program where the screen dump should be sent. If the -f option is
specified, the filename specified (or stdout if the filename is '-') is
used as the output file. The -s option can be used to specify a scale
value (the default is to autoscale). The -i option is used to get a dump
in inverse video. The -b option can be used to get the window dumped
without the borders.  

The default is to use no inverse video, to include the borders and to
spool to the laser writer directly via the lpr command, and to
autoscale the output to fit on an A2 sized sheet. 

If you compile the file without -DSTANDALONE you'll get an object file
that can be linked with another program to give it a hardcopy
capability. Look at the function 'hardcopy_window' and the
documentation above it to see how you can use this feature from your
program. 

Enjoy!

-Sundar

---CUT-HERE---------
/*
 *  hardcopy.c 
 *  
 *  To use: 
 *     a. Edit the line DEFAULTPRINTER to be the string that represents
 *        your local lpr command that will accept postscript input from
 *        stdin.
 *        Make sure that the pathnames describing where the cursors are
 *        located is right on your machine.
 *     b. Compile and link with
 *            cc -o hardcopy hardcopy.c -lX -DSTANDALONE
 *     c. If you compile with
 *            cc -c hardcopy.c 
 *  you will get an object file that can be linked in with other programs 
 *  which will then be able to generate screendumps by using the function,
 *  hardcopy_window() in this file. The documentation on the arguments to
 *  this function is given later in this file (just above the function).
 *
 *     d. Invoke the program with -x to see a brief description of what
 *        the options do. 
 * 
 *  BUGS: (to be fixed) 
 *     a. doesn't do color windows correctly.
 *
 *  Sundar Narasimhan. (sundar@hermes.ai.mit.edu)  
 */

#ifndef DEFAULTPRINTER
#define DEFAULTPRINTER   "lpr -Plw9 -v"
#endif

#include <X/Xlib.h>
#include <stdio.h>
#include <ctype.h>
#include "/usr/src/local/mit/X/cursors/cross.cursor"
#include "/usr/src/local/mit/X/cursors/cross_mask.cursor"

static
min(a, b)
int a,b;
{
    if(a < b) return(a);
    else return(b);
}

char prelude[] = "\n\
/ScreenDump {\n\
	10 dict begin\n\
	[/scal /height /bitwidth /width ] {exch def} forall\n\
	/nbits bitwidth height mul def\n\
	/str 100 string def\n\
	gsave\n\
	72 72 scale\n\
	8.5 width 300 scal div div sub 2 div\n\
	11 height 300 scal div div sub 2 div 11 exch sub .625 sub translate\n\
	width height idtransform scale scal dup scale\n\
	newpath 0 0 moveto 0 1 lineto 1 1 lineto 1 0 lineto closepath eoclip\n\
	bitwidth height true [bitwidth 0 0 height 0 0] \n\
	  {	nbits 800 ge {/nbits nbits 800 sub def str} \n\
			     {nbits 8 idiv string /nbits 0 def}\n\
			  ifelse \n\
		currentfile exch readhexstring pop}\n\
	imagemask grestore end\n\
	} def\n\
\n\
";

unsigned char reverse[] = {
0x0, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 
0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x8, 0x88, 0x48, 0xc8, 
0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 
0x78, 0xf8, 0x4, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, 0xc, 0x8c, 
0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 
0x3c, 0xbc, 0x7c, 0xfc, 0x2, 0x82, 0x42, 0xc2, 0x22, 0xa2, 
0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 
0xa, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 
0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x6, 0x86, 0x46, 0xc6, 
0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 
0x76, 0xf6, 0xe, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x1, 0x81, 
0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 
0x31, 0xb1, 0x71, 0xf1, 0x9, 0x89, 0x49, 0xc9, 0x29, 0xa9, 
0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 
0x5, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 
0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0xd, 0x8d, 0x4d, 0xcd, 
0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 
0x7d, 0xfd, 0x3, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0xb, 0x8b, 
0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 
0x3b, 0xbb, 0x7b, 0xfb, 0x7, 0x87, 0x47, 0xc7, 0x27, 0xa7, 
0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 
0xf, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 
0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, 0 };


Window window;
#define DEFAULT_SCALE 2
#define BUFSIZE   1024
 
char hex[] = "FEDCBA9876543210";
char rhex[] =  "0123456789ABCDEF";

#ifdef STANDALONE
main(argc, argv)
int argc;
char *argv[];
{
    int i, scale, inverse, borders,j;
    FILE *fout = NULL;
    scale = 0;
    inverse = 0;
    borders = 1;
    if(XOpenDisplay(NULL) == 0) {
	printf("Couldn't open display?\r\n");
	exit(0);
    }
    XSelectInput(RootWindow, ButtonPressed | ButtonReleased);

    for(i=1;i<argc;i++) {
	if(argv[i][0] == '-') {
	    switch(argv[i][1]) {
	      case 's':
		if((i+1) >= argc) {
		    fprintf(stderr, 
			    "-s should be followed by a scale value\n");
		    goto usage;
		}
		++i; j = 0;
		while(argv[i][j]) {
		    if(!(isdigit(argv[i][j]))) {
			fprintf(stderr,
			     "\'%c\' not a digit\n", argv[i][j]);
			goto usage;
		    }
		    else j++;
		}
		sscanf(argv[i], "%d", &scale);
		break;
	      case 'i':
		inverse = 1;
		break;
	      case 'f':
		if((i+1) >= argc) {
		    fprintf(stderr,
			    "-s should be followed by a filename or \'-\'\n");
		    goto usage;
		}
		++i;
		if(strcmp(argv[i], "-") == 0) fout = stdout;
		else {
		    if((fout = fopen(argv[i], "w")) == NULL) {
			fprintf(stderr, "Couldn't open file %s for output\n",
				argv[i]);
			goto usage;
		    }
		}
		break;
	      case 'b':
		borders = 0;
		break;
	      default:
		goto usage;
		
	    }
	}
	else {
	  usage:
	    fprintf(stderr, "%s -i -b [-s scale] [-f filename]\n", argv[0]);
	    fprintf(stderr, 
		    "   -i prints screen hardcopy in inversevideo\n");
	    fprintf(stderr, 
		    "   -f <filename> redirects output to filename\n");
	    fprintf(stderr, 
		    "   -f - redirects output to stdout\n");
	    fprintf(stderr,
		    "   -b prints a window without borders included\n");
	    fprintf(stderr,
		    "   -s <scale> scales the window by <scale>\n");
	    fprintf(stderr,
		    "      if unspecified, image is autoscaled.\n");
	    exit(0);
	}
    }
    
    window = get_window();
    hardcopy_window(window, inverse, borders, scale, fout); 
}

get_window()
{
  XButtonEvent event;
  Cursor cursor;
  Window window;

  cursor = XCreateCursor(cross_width, cross_height, cross_bits,
			 cross_mask_bits, cross_x_hot, cross_y_hot,
			 BlackPixel, WhitePixel, GXcopy);

  while(1) {
    if(XGrabMouse(RootWindow, cursor,
		  ButtonPressed | ButtonReleased) != 0) break;
    sleep(1);
  }
  while(1) {
    if(XPending()) {
      XNextEvent(&event);
      if(event.type == ButtonPressed) {
	XUngrabMouse();
	goto done;
      }
    }
  }
 done:
  XUngrabMouse();
  window = event.subwindow;
  if(window == 0) return(RootWindow);
  else return(window);

}

/* 
  Utility function to print out various sundry facts to verify my calculations 
*/
get_size(window)
Window window;
{
    WindowInfo winfo;
    
    XQueryWindow(window, &winfo);
    printf("Buffer size %d\n", XYPixmapSize(winfo.width, winfo.height,
					    DisplayPlanes()));
    printf("DisplayPlanes = %d\n", DisplayPlanes());
    printf("Bitmapsize = %d\n", BitmapSize(winfo.width, winfo.height));
    printf("My calculation= %d\n", 
	   ((winfo.width + 15)/16) * winfo.height * 2);
     printf("Width = %d Height %d\n", winfo.width, winfo.height); /*  */

}
#endif

/*
 * Main entry point into this file: 
 * Takes 5 arguments: a window, an integer INVERSE, and an integer BORDERS
 *                    an integer SCALE and a FILE *FOUT.
 * if INVERSE == 1, it screen hardcopies the window in inverse video.
 * if BORDERS == 1, it includes the borders in its output.
 * if SCALE == 0, autoscales the window to fit onto the page.
 *    else sets the scale to be a constant. (2 is a good default value).
 * if FOUT == NULL, it automatically spools the output on the lw.
 *    else writes the output on the file * FOUT;
 */
hardcopy_window(window, inverse, borders, scale, fout)
Window window;
int inverse;
int borders;
int scale;
FILE *fout;
{
  char *cmd = DEFAULTPRINTER;
  int printing = 0;

  if(fout == NULL) {
      printing = 1;
      if((fout = popen(cmd, "w")) == NULL) {
	  fprintf(stderr, "Couldn't open lpr process %s\n",cmd);
	  exit(0);
      }  
  }
  do_hardcopy(window, fout, inverse, borders, scale);
  if(printing) pclose(fout);   
  XFeep(0);
}

do_hardcopy(window, fout, inverse, borders, scale)
Window window;
FILE *fout;
int inverse;
int borders;
int scale;
{
  WindowInfo winfo;
  int buffer_size;
  register unsigned char *buffer;
  int bytes,i,j;
  unsigned char c, c1;
  unsigned char buf[BUFSIZE];
  int count = 0;
  int format = 1;
  int width,iw;
  int lastword,wpad;
  unsigned short data, wmask;
  int real_width, real_height;
  XQueryWindow(window, &winfo);

  if(borders == 1) {
      real_width = winfo.width + (2 * winfo.bdrwidth);
      real_height = winfo.height + (2 * winfo.bdrwidth);
  }
  else {
      real_width = winfo.width;
      real_height = winfo.height;
  }
  if(DisplayPlanes() == 1) 
      buffer_size = XYPixmapSize(real_width, real_height, DisplayPlanes());
  else 
      buffer_size = BZPixmapSize(winfo.width, winfo.height);

  if(scale == 0) {
      scale = min((8*300)/real_width, (10*300)/real_height);
  }
  if((buffer = (unsigned char *)calloc (buffer_size, 1)) == NULL) {
    fprintf(stderr,"Couldn't allocate %d bytes of storage\r\n", buffer_size);
    exit(0);
  }
  if(DisplayPlanes() == 1) {
      if(borders == 1)  
	  XPixmapGetXY(RootWindow, winfo.x, winfo.y, 
		       real_width, real_height,
		       (unsigned short *) buffer);
      else 
	  XPixmapGetXY(window, 0, 0, winfo.width, winfo.height,
		       (unsigned short *) buffer);
  }
  else {
    XPixmapGetZ(window, 0, 0, real_width, real_height,
		(unsigned char *) buffer); 
  }

  fprintf(fout,"%s", prelude);

  /* calculate actual width in pixels of the dump */
  width = ((real_width+15)/16) * 16;
  if(width != real_width) {
      wpad = 16 - (width - real_width);
      for(i=0,wmask=0;i<wpad;i++) wmask |= (1<<i);
  }
  else wpad = 0;
  fprintf(fout, "%d %d %d %d ScreenDump\n",
	  width,
	  width,
	  real_height,
	  scale);
 
  iw = width/16;
  for(i=0; i < real_height;i++) {
      for(j= 0; j < iw; j++) {
	  unsigned char hi,lo;
	  data = *(unsigned short *)buffer++;
	  if(wpad != 0) {
	      if(j == (iw - 1)) {
		  data |= ~wmask;
	      }
	  }
	  /* now output the data */
	  hi = (data >> 8) & 0xff;
	  lo = data & 0xff;
	  hi = reverse[hi];
	  lo = reverse[lo];

	  if(inverse == 0) {
	      putc(hex[(lo>>4)&0x0F], fout);
	      putc(hex[lo&0x0F], fout);
	      putc(hex[(hi>>4)&0x0F], fout);
	      putc(hex[hi&0x0F], fout); 
	  }
	  else {
	      putc(rhex[(lo>>4)&0x0F], fout);
	      putc(rhex[lo&0x0F], fout);
	      putc(rhex[(hi>>4)&0x0F], fout);
	      putc(rhex[hi&0x0F], fout); 
	  }

      }
  }
  fprintf(fout, "\nshowpage\n");
}