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 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 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 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 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.
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. */