Example #1
0
static void
fz_test_fill_image(fz_context *ctx, fz_device *dev_, fz_image *image, fz_matrix ctm, float alpha, const fz_color_params *color_params)
{
	fz_test_device *dev = (fz_test_device*)dev_;

	while (dev->resolved == 0) /* So we can break out */
	{
		fz_compressed_buffer *buffer;

		if (*dev->is_color || !image->colorspace || fz_colorspace_is_gray(ctx, image->colorspace))
			break;

		if ((dev->options & FZ_TEST_OPT_IMAGES) == 0)
		{
			/* Don't test every pixel. Upgrade us from "black and white" to "probably color" */
			if (*dev->is_color == 0)
				*dev->is_color = 1;
			dev->resolved = 1;
			if (dev->passthrough == NULL)
				fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation");
			break;
		}

		buffer = fz_compressed_image_buffer(ctx, image);
		if (buffer && image->bpc == 8)
		{
			fz_stream *stream = fz_open_compressed_buffer(ctx, buffer);
			fz_try(ctx)
				fz_test_fill_compressed_8bpc_image(ctx, dev, image, stream, color_params);
			fz_always(ctx)
				fz_drop_stream(ctx, stream);
			fz_catch(ctx)
				fz_rethrow(ctx);
		}
		else
		{
			fz_pixmap *pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, 0, 0);
			if (pix == NULL) /* Should never happen really, but... */
				break;

			fz_try(ctx)
				fz_test_fill_other_image(ctx, dev, pix, color_params);
			fz_always(ctx)
				fz_drop_pixmap(ctx, pix);
			fz_catch(ctx)
				fz_rethrow(ctx);
		}
		break;
	}
	if (dev->passthrough)
		fz_fill_image(ctx, dev->passthrough, image, ctm, alpha, color_params);
}
Example #2
0
static void
svg_dev_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm,
	fz_colorspace *colorspace, const float *color, float alpha)
{
	svg_device *sdev = (svg_device*)dev;
	fz_compressed_buffer *buffer;

	fz_output *out;
	fz_matrix local_ctm = *ctm;
	fz_matrix scale = { 0 };
	int mask = sdev->id++;

	scale.a = 1.0f / image->w;
	scale.d = 1.0f / image->h;

	fz_concat(&local_ctm, &scale, ctm);
	out = start_def(ctx, sdev);
	fz_printf(ctx, out, "<mask id=\"ma%d\"><image", mask);
	fz_printf(ctx, out, " width=\"%dpx\" height=\"%dpx\" xlink:href=\"data:", image->w, image->h);
	buffer = fz_compressed_image_buffer(ctx, image);
	switch (buffer == NULL ? FZ_IMAGE_JPX : buffer->params.type)
	{
	case FZ_IMAGE_JPEG:
		fz_printf(ctx, out, "image/jpeg;base64,");
		send_data_base64(ctx, out, buffer->buffer);
		break;
	case FZ_IMAGE_PNG:
		fz_printf(ctx, out, "image/png;base64,");
		send_data_base64(ctx, out, buffer->buffer);
		break;
	default:
		{
			fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, image);
			fz_printf(ctx, out, "image/png;base64,");
			send_data_base64(ctx, out, buf);
			fz_drop_buffer(ctx, buf);
			break;
		}
	}
	fz_printf(ctx, out, "\"/></mask>\n");
	out = end_def(ctx, sdev);
	fz_printf(ctx, out, "<rect x=\"0\" y=\"0\" width=\"%d\" height=\"%d\"", image->w, image->h);
	svg_dev_fill_color(ctx, sdev, colorspace, color, alpha);
	svg_dev_ctm(ctx, sdev, &local_ctm);
	fz_printf(ctx, out, " mask=\"url(#ma%d)\"/>\n", mask);
}
Example #3
0
static void
svg_dev_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha)
{
	svg_device *sdev = (svg_device*)dev;
	fz_output *out = sdev->out;
	fz_compressed_buffer *buffer;

	fz_matrix local_ctm = *ctm;
	fz_matrix scale = { 0 };

	scale.a = 1.0f / image->w;
	scale.d = 1.0f / image->h;

	fz_concat(&local_ctm, &scale, ctm);
	if (alpha != 1.0f)
		fz_printf(ctx, out, "<g opacity=\"%g\">", alpha);
	fz_printf(ctx, out, "<image");
	svg_dev_ctm(ctx, sdev, &local_ctm);
	buffer = fz_compressed_image_buffer(ctx, image);
	fz_printf(ctx, out, " width=\"%dpx\" height=\"%dpx\" xlink:href=\"data:", image->w, image->h);
	switch (buffer == NULL ? FZ_IMAGE_JPX : buffer->params.type)
	{
	case FZ_IMAGE_JPEG:
		fz_printf(ctx, out, "image/jpeg;base64,");
		send_data_base64(ctx, out, buffer->buffer);
		break;
	case FZ_IMAGE_PNG:
		fz_printf(ctx, out, "image/png;base64,");
		send_data_base64(ctx, out, buffer->buffer);
		break;
	default:
		{
			fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, image);
			fz_printf(ctx, out, "image/png;base64,");
			send_data_base64(ctx, out, buf);
			fz_drop_buffer(ctx, buf);
			break;
		}
	}
	fz_printf(ctx, out, "\"/>\n");
	if (alpha != 1.0f)
		fz_printf(ctx, out, "</g>");
}
Example #4
0
static void
fz_test_fill_image(fz_context *ctx, fz_device *dev_, fz_image *image, const fz_matrix *ctm, float alpha, const fz_color_params *color_params)
{
	fz_test_device *dev = (fz_test_device*)dev_;

	while (dev->resolved == 0) /* So we can break out */
	{
		fz_pixmap *pix;
		unsigned int count, i, k, h, sa, ss;
		unsigned char *s;
		fz_compressed_buffer *buffer;

		if (*dev->is_color || !image->colorspace || fz_colorspace_is_gray(ctx, image->colorspace))
			break;

		if ((dev->options & FZ_TEST_OPT_IMAGES) == 0)
		{
			/* Don't test every pixel. Upgrade us from "black and white" to "probably color" */
			if (*dev->is_color == 0)
				*dev->is_color = 1;
			dev->resolved = 1;
			if (dev->passthrough == NULL)
				fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation");
			break;
		}

		buffer = fz_compressed_image_buffer(ctx, image);
		if (buffer && image->bpc == 8)
		{
			fz_stream *stream = fz_open_compressed_buffer(ctx, buffer);
			count = (unsigned int)image->w * (unsigned int)image->h;
			if (image->colorspace == fz_device_rgb(ctx))
			{
				int threshold_u8 = dev->threshold * 255;
				for (i = 0; i < count; i++)
				{
					int r = fz_read_byte(ctx, stream);
					int g = fz_read_byte(ctx, stream);
					int b = fz_read_byte(ctx, stream);
					if (is_rgb_color_u8(threshold_u8, r, g, b))
					{
						*dev->is_color = 1;
						dev->resolved = 1;
						fz_drop_stream(ctx, stream);
						if (dev->passthrough == NULL)
							fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation");
						break;
					}
				}
			}
			else
			{
				fz_color_converter cc;
				unsigned int n = (unsigned int)image->n;

				fz_init_cached_color_converter(ctx, &cc, NULL, fz_device_rgb(ctx), image->colorspace, color_params);

				fz_try(ctx)
				{
					for (i = 0; i < count; i++)
					{
						float cs[FZ_MAX_COLORS];
						float ds[FZ_MAX_COLORS];

						for (k = 0; k < n; k++)
							cs[k] = fz_read_byte(ctx, stream) / 255.0f;

						cc.convert(ctx, &cc, ds, cs);

						if (is_rgb_color(dev->threshold, ds[0], ds[1], ds[2]))
						{
							*dev->is_color = 1;
							dev->resolved = 1;
							if (dev->passthrough == NULL)
							{
								fz_drop_stream(ctx, stream);
								fz_fin_cached_color_converter(ctx, &cc);
								fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation");
							}
							break;
						}
					}
				}
				fz_always(ctx)
					fz_fin_cached_color_converter(ctx, &cc);
				fz_catch(ctx)
					fz_rethrow(ctx);
			}
			fz_drop_stream(ctx, stream);
			break;
		}

		pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, 0, 0);
		if (pix == NULL) /* Should never happen really, but... */
			break;

		count = pix->w;
		h = pix->h;
		s = pix->samples;
		sa = pix->alpha;
		ss = pix->stride - pix->w * pix->n;

		if (pix->colorspace == fz_device_rgb(ctx))
		{
			int threshold_u8 = dev->threshold * 255;
			while (h--)
			{
				for (i = 0; i < count; i++)
				{
					if ((!sa || s[3] != 0) && is_rgb_color_u8(threshold_u8, s[0], s[1], s[2]))
					{
						*dev->is_color = 1;
						dev->resolved = 1;
						if (dev->passthrough == NULL)
						{
							fz_drop_pixmap(ctx, pix);
							fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation");
						}
						break;
					}
					s += 3 + sa;
				}
				s += ss;
			}
		}
		else
		{
			fz_color_converter cc;
			unsigned int n = (unsigned int)pix->n-1;

			fz_init_cached_color_converter(ctx, &cc, NULL, fz_device_rgb(ctx), pix->colorspace, color_params);

			fz_try(ctx)
			{
				while (h--)
				{
					for (i = 0; i < count; i++)
					{
						float cs[FZ_MAX_COLORS];
						float ds[FZ_MAX_COLORS];

						for (k = 0; k < n; k++)
							cs[k] = (*s++) / 255.0f;
						if (sa && *s++ == 0)
							continue;

						cc.convert(ctx, &cc, ds, cs);

						if (is_rgb_color(dev->threshold, ds[0], ds[1], ds[2]))
						{
							*dev->is_color = 1;
							dev->resolved = 1;
							if (dev->passthrough == NULL)
							{
								fz_fin_cached_color_converter(ctx, &cc);
								fz_drop_pixmap(ctx, pix);
								fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation");
							}
							break;
						}
					}
					s += ss;
				}
			}
			fz_always(ctx)
				fz_fin_cached_color_converter(ctx, &cc);
			fz_catch(ctx)
				fz_rethrow(ctx);
		}

		fz_drop_pixmap(ctx, pix);
		break;
	}
Example #5
0
static void
pdf_out_BI(fz_context *ctx, pdf_processor *proc, fz_image *img)
{
	fz_output *out = ((pdf_output_processor*)proc)->out;
	int ahx = ((pdf_output_processor*)proc)->ahxencode;
	fz_compressed_buffer *cbuf;
	fz_buffer *buf;
	int i;

	if (img == NULL)
		return;
	cbuf = fz_compressed_image_buffer(ctx, img);
	if (cbuf == NULL)
		return;
	buf = cbuf->buffer;
	if (buf == NULL)
		return;

	fz_printf(ctx, out, "BI\n");
	fz_printf(ctx, out, "/W %d\n", img->w);
	fz_printf(ctx, out, "/H %d\n", img->h);
	fz_printf(ctx, out, "/BPC %d\n", img->bpc);
	if (img->imagemask)
		fz_printf(ctx, out, "/IM true\n");
	else if (img->colorspace == fz_device_gray(ctx))
		fz_printf(ctx, out, "/CS/G\n");
	else if (img->colorspace == fz_device_rgb(ctx))
		fz_printf(ctx, out, "/CS/RGB\n");
	else if (img->colorspace == fz_device_cmyk(ctx))
		fz_printf(ctx, out, "/CS/CMYK\n");
	else if (fz_colorspace_is_indexed(ctx, img->colorspace))
		fz_printf(ctx, out, "/CS/I\n");
	if (img->interpolate)
		fz_printf(ctx, out, "/I true\n");
	fz_printf(ctx, out, "/D[");
	for (i = 0; i < img->n * 2; ++i)
	{
		if (i > 0)
			fz_putc(ctx, out, ' ');
		fz_printf(ctx, out, "%g", img->decode[i]);
	}
	fz_printf(ctx, out, "]\n");

	switch (cbuf->params.type)
	{
	default:
		fz_throw(ctx, FZ_ERROR_GENERIC, "unknown compressed buffer type");
		break;

	case FZ_IMAGE_JPEG:
		fz_printf(ctx, out, ahx ? "/F[/AHx/DCT]\n" : "/F/DCT\n");
		if (cbuf->params.u.jpeg.color_transform != -1)
			fz_printf(ctx, out, "/DP<</ColorTransform %d>>\n",
				cbuf->params.u.jpeg.color_transform);
		break;

	case FZ_IMAGE_FAX:
		fz_printf(ctx, out, ahx ? "/F[/AHx/CCF]\n/DP[null<<\n" : "/F/CCF\n/DP<<\n");
		fz_printf(ctx, out, "/K %d\n", cbuf->params.u.fax.k);
		if (cbuf->params.u.fax.columns != 1728)
			fz_printf(ctx, out, "/Columns %d\n", cbuf->params.u.fax.columns);
		if (cbuf->params.u.fax.rows > 0)
			fz_printf(ctx, out, "/Rows %d\n", cbuf->params.u.fax.rows);
		if (cbuf->params.u.fax.end_of_line)
			fz_printf(ctx, out, "/EndOfLine true\n");
		if (cbuf->params.u.fax.encoded_byte_align)
			fz_printf(ctx, out, "/EncodedByteAlign true\n");
		if (!cbuf->params.u.fax.end_of_block)
			fz_printf(ctx, out, "/EndOfBlock false\n");
		if (cbuf->params.u.fax.black_is_1)
			fz_printf(ctx, out, "/BlackIs1 true\n");
		if (cbuf->params.u.fax.damaged_rows_before_error > 0)
			fz_printf(ctx, out, "/DamagedRowsBeforeError %d\n",
				cbuf->params.u.fax.damaged_rows_before_error);
		fz_printf(ctx, out, ahx ? ">>]\n" : ">>\n");
		break;

	case FZ_IMAGE_RAW:
		if (ahx)
			fz_printf(ctx, out, "/F/AHx\n");
		break;

	case FZ_IMAGE_RLD:
		fz_printf(ctx, out, ahx ? "/F[/AHx/RL]\n" : "/F/RL\n");
		break;

	case FZ_IMAGE_FLATE:
		fz_printf(ctx, out, ahx ? "/F[/AHx/Fl]\n" : "/F/Fl\n");
		if (cbuf->params.u.flate.predictor > 1)
		{
			fz_printf(ctx, out, ahx ? "/DP[null<<\n" : "/DP<<\n");
			fz_printf(ctx, out, "/Predictor %d\n", cbuf->params.u.flate.predictor);
			if (cbuf->params.u.flate.columns != 1)
				fz_printf(ctx, out, "/Columns %d\n", cbuf->params.u.flate.columns);
			if (cbuf->params.u.flate.colors != 1)
				fz_printf(ctx, out, "/Colors %d\n", cbuf->params.u.flate.colors);
			if (cbuf->params.u.flate.bpc != 8)
				fz_printf(ctx, out, "/BitsPerComponent %d\n", cbuf->params.u.flate.bpc);
			fz_printf(ctx, out, ahx ? ">>]\n" : ">>\n");
		}
		break;

	case FZ_IMAGE_LZW:
		fz_printf(ctx, out, ahx ? "/F[/AHx/LZW]\n" : "/F/LZW\n");
		if (cbuf->params.u.lzw.predictor > 1)
		{
			fz_printf(ctx, out, ahx ? "/DP[<<null\n" : "/DP<<\n");
			fz_printf(ctx, out, "/Predictor %d\n", cbuf->params.u.lzw.predictor);
			if (cbuf->params.u.lzw.columns != 1)
				fz_printf(ctx, out, "/Columns %d\n", cbuf->params.u.lzw.columns);
			if (cbuf->params.u.lzw.colors != 1)
				fz_printf(ctx, out, "/Colors %d\n", cbuf->params.u.lzw.colors);
			if (cbuf->params.u.lzw.bpc != 8)
				fz_printf(ctx, out, "/BitsPerComponent %d\n", cbuf->params.u.lzw.bpc);
			if (cbuf->params.u.lzw.early_change != 1)
				fz_printf(ctx, out, "/EarlyChange %d\n", cbuf->params.u.lzw.early_change);
			fz_printf(ctx, out, ahx ? ">>]\n" : ">>\n");
		}
		break;
	}

	fz_printf(ctx, out, "ID\n");
	if (ahx)
	{
		size_t z;
		for (z = 0; z < buf->len; ++z)
		{
			int c = buf->data[z];
			fz_putc(ctx, out, "0123456789abcdef"[(c >> 4) & 0xf]);
			fz_putc(ctx, out, "0123456789abcdef"[c & 0xf]);
			if ((z & 31) == 31)
				fz_putc(ctx, out, '\n');
		}
		fz_putc(ctx, out, '>');
	}
	else
	{
Example #6
0
void
fz_print_stext_page_html(fz_context *ctx, fz_output *out, fz_stext_page *page)
{
	int block_n, line_n, ch_n;
	fz_stext_style *style = NULL;
	fz_stext_line *line;
	fz_stext_span *span;
	void *last_region = NULL;

	fz_printf(ctx, out, "<div class=\"page\">\n");

	for (block_n = 0; block_n < page->len; block_n++)
	{
		switch (page->blocks[block_n].type)
		{
		case FZ_PAGE_BLOCK_TEXT:
		{
			fz_stext_block * block = page->blocks[block_n].u.text;
			fz_printf(ctx, out, "<div class=\"block\"><p>\n");
			for (line_n = 0; line_n < block->len; line_n++)
			{
				int lastcol=-1;
				line = &block->lines[line_n];
				style = NULL;

				if (line->region != last_region)
				{
					if (last_region)
						fz_printf(ctx, out, "</div>");
					fz_printf(ctx, out, "<div class=\"metaline\">");
					last_region = line->region;
				}
				fz_printf(ctx, out, "<div class=\"line\"");
#ifdef DEBUG_INTERNALS
				if (line->region)
					fz_printf(ctx, out, " region=\"%x\"", line->region);
#endif
				fz_printf(ctx, out, ">");
				for (span = line->first_span; span; span = span->next)
				{
					float size = fz_matrix_expansion(&span->transform);
					float base_offset = span->base_offset / size;

					if (lastcol != span->column)
					{
						if (lastcol >= 0)
						{
							fz_printf(ctx, out, "</div>");
						}
						/* If we skipped any columns then output some spacer spans */
						while (lastcol < span->column-1)
						{
							fz_printf(ctx, out, "<div class=\"cell\"></div>");
							lastcol++;
						}
						lastcol++;
						/* Now output the span to contain this entire column */
						fz_printf(ctx, out, "<div class=\"cell\" style=\"");
						{
							fz_stext_span *sn;
							for (sn = span->next; sn; sn = sn->next)
							{
								if (sn->column != lastcol)
									break;
							}
							fz_printf(ctx, out, "width:%g%%;align:%s", span->column_width, (span->align == 0 ? "left" : (span->align == 1 ? "center" : "right")));
						}
						if (span->indent > 1)
							fz_printf(ctx, out, ";padding-left:1em;text-indent:-1em");
						if (span->indent < -1)
							fz_printf(ctx, out, ";text-indent:1em");
						fz_printf(ctx, out, "\">");
					}
#ifdef DEBUG_INTERNALS
					fz_printf(ctx, out, "<span class=\"internal_span\"");
					if (span->column)
						fz_printf(ctx, out, " col=\"%x\"", span->column);
					fz_printf(ctx, out, ">");
#endif
					if (span->spacing >= 1)
						fz_printf(ctx, out, " ");
					if (base_offset > SUBSCRIPT_OFFSET)
						fz_printf(ctx, out, "<sub>");
					else if (base_offset < SUPERSCRIPT_OFFSET)
						fz_printf(ctx, out, "<sup>");
					for (ch_n = 0; ch_n < span->len; ch_n++)
					{
						fz_stext_char *ch = &span->text[ch_n];
						if (style != ch->style)
						{
							if (style)
								fz_print_style_end(ctx, out, style);
							fz_print_style_begin(ctx, out, ch->style);
							style = ch->style;
						}

						if (ch->c == '<')
							fz_printf(ctx, out, "&lt;");
						else if (ch->c == '>')
							fz_printf(ctx, out, "&gt;");
						else if (ch->c == '&')
							fz_printf(ctx, out, "&amp;");
						else if (ch->c >= 32 && ch->c <= 127)
							fz_printf(ctx, out, "%c", ch->c);
						else
							fz_printf(ctx, out, "&#x%x;", ch->c);
					}
					if (style)
					{
						fz_print_style_end(ctx, out, style);
						style = NULL;
					}
					if (base_offset > SUBSCRIPT_OFFSET)
						fz_printf(ctx, out, "</sub>");
					else if (base_offset < SUPERSCRIPT_OFFSET)
						fz_printf(ctx, out, "</sup>");
#ifdef DEBUG_INTERNALS
					fz_printf(ctx, out, "</span>");
#endif
				}
				/* Close our floating span */
				fz_printf(ctx, out, "</div>");
				/* Close the line */
				fz_printf(ctx, out, "</div>");
				fz_printf(ctx, out, "\n");
			}
			/* Close the metaline */
			fz_printf(ctx, out, "</div>");
			last_region = NULL;
			fz_printf(ctx, out, "</p></div>\n");
			break;
		}
		case FZ_PAGE_BLOCK_IMAGE:
		{
			fz_image_block *image = page->blocks[block_n].u.image;
			fz_compressed_buffer *buffer = fz_compressed_image_buffer(ctx, image->image);
			fz_printf(ctx, out, "<img width=%d height=%d src=\"data:", image->image->w, image->image->h);
			switch (buffer == NULL ? FZ_IMAGE_JPX : buffer->params.type)
			{
			case FZ_IMAGE_JPEG:
				fz_printf(ctx, out, "image/jpeg;base64,");
				send_data_base64_stext(ctx, out, buffer->buffer);
				break;
			case FZ_IMAGE_PNG:
				fz_printf(ctx, out, "image/png;base64,");
				send_data_base64_stext(ctx, out, buffer->buffer);
				break;
			default:
				{
					fz_buffer *buf = fz_new_buffer_from_image_as_png(ctx, image->image);
					fz_printf(ctx, out, "image/png;base64,");
					send_data_base64_stext(ctx, out, buf);
					fz_drop_buffer(ctx, buf);
					break;
				}
			}
			fz_printf(ctx, out, "\">\n");
			break;
		}
		}
	}

	fz_printf(ctx, out, "</div>\n");
}
Example #7
0
pdf_obj *
pdf_add_image(fz_context *ctx, pdf_document *doc, fz_image *image)
{
	fz_pixmap *pixmap = NULL;
	pdf_obj *imobj = NULL;
	pdf_obj *dp;
	fz_buffer *buffer = NULL;
	pdf_obj *imref = NULL;
	fz_compressed_buffer *cbuffer;
	unsigned char digest[16];
	int i, n;

	/* If we can maintain compression, do so */
	cbuffer = fz_compressed_image_buffer(ctx, image);

	fz_var(pixmap);
	fz_var(buffer);
	fz_var(imobj);
	fz_var(imref);

	/* Check if the same image already exists in this doc. */
	imref = pdf_find_image_resource(ctx, doc, image, digest);
	if (imref)
		return imref;

	imobj = pdf_add_new_dict(ctx, doc, 3);
	fz_try(ctx)
	{
		dp = pdf_dict_put_dict(ctx, imobj, PDF_NAME(DecodeParms), 3);
		pdf_dict_put(ctx, imobj, PDF_NAME(Type), PDF_NAME(XObject));
		pdf_dict_put(ctx, imobj, PDF_NAME(Subtype), PDF_NAME(Image));

		if (cbuffer)
		{
			fz_compression_params *cp = &cbuffer->params;
			switch (cp ? cp->type : FZ_IMAGE_UNKNOWN)
			{
			default:
				goto raw_or_unknown_compression;
			case FZ_IMAGE_JPEG:
				if (cp->u.jpeg.color_transform != -1)
					pdf_dict_put_int(ctx, dp, PDF_NAME(ColorTransform), cp->u.jpeg.color_transform);
				pdf_dict_put(ctx, imobj, PDF_NAME(Filter), PDF_NAME(DCTDecode));
				break;
			case FZ_IMAGE_JPX:
				if (cp->u.jpx.smask_in_data)
					pdf_dict_put_int(ctx, dp, PDF_NAME(SMaskInData), cp->u.jpx.smask_in_data);
				pdf_dict_put(ctx, imobj, PDF_NAME(Filter), PDF_NAME(JPXDecode));
				break;
			case FZ_IMAGE_FAX:
				if (cp->u.fax.columns)
					pdf_dict_put_int(ctx, dp, PDF_NAME(Columns), cp->u.fax.columns);
				if (cp->u.fax.rows)
					pdf_dict_put_int(ctx, dp, PDF_NAME(Rows), cp->u.fax.rows);
				if (cp->u.fax.k)
					pdf_dict_put_int(ctx, dp, PDF_NAME(K), cp->u.fax.k);
				if (cp->u.fax.end_of_line)
					pdf_dict_put_bool(ctx, dp, PDF_NAME(EndOfLine), cp->u.fax.end_of_line);
				if (cp->u.fax.encoded_byte_align)
					pdf_dict_put_bool(ctx, dp, PDF_NAME(EncodedByteAlign), cp->u.fax.encoded_byte_align);
				if (cp->u.fax.end_of_block)
					pdf_dict_put_bool(ctx, dp, PDF_NAME(EndOfBlock), cp->u.fax.end_of_block);
				if (cp->u.fax.black_is_1)
					pdf_dict_put_bool(ctx, dp, PDF_NAME(BlackIs1), cp->u.fax.black_is_1);
				if (cp->u.fax.damaged_rows_before_error)
					pdf_dict_put_int(ctx, dp, PDF_NAME(DamagedRowsBeforeError), cp->u.fax.damaged_rows_before_error);
				pdf_dict_put(ctx, imobj, PDF_NAME(Filter), PDF_NAME(CCITTFaxDecode));
				break;
			case FZ_IMAGE_FLATE:
				if (cp->u.flate.columns)
					pdf_dict_put_int(ctx, dp, PDF_NAME(Columns), cp->u.flate.columns);
				if (cp->u.flate.colors)
					pdf_dict_put_int(ctx, dp, PDF_NAME(Colors), cp->u.flate.colors);
				if (cp->u.flate.predictor)
					pdf_dict_put_int(ctx, dp, PDF_NAME(Predictor), cp->u.flate.predictor);
				if (cp->u.flate.bpc)
					pdf_dict_put_int(ctx, dp, PDF_NAME(BitsPerComponent), cp->u.flate.bpc);
				pdf_dict_put(ctx, imobj, PDF_NAME(Filter), PDF_NAME(FlateDecode));
				pdf_dict_put_int(ctx, imobj, PDF_NAME(BitsPerComponent), image->bpc);
				break;
			case FZ_IMAGE_LZW:
				if (cp->u.lzw.columns)
					pdf_dict_put_int(ctx, dp, PDF_NAME(Columns), cp->u.lzw.columns);
				if (cp->u.lzw.colors)
					pdf_dict_put_int(ctx, dp, PDF_NAME(Colors), cp->u.lzw.colors);
				if (cp->u.lzw.predictor)
					pdf_dict_put_int(ctx, dp, PDF_NAME(Predictor), cp->u.lzw.predictor);
				if (cp->u.lzw.early_change)
					pdf_dict_put_int(ctx, dp, PDF_NAME(EarlyChange), cp->u.lzw.early_change);
				if (cp->u.lzw.bpc)
					pdf_dict_put_int(ctx, dp, PDF_NAME(BitsPerComponent), cp->u.lzw.bpc);
				pdf_dict_put(ctx, imobj, PDF_NAME(Filter), PDF_NAME(LZWDecode));
				break;
			case FZ_IMAGE_RLD:
				pdf_dict_put(ctx, imobj, PDF_NAME(Filter), PDF_NAME(RunLengthDecode));
				break;
			}

			if (!pdf_dict_len(ctx, dp))
				pdf_dict_del(ctx, imobj, PDF_NAME(DecodeParms));

			buffer = fz_keep_buffer(ctx, cbuffer->buffer);

			if (image->use_decode)
			{
				pdf_obj *ary = pdf_dict_put_array(ctx, imobj, PDF_NAME(Decode), image->n * 2);
				for (i = 0; i < image->n * 2; ++i)
					pdf_array_push_real(ctx, ary, image->decode[i]);
			}
		}
		else
		{
			unsigned int size;
			int h;
			unsigned char *d, *s;

raw_or_unknown_compression:
			/* Currently, set to maintain resolution; should we consider
			 * subsampling here according to desired output res? */
			pixmap = fz_get_pixmap_from_image(ctx, image, NULL, NULL, NULL, NULL);
			n = pixmap->n - pixmap->alpha - pixmap->s; /* number of colorants */
			if (n == 0)
				n = 1; /* treat pixmaps with only alpha or spots as grayscale */

			size = image->w * n;
			h = image->h;
			s = pixmap->samples;
			d = fz_malloc(ctx, size * h);
			buffer = fz_new_buffer_from_data(ctx, d, size * h);

			if (n == pixmap->n)
			{
				/* If we use all channels, we can copy the data as is. */
				while (h--)
				{
					memcpy(d, s, size);
					d += size;
					s += pixmap->stride;
				}
			}
			else
			{
				/* Need to remove the alpha and spot planes. */
				/* TODO: extract alpha plane to a soft mask. */
				/* TODO: convert spots to colors. */

				int line_skip = pixmap->stride - pixmap->w * pixmap->n;
				int skip = pixmap->n - n;
				while (h--)
				{
					int w = pixmap->w;
					while (w--)
					{
						int k;
						for (k = 0; k < n; ++k)
							*d++ = *s++;
						s += skip;
					}
					s += line_skip;
				}
			}
		}

		pdf_dict_put_int(ctx, imobj, PDF_NAME(Width), pixmap ? pixmap->w : image->w);
		pdf_dict_put_int(ctx, imobj, PDF_NAME(Height), pixmap ? pixmap->h : image->h);

		if (image->imagemask)
		{
			pdf_dict_put_bool(ctx, imobj, PDF_NAME(ImageMask), 1);
		}
		else
		{
			fz_colorspace *cs;

			pdf_dict_put_int(ctx, imobj, PDF_NAME(BitsPerComponent), image->bpc);

			cs = pixmap ? pixmap->colorspace : image->colorspace;
			switch (fz_colorspace_type(ctx, cs))
			{
			case FZ_COLORSPACE_INDEXED:
				{
					fz_colorspace *basecs;
					unsigned char *lookup = NULL;
					int high = 0;
					int basen;
					pdf_obj *arr;

					lookup = fz_indexed_colorspace_palette(ctx, cs, &high);
					basecs = fz_colorspace_base(ctx, cs);
					basen = fz_colorspace_n(ctx, basecs);

					arr = pdf_dict_put_array(ctx, imobj, PDF_NAME(ColorSpace), 4);

					pdf_array_push(ctx, arr, PDF_NAME(Indexed));
					switch (fz_colorspace_type(ctx, basecs))
					{
					case FZ_COLORSPACE_GRAY:
						pdf_array_push(ctx, arr, PDF_NAME(DeviceGray));
						break;
					case FZ_COLORSPACE_RGB:
						pdf_array_push(ctx, arr, PDF_NAME(DeviceRGB));
						break;
					case FZ_COLORSPACE_CMYK:
						pdf_array_push(ctx, arr, PDF_NAME(DeviceCMYK));
						break;
					default:
						// TODO: convert to RGB!
						fz_throw(ctx, FZ_ERROR_GENERIC, "only indexed Gray, RGB, and CMYK colorspaces supported");
						break;
					}

					pdf_array_push_int(ctx, arr, high);
					pdf_array_push_string(ctx, arr, (char *) lookup, basen * (high + 1));
				}
				break;
			case FZ_COLORSPACE_NONE:
			case FZ_COLORSPACE_GRAY:
				pdf_dict_put(ctx, imobj, PDF_NAME(ColorSpace), PDF_NAME(DeviceGray));
				break;
			case FZ_COLORSPACE_RGB:
				pdf_dict_put(ctx, imobj, PDF_NAME(ColorSpace), PDF_NAME(DeviceRGB));
				break;
			case FZ_COLORSPACE_CMYK:
				pdf_dict_put(ctx, imobj, PDF_NAME(ColorSpace), PDF_NAME(DeviceCMYK));
				break;
			default:
				// TODO: convert to RGB!
				fz_throw(ctx, FZ_ERROR_GENERIC, "only Gray, RGB, and CMYK colorspaces supported");
				break;
			}
		}

		if (image->mask)
		{
			pdf_dict_put_drop(ctx, imobj, PDF_NAME(SMask), pdf_add_image(ctx, doc, image->mask));
		}

		pdf_update_stream(ctx, doc, imobj, buffer, 1);

		/* Add ref to our image resource hash table. */
		imref = pdf_insert_image_resource(ctx, doc, digest, imobj);
	}
	fz_always(ctx)
	{
		fz_drop_pixmap(ctx, pixmap);
		fz_drop_buffer(ctx, buffer);
		pdf_drop_obj(ctx, imobj);
	}
	fz_catch(ctx)
		fz_rethrow(ctx);
	return imref;
}