[gs-commits] ghostpdl branch, master, updated. jbig2dec-0.14-1684-gd988989

[email protected] (Ken Sharp)
Newsgroups gmane.comp.printing.ghostscript.cvs
Message-ID <[email protected]>
The ghostpdl branch, master has been updated
       via  d9889892feaac3ca09f85d9bbdc001b1aec50cc3 (commit)
      from  107aae1f353db3b62f8b714c5100ec7bc180f2fb (commit)

----------------------------------------------------------------------
commit d9889892feaac3ca09f85d9bbdc001b1aec50cc3
Author: Ken Sharp <[email protected]>
Date:   Wed Sep 18 17:18:29 2019 +0100

    PDF interpreter - handle ObjStm containing more objects than declared
    
    There's no bug number for this one, I can't easily create a reduced file
    without inadvertently fixing the problem, and I don't want to put the
    customer file into Bugzilla.
    
    The problem is that the file contains an ObjStm (a compressed stream
    containing a sequence of PDF objects) which contains more objects than
    the stream declares. Not only that, but the extra object is neither the
    first nor the last object, so its not trivial to ignore.
    
    Most consumers handle ObjStm constructs by using the 'catalog' at the
    head of the stream to find the offset of each object in the stream and
    then reposition to that object and read a single PDF object (note that
    objects in an ObjStm do not begin with 'obj' and do not end with
    'endobj') which is IMO putting a lot of faith in the consumer's
    ability to detect a single object.
    
    For performance reasons Ghostscript's PDF interpreter reads *all* the
    objects in the ObjStm immediately and stores them, thus trading memory
    use for performance (we only need to read and decompress the objects
    once). This approach is totally defeated when there are more objects
    than expected, because we don't know which object relates to which
    object number (no 'x y obj' in an ObjStm).
    
    In this commit we spot the discrepancy; if there are too few objects we
    exit immediately with an error (technically we could do something about
    this too, provided the missing object is never used). If there are extra
    objects then we move to a fallback.
    
    We discard all the objects we've read, then use the offsets in the
    'catalog' to calculate the position and length of each object in the
    ObjStm. We position the file pointer to the start of the object and
    use a SubFileDecode filter to limit the number of bytes read to the
    difference between the offsets. We then use the existing code to read
    all the objects in that stream. If we get more than one then we discard
    all but the first.
    
    Repeat the above until we reach the last object in the catalog; that has
    no known length so just read from there to the end of the stream.
    
    The complications are all around getting the objects on the stack in
    the order expected by the code which follows, and defines the actual
    objects.

diff --git a/Resource/Init/pdf_base.ps b/Resource/Init/pdf_base.ps
index 02503ee..7623dfc 100644
--- a/Resource/Init/pdf_base.ps
+++ b/Resource/Init/pdf_base.ps
@@ -776,11 +776,110 @@ currentdict /token_nofail_dict .undef
   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
+  count counttomark 1 add index ne
+  {
+    count counttomark 1 add index gt {
+      (   **** Error: Incorrect object count in object stream (too many objects).\n) pdfformaterror
+      (               Output may be incorrect.\n) pdfformaterror
+
+      % Having too many objects in an ObjStm is not illegal, dumb but not illegal. We can
+      % recover from this, but its complicated. The extra object could be any of the ones
+      % from the stream, the only way to find out is to process each object individually.
+      % Unfortunately, my attempts to come up with a version of .pdfrun which only tokenised
+      % one object got rapidly bogged down. So a new approach was needed.
+      %
+      % The idea is simple; turn the underlying file into a ReusableStream, then we can
+      % reposition it. Build an array of the object offsets, reposition the file to the
+      % start of each object in turn. For all except the last object, create a sub file
+      % using SubFileDeocde with an empty string, and an EODCount which is the difference
+      % between the offset of this object and the offset of the next.
+      % For the last object we just read from the offset to the end of the stream.
+      %
+      % Then call .pdfrun on that sub file, or the main stream, which will tokenise all the
+      % objects from that point onwards. Then discard all but the first object read.
+      %
+      % The complications arise from the fact that we need to end up with the objects on
+      % the stack, in the right order, preceded by a mark, a count, and an array containing
+      % all the object numbers.
+
+      % First, discard everything we read up to now; we can reuse the mark which was placed
+      % by the preceding code to do this, as long as we remember to replace that mark.
+      % Handily this will leave the count and the array which contains the object numbers in place.
+      cleartomark
+
+      % Replace the mark consumed by conttomark above, so that we match what the code following this error handling expects.
+      mark
+
+      % copy the ObjStm dictionary and then copy the count of objects expected
+      4 index 4 index
+
+      % Copy the ObjStm dictionary, and create a file from it
+      1 index //false resolvestream	        % Convert stream dict into a stream
+
+      /ReusableStreamDecode filter	        % We need to be able to re-position the stream
+
+      1 index array                         % Create array for holding offsets
+
+      % Get the object offsets, these are stored at teh start of the stream, interleaved
+      % between the object numbers. We know reading these can't fail, because we've already
+      % done this to create the array of object numbers, above.
+      0 1                                   % starting index (0), increment (1) for loop
+      4 index 1 sub                         % limit of loop is object count-1 (because we start at 0)
+      {
+        1 index 1 index                     % copy array and index
+        4 index                             % copy the file object
+        token pop pop                       % read a token (object number) and discard it
+        4 index token                       % read another token (offset)
+        pop put pop                         % put the offset into the array at the new index
+      } for
+
+      % We now have an array with all the object offsets in it
+      % So we need to reposition the file to the start of each object and read
+      % from that point. We use the difference between two offsets to setup
+      % a SubFileDecode filter to only read as many bytes as there are
+      % between the objects. Normally this should result in us reading one object,
+      % if there are extra objects then we'll discard the extras. By doing this
+      % we avoid tokenising the same data multiple times.
+
+      % Set the loop to be from 0 to the  number of objects minus 1 (starts from 0), step 1.
+      2 index 1 sub 0 1 3 -1 roll
+      {
+        dup                                         % copy the loop index
+        2 index length 2 sub lt                     % Check if index + 1 exceeds array size (it will on last iteration)
+
+        {                                           % Not the last iteration
+          dup                                       % copy the loop index
+          2 index exch get 5 index /First get add   % get the object offset, and add the value of First from the dictionary.
+          exch 1 add                                % add one to the loop index
+          2 index exch get 5 index /First get add   % and get the offset to the start of the next object
+
+          1 index sub                               % copy the first offset and subtract from the second to get the length
+          exch
+          3 index dup 3 -1 roll setfileposition     % copy the file and position it to the offset of the object
+          exch
+          << /EODCount 3 -1 roll /EODString () >>   % Set up a dictionary for the filter
+          /SubFileDecode filter                     % and apply it to limit the number of bytes read.
+        }
+        {                                           % On the last iteration, simly read from the file, we don't need another SubFileDecode.
+          1 index exch get 4 index /First get add   % get the offset, and add the value of First from the dictionary.
+          2 index dup 3 -1 roll setfileposition     % copy the stream, and position it at that point
+        }ifelse
+
+        mark exch resolveobjstreamopdict .pdfrun    % make a mark and then tokenise all the objects from there to the end of stream (or sub file)
+        counttomark 2 sub                           % Find out how many objects we created (less two, one for the count and one for the first object)
+        0 1 3 -1 roll {pop pop} for                 % pop that many objects off the stack
+        6 1 roll pop                                % roll the new object to be behind our working objects on the stack and pop the mark
+      } for
+
+      % pop the working objects (array, file, count of objects and ObjStrm dictionary)
+      pop pop pop pop
+    }{
+      (   **** Error: Incorrect object count in object stream (too few objects).\n) pdfformaterror
+      (               Output may be incorrect.\n) pdfformaterror
+      /resolveobjectstream cvx /rangecheck signalerror
+    } ifelse
   } if
+
                 % We have the object data
   counttomark array astore	% Put objects into an array
   exch pop exch pop		% Remove mark and count


Summary of changes:
 Resource/Init/pdf_base.ps | 107 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 103 insertions(+), 4 deletions(-)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.