home *** CD-ROM | disk | FTP | other *** search
/ jppd.dyndns.org / jppd.dyndns.org.tar / jppd.dyndns.org / QUERYPRO / Impressora_PDF / converter.exe / GPLGS / pdfopt.ps < prev    next >
Text File  |  2003-12-13  |  37KB  |  1,260 lines

  1. %    Copyright (C) 2000, 2001 Aladdin Enterprises.  All rights reserved.
  2. % This software is provided AS-IS with no warranty, either express or
  3. % implied.
  4. % This software is distributed under license and may not be copied,
  5. % modified or distributed except as expressly authorized under the terms
  6. % of the license contained in the file LICENSE in this distribution.
  7. % For more information about licensing, please refer to
  8. % http://www.ghostscript.com/licensing/. For information on
  9. % commercial licensing, go to http://www.artifex.com/licensing/ or
  10. % contact Artifex Software, Inc., 101 Lucas Valley Road #110,
  11. % San Rafael, CA  94903, U.S.A., +1(415)492-9861.
  12.  
  13. % $Id: pdfopt.ps,v 1.16.2.1 2003/12/12 22:07:59 giles Exp $
  14. % PDF linearizer ("optimizer").
  15.  
  16. .currentglobal true .setglobal
  17. /pdfoptdict 200 dict def
  18. pdfoptdict begin
  19.  
  20. % This linearizer is designed for simplicity, not for performance.
  21. % See the main program (the last procedure in the file) for comments
  22. % describing the main processing sequence.
  23.  
  24. % ---------------- Utilities ---------------- %
  25.  
  26. % ------ Data structures ------ %
  27.  
  28. % Distinguish dictionaries, arrays, and everything else.
  29. /ifdaelse {        % <obj> <dictproc> <arrayproc> <otherproc> ifdaelse -
  30.   3 index type dup /dicttype eq {
  31.     pop pop pop
  32.   } {
  33.     dup /arraytype ne exch /packedarraytype ne and {
  34.       exch
  35.     } if pop exch pop
  36.   } ifelse exec
  37. } bind def
  38.  
  39. % Implement dynamically growable arrays using a dictionary.
  40. /darray {        % <size> darray <darray>
  41.   dict
  42. } bind def
  43. /dadd {            % <darray> <value> dadd -
  44.   1 index length exch put
  45. } bind def
  46. /daforall {        % <darray> <proc> daforall -
  47.   /exch cvx /get cvx 3 -1 roll /exec cvx 5 packedarray cvx
  48.   0 1 2 index 0 get length 1 sub 4 -1 roll for
  49. } bind def
  50. /dacontents {        % <darray> dacontents <array>
  51.   [ exch { } daforall ]
  52. } bind def
  53. /dacontstring {        % <darray> dacontstring <string>
  54.   0 1 index { exch pop length add } forall string
  55.   dup /NullEncode filter
  56.             % Stack: darray str filter
  57.   3 -1 roll { 1 index exch writestring } daforall
  58.   closefile
  59. } bind def
  60.  
  61. % Force an object, mapping it if it is a reference.
  62. /omforcenew {        % <obj> omforce <obj'> <notseen>
  63.   dup oforce 2 copy eq { pop true } { exch 0 get omapnew exch pop } ifelse
  64. } bind def
  65. /omforce {        % <obj> omforce <obj'>
  66.   omforcenew pop
  67. } bind def
  68. /omget {        % <dict|array> <key> omget <obj>
  69.   get omforce
  70. } bind def
  71. % Visit an entire tree.
  72. /omvisit {        % <obj> omvisit -
  73.   omforcenew {
  74.     { { omvisit omvisit } forall }
  75.     { { omvisit } forall }
  76.     { pop }
  77.     ifdaelse
  78.   } {
  79.     pop
  80.   } ifelse
  81. } bind def
  82. % Visit a tree, stopping at references to Page objects.
  83. % (This is only needed for the OpenAction in the Catalog.)
  84. /omvisitnopage {    % <obj> omvisitnopage -
  85.   dup oforce dup type /dicttype eq {
  86.     /Type .knownget { /Page eq } { false } ifelse
  87.   } {
  88.     pop false
  89.   } ifelse {
  90.     pop        % Page reference
  91.   } {
  92.     omforcenew {
  93.       { { omvisitnopage omvisitnopage } forall }
  94.       { { omvisitnopage } forall }
  95.       { pop }
  96.       ifdaelse
  97.     } {
  98.       pop
  99.     } ifelse
  100.   } ifelse
  101. } bind def
  102.  
  103. % Collect the list of currently mapped object numbers, in order.
  104. /omapped {        % - omapped <obj#s>
  105.   RMap ld_length larray exch lgrowto
  106.   RMap {
  107.     2 index 3 1 roll 1 sub exch lput
  108.   } ld_forall
  109. } bind def
  110.  
  111. % Collect the list of object numbers passed to omap by a procedure.
  112. /visited {        % <proc> visited <obj#s>
  113.   false currentomap 2 .execn
  114.   omapped exch setomap
  115. } bind def
  116.  
  117. % ------ Output ------ %
  118.  
  119. % Provide a framework for closure-based streams.
  120. .currentglobal false .setglobal
  121. userdict /clostreams 20 dict put    % stream -> [data endproc]
  122. .setglobal
  123. % Create a closure-based stream.
  124. /clostream {        % <data> <proc> <endproc> clostream <stream>
  125.   2 index 3 -1 roll /exec load 3 packedarray cvx
  126.   /NullEncode filter
  127.         % Stack: data endproc stream
  128.   clostreams 1 index 5 -2 roll 2 array astore put
  129. } bind def
  130. % Close a closure-based stream.
  131. /closend {        % <stream> closend <result>
  132.   dup closefile clostreams exch
  133.   2 copy get 3 1 roll undef aload pop exec
  134. } bind def
  135.  
  136. % Implement in-memory output streams.
  137. /msproc {        % <data> <more> <accum> msproc <scratch>
  138.   3 -1 roll dadd { 100 string } { () } ifelse
  139. } bind def
  140. /mstream {        % - mstream <mstream>
  141.   10 darray {msproc} {dacontstring} clostream
  142. } bind def
  143. /mcontents {        % <mstream> mcontents <string>
  144.   closend
  145. } bind def
  146.  
  147. % Implement a stream that only keeps track of its position.
  148. % (All streams should do this, but the PLRM doesn't require it.)
  149. /posbuf 100 string def
  150. /posproc {        % <data> <more> <accum> posproc <scratch>
  151.   0 2 copy get 5 -1 roll length add put
  152.   pop //posbuf
  153. } bind def
  154. /postream {        % - postream <postream>
  155.   [0] {posproc} {0 get} clostream
  156. } bind def
  157. /poslength {        % <postream> poslength <pos>
  158.   closend
  159. } bind def
  160.  
  161. % Implement streams with variable-bit-width data.
  162. % Note that these are dictionary objects, not stream objects.
  163. /bitstream {        % <stream> bitstream <bstream>
  164.   4 dict begin /S exch def /N 8 def /B 0 def
  165.   currentdict end
  166. } bind def
  167. /bitwrite {        % <bstream> <value> <width> bitwrite -
  168.   PDFOPTDEBUG { ( ) print 1 index =only (:) print dup = } if
  169.   3 -1 roll begin
  170.   N exch sub dup 0 ge {
  171.     /N exch def N bitshift B add
  172.   } {
  173.     2 copy bitshift B add S exch write
  174.             % Stack: value -left
  175.     { 8 add dup 0 ge { exit } if
  176.       2 copy bitshift 255 and S exch write
  177.     } loop
  178.     /N 1 index def bitshift 255 and
  179.   } ifelse /B exch def
  180.   end
  181. } bind def
  182. /bitflush {        % <bstream> bitflush -
  183.   begin N 8 ne { S B write /B 0 def /N 8 def } if end
  184. } bind def
  185.  
  186. /bwn {                  % <value> <width> bwn -
  187.   2 copy                % v w v w
  188.   2 exch exp ge {       % v w v>=2**w
  189.     /bwn cvx /rangecheck signalerror
  190.   } if
  191.   bits 3 1 roll bitwrite
  192. } def
  193.  
  194. % Capture OFile output on the temporary file, in memory, or just as a length.
  195. /totemp {        % <proc> totemp <start> <end>
  196.   TFile fileposition OFile
  197.   /OFile TFile def 3 .execn
  198.   /OFile exch def
  199.   TFile fileposition
  200. } bind def
  201. /tomemory {        % <proc> tomemory <string>
  202.   OFile /OFile mstream def 2 .execn
  203.   OFile mcontents exch /OFile exch def
  204. } bind def
  205. /tolength {        % <proc> tolength <string>
  206.   OFile /OFile postream def 2 .execn
  207.   OFile poslength exch /OFile exch def
  208. } bind def
  209.  
  210. % Copy a range of bytes from TFile to OFile.
  211. /copyrange {        % <start> <end> copybytes -
  212.   TFile 2 index setfileposition
  213.   exch sub 1024 string exch {
  214.         % Stack: buf left
  215.     2 copy 1 index length .min 0 exch getinterval
  216.     TFile exch readstring pop OFile exch writestring
  217.     1 index length sub dup 0 le { exit } if
  218.   } loop pop pop
  219. } bind def
  220.  
  221. % Pad with blanks to a specified position.
  222. /padto {        % <pos> padto -
  223.   OFile fileposition sub
  224.   dup 0 lt {
  225.     (ERROR: file position incorrect by ) print =
  226.     /padto cvx /rangecheck signalerror
  227.   } {
  228.     { ( ) ows } repeat
  229.   } ifelse
  230. } bind def
  231.  
  232. % ---------------- Read objects into memory ---------------- %
  233.  
  234. /touch {        % <object> touch -
  235.   {
  236.     { touch touch } forall
  237.   } {
  238.     dup xcheck {
  239.         % Executable array, must be an indirect object.
  240.       dup 0 get resolved? { pop pop } { oforce touch } ifelse
  241.     } {
  242.       { touch } forall
  243.     } ifelse
  244.   } {
  245.     pop
  246.   } ifdaelse
  247. } bind def
  248.  
  249. % ---------------- Replace references with referents ---------------- %
  250.  
  251. /replaceable? {        % <value> replaceable? <bool>
  252.   dup type /integertype eq exch xcheck not and
  253. } bind def
  254. /replacement {        % <obj|ref> replacement <obj'>
  255.   dup oforce dup replaceable? { exch } if pop
  256. } bind def
  257.  
  258. /replacerefs {        % <object> replacerefs <object>
  259.   {
  260.     dup {
  261.       2 index 2 index undef
  262.       exch replacement exch replacement
  263.       2 index 3 1 roll put
  264.     } forall
  265.   } {
  266.     0 1 2 index length 1 sub {
  267.       1 index exch 2 copy get replacement put
  268.     } for
  269.   } {
  270.   } ifdaelse
  271. } bind def
  272.  
  273. /replaceReferences {    % - replaceReferences -
  274.   Objects { replacerefs pop } lforall
  275.         % Delete replaced objects.
  276.   0 1 Objects llength 1 sub {
  277.     Objects 1 index lget replaceable? {
  278.       PDFOPTDEBUG { (Deleting ) print dup = } if
  279.       Generations 1 index 0 lput
  280.     } if pop
  281.   } for
  282. } bind def
  283.  
  284. % ---------------- Create new objects ---------------- %
  285.  
  286. /createObjects {    % [<obj>...] createObjects <firstobj#>
  287.   Objects llength dup
  288.   dup 3 index length add growPDFobjects
  289.         % Stack: objects objn objn
  290.   3 1 roll exch {
  291.     Objects 2 index 3 -1 roll lput
  292.     Generations 1 index 1 lput
  293.     1 add
  294.   } forall pop
  295. } bind def
  296.  
  297. % ---------------- Propagate attributes ---------------- %
  298.  
  299. /nopropattrs <<
  300.     % Never propagate these.
  301.   /Type dup /Kids dup /Count dup /Parent dup
  302.     % Handle Resources specially.
  303.   /Resources dup
  304. >> def
  305.  
  306. % Merge Resources.
  307. /mergeres {        % <fromdict> <todict> mergeres -
  308.         % Values in todict take priority over fromdict.
  309.   1 index /Resources .knownget {
  310.     1 index /Resources .knownget {
  311.         % Stack: fromdict todict fromres tores
  312.       exch oforce exch oforce
  313.         % todict's Resources may be shared, so make a copy.
  314.       dup length dict .copydict
  315.       exch {
  316.         % Stack: fromdict todict tores' fromkey fromvalue
  317.     2 index 2 index knownoget {
  318.         % Stack: fromdict todict tores' fromkey fromvalue tovalue
  319.       exch oforce exch
  320.         % ProcSet is an array, other types are dictionaries.
  321.       dup type /dicttype eq {
  322.         % Dictionary, not ProcSet.
  323.         exch dup length 2 index length add dict .copydict .copydict
  324.       } {
  325.         % Array or packed array, ProcSet.
  326.         % Use dictionaries to do the merge.
  327.         dup length 2 index length add dict begin
  328.         exch { dup def } forall { dup def } forall
  329.         mark currentdict end { pop } forall .packtomark
  330.       } ifelse
  331.     } if
  332.     2 index 3 1 roll put
  333.       } forall
  334.     } if /Resources exch put pop
  335.   } {
  336.     pop pop
  337.   } ifelse
  338. } bind def
  339.  
  340. % Merge attributes other than Resources.
  341. /mergeattrs {        % <fromdict> <todict> mergeattrs <fromdict> <todict>
  342.         % Values in todict take priority over fromdict.
  343.   1 index {
  344.         % Stack: fromdict todict fromkey fromvalue
  345.     //nopropattrs 2 index known {
  346.       pop pop
  347.     } {
  348.       2 index 2 index known { pop pop } { 2 index 3 1 roll put } ifelse
  349.     } ifelse
  350.   } forall
  351. } bind def
  352.  
  353. % Propagate attributes to a subtree.
  354. /proppage {        % <attrs> <subtree> proppage -
  355.         % We should be able to tell when we reach a leaf
  356.         % by finding a Type unequal to /Pages.  Unfortunately,
  357.         % some files distributed by Adobe lack the Type key
  358.         % in some of the Pages nodes!  Instead, we check for Kids.
  359.   dup /Kids knownoget {
  360.         % Accumulate inherited values.
  361.     3 1 roll
  362.         % Stack: kids attrs pagesnode
  363.     dup length dict .copydict mergeattrs
  364.     dup 3 1 roll mergeres
  365.     exch { oforce 1 index exch proppage } forall pop
  366.   } {
  367.         % Merge inherited values into the leaf.
  368.     mergeattrs mergeres
  369.   } ifelse
  370. } bind def
  371.  
  372. % Propagate attributes to all pages.
  373. /propagateAttributes {    % - propagateAttributes -
  374.   0 dict Trailer /Root oget /Pages oget proppage
  375. } bind def
  376.  
  377. % ---------------- Identify document-level objects ---------------- %
  378.  
  379. /identifyDocumentObjects {    % - identifyDocumentObjects <obj#s>
  380.   {
  381.     Trailer /Root omget
  382.     dup /PageMode .knownget { omvisit } if
  383.     % Don't allow omvisit to trace references to Page objects.
  384.     dup /OpenAction .knownget { omvisitnopage } if
  385.     Trailer /Encrypt .knownget { omvisit } if
  386.     dup /Threads .knownget {
  387.       omforce { omvisit } forall
  388.     } if
  389.     dup /AcroForm .knownget { omvisit } if
  390.     pop
  391.   } visited
  392. } bind def
  393.  
  394. % ---------------- Identify the objects of each page ---------------- %
  395.  
  396. /identifyfont {        % <fontref> identifyfont -
  397.   omforce {
  398.     exch /FontDescriptor eq {
  399.       omforce dup /Flags .knownget { 32 and 0 ne } { false } ifelse
  400.       exch {
  401.     exch dup dup /FontFile eq exch /FontFile2 eq or
  402.     exch /FontFile3 eq or 2 index and {
  403.       fontfiles exch dadd
  404.     } {
  405.       omvisit
  406.     } ifelse
  407.       } forall pop
  408.     } {
  409.       omvisit
  410.     } ifelse
  411.   } forall
  412. } bind def
  413.  
  414. % Collect all the objects referenced from a page.  The first object number
  415. % (which may not be the smallest one) is that of the page object itself.
  416. /identifyPageObjects {    % <extra> <page#> identifyPageObjects <obj#s>
  417.   PDFOPTDEBUG {
  418.     (%Objects for page: ) print dup =
  419.   } if
  420.   pdffindpageref
  421.   dup 0 get 3 1 roll
  422.   4 dict begin
  423.   /images 10 darray def
  424.   /fontfiles 10 darray def
  425.   {
  426.     omforce
  427.         % Stack: pageobj# extra page
  428.         % Visit any extra objects if applicable.
  429.     exch omvisit
  430.         % Visit Annots, if any.
  431.         % We don't try to defer the drawing information.
  432.     dup /Annots .knownget { omvisit } if
  433.         % Visit beads.
  434.     dup /B .knownget { omvisit } if
  435.         % Visit resources dictionaries.
  436.     dup /Resources .knownget {
  437.       omforce dup {
  438.         % Visit the first-level Resource dictionaries.
  439.     omforce pop pop
  440.       } forall {
  441.         % Visit the resources themselves.
  442.         % Skip Image XObjects, and FontFile streams if the
  443.         % FontDescriptor Flags have bit 6 set.
  444.         % We don't try to visit the resources in the order in which
  445.         % the Contents stream(s) reference(s) them.
  446.     exch dup /XObject eq {
  447.       pop oforce {
  448.         dup oforce /Subtype get /Image eq {
  449.           images exch dadd
  450.         } {
  451.           omvisit
  452.         } ifelse pop
  453.       } forall
  454.     } {
  455.       /Font eq {
  456.         oforce { identifyfont pop } forall
  457.       } {
  458.         oforce omvisit
  459.       } ifelse
  460.     } ifelse
  461.       } forall
  462.     } if
  463.         % Visit the Contents stream(s).
  464.     dup /Contents .knownget { omvisit } if
  465.         % Visit Image XObjects.  We don't try to visit them in
  466.         % reference order.
  467.     images { omvisit } daforall
  468.         % Visit FontFile streams.  We don't try to visit them in
  469.         % reference order.
  470.     fontfiles { omvisit } daforall
  471.     pop
  472.   } visited end
  473.         % Stack: pageobj# obj#s_larray
  474.   [ 3 1 roll {
  475.     2 copy eq { pop } { exch } ifelse
  476.   } lforall counttomark 1 roll ]
  477.   PDFOPTDEBUG {
  478.     (%Objects = ) print dup === flush
  479.   } if
  480. } bind def
  481.  
  482. % Identify the objects of the first page.
  483. /identifyFirstPageObjects {    % - identifyFirstPageObjects <obj#s>
  484.   Trailer /Root oget null
  485.   1 index /PageMode knownoget {
  486.     /UseOutlines eq {
  487.       1 index /Outlines knownoget { exch pop } if
  488.     } if
  489.   } if exch pop
  490.   1 identifyPageObjects
  491. } bind def
  492.  
  493. % Identify the non-shared objects of the other pages, and the shared objects.
  494. % Note that the page objects themselves may appear to be shared, because of
  495. % references from Dest entries in annotations, but they must be treated as
  496. % non-shared.  Note also that some objects referenced on the first page may
  497. % also be referenced from other pages.
  498. /identifyOtherPageObjects {    % - identifyOtherPageObjects [<pageobj#s> ...]
  499.                 %   <sharedobj#s>
  500.   4 dict begin
  501.   /marks lstring Objects llength lgrowto def
  502.         % Collect objects of other pages and identify sharing.
  503.   [ 2 1 pdfpagecount { null exch identifyPageObjects } for ]
  504.   dup {
  505.     { marks exch 2 copy lget 1 add 254 .min lput } forall
  506.   } forall
  507.         % Mark document-level and first page objects.
  508.   CatalogNs { marks exch 255 lput } lforall
  509.   FirstPageNs { marks exch 255 lput } forall
  510.         % Mark the page objects themselves as non-shared.
  511.   dup {
  512.     0 get marks exch 1 lput
  513.   } forall
  514.         % Collect the non-shared objects of each page.
  515.   dup
  516.   [ exch {
  517.     [ exch {
  518.       marks 1 index lget 1 ne { pop } if
  519.     } forall ]
  520.   } forall ]
  521.                 % Collect the shared objects of each page.
  522.   exch
  523.   [ exch {
  524.     [ exch {
  525.       marks 1 index lget dup 1 le exch 255 eq or { pop } if
  526.     } forall ]
  527.   } forall ]
  528.  
  529.                 % Collect the shared objects.
  530.   [ 1 1 marks llength 1 sub {
  531.     marks 1 index lget dup 1 le exch 255 eq or { pop } if
  532.   } for ]
  533.  
  534.   end
  535. } bind def
  536.  
  537. % Identify objects not associated with any page.
  538. /identifyNonPageObjects {    % - identifyNonPageObjects <obj#s>
  539.   4 dict begin
  540.   /marks lstring Objects llength lgrowto def
  541.  
  542.   LPDictN     marks exch 1 lput
  543.   PHSN        marks exch 1 lput
  544.   CatalogNs   { marks exch 1 lput } lforall
  545.   FirstPageNs { marks exch 1 lput } forall
  546.   SharedNs    { marks exch 1 lput } forall
  547.   OtherPageNs { { marks exch 1 lput } forall } forall
  548.  
  549.     %****** PUT THESE IN A REASONABLE ORDER ******
  550.   /npobj larray
  551.   0
  552.   1 1 Objects llength 1 sub {
  553.     marks 1 index lget 0 eq {
  554.       Generations exch lget 0 ne { 1 add } if
  555.     } {
  556.       pop
  557.     } ifelse
  558.   } for 
  559.   lgrowto def
  560.  
  561.   0
  562.   1 1 Objects llength 1 sub {
  563.     marks 1 index lget 0 eq {
  564.                                           % i
  565.       Generations 1 index lget 0 ne {
  566.                                           % i
  567.         npobj 2 index                     % i nobj 0
  568.         3 -1 roll                         % nobj 0 i
  569.         lput 1 add
  570.       } {
  571.         pop
  572.       } ifelse
  573.     } {
  574.       pop
  575.     } ifelse
  576.   } for 
  577.   pop
  578.  
  579.   npobj
  580.   end
  581. } bind def
  582.  
  583. % ---------------- Assign object numbers ---------------- %
  584.  
  585. % Assign object numbers to all objects that will be copied.
  586. % Return the first (translated) object number in the First Page xref table.
  587. /assignObjectNumbers {        % - assignObjectNumbers -
  588.   OtherPageNs { { omap pop } forall } forall
  589.   SharedNs { omap pop } forall
  590.   NonPageNs { omap pop } lforall
  591.         % Assign object numbers for the First Page xref table last.
  592.   LPDictN omap    % don't pop, this is the return value
  593.   CatalogNs { omap pop } lforall
  594.   FirstPageNs { omap pop } forall
  595.   PHSN omap pop
  596. } bind def
  597.  
  598. % ---------------- Create the LPDict ---------------- %
  599.  
  600. % Create the contents of the LPDict.
  601. /createLPDict {            % <phsstart> <phsend> <firstpageend>
  602.                 %   <xref0start> <filelength> createLPDict -
  603.   LPDict
  604.   dup /Linearized 1 put
  605.   dup /L 4 -1 roll put        % filelength
  606.   dup /T 4 -1 roll put        % xref0start
  607.   dup /E 4 -1 roll put        % firstpageend
  608.   dup /H 5 -2 roll 1 index sub 2 array astore put    % phsstart, end-start
  609.   dup /O 1 pdffindpageref 0 get omap put
  610.   /N pdfpagecount put
  611. } bind def
  612.  
  613. % ---------------- Adjust object positions ---------------- %
  614.  
  615. /adjustObjectPositions {    % <boundary> <deltabelow> <deltaabove>
  616.                 %   adjustObjectPositions -
  617.     % Objects fall into 4 categories: LPDict, PHS, Catalog, and others.
  618.     % We handle the first two as special cases.
  619.   XRef {
  620.         % Stack: bdy below above key loc
  621.     dup 5 index ge { 2 } { 3 } ifelse index add
  622.     XRef 3 1 roll ld_put
  623.   } ld_forall pop pop pop
  624.   XRef LPDictN omap HeaderLength ld_put
  625.   XRef PHSN omap PHSStart ld_put
  626. } bind def
  627.  
  628. % ---------------- Write the output file ---------------- %
  629.  
  630. % Write objects identified by object number.
  631. /writeobjn {        % <obj#> writeobjn -
  632.   Generations 1 index lget pdfwriteobj
  633. } bind def
  634. /writeobjns {        % <obj#s> writeobjns -
  635.   { writeobjn } forall
  636. } bind def
  637. /lwriteobjns {        % <obj#s> writeobjns -
  638.   { writeobjn } lforall
  639. } bind def
  640.  
  641. % Write a part of the output file.
  642. /writePart {        % <proc> <label> writePart -
  643.   PDFOPTDEBUG {
  644.     dup print ( count=) print count =only ( start=) print
  645.     OFile { .fileposition } stopped { pop (???) } if =
  646.     2 .execn
  647.     print ( end=) print
  648.     OFile { .fileposition } stopped { pop (???) } if =
  649.   } {
  650.     pop exec
  651.   } ifelse
  652. } bind def
  653.  
  654. % Write the header.
  655. /writePart1 {        % - writePart1 -
  656.   {
  657.     pdfwriteheader
  658.   } (part1) writePart
  659. } bind def
  660.  
  661. % Write the linearization parameters dictionary.
  662. /writePart2 {        % - writePart2 -
  663.   {
  664.     LPDictN writeobjn
  665.   } (part2) writePart
  666. } bind def
  667.  
  668. % Write the First Page xref table and trailer.
  669. % Free variables: FirstPageXN.
  670. /writePart3 {        % <xrefstart> writePart3 -
  671.   {
  672.     FirstPageXN NObjects 1 add 1 index sub pdfwritexref
  673.     Trailer dup length 1 add dict copy
  674.     dup /Size NObjects 1 add put
  675.     dup /Prev 4 -1 roll put
  676.     pdfwritetrailer
  677.     0 pdfwritestartxref
  678.   } (part3) writePart
  679. } bind def
  680.  
  681. % Write the Catalog and other required document-level objects.
  682. % Free variables: CatalogNs.
  683. /writePart4 {        % - writePart4 -
  684.   {
  685.     CatalogNs lwriteobjns
  686.   } (part4) writePart
  687. } bind def
  688.  
  689. % Write the Primary Hint Stream.
  690. /writePart5 {        % - writePart5 -
  691.   {
  692.     PHSN writeobjn
  693.   } (part5) writePart
  694. } bind def
  695.  
  696. % Write the First Page's objects.
  697. % Free variables: FirstPageNs.
  698. /writePart6 {        % - writePart6 -
  699.   {
  700.     FirstPageNs writeobjns
  701.   } (part6) writePart
  702. } bind def
  703.  
  704. % Write the objects of other pages (Page + non-shared objects).
  705. % Free variables: OtherPageNs.
  706. /writePart7 {        % - writePart7 <lengths>
  707.   {
  708.     [ OtherPageNs {
  709.       OFile fileposition exch
  710.       writeobjns OFile fileposition exch sub
  711.     } forall ]
  712.   } (part7) writePart
  713. } bind def
  714.  
  715. % Write the shared objects of other pages.
  716. % Free variables: SharedNs.
  717. /writePart8 {        % - writePart8 -
  718.   {
  719.     SharedNs writeobjns
  720.   } (part8) writePart
  721. } bind def
  722.  
  723. % Write the other objects not associated with pages.
  724. % Free variables: NonPageNs.
  725. /writePart9 {        % - writePart9 -
  726.   {
  727.     NonPageNs { writeobjn } lforall
  728.   } (part9) writePart
  729. } bind def
  730.  
  731. % Write the main xref table and trailer.
  732. % Free variables: FirstPageXN.
  733. /writePart11xref {    % writePart11 -
  734.   {
  735.     0 FirstPageXN pdfwritexref
  736.   } (part11xref) writePart
  737. } bind def
  738. /writePart11rest {    % <part3start> writePart11rest -
  739.   {
  740.     << /Size FirstPageXN >> pdfwritetrailer
  741.     pdfwritestartxref
  742.   } (part11rest) writePart
  743. } bind def
  744.  
  745. % ---------------- Write hint tables ---------------- %
  746.  
  747. /bitsneeded {        % <maxvalue> bitsneeded <#bits>
  748.   0 exch { dup 0 eq { pop exit } if exch 1 add exch 2 idiv } loop
  749. } bind def
  750.  
  751. % Find the start and end of objects in the output.
  752. /omstart {        % <obj#> omstart <pos>
  753.   PDFOPTDEBUG { (start\() print dup =only } if
  754.   omap
  755.   PDFOPTDEBUG { (=>) print dup =only } if
  756.   XRef exch ld_get
  757.   PDFOPTDEBUG { (\) = ) print dup = } if
  758. } bind def
  759. /omend {        % <obj#> omend <pos>
  760.     % The end of an object is the start of the next object.
  761.     % The caller must be sure that this object is not the last one
  762.     % in part 9.
  763.   PDFOPTDEBUG { (end\() print dup =only } if
  764.   omap
  765.   PDFOPTDEBUG { (=>) print dup =only } if
  766.   1 add
  767.     % Check that the requested object wasn't the last one in part 6:
  768.     % the next object in the output file is the first in part 7.
  769.   PHSN omap 1 index eq { pop 1 } if
  770.   XRef exch ld_get
  771.   PDFOPTDEBUG { (\) = ) print dup = } if
  772. } bind def
  773. /omlength {        % <obj#> omlength <length>
  774.   dup omend exch omstart sub
  775. } bind def
  776.  
  777. % Find the Contents of a page.
  778. /contentsobjects { % <pagedict> contentsobjects <firstobj#> <lastobj#> true
  779.            % <pagedict> contentsobjects false
  780.   /Contents .knownget {
  781.     dup oforce                   % ref []
  782.     dup type /dicttype eq {
  783.       pop 0 get dup true         % ref ref
  784.     } {
  785.       exch pop                   % []
  786.       dup length 0 ne {
  787.         dup 0 get 0 get          % [] 1st
  788.         exch dup                 % 1st [] [] 
  789.         length 1 sub get 0 get   % 1st last 
  790.         true
  791.       } {
  792.         pop false
  793.       } ifelse
  794.     } ifelse
  795.   } {
  796.     false
  797.   } ifelse
  798. } bind def
  799.  
  800. /contentsstart {    % <pagedict> contentsstart <pos> true
  801.             % <pagedict> contentsstart false
  802.   contentsobjects { pop omstart true } { false } ifelse
  803. } bind def
  804.  
  805. /contentslength {    % <pagedict> contentslength <length>
  806.   contentsobjects { omend exch omstart sub } { 0 } ifelse
  807. } bind def
  808.  
  809.  
  810. /writePageOffsetHints {
  811.   PDFOPTDEBUG { /writePageOffsetHints == } if
  812.   20 dict begin
  813.   /bits OFile bitstream def
  814.  
  815.     % Calculate least length of a page.
  816.   FirstPageLength OtherPageLengths { .min } forall
  817.   /minpl exch def
  818.  
  819.         % Calculate least contents length.
  820.   FirstPageNs 0 get Objects exch lget contentslength
  821.   OtherPageNs { 0 get Objects exch lget contentslength .min } forall
  822.   /mincl exch def
  823.  
  824.     % The Adobe documentation says that all versions of Acrobat
  825.     % require item 8 (mincl) to be zero.  Patch this here.
  826.   /mincl 0 def
  827.  
  828.     % Calculate bits needed to represent greatest page length.
  829.   FirstPageLength OtherPageLengths { .max } forall
  830.   minpl sub bitsneeded /maxplbits exch def
  831.     % Calculate bits needed to represent the greatest Contents length.
  832.   FirstPageNs 0 get Objects exch lget contentslength
  833.   OtherPageNs { 0 get Objects exch lget contentslength .max } forall
  834.   mincl sub bitsneeded /maxclbits exch def
  835.  
  836.     % Per Adobe documentation, Acrobat requires that item 5 (maxplbits)
  837.     % be equal to item 9 (maxclbits).  Set both to the max of the two.
  838.   maxplbits maxclbits .max /maxplbits 1 index def /maxclbits exch def
  839.  
  840.         % Mapping from object number to shared object reference
  841.   /shared_id_dict FirstPageNs length SharedNs length add dict begin
  842.   0 FirstPageNs { 1 index def 1 add } forall
  843.     SharedNs { 1 index def 1 add } forall
  844.   pop
  845.   currentdict end def 
  846.  
  847.                 % Table F.3 Page offset hint table, header section
  848.  
  849.                 % 1: Least number of objects in a page:
  850.   FirstPageNs length OtherPageNs { length .min } forall
  851.   /minnop 1 index def 32 bwn
  852.         % 2: Location of first page's Page object:
  853.   FirstPageNs 0 get omap XRef exch ld_get 32 bwn
  854.         % 3: Bits needed to represent greatest # of objects in a page:
  855.   FirstPageNs length OtherPageNs { length .max } forall
  856.   minnop sub bitsneeded /maxnopbits 1 index def 16 bwn
  857.         % 4: Least length of a page:
  858.   minpl 32 bwn
  859.         % 5: Bits needed to represent the greatest page length:
  860.   maxplbits 16 bwn
  861.         % 6: Least start of Contents offset:
  862.   0        % (Acrobat requires that this be 0.)
  863.   /minsco 1 index def 32 bwn
  864.         % 7: Bits needed to represent the greatest start of Contents
  865.         % offset:
  866.   0        % (Acrobat ignores this.)
  867.   /maxscobits 1 index def 16 bwn
  868.         % 8: Least contents length:
  869.   mincl 32 bwn
  870.         % 9: Bits needed to represent the greatest Contents length:
  871.   maxclbits 16 bwn
  872.         % 10: Bits needed to represent the greatest number of Shared
  873.            % Object references:
  874.   FirstPageNs length SharedPageNs { length .max } forall bitsneeded
  875.   /maxsorbits 1 index def 16 bwn
  876.         % 11: Bits needed to identify a Shared Object:
  877.   FirstPageNs length SharedNs length add bitsneeded
  878.   /sobits 1 index def 16 bwn
  879.         % 12: Bits needed to represent numerator of fraction:
  880.   2
  881.   /numfbits 1 index def 16 bwn
  882.         % 13: Denominator of fraction:
  883.   1
  884.   /denf 1 index def 16 bwn
  885.  
  886.                 % Table F.4 Page offset hint table, per-page entry
  887.  
  888.                 % 1: Number of objects in pages:
  889.   FirstPageNs length minnop sub maxnopbits bwn
  890.   OtherPageNs {
  891.     length minnop sub maxnopbits bwn
  892.   } forall
  893.   bits bitflush
  894.  
  895.         % 2: Total length of pages in bytes;
  896.   FirstPageLength minpl sub maxplbits bwn
  897.   OtherPageLengths {
  898.     minpl sub maxplbits bwn
  899.   } forall
  900.   bits bitflush
  901.  
  902.         % 3: Number of shared objects referenced from page:
  903.   FirstPageNs length maxsorbits bwn 
  904.   SharedPageNs { length maxsorbits bwn } forall
  905.   bits bitflush
  906.                 % 4: A shared object identifier:
  907.   FirstPageNs { shared_id_dict exch get sobits bwn } forall
  908.   SharedPageNs {
  909.     { shared_id_dict exch get sobits bwn
  910.     } forall
  911.   } forall
  912.   bits bitflush
  913.  
  914.                 % 5: Numerator of fractional position for each shared object:
  915.   FirstPageNs { pop 0 numfbits bwn  } forall
  916.   SharedPageNs {
  917.     { pop 0 numfbits bwn 
  918.     } forall
  919.   } forall
  920.   bits bitflush
  921.  
  922.         % 6: Contents offsets:
  923.                 % Following Implementation Note 133 section 6 is empty.
  924.   maxscobits 0 gt {
  925.     [FirstPageNs OtherPageNs aload pop] {
  926.        0 get Objects exch lget contentsstart { minsco sub } { 0 } ifelse
  927.        maxscobits bwn
  928.     } forall
  929.     bits bitflush
  930.   } if
  931.  
  932.                 % 7: Contents lengths:
  933.   [FirstPageNs OtherPageNs aload pop] {
  934.     0 get Objects exch lget contentslength mincl sub maxclbits bwn
  935.   } forall
  936.   bits bitflush
  937.  
  938.   end
  939.  
  940. } bind def
  941.  
  942. /writeSharedObjectHints {
  943.   PDFOPTDEBUG { /writeSharedObjectHints == } if
  944.   20 dict begin
  945.   /bits OFile bitstream def
  946.   /obj_count SharedNs length FirstPageNs length add def
  947.  
  948.          % Table F.5 Shared object hint table, header section
  949.  
  950.                   % 1: Object number of first object in Shared Objects section
  951.   0 32 bwn
  952.         % 2: Location of first object in Shared Objects section:
  953.         % If there are no shared objects,
  954.         % Acrobat sets this to the location of linearization
  955.         % parameters object (the very first object).
  956.   { pdfwriteheader } tomemory length 32 bwn
  957.         % 3: Number of Shared Object entries for first page:
  958.   FirstPageNs length 32 bwn
  959.         % 4: Number of Shared Object entries for Shared Objects
  960.         % section
  961.   obj_count 32 bwn
  962.         % 5: Bits needed to represent the greatest number of objects
  963.         % in a shared object group (always 0, because all groups
  964.         % have only 1 object):
  965.   0 16 bwn
  966.         % 6: Least length of a Shared Object Group in bytes:
  967.   16#7fffffff FirstPageNs { omlength .min } forall
  968.                  SharedNs { omlength .min } forall
  969.   /minsol 1 index def 32 bwn
  970.         % 7: Bits needed to represent the greatest length of a
  971.         % Shared Object Group:
  972.   0 FirstPageNs { omlength .max } forall
  973.        SharedNs { omlength .max } forall
  974.   minsol sub bitsneeded
  975.   /maxsolbits 1 index def 16 bwn
  976.  
  977.                 % Table F.6 Shared object hint table, shared object group entry
  978.  
  979.                 % 1: Lengths of shared object groups:
  980.   FirstPageNs { omlength minsol sub maxsolbits bwn } forall
  981.      SharedNs { omlength minsol sub maxsolbits bwn } forall
  982.   bits bitflush
  983.         % 2: MD5 flag:
  984.   obj_count { 0 1 bwn } repeat
  985.   bits bitflush
  986.                 % 3: No MD5 shared object signatures.
  987.  
  988.                 % 4: No number_number_of_objects_in_the_group - 1
  989.   end
  990. } bind def
  991.  
  992. % ---------------- Main program ---------------- %
  993.  
  994. /pdfOptimize {        % <infile> <outfile> pdfOptimize -
  995.   realtime 3 1 roll
  996.   exch pdfdict begin pdfopenfile dup begin
  997.   40 dict begin
  998.   /IDict exch def
  999.   /OFile exch def
  1000.   /starttime exch def
  1001.   /now {
  1002.     QUIET { pop } { print (, t = ) print realtime starttime sub = flush } ifelse
  1003.   } def
  1004.   omapinit
  1005.   
  1006.     % Create and open a temporary file.
  1007.     % Because of .setsafe, we have to open it as read-write, rather than
  1008.     % opening for writing, then closing it and reopening it for reading.
  1009.  
  1010.   null (w+) .tempfile /TFile exch def /TFileName exch def
  1011.   .setsafe
  1012.  
  1013.     % Read all objects into memory.
  1014.  
  1015.   Trailer touch
  1016.   (Read objects) now
  1017.  
  1018.         % Encrypted files are not yet supported.
  1019.   Trailer /Encrypt known {
  1020.     (ERROR: Encrypted files are not yet supported.) = flush
  1021.     /pdfOptimize cvx /limitcheck signalerror
  1022.   } if
  1023.  
  1024.     % Replace indirect references to numbers.  This is needed
  1025.     % for the Length of streams, and doesn't hurt anything else.
  1026.  
  1027.   replaceReferences
  1028.   (Replaced references) now
  1029.  
  1030.     % Create the two new objects: the linearization parameter
  1031.     % dictionary, and the Primary Hint Stream.
  1032.  
  1033.   /LPDict 10 dict def
  1034.   /PHS 10 dict cvx def        % executable = stream
  1035.   [LPDict PHS] createObjects
  1036.   /LPDictN 1 index def 1 add
  1037.   /PHSN exch def
  1038.   PDFOPTDEBUG { << /LPDictN LPDictN /PHSN PHSN >> === } if
  1039.  
  1040.     % Count the number of objects in the output.
  1041.  
  1042.   0 0 1 Objects llength 1 sub {
  1043.     Generations exch lget 0 ne { 1 add } if
  1044.   } for
  1045.   /NObjects exch def
  1046.   QUIET not { NObjects =only ( objects total) = flush } if
  1047.  
  1048.     % Propagate inherited attributes down the page tree.
  1049.  
  1050.   propagateAttributes
  1051.   (Propagated attributes) now
  1052.  
  1053.     % Identify the document-level objects (part 4).
  1054.  
  1055.   identifyDocumentObjects /CatalogNs exch def
  1056.   QUIET not { CatalogNs === flush } if
  1057.   (Identified Catalog) now
  1058.  
  1059.       % Identify the first page's objects (part 6),
  1060.     % including the Outlines tree if appropriate.
  1061.  
  1062.   pdfopencache
  1063.   /FirstPageNs identifyFirstPageObjects def
  1064.   QUIET not { FirstPageNs === flush } if
  1065.   (Identified first page) now
  1066.  
  1067.     % Identify shared vs. non-shared objects for remaining pages
  1068.     % (parts 7 and 8).
  1069.  
  1070.   identifyOtherPageObjects
  1071.   /SharedNs exch def
  1072.   /SharedPageNs exch def
  1073.   /OtherPageNs exch def
  1074.   QUIET not { OtherPageNs === flush SharedNs === flush } if
  1075.   (Identified other pages) now
  1076.  
  1077.     % Identify objects not associated with any page (part 9).
  1078.  
  1079.   /NonPageNs identifyNonPageObjects def
  1080.   QUIET not { NonPageNs { === } forall flush } if
  1081.   (Identified non-pages) now
  1082.  
  1083.     % Assign final object numbers to all the objects.
  1084.     % (The omap is currently empty.)
  1085.  
  1086.   /FirstPageXN assignObjectNumbers def
  1087.   (Assigned objects #s) now
  1088.  
  1089.     % Write the document-level objects (part 4).
  1090.  
  1091.   { writePart4 } totemp
  1092.   /CatalogTempEnd exch def /CatalogTempStart exch def
  1093.   (Wrote Catalog) now
  1094.  
  1095.     % Write the first page's objects (part 6).
  1096.  
  1097.   { writePart6 } totemp
  1098.   /FirstPageTempEnd exch def /FirstPageTempStart exch def
  1099.   (Wrote first page) now
  1100.  
  1101.     % Write the non-shared objects for other pages (part 7).
  1102.  
  1103.   { writePart7 /OtherPageLengths exch def } totemp
  1104.   /OtherPageTempEnd exch def /OtherPageTempStart exch def
  1105.   (Wrote other pages) now
  1106.  
  1107.     % Write the shared objects for other pages (part 8).
  1108.  
  1109.   { writePart8 } totemp
  1110.   /SharedTempEnd exch def /SharedTempStart exch def
  1111.   (Wrote shared objects) now
  1112.  
  1113.     % Write the objects not associated with pages (part 9).
  1114.  
  1115.   { writePart9 } totemp
  1116.   /NonPageTempEnd exch def /NonPageTempStart exch def
  1117.  
  1118.     % Compute conservative lengths of parts 2,3,5,11 of the output.
  1119.     % It's OK for these to be too large, but not too small.
  1120.  
  1121.   % Make dummy XRef entres for LPDict and PHS.
  1122.   XRef LPDictN omap 0 ld_put
  1123.   XRef PHSN omap 0 ld_put
  1124.  
  1125.   /HeaderLength {    % this is exact
  1126.     writePart1            % part 1
  1127.   } tolength def
  1128.   /CatalogLength    % this is exact
  1129.     CatalogTempEnd CatalogTempStart sub def    % part 4
  1130.   /FirstPageLength    % this is exact
  1131.     FirstPageTempEnd FirstPageTempStart sub def    % part 6
  1132.   /OtherObjectsLength    % this is exact
  1133.     NonPageTempEnd OtherPageTempStart sub def    % parts 7,8,9
  1134.   /ObjectsLength    % this is exact
  1135.     CatalogLength FirstPageLength add OtherObjectsLength add def
  1136.   /XrefLength {            % part 11
  1137.     % The LPDict must end within the first 1024 bytes,
  1138.     % so the start of the FirstPage xref table can't exceed 1024.
  1139.     writePart11xref 1024 writePart11rest
  1140.   } tolength def
  1141.   /NominalFileLength     % Make a generous allowance for parts 2,3,5.
  1142.     HeaderLength ObjectsLength 3 mul add 10000 add 99999 .max def
  1143.   /FirstPageXrefLength {    % part 3
  1144.     NominalFileLength writePart3
  1145.   } tolength def
  1146.   /LPDictLength {        % part 2
  1147.     NominalFileLength dup 2 mul 2 copy add 1 index dup createLPDict writePart2
  1148.   } tolength def
  1149.  
  1150.     % Compute a few additional values from the above.
  1151.  
  1152.   /XrefBeginLength {
  1153.     (xref\n0 ) ows
  1154.     OFile FirstPageXN write=
  1155.   } tolength def
  1156.   HeaderLength LPDictLength add
  1157.   /FirstPageXrefStart 1 index def
  1158.   FirstPageXrefLength add
  1159.   /CatalogStart 1 index def
  1160.   CatalogLength add        % phsstart
  1161.   /PHSStart exch def
  1162.  
  1163.     % Adjust the object positions ignoring PHS.
  1164.     % (Writing the PHS needs these.)
  1165.  
  1166.   0 0 CatalogStart CatalogTempStart sub adjustObjectPositions
  1167.   % Make a temporary XRef entry for the PHS, for the benefit of omend.
  1168.   XRef PHSN omap CatalogStart ld_put
  1169.   (Adjusted positions) now
  1170.  
  1171.         % Construct the hint tables (part 5).
  1172.  
  1173.   { writePageOffsetHints } totemp
  1174.   pop /PHSTempStart exch def
  1175.   { writeSharedObjectHints } totemp
  1176.   exch PHSTempStart sub PHS /S 3 -1 roll put
  1177.   PHSTempStart sub /PHSTempLength exch def
  1178.   (Wrote hints) now
  1179.  
  1180.   % Prepare to read TFile.
  1181. %  TFile closefile
  1182. %  /TFile TFileName (r) file def
  1183.  
  1184.   PHS
  1185.     dup /File TFile put
  1186.     dup /FilePosition PHSTempStart put
  1187.     dup /Length PHSTempLength put
  1188.   pop
  1189.   /PHSLength { writePart5 } tolength def
  1190.  
  1191.     % Construct the linearization parameter dictionary (part 2).
  1192.  
  1193.   PHSStart
  1194.   dup PHSLength add        % phsend
  1195.   /FirstPageStart 1 index def
  1196.   dup FirstPageLength add    % firstpageend
  1197.   dup OtherObjectsLength add
  1198.   /XrefStart 1 index def
  1199.   XrefBeginLength add        % xref0start
  1200.   dup XrefBeginLength sub XrefLength add    % fileend
  1201.     % Because of a bug, Acrobat Reader doesn't recognize any file
  1202.     % shorter than 1K as linearized.  Patch this here.
  1203.   1024 .max
  1204.   /FileLength 1 index def
  1205.   createLPDict
  1206.  
  1207.     % Adjust the object positions again, taking the PHS into account.
  1208.  
  1209.   PHSStart 0 PHSLength adjustObjectPositions
  1210.   (Readjusted positions) now
  1211.  
  1212.     % Finally, write the output file.
  1213.  
  1214.   writePart1
  1215.   writePart2
  1216.   FirstPageXrefStart padto
  1217.   XrefStart writePart3
  1218.   CatalogStart padto
  1219.   CatalogTempStart CatalogTempEnd copyrange    % part 4
  1220.   writePart5
  1221.   FirstPageStart padto
  1222.   FirstPageTempStart NonPageTempEnd copyrange    % parts 6,7,8,9
  1223.   % No Overflow Hint Stream (part 10).
  1224.   XrefStart padto
  1225.   writePart11xref
  1226.   { FirstPageXrefStart writePart11rest } tomemory
  1227.   FileLength 1 index length sub padto ows
  1228.   (Wrote output file) now
  1229.  
  1230.     % Wrap up.
  1231.  
  1232.   TFile closefile TFileName deletefile
  1233.   end        % temporary dict
  1234.   end        % IDict
  1235. } bind def
  1236.  
  1237. end            % pdfoptdict
  1238. .setglobal
  1239.  
  1240. % Check for command line arguments.
  1241. [ shellarguments {
  1242.   ] dup length 2 eq {
  1243.     % Load the pdfwrite utilities if necessary.
  1244.     /omapinit where { pop } { (pdfwrite.ps) runlibfile } ifelse
  1245.     save exch
  1246.     aload pop exch (r) file exch (w) file
  1247.     3000000 setvmthreshold
  1248.     0 setobjectformat
  1249.     pdfoptdict begin pdfOptimize end
  1250.     restore
  1251.   } {
  1252.     (Usage: gs -dNODISPLAY -- pdfopt.ps input.pdf output.pdf) = flush quit
  1253.   } ifelse
  1254. } {
  1255.   pop
  1256. } ifelse
  1257.