fz_pixmap * fz_load_jxr(fz_context *ctx, unsigned char *data, size_t size) { struct info info = { 0 }; fz_pixmap *image = NULL; fz_var(image); jxr_read_image(ctx, data, size, &info, 0); image = fz_new_pixmap(ctx, info.cspace, info.width, info.height, NULL, 1); image->xres = info.xres; image->yres = info.yres; fz_try(ctx) { fz_unpack_tile(ctx, image, info.samples, fz_colorspace_n(ctx, info.cspace) + 1, 8, info.stride, 0); if (info.has_alpha && !info.has_premul) fz_premultiply_pixmap(ctx, image); } fz_always(ctx) { fz_free(ctx, info.samples); fz_drop_colorspace(ctx, info.cspace); } fz_catch(ctx) { fz_drop_pixmap(ctx, image); fz_rethrow(ctx); } return image; }
static void jxr_decode_block_alpha(jxr_image_t image, int mx, int my, int *data) { struct info *info = jxr_get_user_data(image); fz_context *ctx = info->ctx; unsigned char *p; int x, y, n; mx *= 16; my *= 16; n = fz_colorspace_n(ctx, info->cspace); for (y = 0; y < 16; y++) { if ((my + y) >= info->height) return; p = info->samples + (my + y) * info->stride + mx * (n + 1); for (x = 0; x < 16; x++) { if ((mx + x) < info->width) { jxr_unpack_alpha_sample(ctx, info, image, data, p + n); p += n + 1; } data++; } } }
static void jxr_decode_block(jxr_image_t image, int mx, int my, int *data) { struct info *info = jxr_get_user_data(image); fz_context *ctx = info->ctx; unsigned char *p; int x, y, n1; mx *= 16; my *= 16; n1 = fz_colorspace_n(ctx, info->cspace) + 1; for (y = 0; y < 16; y++) { if ((my + y) >= info->height) return; p = info->samples + (my + y) * info->stride + mx * n1; for (x = 0; x < 16; x++) { if ((mx + x) < info->width) { jxr_unpack_sample(ctx, info, image, data, p); p += n1; } data += jxr_get_IMAGE_CHANNELS(image) + jxr_get_ALPHACHANNEL_FLAG(image); data += (info->format == JXRC_FMT_32bppRGBE ? 1 : 0); } } }
static inline void jxr_unpack_sample(fz_context *ctx, struct info *info, jxr_image_t image, int *sp, unsigned char *dp) { int k, bpc, comps, alpha; float v; if (info->format == JXRC_FMT_32bppRGBE) { dp[0] = sRGB_from_scRGB(ldexpf(sp[0], sp[3] - 128 - 8)) * 255 + 0.5f; dp[1] = sRGB_from_scRGB(ldexpf(sp[1], sp[3] - 128 - 8)) * 255 + 0.5f; dp[2] = sRGB_from_scRGB(ldexpf(sp[2], sp[3] - 128 - 8)) * 255 + 0.5f; return; } if (info->format == JXRC_FMT_16bppBGR565) { dp[0] = sp[0] << 3; dp[1] = sp[1] << 2; dp[2] = sp[2] << 3; return; } comps = fz_mini(fz_colorspace_n(ctx, info->cspace), jxr_get_IMAGE_CHANNELS(image)); alpha = jxr_get_ALPHACHANNEL_FLAG(image); bpc = jxr_get_CONTAINER_BPC(image); for (k = 0; k < comps + alpha; k++) { switch (bpc) { default: fz_throw(ctx, FZ_ERROR_GENERIC, "unknown sample type: %d", bpc); case JXR_BD1WHITE1: dp[k] = sp[k] ? 255 : 0; break; case JXR_BD1BLACK1: dp[k] = sp[k] ? 0 : 255; break; case JXR_BD5: dp[k] = sp[k] << 3; break; case JXR_BD8: dp[k] = sp[k]; break; case JXR_BD10: dp[k] = sp[k] >> 2; break; case JXR_BD16: dp[k] = sp[k] >> 8; break; case JXR_BD16S: v = sp[k] * (1.0f / (1 << 13)); goto decode_float32; case JXR_BD32S: v = sp[k] * (1.0f / (1 << 24)); goto decode_float32; case JXR_BD16F: v = float32_from_float16(sp[k]); goto decode_float32; case JXR_BD32F: v = float32_from_int32_bits(sp[k]); goto decode_float32; decode_float32: if (k < comps) dp[k] = sRGB_from_scRGB(fz_clamp(v, 0, 1)) * 255 + 0.5f; else dp[k] = fz_clamp(v, 0, 1) * 255 + 0.5f; break; } } }
static size_t fz_shade_size(fz_context *ctx, fz_shade *s) { if (s == NULL) return 0; if (s->type == FZ_FUNCTION_BASED) return sizeof(*s) + sizeof(float) * s->u.f.xdivs * s->u.f.ydivs * fz_colorspace_n(ctx, s->colorspace); return sizeof(*s) + fz_compressed_buffer_size(s->buffer); }
static void pdf_sample_composite_shade_function(fz_context *ctx, fz_shade *shade, pdf_function *func, float t0, float t1) { int i, n; float t; n = fz_colorspace_n(ctx, shade->colorspace); for (i = 0; i < 256; i++) { t = t0 + (i / 255.0f) * (t1 - t0); pdf_eval_function(ctx, func, &t, 1, shade->function[i], n); shade->function[i][n] = 1; } }
static void pdf_load_function_based_shading(fz_context *ctx, pdf_document *doc, fz_shade *shade, pdf_obj *dict, pdf_function *func) { pdf_obj *obj; float x0, y0, x1, y1; float fv[2]; fz_matrix matrix; int xx, yy; float *p; int n = fz_colorspace_n(ctx, shade->colorspace); x0 = y0 = 0; x1 = y1 = 1; obj = pdf_dict_get(ctx, dict, PDF_NAME(Domain)); if (obj) { x0 = pdf_array_get_real(ctx, obj, 0); x1 = pdf_array_get_real(ctx, obj, 1); y0 = pdf_array_get_real(ctx, obj, 2); y1 = pdf_array_get_real(ctx, obj, 3); } obj = pdf_dict_get(ctx, dict, PDF_NAME(Matrix)); if (obj) pdf_to_matrix(ctx, obj, &matrix); else matrix = fz_identity; shade->u.f.matrix = matrix; shade->u.f.xdivs = FUNSEGS; shade->u.f.ydivs = FUNSEGS; shade->u.f.fn_vals = fz_malloc(ctx, (FUNSEGS+1)*(FUNSEGS+1)*n*sizeof(float)); shade->u.f.domain[0][0] = x0; shade->u.f.domain[0][1] = y0; shade->u.f.domain[1][0] = x1; shade->u.f.domain[1][1] = y1; p = shade->u.f.fn_vals; for (yy = 0; yy <= FUNSEGS; yy++) { fv[1] = y0 + (y1 - y0) * yy / FUNSEGS; for (xx = 0; xx <= FUNSEGS; xx++) { fv[0] = x0 + (x1 - x0) * xx / FUNSEGS; pdf_eval_function(ctx, func, fv, 2, p, n); p += n; } } }
static void pdf_process_extgstate(fz_context *ctx, pdf_processor *proc, pdf_csi *csi, pdf_obj *dict) { pdf_obj *obj; obj = pdf_dict_get(ctx, dict, PDF_NAME_LW); if (pdf_is_number(ctx, obj) && proc->op_w) proc->op_w(ctx, proc, pdf_to_real(ctx, obj)); obj = pdf_dict_get(ctx, dict, PDF_NAME_LC); if (pdf_is_int(ctx, obj) && proc->op_J) proc->op_J(ctx, proc, fz_clampi(pdf_to_int(ctx, obj), 0, 2)); obj = pdf_dict_get(ctx, dict, PDF_NAME_LJ); if (pdf_is_int(ctx, obj) && proc->op_j) proc->op_j(ctx, proc, fz_clampi(pdf_to_int(ctx, obj), 0, 2)); obj = pdf_dict_get(ctx, dict, PDF_NAME_ML); if (pdf_is_number(ctx, obj) && proc->op_M) proc->op_M(ctx, proc, pdf_to_real(ctx, obj)); obj = pdf_dict_get(ctx, dict, PDF_NAME_D); if (pdf_is_array(ctx, obj) && proc->op_d) { pdf_obj *dash_array = pdf_array_get(ctx, obj, 0); pdf_obj *dash_phase = pdf_array_get(ctx, obj, 1); proc->op_d(ctx, proc, dash_array, pdf_to_real(ctx, dash_phase)); } obj = pdf_dict_get(ctx, dict, PDF_NAME_RI); if (pdf_is_name(ctx, obj) && proc->op_ri) proc->op_ri(ctx, proc, pdf_to_name(ctx, obj)); obj = pdf_dict_get(ctx, dict, PDF_NAME_FL); if (pdf_is_number(ctx, obj) && proc->op_i) proc->op_i(ctx, proc, pdf_to_real(ctx, obj)); obj = pdf_dict_get(ctx, dict, PDF_NAME_Font); if (pdf_is_array(ctx, obj) && proc->op_Tf) { pdf_obj *font_ref = pdf_array_get(ctx, obj, 0); pdf_obj *font_size = pdf_array_get(ctx, obj, 1); pdf_font_desc *font = load_font_or_hail_mary(ctx, csi->doc, csi->rdb, font_ref, 0, csi->cookie); fz_try(ctx) proc->op_Tf(ctx, proc, "ExtGState", font, pdf_to_real(ctx, font_size)); fz_always(ctx) pdf_drop_font(ctx, font); fz_catch(ctx) fz_rethrow(ctx); } /* transfer functions */ obj = pdf_dict_get(ctx, dict, PDF_NAME_TR2); if (pdf_is_name(ctx, obj)) if (!pdf_name_eq(ctx, obj, PDF_NAME_Identity) && !pdf_name_eq(ctx, obj, PDF_NAME_Default)) fz_warn(ctx, "ignoring transfer function"); if (!obj) /* TR is ignored in the presence of TR2 */ { pdf_obj *tr = pdf_dict_get(ctx, dict, PDF_NAME_TR); if (pdf_is_name(ctx, tr)) if (!pdf_name_eq(ctx, tr, PDF_NAME_Identity)) fz_warn(ctx, "ignoring transfer function"); } /* transparency state */ obj = pdf_dict_get(ctx, dict, PDF_NAME_CA); if (pdf_is_number(ctx, obj) && proc->op_gs_CA) proc->op_gs_CA(ctx, proc, pdf_to_real(ctx, obj)); obj = pdf_dict_get(ctx, dict, PDF_NAME_ca); if (pdf_is_number(ctx, obj) && proc->op_gs_ca) proc->op_gs_ca(ctx, proc, pdf_to_real(ctx, obj)); obj = pdf_dict_get(ctx, dict, PDF_NAME_BM); if (pdf_is_array(ctx, obj)) obj = pdf_array_get(ctx, obj, 0); if (pdf_is_name(ctx, obj) && proc->op_gs_BM) proc->op_gs_BM(ctx, proc, pdf_to_name(ctx, obj)); obj = pdf_dict_get(ctx, dict, PDF_NAME_SMask); if (proc->op_gs_SMask) { if (pdf_is_dict(ctx, obj)) { pdf_xobject *xobj; pdf_obj *group, *s, *bc, *tr; float softmask_bc[FZ_MAX_COLORS]; fz_colorspace *colorspace; int colorspace_n = 1; int k, luminosity; fz_var(xobj); group = pdf_dict_get(ctx, obj, PDF_NAME_G); if (!group) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot load softmask xobject (%d 0 R)", pdf_to_num(ctx, obj)); xobj = pdf_load_xobject(ctx, csi->doc, group); fz_try(ctx) { colorspace = pdf_xobject_colorspace(ctx, xobj); if (colorspace) { colorspace_n = fz_colorspace_n(ctx, colorspace); fz_drop_colorspace(ctx, colorspace); } /* Default background color is black. */ for (k = 0; k < colorspace_n; k++) softmask_bc[k] = 0; /* Which in CMYK means not all zeros! This should really be * a test for subtractive color spaces, but this will have * to do for now. */ if (colorspace == fz_device_cmyk(ctx)) softmask_bc[3] = 1.0; bc = pdf_dict_get(ctx, obj, PDF_NAME_BC); if (pdf_is_array(ctx, bc)) { for (k = 0; k < colorspace_n; k++) softmask_bc[k] = pdf_to_real(ctx, pdf_array_get(ctx, bc, k)); } s = pdf_dict_get(ctx, obj, PDF_NAME_S); if (pdf_name_eq(ctx, s, PDF_NAME_Luminosity)) luminosity = 1; else luminosity = 0; tr = pdf_dict_get(ctx, obj, PDF_NAME_TR); if (tr && !pdf_name_eq(ctx, tr, PDF_NAME_Identity)) fz_warn(ctx, "ignoring transfer function"); proc->op_gs_SMask(ctx, proc, xobj, csi->rdb, softmask_bc, luminosity); } fz_always(ctx) { pdf_drop_xobject(ctx, xobj); } fz_catch(ctx) { fz_rethrow(ctx); } } else if (pdf_is_name(ctx, obj) && pdf_name_eq(ctx, obj, PDF_NAME_None)) { proc->op_gs_SMask(ctx, proc, NULL, NULL, NULL, 0); } }
static void jxr_read_image(fz_context *ctx, unsigned char *data, int size, struct info *info, int only_metadata) { jxr_container_t container; jxr_image_t image = NULL; jxr_image_t alpha = NULL; int rc, i; fz_try(ctx) { container = jxr_create_container(); rc = jxr_read_image_container_memory(container, data, size); if (rc < 0) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot read jxr image container: %s", jxr_error_string(rc)); info->xres = jxrc_width_resolution(container, 0); info->yres = jxrc_height_resolution(container, 0); info->width = jxrc_image_width(container, 0); info->height = jxrc_image_height(container, 0); info->format = jxrc_image_pixelformat(container, 0); for (i = 0; i < nelem(pixelformats); i++) if (pixelformats[i].format == info->format) { info->comps = pixelformats[i].comps; break; } if (i == nelem(pixelformats)) fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported pixel format: %u", info->format); if (info->comps == 1) info->cspace = fz_device_gray(ctx); else if (info->comps == 3) info->cspace = fz_device_rgb(ctx); else if (info->comps >= 4) info->cspace = fz_device_cmyk(ctx); info->stride = info->width * (fz_colorspace_n(ctx, info->cspace) + 1); if (!only_metadata) { unsigned long image_offset; unsigned char image_band; unsigned long alpha_offset; unsigned char alpha_band; info->ctx = ctx; info->samples = fz_malloc(ctx, info->stride * info->height); memset(info->samples, 0xff, info->stride * info->height); image_offset = jxrc_image_offset(container, 0); image_band = jxrc_image_band_presence(container, 0); alpha_offset = jxrc_alpha_offset(container, 0); alpha_band = jxrc_alpha_band_presence(container, 0); image = jxr_create_input(); jxr_set_PROFILE_IDC(image, 111); jxr_set_LEVEL_IDC(image, 255); jxr_set_pixel_format(image, info->format); jxr_set_container_parameters(image, info->format, info->width, info->height, alpha_offset, image_band, alpha_band, 0); jxr_set_user_data(image, info); jxr_set_block_output(image, jxr_decode_block); rc = jxr_read_image_bitstream_memory(image, data + image_offset, size - image_offset); if (rc < 0) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot read jxr image: %s", jxr_error_string(rc)); if (info->format == JXRC_FMT_32bppPBGRA || info->format == JXRC_FMT_64bppPRGBA || info->format == JXRC_FMT_128bppPRGBAFloat) info->has_premul = 1; if (jxr_get_ALPHACHANNEL_FLAG(image)) info->has_alpha = 1; if (alpha_offset > 0) { info->has_alpha = 1; alpha = jxr_create_input(); jxr_set_PROFILE_IDC(alpha, 111); jxr_set_LEVEL_IDC(alpha, 255); jxr_set_pixel_format(alpha, info->format); jxr_set_container_parameters(alpha, info->format, info->width, info->height, alpha_offset, image_band, alpha_band, 1); jxr_set_user_data(alpha, info); jxr_set_block_output(alpha, jxr_decode_block_alpha); rc = jxr_read_image_bitstream_memory(alpha, data + alpha_offset, size - alpha_offset); if (rc < 0) fz_throw(ctx, FZ_ERROR_GENERIC, "cannot read jxr image: %s", jxr_error_string(rc)); } } } fz_always(ctx) { if (alpha) jxr_destroy(alpha); if (image) jxr_destroy(image); jxr_destroy_container(container); } fz_catch(ctx) { fz_rethrow(ctx); } }
static fz_pixmap * pam_binary_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e, int onlymeta, const unsigned char **out) { fz_pixmap *img = NULL; int bitmap = 0; int minval = 1; int maxval = 65535; fz_var(img); p = pam_binary_read_header(ctx, pnm, p, e); if (pnm->tupletype == PAM_UNKNOWN) switch (pnm->depth) { case 1: pnm->tupletype = pnm->maxval == 1 ? PAM_BW : PAM_GRAY; break; case 2: pnm->tupletype = pnm->maxval == 1 ? PAM_BWA : PAM_GRAYA; break; case 3: pnm->tupletype = PAM_RGB; break; case 4: pnm->tupletype = PAM_CMYK; break; case 5: pnm->tupletype = PAM_CMYKA; break; default: fz_throw(ctx, FZ_ERROR_GENERIC, "cannot guess tuple type based on depth in pnm image"); } if (pnm->tupletype == PAM_BW && pnm->maxval > 1) pnm->tupletype = PAM_GRAY; else if (pnm->tupletype == PAM_GRAY && pnm->maxval == 1) pnm->tupletype = PAM_BW; else if (pnm->tupletype == PAM_BWA && pnm->maxval > 1) pnm->tupletype = PAM_GRAYA; else if (pnm->tupletype == PAM_GRAYA && pnm->maxval == 1) pnm->tupletype = PAM_BWA; switch (pnm->tupletype) { case PAM_BWA: pnm->alpha = 1; /* fallthrough */ case PAM_BW: pnm->cs = fz_device_gray(ctx); maxval = 1; bitmap = 1; break; case PAM_GRAYA: pnm->alpha = 1; /* fallthrough */ case PAM_GRAY: pnm->cs = fz_device_gray(ctx); minval = 2; break; case PAM_RGBA: pnm->alpha = 1; /* fallthrough */ case PAM_RGB: pnm->cs = fz_device_rgb(ctx); break; case PAM_CMYKA: pnm->alpha = 1; /* fallthrough */ case PAM_CMYK: pnm->cs = fz_device_cmyk(ctx); break; default: fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported tuple type"); } if (pnm->depth != fz_colorspace_n(ctx, pnm->cs) + pnm->alpha) fz_throw(ctx, FZ_ERROR_GENERIC, "depth out of tuple type range"); if (pnm->maxval < minval || pnm->maxval > maxval) fz_throw(ctx, FZ_ERROR_GENERIC, "maxval out of range"); pnm->bitdepth = bitdepth_from_maxval(pnm->maxval); if (pnm->height <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image height must be > 0"); if (pnm->width <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image width must be > 0"); if ((unsigned int)pnm->height > UINT_MAX / pnm->width / fz_colorspace_n(ctx, pnm->cs) / (pnm->bitdepth / 8 + 1)) fz_throw(ctx, FZ_ERROR_GENERIC, "image too large"); if (onlymeta) { int packed; int w, h, n; w = pnm->width; h = pnm->height; n = fz_colorspace_n(ctx, pnm->cs) + pnm->alpha; /* some encoders incorrectly pack bits into bytes and invert the image */ packed = 0; if (pnm->maxval == 1) { const unsigned char *e_packed = p + w * h * n / 8; if (e_packed < e - 1 && e_packed[0] == 'P' && e_packed[1] >= '0' && e_packed[1] <= '7') e = e_packed; if (e - p < w * h * n) packed = 1; } if (packed && e - p < w * h * n / 8) fz_throw(ctx, FZ_ERROR_GENERIC, "truncated packed image"); if (!packed && e - p < w * h * n * (pnm->maxval < 256 ? 1 : 2)) fz_throw(ctx, FZ_ERROR_GENERIC, "truncated image"); if (pnm->maxval == 255) p += n * w * h; else if (bitmap && packed) p += ((w + 7) / 8) * h; else if (bitmap) p += n * w * h; else if (pnm->maxval < 255) p += n * w * h; else p += 2 * n * w * h; } if (!onlymeta) { unsigned char *dp; int x, y, k, packed; int w, h, n; img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, NULL, pnm->alpha); fz_try(ctx) { dp = img->samples; w = img->w; h = img->h; n = img->n; /* some encoders incorrectly pack bits into bytes and invert the image */ packed = 0; if (pnm->maxval == 1) { const unsigned char *e_packed = p + w * h * n / 8; if (e_packed < e - 1 && e_packed[0] == 'P' && e_packed[1] >= '0' && e_packed[1] <= '7') e = e_packed; if (e - p < w * h * n) packed = 1; } if (packed && e - p < w * h * n / 8) fz_throw(ctx, FZ_ERROR_GENERIC, "truncated packed image"); if (!packed && e - p < w * h * n * (pnm->maxval < 256 ? 1 : 2)) fz_throw(ctx, FZ_ERROR_GENERIC, "truncated image"); if (pnm->maxval == 255) memcpy(dp, p, w * h * n); else if (bitmap && packed) { for (y = 0; y < h; y++) for (x = 0; x < w; x++) { for (k = 0; k < n; k++) { *dp++ = (*p & (1 << (7 - (x & 0x7)))) ? 0x00 : 0xff; if ((x & 0x7) == 7) p++; } if (w & 0x7) p++; } } else if (bitmap) { for (y = 0; y < h; y++) for (x = 0; x < w; x++) for (k = 0; k < n; k++) *dp++ = *p++ ? 0xff : 0x00; } else if (pnm->maxval < 255) { for (y = 0; y < h; y++) for (x = 0; x < w; x++) for (k = 0; k < n; k++) *dp++ = map_color(ctx, *p++, pnm->maxval, 255); } else { for (y = 0; y < h; y++) for (x = 0; x < w; x++) for (k = 0; k < n; k++) { *dp++ = map_color(ctx, (p[0] << 8) | p[1], pnm->maxval, 255); p += 2; } } if (pnm->alpha) fz_premultiply_pixmap(ctx, img); } fz_catch(ctx) { fz_drop_pixmap(ctx, img); fz_rethrow(ctx); } }
static fz_pixmap * pnm_binary_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e, int onlymeta, int bitmap, const unsigned char **out) { fz_pixmap *img = NULL; pnm->width = 0; p = pnm_read_number(ctx, p, e, &pnm->width); p = pnm_read_white(ctx, p, e, 0); if (bitmap) { pnm->height = 0; p = pnm_read_number(ctx, p, e, &pnm->height); p = pnm_read_white(ctx, p, e, 1); pnm->maxval = 1; } else { pnm->height = 0; p = pnm_read_number(ctx, p, e, &pnm->height); p = pnm_read_white(ctx, p, e, 0); pnm->maxval = 0; p = pnm_read_number(ctx, p, e, &pnm->maxval); p = pnm_read_white(ctx, p, e, 1); } if (pnm->maxval <= 0 || pnm->maxval >= 65536) fz_throw(ctx, FZ_ERROR_GENERIC, "maximum sample value of out range in pnm image: %d", pnm->maxval); pnm->bitdepth = bitdepth_from_maxval(pnm->maxval); if (pnm->height <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image height must be > 0"); if (pnm->width <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image width must be > 0"); if ((unsigned int)pnm->height > UINT_MAX / pnm->width / fz_colorspace_n(ctx, pnm->cs) / (pnm->bitdepth / 8 + 1)) fz_throw(ctx, FZ_ERROR_GENERIC, "image too large"); if (onlymeta) { int w = pnm->width; int h = pnm->height; int n = fz_colorspace_n(ctx, pnm->cs); if (pnm->maxval == 255) p += n * w * h; else if (bitmap) p += ((w + 7) / 8) * h; else if (pnm->maxval < 255) p += n * w * h; else p += 2 * n * w * h; } else { unsigned char *dp; int x, y, k; int w, h, n; img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, NULL, 0); dp = img->samples; w = img->w; h = img->h; n = img->n; if (pnm->maxval == 255) { memcpy(dp, p, w * h * n); p += n * w * h; } else if (bitmap) { for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { *dp++ = (*p & (1 << (7 - (x & 0x7)))) ? 0x00 : 0xff; if ((x & 0x7) == 7) p++; } if (w & 0x7) p++; } } else if (pnm->maxval < 255) { for (y = 0; y < h; y++) for (x = 0; x < w; x++) for (k = 0; k < n; k++) *dp++ = map_color(ctx, *p++, pnm->maxval, 255); } else { for (y = 0; y < h; y++) for (x = 0; x < w; x++) for (k = 0; k < n; k++) { *dp++ = map_color(ctx, (p[0] << 8) | p[1], pnm->maxval, 255); p += 2; } } } if (out) *out = p; return img; }
static fz_pixmap * pnm_ascii_read_image(fz_context *ctx, struct info *pnm, const unsigned char *p, const unsigned char *e, int onlymeta, int bitmap, const unsigned char **out) { fz_pixmap *img = NULL; p = pnm_read_number(ctx, p, e, &pnm->width); p = pnm_read_white(ctx, p, e, 0); if (bitmap) { p = pnm_read_number(ctx, p, e, &pnm->height); p = pnm_read_white(ctx, p, e, 1); pnm->maxval = 1; } else { p = pnm_read_number(ctx, p, e, &pnm->height); p = pnm_read_white(ctx, p, e, 0); p = pnm_read_number(ctx, p, e, &pnm->maxval); p = pnm_read_white(ctx, p, e, 0); } if (pnm->maxval <= 0 || pnm->maxval >= 65536) fz_throw(ctx, FZ_ERROR_GENERIC, "maximum sample value of out range in pnm image: %d", pnm->maxval); pnm->bitdepth = bitdepth_from_maxval(pnm->maxval); if (pnm->height <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image height must be > 0"); if (pnm->width <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image width must be > 0"); if ((unsigned int)pnm->height > UINT_MAX / pnm->width / fz_colorspace_n(ctx, pnm->cs) / (pnm->bitdepth / 8 + 1)) fz_throw(ctx, FZ_ERROR_GENERIC, "image too large"); if (onlymeta) { int x, y, k; int w, h, n; w = pnm->width; h = pnm->height; n = fz_colorspace_n(ctx, pnm->cs); if (bitmap) { for (y = 0; y < h; y++) for (x = -1; x < w; x++) { p = pnm_read_number(ctx, p, e, NULL); p = pnm_read_white(ctx, p, e, 0); } } else { for (y = 0; y < h; y++) for (x = 0; x < w; x++) for (k = 0; k < n; k++) { p = pnm_read_number(ctx, p, e, NULL); p = pnm_read_white(ctx, p, e, 0); } } } else { unsigned char *dp; int x, y, k; int w, h, n; img = fz_new_pixmap(ctx, pnm->cs, pnm->width, pnm->height, NULL, 0); dp = img->samples; w = img->w; h = img->h; n = img->n; if (bitmap) { for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { int v = 0; p = pnm_read_number(ctx, p, e, &v); p = pnm_read_white(ctx, p, e, 0); *dp++ = v ? 0x00 : 0xff; } } } else { for (y = 0; y < h; y++) for (x = 0; x < w; x++) for (k = 0; k < n; k++) { int v = 0; p = pnm_read_number(ctx, p, e, &v); p = pnm_read_white(ctx, p, e, 0); v = fz_clampi(v, 0, pnm->maxval); *dp++ = map_color(ctx, v, pnm->maxval, 255); } } } if (out) *out = p; return img; }
static fz_shade * pdf_load_shading_dict(fz_context *ctx, pdf_document *doc, pdf_obj *dict, const fz_matrix *transform) { fz_shade *shade = NULL; pdf_function *func[FZ_MAX_COLORS] = { NULL }; pdf_obj *obj; int funcs = 0; int type = 0; int i, in, out, n; fz_var(shade); fz_var(func); fz_var(funcs); fz_var(type); fz_try(ctx) { shade = fz_malloc_struct(ctx, fz_shade); FZ_INIT_STORABLE(shade, 1, fz_drop_shade_imp); shade->type = FZ_MESH_TYPE4; shade->use_background = 0; shade->use_function = 0; shade->matrix = *transform; shade->bbox = fz_infinite_rect; shade->colorspace = NULL; funcs = 0; obj = pdf_dict_get(ctx, dict, PDF_NAME(ShadingType)); type = pdf_to_int(ctx, obj); obj = pdf_dict_get(ctx, dict, PDF_NAME(ColorSpace)); if (!obj) fz_throw(ctx, FZ_ERROR_SYNTAX, "shading colorspace is missing"); shade->colorspace = pdf_load_colorspace(ctx, obj); n = fz_colorspace_n(ctx, shade->colorspace); obj = pdf_dict_get(ctx, dict, PDF_NAME(Background)); if (obj) { shade->use_background = 1; for (i = 0; i < n; i++) shade->background[i] = pdf_array_get_real(ctx, obj, i); } obj = pdf_dict_get(ctx, dict, PDF_NAME(BBox)); if (pdf_is_array(ctx, obj)) pdf_to_rect(ctx, obj, &shade->bbox); obj = pdf_dict_get(ctx, dict, PDF_NAME(Function)); if (pdf_is_dict(ctx, obj)) { funcs = 1; if (type == 1) in = 2; else in = 1; out = n; func[0] = pdf_load_function(ctx, obj, in, out); if (!func[0]) fz_throw(ctx, FZ_ERROR_SYNTAX, "cannot load shading function (%d 0 R)", pdf_to_num(ctx, obj)); } else if (pdf_is_array(ctx, obj)) { funcs = pdf_array_len(ctx, obj); if (funcs != 1 && funcs != n) { funcs = 0; fz_throw(ctx, FZ_ERROR_SYNTAX, "incorrect number of shading functions"); } if (funcs > FZ_MAX_COLORS) { funcs = 0; fz_throw(ctx, FZ_ERROR_SYNTAX, "too many shading functions"); } if (type == 1) in = 2; else in = 1; out = 1; for (i = 0; i < funcs; i++) { func[i] = pdf_load_function(ctx, pdf_array_get(ctx, obj, i), in, out); if (!func[i]) fz_throw(ctx, FZ_ERROR_SYNTAX, "cannot load shading function (%d 0 R)", pdf_to_num(ctx, obj)); } } else if (type < 4) { /* Functions are compulsory for types 1,2,3 */ fz_throw(ctx, FZ_ERROR_SYNTAX, "cannot load shading function (%d 0 R)", pdf_to_num(ctx, obj)); } shade->type = type; switch (type) { case 1: pdf_load_function_based_shading(ctx, doc, shade, dict, func[0]); break; case 2: pdf_load_linear_shading(ctx, doc, shade, dict, funcs, func); break; case 3: pdf_load_radial_shading(ctx, doc, shade, dict, funcs, func); break; case 4: pdf_load_type4_shade(ctx, doc, shade, dict, funcs, func); break; case 5: pdf_load_type5_shade(ctx, doc, shade, dict, funcs, func); break; case 6: pdf_load_type6_shade(ctx, doc, shade, dict, funcs, func); break; case 7: pdf_load_type7_shade(ctx, doc, shade, dict, funcs, func); break; default: fz_throw(ctx, FZ_ERROR_SYNTAX, "unknown shading type: %d", type); } } fz_always(ctx) { for (i = 0; i < funcs; i++) pdf_drop_function(ctx, func[i]); } fz_catch(ctx) { fz_drop_shade(ctx, shade); fz_rethrow(ctx); } return shade; }
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; 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))); indexed = 0; use_colorkey = 0; if (imagemask) bpc = 1; if (w <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image width is zero (or less)"); if (h <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image height is zero (or less)"); if (bpc <= 0) fz_throw(ctx, FZ_ERROR_GENERIC, "image depth is zero (or less)"); if (bpc > 16) fz_throw(ctx, FZ_ERROR_GENERIC, "image depth is too large: %d", bpc); if (w > (1 << 16)) fz_throw(ctx, FZ_ERROR_GENERIC, "image is too wide"); if (h > (1 << 16)) fz_throw(ctx, FZ_ERROR_GENERIC, "image is too high"); 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; } 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) || fz_colorspace_is_lab_icc(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_geta(ctx, dict, PDF_NAME(SMask), 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] = pdf_array_get_real(ctx, obj, i) * 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. */ buffer = pdf_load_compressed_stream(ctx, doc, pdf_to_num(ctx, dict)); image = fz_new_image_from_compressed_buffer(ctx, w, h, bpc, colorspace, 96, 96, interpolate, imagemask, decode, use_colorkey ? colorkey : NULL, buffer, mask); image->invert_cmyk_jpeg = 0; } 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); image->invert_cmyk_jpeg = 0; pdf_load_compressed_inline_image(ctx, doc, dict, stride * h, cstm, indexed, (fz_compressed_image *)image); } } 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; }
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; }