static int filter_flatedecode(struct pdf_struct *pdf, struct pdf_obj *obj,
			      const char *buf, off_t len, int fout, off_t *sum)
{
    int skipped = 0;
    int zstat;
    z_stream stream;
    off_t nbytes;
    char output[BUFSIZ];

    if (len == 0)
	return CL_CLEAN;

    if (*buf == '\r') {
	buf++;
	len--;
	pdfobj_flag(pdf, obj, BAD_STREAMSTART);
	/* PDF spec says stream is followed by \r\n or \n, but not \r alone.
	 * Sample 0015315109, it has \r followed by zlib header.
	 * Flag pdf as suspicious, and attempt to extract by skipping the \r.
	 */
	if (!len)
	    return CL_CLEAN;
    }

    memset(&stream, 0, sizeof(stream));
    stream.next_in = (Bytef *)buf;
    stream.avail_in = len;
    stream.next_out = (Bytef *)output;
    stream.avail_out = sizeof(output);

    zstat = inflateInit(&stream);
    if(zstat != Z_OK) {
	cli_warnmsg("cli_pdf: inflateInit failed\n");
	return CL_EMEM;
    }

    nbytes = 0;
    while(stream.avail_in) {
	int written;
	zstat = inflate(&stream, Z_NO_FLUSH);	/* zlib */
	switch(zstat) {
	    case Z_OK:
		if(stream.avail_out == 0) {
		    if ((written=filter_writen(pdf, obj, fout, output, sizeof(output), sum))!=sizeof(output)) {
			cli_errmsg("cli_pdf: failed to write output file\n");
			inflateEnd(&stream);
			return CL_EWRITE;
		    }
		    nbytes += written;
		    stream.next_out = (Bytef *)output;
		    stream.avail_out = sizeof(output);
		}
		continue;
	    case Z_STREAM_END:
	    default:
		written = sizeof(output) - stream.avail_out;
		if (!written && !nbytes && !skipped) {
		    /* skip till EOL, and try inflating from there, sometimes
		     * PDFs contain extra whitespace */
		    const char *q = pdf_nextlinestart(buf, len);
		    if (q) {
			skipped = 1;
			inflateEnd(&stream);
			len -= q - buf;
			buf = q;
			stream.next_in = (Bytef *)buf;
			stream.avail_in = len;
			stream.next_out = (Bytef *)output;
			stream.avail_out = sizeof(output);
			zstat = inflateInit(&stream);
			if(zstat != Z_OK) {
			    cli_warnmsg("cli_pdf: inflateInit failed\n");
			    return CL_EMEM;
			}
			pdfobj_flag(pdf, obj, BAD_FLATESTART);
			continue;
		    }
		}

		if (filter_writen(pdf, obj, fout, output, written, sum)!=written) {
		    cli_errmsg("cli_pdf: failed to write output file\n");
		    inflateEnd(&stream);
		    return CL_EWRITE;
		}
		nbytes += written;
		stream.next_out = (Bytef *)output;
		stream.avail_out = sizeof(output);
		if (zstat == Z_STREAM_END)
		    break;

		if(stream.msg)
		    cli_dbgmsg("cli_pdf: after writing %lu bytes, got error \"%s\" inflating PDF stream in %u %u obj\n",
			       (unsigned long)nbytes,
			       stream.msg, obj->id>>8, obj->id&0xff);
		else
		    cli_dbgmsg("cli_pdf: after writing %lu bytes, got error %d inflating PDF stream in %u %u obj\n",
			       (unsigned long)nbytes, zstat, obj->id>>8, obj->id&0xff);
		/* mark stream as bad only if not encrypted */
		inflateEnd(&stream);
		if (!nbytes) {
		    cli_dbgmsg("cli_pdf: dumping raw stream (probably encrypted)\n");
		    if (filter_writen(pdf, obj, fout, buf, len, sum) != len) {
			cli_errmsg("cli_pdf: failed to write output file\n");
			return CL_EWRITE;
		    }
		    pdfobj_flag(pdf, obj, BAD_FLATESTART);
		} else {
		    pdfobj_flag(pdf, obj, BAD_FLATE);
		}
		return CL_CLEAN;
	}
	break;
    }

    if(stream.avail_out != sizeof(output)) {
	if(filter_writen(pdf, obj, fout, output, sizeof(output) - stream.avail_out, sum) < 0) {
	    cli_errmsg("cli_pdf: failed to write output file\n");
	    inflateEnd(&stream);
	    return CL_EWRITE;
	}
    }

    inflateEnd(&stream);
    return CL_CLEAN;
}