% Copyright (C) 1994-2003 artofcode LLC. 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: pdf_base.ps,v 1.48 2005/09/16 19:01:30 ray Exp $% pdf_base.ps% Basic parser for PDF reader. % This handles basic parsing of the file (including the trailer% and cross-reference table), as well as objects, object references,% streams, and name/number trees; it doesn't include any facilities for% making marks on the page. /.setlanguagelevel where { pop 2 .setlanguagelevel } if.currentglobal true .setglobal/pdfdict where { pop } { /pdfdict 100 dict def } ifelsepdfdict begin % Define the name interpretation dictionary for reading values./valueopdict mark (<<) cvn { mark } bind % don't push an actual mark! (>>) cvn { { .dicttomark } stopped { ( **** File has an unbalanced >> \(close dictionary\).\n) pdfformaterror } if } bind ([) cvn { mark } bind % ditto (]) cvn dup load% /true true % see .pdfexectoken below% /false false % ibid.% /null null % ibid. /F dup cvx % see Objects section below /R dup cvx % see Objects section below /stream dup cvx % see Streams section below.dicttomark readonly def % ------ Utilities ------ % % Define a scratch string. The PDF language definition says that% no line in a PDF file can exceed 255 characters./pdfstring 255 string def % Read the previous line of a file. If we aren't at a line boundary,% read the line containing the current position.% Skip any blank lines./prevline % - prevline <startpos> <substring> { PDFfile fileposition dup () pdfstring 2 index 257 sub 0 .max PDFfile exch setfileposition { % Stack: initpos linepos line string PDFfile fileposition PDFfile 2 index readline pop dup length 0 gt { 3 2 roll 5 -2 roll pop pop 2 index } { pop } ifelse % Stack: initpos linepos line string startpos PDFfile fileposition 5 index ge { exit } if pop } loop pop pop 3 -1 roll pop } bind def % Handle the PDF 1.2 #nn escape convention when reading from a file.% This should eventually be done in C./.pdffixname { % <execname> .pdffixname <execname'> PDFversion 1.2 ge { dup .namestring (#) search { name#escape cvn exch pop } { pop } ifelse } if} bind def/name#escape % <post> <(#)> <pre> name#escape <string>{ exch pop 1 index 2 () /SubFileDecode filter dup (x) readhexstring % Stack: post pre stream char t/f not { % tolerate, but complain about bad syntax pop closefile (#) concatstrings exch ( **** Warning: Invalid hex following '#' name escape, using literal '#' in name.\n) pdfformaterror } { exch closefile concatstrings exch 2 1 index length 2 sub getinterval } ifelse (#) search { name#escape } if concatstrings} bind def % Execute a file, interpreting its executable names in a given% dictionary. The name procedures may do whatever they want% to the operand stack./.pdftokenerror { % <count> <opdict> <errtoken> .pdftokenerror - BXlevel 0 le { ( **** Unknown operator: ') pdfformaterror dup =string cvs pdfformaterror % Attempt a retry scan of the element after changing to PDFScanInvNum << /PDFScanInvNum true >> setuserparams =string cvs token pop exch pop dup type dup /integertype eq exch /realtype eq or { exch pop exch pop (', processed as number, value: ) pdfformaterror dup =string cvs pdfformaterror (\n) pdfformaterror << /PDFScanInvNum null >> setuserparams % reset to default scanning rules false % suppress any stack cleanup } { % error was non-recoverable with modified scanning rules ('\n) pdfformaterror true } ifelse } { true } ifelse { % clean up the operand stack if this was non-recoverable pop pop count exch sub { pop } repeat % pop all the operands } if} bind def/.pdfexectoken { % <count> <opdict> <exectoken> .pdfexectoken ? PDFDEBUG { pdfdict /PDFSTEPcount known not { pdfdict /PDFSTEPcount 1 .forceput } if PDFSTEP { pdfdict /PDFtokencount 2 copy .knownget { 1 add } { 1 } ifelse .forceput PDFSTEPcount 1 gt { pdfdict /PDFSTEPcount PDFSTEPcount 1 sub .forceput } { dup ==only ( step # ) print PDFtokencount =only ( ? ) print flush 1 false .outputpage (%stdin) (r) file 255 string readline { token { exch pop pdfdict /PDFSTEPcount 3 -1 roll .forceput } { pdfdict /PDFSTEPcount 1 .forceput } ifelse % token } { pop /PDFSTEP false def % EOF on stdin } ifelse % readline } ifelse % PDFSTEPcount > 1 } { dup ==only () = flush } ifelse % PDFSTEP } if % PDFDEBUG 2 copy .knownget { exch pop exch pop exch pop exec } { % Normally, true, false, and null would appear in opdict % and be treated as "operators". However, there is a % special fast case in the PostScript interpreter for names % that are defined in, and only in, systemdict and/or % userdict: putting these three names in the PDF dictionaries % destroys this property for them, slowing down their % interpretation in all PostScript code. Therefore, we % check for them explicitly here instead. dup dup dup /true eq exch /false eq or exch /null eq or { exch pop exch pop //systemdict exch get } { .pdftokenerror } ifelse } ifelse} bind def/.pdfrun { % <file> <opdict> .pdfrun - % Construct a procedure with the stack depth, file and opdict % bound into it. 1 index cvlit count 2 sub 3 1 roll mark mark 5 2 roll { % Stack: ..operands.. count opdict file token { dup type /nametype eq { dup xcheck { .pdfexectoken } { .pdffixname exch pop exch pop PDFDEBUG { PDFSTEPcount 1 le { dup ==only ( ) print flush } if } if } ifelse } { exch pop exch pop PDFDEBUG { PDFSTEPcount 1 le { dup ==only ( ) print flush } if } if } ifelse } { (%%EOF) cvn cvx .pdfexectoken } ifelse } aload pop .packtomark cvx /loop cvx 2 packedarray cvx { stopped /PDFsource } aload pop PDFsource { store { stop } if } aload pop .packtomark cvx /PDFsource 3 -1 roll store exec} bind def % Execute a file, like .pdfrun, for a marking context.% This temporarily rebinds LocalResources and DefaultQstate./.pdfruncontext { % <resdict> <file> <opdict> .pdfruncontext - /.pdfrun load LocalResources DefaultQstate /LocalResources 7 -1 roll store /DefaultQstate qstate store 3 .execn /DefaultQstate exch store /LocalResources exch store} bind def % Get the depth of the PDF operand stack. The caller sets pdfemptycount% before calling .pdfrun or .pdfruncontext. It is initially set by% pdf_main, and is also set by any routine which changes the operand% stack depth (currently .pdfpaintproc, although there are other callers% of .pdfrun{context} which have not been checked for opstack depth./.pdfcount { % - .pdfcount <count> count pdfemptycount sub} bind def % ================================ Objects ================================ % % Since we may have more than 64K objects, we have to use a 2-D array to% hold them (and the parallel Generations structure)./lshift 9 def/lnshift lshift neg def/lsubmask 1 lshift bitshift 1 sub def/lsublen lsubmask 1 add def/larray { % - larray <larray> [ [] ]} bind def/lstring { % - lstring <lstring> [ () ]} bind def/ltype { % <lseq> type <type> 0 get type} bind def/lget { % <lseq> <index> lget <value> dup //lsubmask and 3 1 roll //lnshift bitshift get exch get} bind def/lput { % <lseq> <index> <value> lput - 3 1 roll dup //lsubmask and 4 1 roll //lnshift bitshift get 3 1 roll put} bind def/llength { % <lseq> llength <length> dup length 1 sub dup //lshift bitshift 3 1 roll get length add} bind def% lgrowto assumes newlength > llength(lseq)/growto { % <string/array> <length> growto <string'/array'> 1 index type /stringtype eq { string } { array } ifelse 2 copy copy pop exch pop} bind def/lgrowto { % <lseq> <newlength> lgrowto <lseq'> dup //lsubmask add //lnshift bitshift dup 3 index length gt { % Add more sub-arrays. Start by completing the last existing one. % Stack: lseq newlen newtoplen 3 -1 roll dup llength 1 sub //lsubmask or 1 add lgrowto % Stack: newlen newtoplen lseq [ exch aload pop counttomark 2 add -1 roll % newtoplen counttomark sub { dup 0 0 getinterval lsublen growto } repeat dup 0 0 getinterval ] exch } { pop } ifelse % Expand the last sub-array. 1 sub //lsubmask and 1 add exch dup dup length 1 sub 2 copy % Stack: newsublen lseq lseq len-1 lseq len-1 get 5 -1 roll growto put} bind def/lforall { % <lseq> <proc> lforall - /forall cvx 2 packedarray cvx forall} bind def % We keep track of PDF objects using the following PostScript variables:%% Generations (lstring): Generations[N] holds 1+ the current% generation number for object number N. (As far as we can tell,% this is needed only for error checking.) For free objects,% Generations[N] is 0.%% Objects (larray): If object N is loaded, Objects[N] is the actual% object; otherwise, Objects[N] is an executable integer giving% the file offset of the object's location in the file. If% ObjectStream[N] is non-zero then Objects[N] contains the index% into the object stream instead of the file offset of the object.%% ObjectStream (larray): If object N is in an object stream then% ObjectStream[N] holds the object number of the object stream.% Otherwise ObjectStream[N] contains 0. If ObjectStream[N]% is non-zero then Objects[N] contains the index into the object% stream.%% GlobalObjects (dictionary): If object N has been resolved in% global VM, GlobalObjects[N] is the same as Objects[N]% (except that GlobalObjects itself is stored in global VM,% so the entry will not be deleted at the end of the page).%% IsGlobal (lstring): IsGlobal[N] = 1 iff object N was resolved in% global VM. This is an accelerator to avoid having to do a% dictionary lookup in GlobalObjects when resolving every object. % Initialize the PDF object tables./initPDFobjects { % - initPDFobjects - /ObjectStream larray def /Objects larray def /Generations lstring def .currentglobal true .setglobal /GlobalObjects 20 dict def .setglobal /IsGlobal lstring def} bind def % Grow the tables to a specified size./growPDFobjects { % <minsize> growPDFobjects - dup ObjectStream llength gt { dup ObjectStream exch lgrowto /ObjectStream exch def } if dup Objects llength gt { dup Objects exch lgrowto /Objects exch def } if dup Generations llength gt { dup Generations exch lgrowto /Generations exch def } if dup IsGlobal llength gt { dup IsGlobal exch lgrowto /IsGlobal exch def } if pop} bind def % We represent an unresolved object reference by a procedure of the form% {obj# gen# resolveR}. This is not a possible PDF object, because PDF has% no way to represent procedures. Since PDF in fact has no way to represent% any PostScript object that doesn't evaluate to itself, we can 'force'% a possibly indirect object painlessly with 'exec'.% Note that since we represent streams by executable dictionaries% (see below), we need both an xcheck and a type check to determine% whether an object has been resolved./resolved? { % <object#> resolved? <value> true % <object#> resolved? false Objects 1 index lget dup xcheck { % Check if executable dup type /integertype eq { % Check if an integer % Check whether the object is in GlobalObjects. pop IsGlobal 1 index lget 0 eq { % 0 --> Not in GlabalObjects pop false % The object is not resolved } { % The object is in GlobalObjects % Update Objects from GlobalObjects PDFDEBUG { (%Global=>local: ) print dup == } if GlobalObjects 1 index get dup Objects 4 1 roll lput true } ifelse } { % Else object is executable but not integer exch pop true % Therefore must be executable dict. (stream) } ifelse } { % Else object is not executable. exch pop true % Therefore it must have been resolved. } ifelse} bind def/oforce /exec load def/oget { % <array> <index> oget <object> % <dict> <key> oget <object> % Before release 6.20, this procedure stored the resolved % object back into the referring slot. In order to support % PDF linearization, we no longer do this. get oforce} bind def/oforce_array { % <array> oforce_array <array> [ exch { oforce } forall ]} bind def/oforce_elems { % <array> oforce_elems <first> ... <last> { oforce } forall} bind def% A null value in a dictionary is equivalent to an omitted key;% we must check for this specially./knownoget { % <dict> <key> knownoget <value> true % <dict> <key> knownoget false % See oget above regarding this procedure. .knownget { oforce dup null eq { pop false } { true } ifelse } { false } ifelse} bind def % PDF 1.1 defines a 'foreign file reference', but not its meaning.% Per the specification, we convert these to nulls./F { % <file#> <object#> <generation#> F <object> % Some PDF 1.1 files use F as a synonym for f! .pdfcount 3 lt { f } { pop pop pop null } ifelse} bind def % Verify the generation number for a specified object% Note: The values in Generations is the generation number plus 1.% If the value in Generations is zero then the object is free./checkgeneration { % <object#> <generation#> checkgeneration <object#> <OK> Generations 2 index lget 1 sub 1 index eq { % If generation # match ... pop true % Then return true } { % Else not a match ... QUIET not { % Create warning message if not QUIET Generations 2 index lget 0 eq { % Check if object is free ... ( **** Warning: reference to free object: ) } { ( **** Warning: wrong generation: ) } ifelse 2 index =string cvs concatstrings ( ) concatstrings % put obj # exch =string cvs concatstrings ( R\n) concatstrings % put gen # pdfformaterror % Output warning message } { % Else QUIET ... pop % Pop generation umber } ifelse false % Return false if gen # not match } ifelse} bind def/R { % <object#> <generation#> R <object> /resolveR cvx 3 packedarray cvx} bind def % If we encounter an object definition while reading sequentially,% we just store it away and keep going./objopdict mark valueopdict { } forall /endobj dup cvx.dicttomark readonly def /obj { % <object#> <generation#> obj <object> PDFfile objopdict .pdfrun} bind def /endobj { % <object#> <generation#> <object> endobj <object> 3 1 roll % Read the xref entry if we haven't yet done so. % This is only needed for generation # checking. 1 index resolved? { pop } if checkgeneration { % The only global objects we bother to save are % (resource) dictionaries. 1 index dup gcheck exch type /dicttype eq and { PDFDEBUG { (%Local=>global: ) print dup == } if GlobalObjects 1 index 3 index put IsGlobal 1 index 1 put } if Objects exch 2 index lput } { pop pop null } ifelse} bind def % When resolving an object reference in an object stream, we stop at% the end of file. Note: Objects in an object stream do not have either% a starting 'obj' or and ending 'endobj'./resolveobjstreamopdict mark valueopdict { } forall (%%EOF) cvn { exit } bind.dicttomark readonly def % Note: This version of this function is not currently being used.% Resolve all objects in an object stream/resolveobjectstream { % <object stream #> resolveobjectstream - PDFDEBUG { (%Resolving object stream: ) print } if 0 resolveR % Get the objectstream dict, all objstrms use 0 as the gen # dup /First get % Save location of first object onto the stack 1 index /N get % Save number of objects onto the stack 2 index false resolvestream % Convert stream dict into a stream /ReusableStreamDecode filter % We need to be able to position stream % Objectstreams begin with list of object numbers and locations % Create two arrays to hold object numbers and stream location 1 index array % Array for holding object number 2 index array % Array for holding stream object location % Get the object numbers and locations. 0 1 5 index 1 sub { % Loop and collect obj # and locations % Stack: objstreamdict First N objectstream [obj#] [loc] index 2 index 1 index % Setup to put obj# into object number array 5 index token pop put % Get stream, then get obj# and put into array 1 index 1 index % Setup to put object loc into location array 5 index token pop put % Get stream, get obj loc and put into array pop % Remove loop index } for % Create a bytestring big enough for reading any object data % Scan for the size of the largest object 0 0 % Init max object size and previous location 2 index { % Loop through all object locations % Stack: ... maxsize prevloc currentloc dup 4 1 roll % Save copy of object location into stack exch sub % Object size = currentloc - prevloc .max % Determine maximum object size exch % Put max size under previous location } forall pop % Remove previous location .bigstring % Create bytestring based upon max obj size % Move to the start of the object dataLines 1–500 of 922. Load more
% Copyright (C) 1994-2003 artofcode LLC. 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: pdf_base.ps,v 1.48 2005/09/16 19:01:30 ray Exp $ % pdf_base.ps % Basic parser for PDF reader. % This handles basic parsing of the file (including the trailer % and cross-reference table), as well as objects, object references, % streams, and name/number trees; it doesn't include any facilities for % making marks on the page. /.setlanguagelevel where { pop 2 .setlanguagelevel } if .currentglobal true .setglobal /pdfdict where { pop } { /pdfdict 100 dict def } ifelse pdfdict begin % Define the name interpretation dictionary for reading values. /valueopdict mark (<<) cvn { mark } bind % don't push an actual mark! (>>) cvn { { .dicttomark } stopped { ( **** File has an unbalanced >> \(close dictionary\).\n) pdfformaterror } if } bind ([) cvn { mark } bind % ditto (]) cvn dup load % /true true % see .pdfexectoken below % /false false % ibid. % /null null % ibid. /F dup cvx % see Objects section below /R dup cvx % see Objects section below /stream dup cvx % see Streams section below .dicttomark readonly def % ------ Utilities ------ % % Define a scratch string. The PDF language definition says that % no line in a PDF file can exceed 255 characters. /pdfstring 255 string def % Read the previous line of a file. If we aren't at a line boundary, % read the line containing the current position. % Skip any blank lines. /prevline % - prevline <startpos> <substring> { PDFfile fileposition dup () pdfstring 2 index 257 sub 0 .max PDFfile exch setfileposition { % Stack: initpos linepos line string PDFfile fileposition PDFfile 2 index readline pop dup length 0 gt { 3 2 roll 5 -2 roll pop pop 2 index } { pop } ifelse % Stack: initpos linepos line string startpos PDFfile fileposition 5 index ge { exit } if pop } loop pop pop 3 -1 roll pop } bind def % Handle the PDF 1.2 #nn escape convention when reading from a file. % This should eventually be done in C. /.pdffixname { % <execname> .pdffixname <execname'> PDFversion 1.2 ge { dup .namestring (#) search { name#escape cvn exch pop } { pop } ifelse } if } bind def /name#escape % <post> <(#)> <pre> name#escape <string> { exch pop 1 index 2 () /SubFileDecode filter dup (x) readhexstring % Stack: post pre stream char t/f not { % tolerate, but complain about bad syntax pop closefile (#) concatstrings exch ( **** Warning: Invalid hex following '#' name escape, using literal '#' in name.\n) pdfformaterror } { exch closefile concatstrings exch 2 1 index length 2 sub getinterval } ifelse (#) search { name#escape } if concatstrings } bind def % Execute a file, interpreting its executable names in a given % dictionary. The name procedures may do whatever they want % to the operand stack. /.pdftokenerror { % <count> <opdict> <errtoken> .pdftokenerror - BXlevel 0 le { ( **** Unknown operator: ') pdfformaterror dup =string cvs pdfformaterror % Attempt a retry scan of the element after changing to PDFScanInvNum << /PDFScanInvNum true >> setuserparams =string cvs token pop exch pop dup type dup /integertype eq exch /realtype eq or { exch pop exch pop (', processed as number, value: ) pdfformaterror dup =string cvs pdfformaterror (\n) pdfformaterror << /PDFScanInvNum null >> setuserparams % reset to default scanning rules false % suppress any stack cleanup } { % error was non-recoverable with modified scanning rules ('\n) pdfformaterror true } ifelse } { true } ifelse { % clean up the operand stack if this was non-recoverable pop pop count exch sub { pop } repeat % pop all the operands } if } bind def /.pdfexectoken { % <count> <opdict> <exectoken> .pdfexectoken ? PDFDEBUG { pdfdict /PDFSTEPcount known not { pdfdict /PDFSTEPcount 1 .forceput } if PDFSTEP { pdfdict /PDFtokencount 2 copy .knownget { 1 add } { 1 } ifelse .forceput PDFSTEPcount 1 gt { pdfdict /PDFSTEPcount PDFSTEPcount 1 sub .forceput } { dup ==only ( step # ) print PDFtokencount =only ( ? ) print flush 1 false .outputpage (%stdin) (r) file 255 string readline { token { exch pop pdfdict /PDFSTEPcount 3 -1 roll .forceput } { pdfdict /PDFSTEPcount 1 .forceput } ifelse % token } { pop /PDFSTEP false def % EOF on stdin } ifelse % readline } ifelse % PDFSTEPcount > 1 } { dup ==only () = flush } ifelse % PDFSTEP } if % PDFDEBUG 2 copy .knownget { exch pop exch pop exch pop exec } { % Normally, true, false, and null would appear in opdict % and be treated as "operators". However, there is a % special fast case in the PostScript interpreter for names % that are defined in, and only in, systemdict and/or % userdict: putting these three names in the PDF dictionaries % destroys this property for them, slowing down their % interpretation in all PostScript code. Therefore, we % check for them explicitly here instead. dup dup dup /true eq exch /false eq or exch /null eq or { exch pop exch pop //systemdict exch get } { .pdftokenerror } ifelse } ifelse } bind def /.pdfrun { % <file> <opdict> .pdfrun - % Construct a procedure with the stack depth, file and opdict % bound into it. 1 index cvlit count 2 sub 3 1 roll mark mark 5 2 roll { % Stack: ..operands.. count opdict file token { dup type /nametype eq { dup xcheck { .pdfexectoken } { .pdffixname exch pop exch pop PDFDEBUG { PDFSTEPcount 1 le { dup ==only ( ) print flush } if } if } ifelse } { exch pop exch pop PDFDEBUG { PDFSTEPcount 1 le { dup ==only ( ) print flush } if } if } ifelse } { (%%EOF) cvn cvx .pdfexectoken } ifelse } aload pop .packtomark cvx /loop cvx 2 packedarray cvx { stopped /PDFsource } aload pop PDFsource { store { stop } if } aload pop .packtomark cvx /PDFsource 3 -1 roll store exec } bind def % Execute a file, like .pdfrun, for a marking context. % This temporarily rebinds LocalResources and DefaultQstate. /.pdfruncontext { % <resdict> <file> <opdict> .pdfruncontext - /.pdfrun load LocalResources DefaultQstate /LocalResources 7 -1 roll store /DefaultQstate qstate store 3 .execn /DefaultQstate exch store /LocalResources exch store } bind def % Get the depth of the PDF operand stack. The caller sets pdfemptycount % before calling .pdfrun or .pdfruncontext. It is initially set by % pdf_main, and is also set by any routine which changes the operand % stack depth (currently .pdfpaintproc, although there are other callers % of .pdfrun{context} which have not been checked for opstack depth. /.pdfcount { % - .pdfcount <count> count pdfemptycount sub } bind def % ================================ Objects ================================ % % Since we may have more than 64K objects, we have to use a 2-D array to % hold them (and the parallel Generations structure). /lshift 9 def /lnshift lshift neg def /lsubmask 1 lshift bitshift 1 sub def /lsublen lsubmask 1 add def /larray { % - larray <larray> [ [] ] } bind def /lstring { % - lstring <lstring> [ () ] } bind def /ltype { % <lseq> type <type> 0 get type } bind def /lget { % <lseq> <index> lget <value> dup //lsubmask and 3 1 roll //lnshift bitshift get exch get } bind def /lput { % <lseq> <index> <value> lput - 3 1 roll dup //lsubmask and 4 1 roll //lnshift bitshift get 3 1 roll put } bind def /llength { % <lseq> llength <length> dup length 1 sub dup //lshift bitshift 3 1 roll get length add } bind def % lgrowto assumes newlength > llength(lseq) /growto { % <string/array> <length> growto <string'/array'> 1 index type /stringtype eq { string } { array } ifelse 2 copy copy pop exch pop } bind def /lgrowto { % <lseq> <newlength> lgrowto <lseq'> dup //lsubmask add //lnshift bitshift dup 3 index length gt { % Add more sub-arrays. Start by completing the last existing one. % Stack: lseq newlen newtoplen 3 -1 roll dup llength 1 sub //lsubmask or 1 add lgrowto % Stack: newlen newtoplen lseq [ exch aload pop counttomark 2 add -1 roll % newtoplen counttomark sub { dup 0 0 getinterval lsublen growto } repeat dup 0 0 getinterval ] exch } { pop } ifelse % Expand the last sub-array. 1 sub //lsubmask and 1 add exch dup dup length 1 sub 2 copy % Stack: newsublen lseq lseq len-1 lseq len-1 get 5 -1 roll growto put } bind def /lforall { % <lseq> <proc> lforall - /forall cvx 2 packedarray cvx forall } bind def % We keep track of PDF objects using the following PostScript variables: % % Generations (lstring): Generations[N] holds 1+ the current % generation number for object number N. (As far as we can tell, % this is needed only for error checking.) For free objects, % Generations[N] is 0. % % Objects (larray): If object N is loaded, Objects[N] is the actual % object; otherwise, Objects[N] is an executable integer giving % the file offset of the object's location in the file. If % ObjectStream[N] is non-zero then Objects[N] contains the index % into the object stream instead of the file offset of the object. % % ObjectStream (larray): If object N is in an object stream then % ObjectStream[N] holds the object number of the object stream. % Otherwise ObjectStream[N] contains 0. If ObjectStream[N] % is non-zero then Objects[N] contains the index into the object % stream. % % GlobalObjects (dictionary): If object N has been resolved in % global VM, GlobalObjects[N] is the same as Objects[N] % (except that GlobalObjects itself is stored in global VM, % so the entry will not be deleted at the end of the page). % % IsGlobal (lstring): IsGlobal[N] = 1 iff object N was resolved in % global VM. This is an accelerator to avoid having to do a % dictionary lookup in GlobalObjects when resolving every object. % Initialize the PDF object tables. /initPDFobjects { % - initPDFobjects - /ObjectStream larray def /Objects larray def /Generations lstring def .currentglobal true .setglobal /GlobalObjects 20 dict def .setglobal /IsGlobal lstring def } bind def % Grow the tables to a specified size. /growPDFobjects { % <minsize> growPDFobjects - dup ObjectStream llength gt { dup ObjectStream exch lgrowto /ObjectStream exch def } if dup Objects llength gt { dup Objects exch lgrowto /Objects exch def } if dup Generations llength gt { dup Generations exch lgrowto /Generations exch def } if dup IsGlobal llength gt { dup IsGlobal exch lgrowto /IsGlobal exch def } if pop } bind def % We represent an unresolved object reference by a procedure of the form % {obj# gen# resolveR}. This is not a possible PDF object, because PDF has % no way to represent procedures. Since PDF in fact has no way to represent % any PostScript object that doesn't evaluate to itself, we can 'force' % a possibly indirect object painlessly with 'exec'. % Note that since we represent streams by executable dictionaries % (see below), we need both an xcheck and a type check to determine % whether an object has been resolved. /resolved? { % <object#> resolved? <value> true % <object#> resolved? false Objects 1 index lget dup xcheck { % Check if executable dup type /integertype eq { % Check if an integer % Check whether the object is in GlobalObjects. pop IsGlobal 1 index lget 0 eq { % 0 --> Not in GlabalObjects pop false % The object is not resolved } { % The object is in GlobalObjects % Update Objects from GlobalObjects PDFDEBUG { (%Global=>local: ) print dup == } if GlobalObjects 1 index get dup Objects 4 1 roll lput true } ifelse } { % Else object is executable but not integer exch pop true % Therefore must be executable dict. (stream) } ifelse } { % Else object is not executable. exch pop true % Therefore it must have been resolved. } ifelse } bind def /oforce /exec load def /oget { % <array> <index> oget <object> % <dict> <key> oget <object> % Before release 6.20, this procedure stored the resolved % object back into the referring slot. In order to support % PDF linearization, we no longer do this. get oforce } bind def /oforce_array { % <array> oforce_array <array> [ exch { oforce } forall ] } bind def /oforce_elems { % <array> oforce_elems <first> ... <last> { oforce } forall } bind def % A null value in a dictionary is equivalent to an omitted key; % we must check for this specially. /knownoget { % <dict> <key> knownoget <value> true % <dict> <key> knownoget false % See oget above regarding this procedure. .knownget { oforce dup null eq { pop false } { true } ifelse } { false } ifelse } bind def % PDF 1.1 defines a 'foreign file reference', but not its meaning. % Per the specification, we convert these to nulls. /F { % <file#> <object#> <generation#> F <object> % Some PDF 1.1 files use F as a synonym for f! .pdfcount 3 lt { f } { pop pop pop null } ifelse } bind def % Verify the generation number for a specified object % Note: The values in Generations is the generation number plus 1. % If the value in Generations is zero then the object is free. /checkgeneration { % <object#> <generation#> checkgeneration <object#> <OK> Generations 2 index lget 1 sub 1 index eq { % If generation # match ... pop true % Then return true } { % Else not a match ... QUIET not { % Create warning message if not QUIET Generations 2 index lget 0 eq { % Check if object is free ... ( **** Warning: reference to free object: ) } { ( **** Warning: wrong generation: ) } ifelse 2 index =string cvs concatstrings ( ) concatstrings % put obj # exch =string cvs concatstrings ( R\n) concatstrings % put gen # pdfformaterror % Output warning message } { % Else QUIET ... pop % Pop generation umber } ifelse false % Return false if gen # not match } ifelse } bind def /R { % <object#> <generation#> R <object> /resolveR cvx 3 packedarray cvx } bind def % If we encounter an object definition while reading sequentially, % we just store it away and keep going. /objopdict mark valueopdict { } forall /endobj dup cvx .dicttomark readonly def /obj { % <object#> <generation#> obj <object> PDFfile objopdict .pdfrun } bind def /endobj { % <object#> <generation#> <object> endobj <object> 3 1 roll % Read the xref entry if we haven't yet done so. % This is only needed for generation # checking. 1 index resolved? { pop } if checkgeneration { % The only global objects we bother to save are % (resource) dictionaries. 1 index dup gcheck exch type /dicttype eq and { PDFDEBUG { (%Local=>global: ) print dup == } if GlobalObjects 1 index 3 index put IsGlobal 1 index 1 put } if Objects exch 2 index lput } { pop pop null } ifelse } bind def % When resolving an object reference in an object stream, we stop at % the end of file. Note: Objects in an object stream do not have either % a starting 'obj' or and ending 'endobj'. /resolveobjstreamopdict mark valueopdict { } forall (%%EOF) cvn { exit } bind .dicttomark readonly def % Note: This version of this function is not currently being used. % Resolve all objects in an object stream /resolveobjectstream { % <object stream #> resolveobjectstream - PDFDEBUG { (%Resolving object stream: ) print } if 0 resolveR % Get the objectstream dict, all objstrms use 0 as the gen # dup /First get % Save location of first object onto the stack 1 index /N get % Save number of objects onto the stack 2 index false resolvestream % Convert stream dict into a stream /ReusableStreamDecode filter % We need to be able to position stream % Objectstreams begin with list of object numbers and locations % Create two arrays to hold object numbers and stream location 1 index array % Array for holding object number 2 index array % Array for holding stream object location % Get the object numbers and locations. 0 1 5 index 1 sub { % Loop and collect obj # and locations % Stack: objstreamdict First N objectstream [obj#] [loc] index 2 index 1 index % Setup to put obj# into object number array 5 index token pop put % Get stream, then get obj# and put into array 1 index 1 index % Setup to put object loc into location array 5 index token pop put % Get stream, get obj loc and put into array pop % Remove loop index } for % Create a bytestring big enough for reading any object data % Scan for the size of the largest object 0 0 % Init max object size and previous location 2 index { % Loop through all object locations % Stack: ... maxsize prevloc currentloc dup 4 1 roll % Save copy of object location into stack exch sub % Object size = currentloc - prevloc .max % Determine maximum object size exch % Put max size under previous location } forall pop % Remove previous location .bigstring % Create bytestring based upon max obj size % Move to the start of the object data