// Copyright (C) 2004-2021 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 // // Alternative licensing terms are available from the licensor. // For commercial licensing, see or contact // Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, // CA 94945, U.S.A., +1(415)492-9861, for further information. #include "mupdf/fitz.h" #include "mupdf/pdf.h" #include #include #include typedef struct { void *opaque; pdf_recolor_vertex *recolor; fz_colorspace *dst_cs; fz_colorspace *src_cs; int funcs; } recolor_details; #define FUNSEGS 64 /* size of sampled mesh for function-based shadings */ #define FUNBPS 8 /* Bits per sample in output functions */ static void fz_recolor_shade_type1(fz_context *ctx, pdf_obj *shade, pdf_function **func, recolor_details *rd) { float x0 = 0; float y0 = 0; float x1 = 1; float y1 = 1; float in[FZ_MAX_COLORS] = { 0 }; float out[(FUNSEGS+1)*(FUNSEGS+1)*FZ_MAX_COLORS]; float *p; float fv[2]; int xx, yy; pdf_obj *obj; int n_in = rd->src_cs->n; int n_out = rd->dst_cs->n; pdf_obj *fun_obj = NULL; float range[FZ_MAX_COLORS]; int i; pdf_document *doc = pdf_get_bound_document(ctx, shade); pdf_obj *ref = NULL; fz_buffer *buf = NULL; fz_output *output = NULL; obj = pdf_dict_get(ctx, shade, 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); } if (rd->funcs != 1 && rd->funcs != n_in) { fz_throw(ctx, FZ_ERROR_GENERIC, "Unexpected function-arity."); } /* Sample the function, rewriting it. */ for (i = 0; i < n_out; i++) { range[2 * i] = FLT_MAX; range[2 * i + 1] = -FLT_MAX; } p = out; 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; if (rd->funcs == 1) pdf_eval_function(ctx, func[0], fv, 2, in, n_in); else { int zz; for (zz = 0; zz < n_in; zz++) pdf_eval_function(ctx, func[zz], fv, 2, &in[zz], 1); } rd->recolor(ctx, rd->opaque, rd->dst_cs, p, rd->src_cs, in); for (i = 0; i < n_out; i++) { if (range[2 * i] > p[i]) range[2 * i] = p[i]; if (range[2 * i + 1] < p[i]) range[2 * i + 1] = p[i]; } p += n_out; } } /* Now write the function out again. */ fun_obj = pdf_new_dict(ctx, doc, 3); pdf_dict_put_int(ctx, fun_obj, PDF_NAME(FunctionType), 0); /* Domain */ obj = pdf_dict_put_array(ctx, fun_obj, PDF_NAME(Domain), 4); pdf_array_push_real(ctx, obj, x0); pdf_array_push_real(ctx, obj, x1); pdf_array_push_real(ctx, obj, y0); pdf_array_push_real(ctx, obj, y1); /* Range */ obj = pdf_dict_put_array(ctx, fun_obj, PDF_NAME(Range), 4); for (i = 0; i < 2*n_out; i++) pdf_array_push_real(ctx, obj, range[i]); /* Size */ obj = pdf_dict_put_array(ctx, fun_obj, PDF_NAME(Size), 2); pdf_array_push_int(ctx, obj, FUNSEGS+1); pdf_array_push_int(ctx, obj, FUNSEGS+1); /* BitsPerSample */ pdf_dict_put_int(ctx, fun_obj, PDF_NAME(BitsPerSample), FUNBPS); buf = fz_new_buffer(ctx, 1); output = fz_new_output_with_buffer(ctx, buf); p = out; for (yy = 0; yy <= FUNSEGS; yy++) { for (xx = 0; xx <= FUNSEGS; xx++) { for (i = 0; i < n_out; i++) { float v = p[i]; float d = range[2 * i + 1] - range[2 * i]; int iv; v -= range[2 * i]; if (d != 0) v = v * ((1<src_cs); int n_out = fz_colorspace_n(ctx, rd->dst_cs); float localp[256*FZ_MAX_COLORS]; float *q = localp; float p[FZ_MAX_COLORS]; pdf_obj *fun_obj = NULL; pdf_document *doc = pdf_get_bound_document(ctx, shade); pdf_obj *obj; float t0 = 0; float t1 = 1; float range[FZ_MAX_COLORS]; pdf_obj *ref = NULL; fz_buffer *buf = NULL; fz_output *output = NULL; int t; obj = pdf_dict_get(ctx, shade, PDF_NAME(Domain)); if (obj) { t0 = pdf_array_get_real(ctx, obj, 0); t1 = pdf_array_get_real(ctx, obj, 1); } for (i = 0; i < n_out; i++) { range[2 * i] = FLT_MAX; range[2 * i + 1] = -FLT_MAX; } for (t = 0; t < 256; t++) { for (i = 0; i < n_in; i++) p[i] = samples[t][i]; rd->recolor(ctx, rd->opaque, rd->dst_cs, q, rd->src_cs, p); for (i = 0; i < n_out; i++) { if (range[2 * i] > q[i]) range[2 * i] = q[i]; if (range[2 * i + 1] < q[i]) range[2 * i + 1] = q[i]; } q += n_out; } fz_var(ref); fz_var(output); /* Now write the function out again. */ fun_obj = pdf_new_dict(ctx, doc, 3); fz_try(ctx) { pdf_dict_put_int(ctx, fun_obj, PDF_NAME(FunctionType), 0); /* Domain */ obj = pdf_dict_put_array(ctx, fun_obj, PDF_NAME(Domain), 2); pdf_array_push_real(ctx, obj, t0); pdf_array_push_real(ctx, obj, t1); /* Range */ obj = pdf_dict_put_array(ctx, fun_obj, PDF_NAME(Range), 2 * n_out); for (i = 0; i < 2 * n_out; i++) pdf_array_push_real(ctx, obj, range[i]); obj = pdf_dict_put_array(ctx, fun_obj, PDF_NAME(Size), 1); pdf_array_push_int(ctx, obj, 256); pdf_dict_put_int(ctx, fun_obj, PDF_NAME(BitsPerSample), FUNBPS); buf = fz_new_buffer(ctx, 1); output = fz_new_output_with_buffer(ctx, buf); q = localp; for (t = 0; t < 256; t++) { for (i = 0; i < n_out; i++) { float v = q[i]; float d = range[2 * i + 1] - range[2 * i]; int iv; v -= range[2 * i]; if (d != 0) v = v * ((1< max) val = max; val -= min; if (max != min) val /= (max - min); /* Now 0 <= val <= 1 */ fz_write_bits(ctx, out, (int)(val * bitscale), bits); } typedef struct { float *p; int len; int max; int pos; } float_queue; static void float_queue_push(fz_context *ctx, float_queue *p, float f) { if (p->len == p->max) { int new_max = p->max * 2; if (new_max == 0) new_max = 32; p->p = fz_realloc(ctx, p->p, sizeof(float) * new_max); p->max = new_max; } p->p[p->len++] = f; } static float float_queue_pop(fz_context *ctx, float_queue *p) { return p->p[p->pos++]; } static void float_queue_drop(fz_context *ctx, float_queue *p) { fz_free(ctx, p->p); } static void read_decode(fz_context *ctx, pdf_obj *shade, int n_in, float *c_min, float *c_max, int n_out, float *d_min, float *d_max) { int i; pdf_obj *obj = pdf_dict_get(ctx, shade, PDF_NAME(Decode)); for (i = 0; i < n_in; i++) { c_min[i] = pdf_array_get_int(ctx, obj, 2 * i + 4); c_max[i] = pdf_array_get_int(ctx, obj, 2 * i + 5); } for (i = 0; i < n_out; i++) { d_min[i] = FLT_MAX; d_max[i] = -FLT_MAX; } } static void rewrite_decode(fz_context *ctx, pdf_obj *shade, int n_out, float *d_min, float *d_max) { int i; pdf_obj *obj = pdf_keep_obj(ctx, pdf_dict_get(ctx, shade, PDF_NAME(Decode))); pdf_obj *obj2; fz_try(ctx) { obj2 = pdf_dict_put_array(ctx, shade, PDF_NAME(Decode), 4); for (i = 0; i < 4; i++) { pdf_array_push(ctx, obj2, pdf_array_get(ctx, obj, i)); } for (i = 0; i < n_out; i++) { pdf_array_push_real(ctx, obj2, d_min[i]); pdf_array_push_real(ctx, obj2, d_max[i]); } } fz_always(ctx) pdf_drop_obj(ctx, obj); fz_catch(ctx) fz_rethrow(ctx); } static void fz_recolor_shade_type4(fz_context *ctx, pdf_obj *shade, recolor_details *rd) { fz_stream *stream; int i, n_in = rd->src_cs->n; int n_out = rd->dst_cs->n; int bpflag = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerFlag)); int bpcoord = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerCoordinate)); int bpcomp = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerComponent)); pdf_document *doc = pdf_get_bound_document(ctx, shade); float c[FZ_MAX_COLORS]; float d[FZ_MAX_COLORS]; float c_min[FZ_MAX_COLORS]; float c_max[FZ_MAX_COLORS]; float d_min[FZ_MAX_COLORS]; float d_max[FZ_MAX_COLORS]; fz_buffer *outbuf = NULL; fz_output *out = NULL; float_queue fq = { 0 }; fz_var(outbuf); fz_var(out); read_decode(ctx, shade, n_in, c_min, c_max, n_out, d_min, d_max); stream = pdf_open_stream(ctx, shade); fz_try(ctx) { while (!fz_is_eof_bits(ctx, stream)) { /* flag */ (void)fz_read_bits(ctx, stream, bpflag); /* x_bits */ (void)fz_read_bits(ctx, stream, bpcoord); /* y_bits */ (void)fz_read_bits(ctx, stream, bpcoord); for (i = 0; i < n_in; i++) c[i] = read_sample(ctx, stream, bpcomp, c_min[i], c_max[i]); rd->recolor(ctx, rd->opaque, rd->dst_cs, d, rd->src_cs, c); for (i = 0; i < n_out; i++) { if (d[i] < d_min[i]) d_min[i] = d[i]; if (d[i] > d_max[i]) d_max[i] = d[i]; float_queue_push(ctx, &fq, d[i]); } } fz_drop_stream(ctx, stream); stream = NULL; rewrite_decode(ctx, shade, n_out, d_min, d_max); stream = pdf_open_stream(ctx, shade); outbuf = fz_new_buffer(ctx, 1); out = fz_new_output_with_buffer(ctx, outbuf); while (!fz_is_eof_bits(ctx, stream)) { unsigned int flag = fz_read_bits(ctx, stream, bpflag); unsigned int x_bits = fz_read_bits(ctx, stream, bpcoord); unsigned int y_bits = fz_read_bits(ctx, stream, bpcoord); for (i = 0; i < n_in; i++) (void)fz_read_bits(ctx, stream, bpcomp); fz_write_bits(ctx, out, flag, bpflag); fz_write_bits(ctx, out, x_bits, bpcoord); fz_write_bits(ctx, out, y_bits, bpcoord); for (i = 0; i < n_out; i++) { float f = float_queue_pop(ctx, &fq); write_sample(ctx, out, 8, d_min[i], d_max[i], f); } } fz_write_bits_sync(ctx, out); fz_close_output(ctx, out); pdf_dict_put_int(ctx, shade, PDF_NAME(BitsPerComponent), 8); pdf_update_stream(ctx, doc, shade, outbuf, 0); } fz_always(ctx) { float_queue_drop(ctx, &fq); fz_drop_stream(ctx, stream); fz_drop_output(ctx, out); fz_drop_buffer(ctx, outbuf); } fz_catch(ctx) { fz_rethrow(ctx); } } static void fz_recolor_shade_type5(fz_context *ctx, pdf_obj *shade, recolor_details *rd) { fz_stream *stream; int i, k, n_in = rd->src_cs->n; int n_out = rd->dst_cs->n; int bpcoord = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerCoordinate)); int bpcomp = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerComponent)); int vprow = pdf_dict_get_int(ctx, shade, PDF_NAME(VerticesPerRow)); pdf_document *doc = pdf_get_bound_document(ctx, shade); float c[FZ_MAX_COLORS]; float d[FZ_MAX_COLORS]; float c_min[FZ_MAX_COLORS]; float c_max[FZ_MAX_COLORS]; float d_min[FZ_MAX_COLORS]; float d_max[FZ_MAX_COLORS]; fz_buffer *outbuf = NULL; fz_output *out = NULL; float_queue fq = { 0 }; fz_var(outbuf); fz_var(out); read_decode(ctx, shade, n_in, c_min, c_max, n_out, d_min, d_max); stream = pdf_open_stream(ctx, shade); fz_try(ctx) { while (!fz_is_eof_bits(ctx, stream)) { for (i = 0; i < vprow; i++) { /* x_bits */ (void)fz_read_bits(ctx, stream, bpcoord); /* y_bits */ (void)fz_read_bits(ctx, stream, bpcoord); for (k = 0; k < n_in; k++) c[k] = read_sample(ctx, stream, bpcomp, c_min[k], c_max[k]); rd->recolor(ctx, rd->opaque, rd->dst_cs, d, rd->src_cs, c); for (k = 0; k < n_out; k++) { if (d[k] < d_min[k]) d_min[k] = d[k]; if (d[k] > d_max[k]) d_max[k] = d[k]; float_queue_push(ctx, &fq, d[k]); } } } fz_drop_stream(ctx, stream); stream = NULL; rewrite_decode(ctx, shade, n_out, d_min, d_max); stream = pdf_open_stream(ctx, shade); outbuf = fz_new_buffer(ctx, 1); out = fz_new_output_with_buffer(ctx, outbuf); while (!fz_is_eof_bits(ctx, stream)) { for (i = 0; i < vprow; i++) { unsigned int x_bits = fz_read_bits(ctx, stream, bpcoord); unsigned int y_bits = fz_read_bits(ctx, stream, bpcoord); for (k = 0; k < n_in; k++) (void)fz_read_bits(ctx, stream, bpcomp); fz_write_bits(ctx, out, x_bits, bpcoord); fz_write_bits(ctx, out, y_bits, bpcoord); for (k = 0; k < n_out; k++) { float f = float_queue_pop(ctx, &fq); write_sample(ctx, out, 8, d_min[k], d_max[k], f); } } } fz_write_bits_sync(ctx, out); fz_close_output(ctx, out); pdf_dict_put_int(ctx, shade, PDF_NAME(BitsPerComponent), 8); pdf_update_stream(ctx, doc, shade, outbuf, 0); } fz_always(ctx) { float_queue_drop(ctx, &fq); fz_drop_stream(ctx, stream); fz_drop_output(ctx, out); fz_drop_buffer(ctx, outbuf); } fz_catch(ctx) { fz_rethrow(ctx); } } static void fz_recolor_shade_type6(fz_context *ctx, pdf_obj *shade, recolor_details *rd) { fz_stream *stream; int i, k, n_in = rd->src_cs->n; int n_out = rd->dst_cs->n; int bpflag = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerFlag)); int bpcoord = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerCoordinate)); int bpcomp = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerComponent)); pdf_document *doc = pdf_get_bound_document(ctx, shade); float c[FZ_MAX_COLORS]; float d[FZ_MAX_COLORS]; float c_min[FZ_MAX_COLORS]; float c_max[FZ_MAX_COLORS]; float d_min[FZ_MAX_COLORS]; float d_max[FZ_MAX_COLORS]; fz_buffer *outbuf = NULL; fz_output *out = NULL; float_queue fq = { 0 }; fz_var(outbuf); fz_var(out); read_decode(ctx, shade, n_in, c_min, c_max, n_out, d_min, d_max); stream = pdf_open_stream(ctx, shade); fz_try(ctx) { while (!fz_is_eof_bits(ctx, stream)) { int startcolor; int startpt; int flag = fz_read_bits(ctx, stream, bpflag); if (flag == 0) { startpt = 0; startcolor = 0; } else { startpt = 4; startcolor = 2; } for (i = startpt; i < 12; i++) { unsigned int x_bits = fz_read_bits(ctx, stream, bpcoord); unsigned int y_bits = fz_read_bits(ctx, stream, bpcoord); fz_write_bits(ctx, out, x_bits, bpcoord); fz_write_bits(ctx, out, y_bits, bpcoord); } for (i = startcolor; i < 4; i++) { for (k = 0; k < n_in; k++) c[k] = read_sample(ctx, stream, bpcomp, c_min[k], c_max[k]); rd->recolor(ctx, rd->opaque, rd->dst_cs, d, rd->src_cs, c); for (k = 0; k < n_out; k++) { if (d[k] < d_min[k]) d_min[k] = d[k]; if (d[k] > d_max[k]) d_max[k] = d[k]; float_queue_push(ctx, &fq, d[k]); } } } fz_drop_stream(ctx, stream); stream = NULL; rewrite_decode(ctx, shade, n_out, d_min, d_max); stream = pdf_open_stream(ctx, shade); outbuf = fz_new_buffer(ctx, 1); out = fz_new_output_with_buffer(ctx, outbuf); while (!fz_is_eof_bits(ctx, stream)) { int startcolor; int startpt; int flag = fz_read_bits(ctx, stream, bpflag); fz_write_bits(ctx, out, flag, bpflag); if (flag == 0) { startpt = 0; startcolor = 0; } else { startpt = 4; startcolor = 2; } for (i = startpt; i < 12; i++) { unsigned int x_bits = fz_read_bits(ctx, stream, bpcoord); unsigned int y_bits = fz_read_bits(ctx, stream, bpcoord); fz_write_bits(ctx, out, x_bits, bpcoord); fz_write_bits(ctx, out, y_bits, bpcoord); } for (i = startcolor; i < 4; i++) { for (k = 0; k < n_in; k++) (void)fz_read_bits(ctx, stream, bpcomp); for (k = 0; k < n_out; k++) { float f = float_queue_pop(ctx, &fq); write_sample(ctx, out, 8, d_min[k], d_max[k], f); } } } fz_write_bits_sync(ctx, out); fz_close_output(ctx, out); pdf_dict_put_int(ctx, shade, PDF_NAME(BitsPerComponent), 8); pdf_update_stream(ctx, doc, shade, outbuf, 0); } fz_always(ctx) { float_queue_drop(ctx, &fq); fz_drop_stream(ctx, stream); fz_drop_output(ctx, out); fz_drop_buffer(ctx, outbuf); } fz_catch(ctx) { fz_rethrow(ctx); } } static void fz_recolor_shade_type7(fz_context *ctx, pdf_obj *shade, recolor_details *rd) { fz_stream *stream; int i, k, n_in = rd->src_cs->n; int n_out = rd->dst_cs->n; int bpflag = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerFlag)); int bpcoord = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerCoordinate)); int bpcomp = pdf_dict_get_int(ctx, shade, PDF_NAME(BitsPerComponent)); pdf_document *doc = pdf_get_bound_document(ctx, shade); float c[FZ_MAX_COLORS]; float d[FZ_MAX_COLORS]; float c_min[FZ_MAX_COLORS]; float c_max[FZ_MAX_COLORS]; float d_min[FZ_MAX_COLORS]; float d_max[FZ_MAX_COLORS]; fz_buffer *outbuf = NULL; fz_output *out = NULL; float_queue fq = { 0 }; fz_var(outbuf); fz_var(out); read_decode(ctx, shade, n_in, c_min, c_max, n_out, d_min, d_max); stream = pdf_open_stream(ctx, shade); fz_try(ctx) { while (!fz_is_eof_bits(ctx, stream)) { int startcolor; int startpt; int flag = fz_read_bits(ctx, stream, bpflag); if (flag == 0) { startpt = 0; startcolor = 0; } else { startpt = 4; startcolor = 2; } for (i = startpt; i < 16; i++) { /* x_bits */ (void)fz_read_bits(ctx, stream, bpcoord); /* y_bits */ (void)fz_read_bits(ctx, stream, bpcoord); } for (i = startcolor; i < 4; i++) { for (k = 0; k < n_in; k++) c[k] = read_sample(ctx, stream, bpcomp, c_min[k], c_max[k]); rd->recolor(ctx, rd->opaque, rd->dst_cs, d, rd->src_cs, c); for (k = 0; k < n_out; k++) { if (d[k] < d_min[k]) d_min[k] = d[k]; if (d[k] > d_max[k]) d_max[k] = d[k]; float_queue_push(ctx, &fq, d[k]); } } } fz_drop_stream(ctx, stream); stream = NULL; rewrite_decode(ctx, shade, n_out, d_min, d_max); stream = pdf_open_stream(ctx, shade); outbuf = fz_new_buffer(ctx, 1); out = fz_new_output_with_buffer(ctx, outbuf); while (!fz_is_eof_bits(ctx, stream)) { int startcolor; int startpt; int flag = fz_read_bits(ctx, stream, bpflag); fz_write_bits(ctx, out, flag, bpflag); if (flag == 0) { startpt = 0; startcolor = 0; } else { startpt = 4; startcolor = 2; } for (i = startpt; i < 16; i++) { unsigned int x_bits = fz_read_bits(ctx, stream, bpcoord); unsigned int y_bits = fz_read_bits(ctx, stream, bpcoord); fz_write_bits(ctx, out, x_bits, bpcoord); fz_write_bits(ctx, out, y_bits, bpcoord); } for (i = startcolor; i < 4; i++) { for (k = 0; k < n_in; k++) (void)fz_read_bits(ctx, stream, bpcomp); for (k = 0; k < n_out; k++) { float f = float_queue_pop(ctx, &fq); write_sample(ctx, out, 8, d_min[k], d_max[k], f); } } } fz_write_bits_sync(ctx, out); fz_close_output(ctx, out); pdf_dict_put_int(ctx, shade, PDF_NAME(BitsPerComponent), 8); pdf_update_stream(ctx, doc, shade, outbuf, 0); } fz_always(ctx) { float_queue_drop(ctx, &fq); fz_drop_stream(ctx, stream); fz_drop_output(ctx, out); fz_drop_buffer(ctx, outbuf); } fz_catch(ctx) { fz_rethrow(ctx); } } pdf_obj * pdf_new_colorspace(fz_context *ctx, fz_colorspace *cs) { switch (fz_colorspace_type(ctx, cs)) { case FZ_COLORSPACE_GRAY: return PDF_NAME(DeviceGray); case FZ_COLORSPACE_RGB: return PDF_NAME(DeviceRGB); case FZ_COLORSPACE_CMYK: return PDF_NAME(DeviceCMYK); default: fz_throw(ctx, FZ_ERROR_GENERIC, "Unimplemented colorspace"); } return NULL; } pdf_obj * pdf_recolor_shade(fz_context *ctx, pdf_obj *shade, pdf_shade_recolorer *reshade, void *opaque) { recolor_details rd; fz_colorspace *src_cs; pdf_obj *background, *new_bg = NULL; pdf_obj *function; pdf_obj *rewritten = NULL; pdf_obj *obj; int type, i; pdf_function *func[FZ_MAX_COLORS] = { NULL }; float d0, d1; float samples[256][FZ_MAX_COLORS + 1]; pdf_document *doc = pdf_get_bound_document(ctx, shade); src_cs = pdf_load_colorspace(ctx, pdf_dict_get(ctx, shade, PDF_NAME(ColorSpace))); fz_var(rewritten); rd.funcs = 0; fz_try(ctx) { rd.recolor = reshade(ctx, opaque, src_cs, &rd.dst_cs); if (rd.recolor == NULL) break; rd.src_cs = src_cs; rd.opaque = opaque; rewritten = pdf_deep_copy_obj(ctx, shade); type = pdf_dict_get_int(ctx, shade, PDF_NAME(ShadingType)); pdf_dict_put_drop(ctx, rewritten, PDF_NAME(ColorSpace), pdf_new_colorspace(ctx, rd.dst_cs)); background = pdf_dict_get(ctx, shade, PDF_NAME(Background)); if (background) { int i, n = pdf_array_len(ctx, background); float bg[FZ_MAX_COLORS]; float nbg[FZ_MAX_COLORS]; if (n > FZ_MAX_COLORS) fz_throw(ctx, FZ_ERROR_GENERIC, "Too many background components"); if (n != src_cs->n) fz_throw(ctx, FZ_ERROR_GENERIC, "Wrong background dimension"); for (i = 0; i < n; i++) bg[i] = pdf_array_get_real(ctx, background, i); rd.recolor(ctx, rd.opaque, rd.dst_cs, nbg, src_cs, bg); new_bg = pdf_dict_put_array(ctx, rewritten, PDF_NAME(Background), rd.dst_cs->n); for (i = 0; i < n; i++) pdf_array_put(ctx, new_bg, i, pdf_new_real(ctx, bg[i])); pdf_dict_put(ctx, rewritten, PDF_NAME(Background), new_bg); } d0 = 0; d1 = 1; obj = pdf_dict_get(ctx, shade, PDF_NAME(Domain)); if (obj) { d0 = pdf_array_get_real(ctx, obj, 0); d1 = pdf_array_get_real(ctx, obj, 1); } function = pdf_dict_get(ctx, shade, PDF_NAME(Function)); if (pdf_is_dict(ctx, function)) { rd.funcs = 1; func[0] = pdf_load_function(ctx, function, type == 1 ? 2 : 1, src_cs->n); if (!func[0]) fz_throw(ctx, FZ_ERROR_SYNTAX, "cannot load shading function (%d 0 R)", pdf_to_num(ctx, obj)); if (type != 1) pdf_sample_shade_function(ctx, samples, src_cs->n, 1, func, d0, d1); } else if (pdf_is_array(ctx, function)) { int in, i; rd.funcs = pdf_array_len(ctx, function); if (rd.funcs != 1 && rd.funcs != src_cs->n) fz_throw(ctx, FZ_ERROR_SYNTAX, "incorrect number of shading functions"); if (rd.funcs > FZ_MAX_COLORS) fz_throw(ctx, FZ_ERROR_SYNTAX, "too many shading functions"); if (type == 1) in = 2; else in = 1; for (i = 0; i < rd.funcs; i++) { func[i] = pdf_load_function(ctx, pdf_array_get(ctx, function, i), in, 1); if (!func[i]) fz_throw(ctx, FZ_ERROR_SYNTAX, "cannot load shading function (%d 0 R)", pdf_to_num(ctx, obj)); } if (type != 1) pdf_sample_shade_function(ctx, samples, src_cs->n, rd.funcs, func, d0, d1); } 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)); } /* For function based shadings, we rewrite the 2d function. */ if (type == 1) { fz_recolor_shade_type1(ctx, rewritten, func, &rd); break; } /* For all other function based shadings, we just rewrite the 1d function. */ if (rd.funcs) { fz_recolor_shade_function(ctx, rewritten, samples, &rd); break; } /* From here on in, we're changing the mesh, which means altering a stream. * We'll need to be an indirect for that to work. */ obj = pdf_add_object(ctx, doc, rewritten); pdf_drop_obj(ctx, rewritten); rewritten = obj; switch (type) { case FZ_FUNCTION_BASED: /* Can never reach here. */ break; case FZ_LINEAR: case FZ_RADIAL: fz_throw(ctx, FZ_ERROR_GENERIC, "Linear/Radial shadings must use functions"); break; case FZ_MESH_TYPE4: fz_recolor_shade_type4(ctx, rewritten, &rd); break; case FZ_MESH_TYPE5: fz_recolor_shade_type5(ctx, rewritten, &rd); break; case FZ_MESH_TYPE6: fz_recolor_shade_type6(ctx, rewritten, &rd); break; case FZ_MESH_TYPE7: fz_recolor_shade_type7(ctx, rewritten, &rd); break; default: fz_throw(ctx, FZ_ERROR_GENERIC, "Unexpected mesh type %d\n", type); } } fz_always(ctx) { for (i = 0; i < rd.funcs; i++) pdf_drop_function(ctx, func[i]); fz_drop_colorspace(ctx, src_cs); } fz_catch(ctx) { pdf_drop_obj(ctx, rewritten); fz_rethrow(ctx); } return rewritten ? rewritten : shade; }