[comp.lang.postscript] showpage-layering

tinkelman@ccavax.camb.com (08/14/90)

A week or so ago, I responded to a comp.lang.postscript posting, saying
something like:

      ``Printing every other page of a PostScript document?  That
      shouldn't be hard.  I'll just write a showpage-replacement
      that'll eat every other page.  It should work with any (well
      almost any) PostScript file...''

(Me and my big mouth!)

So, I eventually got around to writing it.  It wasn't too hard, and it
worked on my own handwritten PostScript.  [Usual comments apply here about
debugging == `training it to work on the test data'.]  It worked on the
PostScript produced by the program (PSPRINT) we use to convert TeX DVI files
into PostScript.  It even layered on top of DEC's ScriptPrinter software,
eg. using PARAM=NUMBER_UP=2 (though the utility of this is questionable). 
So far so good.  I even mailed it out to a few people who had sent me
mail-requests.  So much for the good news.

Now for the bad news.  It worked with almost no other program-produced
PostScript that I've tried since.  I looked around our system for PostScript
files that had been produced by various packages, some of which we don't run
here.  Without exception, they have all implemented `next page' in a way
that thwarts many types of showpage-layering.  I've appended some excerpts.  

I'll also append my odd.ps file (as it's pretty short).  But beware, from my
testing it very well might not work with your PostScript.

Now my request to the net:  Am I doing something dumb?  Are the programs
that produced the attached PostScript doing something bad?  Do the Adobe
specs/guidelines cover this area explicitly enough to say?  And, if you 
do happen to try the attached every-other-page code, I would appreciate 
mail reporting the results.

Thanks.
--
Bob Tinkelman, Cambridge Computer Associates, Inc., 212-425-5830              
bob@camb.com  or ...!{uupsi,uunet}!camb.com!bob      
--------------------------------------------------------------------------------
%%%%%%%%%% The following lines excerpted from eroff output   %%%%%%%%%%
%%%%%%%%%% The sequence save-...-showpage-restore defeats    %%%%%%%%%%
%%%%%%%%%% any layered showpage which wants to retain state. %%%%%%%%%%

%%Creator: user@host with Eroff/eps release 2.2C
%[lots of stuff deleted]
%[The following is at the page break between pages 1 and 2; others are similar]
showpage
PageState8620 restore
%%PageFonts: Times-Bold Times-Roman Helvetica Symbol
%%Page: label 2
%%PageFonts: (atend)
/PageState8620 save def

%%%%%%%%%% The following lines excerpted from psprint output %%%%%%%%%%
%%%%%%%%%% The sequence showpage-save-...-restore allows a   %%%%%%%%%%
%%%%%%%%%% layering of a showpage with retained state, but   %%%%%%%%%%
%%%%%%%%%% the `initgraphics' seems scary.                   %%%%%%%%%%
/@bop1                                % begin setting DVI page n
{ pop                                 % throw away page number
  initgraphics                        % start with a clean slate
  mtrx setmatrix                      % switch to our TeX coordinate system
  /prepageVM save def                 % save state of VM at start of page
} def
/@eop                                 % end DVI page n
{ pop                                 % throw away page number
  prepageVM restore                   % restore VM to state at start of page
  showpage
} def
%[lots of stuff deleted]
@eop
2 @bop0
2 @bop1

%%%%%%%%%% The following lines excerpted from softquad troff output %%%%%%%%%%
%%%%%%%%%% The gsave-showpage-grestore is less harmful than the     %%%%%%%%%%
%%%%%%%%%% same sequence with save/restore, but it still seems to   %%%%%%%%%%
%%%%%%%%%% disallow showpage-layering to things like 2-up, where    %%%%%%%%%% 
%%%%%%%%%% you need to do things like define a new clip path...     %%%%%%%%%% 
%%Creator: SoftQuad Troff
% @(#)preamble	1.37 88/03/18 Copyright 1986, 1987, 1988 SoftQuad Inc.
%[lots of stuff deleted]
/endofpage	{
	/#copies
	exch def
	gsave showpage grestore
}def
%[lots of stuff deleted]
1791(Cambridge)s
2006(Computer)s
1 endofpage
%%PageFonts: Times-Roman
sqr
%%Page: 2 2
%%PageFonts: (atend)
sqs
2 2 1 startofpage

%%%%%%%%%% The following lines excerpted from wordperfect output. %%%%%%%%%%
%%%%%%%%%% This is a showpage-restore-save-... sequence, which    %%%%%%%%%%
%%%%%%%%%% (again) kills layered showpages which retain state.    %%%%%%%%%%
/_bp	{save 2 setmiterlimit .06 .06 scale 0 0 moveto} bdef

/_ep	{showpage restore 0 0 moveto} bdef

%[lots of stuff deleted]
_u _ep
 _bp /Times-RomanR 507 _ff
 0 13200
10200 _ornt 3954 11712 _m
 (AGREEMENT)_S 62 _rm (FOR)_S 62 _rm (SERVICES)_S 3693 11543 _m

%%%%%%%%%%%%% And finally, the following was my `odd pages' try. %%%%%%%%%%%%%
%%%%%%%%%%%%% (You need to change only one line to make even.ps) %%%%%%%%%%%%%
%---------------------------------- CUT HERE ----------------------------------
%  Odd.ps
%
%  Define a replacement showpage module which prints every other page.
%  It is written so it can be `nested' with other showpage replacements.
%
%  Revision history:
%	12-Aug-1990 Original Version: Bob Tinkelman <bob@camb.com>
%
%  Limitations:
%	Cannot be used `after' (layered on top of) other `showpage
%	replacements' which paint a background for the next page.
%	(eg DRAFT).  For cases like this, it must be used `before'.
%
%  Logic overview: On alternate invocations, it will:
%	(1) Invoke the `real' showpage 
%	(2) Erase everything written since the previous call.

/showpage 

{ 0 begin		% Use local dictionary (inserted for `0' below)

%   page_no 2 mod 0 eq	% See if even; if so, need a real showpage
    page_no 2 mod 1 eq	% See if odd;  if so, need a real showpage
    {
	page_no 0 ne {s} if	  % Do a real showpage (unless page 0)
	gsave			  % Save graphics state for grestore after
     }				  % our `client' has drawn page we'll erase
    { 
	page_no 0 ne {grestore} if% Restore saved graphics state
        c			  % Set current path to clippath
        1 currentgray sub setgray %    Reverse current gray
        fill			  %    Fill (erase) the clippath bounded region
        1 currentgray sub setgray %    Set the gray back to the way it was
     }
    ifelse
    /page_no page_no 1 add def	% Increment the page number
  end
} bind dup 0		% Stack: /showpage {...} {...} 0

% Make new showpages's local dict

4 dict dup begin		% Stack: /showpage {...} {...} 0 showpage-dict
  put				% Stack: /showpage {...}
  /page_no 0 def		% Used for even/odd check and 1st page check
  /c /clippath load def		% Get current definition of clippath
  /s /showpage load def		% Get definition of current showpage
end				% Stack: /showpage {...}

% Define the new showpage and `do page zero'

def				% Define new showpage.
showpage
%---------------------------------- CUT HERE ----------------------------------

heiney@wsl.dec.com (Bob Heiney) (08/14/90)

The best way to achieve what you want would be to exploit the page
comments that
a good PostScript file gives you.  By putting "%!-PS-Adobe-2.0" (or
other version #)
at the top, documents claim that they will follow a set of conventions
that would
easily give you the tools you need.  You can get "struct.ps" from the
Adobe file
server if you want to see what's in the lastest version of the conventions.

Unfortunately, in the words of Brian Reid (who came up with the original
commenting
conventions):

	"They're one of those things that everyone agrees is a great idea, easy
	 easy to do, etc. -- but no one follows them."

I foolishly whipped up an awk script to do segmentation of large files. 
I was at
Berkeley at the time, and was constantly annoyed by people printing HUGE
ps files.
My 1000 byte job was always next in the queue after one of those monsters.  So,
since I occasionally need to print big ps files too, I decided to write
a segmenter
that would split the ps file into n page chunks.  That way no one would
curse my
name when I printed large documents.

Unfortunately, the absolute worst thing about this whole matter is that
while few
(I can count them on one hand... :-( ) applications *actually* follow
the document
structuring (a.k.a. commenting) conventions, almost *ALL* claim they do
by putting "%!-PS-Adobe-2.0" at the top of their code.  This the
requires artificial intelligence
from the paging software to have it figure out what's going on.  The
most common
problem is that pages are not independent like they're supposed to be. 
On the Mac,
for example, the first time a font is used, a revectoring is done.  Since this
revectoring is crucial, and only done once, if you don't execute the page that
the revectoring occurs on, you'll get an error.

I wish I had some more helpful advice, but since messing around with
showpage is
bound to be hairy, and since people don't follow the commenting
conventions, I think
this is one of those "fix it by hand when you really have to do it" kind
of problems.  That or you can write really smart software.

In the end, I think the best solution would be for all applications to either
generate properly structured code, or stop claiming that they are
compliant when
they're not.

Bob Heiney
Graphics Consultant

heiney@wsl.dec.com (Bob Heiney) (08/15/90)

First, sorry to all (including me) who suffered through miswrapped
text on my previous posting.  I didn't realize my window was more
than 80 columns wide.

Anyway, I looked at version 2.1 of the Document Structuring Conventions
from Adobe and found the following relevant quotes:

(Section 4, "Conforming Files", p. 5)

	... If these structuring conventions are to be employed, care
	should be taken to use them correctly and in accordance with
	their intended goals.  Failure to do so may result in
	unexpected behavior of document files within some document
	handling systems.

(p. 6)

	If the showpage operator is used with save and restore, the
	showpage operator shall occur after the page-level restore
	operation.  The motivation for this is to be able to handily
	redefine the showpage operator to have side effects in the
	printer VM such as maintaining page counts for printing n-up
	copies on a single sheet of paper.  If the showpage is executed
	within the confines of a page-level save/restore, then attempts
	to redefine showpage to perform extra operations will not
	work as intended.

Thus, the original poster can see that his software *ought* to work.
If only every file that claimed to be well-behaved actually was.

Bob Heiney
Graphics Consultant
heiney@dec.com

tinkelman@ccavax.camb.com (08/16/90)

A number of people have responded to my question about showpage-layering, 
both via news and via mail.  In article <1990Aug15.163702.15828@wrl.dec.com>, 
heiney@wsl.dec.com (Bob Heiney) wrote:

> Thus, the original poster can see that his software *ought* to work.
> If only every file that claimed to be well-behaved actually was.
 
and he quoted version 2.1 of Adobe's Document Structuring Conventions 

> 	If the showpage operator is used with save and restore, the
> 	showpage operator shall occur after the page-level restore
> 	operation.  The motivation for this is to be able to handily
> 	redefine the showpage operator to have side effects in the
> 	printer VM such as maintaining page counts for printing n-up
> 	copies on a single sheet of paper.  

An interesting point was made in an e-mail response that I received from
James Clark <uupsi!relay.EU.net!jclark!jjc>

> I tried your code with two drivers I have written (dvitops and groff),
> and it worked fine with both.  But I had this sort of showpage-layering 
> in mind when I wrote them.
> 
> The document structuring conventions (version 2.1) specifically forbid
> enclosing showpage within save/restore; but I can't find anything that
> forbids enclosing it with gsave/grestore.

The reason he mentioned gsave/grestore, is that I'd used gsave and grestore 
in order to obtain (at the end of the page I wanted to skip) the clippath 
that had been in effect (at the start of that page).  In fact, in one of 
the cases where my routine broke, it was because the program generating the 
PostScript document had enclosed showpages within gsave-grestore pairs!
[I guess I could make the usually-true assumption that clippaths will be
rectangles and save the bounding box values at `start of page', but it
feels kludgy.]

Incidentally, James Clark also pointed something out to me that I thought I
should pass along:

> I think the technique of writing a dictionary into a procedure after
> definining it is no longer thought to be advisable because it doesn't
> work when setpacking is true.

A number of other people responded with helpful suggestions about getting
around specific problems.  For example, Brian Thomson <thomson@hub.toronto.edu>
suggested a clever trick to preserve variables across a restore:

> eg. if you want to save the value of "x" across a restore,
> 
> /restore { x exch restore /x exch def } bind def

Of course, this makes a lot of assumptions about the environment, such as
you've chosen a unique name `x' that will not conflict with any body else's.

I would like to thank all who responded, both in news and mail.
-- 
Bob Tinkelman, Cambridge Computer Associates, Inc., 212-425-5830              
bob@camb.com  or ...!{uupsi,uunet}!camb.com!bob