Two Up


There are occassions when you might want to print more than one page of a PostScript document on a piece of paper. For example, you may have a collection of slides for a presentation, and you may want to print them out in condensed form for a kind of digest hand-out. This kind of printing, where two pages are printed side-by-side on a piece of paper is called "two-up," for the two pages facing up. This idea generalizes readily to any number of pages (though, of course, legibility goes down quickly as the number of pages goes up). In its general form, it is called "n-up."

The PostScript

What is it that is necessary to print in two-up mode? First, we need to translate and rotate each page into the right location of the page, then we need to make sure that the page fits in the new area reserved for it (we will need to scale it down to about half its original size). If we place the two pages side by side, we will get proper two-up form.

The code I will present here will place the odd pages on the left (as your looking at the page in landscape orientation) and the even pages on the right. You could do it the other way around, if that makes more sense to you.

Here is the code we must wrap around the odd pages:

        gsave
         504 30 translate  % Position page in middle of region
         90 rotate         % Aim it in the right direction
         .5 .5 scale       % make it small enough
         % original page code here...
        grestore
And here is the code for the even pages:
        gsave
         504 426 translate % Position page in middle of region
         90 rotate         % Aim it in the right direction
         .5 .5 scale       % make it small enough
         % original page code here...
        grestore
Now, you will notice that I used some curious numbers in the translate command. The reason I chose these particular numbers was that I wanted to center each page in its half of the page. I knew I was going to scale by 0.5, so I computed how much whitespace was left and added in the appropriate fudge-factor to center the pages.

You may also notice that I wrap a gsave and a grestore around the page and the additional code? The reason for this is that each page must leave the state of the printer unchanged when it has been printed. If you permanently change the state, that state change will be in affect for all subsequent pages. By following this rule, you make the pages independent of order. Some print servers must shuffle page order in order to print the document correctly; since my pages are independent (at least as far as my code is concerned), they will print correctly.

The Hard Part

Now comes the hard part of recognizing where the pages begin. The technique is essentially the same as what we used for galley proofs, so I will spare you the logic here. Essentially, we will look for %%Page: comments. We will, however, need to keep track of whether the current page is an odd page or an even page and insert the correct translation code. Also, as before, we must be careful about inserting grestores before subsequent pages and before the %%Trailer or %%EOF comments.

Here is the PERL script to do the job:

#!/usr/local/bin/perl
$flag = 0;			# We have not yet found a page
$even = 0;			# First page is an odd page
$page = 1;			# Start at page #1
$pages = 1;			# Allow %%Pages comment
while (<>) {
    if (/^%%Pages:/ && $pages) {
	print "%%Pages: (atend)\n";
	$pages = 0;
    } elsif (/^%%Page:/) {      # We have found a page
	if ($flag) {
	    print "restore\n";	# restore if it isn't the first
	}
	$flag = 1;
	if ($even) {		# Translate for even pages
	    print "save\n";	# gsave
	    print "504 426 translate\n";
	    $even = 0;
	    $page++;
	} else {		# Translate for odd pages
	    printf("%%%%Page: %d %d\n", $page, $page);
	    print "save /showpage {} def\n";
	    print "504 30 translate\n";
	    $even = 1;
	}			# Code to rotate and shrink
	print "90 rotate .5 .5 scale\n";
    } elsif (/^%%Trail/) {	# Cleanup if a %%Trailer is found
	if ($flag) {
	    print "restore\n";
	}
	print $_;
	printf("%%%%Pages: %d\n", $page);
	$flag = 0;
    } elsif (/^%%EOF/) {	# Cleanup if an %%EOF is found
	if ($flag) {
	    print "restore\n";
	}
	print $_;
	$flag = 0;
    } else {
	print;
    }
}
Note the basic similarity with the script for the galley proofs. There are some additions, however. Because we are taking two pages and printing them on one page, we need to modify the page numbers. The %%Pages: comment specifies how many pages are in the document. If you specify "%%Pages: (atend)", you are specifying that you do not know the exact number of pages, but you will give the information later.

An additional complication is the use of save and restore rather than gsave grestore. These operators save the entire state of the printer and restore it just as gsave and grestore work with the graphics state. In fact, an implicit gsave is done by save; and an implicit grestore is done by restore. The reason these are used is so that I can redefine showpage to a do-nothing procedure (/showpage {} def) for the odd pages. This trick prevents the page from being ejected when the odd page does its end of page routines. Unfortunately, this trick only works if the document calls showpage by name. If the document bound showpage up or calls some of the lower level operators, this program would need to be more sophisticated.


Prev Funky Stuff Next