home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 October / Chip_2002-10_cd1.bin / zkuste / pdf / download / gs704w32.exe / gs7.04 / lib / pdfopt.ps < prev    next >
Encoding:
Text File  |  2002-01-31  |  32.5 KB  |  1,128 lines

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