anastigmatix.net

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
  • Nested Begin/EndPage
  • Applications
  • Manipulate existing PS files
  • Direct PostScript composition
  • Examples
  • Admission tickets
  • Address labels
  • Addressing postcards
  • The protocol
  • Nesting BeginPage
  • Nesting EndPage
  • Caching BeginPage effects
  • Page-counter management
  • Common issues and workarounds
  • showpage before restore
  • Acrobat auto-adjustment
  • Ghostscript bugs
  • No deactivate on exit
  • No page count reset
  • PageNest: reference
  • PageNest dictionary contents
  • Handler stack management
  • InstallBeginEndPage
  • PushBeginEndPage
  • PopBeginEndPage
  • Handler constructors
  • cumulate
  • select
  • seqnumbered
  • tile
  • Utilities for impositions
  • minscale
  • concatmm
  • origins
  • rowmaj
    colmaj
  • Rectangle orientations
  • uurr udrl ruul rdur
    UURL UDRR RUUR RDUL
  • Convenience procedures
  • onclip
  • orfit
  • Workarounds for common issues
  • InstallRestoreHack
  • PageNest: nested begin/end page handling in PostScript

    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

    Applications

    Manipulation of existing PostScript files

    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.

    Direct PostScript composition

    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.

    Examples

    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

    The protocol

    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 showpages 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.

    Nesting BeginPage

    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.

    Nesting EndPage

    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.

    Caching BeginPage effects

    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.

    Page-counter management

    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.

    Common issues and workarounds

    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.

    showpage before restore

    Many applications generate PostScript that uses a save/restore pair around each page. Here are two ways to do that:

    The right wayThe 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.

    Acrobat auto-adjustment

    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.

    Ghostscript bugs

    No deactivate on exit

    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.

    No page count reset on setpagedevice

    This issue is now discussed in Page-counter management, describing how PageNest provides predictable under Ghostscript and PostScript.

    PageNest: reference

    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:

    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.

    PageNest dictionary contents

    This section describes the contents of the read-only dictionary that is returned by /net.anastigmatix.PageNest /ProcSet findresource.

    Handler stack management

    The dictionary contains three procedures for managing the stack of handlers.

    InstallBeginEndPage
    dict InstallBeginEndPage

    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).

    PushBeginEndPage
    dict PushBeginEndPage

    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.

    PopBeginEndPage
    PopBeginEndPage

    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.

    Handler constructors

    Four procedures conveniently construct handler dictionaries that can be passed to InstallBeginEndPage.

    cumulate
    cumulate dict

    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.

    select
    image advance select dict

    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.

    seqnumbered
    start n nup faceup paintproc seqnumbered dict

    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.

    tile
    array tile dict

    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.

    Utilities for impositions

    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).

    minscale
    mw mh rw rh minscale factor

    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.

    concatmm
    mat0 [mat1...matn] concatmm [mat0mat1...mat0matn]
    mat0 [{mat1...}...{matn...}] concatmm [{mat0mat1...}...{mat0matn...}]
    [mat01...mat0n] [mat11...mat1n] concatmm [mat01mat11...mat0nmat1n]
    [mat01...mat0n] [{mat11...}...{mat1n...}] concatmm [{mat01mat11...}...{mat0nmat1n...}]

    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).

    origins
    {orient1 i1x i1y...orientn inx iny} rw rh origins [{mat1 cliprect1}...{matn cliprectn}]

    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
         
    rowmaj
    colmaj
    orient cols rows rowmaj [orient i1x i1y...orient inx iny]
    orient cols rows colmaj [orient i1x i1y...orient inx iny]

    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).

    Illustrates eight orientations: uurr udrl ruul rdur UURL UDRR RUUR RDUL
    Rectangle orientations

    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.

    Convenience procedures

    onclip
    xfrac yfrac onclip

    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.

    orfit
    rw rh llx lly urx ury orfit

    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).

    Workarounds for common issues

    One procedure is provided to enable the workaround for misplaced save and restore described earlier:

    InstallRestoreHack
    dict InstallRestoreHack

    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.

    Valid XHTML 1.0! Valid CSS! $Id: PageNest.html,v 1.19 2005/11/23 19:27:25 chap Exp $