home *** CD-ROM | disk | FTP | other *** search
/ PC World 2001 April / PCWorld_2001-04_cd.bin / Software / Vyzkuste / gs / gs650w32.exe / gs6.50 / lib / pdfopt.ps < prev    next >
Text File  |  2000-12-05  |  30KB  |  1,055 lines

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