Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/source/pdf/pdf-image.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/source/pdf/pdf-image.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,828 @@ +// Copyright (C) 2004-2025 Artifex Software, Inc. +// +// This file is part of MuPDF. +// +// MuPDF is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License +// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> +// +// Alternative licensing terms are available from the licensor. +// For commercial licensing, see <https://www.artifex.com/> or contact +// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, +// CA 94129, USA, for further information. + +#include "mupdf/fitz.h" +#include "mupdf/pdf.h" + +#include <string.h> + +static fz_image *pdf_load_jpx(fz_context *ctx, pdf_document *doc, pdf_obj *dict, int forcemask); + +static fz_image * +pdf_load_jpx_imp(fz_context *ctx, pdf_document *doc, pdf_obj *rdb, pdf_obj *dict, fz_stream *cstm, int forcemask) +{ + fz_image *image = pdf_load_jpx(ctx, doc, dict, forcemask); + + if (forcemask) + { + fz_pixmap_image *cimg = (fz_pixmap_image *)image; + fz_pixmap *mask_pixmap; + fz_pixmap *tile = fz_pixmap_image_tile(ctx, cimg); + + if (tile->n != 1) + { + fz_pixmap *gray = fz_convert_pixmap(ctx, tile, fz_device_gray(ctx), NULL, NULL, fz_default_color_params, 0); + fz_drop_pixmap(ctx, tile); + tile = gray; + } + + mask_pixmap = fz_alpha_from_gray(ctx, tile); + fz_drop_pixmap(ctx, tile); + fz_set_pixmap_image_tile(ctx, cimg, mask_pixmap); + } + + return image; +} + +static fz_image * +pdf_load_image_imp(fz_context *ctx, pdf_document *doc, pdf_obj *rdb, pdf_obj *dict, fz_stream *cstm, int forcemask) +{ + fz_image *image = NULL; + pdf_obj *obj, *res; + + int w, h, bpc, n; + int imagemask; + int interpolate; + int indexed; + fz_image *mask = NULL; /* explicit mask/soft mask image */ + int use_colorkey = 0; + fz_colorspace *colorspace = NULL; + float decode[FZ_MAX_COLORS * 2]; + int colorkey[FZ_MAX_COLORS * 2]; + int stride; + pdf_obj *intent; + + int i; + fz_compressed_buffer *buffer; + + /* special case for JPEG2000 images */ + if (pdf_is_jpx_image(ctx, dict)) + return pdf_load_jpx_imp(ctx, doc, rdb, dict, cstm, forcemask); + + w = pdf_to_int(ctx, pdf_dict_geta(ctx, dict, PDF_NAME(Width), PDF_NAME(W))); + h = pdf_to_int(ctx, pdf_dict_geta(ctx, dict, PDF_NAME(Height), PDF_NAME(H))); + bpc = pdf_to_int(ctx, pdf_dict_geta(ctx, dict, PDF_NAME(BitsPerComponent), PDF_NAME(BPC))); + if (bpc == 0) + bpc = 8; + imagemask = pdf_to_bool(ctx, pdf_dict_geta(ctx, dict, PDF_NAME(ImageMask), PDF_NAME(IM))); + interpolate = pdf_to_bool(ctx, pdf_dict_geta(ctx, dict, PDF_NAME(Interpolate), PDF_NAME(I))); + intent = pdf_dict_get(ctx, dict, PDF_NAME(Intent)); + + indexed = 0; + use_colorkey = 0; + + if (imagemask) + bpc = 1; + + if (w <= 0) + fz_throw(ctx, FZ_ERROR_SYNTAX, "image width is zero (or less)"); + if (h <= 0) + fz_throw(ctx, FZ_ERROR_SYNTAX, "image height is zero (or less)"); + if (bpc <= 0) + fz_throw(ctx, FZ_ERROR_SYNTAX, "image depth is zero (or less)"); + if (bpc > 16) + fz_throw(ctx, FZ_ERROR_SYNTAX, "image depth is too large: %d", bpc); + if (SIZE_MAX / w < (size_t)(bpc+7)/8) + fz_throw(ctx, FZ_ERROR_SYNTAX, "image is too large"); + if (SIZE_MAX / h < w * (size_t)((bpc+7)/8)) + fz_throw(ctx, FZ_ERROR_SYNTAX, "image is too large"); + + fz_var(mask); + fz_var(image); + fz_var(colorspace); + + fz_try(ctx) + { + obj = pdf_dict_geta(ctx, dict, PDF_NAME(ColorSpace), PDF_NAME(CS)); + if (obj && !imagemask && !forcemask) + { + /* colorspace resource lookup is only done for inline images */ + if (pdf_is_name(ctx, obj)) + { + res = pdf_dict_get(ctx, pdf_dict_get(ctx, rdb, PDF_NAME(ColorSpace)), obj); + if (res) + obj = res; + } + + colorspace = pdf_load_colorspace(ctx, obj); + indexed = fz_colorspace_is_indexed(ctx, colorspace); + + n = fz_colorspace_n(ctx, colorspace); + } + else + { + n = 1; + } + + if (SIZE_MAX / n < h * ((size_t)w) * ((bpc+7)/8)) + fz_throw(ctx, FZ_ERROR_SYNTAX, "image is too large"); + + obj = pdf_dict_geta(ctx, dict, PDF_NAME(Decode), PDF_NAME(D)); + if (obj) + { + for (i = 0; i < n * 2; i++) + decode[i] = pdf_array_get_real(ctx, obj, i); + } + else if (fz_colorspace_is_lab(ctx, colorspace)) + { + decode[0] = 0; + decode[1] = 100; + decode[2] = -128; + decode[3] = 127; + decode[4] = -128; + decode[5] = 127; + } + else + { + float maxval = indexed ? (1 << bpc) - 1 : 1; + for (i = 0; i < n * 2; i++) + decode[i] = i & 1 ? maxval : 0; + } + + obj = pdf_dict_get(ctx, dict, PDF_NAME(SMask)); + if (!pdf_is_dict(ctx, obj)) + obj = pdf_dict_get(ctx, dict, PDF_NAME(Mask)); + if (pdf_is_dict(ctx, obj)) + { + /* Not allowed for inline images or soft masks */ + if (cstm) + fz_warn(ctx, "Ignoring invalid inline image soft mask"); + else if (forcemask) + fz_warn(ctx, "Ignoring recursive image soft mask"); + else + { + mask = pdf_load_image_imp(ctx, doc, rdb, obj, NULL, 1); + obj = pdf_dict_get(ctx, obj, PDF_NAME(Matte)); + if (pdf_is_array(ctx, obj)) + { + use_colorkey = 1; + for (i = 0; i < n; i++) + colorkey[i] = fz_clamp(pdf_array_get_real(ctx, obj, i), 0, 1) * 255; + } + } + } + else if (pdf_is_array(ctx, obj)) + { + use_colorkey = 1; + for (i = 0; i < n * 2; i++) + { + if (!pdf_is_int(ctx, pdf_array_get(ctx, obj, i))) + { + fz_warn(ctx, "invalid value in color key mask"); + use_colorkey = 0; + } + colorkey[i] = pdf_array_get_int(ctx, obj, i); + } + } + + /* Do we load from a ref, or do we load an inline stream? */ + if (cstm == NULL) + { + /* Just load the compressed image data now and we can decode it on demand. */ + size_t worst_case = w * (size_t)h; + worst_case = (worst_case * bpc + 7) >> 3; + if (colorspace) + worst_case *= colorspace->n; + buffer = pdf_load_compressed_stream(ctx, doc, pdf_to_num(ctx, dict), worst_case); + image = fz_new_image_from_compressed_buffer(ctx, w, h, bpc, colorspace, 96, 96, interpolate, imagemask, decode, use_colorkey ? colorkey : NULL, buffer, mask); + } + else + { + /* Inline stream */ + stride = (w * n * bpc + 7) / 8; + image = fz_new_image_from_compressed_buffer(ctx, w, h, bpc, colorspace, 96, 96, interpolate, imagemask, decode, use_colorkey ? colorkey : NULL, NULL, mask); + pdf_load_compressed_inline_image(ctx, doc, dict, stride * h, cstm, indexed, (fz_compressed_image *)image); + } + if (pdf_name_eq(ctx, intent, PDF_NAME(Perceptual))) + image->has_intent = 1, image->intent = FZ_RI_PERCEPTUAL; + else if (pdf_name_eq(ctx, intent, PDF_NAME(AbsoluteColorimetric))) + image->has_intent = 1, image->intent = FZ_RI_ABSOLUTE_COLORIMETRIC; + else if (pdf_name_eq(ctx, intent, PDF_NAME(RelativeColorimetric))) + image->has_intent = 1, image->intent = FZ_RI_RELATIVE_COLORIMETRIC; + else if (pdf_name_eq(ctx, intent, PDF_NAME(Saturation))) + image->has_intent = 1, image->intent = FZ_RI_SATURATION; + } + fz_always(ctx) + { + fz_drop_colorspace(ctx, colorspace); + fz_drop_image(ctx, mask); + } + fz_catch(ctx) + { + fz_drop_image(ctx, image); + fz_rethrow(ctx); + } + return image; +} + +fz_image * +pdf_load_inline_image(fz_context *ctx, pdf_document *doc, pdf_obj *rdb, pdf_obj *dict, fz_stream *file) +{ + return pdf_load_image_imp(ctx, doc, rdb, dict, file, 0); +} + +int +pdf_is_jpx_image(fz_context *ctx, pdf_obj *dict) +{ + pdf_obj *filter; + int i, n; + + filter = pdf_dict_get(ctx, dict, PDF_NAME(Filter)); + if (pdf_name_eq(ctx, filter, PDF_NAME(JPXDecode))) + return 1; + n = pdf_array_len(ctx, filter); + for (i = 0; i < n; i++) + if (pdf_name_eq(ctx, pdf_array_get(ctx, filter, i), PDF_NAME(JPXDecode))) + return 1; + return 0; +} + +static fz_image * +pdf_load_jpx(fz_context *ctx, pdf_document *doc, pdf_obj *dict, int forcemask) +{ + fz_buffer *buf = NULL; + fz_colorspace *colorspace = NULL; + fz_pixmap *pix = NULL; + pdf_obj *obj; + fz_image *mask = NULL; + fz_image *img = NULL; + + fz_var(pix); + fz_var(buf); + fz_var(colorspace); + fz_var(mask); + + buf = pdf_load_stream(ctx, dict); + + /* FIXME: We can't handle decode arrays for indexed images currently */ + fz_try(ctx) + { + unsigned char *data; + size_t len; + + obj = pdf_dict_get(ctx, dict, PDF_NAME(ColorSpace)); + if (obj) + colorspace = pdf_load_colorspace(ctx, obj); + + len = fz_buffer_storage(ctx, buf, &data); + pix = fz_load_jpx(ctx, data, len, colorspace); + + obj = pdf_dict_geta(ctx, dict, PDF_NAME(SMask), PDF_NAME(Mask)); + if (pdf_is_dict(ctx, obj)) + { + if (forcemask) + fz_warn(ctx, "Ignoring recursive JPX soft mask"); + else + mask = pdf_load_image_imp(ctx, doc, NULL, obj, NULL, 1); + } + + obj = pdf_dict_geta(ctx, dict, PDF_NAME(Decode), PDF_NAME(D)); + if (obj && !fz_colorspace_is_indexed(ctx, colorspace)) + { + float decode[FZ_MAX_COLORS * 2]; + int i; + + for (i = 0; i < pix->n * 2; i++) + decode[i] = pdf_array_get_real(ctx, obj, i); + + fz_decode_tile(ctx, pix, decode); + } + + img = fz_new_image_from_pixmap(ctx, pix, mask); + } + fz_always(ctx) + { + fz_drop_image(ctx, mask); + fz_drop_pixmap(ctx, pix); + fz_drop_colorspace(ctx, colorspace); + fz_drop_buffer(ctx, buf); + } + fz_catch(ctx) + { + fz_morph_error(ctx, FZ_ERROR_FORMAT, FZ_ERROR_SYNTAX); + fz_morph_error(ctx, FZ_ERROR_LIBRARY, FZ_ERROR_SYNTAX); + fz_rethrow(ctx); + } + + return img; +} + +fz_image * +pdf_load_image(fz_context *ctx, pdf_document *doc, pdf_obj *dict) +{ + fz_image *image; + + if ((image = pdf_find_item(ctx, fz_drop_image_imp, dict)) != NULL) + return image; + + image = pdf_load_image_imp(ctx, doc, NULL, dict, NULL, 0); + pdf_store_item(ctx, dict, image, fz_image_size(ctx, image)); + return image; +} + +struct jbig2_segment_header { + int number; + int flags; + /* referred-to-segment numbers */ + int page; + uint32_t length; +}; + +/* coverity[-tainted_data_return] */ +static uint32_t getu32(const unsigned char *data) +{ + return ((uint32_t)data[0]<<24) | ((uint32_t)data[1]<<16) | ((uint32_t)data[2]<<8) | (uint32_t)data[3]; +} + +static size_t +pdf_parse_jbig2_segment_header(fz_context *ctx, + const unsigned char *data, const unsigned char *end, + struct jbig2_segment_header *info) +{ + uint32_t rts; + size_t n = 5; + + if (data + 11 > end) return 0; + + info->number = getu32(data); + info->flags = data[4]; + + rts = (data[5] >> 5) & 0x7; + if (rts == 7) + { + rts = getu32(data+5) & 0x1FFFFFFF; + n += 4 + (rts + 1) / 8; + } + else + { + n += 1; + } + + if (info->number <= 256) + n += rts; + else if (info->number <= 65536) + n += rts * 2; + else + n += rts * 4; + + if (info->flags & 0x40) + { + if (data + n + 4 > end) return 0; + info->page = getu32(data+n); + n += 4; + } + else + { + if (data + n + 1 > end) return 0; + info->page = data[n]; + n += 1; + } + + if (data + n + 4 > end) return 0; + info->length = getu32(data+n); + return n + 4; +} + +static void +pdf_copy_jbig2_segments(fz_context *ctx, fz_buffer *output, const unsigned char *data, size_t size, int page) +{ + struct jbig2_segment_header info; + const unsigned char *end = data + size; + size_t n; + int type; + + while (data < end) + { + n = pdf_parse_jbig2_segment_header(ctx, data, end, &info); + if (n == 0) + fz_throw(ctx, FZ_ERROR_FORMAT, "truncated jbig2 segment header"); + + /* omit end of page, end of file, and segments for other pages */ + type = (info.flags & 63); + if (type == 49 || type == 51 || (info.page > 0 && info.page != page)) + { + data += n; + data += info.length; + } + else + { + fz_append_data(ctx, output, data, n); + data += n; + if (data + info.length > end) + fz_throw(ctx, FZ_ERROR_FORMAT, "truncated jbig2 segment data"); + fz_append_data(ctx, output, data, info.length); + data += info.length; + } + } +} + +static void +pdf_copy_jbig2_random_segments(fz_context *ctx, fz_buffer *output, const unsigned char *data, size_t size, int page) +{ + struct jbig2_segment_header info; + const unsigned char *header = data; + const unsigned char *header_end; + const unsigned char *end = data + size; + size_t n; + int type; + + /* Skip headers until end-of-file segment is found. */ + while (data < end) + { + n = pdf_parse_jbig2_segment_header(ctx, data, end, &info); + if (n == 0) + fz_throw(ctx, FZ_ERROR_FORMAT, "truncated jbig2 segment header"); + data += n; + if ((info.flags & 63) == 51) + break; + } + if (data >= end) + fz_throw(ctx, FZ_ERROR_FORMAT, "truncated jbig2 segment header"); + + /* Copy segment headers and segment data */ + header_end = data; + while (data < end && header < header_end) + { + n = pdf_parse_jbig2_segment_header(ctx, header, header_end, &info); + if (n == 0) + fz_throw(ctx, FZ_ERROR_FORMAT, "truncated jbig2 segment header"); + + /* omit end of page, end of file, and segments for other pages */ + type = (info.flags & 63); + if (type == 49 || type == 51 || (info.page > 0 && info.page != page)) + { + header += n; + data += info.length; + } + else + { + fz_append_data(ctx, output, header, n); + header += n; + if (data + info.length > end) + fz_throw(ctx, FZ_ERROR_FORMAT, "truncated jbig2 segment data"); + fz_append_data(ctx, output, data, info.length); + data += info.length; + } + } +} + +static fz_buffer * +pdf_jbig2_stream_from_file(fz_context *ctx, fz_buffer *input, fz_jbig2_globals *globals_, int page) +{ + fz_buffer *globals = fz_jbig2_globals_data(ctx, globals_); + size_t globals_size = globals ? globals->len : 0; + fz_buffer *output; + int flags; + size_t header = 9; + + if (input->len < 9) + return NULL; /* not enough data! */ + flags = input->data[8]; + if ((flags & 2) == 0) + { + if (input->len < 13) + return NULL; /* not enough data! */ + header = 13; + } + + output = fz_new_buffer(ctx, input->len + globals_size); + fz_try(ctx) + { + if (globals_size > 0) + fz_append_buffer(ctx, output, globals); + if ((flags & 1) == 0) + pdf_copy_jbig2_random_segments(ctx, output, input->data + header, input->len - header, page); + else + pdf_copy_jbig2_segments(ctx, output, input->data + header, input->len - header, page); + } + fz_catch(ctx) + { + fz_drop_buffer(ctx, output); + fz_rethrow(ctx); + } + + return output; +} + +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; + fz_compressed_buffer *cbuffer; + fz_pixmap *smask_pixmap = NULL; + fz_image *smask_image = NULL; + int i, n; + + fz_var(pixmap); + fz_var(buffer); + fz_var(imobj); + fz_var(smask_pixmap); + fz_var(smask_image); + + pdf_begin_operation(ctx, doc, "Add image"); + + fz_try(ctx) + { + /* If we can maintain compression, do so */ + cbuffer = fz_compressed_image_buffer(ctx, image); + + imobj = pdf_add_new_dict(ctx, doc, 3); + + 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->type) + { + default: + goto unknown_compression; + case FZ_IMAGE_RAW: + break; + case FZ_IMAGE_JPEG: + pdf_dict_put(ctx, imobj, PDF_NAME(Filter), PDF_NAME(DCTDecode)); + if (cp->u.jpeg.color_transform >= 0) + pdf_dict_put_int(ctx, dp, PDF_NAME(ColorTransform), cp->u.jpeg.color_transform); + if (cp->u.jpeg.invert_cmyk && image->n == 4) + { + pdf_obj *arr; + arr = pdf_dict_put_array(ctx, imobj, PDF_NAME(Decode), 8); + pdf_array_push_int(ctx, arr, 1); + pdf_array_push_int(ctx, arr, 0); + pdf_array_push_int(ctx, arr, 1); + pdf_array_push_int(ctx, arr, 0); + pdf_array_push_int(ctx, arr, 1); + pdf_array_push_int(ctx, arr, 0); + pdf_array_push_int(ctx, arr, 1); + pdf_array_push_int(ctx, arr, 0); + } + 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_JBIG2: + if (cp->u.jbig2.embedded && cp->u.jbig2.globals) + { + pdf_obj *globals_ref = pdf_add_new_dict(ctx, doc, 1); + pdf_dict_put_drop(ctx, dp, PDF_NAME(JBIG2Globals), globals_ref); + pdf_update_stream(ctx, doc, globals_ref, fz_jbig2_globals_data(ctx, cp->u.jbig2.globals), 0); + } + else + { + buffer = pdf_jbig2_stream_from_file(ctx, cbuffer->buffer, + cp->u.jbig2.globals, + 1); + if (!buffer) + goto unknown_compression; + } + pdf_dict_put(ctx, imobj, PDF_NAME(Filter), PDF_NAME(JBIG2Decode)); + 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)); + break; + case FZ_IMAGE_BROTLI: + if (cp->u.brotli.columns) + pdf_dict_put_int(ctx, dp, PDF_NAME(Columns), cp->u.brotli.columns); + if (cp->u.brotli.colors) + pdf_dict_put_int(ctx, dp, PDF_NAME(Colors), cp->u.brotli.colors); + if (cp->u.brotli.predictor) + pdf_dict_put_int(ctx, dp, PDF_NAME(Predictor), cp->u.brotli.predictor); + if (cp->u.brotli.bpc) + pdf_dict_put_int(ctx, dp, PDF_NAME(BitsPerComponent), cp->u.brotli.bpc); + pdf_dict_put(ctx, imobj, PDF_NAME(Filter), PDF_NAME(BrotliDecode)); + 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)); + + pdf_dict_put_int(ctx, imobj, PDF_NAME(BitsPerComponent), image->bpc); + pdf_dict_put_int(ctx, imobj, PDF_NAME(Width), image->w); + pdf_dict_put_int(ctx, imobj, PDF_NAME(Height), image->h); + + if (!buffer) + 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 + { +unknown_compression: + + 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 */ + + pdf_dict_put_int(ctx, imobj, PDF_NAME(Width), pixmap->w); + pdf_dict_put_int(ctx, imobj, PDF_NAME(Height), pixmap->h); + + if (fz_is_pixmap_monochrome(ctx, pixmap)) + { + int stride = (image->w + 7) / 8; + int h = pixmap->h; + int w = pixmap->w; + unsigned char *s = pixmap->samples; + unsigned char *d = fz_calloc(ctx, h, stride); + buffer = fz_new_buffer_from_data(ctx, d, (size_t)h * stride); + + pdf_dict_put_int(ctx, imobj, PDF_NAME(BitsPerComponent), 1); + + while (h--) + { + int x; + for (x = 0; x < w; ++x) + if (s[x] > 0) + d[x>>3] |= 1 << (7 - (x & 7)); + s += pixmap->stride; + d += stride; + } + } + else + { + size_t size = (size_t)pixmap->w * n; + int h = pixmap->h; + unsigned char *s = pixmap->samples; + unsigned char *d = Memento_label(fz_malloc(ctx, size * h), "pdf_image_samples"); + buffer = fz_new_buffer_from_data(ctx, d, size * h); + + pdf_dict_put_int(ctx, imobj, PDF_NAME(BitsPerComponent), 8); + + 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 + { + size_t line_skip; + int skip; + + /* Need to extract the alpha into a SMask and remove spot planes. */ + /* TODO: convert spots to colors. */ + + if (pixmap->alpha && !image->mask) + { + smask_pixmap = fz_new_pixmap_from_alpha_channel(ctx, pixmap); + smask_image = fz_new_image_from_pixmap(ctx, smask_pixmap, NULL); + pdf_dict_put_drop(ctx, imobj, PDF_NAME(SMask), pdf_add_image(ctx, doc, smask_image)); + fz_drop_image(ctx, smask_image); + smask_image = NULL; + fz_drop_pixmap(ctx, smask_pixmap); + smask_pixmap = NULL; + } + + line_skip = pixmap->stride - pixmap->w * (size_t)pixmap->n; + skip = pixmap->n - n; + if (pixmap->alpha) + { + int n1 = pixmap->n-1; + while (h--) + { + int w = pixmap->w; + while (w--) + { + int a = s[n1]; + int inva = a ? 255 * 256 / a : 0; + int k; + for (k = 0; k < n; k++) + *d++ = (*s++ * inva) >> 8; + s += skip; + } + s += line_skip; + } + } + else + { + while (h--) + { + int w = pixmap->w; + while (w--) + { + int k; + for (k = 0; k < n; ++k) + *d++ = *s++; + s += skip; + } + s += line_skip; + } + } + } + } + } + + if (image->imagemask) + { + pdf_dict_put_bool(ctx, imobj, PDF_NAME(ImageMask), 1); + } + else + { + fz_colorspace *cs = pixmap ? pixmap->colorspace : image->colorspace; + pdf_dict_put_drop(ctx, imobj, PDF_NAME(ColorSpace), pdf_add_colorspace(ctx, doc, cs)); + } + + if (image->mask) + { + if (image->mask->imagemask) + pdf_dict_put_drop(ctx, imobj, PDF_NAME(Mask), pdf_add_image(ctx, doc, image->mask)); + else + pdf_dict_put_drop(ctx, imobj, PDF_NAME(SMask), pdf_add_image(ctx, doc, image->mask)); + } + + pdf_update_stream(ctx, doc, imobj, buffer, 1); + pdf_end_operation(ctx, doc); + } + fz_always(ctx) + { + fz_drop_image(ctx, smask_image); + fz_drop_pixmap(ctx, smask_pixmap); + fz_drop_pixmap(ctx, pixmap); + fz_drop_buffer(ctx, buffer); + } + fz_catch(ctx) + { + pdf_drop_obj(ctx, imobj); + pdf_abandon_operation(ctx, doc); + fz_rethrow(ctx); + } + return imobj; +}
