diff options
Diffstat (limited to 'Master/tlpkg/tlgs/Resource/Init/pdf_base.ps')
-rw-r--r-- | Master/tlpkg/tlgs/Resource/Init/pdf_base.ps | 1225 |
1 files changed, 1225 insertions, 0 deletions
diff --git a/Master/tlpkg/tlgs/Resource/Init/pdf_base.ps b/Master/tlpkg/tlgs/Resource/Init/pdf_base.ps new file mode 100644 index 00000000000..bdfe9ee8feb --- /dev/null +++ b/Master/tlpkg/tlgs/Resource/Init/pdf_base.ps @@ -0,0 +1,1225 @@ +% Copyright (C) 2001-2012 Artifex Software, Inc. +% 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. +% +% Refer to licensing information at http://www.artifex.com or contact +% Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael, +% CA 94903, U.S.A., +1(415)492-9861, for further information. +% + +% 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 { + ( **** Error: File has an unbalanced >> \(close dictionary\).\n) + pdfformaterror + ( Output may be incorrect.\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, but this string +% is also used to search for %PDF-, which needs 1024 characters. +/pdfstring 1024 string 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 + ( **** Error: Invalid hex following '#' name escape, using literal '#' in name.\n) + pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + } { + exch closefile concatstrings + exch 2 1 index length 2 sub getinterval + } ifelse + (#) search { name#escape } if concatstrings +} bind def + +/num-chars-dict mark (0123456789-.) {dup} forall .dicttomark readonly 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 { + ( **** Error: Unknown operator: ') pdfformaterror + dup =string cvs pdfformaterror + (') 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, continue. + dup type /nametype eq { + //true 1 index .namestring { + //num-chars-dict exch known and + } forall { % perhaps, it's a malformed number. + PDFSTOPONERROR {//true}{ + pop pop pop 0 //false + ( looks like a malformed number, replacing with 0.) pdfformaterror + } ifelse + } { + //true % punt + } ifelse + } { + //true % punt + } ifelse + (\n) pdfformaterror + } 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 + ( Output may be incorrect.\n) pdfformaterror +} bind def + +currentdict /num-chars-dict .undef + +/.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 + } { + % Hackish fix to detect missing whitespace after "endobj". Yet another + % problem that (you guessed it!) Adobe Acrobat ignores silently + dup .namestring (endobj) anchorsearch { + ( **** Error: Missing whitespace after 'endobj'.\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + pop pop pop exch pop /endobj get exec + } { + %% First, lets try and see if this 'might' be a broken number + %% we look for 0-9 as well as '.' ',' and '-' to permit those + %% locales in which the separator is a comma, as well as negative + %% numbers. We've seena t least one tool replace '0' with '-' *sometimes* + true exch + dup length 1 sub 0 1 3 -1 roll { + 1 index exch get + dup 44 lt { + pop exch pop false exch exit + }{ + dup 57 gt { + pop exch pop false exch exit + }{ + dup 47 eq { + pop exch pop false exch exit + }{ + pop + } ifelse + } ifelse + } ifelse + } for + pop + { + %% If it looks like a number, try to deal with it as such + PDFSTOPONERROR { + dup .pdftokenerror + }{ + dup {.pdftokenerror} stopped + } ifelse + { pop + %% But if it fails, fall back to converting into a anem. + %% This will propagate through and cause different + %% fallback code to try and take care ot if. + cvlit exch pop exch pop + }{ + exch pop + } ifelse + }{ + %% Doesn't look like a number, convert it into a name + cvlit exch pop exch pop + } ifelse + } ifelse + } ifelse + } ifelse +} bind def +/PDFScanRules_true << /PDFScanRules //true >> def +/PDFScanRules_null << /PDFScanRules //null >> def +/.pdfrun { % <file> <opdict> .pdfrun - + % Construct a procedure with the stack depth, file and opdict + % bound into it. + 1 index cvlit % file <<>> file + count 2 sub % file <<>> file cnt + 3 1 roll mark % file cnt <<>> file [ + /PDFScanRules .getuserparam //null eq { + //PDFScanRules_true { setuserparams } 0 get % force PDF scanning mode + mark 7 4 roll + } { + mark 5 2 roll % file [ [ cnt <<>> file + } ifelse + { % Stack: ..operands.. count opdict file + { token } stopped { + dup type /filetype eq { pop } if + pop pop stop + } if { + 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 + } { + pop pop exit + } ifelse + } + aload pop .packtomark cvx % file [ {cnt <<>> file ... } + { loop } 0 get 2 packedarray cvx % file [ { {cnt <<>> file ... } loop } + PDFSTOPONERROR { {exec //false} } { {stopped} } ifelse + aload pop % file [ { {cnt <<>> file ... } loop } stopped + /PDFScanRules .getuserparam //null eq { + //PDFScanRules_null { setuserparams } 0 get % reset PDF scannig mode if it was off + } if + /PDFsource PDFsource % file [ { {cnt <<>> file ... } loop } stopped /PDFsource PDFsource + { store { + /StreamRunAborted //true store + ( **** Error reading a content stream. The page may be incomplete.\n) + pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + } if + } aload pop % file [ { {cnt <<>> file ... } loop } stopped /PDFsource PDFsource store {...} if + .packtomark cvx % file { { {cnt <<>> file ... } loop } stopped /PDFsource PDFsource store {...} if} + /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 + +% Read a token, but simply return false (no token read) in the case of an +% error. This is messy because 'token' either may or may not pop its operand +% if an error occurs, and because the return values are different depending +% on whether the source is a file or a string. To avoid closing the file +% check for '{' before trying 'token'. +/token_nofail_dict mark + ( ) { dup ( ) readstring pop pop } bind + (\t) 1 index + (\r) 1 index + (\n) 1 index + (\000) 1 index + ({) { //null //true exit } bind +.dicttomark def + +/token_nofail { % <file|string> token_nofail false + % <file> token_nofail <token> true + % <string> token_nofail <post> <token> true + dup type /filetype eq { + { dup ( ) .peekstring not { ({) } if + //token_nofail_dict exch .knownget not { + //null 1 index { token } .internalstopped exit + } if + exec + } loop + { % stack: source null [source] + //null ne { pop } if pop //false + } { % stack: source null ([post] token true | false) + { 3 1 roll pop pop //true } + { pop pop //false } + ifelse + } ifelse + } { + //null 1 index % stack: source null source + { token } .internalstopped { % stack: source null [source] + //null ne { pop } if pop //false + } { % stack: source null ([post] token true | false) + { 4 2 roll pop pop //true } + { pop pop //false } + ifelse + } ifelse + } ifelse +} bind def + +currentdict /token_nofail_dict .undef + +% ================================ Objects ================================ % + +% We keep track of PDF objects using the following PostScript variables: +% +% Generations (string): 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 (array): 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 (array): 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 (string): 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 0 array def + /Objects 0 array def + /Generations 0 string def + .currentglobal //true .setglobal + /GlobalObjects 20 dict def + .setglobal + /IsGlobal 0 string def +} bind def + +% Grow the tables to a specified size. +/growPDFobjects { % <minsize> growPDFobjects - + dup ObjectStream length gt { + dup ObjectStream exch array dup 3 1 roll copy pop /ObjectStream exch def + } if + dup Objects length gt { + dup Objects exch array dup 3 1 roll copy pop /Objects exch def + } if + dup Generations length gt { + dup Generations exch string dup 3 1 roll copy pop /Generations exch def + } if + dup IsGlobal length gt { + dup IsGlobal exch string dup 3 1 roll copy pop /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 get dup xcheck { % Check if executable + dup type /integertype eq { % Check if an integer + % Check whether the object is in GlobalObjects. + pop IsGlobal 1 index get 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 //== exec } if + GlobalObjects 1 index get dup Objects 4 1 roll put //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 + +/oforce_recursive { % <any> oforce_recursive <any> + dup type dup /arraytype eq exch /packedarraytype eq or { + dup rcheck { % protect tint transform functions, etc. + oforce % but dereference {1 0 R} + dup type dup /arraytype eq exch /packedarraytype eq or { + [ exch { oforce_recursive } forall ] + } { + dup type /dicttype eq { + << exch { oforce_recursive exch oforce exch } forall >> + } if + } ifelse + } if + } { + dup type /dicttype eq { + << exch { oforce_recursive exch oforce exch } forall >> + } if + } ifelse +} 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 + +% See /knownoget above. +/oknown { % <dict> <key> oknown <bool> + .knownget { oforce //null ne } { //false } ifelse +} bind def + +/knownogetdict { % <dict> <key> knownogetdict <dict> true + % <dict> <key> knownogetdict false + //knownoget exec dup { + 1 index type /dicttype ne { pop pop //false } if + } if +} 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 get 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 get 0 eq { % Check if object is free ... + ( **** Warning: reference to free object: ) + 2 index =string cvs concatstrings ( ) concatstrings % put obj # + exch =string cvs concatstrings ( R\n) concatstrings % put gen # + } { + ( **** Warning: wrong generation: ) + 2 index =string cvs concatstrings ( ) concatstrings % put obj # + exch =string cvs concatstrings % put gen # + (, xref gen#: ) concatstrings 1 index Generations % put xref gen # + exch get 1 sub =string cvs concatstrings (\n) concatstrings + } ifelse + pdfformatwarning % Output warning message + } { % Else QUIET ... + pop % Pop generation number + } ifelse + % We should return false for an incorrect generation number, however + % we are simply printing a warning and then returning true. This makes + % Ghostscript tolerant of of bad generation numbers. + //true + } ifelse +} bind def +/R { % <object#> <generation#> R <object> + %% Parameter validation; this should not be required but we have found files + %% (Bug 697351) which are corrupted in ways that mean we see a 'R' in a stream + %% even though it is not an indirect object reference. Because a stream + %% may reference objetcs which have not been resolved, we cannot simply + %% 'undef' teh R operator, the only solution is to have it chekc the + %% types of its operands. This is poterntially slow of course, we may + %% need to remove this in future if the performance penalty is too great. + %% We cannot slow down all files significantly purely to work-around + %% files which are invalid. + 1 index type /integertype eq 1 index type /integertype eq and { + /resolveR cvx 3 packedarray cvx + } { + ( **** Error: indirect object reference \(R\) encountered with invalid arguments.) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + } ifelse +} 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> + + %% If we have a stream wioth a broken endstream we can get here without + %% an object definition on nthe stack. If we simply pop the 'extraneous' + %% data we will break the stream definition and throw an error. So we look + %% to see if this appears to be this specific case. + 2 index type /marktype eq 2 index type /marktype eq or { + { + dup type /marktype eq { + exit + }{ + pop + }if + }loop + }{ + { + 2 index type /integertype ne 2 index type /integertype ne or { + count 3 gt { + ( **** Error: obj definition followed by multiple tokens, attempting to recover.\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + pop + } { + //null + ( **** Error: ignoring obj followed by multiple tokens.\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + exit + } ifelse + } { + exit + } ifelse + } loop + + 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 //== exec } if + GlobalObjects 1 index 3 index put + IsGlobal 1 index 1 put + } if + Objects exch 2 index put + } { + pop pop //null + } ifelse + }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 + /endobj { % bug 689795 + ( **** Error: Objects in an object stream should not have 'endobj'.\n) + pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + } 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 + 3 index 6 index % Get objectstream and start of first object + setfileposition % Move to the start of the data + % Read the data for all objects except the last. We do + % not know the size of the last object so we need to treat + % it as a special case. + 0 1 6 index 2 sub { + dup 4 index exch get % Get our current object number + % Stack: objstreamdict First N objectstream [obj#] [loc] + % bytestring loopindex object# + dup resolved? { % If we already have this object + pop pop % Remove object and object number + 1 add 2 index exch get % Get location of next object + 6 index add 6 index exch % Form location of next object and get stream + setfileposition % Move to the start of the next object data + } { % Else this is a new object ... + % We are going to create a string for reading the object + 2 index 0 % use our working string + % Determine the size of the object + 5 index 4 index 1 add get % Get location of the next object + 6 index 5 index get % Get location of this object + sub % Size of object = next loc - this loc + getinterval % Create string for reading object + 6 index exch readstring pop % Read object + /ReusableStreamDecode filter % Convert string into a stream + resolveobjstreamopdict .pdfrun % Get PDF object + Objects exch 2 index exch put % Put object into Objects array + pop pop % Remove object # and loop index + } ifelse + } for + pop pop % Remove our working string and loc array + % Now read the last object in the object stream. Since it + % is the last object, we can use the original stream and + % terminate when we hit the end of the stream + % Stack: objstreamdict First N objectstream [obj#] + 2 index 1 sub get % Get our current object number + dup resolved? not { % If we do not already have this object + exch % Get our object stream + resolveobjstreamopdict .pdfrun % Get PDF object + Objects exch 2 index exch put % Put object into Objects array + } if + pop pop pop pop % Clear stack +} bind def + +/no_debug_dict << + /PDFDEBUG //false +>> readonly def + +% Resolve all objects in an object stream +/resolveobjectstream { % <object stream #> resolveobjectstream - + PDFDEBUG { (%Resolving object stream: ) print } if + dup 0 resolveR % Get the objectstream dict, all objstrms use 0 as the gen # + dup /Type get /ObjStm ne { % Verify type is object stream + ( **** Error: Incorrect Type in object stream dictionary.\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + /resolveobjectstream cvx /typecheck signalerror + } if + dup /N get % Save number of objects onto the stack + 1 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 + 1 index array % Create array for holding object number + % Get the object numbers + 0 1 4 index 1 sub { % Loop and collect obj numbers + % Stack: strm# objstreamdict N PDFDEBUG objectstream [obj#] loopindex + 1 index 1 index % Setup to put obj# into object number array + 4 index token pop put % Get stream, then get obj# and put into array + 2 index token pop pop pop % Get stream, get obj loc and clear stack + } for + % Move to the start of the object data + 1 index 4 index /First get % Get objectstream and start of first object + setfileposition % Move to the start of the data + % We disable PDFDEBUG while reading the data stream. We will + % print the data later + PDFDEBUG { //no_debug_dict begin } if + % Read the data for all objects. We check to see if we get + % the number of objects that we expect. + % Stack: strm# objstreamdict N objectstream [obj#] PDFDEBUG + mark 3 -1 roll % Get objectstream + count 4 index add % Determine stack depth with objects + 3 1 roll + resolveobjstreamopdict .pdfrun % Get PDF objects + count counttomark 1 add index ne { % Check stack depth + ( **** Error: Incorrect object count in object stream.\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + /resolveobjectstream cvx /rangecheck signalerror + } if + % We have the object data + counttomark array astore % Put objects into an array + exch pop exch pop % Remove mark and count + currentdict //no_debug_dict eq { end } if % Restore debug context + % Save the objects into Objects + 0 1 2 index length 1 sub { % Loop through all objects + % Stack: strm# objstreamdict N [obj#] [objects] loopindex + dup 3 index exch get % Get our current object number + % Stack: strm# objstreamdict N [obj#] [objects] loopindex obj# + dup ObjectStream exch get 7 index eq { + dup resolved? { % If we already have this object + pop pop % Remove object and object number + } { % Else if we do not have this object + PDFDEBUG { (%Resolving compressed object: [) print dup =only ( 0]) = } if + Objects exch 3 index % Put the object into Objects + 3 index get + PDFDEBUG { dup === flush } if + put + } ifelse + } { + pop % Ignore old object; remove object number. + } ifelse + pop % Remove loop index + } for + pop pop pop pop pop % Remove strm# objstream, N, (obj#], and [objects] +} bind def + +currentdict /no_debug_dict undef + +% When resolving an object reference, we stop at the endobj or endstream. +/resolveopdict mark + valueopdict { } forall + /endstream { endobj exit } bind + /endobj { endobj exit } bind + /endjobj { % Bug 689876. + ( **** Error: Operator 'endobj' is misspelled as 'endjobj'.\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + endobj exit + } bind + /enbobj { % Bug 690397. + ( **** Error: Operator 'endobj' is misspelled as 'enbobj'.\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + endobj exit + } bind + % OmniForm generates PDF file with endobj missing in some + % objects. AR ignores this. So we have to do it too. + /obj { pop pop endobj exit } bind +.dicttomark readonly def + +/resolveR { % <object#> <generation#> resolveR <object> + 1 index 3 1 roll % copy the object for the code after pdf_run_resolve (may store it) + PDFDEBUG { + PDFSTEPcount 1 le { + (%Resolving: ) print 2 copy 2 array astore //== exec + } if + } if + 1 index dup 0 le exch NumObjects ge or { + ( **** Error: Considering object with an invalid number ) + 2 index 20 string cvs concatstrings + ( as null.\n) concatstrings pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + pop pop //null + } { + 1 index resolved? { % If object has already been resolved ... + exch pop exch pop % then clear stack and return object + } { % Else if not resolved ... + PDFfile fileposition 3 1 roll % Save current file position + 1 index Objects exch get % Get location of object from xref + 3 1 roll checkgeneration { % Verify the generation number + % Stack: savepos objpos obj# + ObjectStream 1 index get dup 0 eq { % Check if obj in not an objstream + pop exch PDFoffset add PDFfile exch setfileposition + PDFfile token pop 2 copy ne + { ( **** Error: Unrecoverable error in xref!\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + /resolveR cvx /rangecheck signalerror + } + if pop PDFfile token pop + PDFfile token pop /obj ne + { ( **** Error: Unrecoverable error in xref!\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + /resolveR cvx /rangecheck signalerror + } + if + pdf_run_resolve % PDFfile resolveopdict .pdfrun + } { % Else the object is in an ObjectStream + % Process an objectstream object. We are going to resolve all + % of the objects in sthe stream and place them into the Objects + % array. + % Stack: savepos objpos obj# objectstream# + resolveobjectstream + resolved? { % If object has already been resolved ... + exch pop % Remove object pos from stack. + } { + pop pop //null % Pop objpos and obj#, put null for object + } ifelse + } ifelse + } { % Else the generation number is wrong + % Don't cache if the generation # is wrong. + pop pop //null % Pop objpos and obj#, put null for object + } ifelse % ifelse generation number is correct + exch PDFfile exch setfileposition % Return to original file position + } ifelse + } ifelse + + dup type /dicttype eq % For dictionaries only + { + dup /Type .knownget % make sure the dictionary has a type + { + dup /Font eq exch /FontDescriptor eq or % and ensure its a Font or FontDescriptor + {dup 3 -1 roll /.gs.pdfobj# exch put} + {exch pop} + ifelse + } + {exch pop} + ifelse + } + {exch pop} + ifelse +} bind def + +% ================================ Streams ================================ % + +% We represent a stream by an executable dictionary that contains, +% in addition to the contents of the original stream dictionary: +% /File - the file or string where the stream contents are stored, +% if the stream is not an external one. +% /FilePosition - iff File is a file, the position in the file +% where the contents start. +% /StreamKey - the key used to decrypt this stream, if any. +% We do the real work of constructing the data stream only when the +% contents are needed. + +% Construct a stream. The length is not reliable in the face of +% different end-of-line conventions, but it's all we've got. +% +% PDF files are inconsistent about what may fall between the 'stream' keyword +% and the actual stream data, and it appears that no one algorithm can +% detect this reliably. We used to try to guess whether the file included +% extraneous \r and/or \n characters, but we no longer attempt to do so, +% especially since the PDF 1.2 specification states flatly that the only +% legal terminators following the 'stream' keyword are \n or \r\n, both of +% which are properly skipped and discarded by the token operator. +% Unfortunately, this doesn't account for other whitespace characters that +% may have preceded the EOL, such as spaces or tabs. Thus we back up one +% character and scan until we find the \n terminator. +/stream { % <dict> stream <modified_dict> + dup /Length knownoget { + 0 eq { + dup /Filter undef % don't confuse any filters that require data + } if + } if + dup /F known dup PDFsource PDFfile eq or { + not { + dup /File PDFfile put + % make sure that we are just past the EOL \n character + PDFfile dup fileposition 1 sub setfileposition % back up one + % Skip spaces till \n or a non-space character is found. + //false + { + PDFfile read pop + dup 32 eq { pop //true or } { exit } ifelse + } loop + dup 13 eq { + pop + PDFfile read pop + } if + 10 ne { + PDFfile dup fileposition 1 sub setfileposition + //true or + } if { + ( **** Error: stream operator isn't terminated by valid EOL.\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + } if + + dup /FilePosition PDFfile fileposition put + PDFDEBUG { + PDFSTEPcount 1 le { + (%FilePosition: ) print dup /FilePosition get //== exec + } if + } if + } if + % Some (bad) PDf files have invalid stream lengths. This causes problems + % if we reposition beyond the end of the file. So we compare the given + % length to number of bytes left in the file. + dup /Length knownoget { + dup PDFfile bytesavailable lt { % compare to to bytes left in file + PDFfile fileposition % reposition to the end of stream + add PDFfile exch setfileposition + } { + pop % bad stream length - do not reposition. + % This will force a length warning below + } ifelse + } if + } { + pop + % We're already reading from a stream, which we can't reposition. + % Capture the sub-stream contents in a string. + dup /Length oget string PDFsource exch readstring + not { + ( **** Error: Unexpected EOF in stream!\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + /stream cvx /rangecheck signalerror + } if + 1 index exch /File exch put + } ifelse + PDFsource token_nofail not { //null } if + dup /endobj eq { + % Another case that Acrobat Reader handles -- 'endobj' without 'endstream'. + ( **** Error: stream missing 'endstream'.\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + pop /endstream % fake a valid endstream + } if + /endstream ne { + ( **** Error: stream Length incorrect.\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + dup /Length undef % prevent the use of the incorrect length. + + %% Bug #696560 If we get a stream whose length is incorrect we cannot + %% safely *assume* it has an object definition as we can get here when + %% reading an XRef stream, and in that case we will not have the object + %% and generation numbers on the stack! + %% So before we call endobj and exit pdfrun, try and see if ths looks like + %% a regular object or not. If it isn't then don't try and ahndle it as + %% one, just return the object. + count 3 ge { + 1 index type /integertype eq 2 index type /integertype eq and { + cvx endobj exit % exit from .pdfrun now. + } if + } if + } { + PDFsource (??) .peekstring pop (>>) eq { % Bug 690161, sample #1 + ( **** Error: Spurious '>>' after 'endstream' ignored.\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + PDFsource (12) readstring pop pop + } if + } ifelse + cvx +} bind def + +/endstream { + exit +} bind def + +% Contrary to the published PDF (1.3) specification, Acrobat Reader +% accepts abbreviated filter names everywhere, not just for in-line images, +% and some applications (notably htmldoc) rely on this. +/unabbrevfilterdict mark + /AHx /ASCIIHexDecode /A85 /ASCII85Decode /CCF /CCITTFaxDecode + /DCT /DCTDecode /Fl /FlateDecode /LZW /LZWDecode /RL /RunLengthDecode +.dicttomark readonly def + +% Extract and apply filters. +/filterparms { % <dict> <DPkey> <Fkey> filterparms + % <dict> <parms> <filternames> + 2 index exch knownoget { + oforce_recursive + exch 2 index exch knownoget { + dup length 0 eq { + pop //false + } { + //true + } ifelse + } { + //false + } ifelse { + % Both filters and parameters. + oforce_recursive + exch dup type /nametype eq { + 1 array astore exch + dup type /arraytype ne { 1 array astore } if exch + } if + } { + % Filters, but no parameters. + //null exch + dup type /nametype eq { 1 array astore } if + } ifelse + } { + % No filters: ignore parameters, if any. + pop //null { } + } ifelse +} bind def +/filtername { % <filtername> filtername <filtername'> + //unabbrevfilterdict 1 index .knownget { exch pop } if + dup /Filter resourcestatus { pop pop } { + Repaired exch % this error is not the creator's fault + RepiredAnError exch + ( **** ERROR: Unable to process ) pdfformaterror + 64 string cvs pdfformaterror + ( data. Page will be missing data.\n) pdfformaterror + /RepairedAnError exch store + /Repaired exch store % restore the previous "Repaired" state + % provide a filter that returns EOF (no data) + /.EOFDecode + } ifelse +} bind def + +/pdf_rules_dict << /PDFRules //true >> readonly def + +% Add PDF option to ASCII85Decode filter +% <source> <name> add_A85_param <source> <dict'> <name> +% <source> <dict> <name> add_A85_param <source> <dict'> <name> +/add_A85_param { + dup /ASCII85Decode eq { + 1 index type /dicttype eq { + exch dup length 1 add dict copy + dup /PDFRules //true put + exch + } { + //pdf_rules_dict exch + } ifelse + } if +} bind def + +currentdict /pdf_rules_dict undef + +/applyfilters { % <parms> <source> <filternames> applyfilters <stream> + 2 index //null eq + { + { filtername add_A85_param filter } + } + { + dup length 3 index length ne %% compare lengths of DecodeParmas and Filter arrays + { %% if they aren't the same, ignore the decode params + ( **** Error: ignoring stream /DecodeParams array as its length is different to the Filters array.\n) pdfformaterror + ( Output may be incorrect.\n) pdfformaterror + 3 -1 roll pop //null 3 1 roll + { filtername add_A85_param filter } + } + { + { % Stack: parms source filtername + 2 index 0 oget dup type /dicttype ne + { pop } + { + exch filtername dup /JBIG2Decode eq { exch jbig2cachectx exch } if + } ifelse + add_A85_param filter + exch dup length 1 sub 1 exch getinterval exch + } + } ifelse + } ifelse + forall exch pop +} bind def + +% JBIG2 streams have an optional 'globals' stream obj for +% sharing redundant data between page images. Here we resolve +% that stream reference (if any) and run it through the decoder, +% creating a special -jbig2globalctx- postscript object our +% JBIG2Decode filter implementation looks for in the parm dict. +/jbig2cachectx { % <parmdict> jbig2cachectx <parmdict> + dup /JBIG2Globals knownoget { + % make global ctx + PDFfile fileposition exch % resolvestream is not reentrant + //true resolvestream % stack after: PDFfileposition -file- + % Read the data in a loop until EOF to so we can move the strings into a bytestring + [ { counttomark 1 add index 60000 string readstring not { exit } if } loop ] + exch pop 0 1 index { length add } forall % compute the total length + % now copy the data from the array of strings into a bytestring + .bytestring exch 0 exch { 3 copy putinterval length add } forall pop + .jbig2makeglobalctx + PDFfile 3 -1 roll setfileposition + 1 index exch + /.jbig2globalctx exch put + } if +} bind def + +% Resolve a stream dictionary to a PostScript stream. +% Streams with no filters require special handling: +% - Whether we are going to interpret the stream, or If we are just +% going to read data from them, we impose a SubFileDecode filter +% that reads just the requisite amount of data. +% Note that, in general, resolving a stream repositions PDFfile. +% Clients must save and restore the position of PDFfile themselves. +/resolvestream { % <streamdict> <readdata?> resolvestream <stream> + 1 index /F knownoget { + % This stream is stored on an external file. + (r) file 3 -1 roll + /FDecodeParms /FFilter filterparms + % Stack: readdata? file dict parms filternames + 4 -1 roll exch + pdf_decrypt_stream + applyfilters + } { + exch + dup /Length knownoget { 0 eq } { //false } ifelse { + () 0 () /SubFileDecode filter + } { + dup /FilePosition .knownget { + 1 index /File get exch setfileposition + } if + % Stack: readdata? dict + /DecodeParms /Filter filterparms + % Stack: readdata? dict parms filternames + 2 index /File .knownget not { + ( **** Error: Stream object is missing the stream data.\n) + ( Output may be incorrect.\n) pdfformaterror + pdfformaterror + () + } if exch + % Stack: readdata? dict parms file/string filternames + dup length 0 eq { + % All the PDF filters have EOD markers, but in this case + % there is no specified filter. + exch dup type /filetype eq 5 index or { + % Use length for any files or reading data from any source. + 3 index /Length knownoget not { 0 } if + } { + 0 % Otherwise length of 0 for whole string + } ifelse + 4 index /IDFlag known { pop } { () /SubFileDecode filter } ifelse + exch + pdf_decrypt_stream % add decryption if needed + pop exch pop + } { + % Stack: readdata? dict parms source filternames + exch 3 index /Length knownoget { + () /SubFileDecode filter + } if exch + pdf_decrypt_stream % add decryption if needed + applyfilters + } ifelse + } ifelse + } ifelse + % Stack: readdata? dict file + exch pop exch pop +} bind def + +% ============================ Name/number trees ============================ % + +/nameoget { % <nametree> <key> nameoget <obj|null> + exch /Names exch .treeget +} bind def + +/numoget { % <numtree> <key> numoget <obj|null> + exch /Nums exch .treeget +} bind def + +/.treeget { % <key> <leafkey> <tree> .treeget <obj|null> + dup /Kids knownoget { + exch pop .branchget + } { + exch oget .leafget + } ifelse +} bind def + +/.branchget { % <key> <leafkey> <kids> .branchget <obj|null> + dup length 0 eq { + pop pop pop //null + } { + dup length -1 bitshift 2 copy oget + % Stack: key leafkey kids mid kids[mid] + dup /Limits oget aload pop + % Stack: key leafkey kids mid kids[mid] min max + 6 index lt { + pop pop + 1 add 1 index length 1 index sub getinterval .branchget + } { + 5 index gt { + pop + 0 exch getinterval .branchget + } { + exch pop exch pop .treeget + } ifelse + } ifelse + } ifelse +} bind def + +/.leafget { % <key> <pairs> .leafget <obj|null> + dup length 2 eq { + dup 0 get 2 index eq { 1 oget } { pop null } ifelse + exch pop + } { + dup length -1 bitshift -2 and 2 copy oget + % Stack: key pairs mid pairs[mid] + 3 index gt { 0 exch } { 1 index length 1 index sub } ifelse + getinterval .leafget + } ifelse +} bind def + +end % pdfdict +.setglobal |