[comp.lang.postscript] Printing Postscript on multiple sheets

golden@vlsi.ll.mit.edu (Richard Goldenberg) (08/04/89)

	Hello!  I am totally unexperienced with postscript and I have
what I hope will be a simple question.
	I have postscript files of large schematics produced with
viewlogic software.  Our postscript printer is an Apple LaserWriter 2
which only prints out 8.5x11 inch sheets.  If I print out the
schematic it either comes out squished onto one page which is
impossible to read, or if I play with a scale line at the top of the
file (something like 2 2 scale) I can get part of the schematic to be
legabile, but the rest is clipped off of the page.
	Is there a good way to alter a postscript file so it will
print on several sheets which can then be taped togather.

	Thanks in advance,
			Richard Goldenberg

Reply to:	golden@vlsi.ll.mit.edu

batcheldern@level.dec.com (Ned Batchelder) (08/16/89)

In general, there is no simple thingy that you can put at the top of any
PostScript file to get it to print on multiple pages. The main problem
is that you have to actually execute the file in its entirety for each
sheet that you need. The reason is that PostScript only works on a
single bitmap at once, 
and each operator puts its mark in only the current bitmap.

There is an example in the Blue Book that does this sort of thing (the
SALE sign), but it requires that the entire image be a single procedure.
If you can
arrange to put your whole drawing into a single executable PostScript
object, then you can use the Blue Book example. If you can't, then you
have to execute the file many times.

Ned Batchelder, Digital Equipment Corp., BatchelderN@Hannah.DEC.com

cplai@daisy.UUCP (Chung-Pang Lai) (08/19/89)

In article <4077@shlump.nac.dec.com> batcheldern@level.dec.com (Ned Batchelder) writes:
]In general, there is no simple thingy that you can put at the top of any
]PostScript file to get it to print on multiple pages. The main problem
]is that you have to actually execute the file in its entirety for each
]sheet that you need. The reason is that PostScript only works on a
]single bitmap at once, 
]and each operator puts its mark in only the current bitmap.
]
]There is an example in the Blue Book that does this sort of thing (the
]SALE sign), but it requires that the entire image be a single procedure.
]If you can
]arrange to put your whole drawing into a single executable PostScript
]object, then you can use the Blue Book example. If you can't, then you
]have to execute the file many times.

I've written a program to do just that awhile ago.  Since the blue book 
example does not work with any normal postscript file, I used the brute 
force approach.  I was hoping someone will come up with a better and cleaner
solution hence I held back from posting it until now.

=== cut into README ====

Poster - a generic PS scaling program

Known restrictions:
1. It only works with one-pager.  Don't try multiple page document with this.
2. Always produce 7.5"x10" tiles with crop marks and grid # at lower right.
3. Won't best fit to use minimum number of tiles by intellegent rotation.  
   (Enhancements are welcome, make sure you debug it before sending your fix 
   to me! )
4. It treats Landscape mode page the same way as Portrait page.  The printing
   is okay, but the width/height specification and grid numbering is rotated.
   (use it on a Landscape page and look at the grid #, you'll see what I mean)
5. It uses brute force, it outputs the whole file once for each tile produced.
   If you're printing a 1/4 Megabyte PS file on 4 tiles, you are creating a
   file over one megabyte in size and takes 4 times as long to print.
6. If no BoundingBox info is found, assumes 0 0 612 792.  Will not look for
   BoundingBox info beyond the first %%EndComments line (try to prevent getting
   the wrong one from other imbedded images.)
7. Only leave 1/2" inch border.  If you need extra spacing around the image,
   you should alter the BoundingBox info yourself.
8. Works well with all my PS images.  Yours?  Good luck! :-)

Compilation:
	cc -o poster poster.c

Usage:
	poster ps-file width height [ row col ]

	ps-file is your well behaving PS program.
	width and height is the output dimension expressed in inches.
		Since the tiles are 7.5"x10", using 15 20 will usually
		produces 2x2 (4) tiles.  Depends on the aspect ratio
		of the BoundingBox, unneeded tiles will not be output.
		The program will fit the image in the area specified
		without altering the aspect ratio.  Hence using 15 10000
		means you don't care about the height.  It will create
		as many tiles as needed to fit the width.  
	row and col is the grid that you want to output.  If not specified,
	all tiles are output in the same stream.  This is useful in case
	some tiles are demaged by paper jam.  Also if your PS program is
	big and sending it out multiple times in one job is impossible,
	then you can use a loop to output the tiles in separate jobs, e.g.

		foreach i (1 2 3 4)
			foreach j (1 2 3 4)
				poster ps-file 30 40 $i $j | lpr -Pps
			end
		end

	will print all 16 tiles (30"x40" output) to the printer.

Advice:
1	print the poster only at off hours.  
2	Enough overlapping of image is produced to assist easy fitting.
3	Paste every other tiles together in a "checker-board" manner (leave
	the borders intact, only cut out the corners near the crop marks) 
	and then clip the rest of the tiles along the crop marks and then paste 
	the tiles on the "checker-board".  The borders that you left behind in 
	the previous step now provide nice backing for the rest of the tiles.

=== cut into poster.c ====
/*
#  poster.c  -  a generic PS scaling program
#  This program outputs a PostScript page to a given size, create tiles if necessary.
#	each tile will be 7.5" by 10" with crop marks.
#
######################
#  CopyRight Notice: #
######################
#
#  Feel free to distribute or use, provided that nothing is changed.
#  Please send comments, bug fixes to pyramid!daisy!cplai
#
#  Author:  C.P. Lai  (All rights reserved)
#  Creation Date: Dec 22, 1987
#  Modification History:  
#		by C.P. Lai on Aug 18, 1989  for BoundingBox Handling.
#
#
#  Usage:  Poster ps-file width height [row col] | lpr -Pps
#	where ps-file is the file name of the PostScript page
#		width and height are the size in inches
#		row and col are option if only one tile is to be printed.
#		default is to print all tiles
*/

#include <stdio.h>
#include <bool.h>
#define BUFSIZE 512
/* tile size */
#define PAGEW 7.5
#define PAGEH 10.0

main (argc, argv)
int argc;
char *argv[];
{
char *pagename;
float width, height;
int row, col, llx, lly, urx, ury;

	row = col = 0;
	/* accepts only two command formats */
	if (!(argc == 4 || argc == 6)) usage(argv[0]);	
	pagename = argv[1];
	if (sscanf (argv[2], "%f", &width) != 1) usage(argv[0]);
	if (sscanf (argv[3], "%f", &height) != 1) usage(argv[0]);
	if (argc == 6) {
		if (sscanf (argv[4], "%d", &row) != 1) usage(argv[0]);
		if (sscanf (argv[5], "%d", &col) != 1) usage(argv[0]);
		if (row<1 || col<1) usage(argv[0]);
	}
	getBoundingBox (pagename, &llx, &lly, &urx, &ury);
	printposter (pagename, llx, lly, urx, ury, width, height, row, col);

	exit (0);
}

/**************************************/
/* output usage message and terminate */
/**************************************/
usage (prog_name)
char *prog_name;
{
	fprintf (stderr, "Usage:\t%s ps-file width height [row col]\n", prog_name);
	fprintf (stderr, "\twhere ps-file is the file name of the PostScript page\n");
	fprintf (stderr, "\twidth and height are the size in inches\n");
	fprintf (stderr, "\trow and col are option if only one tile is to be printed.\n");
	fprintf (stderr, "\tdefault is to print all tiles.\n");
	exit (1);
}

/*********************************************/
/* output the poster, create tiles if needed */
/*********************************************/
printposter (pagename, llx, lly, urx, ury, width, height, row, col)
char *pagename;
int llx, lly, urx, ury, row, col;
float width, height; 
{
int nrow, ncol, imagew, imageh; 
float xscale, yscale, scale;

	imagew = urx - llx;
	imageh = ury - lly;
	xscale = (width * 72) / imagew;
	yscale = (height * 72) / imageh;
	scale = (xscale<yscale)?xscale:yscale;

	/* adjust actual width and height based on aspect ratio */
	width = imagew * scale / 72;
	height = imageh * scale / 72;

	ncol = (int) (width / PAGEW);
	/* add one column if exceed by one user space unit */
	if ((width-(ncol*PAGEW))*72 > 1) { ncol++; }
	nrow = (int) (height / PAGEH);
	/* add one row if exceed by one user space unit */
	if ((height-(nrow*PAGEH))*72 > 1) { nrow++; }

	if (row>nrow || col>ncol) {
		fprintf (stderr, "The image does not fall on grid (%d,%d)\n", row, col);
		exit (1);
	}

	printf ("%%!PS\t");
	if (row == 0 || col == 0)
 		printf ("All grids ");
	else
		printf ("Grid (%d,%d) ", row, col);
	printf ("of poster %s in %dx%d tiles at %f scale\n", 
		pagename, nrow, ncol, scale);

	printprolog (scale, llx, lly);
	if (row == 0 || col == 0) {
		/* all tile */
		for (row = 1; row <= nrow; row++)
			for (col = 1; col <= ncol; col++)
				tile (pagename, row, col);
	} else tile (pagename, row, col);
	printf ("%% End of Poster %s\n", pagename);
	return;
}

/*******************************************************/
/* output PS prolog of the scaling and tiling routines */
/*******************************************************/
printprolog (scale, llx, lly)
float scale;
int llx, lly;
{
printf ("%%%%Creator: poster - a tiling program by C.P. Lai\n\n");

printf ("\
/cropmark	%% x y cropmark -\n\
{	%%def\n\
	/y exch def\n\
	/x exch def\n\
	gsave\n\
	0 y rmoveto\n\
	0 y 2 mul rlineto \n\
	gsave\n\
	1 setgray 3 setlinewidth stroke	%% paint background white to highlite mark\n\
	grestore\n\
	0 setgray 0 setlinewidth stroke	%% use thinnest line on device\n\
	grestore\n\
	x 0 rmoveto\n\
	x 2 mul 0 rlineto \n\
	gsave\n\
	1 setgray 3 setlinewidth stroke	%% paint background white to highlite mark\n\
	grestore\n\
	0 setgray 0 setlinewidth stroke	%% use thinnest line on device\n\
} bind def\n\n");

printf ("\
%% usage: 	row col tileprolog ps-code tilepilog\n\
%% these procedures output the tile specified by row & col\n\
/tileprolog\n\
{ 	%%def\n\
	gsave\n\
	/colcount exch def\n\
	/rowcount exch def\n\
	gsave\n\
	pagewidth colcount 1 sub mul neg\n\
	pageheight rowcount 1 sub mul neg\n\
	translate\n\
	sfactor dup scale\n\
	llx neg lly neg translate\n\
	tiledict begin\n\
} bind def\n\n");

printf("\
/tilepilog\n\
{	%%def\n\
	end %% of tiledict\n\
	grestore\n\
	%% output the crop marks\n\
	0 0 moveto\n\
	-9 -9 cropmark\n\
	0 pageheight moveto\n\
	-9 9 cropmark\n\
	pagewidth 0 moveto\n\
	9 -9 cropmark\n\
	pagewidth pageheight moveto\n\
	9 9 cropmark\n\n\
	1 setgray\n\
	18 -18 moveto\n\
	0 9 rlineto\n\
	72 0 rlineto\n\
	0 -9 rlineto\n\
	closepath\n\
	fill\n\n\
	0 setgray\n\
	18 -18 moveto\n\
	(Grid \\( ) show\n\
	rowcount strg cvs show\n\
	( , ) show\n\
	colcount strg cvs show\n\
	( \\)) show\n\n\
	showpage\n\
	grestore\n\
} bind def\n\n");

printf("\
/sfactor %f def\n\
/llx %d def\n\
/lly %d def\n\
/leftmargin 36 def\n\
/botmargin 36 def\n\
/pagewidth %d def\n\
/pageheight %d def\n\n\
/strg 10 string def\n\
/tiledict 250 dict def\n\
tiledict begin\n\
/showpage {} def	%% delay users showpage until cropmark is printed.\n\
end\n\
%% end of prolog\n\n", scale, llx, lly, (int) (PAGEW * 72), (int) (PAGEH * 72));

printf ("\
/Times-Roman findfont 9 scalefont setfont\n\
%% readjust the origin to coincides with the cropping mark\n\
leftmargin botmargin translate\n\n");
return;
}

/*****************************/
/* output one tile at a time */
/*****************************/
tile (pagename, row, col)
char *pagename;
int row, col;
{
printf ("%d %d tileprolog\n", row, col);
printf ("%% Start of included ps code\n%% vvvvvvvvvvvv\n");
printfile (pagename);
printf ("%% ^^^^^^^^^^^^\n%% End of included ps code\n");
printf ("tilepilog\n");
return;
}

/******************************/
/* copy the PS file to output */
/******************************/
printfile (filename)
char* filename;
{
char tmp_byte;

	if (freopen (filename, "r", stdin) == NULL) {
		fprintf (stderr, "fail to open file %s\n", filename);
		printf ("/systemdict /showpage get exec\n");
		exit (1);
	}
	while ((tmp_byte = getchar()) != EOF)
		putchar (tmp_byte);
	return;
}

/*********************************************/
/* extract BoundingBox info from the PS file */
/*********************************************/
getBoundingBox (filename, llxp, llyp, urxp, uryp)
char *filename;
int *llxp, *llyp, *urxp, *uryp;
{
char buf[BUFSIZE];
BOOL not_found;

	/* initialize to 0 0 612 792 for defaults */
	*llxp = *llyp = 0;
	*urxp = 612; *uryp = 792;

	if (freopen (filename, "r", stdin) == NULL) {
		fprintf (stderr, "fail to open file %s\n", filename);
		exit (1);
	}
	not_found = TRUE;
	while (not_found && (gets(buf) != NULL)) {
		/* cut short if not found */
		if (strncmp (buf, "%%EndComments", 13) == 0) {
			not_found = FALSE;
			break;
		}
		if (sscanf (buf, "%%%%BoundingBox: %d %d %d %d", llxp, llyp, urxp, uryp) == 4)
			not_found = FALSE;
	}
	return;
}
-- 
.signature under construction ...
{pyramid, osu-cis, uunet, killer}!daisy!cplai    C.P. Lai
cplai%daisy.UUCP@uunet.UU.NET   cplai%daisy@killer.DALLAS.TX.USA
Daisy Systems Corp, 700B Middlefield Road, Mtn View CA 94039.  (415)960-6961

cplai@daisy.UUCP (Chung-Pang Lai) (08/22/89)

The poster program that I posted last week can be easier modified for B-size
printer.  I changed the PAGEW to 10.0 and PAGEH to 16.0 and recompiled.  I
then sent the output to a QMS2200 printer.  The tiles came out nicely on
the 11"x17" paper.

The meat of the program is in the PostScript Prolog.  The C program is only
a front end to compute the scaling factor based on desired output size
and also perform the tedious work of outputting all tiles in one job.

You can extract the PS code easily by doing:
	poster dummy-ps-code 7.5 10 1 1 > poster.ps
You can then alter the scaling factor and grid number to output to anysize
you want one grid at a time using the same poster.ps file.  (i.e. The ps 
program can also benefit users who have no C compiler.  If there is enough 
requests, I can post the poster program in PostScript format.)

Question:  How big a scale can PostScript handle?  The poster program in
theory can prepare tiles for bill board size poster.  The only question
is that will the printer be able to print the outrageously scaled up tiles?

Correction:  I suggested to paste the tiles together in a checker-board
manner.  I found that if you clip the top and right edge along the crop
marks and then paste the tiles together in a "fish-scale" manner, your
task of aligning the tiles is much easier.

Happy printing!

-- 
.signature under construction ...
{pyramid, osu-cis, uunet, killer}!daisy!cplai    C.P. Lai
cplai%daisy.UUCP@uunet.UU.NET   cplai%daisy@killer.DALLAS.TX.USA
Daisy Systems Corp, 700B Middlefield Road, Mtn View CA 94039.  (415)960-6961