Bug 224134

Summary: Multiple encode filters in a pipeline fail
Product: Ghostscript Reporter: Raph Levien <raph.levien>
Component: PS InterpreterAssignee: Raph Levien <raph.levien>
Status: NOTIFIED FIXED    
Severity: normal    
Priority: P2    
Version: master   
Hardware: All   
OS: All   
Customer: Word Size: ---

Description Raph Levien 2000-12-02 00:49:09 UTC
Originally reported by: raph@users.sourceforge.net
The following code fragment should output "3E>". It outputs nothing.

%!PS
(%stdout) (w) file
/ASCIIHexEncode filter
/ASCIIHexEncode filter
%dup (hi) writestring
closefile

The immediate reason why this fails is fairly clear. The pipeline has three streams: two ASCIIHexEncode streams followed by a file write stream. All streams have is_temp = 0. However, the following code at stream.c:890 prevents swritebuf from ever advancing to the second stream in the pipeline (assuming that no status=1 return from the first stream, a valid assumption for the above code fragment):

	    if (status != 1) {
		/*
		 * Keep going if we are closing a filter with a sub-stream.
		 * We know status == 0 or EOFC.
		 */
		if (!end || !strm->is_temp)
		    break;
	    }

While I understand what's causing the bug, I'm reluctant at the moment to mess with the code. At some point, I will document the stream invariants, which should help us make changes to the stream code with confidence.
Comment 1 L. Peter Deutsch 2000-12-31 12:17:30 UTC
Comment originally by lpd@users.sourceforge.net
I've raised the priority of the bug. I agree that documenting the invariants is the right thing to do next. The problems seem to relate to closing filters, and in particular to the invariants relating the control flow in swritebuf with the value of end_status in the various filters in the pipeline.
Comment 2 Ray Johnston 2001-03-19 23:05:50 UTC
Comment originally by rayjj@users.sourceforge.net
Logged In: YES 
user_id=11206

This problem report has been moved to doc/Issues.htm where it may remain open for
some time. Relevant comments are captured there along with the ID# of this report.
Comment 3 L. Peter Deutsch 2002-04-08 23:08:25 UTC
Comment originally by lpd@users.sourceforge.net
Logged In: YES 
user_id=8861

The analysis is wrong.  Ghostscript's behavior is correct
(consistent with both the PLRM and Adobe RIPs).  Closing the
outer filter does not, and should not, flush and/or close
the inner filter.  Because of buffering, clients cannot
assume that output is propagated through an output filter
unless the filter is closed or flushed.  Since the inner
filter is not closed, its buffered output is never
propagated to stdout.

The above happens even if the entire code is bracketed in a
save/restore.  Even though the restore closes all the
filters, both the PLRM and private correspondence from Adobe
state that when this happens, the order of closing is not
guaranteed and what happens to buffered output is undefined.

The following code prints "XXXXXXXX" rather than "3E>XXXXX"
on both Ghostscript and an Adobe Level 2 RIP:

%!PS 
/s (XXXXXXXX) def
s
/ASCIIHexEncode filter 
/ASCIIHexEncode filter 
closefile 
/Times-Roman 20 selectfont
100 100 moveto s show showpage
Comment 4 Ray Johnston 2002-04-09 08:49:11 UTC
Comment originally by rayjj@users.sourceforge.net
Logged In: YES 
user_id=11206

I had originally verified this with Adobe Distiller which claims to be
3011.104 based. The file I used to test was:
_______________________________________________________________________
%!PS
(%stdout) (w) file
/ASCIIHexEncode filter
/ASCIIHexEncode filter
closefile
() = flush
version = flush
_______________________________________________________________________
and the output from Adobe Distiller 5 was:
_______________________________________________________________________

3011.104
3E>%%[ Warning: Empty job. No PDF file produced. ] %%

_______________________________________________________________________

I found it interesting that the output from the "() = flush" and the 
"version = flush" preceded the "3E>". Apparently the closefile does
not close the filters and flush the output to stdout, but something
at the end of the job does.

I agree that any PostScript program that relies on this behaviour
is not valid and that GS appears to behave the way the PLRM
describes and apparently the way some Adobe Level 2 versions
did.
Comment 5 Ray Johnston 2004-09-26 09:02:01 UTC
Actually, while the specification specifies that 'closefile'
will not close the target of the filter being closed unless
the /CloseTarget true option is set, there was an error in
the filter close logic that failed to propagate the 'flush'
to the target following the 's_process_write_buf( , true)'
on the filter. This would result in some final bytes being
lost from the downstream.

The patch for this was:

diff -c -r1.24 stream.c
*** src/stream.c	15 Sep 2004 19:41:01 -0000	1.24
--- src/stream.c	26 Sep 2004 14:46:00 -0000
***************
*** 325,340 ****
  }
  
  /* Close a filter.  If this is an encoding filter, flush it first. */
  int
  s_filter_close(register stream * s)
  {
      if (s_is_writing(s)) {
  	int status = s_process_write_buf(s, true);
  
  	if (status != 0 && status != EOFC)
  	    return status;
      }
!     return s_std_close(s);
  }
  
  /* Disregard a stream error message. */
--- 325,352 ----
  }
  
  /* Close a filter.  If this is an encoding filter, flush it first. */
+ /* If CloseTarget was specified (close_strm), then propagate the sclose */
  int
  s_filter_close(register stream * s)
  {
+     int status;
+     stream *stemp = s->strm;
+ 
      if (s_is_writing(s)) {
  	int status = s_process_write_buf(s, true);
  
  	if (status != 0 && status != EOFC)
  	    return status;
+         status = sflush(stemp);
+ 	if (status != 0 && status != EOFC)
+ 	    return status;
      }
!     status = s_std_close(s);
!     if (status != 0 && status != EOFC)
! 	return status;
!     if (s->close_strm && stemp != 0)
! 	return sclose(stemp);
!     return status;
  }
  
  /* Disregard a stream error message. */