home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / ghostscript / 8.64 / lib / pdfopt.ps < prev    next >
Encoding:
Text File  |  2009-04-17  |  35.7 KB  |  1,273 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 9390 2009-01-23 19:04:40Z alexcher $
  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 dup //null ne { { omvisitnopage } forall } { pop } ifelse
  388.     } if
  389.     dup /AcroForm .knownget { omvisitnopage } 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.   /filter_params 10 darray def
  424.   /images 10 darray def
  425.   /fontfiles 10 darray def
  426.   {
  427.     omforce
  428.         % Stack: pageobj# extra page
  429.         % Visit any extra objects if applicable.
  430.     exch omvisitnopage
  431.         % Visit Annots, if any.
  432.         % We don't try to defer the drawing information.
  433.     dup /Annots .knownget { omvisitnopage } if
  434.         % Visit beads.
  435.     dup /B .knownget { omvisitnopage } if
  436.         % Visit resources dictionaries.
  437.     dup /Resources .knownget {
  438.       omforce dup {
  439.         % Visit the first-level Resource dictionaries.
  440.     omforce pop pop
  441.       } forall {
  442.         % Visit the resources themselves.
  443.         % Skip Image XObjects, and FontFile streams if the
  444.         % FontDescriptor Flags have bit 6 set.
  445.         % We don't try to visit the resources in the order in which
  446.         % the Contents stream(s) reference(s) them.
  447.     exch dup /XObject eq {
  448.       pop oforce {
  449.         dup oforce /Subtype get /Image eq {
  450.               dup oforce /DecodeParms .knownget {
  451.                 oforce {
  452.                   exch /JBIG2Globals eq {
  453.                     filter_params exch dadd
  454.                   } {
  455.                     pop
  456.                   } ifelse
  457.                 } forall
  458.               } if
  459.           images exch dadd
  460.         } {
  461.           omvisit
  462.         } ifelse pop
  463.       } forall
  464.     } {
  465.       /Font eq {
  466.         oforce { identifyfont pop } forall
  467.       } {
  468.         oforce omvisit
  469.       } ifelse
  470.     } ifelse
  471.       } forall
  472.     } if
  473.         % Visit the Contents stream(s).
  474.     dup /Contents .knownget { omvisit } if
  475.         % Visit Image XObjects.  We don't try to visit them in
  476.         % reference order.
  477.     filter_params { omvisit } daforall
  478.     images { omvisit } daforall
  479.         % Visit FontFile streams.  We don't try to visit them in
  480.         % reference order.
  481.     fontfiles { omvisit } daforall
  482.     pop
  483.   } visited end
  484.         % Stack: pageobj# obj#s_larray
  485.   [ 3 1 roll {
  486.     2 copy eq { pop } { exch } ifelse
  487.   } lforall counttomark 1 roll ]
  488.   PDFOPTDEBUG {
  489.     (%Objects = ) print dup === flush
  490.   } if
  491. } bind def
  492.  
  493. % Identify the objects of the first page.
  494. /identifyFirstPageObjects {    % - identifyFirstPageObjects <obj#s>
  495.   Trailer /Root oget null
  496.   1 index /PageMode knownoget {
  497.     /UseOutlines eq {
  498.       1 index /Outlines knownoget { exch pop } if
  499.     } if
  500.   } if exch pop
  501.   1 identifyPageObjects
  502. } bind def
  503.  
  504. % Identify the non-shared objects of the other pages, and the shared objects.
  505. % Note that the page objects themselves may appear to be shared, because of
  506. % references from Dest entries in annotations, but they must be treated as
  507. % non-shared.  Note also that some objects referenced on the first page may
  508. % also be referenced from other pages.
  509. /identifyOtherPageObjects {    % - identifyOtherPageObjects [<pageobj#s> ...]
  510.                 %   <sharedobj#s>
  511.   4 dict begin
  512.   /marks lstring Objects llength lgrowto def
  513.         % Collect objects of other pages and identify sharing.
  514.   [ 2 1 pdfpagecount { null exch identifyPageObjects } for ]
  515.   dup {
  516.     { marks exch 2 copy lget 1 add 254 .min lput } forall
  517.   } forall
  518.         % Mark document-level and first page objects.
  519.   CatalogNs { marks exch 255 lput } lforall
  520.   FirstPageNs { marks exch 255 lput } forall
  521.         % Mark the page objects themselves as non-shared.
  522.   dup {
  523.     0 get marks exch 1 lput
  524.   } forall
  525.         % Collect the non-shared objects of each page.
  526.   dup
  527.   [ exch {
  528.     [ exch {
  529.       marks 1 index lget 1 ne { pop } if
  530.     } forall ]
  531.   } forall ]
  532.                 % Collect the shared objects of each page.
  533.   exch
  534.   [ exch {
  535.     [ exch {
  536.       marks 1 index lget dup 1 le exch 255 eq or { pop } if
  537.     } forall ]
  538.   } forall ]
  539.  
  540.                 % Collect the shared objects.
  541.   [ 1 1 marks llength 1 sub {
  542.     marks 1 index lget dup 1 le exch 255 eq or { pop } if
  543.   } for ]
  544.  
  545.   end
  546. } bind def
  547.  
  548. % Identify objects not associated with any page.
  549. /identifyNonPageObjects {    % - identifyNonPageObjects <obj#s>
  550.   4 dict begin
  551.   /marks lstring Objects llength lgrowto def
  552.  
  553.   LPDictN     marks exch 1 lput
  554.   PHSN        marks exch 1 lput
  555.   CatalogNs   { marks exch 1 lput } lforall
  556.   FirstPageNs { marks exch 1 lput } forall
  557.   SharedNs    { marks exch 1 lput } forall
  558.   OtherPageNs { { marks exch 1 lput } forall } forall
  559.  
  560.     %****** PUT THESE IN A REASONABLE ORDER ******
  561.   /npobj larray
  562.   0
  563.   1 1 Objects llength 1 sub {
  564.     marks 1 index lget 0 eq {
  565.       Generations exch lget 0 ne { 1 add } if
  566.     } {
  567.       pop
  568.     } ifelse
  569.   } for 
  570.   lgrowto def
  571.  
  572.   0
  573.   1 1 Objects llength 1 sub {
  574.     marks 1 index lget 0 eq {
  575.                                           % i
  576.       Generations 1 index lget 0 ne {
  577.                                           % i
  578.         npobj 2 index                     % i nobj 0
  579.         3 -1 roll                         % nobj 0 i
  580.         lput 1 add
  581.       } {
  582.         pop
  583.       } ifelse
  584.     } {
  585.       pop
  586.     } ifelse
  587.   } for 
  588.   pop
  589.  
  590.   npobj
  591.   end
  592. } bind def
  593.  
  594. % ---------------- Assign object numbers ---------------- %
  595.  
  596. % Assign object numbers to all objects that will be copied.
  597. % Return the first (translated) object number in the First Page xref table.
  598. /assignObjectNumbers {        % - assignObjectNumbers -
  599.   OtherPageNs { { omap pop } forall } forall
  600.   SharedNs { omap pop } forall
  601.   NonPageNs { omap pop } lforall
  602.         % Assign object numbers for the First Page xref table last.
  603.   LPDictN omap    % don't pop, this is the return value
  604.   CatalogNs { omap pop } lforall
  605.   FirstPageNs { omap pop } forall
  606.   PHSN omap pop
  607. } bind def
  608.  
  609. % ---------------- Create the LPDict ---------------- %
  610.  
  611. % Create the contents of the LPDict.
  612. /createLPDict {            % <phsstart> <phsend> <firstpageend>
  613.                 %   <xref0start> <filelength> createLPDict -
  614.   LPDict
  615.   dup /Linearized 1 put
  616.   dup /L 4 -1 roll put        % filelength
  617.   dup /T 4 -1 roll put        % xref0start
  618.   dup /E 4 -1 roll put        % firstpageend
  619.   dup /H 5 -2 roll 1 index sub 2 array astore put    % phsstart, end-start
  620.   dup /O 1 pdffindpageref 0 get omap put
  621.   /N pdfpagecount put
  622. } bind def
  623.  
  624. % ---------------- Adjust object positions ---------------- %
  625.  
  626. /adjustObjectPositions {    % <boundary> <deltabelow> <deltaabove>
  627.                 %   adjustObjectPositions -
  628.     % Objects fall into 4 categories: LPDict, PHS, Catalog, and others.
  629.     % We handle the first two as special cases.
  630.   XRef {
  631.         % Stack: bdy below above key loc
  632.     dup 5 index ge { 2 } { 3 } ifelse index add
  633.     XRef 3 1 roll ld_put
  634.   } ld_forall pop pop pop
  635.   XRef LPDictN omap HeaderLength ld_put
  636.   XRef PHSN omap PHSStart ld_put
  637. } bind def
  638.  
  639. % ---------------- Write the output file ---------------- %
  640.  
  641. % Write objects identified by object number.
  642. /writeobjn {        % <obj#> writeobjn -
  643.   Generations 1 index lget pdfwriteobj
  644. } bind def
  645. /writeobjns {        % <obj#s> writeobjns -
  646.   { writeobjn } forall
  647. } bind def
  648. /lwriteobjns {        % <obj#s> writeobjns -
  649.   { writeobjn } lforall
  650. } bind def
  651.  
  652. % Write a part of the output file.
  653. /writePart {        % <proc> <label> writePart -
  654.   PDFOPTDEBUG {
  655.     dup print ( count=) print count =only ( start=) print
  656.     OFile { .fileposition } stopped { pop (???) } if =
  657.     2 .execn
  658.     print ( end=) print
  659.     OFile { .fileposition } stopped { pop (???) } if =
  660.   } {
  661.     pop exec
  662.   } ifelse
  663. } bind def
  664.  
  665. % Write the header.
  666. /writePart1 {        % - writePart1 -
  667.   {
  668.     pdfwriteheader
  669.   } (part1) writePart
  670. } bind def
  671.  
  672. % Write the linearization parameters dictionary.
  673. /writePart2 {        % - writePart2 -
  674.   {
  675.     LPDictN writeobjn
  676.   } (part2) writePart
  677. } bind def
  678.  
  679. % Write the First Page xref table and trailer.
  680. % Free variables: FirstPageXN.
  681. /writePart3 {        % <xrefstart> writePart3 -
  682.   {
  683.     FirstPageXN NObjects 1 add 1 index sub pdfwritexref
  684.     Trailer dup length 1 add dict copy Trailer xcheck { cvx } if
  685.     dup /Size NObjects 1 add put
  686.     dup /Prev 4 -1 roll put
  687.     pdfwritetrailer
  688.     0 pdfwritestartxref
  689.   } (part3) writePart
  690. } bind def
  691.  
  692. % Write the Catalog and other required document-level objects.
  693. % Free variables: CatalogNs.
  694. /writePart4 {        % - writePart4 -
  695.   {
  696.     CatalogNs lwriteobjns
  697.   } (part4) writePart
  698. } bind def
  699.  
  700. % Write the Primary Hint Stream.
  701. /writePart5 {        % - writePart5 -
  702.   {
  703.     PHSN writeobjn
  704.   } (part5) writePart
  705. } bind def
  706.  
  707. % Write the First Page's objects.
  708. % Free variables: FirstPageNs.
  709. /writePart6 {        % - writePart6 -
  710.   {
  711.     FirstPageNs writeobjns
  712.   } (part6) writePart
  713. } bind def
  714.  
  715. % Write the objects of other pages (Page + non-shared objects).
  716. % Free variables: OtherPageNs.
  717. /writePart7 {        % - writePart7 <lengths>
  718.   {
  719.     [ OtherPageNs {
  720.       OFile fileposition exch
  721.       writeobjns OFile fileposition exch sub
  722.     } forall ]
  723.   } (part7) writePart
  724. } bind def
  725.  
  726. % Write the shared objects of other pages.
  727. % Free variables: SharedNs.
  728. /writePart8 {        % - writePart8 -
  729.   {
  730.     SharedNs writeobjns
  731.   } (part8) writePart
  732. } bind def
  733.  
  734. % Write the other objects not associated with pages.
  735. % Free variables: NonPageNs.
  736. /writePart9 {        % - writePart9 -
  737.   {
  738.     NonPageNs { writeobjn } lforall
  739.   } (part9) writePart
  740. } bind def
  741.  
  742. % Write the main xref table and trailer.
  743. % Free variables: FirstPageXN.
  744. /writePart11xref {    % writePart11 -
  745.   {
  746.     0 FirstPageXN pdfwritexref
  747.   } (part11xref) writePart
  748. } bind def
  749. /writePart11rest {    % <part3start> writePart11rest -
  750.   {
  751.     << /Size FirstPageXN >> pdfwritetrailer
  752.     pdfwritestartxref
  753.   } (part11rest) writePart
  754. } bind def
  755.  
  756. % ---------------- Write hint tables ---------------- %
  757.  
  758. /bitsneeded {        % <maxvalue> bitsneeded <#bits>
  759.   0 exch { dup 0 eq { pop exit } if exch 1 add exch 2 idiv } loop
  760. } bind def
  761.  
  762. % Find the start and end of objects in the output.
  763. /omstart {        % <obj#> omstart <pos>
  764.   PDFOPTDEBUG { (start\() print dup =only } if
  765.   omap
  766.   PDFOPTDEBUG { (=>) print dup =only } if
  767.   XRef exch ld_get
  768.   PDFOPTDEBUG { (\) = ) print dup = } if
  769. } bind def
  770. /omend {        % <obj#> omend <pos>
  771.     % The end of an object is the start of the next object.
  772.     % The caller must be sure that this object is not the last one
  773.     % in part 9.
  774.   PDFOPTDEBUG { (end\() print dup =only } if
  775.   omap
  776.   PDFOPTDEBUG { (=>) print dup =only } if
  777.   1 add
  778.     % Check that the requested object wasn't the last one in part 6:
  779.     % the next object in the output file is the first in part 7.
  780.   PHSN omap 1 index eq { pop 1 } if
  781.   XRef exch ld_get
  782.   PDFOPTDEBUG { (\) = ) print dup = } if
  783. } bind def
  784. /omlength {        % <obj#> omlength <length>
  785.   dup omend exch omstart sub
  786. } bind def
  787.  
  788. % Find the Contents of a page.
  789. /contentsobjects { % <pagedict> contentsobjects <firstobj#> <lastobj#> true
  790.            % <pagedict> contentsobjects false
  791.   /Contents .knownget {
  792.     dup oforce                   % ref []
  793.     dup type /dicttype eq {
  794.       pop 0 get dup true         % ref ref
  795.     } {
  796.       exch pop                   % []
  797.       dup length 0 ne {
  798.         dup 0 get 0 get          % [] 1st
  799.         exch dup                 % 1st [] [] 
  800.         length 1 sub get 0 get   % 1st last 
  801.         true
  802.       } {
  803.         pop false
  804.       } ifelse
  805.     } ifelse
  806.   } {
  807.     false
  808.   } ifelse
  809. } bind def
  810.  
  811. /contentsstart {    % <pagedict> contentsstart <pos> true
  812.             % <pagedict> contentsstart false
  813.   contentsobjects { pop omstart true } { false } ifelse
  814. } bind def
  815.  
  816. /contentslength {    % <pagedict> contentslength <length>
  817.   contentsobjects { omend exch omstart sub } { 0 } ifelse
  818. } bind def
  819.  
  820.  
  821. /writePageOffsetHints {
  822.   PDFOPTDEBUG { /writePageOffsetHints == } if
  823.   20 dict begin
  824.   /bits OFile bitstream def
  825.  
  826.     % Calculate least length of a page.
  827.   FirstPageLength OtherPageLengths { .min } forall
  828.   /minpl exch def
  829.  
  830.         % Calculate least contents length.
  831.   FirstPageNs 0 get Objects exch lget contentslength
  832.   OtherPageNs { 0 get Objects exch lget contentslength .min } forall
  833.   /mincl exch def
  834.  
  835.     % The Adobe documentation says that all versions of Acrobat
  836.     % require item 8 (mincl) to be zero.  Patch this here.
  837.   /mincl 0 def
  838.  
  839.     % Calculate bits needed to represent greatest page length.
  840.   FirstPageLength OtherPageLengths { .max } forall
  841.   minpl sub bitsneeded /maxplbits exch def
  842.     % Calculate bits needed to represent the greatest Contents length.
  843.   FirstPageNs 0 get Objects exch lget contentslength
  844.   OtherPageNs { 0 get Objects exch lget contentslength .max } forall
  845.   mincl sub bitsneeded /maxclbits exch def
  846.  
  847.     % Per Adobe documentation, Acrobat requires that item 5 (maxplbits)
  848.     % be equal to item 9 (maxclbits).  Set both to the max of the two.
  849.   maxplbits maxclbits .max /maxplbits 1 index def /maxclbits exch def
  850.  
  851.         % Mapping from object number to shared object reference
  852.   /shared_id_dict FirstPageNs length SharedNs length add dict begin
  853.   0 FirstPageNs { 1 index def 1 add } forall
  854.     SharedNs { 1 index def 1 add } forall
  855.   pop
  856.   currentdict end def 
  857.  
  858.                 % Table F.3 Page offset hint table, header section
  859.  
  860.                 % 1: Least number of objects in a page:
  861.   FirstPageNs length OtherPageNs { length .min } forall
  862.   /minnop 1 index def 32 bwn
  863.         % 2: Location of first page's Page object:
  864.   FirstPageNs 0 get omap XRef exch ld_get 32 bwn
  865.         % 3: Bits needed to represent greatest # of objects in a page:
  866.   FirstPageNs length OtherPageNs { length .max } forall
  867.   minnop sub bitsneeded /maxnopbits 1 index def 16 bwn
  868.         % 4: Least length of a page:
  869.   minpl 32 bwn
  870.         % 5: Bits needed to represent the greatest page length:
  871.   maxplbits 16 bwn
  872.         % 6: Least start of Contents offset:
  873.   0        % (Acrobat requires that this be 0.)
  874.   /minsco 1 index def 32 bwn
  875.         % 7: Bits needed to represent the greatest start of Contents
  876.         % offset:
  877.   0        % (Acrobat ignores this.)
  878.   /maxscobits 1 index def 16 bwn
  879.         % 8: Least contents length:
  880.   mincl 32 bwn
  881.         % 9: Bits needed to represent the greatest Contents length:
  882.   maxclbits 16 bwn
  883.         % 10: Bits needed to represent the greatest number of Shared
  884.            % Object references:
  885.   FirstPageNs length SharedPageNs { length .max } forall bitsneeded
  886.   /maxsorbits 1 index def 16 bwn
  887.         % 11: Bits needed to identify a Shared Object:
  888.   FirstPageNs length SharedNs length add bitsneeded
  889.   /sobits 1 index def 16 bwn
  890.         % 12: Bits needed to represent numerator of fraction:
  891.   2
  892.   /numfbits 1 index def 16 bwn
  893.         % 13: Denominator of fraction:
  894.   1
  895.   /denf 1 index def 16 bwn
  896.  
  897.                 % Table F.4 Page offset hint table, per-page entry
  898.  
  899.                 % 1: Number of objects in pages:
  900.   FirstPageNs length minnop sub maxnopbits bwn
  901.   OtherPageNs {
  902.     length minnop sub maxnopbits bwn
  903.   } forall
  904.   bits bitflush
  905.  
  906.         % 2: Total length of pages in bytes;
  907.   FirstPageLength minpl sub maxplbits bwn
  908.   OtherPageLengths {
  909.     minpl sub maxplbits bwn
  910.   } forall
  911.   bits bitflush
  912.  
  913.         % 3: Number of shared objects referenced from page:
  914.   FirstPageNs length maxsorbits bwn 
  915.   SharedPageNs { length maxsorbits bwn } forall
  916.   bits bitflush
  917.                 % 4: A shared object identifier:
  918.   FirstPageNs { shared_id_dict exch get sobits bwn } forall
  919.   SharedPageNs {
  920.     { shared_id_dict exch get sobits bwn
  921.     } forall
  922.   } forall
  923.   bits bitflush
  924.  
  925.                 % 5: Numerator of fractional position for each shared object:
  926.   FirstPageNs { pop 0 numfbits bwn  } forall
  927.   SharedPageNs {
  928.     { pop 0 numfbits bwn 
  929.     } forall
  930.   } forall
  931.   bits bitflush
  932.  
  933.         % 6: Contents offsets:
  934.                 % Following Implementation Note 133 section 6 is empty.
  935.   maxscobits 0 gt {
  936.     [FirstPageNs OtherPageNs aload pop] {
  937.        0 get Objects exch lget contentsstart { minsco sub } { 0 } ifelse
  938.        maxscobits bwn
  939.     } forall
  940.     bits bitflush
  941.   } if
  942.  
  943.                 % 7: Contents lengths:
  944.   [FirstPageNs OtherPageNs aload pop] {
  945.     0 get Objects exch lget contentslength mincl sub maxclbits bwn
  946.   } forall
  947.   bits bitflush
  948.  
  949.   end
  950.  
  951. } bind def
  952.  
  953. /writeSharedObjectHints {
  954.   PDFOPTDEBUG { /writeSharedObjectHints == } if
  955.   20 dict begin
  956.   /bits OFile bitstream def
  957.   /obj_count SharedNs length FirstPageNs length add def
  958.  
  959.          % Table F.5 Shared object hint table, header section
  960.  
  961.                   % 1: Object number of first object in Shared Objects section
  962.   0 32 bwn
  963.         % 2: Location of first object in Shared Objects section:
  964.         % If there are no shared objects,
  965.         % Acrobat sets this to the location of linearization
  966.         % parameters object (the very first object).
  967.   { pdfwriteheader } tomemory length 32 bwn
  968.         % 3: Number of Shared Object entries for first page:
  969.   FirstPageNs length 32 bwn
  970.         % 4: Number of Shared Object entries for Shared Objects
  971.         % section
  972.   obj_count 32 bwn
  973.         % 5: Bits needed to represent the greatest number of objects
  974.         % in a shared object group (always 0, because all groups
  975.         % have only 1 object):
  976.   0 16 bwn
  977.         % 6: Least length of a Shared Object Group in bytes:
  978.   16#7fffffff FirstPageNs { omlength .min } forall
  979.                  SharedNs { omlength .min } forall
  980.   /minsol 1 index def 32 bwn
  981.         % 7: Bits needed to represent the greatest length of a
  982.         % Shared Object Group:
  983.   0 FirstPageNs { omlength .max } forall
  984.        SharedNs { omlength .max } forall
  985.   minsol sub bitsneeded
  986.   /maxsolbits 1 index def 16 bwn
  987.  
  988.                 % Table F.6 Shared object hint table, shared object group entry
  989.  
  990.                 % 1: Lengths of shared object groups:
  991.   FirstPageNs { omlength minsol sub maxsolbits bwn } forall
  992.      SharedNs { omlength minsol sub maxsolbits bwn } forall
  993.   bits bitflush
  994.         % 2: MD5 flag:
  995.   obj_count { 0 1 bwn } repeat
  996.   bits bitflush
  997.                 % 3: No MD5 shared object signatures.
  998.  
  999.                 % 4: No number_number_of_objects_in_the_group - 1
  1000.   end
  1001. } bind def
  1002.  
  1003. % ---------------- Main program ---------------- %
  1004.  
  1005. /pdfOptimize {        % <infile> <outfile> pdfOptimize -
  1006.   realtime 3 1 roll
  1007.   exch pdfdict begin pdfopenfile dup begin
  1008.   40 dict begin
  1009.   /IDict exch def
  1010.   /OFile exch def
  1011.   /starttime exch def
  1012.   /ToWrite 100 dict def
  1013.   /now {
  1014.     QUIET { pop } { print (, t = ) print realtime starttime sub = flush } ifelse
  1015.   } def
  1016.   omapinit
  1017.   
  1018.     % Create and open a temporary file.
  1019.     % Because of .setsafe, we have to open it as read-write, rather than
  1020.     % opening for writing, then closing it and reopening it for reading.
  1021.  
  1022.   null (w+) .tempfile /TFile exch def /TFileName exch def
  1023.   .setsafe
  1024.  
  1025.     % Read all objects into memory.
  1026.  
  1027.   Trailer touch
  1028.   (Read objects) now
  1029.  
  1030.         % Encrypted files are not yet supported.
  1031.   Trailer /Encrypt known {
  1032.     (ERROR: Encrypted files are not yet supported.) = flush
  1033.     /pdfOptimize cvx /limitcheck signalerror
  1034.   } if
  1035.  
  1036.     % Replace indirect references to numbers.  This is needed
  1037.     % for the Length of streams, and doesn't hurt anything else.
  1038.  
  1039.   replaceReferences
  1040.   (Replaced references) now
  1041.  
  1042.     % Create the two new objects: the linearization parameter
  1043.     % dictionary, and the Primary Hint Stream.
  1044.  
  1045.   /LPDict 10 dict def
  1046.   /PHS 10 dict cvx def        % executable = stream
  1047.   [LPDict PHS] createObjects
  1048.   /LPDictN 1 index def 1 add
  1049.   /PHSN exch def
  1050.   PDFOPTDEBUG { << /LPDictN LPDictN /PHSN PHSN >> === } if
  1051.  
  1052.     % Count the number of objects in the output.
  1053.  
  1054.   0 0 1 Objects llength 1 sub {
  1055.     Generations exch lget 0 ne { 1 add } if
  1056.   } for
  1057.   /NObjects exch def
  1058.   QUIET not { NObjects =only ( objects total) = flush } if
  1059.  
  1060.     % Propagate inherited attributes down the page tree.
  1061.  
  1062.   propagateAttributes
  1063.   (Propagated attributes) now
  1064.  
  1065.     % Identify the document-level objects (part 4).
  1066.  
  1067.   identifyDocumentObjects /CatalogNs exch def
  1068.   QUIET not { CatalogNs === flush } if
  1069.   (Identified Catalog) now
  1070.  
  1071.       % Identify the first page's objects (part 6),
  1072.     % including the Outlines tree if appropriate.
  1073.  
  1074.   pdfopencache
  1075.   /FirstPageNs identifyFirstPageObjects def
  1076.   QUIET not { FirstPageNs === flush } if
  1077.   (Identified first page) now
  1078.  
  1079.     % Identify shared vs. non-shared objects for remaining pages
  1080.     % (parts 7 and 8).
  1081.  
  1082.   identifyOtherPageObjects
  1083.   /SharedNs exch def
  1084.   /SharedPageNs exch def
  1085.   /OtherPageNs exch def
  1086.   QUIET not { OtherPageNs === flush SharedNs === flush } if
  1087.   (Identified other pages) now
  1088.  
  1089.     % Identify objects not associated with any page (part 9).
  1090.  
  1091.   /NonPageNs identifyNonPageObjects def
  1092.   QUIET not { NonPageNs { === } forall flush } if
  1093.   (Identified non-pages) now
  1094.  
  1095.     % Assign final object numbers to all the objects.
  1096.     % (The omap is currently empty.)
  1097.  
  1098.   /FirstPageXN assignObjectNumbers def
  1099.   (Assigned objects #s) now
  1100.  
  1101.     % Write the document-level objects (part 4).
  1102.  
  1103.   { writePart4 } totemp
  1104.   /CatalogTempEnd exch def /CatalogTempStart exch def
  1105.   (Wrote Catalog) now
  1106.  
  1107.     % Write the first page's objects (part 6).
  1108.  
  1109.   { writePart6 } totemp
  1110.   /FirstPageTempEnd exch def /FirstPageTempStart exch def
  1111.   (Wrote first page) now
  1112.  
  1113.     % Write the non-shared objects for other pages (part 7).
  1114.  
  1115.   { writePart7 /OtherPageLengths exch def } totemp
  1116.   /OtherPageTempEnd exch def /OtherPageTempStart exch def
  1117.   (Wrote other pages) now
  1118.  
  1119.     % Write the shared objects for other pages (part 8).
  1120.  
  1121.   { writePart8 } totemp
  1122.   /SharedTempEnd exch def /SharedTempStart exch def
  1123.   (Wrote shared objects) now
  1124.  
  1125.     % Write the objects not associated with pages (part 9).
  1126.  
  1127.   { writePart9 } totemp
  1128.   /NonPageTempEnd exch def /NonPageTempStart exch def
  1129.  
  1130.     % Compute conservative lengths of parts 2,3,5,11 of the output.
  1131.     % It's OK for these to be too large, but not too small.
  1132.  
  1133.   % Make dummy XRef entres for LPDict and PHS.
  1134.   XRef LPDictN omap 0 ld_put
  1135.   XRef PHSN omap 0 ld_put
  1136.  
  1137.   /HeaderLength {    % this is exact
  1138.     writePart1            % part 1
  1139.   } tolength def
  1140.   /CatalogLength    % this is exact
  1141.     CatalogTempEnd CatalogTempStart sub def    % part 4
  1142.   /FirstPageLength    % this is exact
  1143.     FirstPageTempEnd FirstPageTempStart sub def    % part 6
  1144.   /OtherObjectsLength    % this is exact
  1145.     NonPageTempEnd OtherPageTempStart sub def    % parts 7,8,9
  1146.   /ObjectsLength    % this is exact
  1147.     CatalogLength FirstPageLength add OtherObjectsLength add def
  1148.   /XrefLength {            % part 11
  1149.     % The LPDict must end within the first 1024 bytes,
  1150.     % so the start of the FirstPage xref table can't exceed 1024.
  1151.     writePart11xref 1024 writePart11rest
  1152.   } tolength def
  1153.   /NominalFileLength     % Make a generous allowance for parts 2,3,5.
  1154.     HeaderLength ObjectsLength 3 mul add 10000 add 99999 .max def
  1155.   /FirstPageXrefLength {    % part 3
  1156.     NominalFileLength writePart3
  1157.   } tolength def
  1158.   /LPDictLength {        % part 2
  1159.     NominalFileLength dup 2 mul 2 copy add 1 index dup createLPDict writePart2
  1160.   } tolength def
  1161.  
  1162.     % Compute a few additional values from the above.
  1163.  
  1164.   /XrefBeginLength {
  1165.     (xref\n0 ) ows
  1166.     OFile FirstPageXN write=
  1167.   } tolength def
  1168.   HeaderLength LPDictLength add
  1169.   /FirstPageXrefStart 1 index def
  1170.   FirstPageXrefLength add
  1171.   /CatalogStart 1 index def
  1172.   CatalogLength add        % phsstart
  1173.   /PHSStart exch def
  1174.  
  1175.     % Adjust the object positions ignoring PHS.
  1176.     % (Writing the PHS needs these.)
  1177.  
  1178.   0 0 CatalogStart CatalogTempStart sub adjustObjectPositions
  1179.   % Make a temporary XRef entry for the PHS, for the benefit of omend.
  1180.   XRef PHSN omap CatalogStart ld_put
  1181.   (Adjusted positions) now
  1182.  
  1183.         % Construct the hint tables (part 5).
  1184.  
  1185.   { writePageOffsetHints } totemp
  1186.   pop /PHSTempStart exch def
  1187.   { writeSharedObjectHints } totemp
  1188.   exch PHSTempStart sub PHS /S 3 -1 roll put
  1189.   PHSTempStart sub /PHSTempLength exch def
  1190.   (Wrote hints) now
  1191.  
  1192.   % Prepare to read TFile.
  1193. %  TFile closefile
  1194. %  /TFile TFileName (r) file def
  1195.  
  1196.   PHS
  1197.     dup /File TFile put
  1198.     dup /FilePosition PHSTempStart put
  1199.     dup /Length PHSTempLength put
  1200.   pop
  1201.   /PHSLength { writePart5 } tolength def
  1202.  
  1203.     % Construct the linearization parameter dictionary (part 2).
  1204.  
  1205.   PHSStart
  1206.   dup PHSLength add        % phsend
  1207.   /FirstPageStart 1 index def
  1208.   dup FirstPageLength add    % firstpageend
  1209.   dup OtherObjectsLength add
  1210.   /XrefStart 1 index def
  1211.   XrefBeginLength add        % xref0start
  1212.   dup XrefBeginLength sub XrefLength add    % fileend
  1213.     % Because of a bug, Acrobat Reader doesn't recognize any file
  1214.     % shorter than 1K as linearized.  Patch this here.
  1215.       % Acrobat 9 insists on 4KB
  1216.   4096 .max
  1217.   /FileLength 1 index def
  1218.   createLPDict
  1219.  
  1220.     % Adjust the object positions again, taking the PHS into account.
  1221.  
  1222.   PHSStart 0 PHSLength adjustObjectPositions
  1223.   (Readjusted positions) now
  1224.  
  1225.     % Finally, write the output file.
  1226.  
  1227.   writePart1
  1228.   writePart2
  1229.   FirstPageXrefStart padto
  1230.   XrefStart writePart3
  1231.   CatalogStart padto
  1232.   CatalogTempStart CatalogTempEnd copyrange    % part 4
  1233.   writePart5
  1234.   FirstPageStart padto
  1235.   FirstPageTempStart NonPageTempEnd copyrange    % parts 6,7,8,9
  1236.   % No Overflow Hint Stream (part 10).
  1237.   XrefStart padto
  1238.   writePart11xref
  1239.   { FirstPageXrefStart writePart11rest } tomemory
  1240.   FileLength 1 index length sub padto ows
  1241.   (Wrote output file) now
  1242.  
  1243.     % Wrap up.
  1244.  
  1245.   TFile closefile TFileName deletefile
  1246.   end        % temporary dict
  1247.   end        % IDict
  1248. } bind def
  1249.  
  1250. end            % pdfoptdict
  1251. .setglobal
  1252.  
  1253. % Check for command line arguments.
  1254. [ shellarguments {
  1255.   ] dup length 2 eq {
  1256.     % Load the pdfwrite utilities if necessary.
  1257.     /omapinit where { pop } { (pdfwrite.ps) runlibfile } ifelse
  1258.     save exch
  1259.     aload pop exch (r) file exch (w) file
  1260.     3000000 setvmthreshold
  1261.     0 setobjectformat
  1262.     pdfoptdict begin pdfOptimize end
  1263.     restore
  1264.   } {
  1265.     (Usage: gs -dNODISPLAY -- pdfopt.ps input.pdf output.pdf) = flush quit
  1266.   } ifelse
  1267. } {
  1268.   pop
  1269. } ifelse
  1270.