PDF 1.5 parser Changes.
"Dan Coby" <[email protected]>
| Newsgroups | gmane.comp.printing.ghostscript.patches |
|---|---|
| Message-ID | <[email protected]> |
Log message:
This is the first pass at the changes to support the PDF 1.5 revisions
to the PDF format.
DETAILS:
The PDF 1.5 revision to the PDF specification contains two major
additions to the format of a PDF document. Objects can be encoded
in compressed data streams called and object streams. Xref tables
were also changed to allow the identification of objects in
object streams and also to allow the compression of xref tables.
Together these changes reduced the size of the PDF 1.5 specification
from 13.5 MB (PDF 1.4 version) to 8.7 MB (PDF 1.5 version).
pdf_main.ps and pdf_rbld.ps - The changes in these modules were for
the support of compressed XRef tables. The major part of these
changes consist of the refactoring of readxref into pieces. One
piece identifies the old format versus compressed XRef tables.
A second piece supports the reading of old format XRef tables.
The procedure readpdf15xref was added to read compressed XRef
tables. A new larray called ObjectStream was added for
identifying the location of objects which are inside a compressed
object stream. The changes for the addition of ObjectStream
also affect pdf_base.ps.
pdf_base.ps - The procedures resolveR and resolved? had to be extended
to support the presence of the objectstream type. A new procedure
called resolveobjectstream was added for resolving objects inside
of object streams.
The code listing below has two versions of the resolveobjectstream
procedure. I want to keep a record of both versions of this
procedure in CVS. The first version is theoretically more robust
than the second. It unpacks and processes objects from an object
stream one at a time. To insure that this occurs. It creates a
string of the exact size of an object and then unpacks and processes
the object. However it is also very slow. Processing of a test
document (PDF 1.5 spec.) with this routine is 4 times slower
than processing a version of the PDF 1.5 spec. which does not use
object streams. The second version of the procedure unpacks the
entire object stream and then processes each object. Thus any
problem which occurs with one object could, in theory, affect another
object. The second version is much faster. There is about a 4%
penalty for processing the compressed version of the PDF 1.5 spec.
After the initial check in, the first version of resolveobjecstream
will be removed unless some need is found for its robustness.
Dan
Index: lib/pdf_base.ps
===================================================================
RCS file: /cvs/ghostscript/gs/lib/pdf_base.ps,v
retrieving revision 1.39
diff -d -u -r1.39 pdf_base.ps
--- lib/pdf_base.ps 26 Oct 2003 00:52:28 -0000 1.39
+++ lib/pdf_base.ps 10 Dec 2003 00:02:01 -0000
@@ -254,8 +254,15 @@
%
% 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 entry in the cross-reference
-% table.
+% 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]
@@ -268,6 +275,7 @@
% Initialize the PDF object tables.
/initPDFobjects { % - initPDFobjects -
+ /ObjectStream larray def
/Objects larray def
/Generations lstring def
.currentglobal true .setglobal
@@ -278,6 +286,9 @@
% 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
@@ -300,21 +311,21 @@
% whether an object has been resolved.
/resolved? { % <object#> resolved? <value> true
% <object#> resolved? false
- Objects 1 index lget dup xcheck {
- dup type /integertype eq {
+ 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 {
- pop false
- } {
+ 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
- } {
- exch pop true
+ } { % Else object is executable but not integer
+ exch pop true % Therefore must be executable dict. (stream)
} ifelse
- } {
- exch pop true
+ } { % Else object is not executable.
+ exch pop true % Therefore it must have been resolved.
} ifelse
} bind def
/oforce /exec load def
@@ -350,22 +361,25 @@
.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 {
- pop true
- } {
- QUIET not {
- Generations 2 index lget 0 eq {
+ 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
- exch =string cvs concatstrings ( R\n) concatstrings
- pdfformaterror
- } {
- pop
- } ifelse false
+ 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>
@@ -404,6 +418,152 @@
} 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
+ 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
+ (yyy) = pstack (yyy) = flush xxx
+ 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 lput % 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 lput % Put object into Objects array
+ } if
+ pop pop pop pop % Clear stack
+} bind def
+
+% 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 /Type get /ObjStm ne { % Verify type is object stream
+ ( **** Incorrect Type in object stream dictionary.\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: 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 /PDFDEBUG false def % Save PDFDEBUG and disable it while reading
+ % Read the data for all objects. We check to see if we get
+ % the number of objects that we expect.
+ % Stack: objstreamdict N objectstream [obj#] PDFDEBUG
+ mark 4 -1 roll % Get objectstream
+ count 5 index add % Determine stack depth with objects
+ /PDFObjectStkCount exch def
+ resolveobjstreamopdict .pdfrun % Get PDF objects
+ PDFObjectStkCount count ne { % Check stack depth
+ ( **** Incorrect object count in object stream.\n) pdfformaterror
+ /resolveobjectstream cvx /rangecheck signalerror
+ } if
+ % We have the object data
+ counttomark array astore % Put objects into an array
+ exch pop % Remove mark
+ exch /PDFDEBUG exch def % Restore PDFDEBUG flag
+ % Save the objects into Objects
+ 0 1 2 index length 1 sub { % Loop through all objects
+ % Stack: objstreamdict N [obj#] [objects] loopindex
+ dup 3 index exch get % Get our current object number
+ 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
+ lput
+ } ifelse
+ pop % Remove loop index
+ } for
+ pop pop pop pop % Remove objstream, N, (obj#], and [objects]
+} bind def
+
% When resolving an object reference, we stop at the endobj.
/resolveopdict mark
valueopdict { } forall
@@ -415,14 +575,15 @@
/resolveR { % <object#> <generation#> resolveR <object>
PDFDEBUG { (%Resolving: ) print 2 copy 2 array astore == } if
- 1 index resolved? {
- exch pop exch pop
- } {
- PDFfile fileposition 3 1 roll
- 1 index Objects exch lget
- 3 1 roll checkgeneration {
+ 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 lget % Get location of object from xref
+ 3 1 roll checkgeneration { % Verify the genration number
% Stack: savepos objpos obj#
- exch PDFoffset add PDFfile exch setfileposition
+ ObjectStream 1 index lget dup 0 eq { % Check if obj in not an
objstream
+ pop exch PDFoffset add PDFfile exch setfileposition
PDFfile token pop 2 copy ne
{ ( **** Unrecoverable error in xref!\n) pdfformaterror
/resolveR cvx /rangecheck signalerror
@@ -434,11 +595,23 @@
}
if
pdf_run_resolve % PDFfile resolveopdict .pdfrun
- }
- { % Don't cache if the generation # is wrong.
- pop pop null
- } ifelse
- exch PDFfile exch setfileposition
+ } { % 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
} bind def
Index: lib/pdf_main.ps
===================================================================
RCS file: /cvs/ghostscript/gs/lib/pdf_main.ps,v
retrieving revision 1.81
diff -d -u -r1.81 pdf_main.ps
--- lib/pdf_main.ps 1 Nov 2003 04:43:37 -0000 1.81
+++ lib/pdf_main.ps 10 Dec 2003 00:02:01 -0000
@@ -211,25 +211,13 @@
} bind def
/linene { lineeq not } bind def
-% Read the cross-reference table.
-/readxref % <pos> readxref <trailerdict>
+ % Read original version (pre PDF 1.5) of the xref table.
+ % Note: The position is the location of 'xref'. The current PDFfile
+ % position is just after the 'XREF'.
+/readorigxref % <pos> readorigxref <trailerdict>
{
- PDFoffset add PDFfile exch setfileposition
- % In some PDF files, this position actually points to
- % white space before the xref line. Skip over this here.
- { PDFfile fileposition PDFfile read pop 32 gt { exit } if pop
- } loop
- PDFfile exch setfileposition
- 0 % Initialize xref table error counter
-
- % The PDF specification says that the 'xref' must be on a line
- % by itself. The code here formerly used readline and linene to
- % check this. However, Acrobat Reader only requires the line to
- % begin with 'xref', and there are enough applications producing
- % non-compliant PDF files that we have to do this too.
- PDFfile pdfstring 0 4 getinterval readstring pop
- (xref) ne { /readxref cvx /syntaxerror signalerror } if
- % Store the xref table entry position for each object.
+ pop % We do not need the position.
+ 0 % Initialize xref table error counter
{ PDFfile token pop % first object # or trailer
dup /trailer eq { pop exit } if
PDFfile pdfstring readline pop
@@ -251,7 +239,9 @@
dup /n eq { % xref line tag is /n
pop % pop dup of line tag
Objects 3 index lget null eq { % later update might have set it
- setxrefentry
+ 0 3 1 roll % Set ObjectStream object number = 0
+ setxrefentry % Save xref entry
+ 3 -1 roll pop % Remove ObjectStream object onumber
} if
}
{ % xref line tag was not /n
@@ -278,6 +268,139 @@
PDFfile traileropdict .pdfrun
} bind def
+ % This dicitonary is used to read the xref dictionary. It should work for
+ % reading any dictionary. dictlevelcount must contain 0.
+/xrefopdict mark
+ (<<) cvn { /dictlevelcount dictlevelcount 1 add def mark } bind
+ (>>) cvn { .dicttomark /dictlevelcount dictlevelcount 1 sub def
+ dictlevelcount 0 eq { exit} if } bind
+ ([) cvn { mark } bind % ditto
+ (]) cvn dup load
+% /true true % see .pdfexectoken in pdf_base.ps
+% /false false % ibid.
+% /null null % ibid.
+ /R { /resolveR cvx 3 packedarray cvx } bind % see Objects below
+.dicttomark readonly def
+
+% Get a variable length positive integer value from a stream. A value
+% of zero is returned if the count is zero.
+/getintn { % <stream> <count> getintn int
+ 0 exch { 256 mul 1 index read pop add } repeat
+ exch pop % Discard stream
+} bind def
+
+% This array contains handlers for processing the different types of
+% entries in the XRef stream.
+% Stack: <Xrefdict> <xref stream> <Index array> <pair loc> <obj num>
+% <field 2> <field 3>
+% The handlers leave the stack unchanged.
+/xref15entryhandlers [
+ { % XRef entry type 0 - free or f type xref entry
+% (free ) print
+% (obj num: ) print 2 index pdfstring cvs print ( ) print
+% (loc: ) print 1 index pdfstring cvs print ( ) print
+% (gen: ) print dup === flush
+ } bind % Do nothing for free xref entries
+ % XRef entry type 1 - normal or n type xref entry
+ { % field 2 = obj loc, field 3 = gen num
+% (normal ) print
+% (obj num: ) print 2 index pdfstring cvs print ( ) print
+% (loc: ) print 1 index pdfstring cvs print ( ) print
+% (gen: ) print dup === flush
+ 0 3 1 roll % set stream number = 0
+ setxrefentry
+ 3 -1 roll pop % remove stream number
+ } bind
+ % XRef entry type 2 - compressed object type xref entry
+ { % field 2 = object stream num, field 3 = index into object stream
+% (Compressed objects: ) print
+% (obj num: ) print 2 index pdfstring cvs print ( ) print
+% (field 2: ) print 1 index pdfstring cvs print ( ) print
+% (field 3: ) print dup === flush
+ 0 setxrefentry pop % set generation number = 0
+ } bind
+] def
+
+ % Read the PDF 1.5 version of the xref table.
+ % Note: The position is the location of the start of the dictionary
object
+ % In PDF 1.5, the XRef dictionary also serves as the trailer dictionary
+/readpdf15xref % <pos> readpdf15xref <trailerdict>
+ {
+ PDFfile exch setfileposition % move to start of object
+ % Get object number, revision, and 'obj' and discard
+ PDFfile token pop pop
+ PDFfile token pop pop
+ PDFfile token pop pop
+ % Get the XRef dicitionary
+ /dictlevelcount 0 def PDFfile xrefopdict .pdfrun
+ % Verify that we have an XRef dictionary
+ dup /Type get /XRef ne {
+ /readpdf15xref cvx /syntaxerror signalerror
+ } if
+ % Ensure that we we have room in the objects array, etc.
+ dup /Size get growPDFobjects
+ % Create a stream for the XRef data
+ PDFfile token pop pop % Skip over 'stream'
+ dup stream false resolvestream
+ % Stack: <XRefdict> <xref stream>
+ % The Index array defines the ranges of object numbers in the
+ % XRef stream. Each value pair is consists of starting object
+ % number and the count of consecutive objects.
+ % Get the Index array, if present
+ 1 index /Index .knownget not { % If no Index array ...
+ [ 0 3 index /Size get ] % Default = [ 0 Size ]
+ } if
+ % Loop through the Index array
+ 0 2 2 index length 1 sub {
+ % Get start and end of object range
+ 2 copy get % Start of the range
+ dup 3 index 3 index 1 add get % Number of entries in range
+ % Loop through the range of object numbers
+ add 1 sub 1 exch { % Form end of range, set increment = 1
+ % Stack: <Xrefdict> <xref stream> <Index array> <pair loc> <obj num>
+ % Get xref parameters. Note: The number of bytes for each parameter
+ % is defined by the entries in the W array.
+ 4 index /W get aload pop % Get W array values
+ % The first field indicates type of entry. Get first field value.
+ % If the num. of bytes for field 1 is 0 then default field value is 1
+ 3 -1 roll dup 0 eq { pop 1 } { 6 index exch getintn } ifelse
+ % Get the handler for the xref entry type. We will execute the
+ % handler after we get the other two field values.
+ xref15entryhandlers exch get
+ 3 -1 roll 6 index exch getintn % Get second field
+ 3 -1 roll 6 index exch getintn % Get third field
+ 3 -1 roll exec % Execute Xref entry handler
+ pop pop pop % Remove field values and obj num
+ } for % Loop through Xref entries
+ pop % Remove Index array pair loc
+ } for % Loop through Index array entries
+ pop pop % Remove Index array and xref stream
+ } bind def
+
+% Read the cross-reference table.
+% <pos> is the position either from the startxref statement or the /Prev
+% entry in the prior trailer dictionary.
+/readxref % <pos> readxref <trailerdict>
+ {
+ PDFoffset add PDFfile exch setfileposition
+ % In some PDF files, this position actually points to
+ % white space before the xref line. Skip over this here.
+ { PDFfile fileposition PDFfile read pop 32 gt { exit } if pop
+ } loop
+ dup % Make copy of the file position (before last char was read).
+ PDFfile exch setfileposition
+ % The PDF specification says that the 'xref' must be on a line
+ % by itself. The code here formerly used readline and linene to
+ % check this. However, Acrobat Reader only requires the line to
+ % begin with 'xref', and there are enough applications producing
+ % non-compliant PDF files that we have to do this too.
+ PDFfile pdfstring 0 4 getinterval readstring pop
+ (xref) eq
+ { readorigxref } % 'xref' -> original xref table
+ { readpdf15xref } % otherwise assume PDF 1.5 xref stream
+ ifelse
+ } bind def
+
% Open a PDF file and read the header, trailer, and cross-reference.
/pdfopen { % <file> pdfopen <dict>
% Color space substitution in PDF is handled somewhat differently
@@ -344,28 +467,22 @@
length /PDFoffset exch def pop cvr /PDFversion exch def
% Read the last cross-reference table.
count /pdfemptycount exch def
+ /Trailer << >> def % Initialize to an emptry dict.
{ initPDFobjects findxref readxref } .internalstopped {
- % Read failed. Attempt to recover xref data.
- print_xref_warning
- count pdfemptycount sub { pop } repeat % remove anything left by
readxref
- search_objects
- search_trailer
- } { /Trailer exch def
+ recover_xref_data % Read failed. Attempt to recover xref data.
+ search_trailer % Search for the primary trailer
+ } { /Trailer exch def % Save trailer dict after first xref table
% Read any previous cross-reference tables.
Trailer
{ /Prev knownoget not { exit } if
{ readxref } .internalstopped {
- % Read failed. Attempt to recover xref data.
- print_xref_warning
- count pdfemptycount sub { pop } repeat % remove anything left by
readxref
- search_objects
- search_trailer
- exit
- } if
- } loop
- } ifelse
- Trailer /Encrypt knownoget {
- pop + recover_xref_data % Read failed. Attempt to recover
xref data.
+ exit % Exit loop since recover gets all obj data.
+ } if % If readxref stopped
+ } loop % Loop to previous trailer
+ } ifelse % Ifelse readxref stopped
+ Trailer /Encrypt known {
+ pop
pdf_process_Encrypt % signal error
} if
currentdict end
Index: lib/pdf_rbld.ps
===================================================================
RCS file: /cvs/ghostscript/gs/lib/pdf_rbld.ps,v
retrieving revision 1.4
diff -d -u -r1.4 pdf_rbld.ps
--- lib/pdf_rbld.ps 18 Aug 2003 20:43:05 -0000 1.4
+++ lib/pdf_rbld.ps 10 Dec 2003 00:02:02 -0000
@@ -31,10 +31,11 @@
% Note: This routine is also used by non-rebuild code.
% Store a line in the xref array (Actually Objects and Generations arrays)
-% <obj num> <obj loc> <gen num> setxrefentry <obj num> <obj loc> <gen num>
+% <obj num> (strm num> <obj loc> <gen num> setxrefentry <obj num> strm
num>
+% <obj loc> <gen num>
/setxrefentry
{ % We store generation numbers as value + 1
- % We reserve 0 to indicate an unknown generation (or xref entry)
+ % We reserve 0 to indicate an free xref entry
1 add % increment generation number
% To save space, generations numbers are stored in a lstring unless we
% find a generation number greater than 255. If so then transfer to
@@ -51,9 +52,10 @@
} if
% Verify that the new values are for a new object. If the current
% entry is null then we have a new entry.
- Objects 3 index lget null eq {
- Objects 3 index 3 index cvx lput % Save object location
- Generations 3 index 2 index lput % Save geenration number
+ Objects 4 index lget null eq {
+ ObjectStream 4 index 4 index cvx lput % Save ObjectStream object number
+ Objects 4 index 3 index cvx lput % Save object location
+ Generations 4 index 2 index lput % Save geenration number
} {
% Verify that the new entry has at least as high a generaton number
% We accept equal entry number because we have found PDF files in
@@ -61,12 +63,13 @@
% numbers. The normal xref logic only accepts the first such
% entry that it finds. However the 'rebuild PDF' logic can find
% both such entries. The correct one is usually the last one.
- Generations 3 index lget 1 index le {
- Objects 3 index 3 index cvx lput % Save object location
- Generations 3 index 2 index lput % Save geenration number
+ Generations 4 index lget 1 index le {
+ ObjectStream 4 index 4 index cvx lput % Save ObjectStream object
number
+ Objects 4 index 3 index cvx lput % Save object location
+ Generations 4 index 2 index lput % Save geenration number
} if
% Set error flag if we have equal object and generation numbers
- Generations 3 index lget 1 index eq { /dup_obj_gen_num true def } if
+ Generations 4 index lget 1 index eq { /dup_obj_gen_num true def } if
} ifelse
} bind def
@@ -80,6 +83,8 @@
( ) print
dup Generations exch lget 1 sub =only % print Generation number
( ) print
+ ObjectStream exch lget === % print ObjectStream object number
+ ( ) print
Objects exch lget === % print object location
} for
} bind def
@@ -259,10 +264,10 @@
% of 20 each time we increase the size.
1 index 20 add 20 idiv 20 mul
growPDFobjects
- % save xref parameters into Objects and Generations
- 1 index 3 index 2 index % rearrange parms for setxrefentry
+ % save xref parameters into ObjectStream, Objects and Generations
+ 1 index 0 4 index 3 index % rearrange parms for setxrefentry
setxrefentry % save parameters
- pop pop pop % clear parameters
+ pop pop pop pop % clear parameters
} if % check if name is 'obj'
} if % check if we got 'obj" string
pop % remove ref number
@@ -288,10 +293,10 @@
} if
} bind def
-% Print xref warning message
+% Print warning message because we found a problem while reading the xref
+% tables
/print_xref_warning
-{
- ( **** Warning: An error occurred while reading an XREF table.\n)
+{ ( **** Warning: An error occurred while reading an XREF table.\n)
pdfformaterror
( **** The file has been damaged. This may have been caused\n)
pdfformaterror
@@ -301,3 +306,11 @@
pdfformaterror
} bind def
+% Attempt to recover the XRef data. This is called if we have a failure
+% while reading the normal XRef tables. This routine usually works
+% only for pre PDF1.5 versions of PDF files.
+/recover_xref_data % - recover_xref_data -
+{ print_xref_warning % Print warning message
+ count pdfemptycount sub { pop } repeat % remove anything left by readxref
+ search_objects % Search for objects
+} bind def