rossc@extro.ucc.su.oz (Ross Cartlidge) (07/20/89)
#! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # README # eg.sh # multi.ps # This archive created: Wed Jul 19 16:45:24 1989 export PATH; PATH=/bin:/usr/bin:$PATH if test -f 'README' then echo shar: "will not over-write existing file 'README'" else cat << \SHAR_EOF > 'README' This package is a PostScript prolog which when used with almost any valid PostScript will output it with multiple logical pages on each physical page. Each logical page will be identical (but smaller) to the original result of each showpage. Written By: Ross Cartlidge University Computing Service Building H08, Sydney University NSW, 2006, Australia rossc@extro.ucc.su.oz Email +61 2 692 3495 Phone +61 2 660 6557 Fax It works by overlaying each PostScript Operator which is affected by or affects the absolute graphics state with a procedure which first goes back to normal full page graphics state, does the operator, and then restores the multiple page graphics state. Thus when "initgraphics", eg, is executed its transformation of CTM, path and clippath is mapped onto the multiple page representation. Also when a "save" is done the full page version is saved, thus when "restore" is done - possibly many logical pages into the future - the graphics state saved is transformed into the new multi page. All its local variables are stored in private (unnamed) directories and the only "pollution" of the current directory is with the entry and exit points "multi" and "endmulti". It is written to be able to be included in the "exitserver" state of the the server, thus it can be loaded before any other permanent prologs which may use absolute graphics commands (like "showpage"). It works recursively so a "multi" can be called inside another "multi" to any level (VM permitting). It tries to use as little VM as possible but some is consumed in restoring paths between multi and single page graphics states so large documents may cause VMerror earlier than in normal representation unless "save-restore" is used appropriately. USAGE: "multi.ps" defines 2 procedures:- (i) landscape nrows ncols dividers multi - Go into multi page representation landscape boolean, if true divide page in landscape orientation nrows integer, number of logical pages down physical page ncols integer, number of logical pages across physical page dividers boolean, if true divide logical pages by lines (ii) endmulti - End multi page representation EXAMPLES: Display pages in "side by side" form % landscape mode, 1 row, 2 cols, dividers true 1 2 true multi % /Helvetica findfont 12 scalefont setfont 100 100 moveto save (This is Page One) show showpage restore save (This is Page Two) show showpage restore % endmulti To run this example type:- sh eg.sh | <your PostScript print command> It's not a bad idea to load multi.ps in to the "exitserver" state of your LaserWriter as then it will work with any prologs you may be using. Make sure you load it first! To load it into the "exitserver" state see the comments at the start of multi.ps. You then only need the :- true 1 2 false multi line in code sent to the LaserWriter. CAVEATS: More memory is consumed than normal output Direct references to operators through "systemdict" will circumvent overlays Programs which rely on the overlayed named being operators and not procedures will fail. For example:- /showpage { showpage } bind def will create an infinite loop when showpage is called. Also procedures directly on the stack do not execute unlike operators so the program:- /s [ /showpage load ] cvx def s will not work ( an "exec" is required after the "load") Device changing operators such as "letter", etc are mapped to null procedures SHAR_EOF fi if test -f 'eg.sh' then echo shar: "will not over-write existing file 'eg.sh'" else cat << \SHAR_EOF > 'eg.sh' #!/bin/sh cat multi.ps - <<-'!' % landscape mode, 1 row, 2 cols, no dividers true 1 2 true multi % /Helvetica findfont 12 scalefont setfont 100 100 moveto save (This is Page One) show showpage restore save (This is Page Two) show showpage restore % endmulti ! SHAR_EOF chmod +x 'eg.sh' fi if test -f 'multi.ps' then echo shar: "will not over-write existing file 'multi.ps'" else cat << \SHAR_EOF > 'multi.ps' %!PS-Adobe-1.0 %%Creator: Ross Cartlidge <rossc@extro.ucc.su.oz> %%Title: Multiple pages on one page %%CreationDate: Wednesday July 29 16:00:00 1989 %%Pages: 0 %%DocumentFonts: %%BoundingBox: 0 0 0 0 %%EndComments % % Uncomment the next line if you wish to load multi into the "exitserver" % state of the PostScript device % serverdict begin 0 exitserver % % % make each operator to overlay a procedure so a bind in % a prolog will not stop the overlaying by "multi" % [ /gsave /grestore /grestoreall /initgraphics /initmatrix /currentmatrix /setmatrix % Path construction operators /initclip % Virtual memory operators /save % ones which needed special overloading /showpage /erasepage /copypage /restore % ignore these /letter /legal /a4 /b5 /lettersmall /note ] { dup load % /n -> {n} 2 array cvx dup 0 4 -1 roll % if operator don't need exec dup type /operatortype eq { put % {}[0] = (n) dup 1 /null cvx put % {}[1] = null } { put % {}[0] = (n) dup 1 /exec cvx put % {}[1] = exec } ifelse bind def } forall % % Initialise endmulti to execute an error % /endmulti { count array astore /ostack exch def 250 array execstack /estack exch def 20 array dictstack /dstack exch def $error /newerror true put $error /errorname (No matching multi) cvn put $error /command (endmulti) put $error /ostack ostack put $error /estack estack put $error /dstack dstack put stop } bind def % % Put multiple logical pages on one physical page % until "endmulti" called % % landscape nrows ncols dividers multi - % % landscape boolean, if true divide page in landscape orientation % nrows integer, number of logical pages down physical page % ncols integer, number of logical pages across physical page % dividers boolean, if true divide logical pages by lines % /multi { currentdict 64 dict begin /initdict exch def % store initial dict for backward reference /dividers exch def /cols exch def /rows exch def % % get size of current page % initgraphics clippath pathbbox /Y exch def % Max Y /X exch def % Max X /y exch def % Min Y /x exch def % Min X /W X x add def % Width of Page /H Y y add def % Height of page % if landscape { % % Note: x and y are reversed % /w Y y sub def % Width of imageable region /h X x sub def % Height of imageable region /L % Map to landscape -90 matrix rotate 0 H matrix translate matrix concatmatrix def /O y x matrix translate def % Move to origin } { /w X x sub def /h Y y sub def /L matrix def /O x y matrix translate def } ifelse % % CTM (multi) = C x T x M x L x I % CTM (normal) = C x I % CTM (normal) = CTM (multi) x (T x M x L x I)-1 x I % M = (Scale rows/cols) x (Scale logical to physical) x % (Translate to physical clip origin % T = (Convert logical page to spot and physical) % L = (Convert to landscape) % I = Initial Physical CTM % C = Random transform on logical page /I matrix currentmatrix def /I_inv I matrix invertmatrix def /M w W div cols div h H div rows div matrix scale %TMP O matrix concatmatrix def % matrix T <current T> /T { page# cols mod W mul rows page# cols idiv sub 1 sub H mul 3 -1 roll translate } def % % Utility functions % NB: *_t1 are temporary variables % % matrix fromcanon <I-1 x T x M x L x I> /From_t1 matrix def /From_t2 matrix def /From_t3 matrix def /From_t4 matrix def /fromcanon { I_inv From_t1 T M L I From_t2 concatmatrix From_t3 concatmatrix From_t4 concatmatrix 3 -1 roll concatmatrix } def % /n {} mkmulti - % makes a new function called "n" in previous dict with:- % {}[0] = /n % {}[1] = currentdict % currentdict.n = prevdict.n % /mkmulti { 1 index dup load def %define old val in current dict 5 array cvx dup 3 4 -1 roll put % A[3] = {} dup 0 3 index put % A[0] = /n dup 1 currentdict put % A[1] = currentdict dup 2 /begin cvx put % A[2] = begin dup 4 /exec cvx put % A[4] = exec initdict 3 1 roll put % define initdict.n to multi function } def % % path_to_proc {} % make proc represenation of current path % /path_to_proc { { [ /newpath cvx { /moveto cvx} { /lineto cvx} { /curveto cvx} { /closepath cvx } pathforall ] cvx exch pop } stopped { $error /errorname get /invalidaccess eq { cleartomark $error /newerror false put (%%Warning%% charpath in path - path nulled) = cvx exec } { stop } ifelse } if } def /path_def { { currentpoint } stopped { $error /newerror false put { newpath } } { /moveto cvx 3 array astore cvx } ifelse } cvlit def % % Draw lines round logical pages % /draw_dividers { initgraphics L concat M concat 1 1 cols 1 sub { W mul dup 0 moveto rows H mul lineto } for 1 1 rows 1 sub { H mul dup 0 exch moveto cols W mul exch lineto } for stroke } def % % for each graphics operator which affects absolute state % /M1 matrix def /M3 matrix def /M2 matrix def [ /gsave /grestore /grestoreall /initgraphics /initmatrix /currentmatrix /setmatrix % Path construction operators /initclip % Virtual memory operators /save ] { { % Save paths path_def path_to_proc clippath { {} } path_to_proc % % CTM <- CTM x Tocano (canon mode) % M1 currentmatrix Tocanon M2 concatmatrix setmatrix % Restore paths initclip exec clip exec load exec % Save paths path_def path_to_proc clippath { {} } path_to_proc % % CTM <- CTM x Fromcanon (Non canon mode) % M1 currentmatrix Fromcanon M2 concatmatrix setmatrix % Restore paths initclip exec clip exec end } mkmulti } forall % % Define the operators which can't use the standard template % /showpage { /page# page# 1 add def % Update the transform matrices page# npages eq { dividers { draw_dividers } if load exec % the previous showpage /page# 0 def } { pop } ifelse /Fromcanon Fromcanon fromcanon def /Tocanon Fromcanon Tocanon invertmatrix def end initgraphics % the new initgraphics } mkmulti /copypage { pop end gsave showpage grestore } mkmulti /erasepage { pop end gsave initclip clippath 1 setgray fill grestore } mkmulti [ /letter /legal /a4 /b5 /lettersmall /note ] { { pop end (%%Warning%% Device change ignored) = } mkmulti } forall % % Define restore separately as it affects the value of page#, etc % /restore { pop % Push the values to restore after restore mark exch % put mark under -save- page# Fromcanon aload pop Tocanon aload pop counttomark -1 roll % get -save- to the top restore % Restore popped values Tocanon astore pop Fromcanon astore pop /page# exch def pop % mark % Save paths path_def path_to_proc clippath { { } } path_to_proc % % CTM <- CTM x Fromcanon (Non canon mode) % M1 currentmatrix Fromcanon M2 concatmatrix setmatrix % Restore paths initclip exec clip exec end } mkmulti % % procedure to undo the effect of multi % /endmulti { pop % don't need /endmulti [ /gsave /grestore /grestoreall /initgraphics /initmatrix /currentmatrix /setmatrix % Path construction operators /initclip % Virtual memory operators /save % ones which needed special overloading /showpage /erasepage /copypage /restore % ignore these /letter /legal /a4 /b5 /lettersmall /note % /endmulti ] { initdict exch dup load % get old value put % restore old value } forall page# 0 ne % if not at new page show uncomplete page { dividers { draw_dividers } if showpage } if end } mkmulti % % Set up in multi(non canon) mode % /page# 0 def /npages rows cols mul def /Fromcanon matrix fromcanon def /Tocanon Fromcanon matrix invertmatrix def end initgraphics } bind def SHAR_EOF fi exit 0 # End of shell archive