diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 16:53:33 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 16:53:33 +0300 |
commit | e463eb40363ff4c68b1d903f4e0cdd0ac1c5977f (patch) | |
tree | d5e9f57c28f026cb21de3bd77cc10cd7f64aaa85 /sys/lib/ghostscript/pdfopt.ps | |
parent | b41b9034225ab3e49980d9de55c141011b6383b0 (diff) |
Import sources from 2011-03-30 iso image - sys/lib
Diffstat (limited to 'sys/lib/ghostscript/pdfopt.ps')
-rwxr-xr-x | sys/lib/ghostscript/pdfopt.ps | 1259 |
1 files changed, 1259 insertions, 0 deletions
diff --git a/sys/lib/ghostscript/pdfopt.ps b/sys/lib/ghostscript/pdfopt.ps new file mode 100755 index 000000000..91344e4a2 --- /dev/null +++ b/sys/lib/ghostscript/pdfopt.ps @@ -0,0 +1,1259 @@ +% Copyright (C) 2000, 2001 Aladdin Enterprises. All rights reserved. +% +% This software is provided AS-IS with no warranty, either express or +% implied. +% +% This software is distributed under license and may not be copied, +% modified or distributed except as expressly authorized under the terms +% of the license contained in the file LICENSE in this distribution. +% +% For more information about licensing, please refer to +% http://www.ghostscript.com/licensing/. For information on +% commercial licensing, go to http://www.artifex.com/licensing/ or +% contact Artifex Software, Inc., 101 Lucas Valley Road #110, +% San Rafael, CA 94903, U.S.A., +1(415)492-9861. + +% $Id: pdfopt.ps,v 1.20 2003/06/02 19:52:58 alexcher Exp $ +% PDF linearizer ("optimizer"). + +.currentglobal true .setglobal +/pdfoptdict 200 dict def +pdfoptdict begin + +% This linearizer is designed for simplicity, not for performance. +% See the main program (the last procedure in the file) for comments +% describing the main processing sequence. + +% ---------------- Utilities ---------------- % + +% ------ Data structures ------ % + +% Distinguish dictionaries, arrays, and everything else. +/ifdaelse { % <obj> <dictproc> <arrayproc> <otherproc> ifdaelse - + 3 index type dup /dicttype eq { + pop pop pop + } { + dup /arraytype ne exch /packedarraytype ne and { + exch + } if pop exch pop + } ifelse exec +} bind def + +% Implement dynamically growable arrays using a dictionary. +/darray { % <size> darray <darray> + dict +} bind def +/dadd { % <darray> <value> dadd - + 1 index length exch put +} bind def +/daforall { % <darray> <proc> daforall - + /exch cvx /get cvx 3 -1 roll /exec cvx 5 packedarray cvx + 0 1 2 index 0 get length 1 sub 4 -1 roll for +} bind def +/dacontents { % <darray> dacontents <array> + [ exch { } daforall ] +} bind def +/dacontstring { % <darray> dacontstring <string> + 0 1 index { exch pop length add } forall string + dup /NullEncode filter + % Stack: darray str filter + 3 -1 roll { 1 index exch writestring } daforall + closefile +} bind def + +% Force an object, mapping it if it is a reference. +/omforcenew { % <obj> omforce <obj'> <notseen> + dup oforce 2 copy eq { pop true } { exch 0 get omapnew exch pop } ifelse +} bind def +/omforce { % <obj> omforce <obj'> + omforcenew pop +} bind def +/omget { % <dict|array> <key> omget <obj> + get omforce +} bind def +% Visit an entire tree. +/omvisit { % <obj> omvisit - + omforcenew { + { { omvisit omvisit } forall } + { { omvisit } forall } + { pop } + ifdaelse + } { + pop + } ifelse +} bind def +% Visit a tree, stopping at references to Page objects. +% (This is only needed for the OpenAction in the Catalog.) +/omvisitnopage { % <obj> omvisitnopage - + dup oforce dup type /dicttype eq { + /Type .knownget { /Page eq } { false } ifelse + } { + pop false + } ifelse { + pop % Page reference + } { + omforcenew { + { { omvisitnopage omvisitnopage } forall } + { { omvisitnopage } forall } + { pop } + ifdaelse + } { + pop + } ifelse + } ifelse +} bind def + +% Collect the list of currently mapped object numbers, in order. +/omapped { % - omapped <obj#s> + RMap ld_length larray exch lgrowto + RMap { + 2 index 3 1 roll 1 sub exch lput + } ld_forall +} bind def + +% Collect the list of object numbers passed to omap by a procedure. +/visited { % <proc> visited <obj#s> + false currentomap 2 .execn + omapped exch setomap +} bind def + +% ------ Output ------ % + +% Provide a framework for closure-based streams. +.currentglobal false .setglobal +userdict /clostreams 20 dict put % stream -> [data endproc] +.setglobal +% Create a closure-based stream. +/clostream { % <data> <proc> <endproc> clostream <stream> + 2 index 3 -1 roll /exec load 3 packedarray cvx + /NullEncode filter + % Stack: data endproc stream + clostreams 1 index 5 -2 roll 2 array astore put +} bind def +% Close a closure-based stream. +/closend { % <stream> closend <result> + dup closefile clostreams exch + 2 copy get 3 1 roll undef aload pop exec +} bind def + +% Implement in-memory output streams. +/msproc { % <data> <more> <accum> msproc <scratch> + 3 -1 roll dadd { 100 string } { () } ifelse +} bind def +/mstream { % - mstream <mstream> + 10 darray {msproc} {dacontstring} clostream +} bind def +/mcontents { % <mstream> mcontents <string> + closend +} bind def + +% Implement a stream that only keeps track of its position. +% (All streams should do this, but the PLRM doesn't require it.) +/posbuf 100 string def +/posproc { % <data> <more> <accum> posproc <scratch> + 0 2 copy get 5 -1 roll length add put + pop //posbuf +} bind def +/postream { % - postream <postream> + [0] {posproc} {0 get} clostream +} bind def +/poslength { % <postream> poslength <pos> + closend +} bind def + +% Implement streams with variable-bit-width data. +% Note that these are dictionary objects, not stream objects. +/bitstream { % <stream> bitstream <bstream> + 4 dict begin /S exch def /N 8 def /B 0 def + currentdict end +} bind def +/bitwrite { % <bstream> <value> <width> bitwrite - + PDFOPTDEBUG { ( ) print 1 index =only (:) print dup = } if + 3 -1 roll begin + N exch sub dup 0 ge { + /N exch def N bitshift B add + } { + 2 copy bitshift B add S exch write + % Stack: value -left + { 8 add dup 0 ge { exit } if + 2 copy bitshift 255 and S exch write + } loop + /N 1 index def bitshift 255 and + } ifelse /B exch def + end +} bind def +/bitflush { % <bstream> bitflush - + begin N 8 ne { S B write /B 0 def /N 8 def } if end +} bind def + +/bwn { % <value> <width> bwn - + 2 copy % v w v w + 2 exch exp ge { % v w v>=2**w + /bwn cvx /rangecheck signalerror + } if + bits 3 1 roll bitwrite +} def + +% Capture OFile output on the temporary file, in memory, or just as a length. +/totemp { % <proc> totemp <start> <end> + TFile fileposition OFile + /OFile TFile def 3 .execn + /OFile exch def + TFile fileposition +} bind def +/tomemory { % <proc> tomemory <string> + OFile /OFile mstream def 2 .execn + OFile mcontents exch /OFile exch def +} bind def +/tolength { % <proc> tolength <string> + OFile /OFile postream def 2 .execn + OFile poslength exch /OFile exch def +} bind def + +% Copy a range of bytes from TFile to OFile. +/copyrange { % <start> <end> copybytes - + TFile 2 index setfileposition + exch sub 1024 string exch { + % Stack: buf left + 2 copy 1 index length .min 0 exch getinterval + TFile exch readstring pop OFile exch writestring + 1 index length sub dup 0 le { exit } if + } loop pop pop +} bind def + +% Pad with blanks to a specified position. +/padto { % <pos> padto - + OFile fileposition sub + dup 0 lt { + (ERROR: file position incorrect by ) print = + /padto cvx /rangecheck signalerror + } { + { ( ) ows } repeat + } ifelse +} bind def + +% ---------------- Read objects into memory ---------------- % + +/touch { % <object> touch - + { + { touch touch } forall + } { + dup xcheck { + % Executable array, must be an indirect object. + dup 0 get resolved? { pop pop } { oforce touch } ifelse + } { + { touch } forall + } ifelse + } { + pop + } ifdaelse +} bind def + +% ---------------- Replace references with referents ---------------- % + +/replaceable? { % <value> replaceable? <bool> + dup type /integertype eq exch xcheck not and +} bind def +/replacement { % <obj|ref> replacement <obj'> + dup oforce dup replaceable? { exch } if pop +} bind def + +/replacerefs { % <object> replacerefs <object> + { + dup { + 2 index 2 index undef + exch replacement exch replacement + 2 index 3 1 roll put + } forall + } { + 0 1 2 index length 1 sub { + 1 index exch 2 copy get replacement put + } for + } { + } ifdaelse +} bind def + +/replaceReferences { % - replaceReferences - + Objects { replacerefs pop } lforall + % Delete replaced objects. + 0 1 Objects llength 1 sub { + Objects 1 index lget replaceable? { + PDFOPTDEBUG { (Deleting ) print dup = } if + Generations 1 index 0 lput + } if pop + } for +} bind def + +% ---------------- Create new objects ---------------- % + +/createObjects { % [<obj>...] createObjects <firstobj#> + Objects llength dup + dup 3 index length add growPDFobjects + % Stack: objects objn objn + 3 1 roll exch { + Objects 2 index 3 -1 roll lput + Generations 1 index 1 lput + 1 add + } forall pop +} bind def + +% ---------------- Propagate attributes ---------------- % + +/nopropattrs << + % Never propagate these. + /Type dup /Kids dup /Count dup /Parent dup + % Handle Resources specially. + /Resources dup +>> def + +% Merge Resources. +/mergeres { % <fromdict> <todict> mergeres - + % Values in todict take priority over fromdict. + 1 index /Resources .knownget { + 1 index /Resources .knownget { + % Stack: fromdict todict fromres tores + exch oforce exch oforce + % todict's Resources may be shared, so make a copy. + dup length dict .copydict + exch { + % Stack: fromdict todict tores' fromkey fromvalue + 2 index 2 index knownoget { + % Stack: fromdict todict tores' fromkey fromvalue tovalue + exch oforce exch + % ProcSet is an array, other types are dictionaries. + dup type /dicttype eq { + % Dictionary, not ProcSet. + exch dup length 2 index length add dict .copydict .copydict + } { + % Array or packed array, ProcSet. + % Use dictionaries to do the merge. + dup length 2 index length add dict begin + exch { dup def } forall { dup def } forall + mark currentdict end { pop } forall .packtomark + } ifelse + } if + 2 index 3 1 roll put + } forall + } if /Resources exch put pop + } { + pop pop + } ifelse +} bind def + +% Merge attributes other than Resources. +/mergeattrs { % <fromdict> <todict> mergeattrs <fromdict> <todict> + % Values in todict take priority over fromdict. + 1 index { + % Stack: fromdict todict fromkey fromvalue + //nopropattrs 2 index known { + pop pop + } { + 2 index 2 index known { pop pop } { 2 index 3 1 roll put } ifelse + } ifelse + } forall +} bind def + +% Propagate attributes to a subtree. +/proppage { % <attrs> <subtree> proppage - + % We should be able to tell when we reach a leaf + % by finding a Type unequal to /Pages. Unfortunately, + % some files distributed by Adobe lack the Type key + % in some of the Pages nodes! Instead, we check for Kids. + dup /Kids knownoget { + % Accumulate inherited values. + 3 1 roll + % Stack: kids attrs pagesnode + dup length dict .copydict mergeattrs + dup 3 1 roll mergeres + exch { oforce 1 index exch proppage } forall pop + } { + % Merge inherited values into the leaf. + mergeattrs mergeres + } ifelse +} bind def + +% Propagate attributes to all pages. +/propagateAttributes { % - propagateAttributes - + 0 dict Trailer /Root oget /Pages oget proppage +} bind def + +% ---------------- Identify document-level objects ---------------- % + +/identifyDocumentObjects { % - identifyDocumentObjects <obj#s> + { + Trailer /Root omget + dup /PageMode .knownget { omvisit } if + % Don't allow omvisit to trace references to Page objects. + dup /OpenAction .knownget { omvisitnopage } if + Trailer /Encrypt .knownget { omvisit } if + dup /Threads .knownget { + omforce { omvisit } forall + } if + dup /AcroForm .knownget { omvisit } if + pop + } visited +} bind def + +% ---------------- Identify the objects of each page ---------------- % + +/identifyfont { % <fontref> identifyfont - + omforce { + exch /FontDescriptor eq { + omforce dup /Flags .knownget { 32 and 0 ne } { false } ifelse + exch { + exch dup dup /FontFile eq exch /FontFile2 eq or + exch /FontFile3 eq or 2 index and { + fontfiles exch dadd + } { + omvisit + } ifelse + } forall pop + } { + omvisit + } ifelse + } forall +} bind def + +% Collect all the objects referenced from a page. The first object number +% (which may not be the smallest one) is that of the page object itself. +/identifyPageObjects { % <extra> <page#> identifyPageObjects <obj#s> + PDFOPTDEBUG { + (%Objects for page: ) print dup = + } if + pdffindpageref + dup 0 get 3 1 roll + 4 dict begin + /images 10 darray def + /fontfiles 10 darray def + { + omforce + % Stack: pageobj# extra page + % Visit any extra objects if applicable. + exch omvisit + % Visit Annots, if any. + % We don't try to defer the drawing information. + dup /Annots .knownget { omvisit } if + % Visit beads. + dup /B .knownget { omvisit } if + % Visit resources dictionaries. + dup /Resources .knownget { + omforce dup { + % Visit the first-level Resource dictionaries. + omforce pop pop + } forall { + % Visit the resources themselves. + % Skip Image XObjects, and FontFile streams if the + % FontDescriptor Flags have bit 6 set. + % We don't try to visit the resources in the order in which + % the Contents stream(s) reference(s) them. + exch dup /XObject eq { + pop oforce { + dup oforce /Subtype get /Image eq { + images exch dadd + } { + omvisit + } ifelse pop + } forall + } { + /Font eq { + oforce { identifyfont pop } forall + } { + oforce omvisit + } ifelse + } ifelse + } forall + } if + % Visit the Contents stream(s). + dup /Contents .knownget { omvisit } if + % Visit Image XObjects. We don't try to visit them in + % reference order. + images { omvisit } daforall + % Visit FontFile streams. We don't try to visit them in + % reference order. + fontfiles { omvisit } daforall + pop + } visited end + % Stack: pageobj# obj#s_larray + [ 3 1 roll { + 2 copy eq { pop } { exch } ifelse + } lforall counttomark 1 roll ] + PDFOPTDEBUG { + (%Objects = ) print dup === flush + } if +} bind def + +% Identify the objects of the first page. +/identifyFirstPageObjects { % - identifyFirstPageObjects <obj#s> + Trailer /Root oget null + 1 index /PageMode knownoget { + /UseOutlines eq { + 1 index /Outlines knownoget { exch pop } if + } if + } if exch pop + 1 identifyPageObjects +} bind def + +% Identify the non-shared objects of the other pages, and the shared objects. +% Note that the page objects themselves may appear to be shared, because of +% references from Dest entries in annotations, but they must be treated as +% non-shared. Note also that some objects referenced on the first page may +% also be referenced from other pages. +/identifyOtherPageObjects { % - identifyOtherPageObjects [<pageobj#s> ...] + % <sharedobj#s> + 4 dict begin + /marks lstring Objects llength lgrowto def + % Collect objects of other pages and identify sharing. + [ 2 1 pdfpagecount { null exch identifyPageObjects } for ] + dup { + { marks exch 2 copy lget 1 add 254 .min lput } forall + } forall + % Mark document-level and first page objects. + CatalogNs { marks exch 255 lput } lforall + FirstPageNs { marks exch 255 lput } forall + % Mark the page objects themselves as non-shared. + dup { + 0 get marks exch 1 lput + } forall + % Collect the non-shared objects of each page. + dup + [ exch { + [ exch { + marks 1 index lget 1 ne { pop } if + } forall ] + } forall ] + % Collect the shared objects of each page. + exch + [ exch { + [ exch { + marks 1 index lget dup 1 le exch 255 eq or { pop } if + } forall ] + } forall ] + + % Collect the shared objects. + [ 1 1 marks llength 1 sub { + marks 1 index lget dup 1 le exch 255 eq or { pop } if + } for ] + + end +} bind def + +% Identify objects not associated with any page. +/identifyNonPageObjects { % - identifyNonPageObjects <obj#s> + 4 dict begin + /marks lstring Objects llength lgrowto def + + LPDictN marks exch 1 lput + PHSN marks exch 1 lput + CatalogNs { marks exch 1 lput } lforall + FirstPageNs { marks exch 1 lput } forall + SharedNs { marks exch 1 lput } forall + OtherPageNs { { marks exch 1 lput } forall } forall + + %****** PUT THESE IN A REASONABLE ORDER ****** + /npobj larray + 0 + 1 1 Objects llength 1 sub { + marks 1 index lget 0 eq { + Generations exch lget 0 ne { 1 add } if + } { + pop + } ifelse + } for + lgrowto def + + 0 + 1 1 Objects llength 1 sub { + marks 1 index lget 0 eq { + % i + Generations 1 index lget 0 ne { + % i + npobj 2 index % i nobj 0 + 3 -1 roll % nobj 0 i + lput 1 add + } { + pop + } ifelse + } { + pop + } ifelse + } for + pop + + npobj + end +} bind def + +% ---------------- Assign object numbers ---------------- % + +% Assign object numbers to all objects that will be copied. +% Return the first (translated) object number in the First Page xref table. +/assignObjectNumbers { % - assignObjectNumbers - + OtherPageNs { { omap pop } forall } forall + SharedNs { omap pop } forall + NonPageNs { omap pop } lforall + % Assign object numbers for the First Page xref table last. + LPDictN omap % don't pop, this is the return value + CatalogNs { omap pop } lforall + FirstPageNs { omap pop } forall + PHSN omap pop +} bind def + +% ---------------- Create the LPDict ---------------- % + +% Create the contents of the LPDict. +/createLPDict { % <phsstart> <phsend> <firstpageend> + % <xref0start> <filelength> createLPDict - + LPDict + dup /Linearized 1 put + dup /L 4 -1 roll put % filelength + dup /T 4 -1 roll put % xref0start + dup /E 4 -1 roll put % firstpageend + dup /H 5 -2 roll 1 index sub 2 array astore put % phsstart, end-start + dup /O 1 pdffindpageref 0 get omap put + /N pdfpagecount put +} bind def + +% ---------------- Adjust object positions ---------------- % + +/adjustObjectPositions { % <boundary> <deltabelow> <deltaabove> + % adjustObjectPositions - + % Objects fall into 4 categories: LPDict, PHS, Catalog, and others. + % We handle the first two as special cases. + XRef { + % Stack: bdy below above key loc + dup 5 index ge { 2 } { 3 } ifelse index add + XRef 3 1 roll ld_put + } ld_forall pop pop pop + XRef LPDictN omap HeaderLength ld_put + XRef PHSN omap PHSStart ld_put +} bind def + +% ---------------- Write the output file ---------------- % + +% Write objects identified by object number. +/writeobjn { % <obj#> writeobjn - + Generations 1 index lget pdfwriteobj +} bind def +/writeobjns { % <obj#s> writeobjns - + { writeobjn } forall +} bind def +/lwriteobjns { % <obj#s> writeobjns - + { writeobjn } lforall +} bind def + +% Write a part of the output file. +/writePart { % <proc> <label> writePart - + PDFOPTDEBUG { + dup print ( count=) print count =only ( start=) print + OFile { .fileposition } stopped { pop (???) } if = + 2 .execn + print ( end=) print + OFile { .fileposition } stopped { pop (???) } if = + } { + pop exec + } ifelse +} bind def + +% Write the header. +/writePart1 { % - writePart1 - + { + pdfwriteheader + } (part1) writePart +} bind def + +% Write the linearization parameters dictionary. +/writePart2 { % - writePart2 - + { + LPDictN writeobjn + } (part2) writePart +} bind def + +% Write the First Page xref table and trailer. +% Free variables: FirstPageXN. +/writePart3 { % <xrefstart> writePart3 - + { + FirstPageXN NObjects 1 add 1 index sub pdfwritexref + Trailer dup length 1 add dict copy + dup /Size NObjects 1 add put + dup /Prev 4 -1 roll put + pdfwritetrailer + 0 pdfwritestartxref + } (part3) writePart +} bind def + +% Write the Catalog and other required document-level objects. +% Free variables: CatalogNs. +/writePart4 { % - writePart4 - + { + CatalogNs lwriteobjns + } (part4) writePart +} bind def + +% Write the Primary Hint Stream. +/writePart5 { % - writePart5 - + { + PHSN writeobjn + } (part5) writePart +} bind def + +% Write the First Page's objects. +% Free variables: FirstPageNs. +/writePart6 { % - writePart6 - + { + FirstPageNs writeobjns + } (part6) writePart +} bind def + +% Write the objects of other pages (Page + non-shared objects). +% Free variables: OtherPageNs. +/writePart7 { % - writePart7 <lengths> + { + [ OtherPageNs { + OFile fileposition exch + writeobjns OFile fileposition exch sub + } forall ] + } (part7) writePart +} bind def + +% Write the shared objects of other pages. +% Free variables: SharedNs. +/writePart8 { % - writePart8 - + { + SharedNs writeobjns + } (part8) writePart +} bind def + +% Write the other objects not associated with pages. +% Free variables: NonPageNs. +/writePart9 { % - writePart9 - + { + NonPageNs { writeobjn } lforall + } (part9) writePart +} bind def + +% Write the main xref table and trailer. +% Free variables: FirstPageXN. +/writePart11xref { % writePart11 - + { + 0 FirstPageXN pdfwritexref + } (part11xref) writePart +} bind def +/writePart11rest { % <part3start> writePart11rest - + { + << /Size FirstPageXN >> pdfwritetrailer + pdfwritestartxref + } (part11rest) writePart +} bind def + +% ---------------- Write hint tables ---------------- % + +/bitsneeded { % <maxvalue> bitsneeded <#bits> + 0 exch { dup 0 eq { pop exit } if exch 1 add exch 2 idiv } loop +} bind def + +% Find the start and end of objects in the output. +/omstart { % <obj#> omstart <pos> + PDFOPTDEBUG { (start\() print dup =only } if + omap + PDFOPTDEBUG { (=>) print dup =only } if + XRef exch ld_get + PDFOPTDEBUG { (\) = ) print dup = } if +} bind def +/omend { % <obj#> omend <pos> + % The end of an object is the start of the next object. + % The caller must be sure that this object is not the last one + % in part 9. + PDFOPTDEBUG { (end\() print dup =only } if + omap + PDFOPTDEBUG { (=>) print dup =only } if + 1 add + % Check that the requested object wasn't the last one in part 6: + % the next object in the output file is the first in part 7. + PHSN omap 1 index eq { pop 1 } if + XRef exch ld_get + PDFOPTDEBUG { (\) = ) print dup = } if +} bind def +/omlength { % <obj#> omlength <length> + dup omend exch omstart sub +} bind def + +% Find the Contents of a page. +/contentsobjects { % <pagedict> contentsobjects <firstobj#> <lastobj#> true + % <pagedict> contentsobjects false + /Contents .knownget { + dup oforce % ref [] + dup type /dicttype eq { + pop 0 get dup true % ref ref + } { + exch pop % [] + dup length 0 ne { + dup 0 get 0 get % [] 1st + exch dup % 1st [] [] + length 1 sub get 0 get % 1st last + true + } { + pop false + } ifelse + } ifelse + } { + false + } ifelse +} bind def + +/contentsstart { % <pagedict> contentsstart <pos> true + % <pagedict> contentsstart false + contentsobjects { pop omstart true } { false } ifelse +} bind def + +/contentslength { % <pagedict> contentslength <length> + contentsobjects { omend exch omstart sub } { 0 } ifelse +} bind def + + +/writePageOffsetHints { + PDFOPTDEBUG { /writePageOffsetHints == } if + 20 dict begin + /bits OFile bitstream def + + % Calculate least length of a page. + FirstPageLength OtherPageLengths { .min } forall + /minpl exch def + + % Calculate least contents length. + FirstPageNs 0 get Objects exch lget contentslength + OtherPageNs { 0 get Objects exch lget contentslength .min } forall + /mincl exch def + + % The Adobe documentation says that all versions of Acrobat + % require item 8 (mincl) to be zero. Patch this here. + /mincl 0 def + + % Calculate bits needed to represent greatest page length. + FirstPageLength OtherPageLengths { .max } forall + minpl sub bitsneeded /maxplbits exch def + % Calculate bits needed to represent the greatest Contents length. + FirstPageNs 0 get Objects exch lget contentslength + OtherPageNs { 0 get Objects exch lget contentslength .max } forall + mincl sub bitsneeded /maxclbits exch def + + % Per Adobe documentation, Acrobat requires that item 5 (maxplbits) + % be equal to item 9 (maxclbits). Set both to the max of the two. + maxplbits maxclbits .max /maxplbits 1 index def /maxclbits exch def + + % Mapping from object number to shared object reference + /shared_id_dict FirstPageNs length SharedNs length add dict begin + 0 FirstPageNs { 1 index def 1 add } forall + SharedNs { 1 index def 1 add } forall + pop + currentdict end def + + % Table F.3 Page offset hint table, header section + + % 1: Least number of objects in a page: + FirstPageNs length OtherPageNs { length .min } forall + /minnop 1 index def 32 bwn + % 2: Location of first page's Page object: + FirstPageNs 0 get omap XRef exch ld_get 32 bwn + % 3: Bits needed to represent greatest # of objects in a page: + FirstPageNs length OtherPageNs { length .max } forall + minnop sub bitsneeded /maxnopbits 1 index def 16 bwn + % 4: Least length of a page: + minpl 32 bwn + % 5: Bits needed to represent the greatest page length: + maxplbits 16 bwn + % 6: Least start of Contents offset: + 0 % (Acrobat requires that this be 0.) + /minsco 1 index def 32 bwn + % 7: Bits needed to represent the greatest start of Contents + % offset: + 0 % (Acrobat ignores this.) + /maxscobits 1 index def 16 bwn + % 8: Least contents length: + mincl 32 bwn + % 9: Bits needed to represent the greatest Contents length: + maxclbits 16 bwn + % 10: Bits needed to represent the greatest number of Shared + % Object references: + FirstPageNs length SharedPageNs { length .max } forall bitsneeded + /maxsorbits 1 index def 16 bwn + % 11: Bits needed to identify a Shared Object: + FirstPageNs length SharedNs length add bitsneeded + /sobits 1 index def 16 bwn + % 12: Bits needed to represent numerator of fraction: + 2 + /numfbits 1 index def 16 bwn + % 13: Denominator of fraction: + 1 + /denf 1 index def 16 bwn + + % Table F.4 Page offset hint table, per-page entry + + % 1: Number of objects in pages: + FirstPageNs length minnop sub maxnopbits bwn + OtherPageNs { + length minnop sub maxnopbits bwn + } forall + bits bitflush + + % 2: Total length of pages in bytes; + FirstPageLength minpl sub maxplbits bwn + OtherPageLengths { + minpl sub maxplbits bwn + } forall + bits bitflush + + % 3: Number of shared objects referenced from page: + FirstPageNs length maxsorbits bwn + SharedPageNs { length maxsorbits bwn } forall + bits bitflush + % 4: A shared object identifier: + FirstPageNs { shared_id_dict exch get sobits bwn } forall + SharedPageNs { + { shared_id_dict exch get sobits bwn + } forall + } forall + bits bitflush + + % 5: Numerator of fractional position for each shared object: + FirstPageNs { pop 0 numfbits bwn } forall + SharedPageNs { + { pop 0 numfbits bwn + } forall + } forall + bits bitflush + + % 6: Contents offsets: + % Following Implementation Note 133 section 6 is empty. + maxscobits 0 gt { + [FirstPageNs OtherPageNs aload pop] { + 0 get Objects exch lget contentsstart { minsco sub } { 0 } ifelse + maxscobits bwn + } forall + bits bitflush + } if + + % 7: Contents lengths: + [FirstPageNs OtherPageNs aload pop] { + 0 get Objects exch lget contentslength mincl sub maxclbits bwn + } forall + bits bitflush + + end + +} bind def + +/writeSharedObjectHints { + PDFOPTDEBUG { /writeSharedObjectHints == } if + 20 dict begin + /bits OFile bitstream def + /obj_count SharedNs length FirstPageNs length add def + + % Table F.5 Shared object hint table, header section + + % 1: Object number of first object in Shared Objects section + 0 32 bwn + % 2: Location of first object in Shared Objects section: + % If there are no shared objects, + % Acrobat sets this to the location of linearization + % parameters object (the very first object). + { pdfwriteheader } tomemory length 32 bwn + % 3: Number of Shared Object entries for first page: + FirstPageNs length 32 bwn + % 4: Number of Shared Object entries for Shared Objects + % section + obj_count 32 bwn + % 5: Bits needed to represent the greatest number of objects + % in a shared object group (always 0, because all groups + % have only 1 object): + 0 16 bwn + % 6: Least length of a Shared Object Group in bytes: + 16#7fffffff FirstPageNs { omlength .min } forall + SharedNs { omlength .min } forall + /minsol 1 index def 32 bwn + % 7: Bits needed to represent the greatest length of a + % Shared Object Group: + 0 FirstPageNs { omlength .max } forall + SharedNs { omlength .max } forall + minsol sub bitsneeded + /maxsolbits 1 index def 16 bwn + + % Table F.6 Shared object hint table, shared object group entry + + % 1: Lengths of shared object groups: + FirstPageNs { omlength minsol sub maxsolbits bwn } forall + SharedNs { omlength minsol sub maxsolbits bwn } forall + bits bitflush + % 2: MD5 flag: + obj_count { 0 1 bwn } repeat + bits bitflush + % 3: No MD5 shared object signatures. + + % 4: No number_number_of_objects_in_the_group - 1 + end +} bind def + +% ---------------- Main program ---------------- % + +/pdfOptimize { % <infile> <outfile> pdfOptimize - + realtime 3 1 roll + exch pdfdict begin pdfopenfile dup begin + 40 dict begin + /IDict exch def + /OFile exch def + /starttime exch def + /now { + QUIET { pop } { print (, t = ) print realtime starttime sub = flush } ifelse + } def + omapinit + + % Create and open a temporary file. + % Because of .setsafe, we have to open it as read-write, rather than + % opening for writing, then closing it and reopening it for reading. + + null (w+) .tempfile /TFile exch def /TFileName exch def + .setsafe + + % Read all objects into memory. + + Trailer touch + (Read objects) now + + % Encrypted files are not yet supported. + Trailer /Encrypt known { + (ERROR: Encrypted files are not yet supported.) = flush + /pdfOptimize cvx /limitcheck signalerror + } if + + % Replace indirect references to numbers. This is needed + % for the Length of streams, and doesn't hurt anything else. + + replaceReferences + (Replaced references) now + + % Create the two new objects: the linearization parameter + % dictionary, and the Primary Hint Stream. + + /LPDict 10 dict def + /PHS 10 dict cvx def % executable = stream + [LPDict PHS] createObjects + /LPDictN 1 index def 1 add + /PHSN exch def + PDFOPTDEBUG { << /LPDictN LPDictN /PHSN PHSN >> === } if + + % Count the number of objects in the output. + + 0 0 1 Objects llength 1 sub { + Generations exch lget 0 ne { 1 add } if + } for + /NObjects exch def + QUIET not { NObjects =only ( objects total) = flush } if + + % Propagate inherited attributes down the page tree. + + propagateAttributes + (Propagated attributes) now + + % Identify the document-level objects (part 4). + + identifyDocumentObjects /CatalogNs exch def + QUIET not { CatalogNs === flush } if + (Identified Catalog) now + + % Identify the first page's objects (part 6), + % including the Outlines tree if appropriate. + + pdfopencache + /FirstPageNs identifyFirstPageObjects def + QUIET not { FirstPageNs === flush } if + (Identified first page) now + + % Identify shared vs. non-shared objects for remaining pages + % (parts 7 and 8). + + identifyOtherPageObjects + /SharedNs exch def + /SharedPageNs exch def + /OtherPageNs exch def + QUIET not { OtherPageNs === flush SharedNs === flush } if + (Identified other pages) now + + % Identify objects not associated with any page (part 9). + + /NonPageNs identifyNonPageObjects def + QUIET not { NonPageNs { === } forall flush } if + (Identified non-pages) now + + % Assign final object numbers to all the objects. + % (The omap is currently empty.) + + /FirstPageXN assignObjectNumbers def + (Assigned objects #s) now + + % Write the document-level objects (part 4). + + { writePart4 } totemp + /CatalogTempEnd exch def /CatalogTempStart exch def + (Wrote Catalog) now + + % Write the first page's objects (part 6). + + { writePart6 } totemp + /FirstPageTempEnd exch def /FirstPageTempStart exch def + (Wrote first page) now + + % Write the non-shared objects for other pages (part 7). + + { writePart7 /OtherPageLengths exch def } totemp + /OtherPageTempEnd exch def /OtherPageTempStart exch def + (Wrote other pages) now + + % Write the shared objects for other pages (part 8). + + { writePart8 } totemp + /SharedTempEnd exch def /SharedTempStart exch def + (Wrote shared objects) now + + % Write the objects not associated with pages (part 9). + + { writePart9 } totemp + /NonPageTempEnd exch def /NonPageTempStart exch def + + % Compute conservative lengths of parts 2,3,5,11 of the output. + % It's OK for these to be too large, but not too small. + + % Make dummy XRef entres for LPDict and PHS. + XRef LPDictN omap 0 ld_put + XRef PHSN omap 0 ld_put + + /HeaderLength { % this is exact + writePart1 % part 1 + } tolength def + /CatalogLength % this is exact + CatalogTempEnd CatalogTempStart sub def % part 4 + /FirstPageLength % this is exact + FirstPageTempEnd FirstPageTempStart sub def % part 6 + /OtherObjectsLength % this is exact + NonPageTempEnd OtherPageTempStart sub def % parts 7,8,9 + /ObjectsLength % this is exact + CatalogLength FirstPageLength add OtherObjectsLength add def + /XrefLength { % part 11 + % The LPDict must end within the first 1024 bytes, + % so the start of the FirstPage xref table can't exceed 1024. + writePart11xref 1024 writePart11rest + } tolength def + /NominalFileLength % Make a generous allowance for parts 2,3,5. + HeaderLength ObjectsLength 3 mul add 10000 add 99999 .max def + /FirstPageXrefLength { % part 3 + NominalFileLength writePart3 + } tolength def + /LPDictLength { % part 2 + NominalFileLength dup 2 mul 2 copy add 1 index dup createLPDict writePart2 + } tolength def + + % Compute a few additional values from the above. + + /XrefBeginLength { + (xref\n0 ) ows + OFile FirstPageXN write= + } tolength def + HeaderLength LPDictLength add + /FirstPageXrefStart 1 index def + FirstPageXrefLength add + /CatalogStart 1 index def + CatalogLength add % phsstart + /PHSStart exch def + + % Adjust the object positions ignoring PHS. + % (Writing the PHS needs these.) + + 0 0 CatalogStart CatalogTempStart sub adjustObjectPositions + % Make a temporary XRef entry for the PHS, for the benefit of omend. + XRef PHSN omap CatalogStart ld_put + (Adjusted positions) now + + % Construct the hint tables (part 5). + + { writePageOffsetHints } totemp + pop /PHSTempStart exch def + { writeSharedObjectHints } totemp + exch PHSTempStart sub PHS /S 3 -1 roll put + PHSTempStart sub /PHSTempLength exch def + (Wrote hints) now + + % Prepare to read TFile. +% TFile closefile +% /TFile TFileName (r) file def + + PHS + dup /File TFile put + dup /FilePosition PHSTempStart put + dup /Length PHSTempLength put + pop + /PHSLength { writePart5 } tolength def + + % Construct the linearization parameter dictionary (part 2). + + PHSStart + dup PHSLength add % phsend + /FirstPageStart 1 index def + dup FirstPageLength add % firstpageend + dup OtherObjectsLength add + /XrefStart 1 index def + XrefBeginLength add % xref0start + dup XrefBeginLength sub XrefLength add % fileend + % Because of a bug, Acrobat Reader doesn't recognize any file + % shorter than 1K as linearized. Patch this here. + 1024 .max + /FileLength 1 index def + createLPDict + + % Adjust the object positions again, taking the PHS into account. + + PHSStart 0 PHSLength adjustObjectPositions + (Readjusted positions) now + + % Finally, write the output file. + + writePart1 + writePart2 + FirstPageXrefStart padto + XrefStart writePart3 + CatalogStart padto + CatalogTempStart CatalogTempEnd copyrange % part 4 + writePart5 + FirstPageStart padto + FirstPageTempStart NonPageTempEnd copyrange % parts 6,7,8,9 + % No Overflow Hint Stream (part 10). + XrefStart padto + writePart11xref + { FirstPageXrefStart writePart11rest } tomemory + FileLength 1 index length sub padto ows + (Wrote output file) now + + % Wrap up. + + TFile closefile TFileName deletefile + end % temporary dict + end % IDict +} bind def + +end % pdfoptdict +.setglobal + +% Check for command line arguments. +[ shellarguments { + ] dup length 2 eq { + % Load the pdfwrite utilities if necessary. + /omapinit where { pop } { (pdfwrite.ps) runlibfile } ifelse + save exch + aload pop exch (r) file exch (w) file + 3000000 setvmthreshold + 0 setobjectformat + pdfoptdict begin pdfOptimize end + restore + } { + (Usage: gs -dNODISPLAY -- pdfopt.ps input.pdf output.pdf) = flush quit + } ifelse +} { + pop +} ifelse |