Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/source/pdf/pdf-op-color.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-op-color.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,1874 @@ +// Copyright (C) 2004-2024 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> + +enum { + UNMARKED_STROKE = 1, + UNMARKED_FILL = 2 +}; + +typedef struct gstate_stack +{ + struct gstate_stack *next; + pdf_obj *cs_stroke; + pdf_obj *cs_fill; + int unmarked; + fz_matrix ctm; +} gstate_stack; + +typedef struct resources_stack +{ + struct resources_stack *next; + pdf_obj *old_rdb; + pdf_obj *new_rdb; +} resources_stack; + +typedef struct +{ + pdf_obj *cs; + float color[FZ_MAX_COLORS]; +} cs_color; + +#define MAX_REWRITTEN_NAME 32 + +typedef struct +{ + pdf_obj *im_obj; + fz_image *after; + char name[MAX_REWRITTEN_NAME]; +} rewritten_image; + +typedef struct +{ + int max; + int len; + rewritten_image *res; +} rewritten_images; + +typedef struct +{ + pdf_obj *before; + fz_shade *after; + char name[MAX_REWRITTEN_NAME]; +} rewritten_shade; + +typedef struct +{ + int max; + int len; + rewritten_shade *res; +} rewritten_shades; + +typedef struct +{ + pdf_processor super; + pdf_document *doc; + int structparents; + pdf_processor *chain; + pdf_filter_options *global_options; + pdf_color_filter_options *options; + resources_stack *rstack; + gstate_stack *gstate; + cs_color *stroke; + cs_color *fill; + rewritten_images images; + rewritten_shades shades; +} pdf_color_processor; + +static void +push_rewritten_image(fz_context *ctx, pdf_color_processor *p, pdf_obj *im_obj, fz_image *after, char *name) +{ + rewritten_images *list = &p->images; + + if (list->max == list->len) + { + int new_max = list->max * 2; + if (new_max == 0) + new_max = 32; + list->res = fz_realloc(ctx, list->res, sizeof(*list->res) * new_max); + list->max = new_max; + } + list->res[list->len].im_obj = pdf_keep_obj(ctx, im_obj); + list->res[list->len].after = fz_keep_image(ctx, after); + memcpy(list->res[list->len].name, name, MAX_REWRITTEN_NAME); + list->len++; +} + +static fz_image * +find_rewritten_image(fz_context *ctx, pdf_color_processor *p, pdf_obj *im_obj, char *name) +{ + rewritten_images *list = &p->images; + int i; + + for (i = 0; i < list->len; i++) + if (list->res[i].im_obj == im_obj) + { + memcpy(name, list->res[i].name, MAX_REWRITTEN_NAME); + return list->res[i].after; + } + + return NULL; +} + +static void +drop_rewritten_images(fz_context *ctx, pdf_color_processor *p) +{ + rewritten_images *list = &p->images; + int i; + + for (i = 0; i < list->len; i++) + { + pdf_drop_obj(ctx, list->res[i].im_obj); + fz_drop_image(ctx, list->res[i].after); + } + fz_free(ctx, list->res); + list->res = NULL; + list->len = 0; + list->max = 0; +} + +static void +push_rewritten_shade(fz_context *ctx, pdf_color_processor *p, pdf_obj *before, fz_shade *after, char *name) +{ + rewritten_shades *list = &p->shades; + + if (list->max == list->len) + { + int new_max = list->max * 2; + if (new_max == 0) + new_max = 32; + list->res = fz_realloc(ctx, list->res, sizeof(*list->res) * new_max); + list->max = new_max; + } + list->res[list->len].before = pdf_keep_obj(ctx, before); + list->res[list->len].after = fz_keep_shade(ctx, after); + memcpy(list->res[list->len].name, name, MAX_REWRITTEN_NAME); + list->len++; +} + +static fz_shade * +find_rewritten_shade(fz_context *ctx, pdf_color_processor *p, pdf_obj *before, char *name) +{ + rewritten_shades *list = &p->shades; + int i; + + for (i = 0; i < list->len; i++) + if (list->res[i].before == before) + { + memcpy(name, list->res[i].name, MAX_REWRITTEN_NAME); + return list->res[i].after; + } + + return NULL; +} + +static void +drop_rewritten_shades(fz_context *ctx, pdf_color_processor *p) +{ + rewritten_shades *list = &p->shades; + int i; + + for (i = 0; i < list->len; i++) + { + pdf_drop_obj(ctx, list->res[i].before); + fz_drop_shade(ctx, list->res[i].after); + } + fz_free(ctx, list->res); + list->res = NULL; + list->len = 0; + list->max = 0; +} + +static void +make_resource_instance(fz_context *ctx, pdf_color_processor *p, pdf_obj *key, const char *prefix, char *buf, int len, pdf_obj *target) +{ + int i; + + /* key gives us our category. Make sure we have such a category. */ + pdf_obj *res = pdf_dict_get(ctx, p->rstack->new_rdb, key); + if (!res) + res = pdf_dict_put_dict(ctx, p->rstack->new_rdb, key, 8); + + /* Now check through the category for each possible prefixed name + * in turn. */ + for (i = 1; i < 65536; ++i) + { + pdf_obj *obj; + fz_snprintf(buf, len, "%s%d", prefix, i); + + obj = pdf_dict_gets(ctx, res, buf); + if (!obj) + { + /* We've run out of names. At least that means we haven't + * previously added one ourselves. So add it now. */ + pdf_dict_puts(ctx, res, buf, target); + return; + } + if (pdf_objcmp_resolve(ctx, obj, target) == 0) + { + /* We've found this one before! */ + return; + } + } + fz_throw(ctx, FZ_ERROR_LIMIT, "Cannot create unique resource name"); +} + +static void +rewrite_cs(fz_context *ctx, pdf_color_processor *p, pdf_obj *cs_obj, int n, float *color, int stroking) +{ + char new_name[MAX_REWRITTEN_NAME]; + fz_colorspace *cs = NULL; + pdf_pattern *pat = NULL; + fz_shade *shade = NULL; + int type; + + if (stroking) + p->gstate->unmarked &= ~UNMARKED_STROKE; + else + p->gstate->unmarked &= ~UNMARKED_FILL; + + /* Otherwise, if it's a name, look it up as a colorspace. */ + if (pdf_name_eq(ctx, cs_obj, PDF_NAME(DeviceGray)) || + pdf_name_eq(ctx, cs_obj, PDF_NAME(DeviceCMYK)) || + pdf_name_eq(ctx, cs_obj, PDF_NAME(DeviceRGB)) || + pdf_name_eq(ctx, cs_obj, PDF_NAME(Pattern))) + { + /* These names should not be looked up. */ + } + else if (pdf_is_name(ctx, cs_obj)) + { + /* Any other names should be looked up in the resource dict, + * because our rewrite function doesn't have access to that. */ + cs_obj = pdf_dict_get(ctx, pdf_dict_get(ctx, p->rstack->old_rdb, PDF_NAME(ColorSpace)), cs_obj); + } + + /* Until now, cs_obj has been a borrowed reference. Make it a real one. */ + pdf_keep_obj(ctx, cs_obj); + + /* Whatever happens, from here on in, we must drop cs_obj. */ + fz_var(cs); + fz_var(pat); + fz_var(shade); + + fz_try(ctx) + { + /* Our gstate always has to contain colorspaces BEFORE rewriting. + * Consider the case where we are given a separation space, and + * we rewrite it to be RGB. Then we change the 'amount' of that + * separation; we can't do that with the RGB value. */ + if (stroking) + { + pdf_drop_obj(ctx, p->gstate->cs_stroke); + p->gstate->cs_stroke = pdf_keep_obj(ctx, cs_obj); + } + else + { + pdf_drop_obj(ctx, p->gstate->cs_fill); + p->gstate->cs_fill = pdf_keep_obj(ctx, cs_obj); + } + /* Now, do any rewriting. This might drop the reference to cs_obj and + * return with a different one. */ + if (p->options->color_rewrite) + p->options->color_rewrite(ctx, p->options->opaque, &cs_obj, &n, color); + + /* If we've rewritten it to be a simple name, great! */ + if (pdf_name_eq(ctx, cs_obj, PDF_NAME(DeviceGray))) + { + if (stroking) + { + if (n == 1) + p->chain->op_G(ctx, p->chain, color[0]); + else + p->chain->op_CS(ctx, p->chain, "DeviceGray", fz_device_gray(ctx)); + } + else + { + if (n == 1) + p->chain->op_g(ctx, p->chain, color[0]); + else + p->chain->op_cs(ctx, p->chain, "DeviceGray", fz_device_gray(ctx)); + } + break; + } + + if (pdf_name_eq(ctx, cs_obj, PDF_NAME(DeviceRGB))) + { + if (stroking) + { + if (n == 3) + p->chain->op_RG(ctx, p->chain, color[0], color[1], color[2]); + else + p->chain->op_CS(ctx, p->chain, "DeviceRGB", fz_device_rgb(ctx)); + } + else + { + if (n == 3) + p->chain->op_rg(ctx, p->chain, color[0], color[1], color[2]); + else + p->chain->op_cs(ctx, p->chain, "DeviceRGB", fz_device_rgb(ctx)); + } + break; + } + + if (pdf_name_eq(ctx, cs_obj, PDF_NAME(DeviceCMYK))) + { + if (stroking) + { + if (n == 4) + p->chain->op_K(ctx, p->chain, color[0], color[1], color[2], color[3]); + else + p->chain->op_CS(ctx, p->chain, "DeviceCMYK", fz_device_cmyk(ctx)); + } + else + { + if (n == 4) + p->chain->op_k(ctx, p->chain, color[0], color[1], color[2], color[3]); + else + p->chain->op_cs(ctx, p->chain, "DeviceCMYK", fz_device_cmyk(ctx)); + } + break; + } + + /* Accept both /Pattern and [ /Pattern ] */ + if (pdf_name_eq(ctx, cs_obj, PDF_NAME(Pattern)) || + (pdf_array_len(ctx, cs_obj) == 1 && pdf_name_eq(ctx, pdf_array_get(ctx, cs_obj, 0), PDF_NAME(Pattern)))) + { + assert(n == 0); + if (stroking) + p->chain->op_CS(ctx, p->chain, "Pattern", NULL); + else + p->chain->op_cs(ctx, p->chain, "Pattern", NULL); + break; + } + + /* Has it been rewritten to be an array? */ + if (pdf_is_array(ctx, cs_obj)) + { + /* Make a new entry (or find an existing one), and send that. */ + make_resource_instance(ctx, p, PDF_NAME(ColorSpace), "CS", new_name, sizeof(new_name), cs_obj); + + cs = pdf_load_colorspace(ctx, cs_obj); + if (stroking) + p->chain->op_CS(ctx, p->chain, new_name, cs); + else + p->chain->op_cs(ctx, p->chain, new_name, cs); + + if (n > 0) + { + if (stroking) + p->chain->op_SC_color(ctx, p->chain, n, color); + else + p->chain->op_sc_color(ctx, p->chain, n, color); + } + break; + } + + /* Has it been rewritten to be a pattern? */ + type = pdf_dict_get_int(ctx, cs_obj, PDF_NAME(PatternType)); + if (type < 1 || type > 2) + fz_throw(ctx, FZ_ERROR_FORMAT, "Bad PatternType"); + + /* Make a new entry (or find an existing one), and send that. */ + make_resource_instance(ctx, p, PDF_NAME(Pattern), "Pa", new_name, sizeof(new_name), cs_obj); + + if (type == 1) + { + pat = pdf_load_pattern(ctx, p->doc, cs_obj); + if (stroking) + p->chain->op_SC_pattern(ctx, p->chain, new_name, pat, n, color); + else + p->chain->op_sc_pattern(ctx, p->chain, new_name, pat, n, color); + break; + } + else if (type == 2) + { + shade = pdf_load_shading(ctx, p->doc, cs_obj); + if (stroking) + p->chain->op_SC_shade(ctx, p->chain, new_name, shade); + else + p->chain->op_sc_shade(ctx, p->chain, new_name, shade); + break; + } + + fz_throw(ctx, FZ_ERROR_FORMAT, "Illegal rewritten colorspace"); + } + fz_always(ctx) + { + fz_drop_shade(ctx, shade); + fz_drop_colorspace(ctx, cs); + pdf_drop_pattern(ctx, pat); + pdf_drop_obj(ctx, cs_obj); + } + fz_catch(ctx) + fz_rethrow(ctx); +} + +static void +mark_stroke(fz_context *ctx, pdf_color_processor *p) +{ + float zero[FZ_MAX_COLORS] = { 0 }; + + rewrite_cs(ctx, p, PDF_NAME(DeviceGray), 1, zero, 1); + + p->gstate->unmarked &= ~UNMARKED_STROKE; +} + +static void +mark_fill(fz_context *ctx, pdf_color_processor *p) +{ + float zero[FZ_MAX_COLORS] = { 0 }; + + rewrite_cs(ctx, p, PDF_NAME(DeviceGray), 1, zero, 0); + + p->gstate->unmarked &= ~UNMARKED_FILL; +} + +/* general graphics state */ + +static void +pdf_color_w(fz_context *ctx, pdf_processor *proc, float linewidth) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_w) + p->chain->op_w(ctx, p->chain, linewidth); +} + +static void +pdf_color_j(fz_context *ctx, pdf_processor *proc, int linejoin) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_j) + p->chain->op_j(ctx, p->chain, linejoin); +} + +static void +pdf_color_J(fz_context *ctx, pdf_processor *proc, int linecap) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_J) + p->chain->op_J(ctx, p->chain, linecap); +} + +static void +pdf_color_M(fz_context *ctx, pdf_processor *proc, float miterlimit) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_M) + p->chain->op_M(ctx, p->chain, miterlimit); +} + +static void +pdf_color_d(fz_context *ctx, pdf_processor *proc, pdf_obj *array, float phase) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_d) + p->chain->op_d(ctx, p->chain, array, phase); +} + +static void +pdf_color_ri(fz_context *ctx, pdf_processor *proc, const char *intent) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_ri) + p->chain->op_ri(ctx, p->chain, intent); +} + +static void +pdf_color_gs_OP(fz_context *ctx, pdf_processor *proc, int b) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_gs_OP) + p->chain->op_gs_OP(ctx, p->chain, b); +} + +static void +pdf_color_gs_op(fz_context *ctx, pdf_processor *proc, int b) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_gs_op) + p->chain->op_gs_op(ctx, p->chain, b); +} + +static void +pdf_color_gs_OPM(fz_context *ctx, pdf_processor *proc, int i) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_gs_OPM) + p->chain->op_gs_OPM(ctx, p->chain, i); +} + +static void +pdf_color_gs_UseBlackPtComp(fz_context *ctx, pdf_processor *proc, pdf_obj *name) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_gs_UseBlackPtComp) + p->chain->op_gs_UseBlackPtComp(ctx, p->chain, name); +} + +static void +pdf_color_i(fz_context *ctx, pdf_processor *proc, float flatness) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_i) + p->chain->op_i(ctx, p->chain, flatness); +} + +static void +pdf_color_gs_begin(fz_context *ctx, pdf_processor *proc, const char *name, pdf_obj *extgstate) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_gs_begin) + p->chain->op_gs_begin(ctx, p->chain, name, extgstate); +} + +static void +pdf_color_gs_BM(fz_context *ctx, pdf_processor *proc, const char *blendmode) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_gs_BM) + p->chain->op_gs_BM(ctx, p->chain, blendmode); +} + +static void +pdf_color_gs_CA(fz_context *ctx, pdf_processor *proc, float alpha) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_gs_CA) + p->chain->op_gs_CA(ctx, p->chain, alpha); +} + +static void +pdf_color_gs_ca(fz_context *ctx, pdf_processor *proc, float alpha) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_gs_ca) + p->chain->op_gs_ca(ctx, p->chain, alpha); +} + +static void +pdf_color_gs_SMask(fz_context *ctx, pdf_processor *proc, pdf_obj *smask, fz_colorspace *smask_cs, float *bc, int luminosity, pdf_obj *tr) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_gs_SMask) + p->chain->op_gs_SMask(ctx, p->chain, smask, smask_cs, bc, luminosity, tr); +} + +static void +pdf_color_gs_end(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_gs_end) + p->chain->op_gs_end(ctx, p->chain); +} + +/* special graphics state */ + +static void +pdf_color_q(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + gstate_stack *gs = fz_malloc_struct(ctx, gstate_stack); + + gs->next = p->gstate; + gs->cs_fill = pdf_keep_obj(ctx, p->gstate->cs_fill); + gs->cs_stroke = pdf_keep_obj(ctx, p->gstate->cs_stroke); + gs->unmarked = p->gstate->unmarked; + gs->ctm = p->gstate->ctm; + p->gstate = gs; + + if (p->chain->op_q) + p->chain->op_q(ctx, p->chain); +} + +static void +pdf_color_cm(fz_context *ctx, pdf_processor *proc, float a, float b, float c, float d, float e, float f) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + fz_matrix m; + + m.a = a; + m.b = b; + m.c = c; + m.d = d; + m.e = e; + m.f = f; + + p->gstate->ctm = fz_concat(m, p->gstate->ctm); + + if (p->chain->op_cm) + p->chain->op_cm(ctx, p->chain, a, b, c, d, e, f); +} + +/* path construction */ + +static void +pdf_color_m(fz_context *ctx, pdf_processor *proc, float x, float y) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_m) + p->chain->op_m(ctx, p->chain, x, y); +} + +static void +pdf_color_l(fz_context *ctx, pdf_processor *proc, float x, float y) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_l) + p->chain->op_l(ctx, p->chain, x, y); +} + +static void +pdf_color_c(fz_context *ctx, pdf_processor *proc, float x1, float y1, float x2, float y2, float x3, float y3) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_c) + p->chain->op_c(ctx, p->chain, x1, y1, x2, y2, x3, y3); +} + +static void +pdf_color_v(fz_context *ctx, pdf_processor *proc, float x2, float y2, float x3, float y3) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_v) + p->chain->op_v(ctx, p->chain, x2, y2, x3, y3); +} + +static void +pdf_color_y(fz_context *ctx, pdf_processor *proc, float x1, float y1, float x3, float y3) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_y) + p->chain->op_y(ctx, p->chain, x1, y1, x3, y3); +} + +static void +pdf_color_h(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_h) + p->chain->op_h(ctx, p->chain); +} + +static void +pdf_color_re(fz_context *ctx, pdf_processor *proc, float x, float y, float w, float h) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_re) + p->chain->op_re(ctx, p->chain, x, y, w, h); +} + +/* path painting */ + +static void +pdf_color_S(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->gstate->unmarked & UNMARKED_STROKE) + mark_stroke(ctx, p); + + if (p->chain->op_S) + p->chain->op_S(ctx, p->chain); +} + +static void +pdf_color_s(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->gstate->unmarked & UNMARKED_FILL) + mark_fill(ctx, p); + + if (p->chain->op_s) + p->chain->op_s(ctx, p->chain); +} + +static void +pdf_color_F(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->gstate->unmarked & UNMARKED_FILL) + mark_fill(ctx, p); + + if (p->chain->op_F) + p->chain->op_F(ctx, p->chain); +} + +static void +pdf_color_f(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->gstate->unmarked & UNMARKED_FILL) + mark_fill(ctx, p); + + if (p->chain->op_f) + p->chain->op_f(ctx, p->chain); +} + +static void +pdf_color_fstar(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->gstate->unmarked & UNMARKED_FILL) + mark_fill(ctx, p); + + if (p->chain->op_fstar) + p->chain->op_fstar(ctx, p->chain); +} + +static void +pdf_color_B(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->gstate->unmarked & UNMARKED_STROKE) + mark_stroke(ctx, p); + if (p->gstate->unmarked & UNMARKED_FILL) + mark_fill(ctx, p); + + if (p->chain->op_B) + p->chain->op_B(ctx, p->chain); +} + +static void +pdf_color_Bstar(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->gstate->unmarked & UNMARKED_STROKE) + mark_stroke(ctx, p); + if (p->gstate->unmarked & UNMARKED_FILL) + mark_fill(ctx, p); + + if (p->chain->op_Bstar) + p->chain->op_Bstar(ctx, p->chain); +} + +static void +pdf_color_b(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->gstate->unmarked & UNMARKED_STROKE) + mark_stroke(ctx, p); + if (p->gstate->unmarked & UNMARKED_FILL) + mark_fill(ctx, p); + + if (p->chain->op_b) + p->chain->op_b(ctx, p->chain); +} + +static void +pdf_color_bstar(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->gstate->unmarked & UNMARKED_STROKE) + mark_stroke(ctx, p); + if (p->gstate->unmarked & UNMARKED_FILL) + mark_fill(ctx, p); + + if (p->chain->op_bstar) + p->chain->op_bstar(ctx, p->chain); +} + +static void +pdf_color_n(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_n) + p->chain->op_n(ctx, p->chain); +} + +/* clipping paths */ + +static void +pdf_color_W(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_W) + p->chain->op_W(ctx, p->chain); +} + +static void +pdf_color_Wstar(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_Wstar) + p->chain->op_Wstar(ctx, p->chain); +} + +/* text objects */ + +static void +pdf_color_BT(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_BT) + p->chain->op_BT(ctx, p->chain); +} + +static void +pdf_color_ET(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_ET) + p->chain->op_ET(ctx, p->chain); +} + +static void +pdf_color_Q(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + gstate_stack *gs = p->gstate; + + p->gstate = gs->next; + pdf_drop_obj(ctx, gs->cs_fill); + pdf_drop_obj(ctx, gs->cs_stroke); + + fz_try(ctx) + if (p->chain->op_Q) + p->chain->op_Q(ctx, p->chain); + fz_always(ctx) + fz_free(ctx, gs); + fz_catch(ctx) + fz_rethrow(ctx); +} + +/* text state */ + +static void +pdf_color_Tc(fz_context *ctx, pdf_processor *proc, float charspace) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_Tc) + p->chain->op_Tc(ctx, p->chain, charspace); +} + +static void +pdf_color_Tw(fz_context *ctx, pdf_processor *proc, float wordspace) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_Tw) + p->chain->op_Tw(ctx, p->chain, wordspace); +} + +static void +pdf_color_Tz(fz_context *ctx, pdf_processor *proc, float scale) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_Tz) + p->chain->op_Tz(ctx, p->chain, scale); +} + +static void +pdf_color_TL(fz_context *ctx, pdf_processor *proc, float leading) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_TL) + p->chain->op_TL(ctx, p->chain, leading); +} + +static void +pdf_color_Tf(fz_context *ctx, pdf_processor *proc, const char *name, pdf_font_desc *font, float size) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_Tf) + p->chain->op_Tf(ctx, p->chain, name, font, size); +} + +static void +pdf_color_Tr(fz_context *ctx, pdf_processor *proc, int render) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_Tr) + p->chain->op_Tr(ctx, p->chain, render); +} + +static void +pdf_color_Ts(fz_context *ctx, pdf_processor *proc, float rise) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_Ts) + p->chain->op_Ts(ctx, p->chain, rise); +} + +/* text positioning */ + +static void +pdf_color_Td(fz_context *ctx, pdf_processor *proc, float tx, float ty) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_Td) + p->chain->op_Td(ctx, p->chain, tx, ty); +} + +static void +pdf_color_TD(fz_context *ctx, pdf_processor *proc, float tx, float ty) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_TD) + p->chain->op_TD(ctx, p->chain, tx, ty); +} + +static void +pdf_color_Tm(fz_context *ctx, pdf_processor *proc, float a, float b, float c, float d, float e, float f) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_Tm) + p->chain->op_Tm(ctx, p->chain, a, b, c, d, e, f); +} + +static void +pdf_color_Tstar(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_Tstar) + p->chain->op_Tstar(ctx, p->chain); +} + +/* text showing */ + +static void +pdf_color_TJ(fz_context *ctx, pdf_processor *proc, pdf_obj *array) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + /* FIXME: We could optimise this if we knew the Tr, maybe. */ + if (p->gstate->unmarked & UNMARKED_STROKE) + mark_stroke(ctx, p); + if (p->gstate->unmarked & UNMARKED_FILL) + mark_fill(ctx, p); + + if (p->chain->op_TJ) + p->chain->op_TJ(ctx, p->chain, array); +} + +static void +pdf_color_Tj(fz_context *ctx, pdf_processor *proc, char *str, size_t len) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + /* FIXME: We could optimise this if we knew the Tr, maybe. */ + if (p->gstate->unmarked & UNMARKED_STROKE) + mark_stroke(ctx, p); + if (p->gstate->unmarked & UNMARKED_FILL) + mark_fill(ctx, p); + + if (p->chain->op_Tj) + p->chain->op_Tj(ctx, p->chain, str, len); +} + +static void +pdf_color_squote(fz_context *ctx, pdf_processor *proc, char *str, size_t len) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + /* FIXME: We could optimise this if we knew the Tr, maybe. */ + if (p->gstate->unmarked & UNMARKED_STROKE) + mark_stroke(ctx, p); + if (p->gstate->unmarked & UNMARKED_FILL) + mark_fill(ctx, p); + + if (p->chain->op_squote) + p->chain->op_squote(ctx, p->chain, str, len); +} + +static void +pdf_color_dquote(fz_context *ctx, pdf_processor *proc, float aw, float ac, char *str, size_t len) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + /* FIXME: We could optimise this if we knew the Tr, maybe. */ + if (p->gstate->unmarked & UNMARKED_STROKE) + mark_stroke(ctx, p); + if (p->gstate->unmarked & UNMARKED_FILL) + mark_fill(ctx, p); + + if (p->chain->op_dquote) + p->chain->op_dquote(ctx, p->chain, aw, ac, str, len); +} + +/* type 3 fonts */ + +static void +pdf_color_d0(fz_context *ctx, pdf_processor *proc, float wx, float wy) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_d0) + p->chain->op_d0(ctx, p->chain, wx, wy); +} + +static void +pdf_color_d1(fz_context *ctx, pdf_processor *proc, float wx, float wy, float llx, float lly, float urx, float ury) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_d1) + p->chain->op_d1(ctx, p->chain, wx, wy, llx, lly, urx, ury); +} + +/* color */ + +static void +pdf_color_CS(fz_context *ctx, pdf_processor *proc, const char *name, fz_colorspace *cs) +{ + pdf_color_processor *p = (pdf_color_processor *)proc; + pdf_obj *cs_obj = pdf_new_name(ctx, name); + float color[FZ_MAX_COLORS] = { 1 }; + + fz_try(ctx) + rewrite_cs(ctx, p, cs_obj, 0, color, 1); + fz_always(ctx) + pdf_drop_obj(ctx, cs_obj); + fz_catch(ctx) + fz_rethrow(ctx); +} + +static void +pdf_color_cs(fz_context *ctx, pdf_processor *proc, const char *name, fz_colorspace *cs) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + pdf_obj *cs_obj = pdf_new_name(ctx, name); + float color[FZ_MAX_COLORS] = { 1 }; + + fz_try(ctx) + rewrite_cs(ctx, p, cs_obj, 0, color, 0); + fz_always(ctx) + pdf_drop_obj(ctx, cs_obj); + fz_catch(ctx) + fz_rethrow(ctx); +} + +static void +pdf_color_SC_pattern(fz_context *ctx, pdf_processor *proc, const char *name, pdf_pattern *pat, int n, float *color) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + float local_color[FZ_MAX_COLORS] = { 0 }; + pdf_obj *cs_obj = pdf_dict_gets(ctx, pdf_dict_get(ctx, p->rstack->old_rdb, PDF_NAME(Pattern)), name); + + memcpy(local_color, color, sizeof(float) * n); + rewrite_cs(ctx, p, cs_obj, n, local_color, 1); +} + +static void +pdf_color_sc_pattern(fz_context *ctx, pdf_processor *proc, const char *name, pdf_pattern *pat, int n, float *color) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + float local_color[FZ_MAX_COLORS] = { 0 }; + pdf_obj *cs_obj = pdf_dict_gets(ctx, pdf_dict_get(ctx, p->rstack->old_rdb, PDF_NAME(Pattern)), name); + + memcpy(local_color, color, sizeof(float) * n); + rewrite_cs(ctx, p, cs_obj, n, local_color, 0); +} + +static void +pdf_color_SC_shade(fz_context *ctx, pdf_processor *proc, const char *name, fz_shade *shade) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + pdf_obj *orig; + pdf_obj *dict = NULL; + pdf_obj *dict2 = NULL; + char new_name[MAX_REWRITTEN_NAME]; + pdf_obj *rewritten; + fz_shade *new_shade = NULL; + + if (p->options->shade_rewrite == NULL) + { + /* Must copy shading over to new resources dict. */ + pdf_obj *old_obj = pdf_dict_gets(ctx, pdf_dict_get(ctx, p->rstack->old_rdb, PDF_NAME(Shading)), name); + pdf_obj *new_shading_dict = pdf_dict_get(ctx, p->rstack->new_rdb, PDF_NAME(Shading)); + if (new_shading_dict == NULL) + pdf_dict_put_drop(ctx, p->rstack->new_rdb, PDF_NAME(Shading), new_shading_dict = pdf_new_dict(ctx, p->doc, 4)); + pdf_dict_puts(ctx, new_shading_dict, name, old_obj); + + if (p->chain->op_SC_shade) + p->chain->op_SC_shade(ctx, p->chain, name, shade); + return; + } + + orig = pdf_dict_gets(ctx, pdf_dict_get(ctx, p->rstack->old_rdb, PDF_NAME(Pattern)), name); + orig = pdf_dict_get(ctx, orig, PDF_NAME(Shading)); + + new_shade = find_rewritten_shade(ctx, p, orig, new_name); + if (new_shade) + { + /* Must copy shading over to new resources dict. */ + pdf_obj *old_obj = pdf_dict_gets(ctx, pdf_dict_get(ctx, p->rstack->old_rdb, PDF_NAME(Shading)), name); + pdf_obj *new_shading_dict = pdf_dict_get(ctx, p->rstack->new_rdb, PDF_NAME(Shading)); + if (new_shading_dict == NULL) + pdf_dict_put_drop(ctx, p->rstack->new_rdb, PDF_NAME(Shading), new_shading_dict = pdf_new_dict(ctx, p->doc, 4)); + pdf_dict_puts(ctx, new_shading_dict, name, old_obj); + + if (p->chain->op_SC_shade) + p->chain->op_SC_shade(ctx, p->chain, new_name, new_shade); + return; + } + + rewritten = pdf_recolor_shade(ctx, orig, p->options->shade_rewrite, p->options->opaque); + + fz_var(new_shade); + fz_var(dict); + fz_var(dict2); + + fz_try(ctx) + { + dict = pdf_new_dict(ctx, p->doc, 1); + pdf_dict_put_int(ctx, dict, PDF_NAME(PatternType), 2); + pdf_dict_put(ctx, dict, PDF_NAME(Shading), rewritten); + dict2 = pdf_add_object(ctx, p->doc, dict); + make_resource_instance(ctx, p, PDF_NAME(Pattern), "Pa", new_name, sizeof(new_name), dict2); + + new_shade = pdf_load_shading(ctx, p->doc, rewritten); + + /* Remember that we've done this one before. */ + push_rewritten_shade(ctx, p, orig, new_shade, new_name); + + if (p->chain->op_sh) + p->chain->op_SC_shade(ctx, p->chain, new_name, new_shade); + } + fz_always(ctx) + { + fz_drop_shade(ctx, new_shade); + pdf_drop_obj(ctx, rewritten); + pdf_drop_obj(ctx, dict); + pdf_drop_obj(ctx, dict2); + } + fz_catch(ctx) + fz_rethrow(ctx); +} + +static void +pdf_color_sc_shade(fz_context *ctx, pdf_processor *proc, const char *name, fz_shade *shade) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + pdf_obj *orig; + pdf_obj *dict = NULL; + pdf_obj *dict2 = NULL; + char new_name[MAX_REWRITTEN_NAME]; + pdf_obj *rewritten; + fz_shade *new_shade = NULL; + + if (p->options->shade_rewrite == NULL) + { + /* Must copy shading over to new resources dict. */ + pdf_obj *old_obj = pdf_dict_gets(ctx, pdf_dict_get(ctx, p->rstack->old_rdb, PDF_NAME(Shading)), name); + pdf_obj *new_shading_dict = pdf_dict_get(ctx, p->rstack->new_rdb, PDF_NAME(Shading)); + if (new_shading_dict == NULL) + pdf_dict_put_drop(ctx, p->rstack->new_rdb, PDF_NAME(Shading), new_shading_dict = pdf_new_dict(ctx, p->doc, 4)); + pdf_dict_puts(ctx, new_shading_dict, name, old_obj); + + if (p->chain->op_sc_shade) + p->chain->op_sc_shade(ctx, p->chain, name, shade); + return; + } + + orig = pdf_dict_gets(ctx, pdf_dict_get(ctx, p->rstack->old_rdb, PDF_NAME(Pattern)), name); + orig = pdf_dict_get(ctx, orig, PDF_NAME(Shading)); + + new_shade = find_rewritten_shade(ctx, p, orig, new_name); + if (new_shade) + { + if (p->chain->op_sc_shade) + p->chain->op_sc_shade(ctx, p->chain, new_name, new_shade); + return; + } + + rewritten = pdf_recolor_shade(ctx, orig, p->options->shade_rewrite, p->options->opaque); + + fz_var(new_shade); + fz_var(dict); + fz_var(dict2); + + fz_try(ctx) + { + dict = pdf_new_dict(ctx, p->doc, 1); + pdf_dict_put_int(ctx, dict, PDF_NAME(PatternType), 2); + pdf_dict_put(ctx, dict, PDF_NAME(Shading), rewritten); + dict2 = pdf_add_object(ctx, p->doc, dict); + make_resource_instance(ctx, p, PDF_NAME(Pattern), "Pa", new_name, sizeof(new_name), dict2); + + new_shade = pdf_load_shading(ctx, p->doc, rewritten); + + /* Remember that we've done this one before. */ + push_rewritten_shade(ctx, p, orig, new_shade, new_name); + + if (p->chain->op_sh) + p->chain->op_sc_shade(ctx, p->chain, new_name, new_shade); + } + fz_always(ctx) + { + fz_drop_shade(ctx, new_shade); + pdf_drop_obj(ctx, rewritten); + pdf_drop_obj(ctx, dict); + pdf_drop_obj(ctx, dict2); + } + fz_catch(ctx) + fz_rethrow(ctx); +} + +static void +pdf_color_SC_color(fz_context *ctx, pdf_processor *proc, int n, float *color) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + float local_color[FZ_MAX_COLORS] = { 0 }; + pdf_obj *cs_obj = p->gstate->cs_stroke; + + memcpy(local_color, color, sizeof(float) * n); + rewrite_cs(ctx, p, cs_obj, n, local_color, 1); +} + +static void +pdf_color_sc_color(fz_context *ctx, pdf_processor *proc, int n, float *color) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + float local_color[FZ_MAX_COLORS] = { 0 }; + pdf_obj *cs_obj = p->gstate->cs_fill; + + memcpy(local_color, color, sizeof(float) * n); + rewrite_cs(ctx, p, cs_obj, n, local_color, 0); +} + +static void +pdf_color_G(fz_context *ctx, pdf_processor *proc, float g) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + float local_color[FZ_MAX_COLORS] = { g }; + + rewrite_cs(ctx, p, PDF_NAME(DeviceGray), 1, local_color, 1); +} + +static void +pdf_color_g(fz_context *ctx, pdf_processor *proc, float g) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + float local_color[FZ_MAX_COLORS] = { g }; + + rewrite_cs(ctx, p, PDF_NAME(DeviceGray), 1, local_color, 0); +} + +static void +pdf_color_RG(fz_context *ctx, pdf_processor *proc, float r, float g, float b) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + float local_color[FZ_MAX_COLORS] = { r, g, b }; + + rewrite_cs(ctx, p, PDF_NAME(DeviceRGB), 3, local_color, 1); +} + +static void +pdf_color_rg(fz_context *ctx, pdf_processor *proc, float r, float g, float b) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + float local_color[FZ_MAX_COLORS] = { r, g, b }; + + rewrite_cs(ctx, p, PDF_NAME(DeviceRGB), 3, local_color, 0); +} + +static void +pdf_color_K(fz_context *ctx, pdf_processor *proc, float c, float m, float y, float k) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + float local_color[FZ_MAX_COLORS] = { c, m, y, k }; + + rewrite_cs(ctx, p, PDF_NAME(DeviceCMYK), 4, local_color, 1); +} + +static void +pdf_color_k(fz_context *ctx, pdf_processor *proc, float c, float m, float y, float k) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + float local_color[FZ_MAX_COLORS] = { c, m, y, k }; + + rewrite_cs(ctx, p, PDF_NAME(DeviceCMYK), 4, local_color, 0); +} + +/* shadings, images, xobjects */ + +static void +pdf_color_BI(fz_context *ctx, pdf_processor *proc, fz_image *image, const char *colorspace) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (image->imagemask) + { + /* Imagemasks require the color to have been set. */ + if (p->gstate->unmarked & UNMARKED_FILL) + mark_fill(ctx, p); + } + + fz_keep_image(ctx, image); + if (p->options->image_rewrite) + p->options->image_rewrite(ctx, p->options->opaque, &image, p->gstate->ctm, NULL); + + if (p->chain->op_BI) + p->chain->op_BI(ctx, p->chain, image, colorspace); + fz_drop_image(ctx, image); +} + +static void +pdf_color_sh(fz_context *ctx, pdf_processor *proc, const char *name, fz_shade *shade) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + pdf_obj *orig; + char new_name[MAX_REWRITTEN_NAME]; + pdf_obj *rewritten; + fz_shade *new_shade = NULL; + + if (p->options->shade_rewrite == NULL) + { + /* Must copy shading over to new resources dict. */ + pdf_obj *old_obj = pdf_dict_gets(ctx, pdf_dict_get(ctx, p->rstack->old_rdb, PDF_NAME(Shading)), name); + pdf_obj *new_shading_dict = pdf_dict_get(ctx, p->rstack->new_rdb, PDF_NAME(Shading)); + if (new_shading_dict == NULL) + pdf_dict_put_drop(ctx, p->rstack->new_rdb, PDF_NAME(Shading), new_shading_dict = pdf_new_dict(ctx, p->doc, 4)); + pdf_dict_puts(ctx, new_shading_dict, name, old_obj); + + if (p->chain->op_sh) + p->chain->op_sh(ctx, p->chain, name, shade); + return; + } + + orig = pdf_dict_gets(ctx, pdf_dict_get(ctx, p->rstack->old_rdb, PDF_NAME(Shading)), name); + + new_shade = find_rewritten_shade(ctx, p, orig, new_name); + if (new_shade) + { + if (p->chain->op_sh) + p->chain->op_sh(ctx, p->chain, new_name, new_shade); + return; + } + + rewritten = pdf_recolor_shade(ctx, orig, p->options->shade_rewrite, p->options->opaque); + + fz_var(new_shade); + + fz_try(ctx) + { + make_resource_instance(ctx, p, PDF_NAME(Shading), "Sh", new_name, sizeof(new_name), rewritten); + + new_shade = pdf_load_shading(ctx, p->doc, rewritten); + + /* Remember that we've done this one before. */ + push_rewritten_shade(ctx, p, orig, new_shade, new_name); + + if (p->chain->op_sh) + p->chain->op_sh(ctx, p->chain, new_name, new_shade); + } + fz_always(ctx) + { + fz_drop_shade(ctx, new_shade); + pdf_drop_obj(ctx, rewritten); + } + fz_catch(ctx) + fz_rethrow(ctx); +} + +static void +pdf_color_Do_image(fz_context *ctx, pdf_processor *proc, const char *name, fz_image *image) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + fz_image *orig = image; + char new_name[MAX_REWRITTEN_NAME]; + pdf_obj *im_obj = pdf_dict_gets(ctx, pdf_dict_get(ctx, p->rstack->old_rdb, PDF_NAME(XObject)), name); + + /* Have we done this one before? */ + if (!p->options->repeated_image_rewrite) + { + image = find_rewritten_image(ctx, p, im_obj, new_name); + if (image) + { + if (p->chain->op_Do_image) + p->chain->op_Do_image(ctx, p->chain, new_name, image); + return; + } + } + + image = orig; + fz_keep_image(ctx, image); + pdf_keep_obj(ctx, im_obj); + + fz_var(im_obj); + fz_try(ctx) + { + + if (image->imagemask) + { + /* Imagemasks require the color to have been set. */ + if (p->gstate->unmarked & UNMARKED_FILL) + mark_fill(ctx, p); + } + else + { + if (p->options->image_rewrite) + p->options->image_rewrite(ctx, p->options->opaque, &image, p->gstate->ctm, im_obj); + } + + /* If it's been rewritten add the new one, otherwise copy the old one across. */ + if (image != orig) + { + pdf_drop_obj(ctx, im_obj); + im_obj = NULL; + im_obj = pdf_add_image(ctx, p->doc, image); + } + + make_resource_instance(ctx, p, PDF_NAME(XObject), "Im", new_name, sizeof(new_name), im_obj); + + if (!p->options->repeated_image_rewrite) + { + /* Remember that we've done this one before. */ + push_rewritten_image(ctx, p, im_obj, image, new_name); + } + + if (p->chain->op_Do_image) + p->chain->op_Do_image(ctx, p->chain, new_name, image); + } + fz_always(ctx) + { + pdf_drop_obj(ctx, im_obj); + fz_drop_image(ctx, image); + } + fz_catch(ctx) + fz_rethrow(ctx); +} + +static void +pdf_color_Do_form(fz_context *ctx, pdf_processor *proc, const char *name, pdf_obj *xobj) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + char new_name[MAX_REWRITTEN_NAME]; + pdf_obj *xres; + pdf_obj *new_xres = NULL; + + fz_var(new_xres); + + /* FIXME: Ideally we'd look at p->global_options->instance_forms here + * to avoid flattening all the XObjects. This is non-trivial, and + * currently incomplete, hence disabled. We have to find some way to + * tell the underlying filters (in particular the output filter) that + * we are in a new XObject (so the output filter can start a new buffer). + * Maybe it could look at p->global_options->instance_forms too on a + * push/pop of the resources? For now, let's just leave it disabled. */ + if (0 && p->global_options->instance_forms) + { + make_resource_instance(ctx, p, PDF_NAME(XObject), "Xo", new_name, sizeof(new_name), xobj); + + xres = pdf_xobject_resources(ctx, xobj); + fz_try(ctx) + pdf_process_contents(ctx, (pdf_processor *)p, p->doc, xres, xobj, NULL, &new_xres); + fz_catch(ctx) + { + pdf_drop_obj(ctx, new_xres); + fz_rethrow(ctx); + } + + pdf_dict_put_drop(ctx, xobj, PDF_NAME(Resources), new_xres); + + if (p->chain->op_Do_form) + p->chain->op_Do_form(ctx, p->chain, new_name, xobj); + } + else + { + /* In this case, we just copy the XObject across (renaming it). Our caller will arrange to + * filter it. */ + make_resource_instance(ctx, p, PDF_NAME(XObject), "Xo", new_name, sizeof(new_name), xobj); + if (p->chain->op_Do_form) + p->chain->op_Do_form(ctx, p->chain, new_name, xobj); + } +} + +/* marked content */ + +static void +pdf_color_MP(fz_context *ctx, pdf_processor *proc, const char *tag) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_MP) + p->chain->op_MP(ctx, p->chain, tag); +} + +static void +pdf_color_DP(fz_context *ctx, pdf_processor *proc, const char *tag, pdf_obj *raw, pdf_obj *cooked) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_DP) + p->chain->op_DP(ctx, p->chain, tag, raw, cooked); +} + +static void +pdf_color_BMC(fz_context *ctx, pdf_processor *proc, const char *tag) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_BMC) + p->chain->op_BMC(ctx, p->chain, tag); +} + +static void +pdf_color_BDC(fz_context *ctx, pdf_processor *proc, const char *tag, pdf_obj *raw, pdf_obj *cooked) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_BDC) + p->chain->op_BDC(ctx, p->chain, tag, raw, cooked); +} + +static void +pdf_color_EMC(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_EMC) + p->chain->op_EMC(ctx, p->chain); +} + +/* compatibility */ + +static void +pdf_color_BX(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_BX) + p->chain->op_BX(ctx, p->chain); +} + +static void +pdf_color_EX(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_EX) + p->chain->op_EX(ctx, p->chain); +} + +static void +pdf_color_END(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + if (p->chain->op_END) + p->chain->op_END(ctx, p->chain); +} + +static void +pdf_close_color_processor(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + pdf_close_processor(ctx, p->chain); +} + +static void +pdf_drop_color_processor(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + gstate_stack *gs = p->gstate; + + while (gs) + { + gstate_stack *gs_next = gs->next; + + pdf_drop_obj(ctx, gs->cs_fill); + pdf_drop_obj(ctx, gs->cs_stroke); + fz_free(ctx, gs); + gs = gs_next; + } + drop_rewritten_images(ctx, p); + drop_rewritten_shades(ctx, p); + + pdf_drop_document(ctx, p->doc); +} + +static void +pdf_color_push_resources(fz_context *ctx, pdf_processor *proc, pdf_obj *res) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + resources_stack *stk = fz_malloc_struct(ctx, resources_stack); + pdf_obj *obj; + + p->gstate->unmarked = UNMARKED_STROKE | UNMARKED_FILL; + + stk->next = p->rstack; + p->rstack = stk; + fz_try(ctx) + { + stk->old_rdb = pdf_keep_obj(ctx, res); + /* At the moment we know that we'll always be flattening XObjects. + * So only the top level 'push' makes a new resource dict. Any + * subsequent one will share the previous levels one. */ + if (stk->next) + stk->new_rdb = pdf_keep_obj(ctx, stk->next->new_rdb); + else + stk->new_rdb = pdf_new_dict(ctx, p->doc, 1); + + obj = pdf_dict_get(ctx, res, PDF_NAME(Properties)); + if (obj) + pdf_dict_put(ctx, stk->new_rdb, PDF_NAME(Properties), obj); + obj = pdf_dict_get(ctx, res, PDF_NAME(ExtGState)); + if (obj) + pdf_dict_put(ctx, stk->new_rdb, PDF_NAME(ExtGState), obj); + obj = pdf_dict_get(ctx, res, PDF_NAME(Font)); + if (obj) + pdf_dict_put(ctx, stk->new_rdb, PDF_NAME(Font), obj); + + pdf_processor_push_resources(ctx, p->chain, stk->new_rdb); + } + fz_catch(ctx) + { + pdf_drop_obj(ctx, stk->old_rdb); + pdf_drop_obj(ctx, stk->new_rdb); + fz_free(ctx, stk); + p->rstack = stk->next; + fz_rethrow(ctx); + } +} + +static pdf_obj * +pdf_color_pop_resources(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + resources_stack *stk = p->rstack; + + p->rstack = stk->next; + pdf_drop_obj(ctx, stk->old_rdb); + pdf_drop_obj(ctx, stk->new_rdb); + fz_free(ctx, stk); + + return pdf_processor_pop_resources(ctx, p->chain); +} + +static void +pdf_reset_color_processor(fz_context *ctx, pdf_processor *proc) +{ + pdf_color_processor *p = (pdf_color_processor*)proc; + + pdf_reset_processor(ctx, p->chain); +} + +pdf_processor * +pdf_new_color_filter( + fz_context *ctx, + pdf_document *doc, + pdf_processor *chain, + int struct_parents, + fz_matrix transform, + pdf_filter_options *global_options, + void *options_) +{ + pdf_color_processor *proc = pdf_new_processor(ctx, sizeof * proc); + pdf_color_filter_options *options = (pdf_color_filter_options *)options_; + + proc->super.close_processor = pdf_close_color_processor; + proc->super.drop_processor = pdf_drop_color_processor; + proc->super.reset_processor = pdf_reset_color_processor; + + proc->super.push_resources = pdf_color_push_resources; + proc->super.pop_resources = pdf_color_pop_resources; + + /* general graphics state */ + proc->super.op_w = pdf_color_w; + proc->super.op_j = pdf_color_j; + proc->super.op_J = pdf_color_J; + proc->super.op_M = pdf_color_M; + proc->super.op_d = pdf_color_d; + proc->super.op_ri = pdf_color_ri; + proc->super.op_i = pdf_color_i; + proc->super.op_gs_begin = pdf_color_gs_begin; + proc->super.op_gs_end = pdf_color_gs_end; + + /* transparency graphics state */ + proc->super.op_gs_BM = pdf_color_gs_BM; + proc->super.op_gs_CA = pdf_color_gs_CA; + proc->super.op_gs_ca = pdf_color_gs_ca; + proc->super.op_gs_SMask = pdf_color_gs_SMask; + + /* special graphics state */ + proc->super.op_q = pdf_color_q; + proc->super.op_Q = pdf_color_Q; + proc->super.op_cm = pdf_color_cm; + + /* path construction */ + proc->super.op_m = pdf_color_m; + proc->super.op_l = pdf_color_l; + proc->super.op_c = pdf_color_c; + proc->super.op_v = pdf_color_v; + proc->super.op_y = pdf_color_y; + proc->super.op_h = pdf_color_h; + proc->super.op_re = pdf_color_re; + + /* path painting */ + proc->super.op_S = pdf_color_S; + proc->super.op_s = pdf_color_s; + proc->super.op_F = pdf_color_F; + proc->super.op_f = pdf_color_f; + proc->super.op_fstar = pdf_color_fstar; + proc->super.op_B = pdf_color_B; + proc->super.op_Bstar = pdf_color_Bstar; + proc->super.op_b = pdf_color_b; + proc->super.op_bstar = pdf_color_bstar; + proc->super.op_n = pdf_color_n; + + /* clipping paths */ + proc->super.op_W = pdf_color_W; + proc->super.op_Wstar = pdf_color_Wstar; + + /* text objects */ + proc->super.op_BT = pdf_color_BT; + proc->super.op_ET = pdf_color_ET; + + /* text state */ + proc->super.op_Tc = pdf_color_Tc; + proc->super.op_Tw = pdf_color_Tw; + proc->super.op_Tz = pdf_color_Tz; + proc->super.op_TL = pdf_color_TL; + proc->super.op_Tf = pdf_color_Tf; + proc->super.op_Tr = pdf_color_Tr; + proc->super.op_Ts = pdf_color_Ts; + + /* text positioning */ + proc->super.op_Td = pdf_color_Td; + proc->super.op_TD = pdf_color_TD; + proc->super.op_Tm = pdf_color_Tm; + proc->super.op_Tstar = pdf_color_Tstar; + + /* text showing */ + proc->super.op_TJ = pdf_color_TJ; + proc->super.op_Tj = pdf_color_Tj; + proc->super.op_squote = pdf_color_squote; + proc->super.op_dquote = pdf_color_dquote; + + /* type 3 fonts */ + proc->super.op_d0 = pdf_color_d0; + proc->super.op_d1 = pdf_color_d1; + + /* color */ + proc->super.op_CS = pdf_color_CS; + proc->super.op_cs = pdf_color_cs; + proc->super.op_SC_color = pdf_color_SC_color; + proc->super.op_sc_color = pdf_color_sc_color; + proc->super.op_SC_pattern = pdf_color_SC_pattern; + proc->super.op_sc_pattern = pdf_color_sc_pattern; + proc->super.op_SC_shade = pdf_color_SC_shade; + proc->super.op_sc_shade = pdf_color_sc_shade; + + proc->super.op_G = pdf_color_G; + proc->super.op_g = pdf_color_g; + proc->super.op_RG = pdf_color_RG; + proc->super.op_rg = pdf_color_rg; + proc->super.op_K = pdf_color_K; + proc->super.op_k = pdf_color_k; + + /* shadings, images, xobjects */ + proc->super.op_BI = pdf_color_BI; + proc->super.op_sh = pdf_color_sh; + proc->super.op_Do_image = pdf_color_Do_image; + proc->super.op_Do_form = pdf_color_Do_form; + + /* marked content */ + proc->super.op_MP = pdf_color_MP; + proc->super.op_DP = pdf_color_DP; + proc->super.op_BMC = pdf_color_BMC; + proc->super.op_BDC = pdf_color_BDC; + proc->super.op_EMC = pdf_color_EMC; + + /* compatibility */ + proc->super.op_BX = pdf_color_BX; + proc->super.op_EX = pdf_color_EX; + + /* extgstate */ + proc->super.op_gs_OP = pdf_color_gs_OP; + proc->super.op_gs_op = pdf_color_gs_op; + proc->super.op_gs_OPM = pdf_color_gs_OPM; + proc->super.op_gs_UseBlackPtComp = pdf_color_gs_UseBlackPtComp; + + proc->super.op_END = pdf_color_END; + + fz_try(ctx) + proc->gstate = fz_malloc_struct(ctx, gstate_stack); + fz_catch(ctx) + { + fz_free(ctx, proc); + fz_rethrow(ctx); + } + proc->gstate->ctm = fz_identity; + + proc->doc = pdf_keep_document(ctx, doc); + proc->chain = chain; + proc->global_options = global_options; + proc->options = options; + + proc->super.requirements = PDF_PROCESSOR_REQUIRES_DECODED_IMAGES | proc->chain->requirements; + + return (pdf_processor*)proc; +}
