summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2014-03-03 18:20:57 +0000
committerRobin Watts <robin.watts@artifex.com>2014-03-04 11:31:49 +0000
commit67aba4046c0d54b03b5f97e0952316eff98c1c2f (patch)
tree6a97c72f9619404898bd24a9bee8a42ca1a2fefc
parent7c4844060a313833202b91767a878c1d87c4a61c (diff)
downloadmupdf-67aba4046c0d54b03b5f97e0952316eff98c1c2f.tar.xz
Add pdf_process for filtering operator streams.
Currently this knows about q/Q matching/eliding and avoiding repeated/unneccesary color/colorspace setting. It will also collect a dictionary of resources used by a page. This can be extended to be cleverer in future.
-rw-r--r--platform/win32/libmupdf.vcproj4
-rw-r--r--source/pdf/pdf-interpret-imp.h1
-rw-r--r--source/pdf/pdf-op-filter.c1222
3 files changed, 1227 insertions, 0 deletions
diff --git a/platform/win32/libmupdf.vcproj b/platform/win32/libmupdf.vcproj
index 1ac11467..553e7f67 100644
--- a/platform/win32/libmupdf.vcproj
+++ b/platform/win32/libmupdf.vcproj
@@ -806,6 +806,10 @@
>
</File>
<File
+ RelativePath="..\..\source\pdf\pdf-op-filter.c"
+ >
+ </File>
+ <File
RelativePath="..\..\source\pdf\pdf-op-run.c"
>
</File>
diff --git a/source/pdf/pdf-interpret-imp.h b/source/pdf/pdf-interpret-imp.h
index 09decb94..6c3869cf 100644
--- a/source/pdf/pdf-interpret-imp.h
+++ b/source/pdf/pdf-interpret-imp.h
@@ -142,6 +142,7 @@ void pdf_process_stream(pdf_csi *csi, pdf_lexbuf *buf);
/* Functions to set up pdf_process structures */
pdf_process *pdf_process_run(pdf_process *process, fz_device *dev, const fz_matrix *ctm, const char *event, pdf_gstate *gstate, int nested);
pdf_process *pdf_process_buffer(pdf_process *process, fz_context *ctx, fz_buffer *buffer);
+pdf_process *pdf_process_filter(pdf_process *process, fz_context *ctx, pdf_process *underlying, pdf_obj *resources);
/* Functions to actually use the pdf_process structures to process pages,
* annotations and glyphs */
diff --git a/source/pdf/pdf-op-filter.c b/source/pdf/pdf-op-filter.c
new file mode 100644
index 00000000..b938141d
--- /dev/null
+++ b/source/pdf/pdf-op-filter.c
@@ -0,0 +1,1222 @@
+#include "pdf-interpret-imp.h"
+
+typedef struct filter_gstate_s filter_gstate;
+
+typedef enum
+{
+ FLUSH_CTM = 1,
+ FLUSH_COLOR = 2,
+ FLUSH_COLOR_S = 4,
+
+ FLUSH_ALL = 7,
+ FLUSH_STROKE = 1+4,
+ FLUSH_FILL = 1+2
+} gstate_flush_flags;
+
+struct filter_gstate_s
+{
+ filter_gstate *next;
+ int pushed;
+ fz_matrix ctm;
+ fz_matrix current_ctm;
+ float color[FZ_MAX_COLORS];
+ int color_n;
+ float current_color[FZ_MAX_COLORS];
+ int current_color_n;
+ float color_s[FZ_MAX_COLORS];
+ int color_s_n;
+ float current_color_s[FZ_MAX_COLORS];
+ int current_color_s_n;
+ char cs[256];
+ char cs_s[256];
+ char cs_name[256];
+ char cs_name_s[256];
+ char current_cs[256];
+ char current_cs_s[256];
+ char current_cs_name[256];
+ char current_cs_name_s[256];
+};
+
+typedef struct pdf_filter_state_s
+{
+ pdf_process process;
+ fz_context *ctx;
+ filter_gstate *gstate;
+ pdf_obj *resources;
+} pdf_filter_state;
+
+static void insert_resource(pdf_csi *csi, pdf_filter_state *state, const char *key)
+{
+ pdf_obj *xobj;
+ pdf_obj *obj;
+
+ if (!state->resources)
+ return;
+
+ xobj = pdf_dict_gets(csi->rdb, key);
+ obj = pdf_dict_gets(xobj, csi->name);
+
+ xobj = pdf_dict_gets(state->resources, key);
+ if (xobj == NULL) {
+ xobj = pdf_new_dict(csi->doc, 1);
+ pdf_dict_puts(state->resources, key, xobj);
+ }
+ pdf_dict_putp(xobj, csi->name, obj);
+}
+
+static inline void call_op(pdf_csi *csi, pdf_filter_state *state, int op)
+{
+ pdf_process_op(csi, op, &state->process);
+}
+
+static void filter_push(pdf_csi *csi, pdf_filter_state *state)
+{
+ filter_gstate *gstate = state->gstate;
+ filter_gstate *new_gstate = fz_malloc_struct(state->ctx, filter_gstate);
+
+ *new_gstate = *gstate;
+ new_gstate->pushed = 0;
+ new_gstate->next = gstate;
+ state->gstate = new_gstate;
+}
+
+static int filter_pop(pdf_csi *csi, pdf_filter_state *state)
+{
+ filter_gstate *gstate = state->gstate;
+ filter_gstate *old = gstate->next;
+
+ /* We are at the top, so nothing to pop! */
+ if (old == NULL)
+ return 1;
+
+ if (gstate->pushed)
+ call_op(csi, state, PDF_OP_Q);
+
+ fz_free(state->ctx, gstate);
+ state->gstate = old;
+ return 0;
+}
+
+static void forward(pdf_csi *csi, pdf_filter_state *state, int op, float *f_argv, int f_argc, char *arg)
+{
+ int top = csi->top;
+ int to_save = top;
+ float save_f[FZ_MAX_COLORS];
+ char save_name[sizeof(csi->name)];
+ int i;
+
+ /* Store the stack */
+ if (to_save > f_argc)
+ to_save = 6;
+ for (i = 0; i < to_save; i++)
+ {
+ save_f[i] = csi->stack[i];
+ csi->stack[i] = f_argv[i];
+ }
+ for (;i < f_argc; i++)
+ {
+ csi->stack[i] = f_argv[i];
+ }
+ csi->top = f_argc;
+
+ /* Store the name */
+ fz_strlcpy(save_name, csi->name, sizeof(csi->name));
+ if (arg)
+ {
+ fz_strlcpy(csi->name, arg, sizeof(csi->name));
+ }
+ else
+ {
+ csi->name[0] = 0;
+ }
+
+ call_op(csi, state, op);
+
+ /* Restore the name */
+ fz_strlcpy(csi->name, save_name, sizeof(save_name));
+
+ /* Restore the stack */
+ for (i = 0; i < to_save; i++)
+ csi->stack[i] = save_f[i];
+ csi->top = top;
+}
+
+/* We never allow the topmost gstate to be changed. This allows us
+ * to pop back to the zeroth level and be sure that our gstate is
+ * sane. This is important for being able to add new operators at
+ * the end of pages in a sane way. */
+static filter_gstate *
+gstate_to_update(pdf_csi *csi, pdf_filter_state *state)
+{
+ filter_gstate *gstate = state->gstate;
+
+ /* If we're not the top, that's fine */
+ if (gstate->next != NULL)
+ return gstate;
+
+ /* We are the top. Push a group, so we're not */
+ filter_push(csi, state);
+ gstate = state->gstate;
+ gstate->pushed = 1;
+ call_op(csi, state, PDF_OP_q);
+
+ return state->gstate;
+}
+
+static void filter_flush(pdf_csi *csi, pdf_filter_state *state, int flush)
+{
+ filter_gstate *gstate = state->gstate;
+ int i;
+
+ if (gstate->pushed == 0)
+ {
+ gstate->pushed = 1;
+ call_op(csi, state, PDF_OP_q);
+ }
+
+ if (flush & FLUSH_CTM)
+ {
+ if (gstate->ctm.a != 1 || gstate->ctm.b != 0 ||
+ gstate->ctm.c != 0 || gstate->ctm.d != 1 ||
+ gstate->ctm.e != 0 || gstate->ctm.f != 0)
+ {
+ fz_matrix current = gstate->current_ctm;
+
+ forward(csi, state, PDF_OP_cm, (float *)&gstate->ctm.a, 6, NULL);
+ fz_concat(&gstate->current_ctm, &current, &gstate->ctm);
+ gstate->ctm.a = 1;
+ gstate->ctm.b = 0;
+ gstate->ctm.c = 0;
+ gstate->ctm.d = 1;
+ gstate->ctm.e = 0;
+ gstate->ctm.f = 0;
+ }
+ }
+ if (flush & FLUSH_COLOR)
+ {
+ if (strcmp(gstate->cs, gstate->current_cs) ||
+ gstate->color_n != gstate->current_color_n)
+ {
+ /* Colorspace has changed. Send both colorspace (and
+ * color if we have it. */
+ if (!strcmp(gstate->cs, "DeviceRGB"))
+ {
+ forward(csi, state, PDF_OP_rg, gstate->color, 3, NULL);
+ }
+ else if (!strcmp(gstate->cs, "DeviceGray"))
+ {
+ forward(csi, state, PDF_OP_g, gstate->color, 1, NULL);
+ }
+ else if (!strcmp(gstate->cs, "DeviceCMYK"))
+ {
+ forward(csi, state, PDF_OP_k, gstate->color, 4, NULL);
+ }
+ else if (gstate->cs_name[0])
+ {
+ if (strcmp(gstate->cs, gstate->current_cs))
+ {
+ forward(csi, state, PDF_OP_cs, NULL, 0, gstate->cs);
+ }
+ forward(csi, state, PDF_OP_scn, gstate->color, gstate->color_n, gstate->cs_name);
+ }
+ else if (gstate->color_n > 0)
+ {
+ if (strcmp(gstate->cs, gstate->current_cs))
+ {
+ forward(csi, state, PDF_OP_cs, NULL, 0, gstate->cs);
+ }
+ forward(csi, state, PDF_OP_scn, gstate->color, gstate->color_n, NULL);
+ }
+ else
+ {
+ forward(csi, state, PDF_OP_cs, NULL, 0, gstate->cs);
+ }
+ strcpy(gstate->current_cs, gstate->cs);
+ strcpy(gstate->current_cs_name, gstate->cs_name);
+ gstate->current_color_n = gstate->color_n;
+ for (i = 0; i < gstate->color_n; i++)
+ gstate->current_color[i] = gstate->color[i];
+ }
+ else if (strcmp(gstate->cs_name, gstate->current_cs_name))
+ {
+ /* Pattern name has changed */
+ forward(csi, state, PDF_OP_scn, gstate->color, gstate->color_n, gstate->cs_name);
+ strcpy(gstate->current_cs_name, gstate->cs_name);
+ }
+ else
+ {
+ /* Has the color changed? */
+ for (i = 0; i < gstate->color_n; i++)
+ {
+ if (gstate->color[i] != gstate->current_color[i])
+ break;
+ }
+ if (i == gstate->color_n)
+ {
+ /* The color has not changed. Do nothing. */
+ }
+ else if (!strcmp(gstate->cs, "DeviceRGB"))
+ {
+ forward(csi, state, PDF_OP_rg, gstate->color, 3, NULL);
+ }
+ else if (!strcmp(gstate->cs, "DeviceGray"))
+ {
+ forward(csi, state, PDF_OP_g, gstate->color, 1, NULL);
+ }
+ else if (!strcmp(gstate->cs, "DeviceCMYK"))
+ {
+ forward(csi, state, PDF_OP_k, gstate->color, 4, NULL);
+ }
+ else
+ {
+ forward(csi, state, PDF_OP_scn, gstate->color, gstate->color_n, NULL);
+ }
+ for (; i < gstate->color_n; i++)
+ gstate->current_color[i] = gstate->color[i];
+ }
+ }
+ if (flush & FLUSH_COLOR_S)
+ {
+ if (strcmp(gstate->cs_s, gstate->current_cs_s) ||
+ gstate->color_s_n != gstate->current_color_s_n)
+ {
+ /* Colorspace has changed. Send both colorspace (and
+ * color if we have it. */
+ if (!strcmp(gstate->cs_s, "DeviceRGB"))
+ {
+ forward(csi, state, PDF_OP_RG, gstate->color_s, 3, NULL);
+ }
+ else if (!strcmp(gstate->cs_s, "DeviceGray"))
+ {
+ forward(csi, state, PDF_OP_G, gstate->color_s, 1, NULL);
+ }
+ else if (!strcmp(gstate->cs_s, "DeviceCMYK"))
+ {
+ forward(csi, state, PDF_OP_K, gstate->color_s, 4, NULL);
+ }
+ else if (gstate->cs_name_s[0])
+ {
+ if (strcmp(gstate->cs_s, gstate->current_cs_s))
+ {
+ forward(csi, state, PDF_OP_CS, NULL, 0, gstate->cs_s);
+ }
+ forward(csi, state, PDF_OP_SCN, gstate->color_s, gstate->color_s_n, gstate->cs_name_s);
+ }
+ else if (gstate->color_s_n > 0)
+ {
+ if (strcmp(gstate->cs_s, gstate->current_cs_s))
+ {
+ forward(csi, state, PDF_OP_CS, NULL, 0, gstate->cs_s);
+ }
+ forward(csi, state, PDF_OP_SCN, gstate->color_s, gstate->color_s_n, NULL);
+ }
+ else
+ {
+ forward(csi, state, PDF_OP_CS, NULL, 0, gstate->cs_s);
+ }
+ strcpy(gstate->current_cs_s, gstate->cs_s);
+ strcpy(gstate->current_cs_name_s, gstate->cs_name_s);
+ gstate->current_color_s_n = gstate->color_s_n;
+ for (i = 0; i < gstate->color_s_n; i++)
+ gstate->current_color_s[i] = gstate->color_s[i];
+ }
+ else if (strcmp(gstate->cs_name_s, gstate->current_cs_name_s))
+ {
+ /* Pattern name has changed */
+ forward(csi, state, PDF_OP_SCN, gstate->color_s, gstate->color_s_n, gstate->cs_name_s);
+ strcpy(gstate->current_cs_name_s, gstate->cs_name_s);
+ }
+ else
+ {
+ /* Has the color changed? */
+ int i;
+
+ for (i = 0; i < gstate->color_s_n; i++)
+ {
+ if (gstate->color_s[i] != gstate->current_color_s[i])
+ break;
+ }
+ if (i == gstate->color_s_n)
+ {
+ /* The color has not changed. Do nothing. */
+ }
+ else if (!strcmp(gstate->cs_s, "DeviceRGB"))
+ {
+ forward(csi, state, PDF_OP_RG, gstate->color_s, 3, NULL);
+ }
+ else if (!strcmp(gstate->cs_s, "DeviceGray"))
+ {
+ forward(csi, state, PDF_OP_G, gstate->color_s, 1, NULL);
+ }
+ else if (!strcmp(gstate->cs_s, "DeviceCMYK"))
+ {
+ forward(csi, state, PDF_OP_K, gstate->color_s, 4, NULL);
+ }
+ else
+ {
+ forward(csi, state, PDF_OP_SCN, gstate->color_s, gstate->color_s_n, NULL);
+ }
+ for (; i < gstate->color_s_n; i++)
+ gstate->current_color_s[i] = gstate->color_s[i];
+ }
+ }
+}
+
+static void
+pdf_filter_dquote(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_ALL);
+ call_op(csi, state, PDF_OP_dquote);
+}
+
+static void
+pdf_filter_squote(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_ALL);
+ call_op(csi, state, PDF_OP_squote);
+}
+
+static void
+pdf_filter_B(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_ALL);
+ call_op(csi, state, PDF_OP_B);
+}
+
+static void
+pdf_filter_Bstar(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_ALL);
+ call_op(csi, state, PDF_OP_Bstar);
+}
+
+static void
+pdf_filter_BDC(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_BDC);
+}
+
+static void
+pdf_filter_BI(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_FILL);
+ call_op(csi, state, PDF_OP_BI);
+}
+
+static void
+pdf_filter_BMC(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_BMC);
+}
+
+static void
+pdf_filter_BT(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_BT);
+}
+
+static void
+pdf_filter_BX(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_BX);
+}
+
+static void
+pdf_filter_CS(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+ filter_gstate *gstate = gstate_to_update(csi, state);
+
+ insert_resource(csi, state, "ColorSpace");
+
+ fz_strlcpy(gstate->cs_s, csi->name, sizeof(gstate->cs_s));
+ gstate->current_color_s_n = 0;
+}
+
+static void
+pdf_filter_DP(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_DP);
+}
+
+static void
+pdf_filter_EMC(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_EMC);
+}
+
+static void
+pdf_filter_ET(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_ET);
+}
+
+static void
+pdf_filter_EX(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_EX);
+}
+
+static void
+pdf_filter_F(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_FILL);
+ call_op(csi, state, PDF_OP_F);
+}
+
+static void
+pdf_filter_G(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+ filter_gstate *gstate = gstate_to_update(csi, state);
+
+ strcpy(gstate->cs_s, "DeviceGray");
+ strcpy(gstate->cs_name_s, "");
+ gstate->color_s[0] = csi->stack[0];
+ gstate->color_s_n = 1;
+}
+
+static void
+pdf_filter_J(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_J);
+}
+
+static void
+pdf_filter_K(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+ filter_gstate *gstate = gstate_to_update(csi, state);
+
+ strcpy(gstate->cs_s, "DeviceCMYK");
+ strcpy(gstate->cs_name_s, "");
+ gstate->color_s[0] = csi->stack[0];
+ gstate->color_s[1] = csi->stack[1];
+ gstate->color_s[2] = csi->stack[2];
+ gstate->color_s[3] = csi->stack[3];
+ gstate->color_s_n = 4;
+}
+
+static void
+pdf_filter_M(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_M);
+}
+
+static void
+pdf_filter_MP(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_MP);
+}
+
+static void
+pdf_filter_Q(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_pop(csi, state);
+}
+
+static void
+pdf_filter_RG(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+ filter_gstate *gstate = gstate_to_update(csi, state);
+
+ strcpy(gstate->cs_s, "DeviceRGB");
+ strcpy(gstate->cs_name_s, "");
+ gstate->color_s[0] = csi->stack[0];
+ gstate->color_s[1] = csi->stack[1];
+ gstate->color_s[2] = csi->stack[2];
+ gstate->color_s_n = 3;
+}
+
+static void
+pdf_filter_S(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_STROKE);
+ call_op(csi, state, PDF_OP_S);
+}
+
+static void
+pdf_filter_SCN(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+ filter_gstate *gstate = gstate_to_update(csi, state);
+ int i;
+
+ if (csi->name[0])
+ insert_resource(csi, state, "Pattern");
+
+ fz_strlcpy(gstate->cs_name_s, csi->name, sizeof(csi->name));
+ for (i = 0; i < csi->top; i++)
+ {
+ gstate->color_s[i] = csi->stack[i];
+ }
+ gstate->color_s_n = csi->top;
+}
+
+static void
+pdf_filter_Tstar(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_CTM);
+ call_op(csi, state, PDF_OP_Tstar);
+}
+
+static void
+pdf_filter_TD(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_CTM);
+ call_op(csi, state, PDF_OP_TD);
+}
+
+static void
+pdf_filter_TJ(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_ALL);
+ call_op(csi, state, PDF_OP_TJ);
+}
+
+static void
+pdf_filter_TL(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_TL);
+}
+
+static void
+pdf_filter_Tc(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_Tc);
+}
+
+static void
+pdf_filter_Td(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_CTM);
+ call_op(csi, state, PDF_OP_Td);
+}
+
+static void
+pdf_filter_Tj(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_ALL);
+ call_op(csi, state, PDF_OP_Tj);
+}
+
+static void
+pdf_filter_Tm(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_CTM);
+ call_op(csi, state, PDF_OP_Tm);
+}
+
+static void
+pdf_filter_Tr(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_Tr);
+}
+
+static void
+pdf_filter_Ts(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_Ts);
+}
+
+static void
+pdf_filter_Tw(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_Tw);
+}
+
+static void
+pdf_filter_Tz(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_Tz);
+}
+
+static void
+pdf_filter_W(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_CTM);
+ call_op(csi, state, PDF_OP_W);
+}
+
+static void
+pdf_filter_Wstar(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_CTM);
+ call_op(csi, state, PDF_OP_Wstar);
+}
+
+static void
+pdf_filter_b(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_ALL);
+ call_op(csi, state, PDF_OP_b);
+}
+
+static void
+pdf_filter_bstar(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_ALL);
+ call_op(csi, state, PDF_OP_bstar);
+}
+
+static void
+pdf_filter_c(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_CTM);
+ call_op(csi, state, PDF_OP_c);
+}
+
+static void
+pdf_filter_cm(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+ filter_gstate *gstate = gstate_to_update(csi, state);
+ fz_matrix old, ctm;
+
+ ctm.a = (csi->top > 0 ? csi->stack[0] : 0.0);
+ ctm.b = (csi->top > 1 ? csi->stack[1] : 0.0);
+ ctm.c = (csi->top > 2 ? csi->stack[2] : 0.0);
+ ctm.d = (csi->top > 3 ? csi->stack[3] : 0.0);
+ ctm.e = (csi->top > 4 ? csi->stack[4] : 0.0);
+ ctm.f = (csi->top > 5 ? csi->stack[5] : 0.0);
+
+ /* If we're being given an identity matrix, don't bother sending it */
+ if (ctm.a == 1 && ctm.b == 0 && ctm.c == 0 && ctm.d == 1 &&
+ ctm.e == 0.0f && ctm.f == 0)
+ return;
+
+ old = gstate->ctm;
+ fz_concat(&gstate->ctm, &ctm, &old);
+}
+
+static void
+pdf_filter_cs(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+ filter_gstate *gstate = gstate_to_update(csi, state);
+
+ insert_resource(csi, state, "ColorSpace");
+
+ fz_strlcpy(gstate->cs, csi->name, sizeof(csi->name));
+ gstate->color_n = 0;
+}
+
+static void
+pdf_filter_d(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_d);
+}
+
+static void
+pdf_filter_d0(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ call_op(csi, state, PDF_OP_d0);
+}
+
+static void
+pdf_filter_d1(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ call_op(csi, state, PDF_OP_d1);
+}
+
+static void
+pdf_filter_f(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_FILL);
+ call_op(csi, state, PDF_OP_f);
+}
+
+static void
+pdf_filter_fstar(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_FILL);
+ call_op(csi, state, PDF_OP_fstar);
+}
+
+static void
+pdf_filter_g(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+ filter_gstate *gstate = gstate_to_update(csi, state);
+
+ strcpy(gstate->cs, "DeviceGray");
+ strcpy(gstate->cs_name, "");
+ gstate->color[0] = csi->stack[0];
+ gstate->color_n = 1;
+}
+
+static void
+pdf_filter_h(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_CTM);
+ call_op(csi, state, PDF_OP_h);
+}
+
+static void
+pdf_filter_i(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_i);
+}
+
+static void
+pdf_filter_j(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_j);
+}
+
+static void
+pdf_filter_k(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+ filter_gstate *gstate = gstate_to_update(csi, state);
+
+ strcpy(gstate->cs, "DeviceCMYK");
+ strcpy(gstate->cs_name, "");
+ gstate->color[0] = csi->stack[0];
+ gstate->color[1] = csi->stack[1];
+ gstate->color[2] = csi->stack[2];
+ gstate->color[3] = csi->stack[3];
+ gstate->color_n = 4;
+}
+
+static void
+pdf_filter_l(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_CTM);
+ call_op(csi, state, PDF_OP_l);
+}
+
+static void
+pdf_filter_m(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_CTM);
+ call_op(csi, state, PDF_OP_m);
+}
+
+static void
+pdf_filter_n(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_CTM);
+ call_op(csi, state, PDF_OP_n);
+}
+
+static void
+pdf_filter_q(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_push(csi, state);
+}
+
+static void
+pdf_filter_re(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_CTM);
+ call_op(csi, state, PDF_OP_re);
+}
+
+static void
+pdf_filter_rg(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+ filter_gstate *gstate = gstate_to_update(csi, state);
+
+ strcpy(gstate->cs, "DeviceRGB");
+ strcpy(gstate->cs_name, "");
+ gstate->color[0] = csi->stack[0];
+ gstate->color[1] = csi->stack[1];
+ gstate->color[2] = csi->stack[2];
+ gstate->color_n = 3;
+}
+
+static void
+pdf_filter_ri(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_ri);
+}
+
+static void
+pdf_filter_s(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_STROKE);
+ call_op(csi, state, PDF_OP_s);
+}
+
+static void
+pdf_filter_scn(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+ filter_gstate *gstate = gstate_to_update(csi, state);
+ int i;
+
+ if (csi->name[0])
+ insert_resource(csi, state, "Pattern");
+
+ fz_strlcpy(gstate->cs_name, csi->name, sizeof(csi->name));
+ for (i = 0; i < csi->top; i++)
+ {
+ gstate->color[i] = csi->stack[i];
+ }
+ gstate->color_n = csi->top;
+}
+
+static void
+pdf_filter_v(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_CTM);
+ call_op(csi, state, PDF_OP_v);
+}
+
+static void
+pdf_filter_w(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_w);
+}
+
+static void
+pdf_filter_y(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ filter_flush(csi, state, FLUSH_CTM);
+ call_op(csi, state, PDF_OP_y);
+}
+
+static void
+pdf_filter_Do(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ insert_resource(csi, state, "XObject");
+
+ filter_flush(csi, state, FLUSH_ALL);
+ call_op(csi, state, PDF_OP_Do);
+}
+
+static void
+pdf_filter_Tf(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ insert_resource(csi, state, "Font");
+
+ filter_flush(csi, state, 0);
+ call_op(csi, state, PDF_OP_Tf);
+}
+
+static void
+pdf_filter_gs(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ insert_resource(csi, state, "ExtGState");
+
+ filter_flush(csi, state, FLUSH_ALL);
+ call_op(csi, state, PDF_OP_gs);
+}
+
+static void
+pdf_filter_sh(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ insert_resource(csi, state, "Shading");
+
+ filter_flush(csi, state, FLUSH_ALL);
+ call_op(csi, state, PDF_OP_sh);
+}
+
+static void
+free_processor_filter(pdf_csi *csi, void *state_)
+{
+ pdf_filter_state *state = (pdf_filter_state *)state_;
+
+ /* csi can permissibly be NULL, but only in the case when we have
+ * failed while setting up csi. So there is nothing to pop. */
+ if (csi)
+ {
+ while (!filter_pop(csi, state))
+ {
+ /* Nothing to do in the loop, all work done above */
+ }
+ }
+
+ call_op(csi, state, PDF_OP_END);
+ fz_free(state->ctx, state);
+}
+
+static void
+process_annot(pdf_csi *csi, void *state, pdf_obj *resources, pdf_annot *annot)
+{
+ fz_context *ctx = csi->doc->ctx;
+ pdf_xobject *xobj = annot->ap;
+
+ /* Avoid infinite recursion */
+ if (xobj == NULL || pdf_mark_obj(xobj->me))
+ return;
+
+ fz_try(ctx)
+ {
+ if (xobj->resources)
+ resources = xobj->resources;
+
+ pdf_process_contents_object(csi, resources, xobj->contents);
+ }
+ fz_always(ctx)
+ {
+ pdf_unmark_obj(xobj->me);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+static void
+process_stream(pdf_csi *csi, void *state, pdf_lexbuf *buf)
+{
+ pdf_process_stream(csi, buf);
+}
+
+static void
+process_contents(pdf_csi *csi, void *state, pdf_obj *resources, pdf_obj *contents)
+{
+ pdf_process_contents_object(csi, resources, contents);
+}
+
+static const pdf_processor pdf_processor_filter =
+{
+ {
+ pdf_filter_dquote,
+ pdf_filter_squote,
+ pdf_filter_B,
+ pdf_filter_Bstar,
+ pdf_filter_BDC,
+ pdf_filter_BI,
+ pdf_filter_BMC,
+ pdf_filter_BT,
+ pdf_filter_BX,
+ pdf_filter_CS,
+ pdf_filter_DP,
+ pdf_filter_EMC,
+ pdf_filter_ET,
+ pdf_filter_EX,
+ pdf_filter_F,
+ pdf_filter_G,
+ pdf_filter_J,
+ pdf_filter_K,
+ pdf_filter_M,
+ pdf_filter_MP,
+ pdf_filter_Q,
+ pdf_filter_RG,
+ pdf_filter_S,
+ pdf_filter_SCN,
+ pdf_filter_SCN,
+ pdf_filter_Tstar,
+ pdf_filter_TD,
+ pdf_filter_TJ,
+ pdf_filter_TL,
+ pdf_filter_Tc,
+ pdf_filter_Td,
+ pdf_filter_Tj,
+ pdf_filter_Tm,
+ pdf_filter_Tr,
+ pdf_filter_Ts,
+ pdf_filter_Tw,
+ pdf_filter_Tz,
+ pdf_filter_W,
+ pdf_filter_Wstar,
+ pdf_filter_b,
+ pdf_filter_bstar,
+ pdf_filter_c,
+ pdf_filter_cm,
+ pdf_filter_cs,
+ pdf_filter_d,
+ pdf_filter_d0,
+ pdf_filter_d1,
+ pdf_filter_f,
+ pdf_filter_fstar,
+ pdf_filter_g,
+ pdf_filter_h,
+ pdf_filter_i,
+ pdf_filter_j,
+ pdf_filter_k,
+ pdf_filter_l,
+ pdf_filter_m,
+ pdf_filter_n,
+ pdf_filter_q,
+ pdf_filter_re,
+ pdf_filter_rg,
+ pdf_filter_ri,
+ pdf_filter_s,
+ pdf_filter_scn,
+ pdf_filter_scn,
+ pdf_filter_v,
+ pdf_filter_w,
+ pdf_filter_y,
+ pdf_filter_Do,
+ pdf_filter_Tf,
+ pdf_filter_gs,
+ pdf_filter_sh,
+ free_processor_filter
+ },
+ process_annot,
+ process_stream,
+ process_contents
+};
+
+pdf_process *
+pdf_process_filter(pdf_process *process, fz_context *ctx, pdf_process *underlying, pdf_obj *resources)
+{
+ pdf_filter_state *p = NULL;
+
+ fz_var(p);
+
+ fz_try(ctx)
+ {
+ p = fz_malloc_struct(ctx, pdf_filter_state);
+ p->ctx = ctx;
+ p->process = *underlying;
+ p->gstate = fz_malloc_struct(ctx, filter_gstate);
+ p->resources = resources;
+ p->gstate->ctm = fz_identity;
+ p->gstate->current_ctm = fz_identity;
+ }
+ fz_catch(ctx)
+ {
+ fz_free(ctx, p);
+ pdf_process_op(NULL, PDF_OP_END, underlying);
+ fz_rethrow(ctx);
+ }
+
+ process->state = p;
+ process->processor = &pdf_processor_filter;
+ return process;
+}