Exemplo n.º 1
0
Arquivo: pdfshow.c Projeto: Limsik/e17
static void showstream(int num, int gen)
{
	fz_error error;
	fz_stream *stm;
	unsigned char buf[2048];
	int n;

	showcolumn = 0;

	if (showdecode)
		error = pdf_openstream(&stm, xref, num, gen);
	else
		error = pdf_openrawstream(&stm, xref, num, gen);
	if (error)
		die(error);

	while (1)
	{
		error = fz_read(&n, stm, buf, sizeof buf);
		if (error)
			die(error);
		if (n == 0)
			break;
		if (showbinary)
			fwrite(buf, 1, n, stdout);
		else
			showsafe(buf, n);
	}

	fz_dropstream(stm);
}
static fz_error
loadpostscriptfunc(pdf_function *func, pdf_xref *xref, fz_obj *dict, int oid, int gen)
{
	fz_error error;
	fz_stream *stream;
	int codeptr;

	pdf_logrsrc("load postscript function (%d %d R)\n", oid, gen);

	error = pdf_openstream(&stream, xref, oid, gen);
	if (error)
		return fz_rethrow(error, "cannot open calculator function stream");

	if (fz_readbyte(stream) != '{')
	{
		fz_dropstream(stream);
		return fz_throw("stream is not a calculator function");
	}

	func->u.p.code = nil;
	func->u.p.cap = 0;

	codeptr = 0;
	error = parsecode(func, stream, &codeptr);
	if (error)
	{
		fz_dropstream(stream);
		return fz_rethrow(error, "cannot parse calculator function");
	}

	fz_dropstream(stream);
	return fz_okay;
}
Exemplo n.º 3
0
static fz_error
loadcharproc(fz_tree **treep, pdf_xref *xref, fz_obj *rdb, fz_obj *stmref)
{
	fz_error error;
	pdf_csi *csi;
	fz_stream *stm;

	error = pdf_newcsi(&csi, 1);
	if (error)
		return fz_rethrow(error, "cannot create interpreter");

	error = pdf_openstream(&stm, xref, fz_tonum(stmref), fz_togen(stmref));
	if (error)
	{
		pdf_dropcsi(csi);
		return fz_rethrow(error, "cannot open glyph content stream");
	}

	error = pdf_runcsi(csi, xref, rdb, stm);
	if (error)
	{
		fz_dropstream(stm);
		pdf_dropcsi(csi);
		return fz_rethrow(error, "cannot interpret glyph content stream (%d %d R)", fz_tonum(stmref), fz_togen(stmref));
	}

	*treep = csi->tree;
	csi->tree = nil;

	fz_dropstream(stm);
	pdf_dropcsi(csi);
	return fz_okay;
}
Exemplo n.º 4
0
static fz_error
pdf_repairobjstm(pdf_xref *xref, int num, int gen)
{
	fz_error error;
	fz_obj *obj;
	fz_stream *stm;
	int tok;
	int i, n, count;
	char buf[256];

	error = pdf_loadobject(&obj, xref, num, gen);
	if (error)
		return fz_rethrow(error, "cannot load object stream object (%d %d R)", num, gen);

	count = fz_toint(fz_dictgets(obj, "N"));

	fz_dropobj(obj);

	error = pdf_openstream(&stm, xref, num, gen);
	if (error)
		return fz_rethrow(error, "cannot open object stream object (%d %d R)", num, gen);

	for (i = 0; i < count; i++)
	{
		error = pdf_lex(&tok, stm, buf, sizeof buf, &n);
		if (error || tok != PDF_TINT)
		{
			fz_close(stm);
			return fz_rethrow(error, "corrupt object stream (%d %d R)", num, gen);
		}

		n = atoi(buf);
		if (n >= xref->len)
			pdf_resizexref(xref, n + 1);

		xref->table[n].ofs = num;
		xref->table[n].gen = i;
		xref->table[n].stmofs = 0;
		xref->table[n].obj = nil;
		xref->table[n].type = 'o';

		error = pdf_lex(&tok, stm, buf, sizeof buf, &n);
		if (error || tok != PDF_TINT)
		{
			fz_close(stm);
			return fz_rethrow(error, "corrupt object stream (%d %d R)", num, gen);
		}
	}

	fz_close(stm);
	return fz_okay;
}
Exemplo n.º 5
0
/*
 * Load uncompressed contents of a stream into buf.
 */
fz_error *
pdf_loadstream(fz_buffer **bufp, pdf_xref *xref, int oid, int gen)
{
	fz_error *error;
	fz_stream *stm;

	error = pdf_openstream(&stm, xref, oid, gen);
	if (error)
		return fz_rethrow(error, "cannot open stream (%d)", oid);

	error = fz_readall(bufp, stm, 0);
	fz_dropstream(stm);
	if (error)
		return fz_rethrow(error, "cannot load stream into buffer (%d)", oid);
	return fz_okay;
}
static fz_error
pdf_loadobjstm(pdf_xref *xref, int num, int gen, char *buf, int cap)
{
    fz_error error;
    fz_stream *stm;
    fz_obj *objstm;
    int *numbuf;
    int *ofsbuf;

    fz_obj *obj;
    int first;
    int count;
    int i, n;
    pdf_token_e tok;

    pdf_logxref("loadobjstm (%d %d R)\n", num, gen);

    error = pdf_loadobject(&objstm, xref, num, gen);
    if (error)
        return fz_rethrow(error, "cannot load object stream object (%d %d R)", num, gen);

    count = fz_toint(fz_dictgets(objstm, "N"));
    first = fz_toint(fz_dictgets(objstm, "First"));

    pdf_logxref("\tcount %d\n", count);

    numbuf = fz_malloc(count * sizeof(int));
    ofsbuf = fz_malloc(count * sizeof(int));

    error = pdf_openstream(&stm, xref, num, gen);
    if (error)
    {
        error = fz_rethrow(error, "cannot open object stream (%d %d R)", num, gen);
        goto cleanupbuf;
    }

    for (i = 0; i < count; i++)
    {
        error = pdf_lex(&tok, stm, buf, cap, &n);
        if (error || tok != PDF_TINT)
        {
            error = fz_rethrow(error, "corrupt object stream (%d %d R)", num, gen);
            goto cleanupstm;
        }
        numbuf[i] = atoi(buf);

        error = pdf_lex(&tok, stm, buf, cap, &n);
        if (error || tok != PDF_TINT)
        {
            error = fz_rethrow(error, "corrupt object stream (%d %d R)", num, gen);
            goto cleanupstm;
        }
        ofsbuf[i] = atoi(buf);
    }

    fz_seek(stm, first, 0);

    for (i = 0; i < count; i++)
    {
        fz_seek(stm, first + ofsbuf[i], 0);

        error = pdf_parsestmobj(&obj, xref, stm, buf, cap);
        if (error)
        {
            error = fz_rethrow(error, "cannot parse object %d in stream (%d %d R)", i, num, gen);
            goto cleanupstm;
        }

        if (numbuf[i] < 1 || numbuf[i] >= xref->len)
        {
            fz_dropobj(obj);
            error = fz_throw("object id (%d 0 R) out of range (0..%d)", numbuf[i], xref->len - 1);
            goto cleanupstm;
        }

        if (xref->table[numbuf[i]].type == 'o' && xref->table[numbuf[i]].ofs == num)
        {
            if (xref->table[numbuf[i]].obj)
                fz_dropobj(xref->table[numbuf[i]].obj);
            xref->table[numbuf[i]].obj = obj;
        }
        else
        {
            fz_dropobj(obj);
        }
    }

    fz_close(stm);
    fz_free(ofsbuf);
    fz_free(numbuf);
    fz_dropobj(objstm);
    return fz_okay;

cleanupstm:
    fz_close(stm);
cleanupbuf:
    fz_free(ofsbuf);
    fz_free(numbuf);
    fz_dropobj(objstm);
    return error; /* already rethrown */
}
/*
 * Load CMap stream in PDF file
 */
fz_error
pdf_loadembeddedcmap(pdf_cmap **cmapp, pdf_xref *xref, fz_obj *stmref)
{
    fz_error error = fz_okay;
    fz_obj *stmobj;
    fz_stream *file = nil;
    pdf_cmap *cmap = nil;
    pdf_cmap *usecmap;
    fz_obj *wmode;
    fz_obj *obj;

    if ((*cmapp = pdf_finditem(xref->store, PDF_KCMAP, stmref)))
    {
	pdf_keepcmap(*cmapp);
	return fz_okay;
    }

    pdf_logfont("load embedded cmap (%d %d R) {\n", fz_tonum(stmref), fz_togen(stmref));

    stmobj = fz_resolveindirect(stmref);

    error = pdf_openstream(&file, xref, fz_tonum(stmref), fz_togen(stmref));
    if (error)
    {
	error = fz_rethrow(error, "cannot open cmap stream");
	goto cleanup;
    }

    error = pdf_parsecmap(&cmap, file);
    if (error)
    {
	error = fz_rethrow(error, "cannot parse cmap stream");
	goto cleanup;
    }

    fz_dropstream(file);

    wmode = fz_dictgets(stmobj, "WMode");
    if (fz_isint(wmode))
    {
	pdf_logfont("wmode %d\n", wmode);
	pdf_setwmode(cmap, fz_toint(wmode));
    }

    obj = fz_dictgets(stmobj, "UseCMap");
    if (fz_isname(obj))
    {
	pdf_logfont("usecmap /%s\n", fz_toname(obj));
	error = pdf_loadsystemcmap(&usecmap, fz_toname(obj));
	if (error)
	{
	    error = fz_rethrow(error, "cannot load system usecmap '%s'", fz_toname(obj));
	    goto cleanup;
	}
	pdf_setusecmap(cmap, usecmap);
	pdf_dropcmap(usecmap);
    }
    else if (fz_isindirect(obj))
    {
	pdf_logfont("usecmap (%d %d R)\n", fz_tonum(obj), fz_togen(obj));
	error = pdf_loadembeddedcmap(&usecmap, xref, obj);
	if (error)
	{
	    error = fz_rethrow(error, "cannot load embedded usecmap");
	    goto cleanup;
	}
	pdf_setusecmap(cmap, usecmap);
	pdf_dropcmap(usecmap);
    }

    pdf_logfont("}\n");

    error = pdf_storeitem(xref->store, PDF_KCMAP, stmref, cmap);
    if (error)
    {
	error = fz_rethrow(error, "cannot store cmap resource");
	goto cleanup;
    }

    *cmapp = cmap;
    return fz_okay;

cleanup:
    if (file)
	fz_dropstream(file);
    if (cmap)
	pdf_dropcmap(cmap);
    return error; /* already rethrown */
}
Exemplo n.º 8
0
static fz_error
pdf_loadimageimp(fz_pixmap **imgp, pdf_xref *xref, fz_obj *rdb, fz_obj *dict, fz_stream *cstm, int forcemask)
{
	fz_stream *stm;
	fz_pixmap *tile;
	fz_obj *obj, *res;
	fz_error error;

	int w, h, bpc, n;
	int imagemask;
	int interpolate;
	int indexed;
	fz_colorspace *colorspace;
	fz_pixmap *mask; /* explicit mask/softmask image */
	int usecolorkey;
	int colorkey[FZ_MAXCOLORS * 2];
	float decode[FZ_MAXCOLORS * 2];

	int scale;
	int stride;
	unsigned char *samples;
	int i, len;

	/* special case for JPEG2000 images */
	if (pdf_isjpximage(dict))
	{
		tile = nil;
		error = pdf_loadjpximage(&tile, xref, dict);
		if (error)
			return fz_rethrow(error, "cannot load jpx image");
		if (forcemask)
		{
			if (tile->n != 2)
			{
				fz_droppixmap(tile);
				return fz_throw("softmask must be grayscale");
			}
			mask = fz_alphafromgray(tile, 1);
			fz_droppixmap(tile);
			*imgp = mask;
			return fz_okay;
		}
		*imgp = tile;
		return fz_okay;
	}

	w = fz_toint(fz_dictgetsa(dict, "Width", "W"));
	h = fz_toint(fz_dictgetsa(dict, "Height", "H"));
	bpc = fz_toint(fz_dictgetsa(dict, "BitsPerComponent", "BPC"));
	imagemask = fz_tobool(fz_dictgetsa(dict, "ImageMask", "IM"));
	interpolate = fz_tobool(fz_dictgetsa(dict, "Interpolate", "I"));

	indexed = 0;
	usecolorkey = 0;
	colorspace = nil;
	mask = nil;

	if (imagemask)
		bpc = 1;

	if (w == 0)
		return fz_throw("image width is zero");
	if (h == 0)
		return fz_throw("image height is zero");
	if (bpc == 0)
		return fz_throw("image depth is zero");
	if (w > (1 << 16))
		return fz_throw("image is too wide");
	if (h > (1 << 16))
		return fz_throw("image is too high");

	obj = fz_dictgetsa(dict, "ColorSpace", "CS");
	if (obj && !imagemask && !forcemask)
	{
		/* colorspace resource lookup is only done for inline images */
		if (fz_isname(obj))
		{
			res = fz_dictget(fz_dictgets(rdb, "ColorSpace"), obj);
			if (res)
				obj = res;
		}

		error = pdf_loadcolorspace(&colorspace, xref, obj);
		if (error)
			return fz_rethrow(error, "cannot load image colorspace");

		if (!strcmp(colorspace->name, "Indexed"))
			indexed = 1;

		n = colorspace->n;
	}
	else
	{
		n = 1;
	}

	obj = fz_dictgetsa(dict, "Decode", "D");
	if (obj)
	{
		for (i = 0; i < n * 2; i++)
			decode[i] = fz_toreal(fz_arrayget(obj, i));
	}
	else
	{
		float maxval = indexed ? (1 << bpc) - 1 : 1;
		for (i = 0; i < n * 2; i++)
			decode[i] = i & 1 ? maxval : 0;
	}

	obj = fz_dictgetsa(dict, "SMask", "Mask");
	if (fz_isdict(obj))
	{
		/* Not allowed for inline images */
		if (!cstm)
		{
			error = pdf_loadimageimp(&mask, xref, rdb, obj, nil, 1);
			if (error)
			{
				if (colorspace)
					fz_dropcolorspace(colorspace);
				return fz_rethrow(error, "cannot load image mask/softmask");
			}
		}
	}
	else if (fz_isarray(obj))
	{
		usecolorkey = 1;
		for (i = 0; i < n * 2; i++)
			colorkey[i] = fz_toint(fz_arrayget(obj, i));
	}

	stride = (w * n * bpc + 7) / 8;
	samples = fz_calloc(h, stride);

	if (cstm)
	{
		stm = pdf_openinlinestream(cstm, xref, dict, stride * h);
	}
	else
	{
		error = pdf_openstream(&stm, xref, fz_tonum(dict), fz_togen(dict));
		if (error)
		{
			if (colorspace)
				fz_dropcolorspace(colorspace);
			if (mask)
				fz_droppixmap(mask);
			return fz_rethrow(error, "cannot open image data stream (%d 0 R)", fz_tonum(dict));
		}
	}

	len = fz_read(stm, samples, h * stride);
	if (len < 0)
	{
		fz_close(stm);
		if (colorspace)
			fz_dropcolorspace(colorspace);
		if (mask)
			fz_droppixmap(mask);
		return fz_rethrow(len, "cannot read image data");
	}

	/* Make sure we read the EOF marker (for inline images only) */
	if (cstm)
	{
		unsigned char tbuf[512];
		int tlen = fz_read(stm, tbuf, sizeof tbuf);
		if (tlen < 0)
			fz_catch(tlen, "ignoring error at end of image");
		if (tlen > 0)
			fz_warn("ignoring garbage at end of image");
	}

	fz_close(stm);

	/* Pad truncated images */
	if (len < stride * h)
	{
		fz_warn("padding truncated image (%d 0 R)", fz_tonum(dict));
		memset(samples + len, 0, stride * h - len);
	}

	/* Invert 1-bit image masks */
	if (imagemask)
	{
		/* 0=opaque and 1=transparent so we need to invert */
		unsigned char *p = samples;
		len = h * stride;
		for (i = 0; i < len; i++)
			p[i] = ~p[i];
	}

	pdf_logimage("size %dx%d n=%d bpc=%d imagemask=%d indexed=%d\n", w, h, n, bpc, imagemask, indexed);

	/* Unpack samples into pixmap */

	tile = fz_newpixmap(colorspace, 0, 0, w, h);

	scale = 1;
	if (!indexed)
	{
		switch (bpc)
		{
		case 1: scale = 255; break;
		case 2: scale = 85; break;
		case 4: scale = 17; break;
		}
	}

	fz_unpacktile(tile, samples, n, bpc, stride, scale);

	if (usecolorkey)
		pdf_maskcolorkey(tile, n, colorkey);

	if (indexed)
	{
		fz_pixmap *conv;

		fz_decodeindexedtile(tile, decode, (1 << bpc) - 1);

		conv = pdf_expandindexedpixmap(tile);
		fz_droppixmap(tile);
		tile = conv;
	}
	else
	{
		fz_decodetile(tile, decode);
	}

	if (colorspace)
		fz_dropcolorspace(colorspace);

	tile->mask = mask;
	tile->interpolate = interpolate;

	fz_free(samples);

	*imgp = tile;
	return fz_okay;
}
Exemplo n.º 9
0
static fz_error
readnewxref(fz_obj **trailerp, pdf_xref *xref, char *buf, int cap)
{
	fz_error error;
	fz_stream *stm;
	fz_obj *trailer;
	fz_obj *index;
	fz_obj *obj;
	int oid, gen, stmofs;
	int size, w0, w1, w2;
	int t;
	int i;

	pdf_logxref("load new xref format\n");

	error = pdf_parseindobj(&trailer, xref, xref->file, buf, cap, &oid, &gen, &stmofs);
	if (error)
		return fz_rethrow(error, "cannot parse compressed xref stream object");

	obj = fz_dictgets(trailer, "Size");
	if (!obj)
	{
		fz_dropobj(trailer);
		return fz_throw("xref stream missing Size entry");
	}
	size = fz_toint(obj);

	if (size >= xref->cap)
	{
		xref->cap = size + 1; /* for hack to allow broken pdf generators with off-by-one errors */
		xref->table = fz_realloc(xref->table, xref->cap * sizeof(pdf_xrefentry));
	}

	if (size > xref->len)
	{
		for (i = xref->len; i < xref->cap; i++)
		{
			xref->table[i].ofs = 0;
			xref->table[i].gen = 0;
			xref->table[i].stmofs = 0;
			xref->table[i].obj = nil;
			xref->table[i].type = 0;
		}
		xref->len = size;
	}

	if (oid < 0 || oid >= xref->len)
	{
		if (oid == xref->len && oid < xref->cap)
		{
			/* allow broken pdf files that have off-by-one errors in the xref */
			fz_warn("object id (%d %d R) out of range (0..%d)", oid, gen, xref->len - 1);
			xref->len ++;
		}
		else
		{
			fz_dropobj(trailer);
			return fz_throw("object id (%d %d R) out of range (0..%d)", oid, gen, xref->len - 1);
		}
	}

	xref->table[oid].type = 'n';
	xref->table[oid].gen = gen;
	xref->table[oid].obj = fz_keepobj(trailer);
	xref->table[oid].stmofs = stmofs;
	xref->table[oid].ofs = 0;

	obj = fz_dictgets(trailer, "W");
	if (!obj) {
		fz_dropobj(trailer);
		return fz_throw("xref stream missing W entry");
	}
	w0 = fz_toint(fz_arrayget(obj, 0));
	w1 = fz_toint(fz_arrayget(obj, 1));
	w2 = fz_toint(fz_arrayget(obj, 2));

	index = fz_dictgets(trailer, "Index");

	error = pdf_openstream(&stm, xref, oid, gen);
	if (error)
	{
		fz_dropobj(trailer);
		return fz_rethrow(error, "cannot open compressed xref stream");
	}

	if (!index)
	{
		error = readnewxrefsection(xref, stm, 0, size, w0, w1, w2);
		if (error)
		{
			fz_dropstream(stm);
			fz_dropobj(trailer);
			return fz_rethrow(error, "cannot read xref stream");
		}
	}
	else
	{
		for (t = 0; t < fz_arraylen(index); t += 2)
		{
			int i0 = fz_toint(fz_arrayget(index, t + 0));
			int i1 = fz_toint(fz_arrayget(index, t + 1));
			error = readnewxrefsection(xref, stm, i0, i1, w0, w1, w2);
			if (error)
			{
				fz_dropstream(stm);
				fz_dropobj(trailer);
				return fz_rethrow(error, "cannot read xref stream section");
			}
		}
	}

	fz_dropstream(stm);

	*trailerp = trailer;

	return fz_okay;
}
Exemplo n.º 10
0
fz_error *
pdf_loadtype7shade(fz_shade *shade, pdf_xref *xref, fz_obj *shading, fz_obj *ref)
{
	fz_error *error;
	fz_stream *stream;
	fz_obj *obj;

	int bpcoord;
	int bpcomp;
	int bpflag;
	int ncomp;

	float x0, x1, y0, y1;

	float c0[FZ_MAXCOLORS];
	float c1[FZ_MAXCOLORS];

	int i, n, j;
	unsigned int t;

	int flag;
	fz_point p[16];
	pdf_tensorpatch patch;

	error = nil;

	ncomp = shade->cs->n;
	bpcoord = fz_toint(fz_dictgets(shading, "BitsPerCoordinate"));
	bpcomp = fz_toint(fz_dictgets(shading, "BitsPerComponent"));
	bpflag = fz_toint(fz_dictgets(shading, "BitsPerFlag"));

	obj = fz_dictgets(shading, "Decode");
	if (fz_isarray(obj))
	{
		pdf_logshade("decode array\n");
		x0 = fz_toreal(fz_arrayget(obj, 0));
		x1 = fz_toreal(fz_arrayget(obj, 1));
		y0 = fz_toreal(fz_arrayget(obj, 2));
		y1 = fz_toreal(fz_arrayget(obj, 3));
		for (i=0; i < fz_arraylen(obj) / 2; ++i) {
			c0[i] = fz_toreal(fz_arrayget(obj, i*2+4));
			c1[i] = fz_toreal(fz_arrayget(obj, i*2+5));
		}
	}
	else {
		error = fz_throw("syntaxerror: No Decode key in Type 6 Shade");
		goto cleanup;
	}

	obj = fz_dictgets(shading, "Function");
	if (obj) {
		ncomp = 1;
		pdf_loadshadefunction(shade, xref, shading, c0[0], c1[0]);
		shade->usefunction = 1;
	} 
	else
		shade->usefunction = 0;

	shade->meshcap = 0;
	shade->mesh = nil;
	error = growshademesh(shade, 1024);
	if (error) goto cleanup;

	n = 2 + shade->cs->n;
	j = 0;

	error = pdf_openstream(&stream, xref, fz_tonum(ref), fz_togen(ref));
	if (error) goto cleanup;

	while (fz_peekbyte(stream) != EOF)
	{
		flag = getdata(stream, bpflag);

		for (i = 0; i < 16; ++i) {
			t = getdata(stream, bpcoord);
			p[i].x = x0 + (t * (x1 - x0) / (pow(2, bpcoord) - 1.));
			t = getdata(stream, bpcoord);
			p[i].y = y0 + (t * (y1 - y0) / (pow(2, bpcoord) - 1.));
		}

		for (i = 0; i < 4; ++i) {
			int k;
			for (k=0; k < ncomp; ++k) {
				t = getdata(stream, bpcomp);
				patch.color[i][k] = 
					c0[k] + (t * (c1[k] - c0[k]) / (pow(2, bpcomp) - 1.0f));
			}
		}

		patch.pole[0][0] = p[0];
		patch.pole[0][1] = p[1];
		patch.pole[0][2] = p[2];
		patch.pole[0][3] = p[3];
		patch.pole[1][3] = p[4];
		patch.pole[2][3] = p[5];
		patch.pole[3][3] = p[6];
		patch.pole[3][2] = p[7];
		patch.pole[3][1] = p[8];
		patch.pole[3][0] = p[9];
		patch.pole[2][0] = p[10];
		patch.pole[1][0] = p[11];
		patch.pole[1][1] = p[12];
		patch.pole[1][2] = p[13];
		patch.pole[2][2] = p[14];
		patch.pole[2][1] = p[15];

		j = drawpatch(patch, shade, j, ncomp, 0);
	}

	fz_dropstream(stream);

	shade->meshlen = j / n / 3;

cleanup:

	return nil;
}
Exemplo n.º 11
0
fz_error *
pdf_loadtype5shade(fz_shade *shade, pdf_xref *xref, fz_obj *shading, fz_obj *ref)
{
	fz_error *error;
	fz_stream *stream;
	fz_obj *obj;

	int bpcoord;
	int bpcomp;
	int vpr, vpc;
	int ncomp;

	float x0, x1, y0, y1;

	float c0[FZ_MAXCOLORS];
	float c1[FZ_MAXCOLORS];

	int i, n, j;
	int p, q;
	unsigned int t;

	float *x, *y, *c[FZ_MAXCOLORS];

	error = nil;

	ncomp = shade->cs->n;
	bpcoord = fz_toint(fz_dictgets(shading, "BitsPerCoordinate"));
	bpcomp = fz_toint(fz_dictgets(shading, "BitsPerComponent"));
	vpr = fz_toint(fz_dictgets(shading, "VerticesPerRow"));
	if (vpr < 2) {
		error = fz_throw("VerticesPerRow must be greater than or equal to 2");
		goto cleanup;
	}

	obj = fz_dictgets(shading, "Decode");
	if (fz_isarray(obj))
	{
		pdf_logshade("decode array\n");
		x0 = fz_toreal(fz_arrayget(obj, 0));
		x1 = fz_toreal(fz_arrayget(obj, 1));
		y0 = fz_toreal(fz_arrayget(obj, 2));
		y1 = fz_toreal(fz_arrayget(obj, 3));
		for (i=0; i < fz_arraylen(obj) / 2; ++i) {
			c0[i] = fz_toreal(fz_arrayget(obj, i*2+4));
			c1[i] = fz_toreal(fz_arrayget(obj, i*2+5));
		}
	}
	else {
		error = fz_throw("syntaxerror: No Decode key in Type 4 Shade");
		goto cleanup;
	}

	obj = fz_dictgets(shading, "Function");
	if (obj) {
		ncomp = 1;
		pdf_loadshadefunction(shade, xref, shading, c0[0], c1[0]);
		shade->usefunction = 1;
	} 
	else
		shade->usefunction = 0;

	n = 2 + shade->cs->n;
	j = 0;

#define BIGNUM 1024

	x = fz_malloc(sizeof(float) * vpr * BIGNUM);
	y = fz_malloc(sizeof(float) * vpr * BIGNUM);
	for (i = 0; i < ncomp; ++i) {
		c[i] = fz_malloc(sizeof(float) * vpr * BIGNUM);
	}
	q = 0;

	error = pdf_openstream(&stream, xref, fz_tonum(ref), fz_togen(ref));
	if (error) goto cleanup;

	while (fz_peekbyte(stream) != EOF)
	{
		for (p = 0; p < vpr; ++p) {
			int idx;
			idx = q * vpr + p;

			t = getdata(stream, bpcoord);
			x[idx] = x0 + (t * (x1 - x0) / ((float)pow(2, bpcoord) - 1));
			t = getdata(stream, bpcoord);
			y[idx] = y0 + (t * (y1 - y0) / ((float)pow(2, bpcoord) - 1));

			for (i=0; i < ncomp; ++i) {
				t = getdata(stream, bpcomp);
				c[i][idx] = c0[i] + (t * (c1[i] - c0[i]) / (float)(pow(2, bpcomp) - 1));
			}
		}
		q++;
	}

	fz_dropstream(stream);

#define ADD_VERTEX(idx) \
			{\
				int z;\
				shade->mesh[j++] = x[idx];\
				shade->mesh[j++] = y[idx];\
				for (z = 0; z < shade->cs->n; ++z) {\
					shade->mesh[j++] = c[z][idx];\
				}\
			}\

	vpc = q;

	shade->meshcap = 0;
	shade->mesh = fz_malloc(sizeof(float) * 1024);
	if (!shade) {
		error = fz_outofmem;
		goto cleanup;
	}

	j = 0;
	for (p = 0; p < vpr-1; ++p) {
		for (q = 0; q < vpc-1; ++q) {
			ADD_VERTEX(q * vpr + p);
			ADD_VERTEX(q * vpr + p + 1);
			ADD_VERTEX((q + 1) * vpr + p + 1);
			
			ADD_VERTEX(q * vpr + p);
			ADD_VERTEX((q + 1) * vpr + p + 1);
			ADD_VERTEX((q + 1) * vpr + p);
		}
	}

	shade->meshlen = j / n / 3;

	fz_free(x);
	fz_free(y);
	for (i = 0; i < ncomp; ++i) {
		fz_free(c[i]);
	}


cleanup:

	return nil;
}
fz_error
pdf_loadobjstm(pdf_xref *xref, int oid, int gen, char *buf, int cap)
{
    fz_error error;
    fz_stream *stm;
    fz_obj *objstm;
    int *oidbuf;
    int *ofsbuf;

    fz_obj *obj;
    int first;
    int count;
    int i, n;
    pdf_token_e tok;

    pdf_logxref("loadobjstm (%d %d R)\n", oid, gen);

    error = pdf_loadobject(&objstm, xref, oid, gen);
    if (error)
        return fz_rethrow(error, "cannot load object stream object");

    count = fz_toint(fz_dictgets(objstm, "N"));
    first = fz_toint(fz_dictgets(objstm, "First"));

    pdf_logxref("  count %d\n", count);

    oidbuf = fz_malloc(count * sizeof(int));
    if (!oidbuf)
    {
        error = fz_rethrow(-1, "out of memory: object id buffer");
        goto cleanupobj;
    }

    ofsbuf = fz_malloc(count * sizeof(int));
    if (!ofsbuf)
    {
        error = fz_rethrow(-1, "out of memory: offset buffer");
        goto cleanupoid;
    }

    error = pdf_openstream(&stm, xref, oid, gen);
    if (error)
    {
        error = fz_rethrow(error, "cannot open object stream");
        goto cleanupofs;
    }

    for (i = 0; i < count; i++)
    {
        error = pdf_lex(&tok, stm, buf, cap, &n);
        if (error || tok != PDF_TINT)
        {
            error = fz_rethrow(error, "corrupt object stream");
            goto cleanupstm;
        }
        oidbuf[i] = atoi(buf);

        error = pdf_lex(&tok, stm, buf, cap, &n);
        if (error || tok != PDF_TINT)
        {
            error = fz_rethrow(error, "corrupt object stream");
            goto cleanupstm;
        }
        ofsbuf[i] = atoi(buf);
    }

    error = fz_seek(stm, first, 0);
    if (error)
    {
        error = fz_rethrow(error, "cannot seek in object stream");
        goto cleanupstm;
    }

    for (i = 0; i < count; i++)
    {
        /* FIXME: seek to first + ofsbuf[i] */

        error = pdf_parsestmobj(&obj, xref, stm, buf, cap);
        if (error)
        {
            error = fz_rethrow(error, "cannot parse object %d in stream", i);
            goto cleanupstm;
        }

        if (oidbuf[i] < 1 || oidbuf[i] >= xref->len)
        {
            fz_dropobj(obj);
            error = fz_throw("object id (%d 0 R) out of range (0..%d)", oidbuf[i], xref->len - 1);
            goto cleanupstm;
        }

        if (xref->table[oidbuf[i]].obj)
            fz_dropobj(xref->table[oidbuf[i]].obj);
        xref->table[oidbuf[i]].obj = obj;
    }

    fz_dropstream(stm);
    fz_free(ofsbuf);
    fz_free(oidbuf);
    fz_dropobj(objstm);
    return fz_okay;

cleanupstm:
    fz_dropstream(stm);
cleanupofs:
    fz_free(ofsbuf);
cleanupoid:
    fz_free(oidbuf);
cleanupobj:
    fz_dropobj(objstm);
    return error; /* already rethrown */
}