This document has a standard, validated CSS2 stylesheet, which your browser does not seem to display properly. In a browser supporting web standards, this table of contents would be fixed at the side of the page for easy reference.
anastigmatix home
PageNest r. 0.1 21, available from 12 October 2005 to
22 November 2005, could be tripped by a bug in some Adobe interpreters
where upath
can cause a nocurrentpoint
error
contrary to Adobe's documentation. Revision 0.1 24 works around the
problem.
Since language level 2, the PostScript language has allowed
BeginPage
and EndPage
handlers: a document can
supply procedures that will be called back by the interpreter at the time of
setting up the initial graphics state for a page and at the time of
showpage
, respectively. They offer a clear and concise way to set
up running copy or watermarks, to select only certain pages from a document,
to apply selective margin shifts, to impose multiple virtual pages on a
physical page, or do anything else that can be done by PostScript code
hooked into the setup and showing of pages.
The trouble was, while BeginPage
and EndPage
are easy to use for any one of those purposes, they were difficult to
use for any combination of them at the same time. Adobe only allowed for
a single BeginPage
and a single EndPage
at a
time. If you wanted to print a document 4-up on physical pages, and
print only the recto physical pages, and add a company watermark
to each of the four virtual pages on each physical page, you could write
hairy custom BeginPage
and EndPage
procedures
that would do all that; what you could not do was grow a collection of
simple BeginPage
and EndPage
handlers that do
those things individually, and combine them as in:
{ 2 mod 0 eq } bind dup select InstallBeginEndPage 4up InstallBeginEndPage AcmeWatermark InstallBeginEndPage
Now you can. The flexibility of a stack of handlers is apparent when you start playing with the order. Would it look better to have the company watermark once across each physical page instead of on each of the 4-up virtual pages?
{ 2 mod 0 eq } bind dup select InstallBeginEndPage AcmeWatermark InstallBeginEndPage 4up InstallBeginEndPage
Maybe the intent was really to get every other page of the original document, not every other physical page of the 4-up result:
AcmeWatermark InstallBeginEndPage 4up InstallBeginEndPage { 2 mod 0 eq } bind dup select InstallBeginEndPage
Existing PostScript files sometimes need to be manipulated to select
particular pages, adapt to different dimensions, or create multipage
impositions for printing, either because they were created by applications
that did not offer those options, or the applications belonged to somebody
else, or just because to learn a particular application's way of doing
something can be more trouble than to simply produce the output in default
style and then use a familiar tool that does the trick. An excellent set of
tools I have used for many years myself can be found in Angus Duggan's
PSUtils.
The most general is
pstops,
which can select, reorder, shift, scale, and combine pages, for example
to create arbitrary impositions. It generalizes the simpler tools
psselect
, psbook
, and psnup
;
psbook
, for example, produces a fixed page ordering for
one possible quarto imposition, but pstops
can do that or
any other.
Those tools are programs written in conventional languages that read PostScript files and write rearranged versions of them without interpreting the PostScript code; they depend on Document Structuring Convention comments in the file, and shuffle the code between them as so much opaque text, a technique that often works quite well for well-formed files that do what their DSC comments say they do.
A technique implemented in PostScript naturally does not rely on what the
comments say, but hooks what the PostScript code actually does. For
manipulating existing PostScript, a solution based on PageNest
,
which may only prepend a prolog to the original file, may work for a more
general class of input files than DSC-based file rewriting techniques.
There are tradeoffs, and no need to use only one type of solution or the
other. A PageNest
-based solution for selecting certain pages
from a PostScript file will transmit and interpret the entire file, discarding
the unwanted pages; a tool like Duggan's psselect
will create
a new file for transmission containing only the wanted pages, saving
bandwidth and interpreter time at the expense of an extra step in host
processing, and possibly disk space. Arranging multiple page images per
sheet is very easily done with PageNest
, but shuffling the
file's pages into the proper order for folding into signatures may be
easiest with a tool like psselect
, as in general storage space
will be required to buffer pages, and only some PostScript interpreters
have attached disks. Hybrid approaches are possible: a tool can achieve part
of its effect through rewriting of a PostScript file and part by emitting
the rewritten file with a prolog of PostScript resources to do the rest;
PageNest
can be used in that way too.
For
direct PostScript—PostScript as the
original form of a document
so that one form is both editable and printable—PageNest
can offer a very concise way to set up such features as running heads,
watermarks, multiple columns, or step-and-repeat, without adding complexity
to the underlying procedures used for setting text. Nested handlers
responsible for page-level elements and sub-page regions can be a simple
model for page layout.
These examples show PageNest
—and some other resources,
like
Markup
,
MetaPre
, and
Order
—in use.
One produces serially-numbered tickets for an obscure appearance by the
Second Greatest Show on Earth (if there really is such a thing, please do
not use these tickets to attend).
One produces address labels and one
addresses post cards; both consist of a short file that can simply be stuck
at the beginning of a file of addresses in a simple format, and the resulting
file directly printed.
You may view the examples either in a PostScript previewer or in a plain text editor, according as you would rather see what comes out, or read how it is done. Each example comes in two flavors. Each self-contained file has all the resources it needs included in its prolog, so it will work without any prearrangement on any PostScript printer or viewer.
If you did this sort of thing a lot, you would probably download or save
the resources in advance where your printer or viewer could find them, and
then the bare files would work just as is. The
reference
section describes downloading and installing. The self-contained
files were generated from the bare files by a simple script that
copies in the resources where the %%IncludeResource
comments
are.
Bare file | Self-contained file | ||
---|---|---|---|
Admission tickets |
PostScript view text view |
PostScript view text view |
PDF view |
Address labels |
PostScript view text view |
PostScript view text view |
PDF view |
Postcard addressing |
PostScript view text view |
PostScript view text view |
PDF view |
As BeginPage
and EndPage
procedures are defined
as the PostScript Language Reference Manual, a
BeginPage
procedure is passed a single integer
on the stack, the page number, and consumes it, returning nothing;
EndPage
consumes two integers, page number and a reason code, and returns
a boolean determining what the PostScript interpreter should do next.
Without nesting, the interpreter calls BeginPage
as its last
action on any showpage
or setpagedevice
; the
number on the stack is the number of the page being begun. (In fact it is
the count of showpage
s since the last setpagedevice
,
so the first page is numbered zero; this is a bit confusing as it will
probably not match the document numbering, and it makes recto pages even
and verso pages odd, but one gets used to it.) The interpreter calls
EndPage
as its first action on any
showpage
, before marking the medium, passing the same page
number that was given last to BeginPage
, and a reason code
of zero. If EndPage
returns true, the interpreter marks
the medium, erases raster memory, and restores the initial graphics
state; if false, the interpreter makes no marks and does not erase
raster memory, but does restore the initial graphics state. In either case,
as always, the showpage
finishes by calling
BeginPage
with the next page number. Effects such as four-up
printing could be achieved with an EndPage
procedure that
returns true only every fourth page, and a BeginPage
that cycles through four regions on the page as it sets up the initial
transformation matrix.
The interpreter calls EndPage
with the page number and reason
code 2 at the end of a job, or as the first action of a
setpagedevice
. Ordinarily, an EndPage
procedure
would return false in that case, though something like a four-up
procedure would want to return true if there has been at least one
showpage
since the last physical page was produced, to emit
the final partial page. The only other defined reason code, 1, is associated
with copypage
, which is now deprecated and in level 3 has the
same meaning as showpage
.
How the protocol extends to nested BeginPage
is straightforward.
At every time the interpreter normally calls BeginPage
,
the effect of executing all installed BeginPage
procedures, in order from the first installed to the most recently, is needed.
The reason is that the interpreter has always executed
initgraphics
, creating the initial default user space.
Each BeginPage
procedure inherits a user space and may transform
it before the next, more recently installed, sees it, so re-executing them
in sequence will establish the proper graphics state. In practice,
PageNest
caches effects on the graphics state, as described
below, to avoid actually executing any BeginPage
more than once
between calls of its corresponding EndPage
.
The page number passed to the topmost BeginPage
procedure is
zero just after it has been pushed, increments after each interpreter
callback of EndPage
with a reason code other than 2,
and resets to zero after any EndPage
callback with reason
code 2 (device deactivation).
The page number passed to
each older/outer BeginPage
is the count of times its
next inner neighbor's EndPage
has returned true.
When a topmost handler ceases to be topmost, because another has been
pushed, its current page count is retained.
PageNest
invokes each BeginPage
procedure with
a clean operand stack containing only the page number. An ordinary
BeginPage
procedure will consume the page number and leave
nothing on the stack, but PageNest
allows the procedure to
leave the stack nonempty. Anything left on the operand stack when a
BeginPage
procedure completes will be cleared from the stack
and saved for the corresponding EndPage
procedure to see later.
The extension to EndPage
is more interesting. When the
interpreter calls EndPage
, PageNest
executes the
known EndPage
handlers, this time in the order from most recent
to outermost. However, it stops as soon as one returns false. The page
number passed to an EndPage
handler is the same number most
recently passed to its corresponding BeginPage
handler, and is
then incremented if the handler is topmost or the handler above returned
true. In other words,
when a handler h returns true, the
page number passed to its next outer neighbor h' is the count of times
h has returned true, not including this time.
Only if all EndPage
handlers return
true will true be returned to the PostScript interpreter,
causing a physical page to be marked.
An exception applies when the reason code is 2. Because the
device is being deactivated, all EndPage
handlers are executed
in sequence regardless of the values returned, and true is returned
to the PostScript interpreter if any handler returned true.
Otherwise, a final partial four-up page, for example, might be lost if
an outer EndPage
handler simply returned false for
device deactivation, as many do. The page count for the topmost handler
is reset to zero after a call to EndPage
with reason code 2.
PageNest
invokes each EndPage
procedure with a
clean operand stack with the page number and reason code on top; if
there were items left on the stack by the corresponding
BeginPage
, they are on the stack with the page number and
reason code pushed just above them. The lowest item on the stack, accessible
with count 1 sub index
, is always
the cached partial graphics state
captured after BeginPage
, in the form of a procedure that can
be executed to reinitialize the state; this is explained below.
The top of the stack
must contain a boolean when an EndPage
procedure completes;
anything below it on the stack at that point is discarded, so it is
not necessary for an EndPage
handler to explicitly consume
any of the cached items from BeginPage
. An EndPage
handler can explicitly control the page counter it shares with its
corresponding BeginPage
handler, by pushing an integer on the
stack above its boolean result. The integer becomes the next value to be
passed to BeginPage
.
Something like a four-up handler can be constructed by defining an
EndPage
handler that returns true only on every
fourth call. With such a handler on the stack, any
outer BeginPage
handlers, below it on the stack, are
conceptually executed
more than once with the same page number. That would be no problem for
BeginPage
handlers that only set graphics state, but could have
unexpected results with a handler that makes marks on the page or has other
side effects. For that reason, PageNest
executes any
BeginPage
handler only once between calls of the corresponding
EndPage
handler, and on subsequent callbacks simply resets
the graphics state with values cached after BeginPage
returned.
Any time the corresponding EndPage
executes, regardless of the
value it returns, the cached result of the corresponding
BeginPage
is invalidated so the next BeginPage
callback will execute the procedure.
PageNest
does not cache the entire graphics state after
executing BeginPage
, but only exactly those parameters
that are reset by initgraphics
as defined in the PostScript
Language Reference Manual: CTM, clipping path, current path, color space
and color, linewidth, linecap, linejoin, miterlimit, and dash pattern.
The effect can be defined this way: if the handler stack is
(BP0,EP0)...(BPj,EPj),(BPk,EPk)...(BPn,EPn) with (BPn,EPn) pushed last, and a showpage
is executed where
EPk returns false so that
EPj...EP0 are not executed,
then BP0...BPj are not executed,
BPk...BPn are executed, and the initial
graphics state seen by BPk is as if the
initgraphics
performed by showpage
applied values
established by BPj rather than defaults for the physical
device.
BeginPage
handlers should not use outlines from a protected
font to establish either a current path or a clipping path,
because PageNest
's caching mechanism will fail in that case.
Adobe's specification for the PostScript language requires that the
interpreter-managed page counter reset to zero on every change of page
device or use of the setpagedevice
operator. Ghostscript
has a different behavior, which is documented in
Ghostscript
bug #688213, currently tagged as WONTFIX, so Ghostscript and PostScript
are resolved to be different languages on this point. To facilitate writing
BeginPage
and EndPage
handlers with predictable
behavior on both platforms, PageNest
ignores the interpreter's
page counter and manages its own, according to a simple rule. The counter
for the topmost handler resets to zero after any
EndPage
with reason code 2; this is the Adobe-specified
behavior. Counters below the topmost do not automatically reset, a behavior
closer to Ghostscript and retained for compatibility with earlier versions
of PageNest
(where the interpreter's page count was passed to
the topmost handler, and PageNest
maintained the counts for other
handlers on the stack). However, PageNest
allows a handler to
obtain strict PostScript or Ghostscript counter behavior in all cases by a
slight modification to its EndPage
procedure.
EndPage
can return an integer that becomes the next value of
the counter. Strict PostScript behavior is obtained by returning zero on
any call with reason code 2; Ghostscript behavior by returning the current
counter value on any call with reason code 2. Other counter
behaviors can be obtained using the same mechanism.
When using BeginPage
and EndPage
handlers to
rearrange or reposition PostScript generated by applications, you are
likely before long to run into some PostScript file that in some way or
other refuses to be manipulated. Some applications generate PostScript
that falls short of Adobe's recommendations for well-behaved page
descriptions, and more or less clever workarounds can be needed.
Many applications generate PostScript that uses a
save
/restore
pair around each page.
Here are two ways to do that:
The right way | The wrong way |
---|---|
save ... restore showpage |
save ... showpage restore |
If you've got a PostScript file that was generated the wrong way, then
it just won't matter how your BeginPage
procedure sets up
the graphics state—except for the first time—because it does
so as the last step of showpage
and no sooner is it done than
the restore
comes along and reinstates the graphics state of
the previous page.
To use PageNest
successfully with such a file, execute
InstallRestoreHack
as
part of document setup. This tells PageNest
to defer any
showpage
that comes at a deeper level of save
nesting than the BeginPage
call that began the page, and
installs a wrapper for restore
that will execute
showpage
if it is deferred and pending, upon restoring to
the correct save
level.
PostScript pages generated from PDF documents by Acrobat or Adobe Reader
can seem to have minds of their own, defying any translation or scaling
you set up in advance. What they are doing is centering themselves in the
rectangle that bounds the current clipping path
(clippath pathbbox
) and scaling themselves to just fit.
If you do not need to crop them, you can get them where you want them if,
as part of page setup, you establish a clipping path with aspect ratio
matching the page image and located where you want it. If you want the
image cropped, there is nothing for it but to cripple the AS
procedure that has been generated into the file. One easy way that works
in level 3 PostScript is to include an
IdiomSet resource that zaps
out the procedure by idiom recognition. Another approach is to define a
version of clippath
that lies when AS
calls it;
it can tell because a dictionary containing Pllx, Plly, Purx, Pury,
Dllx, Dlly, Durx,
and Dury
will be on the lookup stack.
Versions of Ghostscript before version
8.31
fail to process device deactivation, and do not invoke EndPage
with reason code 2, when normally exiting at the end of a job. This is
bug #687557
and can cause, for example, a last incomplete n-up page from the
tile
handler to be lost. If upgrading
Ghostscript is not an option, deactivation can be forced by arranging to
execute setpagedevice
(even with an empty dictionary:
0 dict setpagedevice
) before exiting at the end of a job.
This issue is now discussed in
Page-counter
management, describing how PageNest
provides predictable
under Ghostscript and PostScript.
PageNest
is a ProcSet
resource.
To make it available to your own code, include in the setup section of
your file:
/net.anastigmatix.PageNest /ProcSet findresource
The findresource
will succeed, leaving a dictionary on the
operand stack, if you have made
the
PageNest
resource file [download]
available in any of these ways:
findresource
(which belongs in the
setup section)%%DocumentNeededResources
and
%%IncludeResource
DSC comments, you include these comments at
the right position in your file to specify that it needs
net.anastigmatix.PageNest, your document manager software is configured to
automatically insert needed resources in files being printed, and you have
put the PageNest resource file where your document manager can find
it.
PageNest
relies on another resource,
MetaPre
.
You will need that file also. If you use the first method, you should
include both files in the prolog of your document, MetaPre
first. The other methods should Just Work as long as both files are where
they need to be. In any case, your document only needs the single
findresource
line shown above. A
findresource
for MetaPre
is not needed unless you
also use MetaPre
features in your own document. You pay no
penalty to do so, as the resource must be there anyway.
The resource files are in a compact form. That is for efficiency, not to keep you from viewing them; there is a script for that on the resource packaging page.
The PageNest
dictionary may be placed on the lookup stack (with
begin
) for convenient access to the definitions in it, without
the bother of get
and exec
. The dictionary is
read-only, so before creating any
definitions, you will want either userdict begin
or your
own dict begin
so that you have a writable dictionary on top
of the dictionary stack.
This section describes the contents of the read-only dictionary that is
returned by /net.anastigmatix.PageNest /ProcSet findresource
.
The dictionary contains three procedures for managing the stack of handlers.
passes dict, a setpagedevice parameter dictionary, to
setpagedevice, with special treatment for BeginPage and
EndPage entries if present. BeginPage and EndPage
entries are pushed on PageNest
's own stack, on top of any
pushed by earlier uses of InstallBeginEndPage. They are removed
from the dictionary before it is passed on to setpagedevice,
which handles normally any other parameters that may be present. On the
first use, any existing BeginPage/EndPage handlers
returned by currentpagedevice are placed on PageNest
's
stack—before those in dict—and
PageNest
's own handlers are placed in the dictionary to
register them with setpagedevice.
BeginPage and EndPage handlers are always pushed in pairs. If dict includes only one, a default procedure is pushed for the other. As described in the PostScript Language Reference Manual, the default for BeginPage has no effect but to consume its operand, and the default EndPage consumes its operands and produces true for reason codes 0 or 1, false for 2.
Every use of InstallBeginEndPage results in a setpagedevice. The setpagedevice will call every currently registered EndPage handler, not including the new one from dict, in order from most recent to oldest, with a reason code of 2 (device deactivation). It will then execute erasepage and initgraphics, erasing the entire current physical page, and call every registered BeginPage handler, including the new one, in order from oldest to most recent.
Because InstallBeginEndPage calls setpagedevice and so
inevitably affects the entire physical page, it is useful for setting
up running elements, watermarks, and impositions either at the start
of a job or at places known to be physical page breaks. For the more
general problem of defining arbitrary sub-“pages” on
individual pages, or of nesting jobs that use PageNest
,
an alternative PushBeginEndPage is provided that does not call
setpagedevice.
The handler stack is subject to save and restore. If restore is executed and a handler has been added with InstallBeginEndPage since the corresponding save, the interpreter will see a change of page device and perform an EndPage callback with reason code 2 before popping the handler. This does not happen if the handler was added with PushBeginEndPage instead (unless some pagedevice update was also made since the corresponding save).
As for InstallBeginEndPage except that setpagedevice is
never called, dict must contain no entries other than
BeginPage and EndPage, and the operation fails if
InstallBeginEndPage has not been called at least once to
activate PageNest
itself. After installing the new
handlers, executes initgraphics and then the stack of
BeginPage handlers in the usual way; the usual effect will
be that the new BeginPage handler executes in the restored
graphics environment set up by the previously topmost. No EndPage
handler is called and no page counter or page content is disturbed.
The handler stack is subject to save and restore. If restore is executed and a handler has been added with PushBeginEndPage since the corresponding save, the handler is simply popped and its EndPage is not executed (unless some pagedevice update was also made since the corresponding save). This behavior differs from that of InstallBeginEndPage and could lead to surprises. PopBeginEndPage can be used to pop the topmost handler and ensure that its EndPage is executed.
removes the most recent BeginPage/EndPage handler pair
from PageNest
's stack. Does not execute
setpagedevice. Executes the popped EndPage handler with
a reason code of 2 (device deactivation), as if by
setpagedevice and, if that handler returns true, executes
showpage, causing the remaining stack of handlers to be executed
with reason code 0 in the usual way.
Four procedures conveniently construct handler dictionaries that can be passed to InstallBeginEndPage.
constructs a dictionary containing a do-nothing BeginPage handler. While it will have no other effect, if pushed as the last handler on the stack, it will ensure that the handler below it sees a steadily cumulative page count rather than one that resets to zero with each use of setpagedevice.
constructs a dictionary containing BeginPage and EndPage handlers to select pages for printing based on the procedures image and advance. Each procedure is executed with a page number on the stack and expected to consume the number and produce a boolean. BeginPage calls image to determine if the page should be imaged at all; if false, BeginPage sets a degenerate clipping path for the page. EndPage calls advance to determine if a page should have been “produced”; if false, EndPage returns false to suppress the page.
When both image and advance return true, a normal page is produced; when both return false, a page is suppressed entirely. When image returns false and advance returns true, a blank page is produced at that position. When image returns true and advance false, the page and the next will be superimposed.
constructs a dictionary containing BeginPage and EndPage handlers to sequentially number pages. EndPage will restore the graphics state saved by BeginPage and then execute paintproc with a single integer on the stack, the sequence number to be painted on the page. These are simple procedures intended for producing strictly serial-numbered tickets or documents, not for the more flexible page numbering of general text processing.
If nup is 1, then if faceup is false, the number passed to paintproc is simply the page number passed to EndPage plus start, and if faceup is true, it is the page number subtracted from start+n-1; the point is to leave the lowest-numbered piece on top of the output stack.
When nup is greater than one, seqnumbered assumes its BeginPage/EndPage procedures are to be pushed over procedures that combine multiple pieces on a physical page (see tile). Naturally, nup should match the number of pieces arranged per page by the lower procedures. In this case, the numbers passed to paintproc are in an order such that the resulting stack of sheets can be cut into nup stacks of consecutively numbered pieces. The stacks can be put one atop another in the obvious way to get a single ordered stack. As always, the value of faceup adjusts the order to match the paper delivery.
If n is not a multiple of nup, each small stack may need to have a single unprinted piece removed from the top or the bottom before the small stacks are put together.
There is no problem giving nup as 1 even if pieces are being combined by an underlying handler; you will simply get pieces numbered consecutively within sheets rather than down stacks.
The value of n is unimportant if nup is 1 and faceup is false: if more than n showpages are executed, numbers will just keep increasing as you would expect. If faceup is true, they will continue decreasing below the value of start. If nup is greater than 1 and more than n showpages are executed,the resulting numbers will have a messy pattern and repeat numbers already generated.
constructs a dictionary containing BeginPage and EndPage handlers to arrange n virtual pages on each page produced, where n is the number of elements in array. Each element is an executable array containing a transformation matrix and four numbers defining a clipping rectangle as with rectclip. BeginPage reduces the current page number modulo n and executes the corresponding element, followed by rectclip concatmatrix—note that the clipping rectangle is installed before the matrix is applied, so its coordinates are not in the new virtual page's coordinate system but that of the enclosing page. EndPage returns false except when ending every nth page, except on device deactivation, reason code 2, where it returns true unless the current page number is zero mod n.
Because tile accepts an array of arbitrary transformation matrices and clip rectangles, it is not limited to pure tiling, but simply arranges n virtual areas on a page, whether tiled, overlapping, or superimposed. Utility procedures are provided (described in the next section) to construct array for common cases of tiling that come up in step-and-repeat or page imposition.
Several utility procedures are provided to simplify setting up virtual pages that tile a larger page, with orientations that can vary (as in a page imposition for signatures that will be folded).
Computes the largest scale factor at which the matter (with dimensions mw mh) will just fit in a region of dimensions rw rh: the smaller of rw/mw and rh/mh. Signs are ignored on the widths and heights; factor is never negative.
concatenates either a single matrix to each of a list of matrices, or corresponding elements of two lists of matrices. The matrices in the right-hand list are updated in place. The right hand may be a simple array of matrices, or an array of executable arrays each having a matrix as its first element (the data structure produced by origins and used by tile).
given an array whose length is a multiple of 3, taken as a sequence of orient ix iy triples, and region width and height rw and rh, construct an array of the form used by tile, where each matk is a translation to ikxrw ikyrh and reorientation as specified by orientk, and each cliprectk has width and height rw rh and lower left corner ikxrw ikyrh. Each orientk is a procedure that takes rw rh on the stack and produces a transformation matrix. The eight obvious ones are named and described below. The simple way to use them is to use executable-array syntax for the {orient ix iy...} array, and immediate-name form for the orientations, which will ensure the procedures are copied into the array and not prematurely executed:
% a possible quarto imposition for landscape pages on sheet run portrait: { //ruul 0 1 //ruul 0 0 //rdur 1 1 //rdur 1 0 } pw 2 div ph 2 div origins
produces an array usable as input to origins for the simple case of a grid of rows rows and cols columns and the same orientation for each region. rowmaj varies the column indices faster than the row indices, and colmaj does the reverse. If either rows or cols is negative, the corresponding indices will run downward (from the absolute value less one, down to zero).
Eight named procedures provide the obvious orientations for rectangular matter being placed in a rectangular region. I always spend more time than I should making hand gestures in the air while setting up a rotation and translation, so these procedures have names chosen to be concise and descriptive. Each name is four letters. The first and third refer to the upper and right edges of the matter, the second and fourth to the corresponding edges of the region. So, uurr is the identity orientation (up to up and right to right), udrl is 180 degree rotation, and rdur means the matter is rotated 90 degrees clockwise to be placed in the region. The names in which u comes first are those where the aspect ratios of the matter and region match (zero and 180 rotations); r comes first for those that invert the aspect ratio (90 and 270 rotations). The names that involve mirroring, for example UURL, are capitalized.
Each procedure takes the region width and height on the stack and returns a transformation matrix.
executes a moveto to the point xfrac yfrac relative to the
current clip region's bounding rectangle (clippath pathbbox).
That is, 0 1
onclip moves to the top left corner,
0.5 1
onclip to the top center, and so on.
onclip can be convenient to use in a BeginPage procedure that establishes an initial current point for direct PostScript typesetting, for example. The code below will set the current point initially for every page, one inch below and right of the top left corner:
<< /BeginPage {0 1 onclip 72 72 rmoveto} bind >> InstallBeginEndPage
The reference to onclip in that procedure will require
PageNest
's dictionary to be on the lookup stack at all times.
If you replace onclip with //onclip exec, you will be able
to end the PageNest
dictionary once you have defined
the procedure.
executes scale and translate to just fit the given bounding box llx lly urx ury, preserving aspect ratio, in a region of dimensions rw rh, with the lower left corner at the (prior) origin. That is, the scaling is by urx-llx ury-lly rw rh minscale, and the translation is by -llx -lly. Signs are ignored for rw and rh.
orfit's behavior is completely determined by its arguments. It makes no attempt to query or account for the current graphics state, so it will not defeat cropping or scaling effects set up in advance (as the earlier-described Acrobat auto-adjustment can).
One procedure is provided to enable the workaround for misplaced save and restore described earlier:
sets an internal flag requiring PageNest
's
begin- and end-page handlers to observe the current
save/restore level, and to defer any showpage
executed at a save level more deeply nested than that recorded by
BeginPage
. Adds to dict a definition for
restore that executes restore followed immediately by
showpage if one was pending and the save level after
restore is not greater than the recorded level.
InstallRestoreHack uses setpagedevice to register
PageNest
's own begin- and end-page handlers, only if
they have not been registered already.
The simple userdict InstallRestoreHack
will solve the
misplaced save/restore problem in many cases. It can fail, though,
with code that relies on the definition of restore
being an operator (the substitute version is a procedure), or that
looks for it in systemdict explicitly. Those complications are
not unheard of; even InstallRestoreHack itself currently relies
on the (prior) definition of restore being an operator.