home *** CD-ROM | disk | FTP | other *** search
/ Internet Magazine 2002 February / INTERNET88.ISO / pc / software / windows / bits / pdf995 / data1.cab / Program_Executable_Files / res / pdfopt.ps < prev    next >
Encoding:
Text File  |  2001-12-08  |  30.5 KB  |  1,056 lines

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