summaryrefslogtreecommitdiff
path: root/source/fitz/filter-flate.c
blob: a7bbb2734d4e45db1834dcd04ffdd58519e51f89 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include "mupdf/fitz.h"

#include <zlib.h>

#include <string.h>

typedef struct fz_inflate_state_s fz_inflate_state;

struct fz_inflate_state_s
{
	fz_stream *chain;
	z_stream z;
	unsigned char buffer[4096];
};

static void *zalloc_flate(void *opaque, unsigned int items, unsigned int size)
{
	return fz_malloc_array_no_throw(opaque, items, size);
}

static void zfree_flate(void *opaque, void *ptr)
{
	fz_free(opaque, ptr);
}

static int
next_flated(fz_context *ctx, fz_stream *stm, size_t required)
{
	fz_inflate_state *state = stm->state;
	fz_stream *chain = state->chain;
	z_streamp zp = &state->z;
	int code;
	unsigned char *outbuf = state->buffer;
	int outlen = sizeof(state->buffer);

	if (stm->eof)
		return EOF;

	zp->next_out = outbuf;
	zp->avail_out = outlen;

	while (zp->avail_out > 0)
	{
		zp->avail_in = (uInt)fz_available(ctx, chain, 1);
		zp->next_in = chain->rp;

		code = inflate(zp, Z_SYNC_FLUSH);

		chain->rp = chain->wp - zp->avail_in;

		if (code == Z_STREAM_END)
		{
			break;
		}
		else if (code == Z_BUF_ERROR)
		{
			fz_warn(ctx, "premature end of data in flate filter");
			break;
		}
		else if (code == Z_DATA_ERROR && zp->avail_in == 0)
		{
			fz_warn(ctx, "ignoring zlib error: %s", zp->msg);
			break;
		}
		else if (code == Z_DATA_ERROR && !strcmp(zp->msg, "incorrect data check"))
		{
			fz_warn(ctx, "ignoring zlib error: %s", zp->msg);
			chain->rp = chain->wp;
			break;
		}
		else if (code != Z_OK)
		{
			fz_throw(ctx, FZ_ERROR_GENERIC, "zlib error: %s", zp->msg);
		}
	}

	stm->rp = state->buffer;
	stm->wp = state->buffer + outlen - zp->avail_out;
	stm->pos += outlen - zp->avail_out;
	if (stm->rp == stm->wp)
	{
		stm->eof = 1;
		return EOF;
	}
	return *stm->rp++;
}

static void
close_flated(fz_context *ctx, void *state_)
{
	fz_inflate_state *state = (fz_inflate_state *)state_;
	int code;

	code = inflateEnd(&state->z);
	if (code != Z_OK)
		fz_warn(ctx, "zlib error: inflateEnd: %s", state->z.msg);

	fz_drop_stream(ctx, state->chain);
	fz_free(ctx, state);
}

fz_stream *
fz_open_flated(fz_context *ctx, fz_stream *chain, int window_bits)
{
	fz_inflate_state *state = NULL;
	int code = Z_OK;

	fz_var(code);
	fz_var(state);

	fz_try(ctx)
	{
		state = fz_malloc_struct(ctx, fz_inflate_state);
		state->chain = chain;

		state->z.zalloc = zalloc_flate;
		state->z.zfree = zfree_flate;
		state->z.opaque = ctx;
		state->z.next_in = NULL;
		state->z.avail_in = 0;

		code = inflateInit2(&state->z, window_bits);
		if (code != Z_OK)
			fz_throw(ctx, FZ_ERROR_GENERIC, "zlib error: inflateInit: %s", state->z.msg);
	}
	fz_catch(ctx)
	{
		if (state && code == Z_OK)
			inflateEnd(&state->z);
		fz_free(ctx, state);
		fz_drop_stream(ctx, chain);
		fz_rethrow(ctx);
	}
	return fz_new_stream(ctx, state, next_flated, close_flated);
}