summaryrefslogtreecommitdiff
path: root/source/pdf/pdf-op-run.c
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2014-03-03 18:07:02 +0000
committerRobin Watts <robin.watts@artifex.com>2014-03-04 11:26:47 +0000
commitb83d0c4c1ad97c0ff68fcbfaf4aceb5fc7e4e642 (patch)
tree615cf067be3d23d06cf34533e788ba76a4e0a9d7 /source/pdf/pdf-op-run.c
parent484f10977e73fbafe1bfc2eda30d4426e75df265 (diff)
downloadmupdf-b83d0c4c1ad97c0ff68fcbfaf4aceb5fc7e4e642.tar.xz
Add pdf_process interface.
Currently the only processing we can do of PDF pages is to run them through an fz_device. We introduce new "pdf_process" functionality here to enable us to do more things. We define a pdf_processor structure with a set of function pointers in, one per PDF operator, together with functions for processing xobjects etc. The guts of pdf_run_page_contents and pdf_run_annot operations are then extracted to give pdf_process_page_contents and pdf_process_annot, and the originals implemented in terms of these. This commit contains just one instance of a pdf_processor, namely the "run" processor, which contains the original code refactored. The graphical state (and device pointer) is now part of private data to the run operator set, rather than being in pdf_csi.
Diffstat (limited to 'source/pdf/pdf-op-run.c')
-rw-r--r--source/pdf/pdf-op-run.c2838
1 files changed, 2838 insertions, 0 deletions
diff --git a/source/pdf/pdf-op-run.c b/source/pdf/pdf-op-run.c
new file mode 100644
index 00000000..22dc76f2
--- /dev/null
+++ b/source/pdf/pdf-op-run.c
@@ -0,0 +1,2838 @@
+#include "pdf-interpret-imp.h"
+
+#define TILE
+
+/*
+ * Emit graphics calls to device.
+ */
+
+typedef struct pdf_material_s pdf_material;
+
+enum
+{
+ PDF_FILL,
+ PDF_STROKE,
+};
+
+enum
+{
+ PDF_MAT_NONE,
+ PDF_MAT_COLOR,
+ PDF_MAT_PATTERN,
+ PDF_MAT_SHADE,
+};
+
+struct pdf_material_s
+{
+ int kind;
+ fz_colorspace *colorspace;
+ pdf_pattern *pattern;
+ fz_shade *shade;
+ int gstate_num;
+ float alpha;
+ float v[FZ_MAX_COLORS];
+};
+
+struct pdf_gstate_s
+{
+ fz_matrix ctm;
+ int clip_depth;
+
+ /* path stroking */
+ fz_stroke_state *stroke_state;
+
+ /* materials */
+ pdf_material stroke;
+ pdf_material fill;
+
+ /* text state */
+ float char_space;
+ float word_space;
+ float scale;
+ float leading;
+ pdf_font_desc *font;
+ float size;
+ int render;
+ float rise;
+
+ /* transparency */
+ int blendmode;
+ pdf_xobject *softmask;
+ fz_matrix softmask_ctm;
+ float softmask_bc[FZ_MAX_COLORS];
+ int luminosity;
+};
+
+typedef struct pdf_run_state_s
+{
+ fz_context *ctx;
+ fz_device *dev;
+ pdf_csi *csi;
+
+ int nested_depth;
+ int in_hidden_ocg;
+
+ /* path object state */
+ fz_path *path;
+ int clip;
+ int clip_even_odd;
+ const char *event;
+
+ /* text object state */
+ fz_text *text;
+ fz_rect text_bbox;
+ fz_matrix tlm;
+ fz_matrix tm;
+ int text_mode;
+ int accumulate;
+
+ /* graphics state */
+ pdf_gstate *gstate;
+ int gcap;
+ int gtop;
+ int gbot;
+ int gparent;
+}
+pdf_run_state;
+
+typedef struct softmask_save_s softmask_save;
+
+struct softmask_save_s
+{
+ pdf_xobject *softmask;
+ fz_matrix ctm;
+};
+
+static void
+run_xobject(pdf_csi *csi, void *state, pdf_obj *resources, pdf_xobject *xobj, const fz_matrix *transform);
+
+static int
+ocg_intents_include(pdf_ocg_descriptor *desc, char *name)
+{
+ int i, len;
+
+ if (strcmp(name, "All") == 0)
+ return 1;
+
+ /* In the absence of a specified intent, it's 'View' */
+ if (!desc->intent)
+ return (strcmp(name, "View") == 0);
+
+ if (pdf_is_name(desc->intent))
+ {
+ char *intent = pdf_to_name(desc->intent);
+ if (strcmp(intent, "All") == 0)
+ return 1;
+ return (strcmp(intent, name) == 0);
+ }
+ if (!pdf_is_array(desc->intent))
+ return 0;
+
+ len = pdf_array_len(desc->intent);
+ for (i=0; i < len; i++)
+ {
+ char *intent = pdf_to_name(pdf_array_get(desc->intent, i));
+ if (strcmp(intent, "All") == 0)
+ return 1;
+ if (strcmp(intent, name) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+int
+pdf_is_hidden_ocg(pdf_obj *ocg, pdf_csi *csi, pdf_run_state *pr, pdf_obj *rdb)
+{
+ char event_state[16];
+ pdf_obj *obj, *obj2;
+ char *type;
+ pdf_ocg_descriptor *desc = csi->doc->ocg;
+ fz_context *ctx = csi->doc->ctx;
+
+ /* Avoid infinite recursions */
+ if (pdf_obj_marked(ocg))
+ return 0;
+
+ /* If no ocg descriptor, everything is visible */
+ if (!desc)
+ return 0;
+
+ /* If we've been handed a name, look it up in the properties. */
+ if (pdf_is_name(ocg))
+ {
+ ocg = pdf_dict_gets(pdf_dict_gets(rdb, "Properties"), pdf_to_name(ocg));
+ }
+ /* If we haven't been given an ocg at all, then we're visible */
+ if (!ocg)
+ return 0;
+
+ fz_strlcpy(event_state, pr->event, sizeof event_state);
+ fz_strlcat(event_state, "State", sizeof event_state);
+
+ type = pdf_to_name(pdf_dict_gets(ocg, "Type"));
+
+ if (strcmp(type, "OCG") == 0)
+ {
+ /* An Optional Content Group */
+ int default_value = 0;
+ int num = pdf_to_num(ocg);
+ int gen = pdf_to_gen(ocg);
+ int len = desc->len;
+ int i;
+
+ /* by default an OCG is visible, unless it's explicitly hidden */
+ for (i = 0; i < len; i++)
+ {
+ if (desc->ocgs[i].num == num && desc->ocgs[i].gen == gen)
+ {
+ default_value = desc->ocgs[i].state == 0;
+ break;
+ }
+ }
+
+ /* Check Intents; if our intent is not part of the set given
+ * by the current config, we should ignore it. */
+ obj = pdf_dict_gets(ocg, "Intent");
+ if (pdf_is_name(obj))
+ {
+ /* If it doesn't match, it's hidden */
+ if (ocg_intents_include(desc, pdf_to_name(obj)) == 0)
+ return 1;
+ }
+ else if (pdf_is_array(obj))
+ {
+ int match = 0;
+ len = pdf_array_len(obj);
+ for (i=0; i<len; i++) {
+ match |= ocg_intents_include(desc, pdf_to_name(pdf_array_get(obj, i)));
+ if (match)
+ break;
+ }
+ /* If we don't match any, it's hidden */
+ if (match == 0)
+ return 1;
+ }
+ else
+ {
+ /* If it doesn't match, it's hidden */
+ if (ocg_intents_include(desc, "View") == 0)
+ return 1;
+ }
+
+ /* FIXME: Currently we do a very simple check whereby we look
+ * at the Usage object (an Optional Content Usage Dictionary)
+ * and check to see if the corresponding 'event' key is on
+ * or off.
+ *
+ * Really we should only look at Usage dictionaries that
+ * correspond to entries in the AS list in the OCG config.
+ * Given that we don't handle Zoom or User, or Language
+ * dicts, this is not really a problem. */
+ obj = pdf_dict_gets(ocg, "Usage");
+ if (!pdf_is_dict(obj))
+ return default_value;
+ /* FIXME: Should look at Zoom (and return hidden if out of
+ * max/min range) */
+ /* FIXME: Could provide hooks to the caller to check if
+ * User is appropriate - if not return hidden. */
+ obj2 = pdf_dict_gets(obj, pr->event);
+ if (strcmp(pdf_to_name(pdf_dict_gets(obj2, event_state)), "OFF") == 0)
+ {
+ return 1;
+ }
+ if (strcmp(pdf_to_name(pdf_dict_gets(obj2, event_state)), "ON") == 0)
+ {
+ return 0;
+ }
+ return default_value;
+ }
+ else if (strcmp(type, "OCMD") == 0)
+ {
+ /* An Optional Content Membership Dictionary */
+ char *name;
+ int combine, on;
+
+ obj = pdf_dict_gets(ocg, "VE");
+ if (pdf_is_array(obj)) {
+ /* FIXME: Calculate visibility from array */
+ return 0;
+ }
+ name = pdf_to_name(pdf_dict_gets(ocg, "P"));
+ /* Set combine; Bit 0 set => AND, Bit 1 set => true means
+ * Off, otherwise true means On */
+ if (strcmp(name, "AllOn") == 0)
+ {
+ combine = 1;
+ }
+ else if (strcmp(name, "AnyOff") == 0)
+ {
+ combine = 2;
+ }
+ else if (strcmp(name, "AllOff") == 0)
+ {
+ combine = 3;
+ }
+ else /* Assume it's the default (AnyOn) */
+ {
+ combine = 0;
+ }
+
+ if (pdf_mark_obj(ocg))
+ return 0; /* Should never happen */
+ fz_try(ctx)
+ {
+ obj = pdf_dict_gets(ocg, "OCGs");
+ on = combine & 1;
+ if (pdf_is_array(obj)) {
+ int i, len;
+ len = pdf_array_len(obj);
+ for (i = 0; i < len; i++)
+ {
+ int hidden;
+ hidden = pdf_is_hidden_ocg(pdf_array_get(obj, i), csi, pr, rdb);
+ if ((combine & 1) == 0)
+ hidden = !hidden;
+ if (combine & 2)
+ on &= hidden;
+ else
+ on |= hidden;
+ }
+ }
+ else
+ {
+ on = pdf_is_hidden_ocg(obj, csi, pr, rdb);
+ if ((combine & 1) == 0)
+ on = !on;
+ }
+ }
+ fz_always(ctx)
+ {
+ pdf_unmark_obj(ocg);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+ return !on;
+ }
+ /* No idea what sort of object this is - be visible */
+ return 0;
+}
+
+static pdf_gstate *
+begin_softmask(pdf_csi *csi, pdf_run_state *pr, softmask_save *save)
+{
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+ pdf_xobject *softmask = gstate->softmask;
+ fz_rect mask_bbox;
+ fz_context *ctx;
+ fz_matrix save_tm, save_tlm, save_ctm;
+
+ save->softmask = softmask;
+ if (softmask == NULL)
+ return gstate;
+ save->ctm = gstate->softmask_ctm;
+ save_ctm = gstate->ctm;
+
+ mask_bbox = softmask->bbox;
+ ctx = pr->ctx;
+ save_tm = pr->tm;
+ save_tlm = pr->tlm;
+
+ if (gstate->luminosity)
+ mask_bbox = fz_infinite_rect;
+ else
+ {
+ fz_transform_rect(&mask_bbox, &softmask->matrix);
+ fz_transform_rect(&mask_bbox, &gstate->softmask_ctm);
+ }
+ gstate->softmask = NULL;
+ gstate->ctm = gstate->softmask_ctm;
+
+ fz_begin_mask(pr->dev, &mask_bbox, gstate->luminosity,
+ softmask->colorspace, gstate->softmask_bc);
+ fz_try(ctx)
+ {
+ run_xobject(csi, pr, NULL, softmask, &fz_identity);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
+ /* FIXME: Ignore error - nasty, but if we throw from
+ * here the clip stack would be messed up. */
+ if (csi->cookie)
+ csi->cookie->errors++;
+ }
+
+ fz_end_mask(pr->dev);
+
+ pr->tm = save_tm;
+ pr->tlm = save_tlm;
+
+ gstate = pr->gstate + pr->gtop;
+ gstate->ctm = save_ctm;
+
+ return gstate;
+}
+
+static void
+end_softmask(pdf_csi *csi, pdf_run_state *pr, softmask_save *save)
+{
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+
+ if (save->softmask == NULL)
+ return;
+
+ gstate->softmask = save->softmask;
+ gstate->softmask_ctm = save->ctm;
+ fz_pop_clip(pr->dev);
+}
+
+static pdf_gstate *
+pdf_begin_group(pdf_csi *csi, pdf_run_state *pr, const fz_rect *bbox, softmask_save *softmask)
+{
+ pdf_gstate *gstate = begin_softmask(csi, pr, softmask);
+
+ if (gstate->blendmode)
+ fz_begin_group(pr->dev, bbox, 1, 0, gstate->blendmode, 1);
+
+ return pr->gstate + pr->gtop;
+}
+
+static void
+pdf_end_group(pdf_csi *csi, pdf_run_state *pr, softmask_save *softmask)
+{
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+
+ if (gstate->blendmode)
+ fz_end_group(pr->dev);
+
+ end_softmask(csi, pr, softmask);
+}
+
+static void
+pdf_show_shade(pdf_csi *csi, pdf_run_state *pr, fz_shade *shd)
+{
+ fz_context *ctx = pr->ctx;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+ fz_rect bbox;
+ softmask_save softmask = { NULL };
+
+ if (pr->in_hidden_ocg > 0)
+ return;
+
+ fz_bound_shade(ctx, shd, &gstate->ctm, &bbox);
+
+ gstate = pdf_begin_group(csi, pr, &bbox, &softmask);
+
+ /* FIXME: The gstate->ctm in the next line may be wrong; maybe
+ * it should be the parent gstates ctm? */
+ fz_fill_shade(pr->dev, shd, &gstate->ctm, gstate->fill.alpha);
+
+ pdf_end_group(csi, pr, &softmask);
+}
+
+static pdf_material *
+pdf_keep_material(fz_context *ctx, pdf_material *mat)
+{
+ if (mat->colorspace)
+ fz_keep_colorspace(ctx, mat->colorspace);
+ if (mat->pattern)
+ pdf_keep_pattern(ctx, mat->pattern);
+ if (mat->shade)
+ fz_keep_shade(ctx, mat->shade);
+ return mat;
+}
+
+static pdf_material *
+pdf_drop_material(fz_context *ctx, pdf_material *mat)
+{
+ if (mat->colorspace)
+ fz_drop_colorspace(ctx, mat->colorspace);
+ if (mat->pattern)
+ pdf_drop_pattern(ctx, mat->pattern);
+ if (mat->shade)
+ fz_drop_shade(ctx, mat->shade);
+ return mat;
+}
+
+static void
+pdf_copy_pattern_gstate(fz_context *ctx, pdf_gstate *gs, const pdf_gstate *old)
+{
+ gs->ctm = old->ctm;
+
+ pdf_drop_font(ctx, gs->font);
+ gs->font = pdf_keep_font(ctx, old->font);
+
+ pdf_drop_xobject(ctx, gs->softmask);
+ gs->softmask = pdf_keep_xobject(ctx, old->softmask);
+
+ fz_drop_stroke_state(ctx, gs->stroke_state);
+ gs->stroke_state = fz_keep_stroke_state(ctx, old->stroke_state);
+}
+
+static void
+pdf_unset_pattern(pdf_run_state *pr, int what)
+{
+ fz_context *ctx = pr->ctx;
+ pdf_gstate *gs = pr->gstate + pr->gtop;
+ pdf_material *mat;
+ mat = what == PDF_FILL ? &gs->fill : &gs->stroke;
+ if (mat->kind == PDF_MAT_PATTERN)
+ {
+ if (mat->pattern)
+ pdf_drop_pattern(ctx, mat->pattern);
+ mat->pattern = NULL;
+ mat->kind = PDF_MAT_COLOR;
+ }
+}
+
+static void
+pdf_keep_gstate(fz_context *ctx, pdf_gstate *gs)
+{
+ pdf_keep_material(ctx, &gs->stroke);
+ pdf_keep_material(ctx, &gs->fill);
+ if (gs->font)
+ pdf_keep_font(ctx, gs->font);
+ if (gs->softmask)
+ pdf_keep_xobject(ctx, gs->softmask);
+ fz_keep_stroke_state(ctx, gs->stroke_state);
+}
+
+static void
+pdf_drop_gstate(fz_context *ctx, pdf_gstate *gs)
+{
+ pdf_drop_material(ctx, &gs->stroke);
+ pdf_drop_material(ctx, &gs->fill);
+ if (gs->font)
+ pdf_drop_font(ctx, gs->font);
+ if (gs->softmask)
+ pdf_drop_xobject(ctx, gs->softmask);
+ fz_drop_stroke_state(ctx, gs->stroke_state);
+}
+
+static void
+pdf_gsave(pdf_run_state *pr)
+{
+ fz_context *ctx = pr->ctx;
+
+ if (pr->gtop == pr->gcap-1)
+ {
+ pr->gstate = fz_resize_array(ctx, pr->gstate, pr->gcap*2, sizeof(pdf_gstate));
+ pr->gcap *= 2;
+ }
+
+ memcpy(&pr->gstate[pr->gtop + 1], &pr->gstate[pr->gtop], sizeof(pdf_gstate));
+
+ pr->gtop++;
+ pdf_keep_gstate(ctx, &pr->gstate[pr->gtop]);
+}
+
+static void
+pdf_grestore(pdf_run_state *pr)
+{
+ fz_context *ctx = pr->ctx;
+ pdf_gstate *gs = pr->gstate + pr->gtop;
+ int clip_depth = gs->clip_depth;
+
+ if (pr->gtop <= pr->gbot)
+ {
+ fz_warn(ctx, "gstate underflow in content stream");
+ return;
+ }
+
+ pdf_drop_gstate(ctx, gs);
+ pr->gtop --;
+
+ gs = pr->gstate + pr->gtop;
+ while (clip_depth > gs->clip_depth)
+ {
+ fz_try(ctx)
+ {
+ fz_pop_clip(pr->dev);
+ }
+ fz_catch(ctx)
+ {
+ /* Silently swallow the problem - restores must
+ * never throw! */
+ }
+ clip_depth--;
+ }
+}
+
+static void
+pdf_show_pattern(pdf_csi *csi, pdf_run_state *pr, pdf_pattern *pat, pdf_gstate *pat_gstate, const fz_rect *area, int what)
+{
+ fz_context *ctx = pr->ctx;
+ pdf_gstate *gstate;
+ int gparent_save;
+ fz_matrix ptm, invptm, gparent_save_ctm;
+ int x0, y0, x1, y1;
+ float fx0, fy0, fx1, fy1;
+ int oldtop;
+ fz_rect local_area;
+
+ pdf_gsave(pr);
+ gstate = pr->gstate + pr->gtop;
+ /* Patterns are run with the gstate of the parent */
+ pdf_copy_pattern_gstate(ctx, gstate, pat_gstate);
+
+ if (pat->ismask)
+ {
+ pdf_unset_pattern(pr, PDF_FILL);
+ pdf_unset_pattern(pr, PDF_STROKE);
+ if (what == PDF_FILL)
+ {
+ pdf_drop_material(ctx, &gstate->stroke);
+ pdf_keep_material(ctx, &gstate->fill);
+ gstate->stroke = gstate->fill;
+ }
+ if (what == PDF_STROKE)
+ {
+ pdf_drop_material(ctx, &gstate->fill);
+ pdf_keep_material(ctx, &gstate->stroke);
+ gstate->fill = gstate->stroke;
+ }
+ }
+ else
+ {
+ // TODO: unset only the current fill/stroke or both?
+ pdf_unset_pattern(pr, what);
+ }
+
+ /* don't apply soft masks to objects in the pattern as well */
+ if (gstate->softmask)
+ {
+ pdf_drop_xobject(ctx, gstate->softmask);
+ gstate->softmask = NULL;
+ }
+
+ fz_concat(&ptm, &pat->matrix, &pat_gstate->ctm);
+ fz_invert_matrix(&invptm, &ptm);
+
+ /* The parent_ctm is amended with our pattern matrix */
+ gparent_save = pr->gparent;
+ pr->gparent = pr->gtop-1;
+ gparent_save_ctm = pr->gstate[pr->gparent].ctm;
+ pr->gstate[pr->gparent].ctm = ptm;
+
+ fz_try(ctx)
+ {
+ /* patterns are painted using the parent_ctm. area = bbox of
+ * shape to be filled in device space. Map it back to pattern
+ * space. */
+ local_area = *area;
+ fz_transform_rect(&local_area, &invptm);
+
+ fx0 = (local_area.x0 - pat->bbox.x0) / pat->xstep;
+ fy0 = (local_area.y0 - pat->bbox.y0) / pat->ystep;
+ fx1 = (local_area.x1 - pat->bbox.x0) / pat->xstep;
+ fy1 = (local_area.y1 - pat->bbox.y0) / pat->ystep;
+ if (fx0 > fx1)
+ {
+ float t = fx0; fx0 = fx1; fx1 = t;
+ }
+ if (fy0 > fy1)
+ {
+ float t = fy0; fy0 = fy1; fy1 = t;
+ }
+
+ oldtop = pr->gtop;
+
+#ifdef TILE
+ /* We have tried various formulations in the past, but this one is
+ * best we've found; only use it as a tile if a whole repeat is
+ * required in at least one direction. Note, that this allows for
+ * 'sections' of 4 tiles to be show, but all non-overlapping. */
+ if (fx1-fx0 > 1 || fy1-fy0 > 1)
+#else
+ if (0)
+#endif
+ {
+ fz_begin_tile(pr->dev, &local_area, &pat->bbox, pat->xstep, pat->ystep, &ptm);
+ gstate->ctm = ptm;
+ pdf_gsave(pr);
+ pdf_process_contents_object(csi, pat->resources, pat->contents);
+ pdf_grestore(pr);
+ while (oldtop < pr->gtop)
+ pdf_grestore(pr);
+ fz_end_tile(pr->dev);
+ }
+ else
+ {
+ int x, y;
+
+ /* When calculating the number of tiles required, we adjust by
+ * a small amount to allow for rounding errors. By choosing
+ * this amount to be smaller than 1/256, we guarantee we won't
+ * cause problems that will be visible even under our most
+ * extreme antialiasing. */
+ x0 = floorf(fx0 + 0.001);
+ y0 = floorf(fy0 + 0.001);
+ x1 = ceilf(fx1 - 0.001);
+ y1 = ceilf(fy1 - 0.001);
+
+ for (y = y0; y < y1; y++)
+ {
+ for (x = x0; x < x1; x++)
+ {
+ gstate->ctm = ptm;
+ fz_pre_translate(&gstate->ctm, x * pat->xstep, y * pat->ystep);
+ pdf_gsave(pr);
+ fz_try(ctx)
+ {
+ pdf_process_contents_object(csi, pat->resources, pat->contents);
+ }
+ fz_always(ctx)
+ {
+ pdf_grestore(pr);
+ while (oldtop < pr->gtop)
+ pdf_grestore(pr);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow_message(ctx, "cannot render pattern tile");
+ }
+ }
+ }
+ }
+ }
+ fz_always(ctx)
+ {
+ pr->gstate[pr->gparent].ctm = gparent_save_ctm;
+ pr->gparent = gparent_save;
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+
+ pdf_grestore(pr);
+}
+
+static void
+pdf_show_image(pdf_csi *csi, pdf_run_state *pr, fz_image *image)
+{
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+ fz_matrix image_ctm;
+ fz_rect bbox;
+ softmask_save softmask = { NULL };
+
+ if (pr->in_hidden_ocg > 0)
+ return;
+
+ /* PDF has images bottom-up, so flip them right side up here */
+ image_ctm = gstate->ctm;
+ fz_pre_scale(fz_pre_translate(&image_ctm, 0, 1), 1, -1);
+
+ bbox = fz_unit_rect;
+ fz_transform_rect(&bbox, &image_ctm);
+
+ if (image->mask)
+ {
+ /* apply blend group even though we skip the soft mask */
+ if (gstate->blendmode)
+ fz_begin_group(pr->dev, &bbox, 0, 0, gstate->blendmode, 1);
+ fz_clip_image_mask(pr->dev, image->mask, &bbox, &image_ctm);
+ }
+ else
+ gstate = pdf_begin_group(csi, pr, &bbox, &softmask);
+
+ if (!image->colorspace)
+ {
+
+ switch (gstate->fill.kind)
+ {
+ case PDF_MAT_NONE:
+ break;
+ case PDF_MAT_COLOR:
+ fz_fill_image_mask(pr->dev, image, &image_ctm,
+ gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha);
+ break;
+ case PDF_MAT_PATTERN:
+ if (gstate->fill.pattern)
+ {
+ fz_clip_image_mask(pr->dev, image, &bbox, &image_ctm);
+ pdf_show_pattern(csi, pr, gstate->fill.pattern, &pr->gstate[gstate->fill.gstate_num], &bbox, PDF_FILL);
+ fz_pop_clip(pr->dev);
+ }
+ break;
+ case PDF_MAT_SHADE:
+ if (gstate->fill.shade)
+ {
+ fz_clip_image_mask(pr->dev, image, &bbox, &image_ctm);
+ fz_fill_shade(pr->dev, gstate->fill.shade, &pr->gstate[gstate->fill.gstate_num].ctm, gstate->fill.alpha);
+ fz_pop_clip(pr->dev);
+ }
+ break;
+ }
+ }
+ else
+ {
+ fz_fill_image(pr->dev, image, &image_ctm, gstate->fill.alpha);
+ }
+
+ if (image->mask)
+ {
+ fz_pop_clip(pr->dev);
+ if (gstate->blendmode)
+ fz_end_group(pr->dev);
+ }
+ else
+ pdf_end_group(csi, pr, &softmask);
+}
+
+static void
+pdf_show_path(pdf_csi *csi, pdf_run_state *pr, int doclose, int dofill, int dostroke, int even_odd)
+{
+ fz_context *ctx = pr->ctx;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+ fz_path *path;
+ fz_rect bbox;
+ softmask_save softmask = { NULL };
+ int knockout_group = 0;
+
+ if (dostroke) {
+ if (pr->dev->flags & (FZ_DEVFLAG_STROKECOLOR_UNDEFINED | FZ_DEVFLAG_LINEJOIN_UNDEFINED | FZ_DEVFLAG_LINEWIDTH_UNDEFINED))
+ pr->dev->flags |= FZ_DEVFLAG_UNCACHEABLE;
+ else if (gstate->stroke_state->dash_len != 0 && pr->dev->flags & (FZ_DEVFLAG_STARTCAP_UNDEFINED | FZ_DEVFLAG_DASHCAP_UNDEFINED | FZ_DEVFLAG_ENDCAP_UNDEFINED))
+ pr->dev->flags |= FZ_DEVFLAG_UNCACHEABLE;
+ else if (gstate->stroke_state->linejoin == FZ_LINEJOIN_MITER && (pr->dev->flags & FZ_DEVFLAG_MITERLIMIT_UNDEFINED))
+ pr->dev->flags |= FZ_DEVFLAG_UNCACHEABLE;
+ }
+ if (dofill) {
+ if (pr->dev->flags & FZ_DEVFLAG_FILLCOLOR_UNDEFINED)
+ pr->dev->flags |= FZ_DEVFLAG_UNCACHEABLE;
+ }
+
+ path = pr->path;
+ pr->path = fz_new_path(ctx);
+
+ fz_try(ctx)
+ {
+ if (doclose)
+ fz_closepath(ctx, path);
+
+ fz_bound_path(ctx, path, (dostroke ? gstate->stroke_state : NULL), &gstate->ctm, &bbox);
+
+ if (pr->clip)
+ {
+ gstate->clip_depth++;
+ fz_clip_path(pr->dev, path, &bbox, pr->clip_even_odd, &gstate->ctm);
+ pr->clip = 0;
+ }
+
+ if (pr->in_hidden_ocg > 0)
+ dostroke = dofill = 0;
+
+ if (dofill || dostroke)
+ gstate = pdf_begin_group(csi, pr, &bbox, &softmask);
+
+ if (dofill && dostroke)
+ {
+ /* We may need to push a knockout group */
+ if (gstate->stroke.alpha == 0)
+ {
+ /* No need for group, as stroke won't do anything */
+ }
+ else if (gstate->stroke.alpha == 1.0f && gstate->blendmode == FZ_BLEND_NORMAL)
+ {
+ /* No need for group, as stroke won't show up */
+ }
+ else
+ {
+ knockout_group = 1;
+ fz_begin_group(pr->dev, &bbox, 0, 1, FZ_BLEND_NORMAL, 1);
+ }
+ }
+
+ if (dofill)
+ {
+ switch (gstate->fill.kind)
+ {
+ case PDF_MAT_NONE:
+ break;
+ case PDF_MAT_COLOR:
+ fz_fill_path(pr->dev, path, even_odd, &gstate->ctm,
+ gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha);
+ break;
+ case PDF_MAT_PATTERN:
+ if (gstate->fill.pattern)
+ {
+ fz_clip_path(pr->dev, path, &bbox, even_odd, &gstate->ctm);
+ pdf_show_pattern(csi, pr, gstate->fill.pattern, &pr->gstate[gstate->fill.gstate_num], &bbox, PDF_FILL);
+ fz_pop_clip(pr->dev);
+ }
+ break;
+ case PDF_MAT_SHADE:
+ if (gstate->fill.shade)
+ {
+ fz_clip_path(pr->dev, path, &bbox, even_odd, &gstate->ctm);
+ /* The cluster and page 2 of patterns.pdf shows that fz_fill_shade should NOT be called with gstate->ctm. */
+ fz_fill_shade(pr->dev, gstate->fill.shade, &pr->gstate[gstate->fill.gstate_num].ctm, gstate->fill.alpha);
+ fz_pop_clip(pr->dev);
+ }
+ break;
+ }
+ }
+
+ if (dostroke)
+ {
+ switch (gstate->stroke.kind)
+ {
+ case PDF_MAT_NONE:
+ break;
+ case PDF_MAT_COLOR:
+ fz_stroke_path(pr->dev, path, gstate->stroke_state, &gstate->ctm,
+ gstate->stroke.colorspace, gstate->stroke.v, gstate->stroke.alpha);
+ break;
+ case PDF_MAT_PATTERN:
+ if (gstate->stroke.pattern)
+ {
+ fz_clip_stroke_path(pr->dev, path, &bbox, gstate->stroke_state, &gstate->ctm);
+ pdf_show_pattern(csi, pr, gstate->stroke.pattern, &pr->gstate[gstate->stroke.gstate_num], &bbox, PDF_STROKE);
+ fz_pop_clip(pr->dev);
+ }
+ break;
+ case PDF_MAT_SHADE:
+ if (gstate->stroke.shade)
+ {
+ fz_clip_stroke_path(pr->dev, path, &bbox, gstate->stroke_state, &gstate->ctm);
+ fz_fill_shade(pr->dev, gstate->stroke.shade, &pr->gstate[gstate->stroke.gstate_num].ctm, gstate->stroke.alpha);
+ fz_pop_clip(pr->dev);
+ }
+ break;
+ }
+ }
+
+ if (knockout_group)
+ fz_end_group(pr->dev);
+
+ if (dofill || dostroke)
+ pdf_end_group(csi, pr, &softmask);
+ }
+ fz_always(ctx)
+ {
+ fz_free_path(ctx, path);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+/*
+ * Assemble and emit text
+ */
+
+static pdf_gstate *
+pdf_flush_text(pdf_csi *csi, pdf_run_state *pr)
+{
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+ fz_text *text;
+ int dofill;
+ int dostroke;
+ int doclip;
+ int doinvisible;
+ fz_context *ctx = pr->ctx;
+ softmask_save softmask = { NULL };
+
+ if (!pr->text)
+ return gstate;
+ text = pr->text;
+ pr->text = NULL;
+
+ dofill = dostroke = doclip = doinvisible = 0;
+ switch (pr->text_mode)
+ {
+ case 0: dofill = 1; break;
+ case 1: dostroke = 1; break;
+ case 2: dofill = dostroke = 1; break;
+ case 3: doinvisible = 1; break;
+ case 4: dofill = doclip = 1; break;
+ case 5: dostroke = doclip = 1; break;
+ case 6: dofill = dostroke = doclip = 1; break;
+ case 7: doclip = 1; break;
+ }
+
+ if (pr->in_hidden_ocg > 0)
+ dostroke = dofill = 0;
+
+ fz_try(ctx)
+ {
+ fz_rect tb = pr->text_bbox;
+
+ fz_transform_rect(&tb, &gstate->ctm);
+
+ /* Don't bother sending a text group with nothing in it */
+ if (text->len == 0)
+ break;
+
+ gstate = pdf_begin_group(csi, pr, &tb, &softmask);
+
+ if (doinvisible)
+ fz_ignore_text(pr->dev, text, &gstate->ctm);
+
+ if (dofill)
+ {
+ switch (gstate->fill.kind)
+ {
+ case PDF_MAT_NONE:
+ break;
+ case PDF_MAT_COLOR:
+ fz_fill_text(pr->dev, text, &gstate->ctm,
+ gstate->fill.colorspace, gstate->fill.v, gstate->fill.alpha);
+ break;
+ case PDF_MAT_PATTERN:
+ if (gstate->fill.pattern)
+ {
+ fz_clip_text(pr->dev, text, &gstate->ctm, 0);
+ pdf_show_pattern(csi, pr, gstate->fill.pattern, &pr->gstate[gstate->fill.gstate_num], &tb, PDF_FILL);
+ fz_pop_clip(pr->dev);
+ }
+ break;
+ case PDF_MAT_SHADE:
+ if (gstate->fill.shade)
+ {
+ fz_clip_text(pr->dev, text, &gstate->ctm, 0);
+ /* Page 2 of patterns.pdf shows that fz_fill_shade should NOT be called with gstate->ctm */
+ fz_fill_shade(pr->dev, gstate->fill.shade, &pr->gstate[gstate->fill.gstate_num].ctm, gstate->fill.alpha);
+ fz_pop_clip(pr->dev);
+ }
+ break;
+ }
+ }
+
+ if (dostroke)
+ {
+ switch (gstate->stroke.kind)
+ {
+ case PDF_MAT_NONE:
+ break;
+ case PDF_MAT_COLOR:
+ fz_stroke_text(pr->dev, text, gstate->stroke_state, &gstate->ctm,
+ gstate->stroke.colorspace, gstate->stroke.v, gstate->stroke.alpha);
+ break;
+ case PDF_MAT_PATTERN:
+ if (gstate->stroke.pattern)
+ {
+ fz_clip_stroke_text(pr->dev, text, gstate->stroke_state, &gstate->ctm);
+ pdf_show_pattern(csi, pr, gstate->stroke.pattern, &pr->gstate[gstate->stroke.gstate_num], &tb, PDF_STROKE);
+ fz_pop_clip(pr->dev);
+ }
+ break;
+ case PDF_MAT_SHADE:
+ if (gstate->stroke.shade)
+ {
+ fz_clip_stroke_text(pr->dev, text, gstate->stroke_state, &gstate->ctm);
+ fz_fill_shade(pr->dev, gstate->stroke.shade, &pr->gstate[gstate->stroke.gstate_num].ctm, gstate->stroke.alpha);
+ fz_pop_clip(pr->dev);
+ }
+ break;
+ }
+ }
+
+ if (doclip)
+ {
+ if (pr->accumulate < 2)
+ gstate->clip_depth++;
+ fz_clip_text(pr->dev, text, &gstate->ctm, pr->accumulate);
+ pr->accumulate = 2;
+ }
+
+ pdf_end_group(csi, pr, &softmask);
+ }
+ fz_always(ctx)
+ {
+ fz_free_text(ctx, text);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+
+ return pr->gstate + pr->gtop;
+}
+
+static void
+pdf_show_char(pdf_csi *csi, pdf_run_state *pr, int cid)
+{
+ fz_context *ctx = pr->ctx;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+ pdf_font_desc *fontdesc = gstate->font;
+ fz_matrix tsm, trm;
+ float w0, w1, tx, ty;
+ pdf_hmtx h;
+ pdf_vmtx v;
+ int gid;
+ int ucsbuf[8];
+ int ucslen;
+ int i;
+ fz_rect bbox;
+ int render_direct;
+
+ tsm.a = gstate->size * gstate->scale;
+ tsm.b = 0;
+ tsm.c = 0;
+ tsm.d = gstate->size;
+ tsm.e = 0;
+ tsm.f = gstate->rise;
+
+ ucslen = 0;
+ if (fontdesc->to_unicode)
+ ucslen = pdf_lookup_cmap_full(fontdesc->to_unicode, cid, ucsbuf);
+ if (ucslen == 0 && cid < fontdesc->cid_to_ucs_len)
+ {
+ ucsbuf[0] = fontdesc->cid_to_ucs[cid];
+ ucslen = 1;
+ }
+ if (ucslen == 0 || (ucslen == 1 && ucsbuf[0] == 0))
+ {
+ ucsbuf[0] = '?';
+ ucslen = 1;
+ }
+
+ gid = pdf_font_cid_to_gid(ctx, fontdesc, cid);
+
+ if (fontdesc->wmode == 1)
+ {
+ v = pdf_lookup_vmtx(ctx, fontdesc, cid);
+ tsm.e -= v.x * fabsf(gstate->size) * 0.001f;
+ tsm.f -= v.y * gstate->size * 0.001f;
+ }
+
+ fz_concat(&trm, &tsm, &pr->tm);
+
+ fz_bound_glyph(ctx, fontdesc->font, gid, &trm, &bbox);
+ /* Compensate for the glyph cache limited positioning precision */
+ bbox.x0 -= 1;
+ bbox.y0 -= 1;
+ bbox.x1 += 1;
+ bbox.y1 += 1;
+
+ /* If we are a type3 font within a type 3 font, or are otherwise
+ * uncachable, then render direct. */
+ render_direct = (!fontdesc->font->ft_face && pr->nested_depth > 0) || !fz_glyph_cacheable(ctx, fontdesc->font, gid);
+
+ /* flush buffered text if face or matrix or rendermode has changed */
+ if (!pr->text ||
+ fontdesc->font != pr->text->font ||
+ fontdesc->wmode != pr->text->wmode ||
+ fabsf(trm.a - pr->text->trm.a) > FLT_EPSILON ||
+ fabsf(trm.b - pr->text->trm.b) > FLT_EPSILON ||
+ fabsf(trm.c - pr->text->trm.c) > FLT_EPSILON ||
+ fabsf(trm.d - pr->text->trm.d) > FLT_EPSILON ||
+ gstate->render != pr->text_mode ||
+ render_direct)
+ {
+ gstate = pdf_flush_text(csi, pr);
+
+ pr->text = fz_new_text(ctx, fontdesc->font, &trm, fontdesc->wmode);
+ pr->text->trm.e = 0;
+ pr->text->trm.f = 0;
+ pr->text_mode = gstate->render;
+ pr->text_bbox = fz_empty_rect;
+ }
+
+ if (render_direct)
+ {
+ /* Render the glyph stream direct here (only happens for
+ * type3 glyphs that seem to inherit current graphics
+ * attributes, or type 3 glyphs within type3 glyphs). */
+ fz_matrix composed;
+ fz_concat(&composed, &trm, &gstate->ctm);
+ fz_render_t3_glyph_direct(ctx, pr->dev, fontdesc->font, gid, &composed, gstate, pr->nested_depth);
+ }
+ else
+ {
+ fz_union_rect(&pr->text_bbox, &bbox);
+
+ /* add glyph to textobject */
+ fz_add_text(ctx, pr->text, gid, ucsbuf[0], trm.e, trm.f);
+
+ /* add filler glyphs for one-to-many unicode mapping */
+ for (i = 1; i < ucslen; i++)
+ fz_add_text(ctx, pr->text, -1, ucsbuf[i], trm.e, trm.f);
+ }
+
+ if (fontdesc->wmode == 0)
+ {
+ h = pdf_lookup_hmtx(ctx, fontdesc, cid);
+ w0 = h.w * 0.001f;
+ tx = (w0 * gstate->size + gstate->char_space) * gstate->scale;
+ fz_pre_translate(&pr->tm, tx, 0);
+ }
+
+ if (fontdesc->wmode == 1)
+ {
+ w1 = v.w * 0.001f;
+ ty = w1 * gstate->size + gstate->char_space;
+ fz_pre_translate(&pr->tm, 0, ty);
+ }
+}
+
+static void
+pdf_show_space(pdf_run_state *pr, float tadj)
+{
+ fz_context *ctx = pr->ctx;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+ pdf_font_desc *fontdesc = gstate->font;
+
+ if (!fontdesc)
+ {
+ fz_warn(ctx, "cannot draw text since font and size not set");
+ return;
+ }
+
+ if (fontdesc->wmode == 0)
+ fz_pre_translate(&pr->tm, tadj * gstate->scale, 0);
+ else
+ fz_pre_translate(&pr->tm, 0, tadj);
+}
+
+static void
+pdf_show_string(pdf_csi *csi, pdf_run_state *pr, unsigned char *buf, int len)
+{
+ fz_context *ctx = pr->ctx;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+ pdf_font_desc *fontdesc = gstate->font;
+ unsigned char *end = buf + len;
+ int cpt, cid;
+
+ if (!fontdesc)
+ {
+ fz_warn(ctx, "cannot draw text since font and size not set");
+ return;
+ }
+
+ while (buf < end)
+ {
+ int w = pdf_decode_cmap(fontdesc->encoding, buf, end, &cpt);
+ buf += w;
+
+ cid = pdf_lookup_cmap(fontdesc->encoding, cpt);
+ if (cid >= 0)
+ pdf_show_char(csi, pr, cid);
+ else
+ fz_warn(ctx, "cannot encode character with code point %#x", cpt);
+ if (cpt == 32 && w == 1)
+ pdf_show_space(pr, gstate->word_space);
+ }
+}
+
+static void
+pdf_show_text(pdf_csi *csi, pdf_run_state *pr, pdf_obj *text)
+{
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+ int i;
+
+ if (pdf_is_array(text))
+ {
+ int n = pdf_array_len(text);
+ for (i = 0; i < n; i++)
+ {
+ pdf_obj *item = pdf_array_get(text, i);
+ if (pdf_is_string(item))
+ pdf_show_string(csi, pr, (unsigned char *)pdf_to_str_buf(item), pdf_to_str_len(item));
+ else
+ pdf_show_space(pr, - pdf_to_real(item) * gstate->size * 0.001f);
+ }
+ }
+ else if (pdf_is_string(text))
+ {
+ pdf_show_string(csi, pr, (unsigned char *)pdf_to_str_buf(text), pdf_to_str_len(text));
+ }
+}
+
+/*
+ * Interpreter and graphics state stack.
+ */
+
+static void
+pdf_init_gstate(fz_context *ctx, pdf_gstate *gs, const fz_matrix *ctm)
+{
+ gs->ctm = *ctm;
+ gs->clip_depth = 0;
+
+ gs->stroke_state = fz_new_stroke_state(ctx);
+
+ gs->stroke.kind = PDF_MAT_COLOR;
+ gs->stroke.colorspace = fz_device_gray(ctx); /* No fz_keep_colorspace as static */
+ gs->stroke.v[0] = 0;
+ gs->stroke.pattern = NULL;
+ gs->stroke.shade = NULL;
+ gs->stroke.alpha = 1;
+ gs->stroke.gstate_num = -1;
+
+ gs->fill.kind = PDF_MAT_COLOR;
+ gs->fill.colorspace = fz_device_gray(ctx); /* No fz_keep_colorspace as static */
+ gs->fill.v[0] = 0;
+ gs->fill.pattern = NULL;
+ gs->fill.shade = NULL;
+ gs->fill.alpha = 1;
+ gs->fill.gstate_num = -1;
+
+ gs->char_space = 0;
+ gs->word_space = 0;
+ gs->scale = 1;
+ gs->leading = 0;
+ gs->font = NULL;
+ gs->size = -1;
+ gs->render = 0;
+ gs->rise = 0;
+
+ gs->blendmode = 0;
+ gs->softmask = NULL;
+ gs->softmask_ctm = fz_identity;
+ gs->luminosity = 0;
+}
+
+static void
+pdf_copy_gstate(fz_context *ctx, pdf_gstate *gs, pdf_gstate *old)
+{
+ pdf_drop_gstate(ctx, gs);
+ *gs = *old;
+ pdf_keep_gstate(ctx, gs);
+}
+
+/*
+ * Material state
+ */
+
+static void
+pdf_set_colorspace(pdf_csi *csi, pdf_run_state *pr, int what, fz_colorspace *colorspace)
+{
+ fz_context *ctx = pr->ctx;
+ pdf_gstate *gs;
+ pdf_material *mat;
+
+ gs = pdf_flush_text(csi, pr);
+
+ mat = what == PDF_FILL ? &gs->fill : &gs->stroke;
+
+ fz_drop_colorspace(ctx, mat->colorspace);
+
+ mat->kind = PDF_MAT_COLOR;
+ mat->colorspace = fz_keep_colorspace(ctx, colorspace);
+
+ mat->v[0] = 0;
+ mat->v[1] = 0;
+ mat->v[2] = 0;
+ mat->v[3] = 1;
+
+ if (pdf_is_tint_colorspace(colorspace))
+ {
+ int i;
+ for (i = 0; i < colorspace->n; i++)
+ mat->v[i] = 1.0f;
+ }
+}
+
+static void
+pdf_set_color(pdf_csi *csi, pdf_run_state *pr, int what, float *v)
+{
+ fz_context *ctx = pr->ctx;
+ pdf_gstate *gs;
+ pdf_material *mat;
+ int i;
+
+ gs = pdf_flush_text(csi, pr);
+
+ mat = what == PDF_FILL ? &gs->fill : &gs->stroke;
+
+ switch (mat->kind)
+ {
+ case PDF_MAT_PATTERN:
+ case PDF_MAT_COLOR:
+ if (fz_colorspace_is_indexed(mat->colorspace))
+ {
+ mat->v[0] = v[0] / 255;
+ break;
+ }
+ for (i = 0; i < mat->colorspace->n; i++)
+ mat->v[i] = v[i];
+ break;
+ default:
+ fz_warn(ctx, "color incompatible with material");
+ }
+}
+
+static void
+pdf_set_shade(pdf_csi *csi, pdf_run_state *pr, int what, fz_shade *shade)
+{
+ fz_context *ctx = pr->ctx;
+ pdf_gstate *gs;
+ pdf_material *mat;
+
+ gs = pdf_flush_text(csi, pr);
+
+ mat = what == PDF_FILL ? &gs->fill : &gs->stroke;
+
+ if (mat->shade)
+ fz_drop_shade(ctx, mat->shade);
+
+ mat->kind = PDF_MAT_SHADE;
+ mat->shade = fz_keep_shade(ctx, shade);
+}
+
+static void
+pdf_set_pattern(pdf_csi *csi, pdf_run_state *pr, int what, pdf_pattern *pat, float *v)
+{
+ fz_context *ctx = pr->ctx;
+ pdf_gstate *gs;
+ pdf_material *mat;
+
+ gs = pdf_flush_text(csi, pr);
+
+ mat = what == PDF_FILL ? &gs->fill : &gs->stroke;
+
+ if (mat->pattern)
+ pdf_drop_pattern(ctx, mat->pattern);
+
+ mat->kind = PDF_MAT_PATTERN;
+ if (pat)
+ mat->pattern = pdf_keep_pattern(ctx, pat);
+ else
+ mat->pattern = NULL;
+ mat->gstate_num = pr->gparent;
+
+ if (v)
+ pdf_set_color(csi, pr, what, v);
+}
+
+static pdf_font_desc *
+load_font_or_hail_mary(pdf_csi *csi, pdf_obj *rdb, pdf_obj *font, int depth)
+{
+ pdf_document *doc = csi->doc;
+ fz_context *ctx = doc->ctx;
+ pdf_font_desc *desc;
+
+ fz_try(ctx)
+ {
+ desc = pdf_load_font(doc, rdb, font, depth);
+ }
+ fz_catch(ctx)
+ {
+ if (fz_caught(ctx) != FZ_ERROR_TRYLATER)
+ fz_rethrow(ctx);
+ if (!csi->cookie || !csi->cookie->incomplete_ok)
+ fz_rethrow(ctx);
+ desc = NULL;
+ csi->cookie->incomplete++;
+ }
+ if (desc == NULL)
+ desc = pdf_load_hail_mary_font(doc);
+ return desc;
+}
+
+static void
+pdf_run_extgstate(pdf_csi *csi, pdf_run_state *pr, pdf_obj *rdb, pdf_obj *extgstate)
+{
+ fz_context *ctx = pr->ctx;
+ pdf_gstate *gstate;
+ fz_colorspace *colorspace;
+ int i, k, n;
+
+ gstate = pdf_flush_text(csi, pr);
+
+ n = pdf_dict_len(extgstate);
+ for (i = 0; i < n; i++)
+ {
+ pdf_obj *key = pdf_dict_get_key(extgstate, i);
+ pdf_obj *val = pdf_dict_get_val(extgstate, i);
+ char *s = pdf_to_name(key);
+
+ if (!strcmp(s, "Font"))
+ {
+ if (pdf_is_array(val) && pdf_array_len(val) == 2)
+ {
+ pdf_obj *font = pdf_array_get(val, 0);
+
+ if (gstate->font)
+ {
+ pdf_drop_font(ctx, gstate->font);
+ gstate->font = NULL;
+ }
+
+ gstate->font = load_font_or_hail_mary(csi, rdb, font, pr->nested_depth);
+ if (!gstate->font)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find font in store");
+ gstate->size = pdf_to_real(pdf_array_get(val, 1));
+ }
+ else
+ fz_throw(ctx, FZ_ERROR_GENERIC, "malformed /Font dictionary");
+ }
+
+ else if (!strcmp(s, "LC"))
+ {
+ pr->dev->flags &= ~(FZ_DEVFLAG_STARTCAP_UNDEFINED | FZ_DEVFLAG_DASHCAP_UNDEFINED | FZ_DEVFLAG_ENDCAP_UNDEFINED);
+ gstate->stroke_state = fz_unshare_stroke_state(ctx, gstate->stroke_state);
+ gstate->stroke_state->start_cap = pdf_to_int(val);
+ gstate->stroke_state->dash_cap = pdf_to_int(val);
+ gstate->stroke_state->end_cap = pdf_to_int(val);
+ }
+ else if (!strcmp(s, "LW"))
+ {
+ pr->dev->flags &= ~FZ_DEVFLAG_LINEWIDTH_UNDEFINED;
+ gstate->stroke_state = fz_unshare_stroke_state(ctx, gstate->stroke_state);
+ gstate->stroke_state->linewidth = pdf_to_real(val);
+ }
+ else if (!strcmp(s, "LJ"))
+ {
+ pr->dev->flags &= ~FZ_DEVFLAG_LINEJOIN_UNDEFINED;
+ gstate->stroke_state = fz_unshare_stroke_state(ctx, gstate->stroke_state);
+ gstate->stroke_state->linejoin = pdf_to_int(val);
+ }
+ else if (!strcmp(s, "ML"))
+ {
+ pr->dev->flags &= ~FZ_DEVFLAG_MITERLIMIT_UNDEFINED;
+ gstate->stroke_state = fz_unshare_stroke_state(ctx, gstate->stroke_state);
+ gstate->stroke_state->miterlimit = pdf_to_real(val);
+ }
+
+ else if (!strcmp(s, "D"))
+ {
+ if (pdf_is_array(val) && pdf_array_len(val) == 2)
+ {
+ pdf_obj *dashes = pdf_array_get(val, 0);
+ int len = pdf_array_len(dashes);
+ gstate->stroke_state = fz_unshare_stroke_state_with_dash_len(ctx, gstate->stroke_state, len);
+ gstate->stroke_state->dash_len = len;
+ for (k = 0; k < len; k++)
+ gstate->stroke_state->dash_list[k] = pdf_to_real(pdf_array_get(dashes, k));
+ gstate->stroke_state->dash_phase = pdf_to_real(pdf_array_get(val, 1));
+ }
+ else
+ fz_throw(ctx, FZ_ERROR_GENERIC, "malformed /D");
+ }
+
+ else if (!strcmp(s, "CA"))
+ gstate->stroke.alpha = fz_clamp(pdf_to_real(val), 0, 1);
+
+ else if (!strcmp(s, "ca"))
+ gstate->fill.alpha = fz_clamp(pdf_to_real(val), 0, 1);
+
+ else if (!strcmp(s, "BM"))
+ {
+ if (pdf_is_array(val))
+ val = pdf_array_get(val, 0);
+ gstate->blendmode = fz_lookup_blendmode(pdf_to_name(val));
+ }
+
+ else if (!strcmp(s, "SMask"))
+ {
+ if (pdf_is_dict(val))
+ {
+ pdf_xobject *xobj;
+ pdf_obj *group, *luminosity, *bc, *tr;
+
+ if (gstate->softmask)
+ {
+ pdf_drop_xobject(ctx, gstate->softmask);
+ gstate->softmask = NULL;
+ }
+
+ group = pdf_dict_gets(val, "G");
+ if (!group)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot load softmask xobject (%d %d R)", pdf_to_num(val), pdf_to_gen(val));
+ xobj = pdf_load_xobject(csi->doc, group);
+
+ colorspace = xobj->colorspace;
+ if (!colorspace)
+ colorspace = fz_device_gray(ctx);
+
+ /* The softmask_ctm no longer has the softmask matrix rolled into it, as this
+ * causes the softmask matrix to be applied twice. */
+ gstate->softmask_ctm = gstate->ctm;
+ gstate->softmask = xobj;
+ for (k = 0; k < colorspace->n; k++)
+ gstate->softmask_bc[k] = 0;
+
+ bc = pdf_dict_gets(val, "BC");
+ if (pdf_is_array(bc))
+ {
+ for (k = 0; k < colorspace->n; k++)
+ gstate->softmask_bc[k] = pdf_to_real(pdf_array_get(bc, k));
+ }
+
+ luminosity = pdf_dict_gets(val, "S");
+ if (pdf_is_name(luminosity) && !strcmp(pdf_to_name(luminosity), "Luminosity"))
+ gstate->luminosity = 1;
+ else
+ gstate->luminosity = 0;
+
+ tr = pdf_dict_gets(val, "TR");
+ if (tr && strcmp(pdf_to_name(tr), "Identity"))
+ fz_warn(ctx, "ignoring transfer function");
+ }
+ else if (pdf_is_name(val) && !strcmp(pdf_to_name(val), "None"))
+ {
+ if (gstate->softmask)
+ {
+ pdf_drop_xobject(ctx, gstate->softmask);
+ gstate->softmask = NULL;
+ }
+ }
+ }
+
+ else if (!strcmp(s, "TR2"))
+ {
+ if (strcmp(pdf_to_name(val), "Identity") && strcmp(pdf_to_name(val), "Default"))
+ fz_warn(ctx, "ignoring transfer function");
+ }
+
+ else if (!strcmp(s, "TR"))
+ {
+ /* TR is ignored in the presence of TR2 */
+ pdf_obj *tr2 = pdf_dict_gets(extgstate, "TR2");
+ if (tr2 && strcmp(pdf_to_name(val), "Identity"))
+ fz_warn(ctx, "ignoring transfer function");
+ }
+ }
+}
+
+static void
+run_xobject(pdf_csi *csi, void *state, pdf_obj *resources, pdf_xobject *xobj, const fz_matrix *transform)
+{
+ fz_context *ctx = csi->doc->ctx;
+ pdf_gstate *gstate = NULL;
+ int oldtop = 0;
+ fz_matrix local_transform = *transform;
+ softmask_save softmask = { NULL };
+ int gparent_save;
+ fz_matrix gparent_save_ctm;
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ /* Avoid infinite recursion */
+ if (xobj == NULL || pdf_mark_obj(xobj->me))
+ return;
+
+ fz_var(gstate);
+ fz_var(oldtop);
+
+ gparent_save = pr->gparent;
+ pr->gparent = pr->gtop;
+
+ fz_try(ctx)
+ {
+ pdf_gsave(pr);
+
+ gstate = pr->gstate + pr->gtop;
+ oldtop = pr->gtop;
+
+ /* apply xobject's transform matrix */
+ fz_concat(&local_transform, &xobj->matrix, &local_transform);
+ fz_concat(&gstate->ctm, &local_transform, &gstate->ctm);
+
+ /* The gparent is updated with the modified ctm */
+ gparent_save_ctm = pr->gstate[pr->gparent].ctm;
+ pr->gstate[pr->gparent].ctm = gstate->ctm;
+
+ /* apply soft mask, create transparency group and reset state */
+ if (xobj->transparency)
+ {
+ fz_rect bbox = xobj->bbox;
+ fz_transform_rect(&bbox, &gstate->ctm);
+ gstate = begin_softmask(csi, pr, &softmask);
+
+ fz_begin_group(pr->dev, &bbox,
+ xobj->isolated, xobj->knockout, gstate->blendmode, gstate->fill.alpha);
+
+ gstate->blendmode = 0;
+ gstate->stroke.alpha = 1;
+ gstate->fill.alpha = 1;
+ }
+
+ pdf_gsave(pr); /* Save here so the clippath doesn't persist */
+
+ /* clip to the bounds */
+ fz_moveto(ctx, pr->path, xobj->bbox.x0, xobj->bbox.y0);
+ fz_lineto(ctx, pr->path, xobj->bbox.x1, xobj->bbox.y0);
+ fz_lineto(ctx, pr->path, xobj->bbox.x1, xobj->bbox.y1);
+ fz_lineto(ctx, pr->path, xobj->bbox.x0, xobj->bbox.y1);
+ fz_closepath(ctx, pr->path);
+ pr->clip = 1;
+ pdf_show_path(csi, pr, 0, 0, 0, 0);
+
+ /* run contents */
+
+ if (xobj->resources)
+ resources = xobj->resources;
+
+ pdf_process_contents_object(csi, resources, xobj->contents);
+ }
+ fz_always(ctx)
+ {
+ pdf_grestore(pr); /* Remove the clippath */
+
+ /* wrap up transparency stacks */
+ if (xobj->transparency)
+ {
+ fz_end_group(pr->dev);
+ end_softmask(csi, pr, &softmask);
+ }
+
+ pr->gstate[pr->gparent].ctm = gparent_save_ctm;
+ pr->gparent = gparent_save;
+
+ if (gstate)
+ {
+ while (oldtop < pr->gtop)
+ pdf_grestore(pr);
+
+ pdf_grestore(pr);
+ }
+
+ pdf_unmark_obj(xobj->me);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+static void pdf_run_BDC(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_obj *ocg;
+ pdf_obj *rdb = csi->rdb;
+
+ /* If we are already in a hidden OCG, then we'll still be hidden -
+ * just increment the depth so we pop back to visibility when we've
+ * seen enough EDCs. */
+ if (pr->in_hidden_ocg > 0)
+ {
+ pr->in_hidden_ocg++;
+ return;
+ }
+
+ ocg = pdf_dict_gets(pdf_dict_gets(rdb, "Properties"), csi->name);
+ if (!ocg)
+ {
+ /* No Properties array, or name not found in the properties
+ * means visible. */
+ return;
+ }
+ if (strcmp(pdf_to_name(pdf_dict_gets(ocg, "Type")), "OCG") != 0)
+ {
+ /* Wrong type of property */
+ return;
+ }
+ if (pdf_is_hidden_ocg(ocg, csi, pr, rdb))
+ pr->in_hidden_ocg++;
+}
+
+static void pdf_run_BI(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ fz_context *ctx = csi->doc->ctx;
+ pdf_obj *rdb = csi->rdb;
+ fz_stream *file = csi->file;
+ int ch;
+ fz_image *img;
+ pdf_obj *obj;
+
+ obj = pdf_parse_dict(csi->doc, file, &csi->doc->lexbuf.base);
+
+ /* read whitespace after ID keyword */
+ ch = fz_read_byte(file);
+ if (ch == '\r')
+ if (fz_peek_byte(file) == '\n')
+ fz_read_byte(file);
+
+ fz_try(ctx)
+ {
+ img = pdf_load_inline_image(csi->doc, rdb, obj, file);
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_obj(obj);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+
+ pdf_show_image(csi, pr, img);
+
+ fz_drop_image(ctx, img);
+
+ /* find EI */
+ ch = fz_read_byte(file);
+ while (ch != 'E' && ch != EOF)
+ ch = fz_read_byte(file);
+ ch = fz_read_byte(file);
+ if (ch != 'I')
+ fz_throw(ctx, FZ_ERROR_GENERIC, "syntax error after inline image");
+}
+
+static void pdf_run_B(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pdf_show_path(csi, pr, 0, 1, 1, 0);
+}
+
+static void pdf_run_BMC(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ /* If we are already in a hidden OCG, then we'll still be hidden -
+ * just increment the depth so we pop back to visibility when we've
+ * seen enough EDCs. */
+ if (pr->in_hidden_ocg > 0)
+ {
+ pr->in_hidden_ocg++;
+ }
+}
+
+static void pdf_run_BT(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pr->tm = fz_identity;
+ pr->tlm = fz_identity;
+}
+
+static void pdf_run_BX(pdf_csi *csi, void *state)
+{
+}
+
+static void pdf_run_Bstar(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pdf_show_path(csi, pr, 0, 1, 1, 1);
+}
+
+static void pdf_run_cs_imp(pdf_csi *csi, pdf_run_state *pr, int what)
+{
+ fz_context *ctx = pr->ctx;
+ fz_colorspace *colorspace;
+ pdf_obj *obj, *dict;
+ pdf_obj *rdb = csi->rdb;
+
+ if (!strcmp(csi->name, "Pattern"))
+ {
+ pdf_set_pattern(csi, pr, what, NULL, NULL);
+ }
+ else
+ {
+ if (!strcmp(csi->name, "DeviceGray"))
+ colorspace = fz_device_gray(ctx); /* No fz_keep_colorspace as static */
+ else if (!strcmp(csi->name, "DeviceRGB"))
+ colorspace = fz_device_rgb(ctx); /* No fz_keep_colorspace as static */
+ else if (!strcmp(csi->name, "DeviceCMYK"))
+ colorspace = fz_device_cmyk(ctx); /* No fz_keep_colorspace as static */
+ else
+ {
+ dict = pdf_dict_gets(rdb, "ColorSpace");
+ if (!dict)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find ColorSpace dictionary");
+ obj = pdf_dict_gets(dict, csi->name);
+ if (!obj)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find colorspace resource '%s'", csi->name);
+ colorspace = pdf_load_colorspace(csi->doc, obj);
+ }
+
+ pdf_set_colorspace(csi, pr, what, colorspace);
+
+ fz_drop_colorspace(ctx, colorspace);
+ }
+}
+
+static void pdf_run_CS(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pr->dev->flags &= ~FZ_DEVFLAG_STROKECOLOR_UNDEFINED;
+
+ pdf_run_cs_imp(csi, pr, PDF_STROKE);
+}
+
+static void pdf_run_cs(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pr->dev->flags &= ~FZ_DEVFLAG_FILLCOLOR_UNDEFINED;
+
+ pdf_run_cs_imp(csi, pr, PDF_FILL);
+}
+
+static void pdf_run_DP(pdf_csi *csi, void *state)
+{
+}
+
+static void pdf_run_Do(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ fz_context *ctx = csi->doc->ctx;
+ pdf_obj *dict;
+ pdf_obj *obj;
+ pdf_obj *subtype;
+ pdf_obj *rdb = csi->rdb;
+
+ dict = pdf_dict_gets(rdb, "XObject");
+ if (!dict)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find XObject dictionary when looking for: '%s'", csi->name);
+
+ obj = pdf_dict_gets(dict, csi->name);
+ if (!obj)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find xobject resource: '%s'", csi->name);
+
+ subtype = pdf_dict_gets(obj, "Subtype");
+ if (!pdf_is_name(subtype))
+ fz_throw(ctx, FZ_ERROR_GENERIC, "no XObject subtype specified");
+
+ if (pdf_is_hidden_ocg(pdf_dict_gets(obj, "OC"), csi, pr, rdb))
+ return;
+
+ if (!strcmp(pdf_to_name(subtype), "Form") && pdf_dict_gets(obj, "Subtype2"))
+ subtype = pdf_dict_gets(obj, "Subtype2");
+
+ if (!strcmp(pdf_to_name(subtype), "Form"))
+ {
+ pdf_xobject *xobj;
+
+ xobj = pdf_load_xobject(csi->doc, obj);
+
+ /* Inherit parent resources, in case this one was empty XXX check where it's loaded */
+ if (!xobj->resources)
+ xobj->resources = pdf_keep_obj(rdb);
+
+ fz_try(ctx)
+ {
+ run_xobject(csi, state, xobj->resources, xobj, &fz_identity);
+ }
+ fz_always(ctx)
+ {
+ pdf_drop_xobject(ctx, xobj);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow_message(ctx, "cannot draw xobject (%d %d R)", pdf_to_num(obj), pdf_to_gen(obj));
+ }
+ }
+
+ else if (!strcmp(pdf_to_name(subtype), "Image"))
+ {
+ if ((pr->dev->hints & FZ_IGNORE_IMAGE) == 0)
+ {
+ fz_image *img = pdf_load_image(csi->doc, obj);
+
+ fz_try(ctx)
+ {
+ pdf_show_image(csi, pr, img);
+ }
+ fz_always(ctx)
+ {
+ fz_drop_image(ctx, img);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+ }
+ }
+
+ else if (!strcmp(pdf_to_name(subtype), "PS"))
+ {
+ fz_warn(ctx, "ignoring XObject with subtype PS");
+ }
+
+ else
+ {
+ fz_throw(ctx, FZ_ERROR_GENERIC, "unknown XObject subtype: '%s'", pdf_to_name(subtype));
+ }
+}
+
+static void pdf_run_EMC(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ if (pr->in_hidden_ocg > 0)
+ pr->in_hidden_ocg--;
+}
+
+static void pdf_run_ET(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pdf_flush_text(csi, pr);
+ pr->accumulate = 1;
+}
+
+static void pdf_run_EX(pdf_csi *csi, void *state)
+{
+}
+
+static void pdf_run_F(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pdf_show_path(csi, pr, 0, 1, 0, 0);
+}
+
+static void pdf_run_G(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pr->dev->flags &= ~FZ_DEVFLAG_STROKECOLOR_UNDEFINED;
+ pdf_set_colorspace(csi, pr, PDF_STROKE, fz_device_gray(csi->doc->ctx));
+ pdf_set_color(csi, pr, PDF_STROKE, csi->stack);
+}
+
+static void pdf_run_J(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+
+ pr->dev->flags &= ~(FZ_DEVFLAG_STARTCAP_UNDEFINED | FZ_DEVFLAG_DASHCAP_UNDEFINED | FZ_DEVFLAG_ENDCAP_UNDEFINED);
+ gstate->stroke_state = fz_unshare_stroke_state(csi->doc->ctx, gstate->stroke_state);
+ gstate->stroke_state->start_cap = csi->stack[0];
+ gstate->stroke_state->dash_cap = csi->stack[0];
+ gstate->stroke_state->end_cap = csi->stack[0];
+}
+
+static void pdf_run_K(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pr->dev->flags &= ~FZ_DEVFLAG_STROKECOLOR_UNDEFINED;
+ pdf_set_colorspace(csi, pr, PDF_STROKE, fz_device_cmyk(csi->doc->ctx));
+ pdf_set_color(csi, pr, PDF_STROKE, csi->stack);
+}
+
+static void pdf_run_M(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+
+ pr->dev->flags &= ~FZ_DEVFLAG_MITERLIMIT_UNDEFINED;
+ gstate->stroke_state = fz_unshare_stroke_state(csi->doc->ctx, gstate->stroke_state);
+ gstate->stroke_state->miterlimit = csi->stack[0];
+}
+
+static void pdf_run_MP(pdf_csi *csi, void *state)
+{
+}
+
+static void pdf_run_Q(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pdf_grestore(pr);
+}
+
+static void pdf_run_RG(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pr->dev->flags &= ~FZ_DEVFLAG_STROKECOLOR_UNDEFINED;
+ pdf_set_colorspace(csi, pr, PDF_STROKE, fz_device_rgb(csi->doc->ctx));
+ pdf_set_color(csi, pr, PDF_STROKE, csi->stack);
+}
+
+static void pdf_run_S(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pdf_show_path(csi, pr, 0, 0, 1, 0);
+}
+
+static void pdf_run_SC_imp(pdf_csi *csi, pdf_run_state *pr, int what, pdf_material *mat)
+{
+ fz_context *ctx = pr->ctx;
+ pdf_obj *patterntype;
+ pdf_obj *dict;
+ pdf_obj *obj;
+ int kind;
+ pdf_obj *rdb = csi->rdb;
+
+ kind = mat->kind;
+ if (csi->name[0])
+ kind = PDF_MAT_PATTERN;
+
+ switch (kind)
+ {
+ case PDF_MAT_NONE:
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot set color in mask objects");
+
+ case PDF_MAT_COLOR:
+ pdf_set_color(csi, pr, what, csi->stack);
+ break;
+
+ case PDF_MAT_PATTERN:
+ dict = pdf_dict_gets(rdb, "Pattern");
+ if (!dict)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find Pattern dictionary");
+
+ obj = pdf_dict_gets(dict, csi->name);
+ if (!obj)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find pattern resource '%s'", csi->name);
+
+ patterntype = pdf_dict_gets(obj, "PatternType");
+
+ if (pdf_to_int(patterntype) == 1)
+ {
+ pdf_pattern *pat;
+ pat = pdf_load_pattern(csi->doc, obj);
+ pdf_set_pattern(csi, pr, what, pat, csi->top > 0 ? csi->stack : NULL);
+ pdf_drop_pattern(ctx, pat);
+ }
+ else if (pdf_to_int(patterntype) == 2)
+ {
+ fz_shade *shd;
+ shd = pdf_load_shading(csi->doc, obj);
+ pdf_set_shade(csi, pr, what, shd);
+ fz_drop_shade(ctx, shd);
+ }
+ else
+ {
+ fz_throw(ctx, FZ_ERROR_GENERIC, "unknown pattern type: %d", pdf_to_int(patterntype));
+ }
+ break;
+
+ case PDF_MAT_SHADE:
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot set color in shade objects");
+ }
+ mat->gstate_num = pr->gparent;
+}
+
+static void pdf_run_SC(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+
+ pr->dev->flags &= ~FZ_DEVFLAG_STROKECOLOR_UNDEFINED;
+ pdf_run_SC_imp(csi, pr, PDF_STROKE, &gstate->stroke);
+}
+
+static void pdf_run_sc(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+
+ pr->dev->flags &= ~FZ_DEVFLAG_FILLCOLOR_UNDEFINED;
+ pdf_run_SC_imp(csi, pr, PDF_FILL, &gstate->fill);
+}
+
+static void pdf_run_Tc(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+
+ gstate->char_space = csi->stack[0];
+}
+
+static void pdf_run_Tw(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+
+ gstate->word_space = csi->stack[0];
+}
+
+static void pdf_run_Tz(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate;
+ float a = csi->stack[0] / 100;
+
+ gstate = pdf_flush_text(csi, pr);
+ gstate->scale = a;
+}
+
+static void pdf_run_TL(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+
+ gstate->leading = csi->stack[0];
+}
+
+static void pdf_run_Tf(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ fz_context *ctx = csi->doc->ctx;
+ pdf_obj *rdb = csi->rdb;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+ pdf_obj *dict;
+ pdf_obj *obj;
+
+ gstate->size = csi->stack[0];
+ if (gstate->font)
+ pdf_drop_font(ctx, gstate->font);
+ gstate->font = NULL;
+
+ dict = pdf_dict_gets(rdb, "Font");
+ if (!dict)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find Font dictionary");
+
+ obj = pdf_dict_gets(dict, csi->name);
+ if (!obj)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find font resource: '%s'", csi->name);
+
+ gstate->font = load_font_or_hail_mary(csi, rdb, obj, pr->nested_depth);
+}
+
+static void pdf_run_Tr(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+
+ gstate->render = csi->stack[0];
+}
+
+static void pdf_run_Ts(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+
+ gstate->rise = csi->stack[0];
+}
+
+static void pdf_run_Td(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ fz_pre_translate(&pr->tlm, csi->stack[0], csi->stack[1]);
+ pr->tm = pr->tlm;
+}
+
+static void pdf_run_TD(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+
+ gstate->leading = -csi->stack[1];
+ fz_pre_translate(&pr->tlm, csi->stack[0], csi->stack[1]);
+ pr->tm = pr->tlm;
+}
+
+static void pdf_run_Tm(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pr->tm.a = csi->stack[0];
+ pr->tm.b = csi->stack[1];
+ pr->tm.c = csi->stack[2];
+ pr->tm.d = csi->stack[3];
+ pr->tm.e = csi->stack[4];
+ pr->tm.f = csi->stack[5];
+ pr->tlm = pr->tm;
+}
+
+static void pdf_run_Tstar(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+
+ fz_pre_translate(&pr->tlm, 0, -gstate->leading);
+ pr->tm = pr->tlm;
+}
+
+static void pdf_run_Tj(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ if (csi->string_len)
+ pdf_show_string(csi, pr, csi->string, csi->string_len);
+ else
+ pdf_show_text(csi, pr, csi->obj);
+}
+
+static void pdf_run_TJ(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ if (csi->string_len)
+ pdf_show_string(csi, pr, csi->string, csi->string_len);
+ else
+ pdf_show_text(csi, pr, csi->obj);
+}
+
+static void pdf_run_W(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pr->clip = 1;
+ pr->clip_even_odd = 0;
+}
+
+static void pdf_run_Wstar(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pr->clip = 1;
+ pr->clip_even_odd = 1;
+}
+
+static void pdf_run_b(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pdf_show_path(csi, pr, 1, 1, 1, 0);
+}
+
+static void pdf_run_bstar(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pdf_show_path(csi, pr, 1, 1, 1, 1);
+}
+
+static void pdf_run_c(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ float a, b, c, d, e, f;
+
+ a = csi->stack[0];
+ b = csi->stack[1];
+ c = csi->stack[2];
+ d = csi->stack[3];
+ e = csi->stack[4];
+ f = csi->stack[5];
+ fz_curveto(csi->doc->ctx, pr->path, a, b, c, d, e, f);
+}
+
+static void pdf_run_cm(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+ fz_matrix m;
+
+ m.a = csi->stack[0];
+ m.b = csi->stack[1];
+ m.c = csi->stack[2];
+ m.d = csi->stack[3];
+ m.e = csi->stack[4];
+ m.f = csi->stack[5];
+
+ fz_concat(&gstate->ctm, &m, &gstate->ctm);
+}
+
+static void pdf_run_d(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+ pdf_obj *array;
+ int i;
+ int len;
+
+ array = csi->obj;
+ len = pdf_array_len(array);
+ gstate->stroke_state = fz_unshare_stroke_state_with_dash_len(csi->doc->ctx, gstate->stroke_state, len);
+ gstate->stroke_state->dash_len = len;
+ for (i = 0; i < len; i++)
+ gstate->stroke_state->dash_list[i] = pdf_to_real(pdf_array_get(array, i));
+ gstate->stroke_state->dash_phase = csi->stack[0];
+}
+
+static void pdf_run_d0(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ if (pr->nested_depth > 1)
+ return;
+ pr->dev->flags |= FZ_DEVFLAG_COLOR;
+}
+
+static void pdf_run_d1(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ if (pr->nested_depth > 1)
+ return;
+ pr->dev->flags |= FZ_DEVFLAG_MASK;
+ pr->dev->flags &= ~(FZ_DEVFLAG_FILLCOLOR_UNDEFINED |
+ FZ_DEVFLAG_STROKECOLOR_UNDEFINED |
+ FZ_DEVFLAG_STARTCAP_UNDEFINED |
+ FZ_DEVFLAG_DASHCAP_UNDEFINED |
+ FZ_DEVFLAG_ENDCAP_UNDEFINED |
+ FZ_DEVFLAG_LINEJOIN_UNDEFINED |
+ FZ_DEVFLAG_MITERLIMIT_UNDEFINED |
+ FZ_DEVFLAG_LINEWIDTH_UNDEFINED);
+}
+
+static void pdf_run_f(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pdf_show_path(csi, pr, 0, 1, 0, 0);
+}
+
+static void pdf_run_fstar(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pdf_show_path(csi, pr, 0, 1, 0, 1);
+}
+
+static void pdf_run_g(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pr->dev->flags &= ~FZ_DEVFLAG_FILLCOLOR_UNDEFINED;
+ pdf_set_colorspace(csi, pr, PDF_FILL, fz_device_gray(csi->doc->ctx));
+ pdf_set_color(csi, pr, PDF_FILL, csi->stack);
+}
+
+static void pdf_run_gs(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_obj *dict;
+ pdf_obj *obj;
+ fz_context *ctx = csi->doc->ctx;
+ pdf_obj *rdb = csi->rdb;
+
+ dict = pdf_dict_gets(rdb, "ExtGState");
+ if (!dict)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find ExtGState dictionary");
+
+ obj = pdf_dict_gets(dict, csi->name);
+ if (!obj)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find extgstate resource '%s'", csi->name);
+
+ pdf_run_extgstate(csi, pr, rdb, obj);
+}
+
+static void pdf_run_h(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ fz_closepath(csi->doc->ctx, pr->path);
+}
+
+static void pdf_run_i(pdf_csi *csi, void *state)
+{
+}
+
+static void pdf_run_j(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+
+ pr->dev->flags &= ~FZ_DEVFLAG_LINEJOIN_UNDEFINED;
+ gstate->stroke_state = fz_unshare_stroke_state(csi->doc->ctx, gstate->stroke_state);
+ gstate->stroke_state->linejoin = csi->stack[0];
+}
+
+static void pdf_run_k(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pr->dev->flags &= ~FZ_DEVFLAG_FILLCOLOR_UNDEFINED;
+ pdf_set_colorspace(csi, pr, PDF_FILL, fz_device_cmyk(csi->doc->ctx));
+ pdf_set_color(csi, pr, PDF_FILL, csi->stack);
+}
+
+static void pdf_run_l(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ float a, b;
+
+ a = csi->stack[0];
+ b = csi->stack[1];
+ fz_lineto(csi->doc->ctx, pr->path, a, b);
+}
+
+static void pdf_run_m(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ float a, b;
+
+ a = csi->stack[0];
+ b = csi->stack[1];
+ fz_moveto(csi->doc->ctx, pr->path, a, b);
+}
+
+static void pdf_run_n(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pdf_show_path(csi, pr, 0, 0, 0, 0);
+}
+
+static void pdf_run_q(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pdf_gsave(pr);
+}
+
+static void pdf_run_re(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ fz_context *ctx = csi->doc->ctx;
+ float x, y, w, h;
+
+ x = csi->stack[0];
+ y = csi->stack[1];
+ w = csi->stack[2];
+ h = csi->stack[3];
+
+ fz_moveto(ctx, pr->path, x, y);
+ fz_lineto(ctx, pr->path, x + w, y);
+ fz_lineto(ctx, pr->path, x + w, y + h);
+ fz_lineto(ctx, pr->path, x, y + h);
+ fz_closepath(ctx, pr->path);
+}
+
+static void pdf_run_rg(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pr->dev->flags &= ~FZ_DEVFLAG_FILLCOLOR_UNDEFINED;
+ pdf_set_colorspace(csi, pr, PDF_FILL, fz_device_rgb(csi->doc->ctx));
+ pdf_set_color(csi, pr, PDF_FILL, csi->stack);
+}
+
+static void pdf_run_ri(pdf_csi *csi, void *state)
+{
+}
+
+static void pdf_run_s(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ pdf_show_path(csi, pr, 1, 0, 1, 0);
+}
+
+static void pdf_run_sh(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ fz_context *ctx = csi->doc->ctx;
+ pdf_obj *rdb = csi->rdb;
+ pdf_obj *dict;
+ pdf_obj *obj;
+ fz_shade *shd;
+
+ dict = pdf_dict_gets(rdb, "Shading");
+ if (!dict)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find shading dictionary");
+
+ obj = pdf_dict_gets(dict, csi->name);
+ if (!obj)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find shading resource: '%s'", csi->name);
+
+ if ((pr->dev->hints & FZ_IGNORE_SHADE) == 0)
+ {
+ shd = pdf_load_shading(csi->doc, obj);
+
+ fz_try(ctx)
+ {
+ pdf_show_shade(csi, pr, shd);
+ }
+ fz_always(ctx)
+ {
+ fz_drop_shade(ctx, shd);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+ }
+}
+
+static void pdf_run_v(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ float a, b, c, d;
+
+ a = csi->stack[0];
+ b = csi->stack[1];
+ c = csi->stack[2];
+ d = csi->stack[3];
+ fz_curvetov(csi->doc->ctx, pr->path, a, b, c, d);
+}
+
+static void pdf_run_w(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate;
+
+ gstate = pdf_flush_text(csi, pr); /* linewidth affects stroked text rendering mode */
+ pr->dev->flags &= ~FZ_DEVFLAG_LINEWIDTH_UNDEFINED;
+ gstate->stroke_state = fz_unshare_stroke_state(csi->doc->ctx, gstate->stroke_state);
+ gstate->stroke_state->linewidth = csi->stack[0];
+}
+
+static void pdf_run_y(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ float a, b, c, d;
+
+ a = csi->stack[0];
+ b = csi->stack[1];
+ c = csi->stack[2];
+ d = csi->stack[3];
+ fz_curvetoy(csi->doc->ctx, pr->path, a, b, c, d);
+}
+
+static void pdf_run_squote(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+
+ fz_pre_translate(&pr->tlm, 0, -gstate->leading);
+ pr->tm = pr->tlm;
+
+ if (csi->string_len)
+ pdf_show_string(csi, pr, csi->string, csi->string_len);
+ else
+ pdf_show_text(csi, pr, csi->obj);
+}
+
+static void pdf_run_dquote(pdf_csi *csi, void *state)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ pdf_gstate *gstate = pr->gstate + pr->gtop;
+
+ gstate->word_space = csi->stack[0];
+ gstate->char_space = csi->stack[1];
+
+ fz_pre_translate(&pr->tlm, 0, -gstate->leading);
+ pr->tm = pr->tlm;
+
+ if (csi->string_len)
+ pdf_show_string(csi, pr, csi->string, csi->string_len);
+ else
+ pdf_show_text(csi, pr, csi->obj);
+}
+
+static void free_processor_normal(pdf_csi *csi, void *state)
+{
+ fz_context *ctx = csi->doc->ctx;
+ pdf_run_state *pr = (pdf_run_state *)state;
+
+ while (pr->gtop)
+ pdf_grestore(pr);
+
+ pdf_drop_material(ctx, &pr->gstate[0].fill);
+ pdf_drop_material(ctx, &pr->gstate[0].stroke);
+ if (pr->gstate[0].font)
+ pdf_drop_font(ctx, pr->gstate[0].font);
+ if (pr->gstate[0].softmask)
+ pdf_drop_xobject(ctx, pr->gstate[0].softmask);
+ fz_drop_stroke_state(ctx, pr->gstate[0].stroke_state);
+
+ while (pr->gstate[0].clip_depth--)
+ fz_pop_clip(pr->dev);
+
+ if (pr->path) fz_free_path(ctx, pr->path);
+ if (pr->text) fz_free_text(ctx, pr->text);
+
+ fz_free(ctx, pr->gstate);
+ fz_free(ctx, pr);
+}
+
+static void
+process_annot(pdf_csi *csi, void *state, pdf_obj *resources, pdf_annot *annot)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ fz_context *ctx = pr->ctx;
+ int flags;
+
+ if (pdf_is_hidden_ocg(pdf_dict_gets(annot->obj, "OC"), csi, pr, resources))
+ return;
+
+ flags = pdf_to_int(pdf_dict_gets(annot->obj, "F"));
+ if (!strcmp(pr->event, "Print") && !(flags & (1 << 2))) /* Print */
+ return;
+ if (!strcmp(pr->event, "View") && (flags & (1 << 5))) /* NoView */
+ return;
+
+ fz_try(ctx)
+ {
+ /* We need to save an extra level here to allow for level 0
+ * to be the 'parent' gstate level. */
+ pdf_gsave(pr);
+ run_xobject(csi, state, resources, annot->ap, &annot->matrix);
+ }
+ fz_catch(ctx)
+ {
+ while (pr->gtop > 0)
+ pdf_grestore(pr);
+ fz_rethrow(ctx);
+ }
+}
+
+static void
+process_stream(pdf_csi *csi, void *state, pdf_lexbuf *buf)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ fz_context *ctx = pr->ctx;
+ int save_gbot;
+
+ save_gbot = pr->gbot;
+ pr->gbot = pr->gtop;
+ fz_try(ctx)
+ {
+ pdf_process_stream(csi, buf);
+ }
+ fz_always(ctx)
+ {
+ while (pr->gtop > pr->gbot)
+ pdf_grestore(pr);
+ pr->gbot = save_gbot;
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+static void
+process_contents(pdf_csi *csi, void *state, pdf_obj *resources, pdf_obj *contents)
+{
+ pdf_run_state *pr = (pdf_run_state *)state;
+ fz_context *ctx = pr->ctx;
+
+ fz_try(ctx)
+ {
+ /* We need to save an extra level here to allow for level 0
+ * to be the 'parent' gstate level. */
+ pdf_gsave(pr);
+ pdf_process_contents_object(csi, resources, contents);
+ }
+ fz_always(ctx)
+ {
+ while (pr->gtop > 0)
+ pdf_grestore(pr);
+ }
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+}
+
+const pdf_processor pdf_processor_normal =
+{
+ {
+ pdf_run_dquote,
+ pdf_run_squote,
+ pdf_run_B,
+ pdf_run_Bstar,
+ pdf_run_BDC,
+ pdf_run_BI,
+ pdf_run_BMC,
+ pdf_run_BT,
+ pdf_run_BX,
+ pdf_run_CS,
+ pdf_run_DP,
+ pdf_run_EMC,
+ pdf_run_ET,
+ pdf_run_EX,
+ pdf_run_F,
+ pdf_run_G,
+ pdf_run_J,
+ pdf_run_K,
+ pdf_run_M,
+ pdf_run_MP,
+ pdf_run_Q,
+ pdf_run_RG,
+ pdf_run_S,
+ pdf_run_SC,
+ pdf_run_SC, /* SCN */
+ pdf_run_Tstar,
+ pdf_run_TD,
+ pdf_run_TJ,
+ pdf_run_TL,
+ pdf_run_Tc,
+ pdf_run_Td,
+ pdf_run_Tj,
+ pdf_run_Tm,
+ pdf_run_Tr,
+ pdf_run_Ts,
+ pdf_run_Tw,
+ pdf_run_Tz,
+ pdf_run_W,
+ pdf_run_Wstar,
+ pdf_run_b,
+ pdf_run_bstar,
+ pdf_run_c,
+ pdf_run_cm,
+ pdf_run_cs,
+ pdf_run_d,
+ pdf_run_d0,
+ pdf_run_d1,
+ pdf_run_f,
+ pdf_run_fstar,
+ pdf_run_g,
+ pdf_run_h,
+ pdf_run_i,
+ pdf_run_j,
+ pdf_run_k,
+ pdf_run_l,
+ pdf_run_m,
+ pdf_run_n,
+ pdf_run_q,
+ pdf_run_re,
+ pdf_run_rg,
+ pdf_run_ri,
+ pdf_run_s,
+ pdf_run_sc,
+ pdf_run_sc, /* scn */
+ pdf_run_v,
+ pdf_run_w,
+ pdf_run_y,
+ pdf_run_Do,
+ pdf_run_Tf,
+ pdf_run_gs,
+ pdf_run_sh,
+ free_processor_normal
+ },
+ process_annot,
+ process_stream,
+ process_contents
+};
+
+pdf_process *pdf_process_run(pdf_process *process, fz_device *dev, const fz_matrix *ctm, const char *event, pdf_gstate *gstate, int nested)
+{
+ fz_context *ctx = dev->ctx;
+ pdf_run_state *pr;
+
+ pr = fz_malloc_struct(ctx, pdf_run_state);
+ fz_try(ctx)
+ {
+ pr->ctx = ctx;
+ pr->dev = dev;
+ pr->in_hidden_ocg = 0;
+ pr->event = event;
+
+ pr->path = fz_new_path(ctx);
+ pr->clip = 0;
+ pr->clip_even_odd = 0;
+
+ pr->text = NULL;
+ pr->tlm = fz_identity;
+ pr->tm = fz_identity;
+ pr->text_mode = 0;
+ pr->accumulate = 1;
+
+ pr->gcap = 64;
+ pr->gstate = fz_malloc_array(ctx, pr->gcap, sizeof(pdf_gstate));
+
+ pr->nested_depth = nested;
+ pdf_init_gstate(ctx, &pr->gstate[0], ctm);
+ if (gstate)
+ {
+ pdf_copy_gstate(ctx, &pr->gstate[0], gstate);
+ pr->gstate[0].ctm = *ctm;
+ }
+ pr->gtop = 0;
+ pr->gbot = 0;
+ pr->gparent = 0;
+ }
+ fz_catch(ctx)
+ {
+ fz_free_path(ctx, pr->path);
+ fz_free(ctx, pr);
+ fz_rethrow(ctx);
+ }
+
+ process->state = pr;
+ process->processor = &pdf_processor_normal;
+ return process;
+}