% Copyright (C) 2000 Aladdin Enterprises. All rights reserved.% % This file is part of AFPL Ghostscript.% % AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND. No author or% distributor accepts any responsibility for the consequences of using it, or% for whether it serves any particular purpose or works at all, unless he or% she says so in writing. Refer to the Aladdin Free Public License (the% "License") for full details.% % Every copy of AFPL Ghostscript must include a copy of the License, normally% in a plain ASCII text file named PUBLIC. The License grants you the right% to copy, modify and redistribute AFPL Ghostscript, but only under certain% conditions described in the License. Among other things, the License% requires that the copyright notice and this notice be preserved on all% copies. % $Id: pdfopt.ps,v 1.4 2000/12/01 04:15:52 lpd Exp $% PDF linearizer ("optimizer"). .currentglobal true .setglobal/pdfoptdict 200 dict defpdfoptdict 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 % Collect the list of currently mapped object numbers, in order./omapped { % - omapped <obj#s> RMap length array RMap { 2 index 3 1 roll 1 sub exch put } 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 .setglobaluserdict /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 - PDEBUG { ( ) 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 % 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? { PDEBUG { (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 dup /OpenAction .knownget { omvisit } if Trailer /Encrypt .knownget { omvisit } if dup /Threads .knownget { omforce { omforce } 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 /identifyPageObjects { % <extra> <page#> identifyPageObjects <obj#s> pdffindpageref 4 dict begin /images 10 darray def /fontfiles 10 darray def { omforce % Stack: 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} bind def % Identify the objects of the first page./identifyFirstPageObjects { % -identifyFirstPageObjects <obj#s> Trailer /Root oget null 1 index /PageMode knownoget { /UseOutlines eq { pop dup /Outlines get } if } if exch pop 1 identifyPageObjects} bind def % Identify the non-shared objects of the other pages, and the shared objects./identifyOtherPageObjects { % - identifyOtherPageObjects [<pageobj#s> ...] % <sharedobj#s> 4 dict begin /marks lstring Objects llength lgrowto def % Mark document-level and first page objectsw. [CatalogNs FirstPageNs] { { marks exch 255 lput } forall } forall % 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 [ exch { [ exch { marks 1 index lget 1 ne { pop } if } forall ] } forall ] [ 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 PHSN] CatalogNs FirstPageNs SharedNs] OtherPageNs] { { { marks exch 1 lput } forall } forall } forall %****** PUT THESE IN A REASONABLE ORDER ****** [ 1 1 Objects llength 1 sub { marks 1 index lget 0 eq { Generations 1 index lget 0 eq { pop } if } { pop } ifelse } for ]} 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.Lines 1–500 of 1054. Load more
% Copyright (C) 2000 Aladdin Enterprises. All rights reserved. % % This file is part of AFPL Ghostscript. % % AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND. No author or % distributor accepts any responsibility for the consequences of using it, or % for whether it serves any particular purpose or works at all, unless he or % she says so in writing. Refer to the Aladdin Free Public License (the % "License") for full details. % % Every copy of AFPL Ghostscript must include a copy of the License, normally % in a plain ASCII text file named PUBLIC. The License grants you the right % to copy, modify and redistribute AFPL Ghostscript, but only under certain % conditions described in the License. Among other things, the License % requires that the copyright notice and this notice be preserved on all % copies. % $Id: pdfopt.ps,v 1.4 2000/12/01 04:15:52 lpd 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 % Collect the list of currently mapped object numbers, in order. /omapped { % - omapped <obj#s> RMap length array RMap { 2 index 3 1 roll 1 sub exch put } 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 - PDEBUG { ( ) 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 % 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? { PDEBUG { (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 dup /OpenAction .knownget { omvisit } if Trailer /Encrypt .knownget { omvisit } if dup /Threads .knownget { omforce { omforce } 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 /identifyPageObjects { % <extra> <page#> identifyPageObjects <obj#s> pdffindpageref 4 dict begin /images 10 darray def /fontfiles 10 darray def { omforce % Stack: 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 } bind def % Identify the objects of the first page. /identifyFirstPageObjects { % -identifyFirstPageObjects <obj#s> Trailer /Root oget null 1 index /PageMode knownoget { /UseOutlines eq { pop dup /Outlines get } if } if exch pop 1 identifyPageObjects } bind def % Identify the non-shared objects of the other pages, and the shared objects. /identifyOtherPageObjects { % - identifyOtherPageObjects [<pageobj#s> ...] % <sharedobj#s> 4 dict begin /marks lstring Objects llength lgrowto def % Mark document-level and first page objectsw. [CatalogNs FirstPageNs] { { marks exch 255 lput } forall } forall % 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 [ exch { [ exch { marks 1 index lget 1 ne { pop } if } forall ] } forall ] [ 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 PHSN] CatalogNs FirstPageNs SharedNs] OtherPageNs] { { { marks exch 1 lput } forall } forall } forall %****** PUT THESE IN A REASONABLE ORDER ****** [ 1 1 Objects llength 1 sub { marks 1 index lget 0 eq { Generations 1 index lget 0 eq { pop } if } { pop } ifelse } for ] } 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.