summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--platform/win32/libmupdf.vcproj12
-rw-r--r--source/pdf/pdf-interpret-imp.h151
-rw-r--r--source/pdf/pdf-interpret.c3008
-rw-r--r--source/pdf/pdf-op-run.c2838
-rw-r--r--source/pdf/pdf-run.c105
5 files changed, 3284 insertions, 2830 deletions
diff --git a/platform/win32/libmupdf.vcproj b/platform/win32/libmupdf.vcproj
index 2c80d712..f501cd9f 100644
--- a/platform/win32/libmupdf.vcproj
+++ b/platform/win32/libmupdf.vcproj
@@ -778,6 +778,10 @@
>
</File>
<File
+ RelativePath="..\..\source\pdf\pdf-interpret-imp.h"
+ >
+ </File>
+ <File
RelativePath="..\..\source\pdf\pdf-interpret.c"
>
</File>
@@ -798,6 +802,10 @@
>
</File>
<File
+ RelativePath="..\..\source\pdf\pdf-op-run.c"
+ >
+ </File>
+ <File
RelativePath="..\..\source\pdf\pdf-outline.c"
>
</File>
@@ -822,6 +830,10 @@
>
</File>
<File
+ RelativePath="..\..\source\pdf\pdf-run.c"
+ >
+ </File>
+ <File
RelativePath="..\..\source\pdf\pdf-shade.c"
>
</File>
diff --git a/source/pdf/pdf-interpret-imp.h b/source/pdf/pdf-interpret-imp.h
new file mode 100644
index 00000000..2aafb830
--- /dev/null
+++ b/source/pdf/pdf-interpret-imp.h
@@ -0,0 +1,151 @@
+#ifndef PDF_INTERPRET_IMP_H
+#define PDF_INTERPRET_IMP_H
+
+#include "mupdf/pdf.h"
+
+typedef struct pdf_csi_s pdf_csi;
+typedef struct pdf_gstate_s pdf_gstate;
+
+typedef void (*pdf_operator_fn)(pdf_csi *, void *user);
+typedef void (*pdf_process_annot_fn)(pdf_csi *csi, void *user, pdf_obj *resources, pdf_annot *annot);
+typedef void (*pdf_process_stream_fn)(pdf_csi *csi, void *user, pdf_lexbuf *buf);
+typedef void (*pdf_process_contents_fn)(pdf_csi *csi, void *user, pdf_obj *resources, pdf_obj *contents);
+
+typedef enum {
+ /* The first section of op's all run without a try/catch */
+ PDF_OP_dquote,
+ PDF_OP_squote,
+ PDF_OP_B,
+ PDF_OP_Bstar,
+ PDF_OP_BDC,
+ PDF_OP_BI,
+ PDF_OP_BMC,
+ PDF_OP_BT,
+ PDF_OP_BX,
+ PDF_OP_CS,
+ PDF_OP_DP,
+ PDF_OP_EMC,
+ PDF_OP_ET,
+ PDF_OP_EX,
+ PDF_OP_F,
+ PDF_OP_G,
+ PDF_OP_J,
+ PDF_OP_K,
+ PDF_OP_M,
+ PDF_OP_MP,
+ PDF_OP_Q,
+ PDF_OP_RG,
+ PDF_OP_S,
+ PDF_OP_SC,
+ PDF_OP_SCN,
+ PDF_OP_Tstar,
+ PDF_OP_TD,
+ PDF_OP_TJ,
+ PDF_OP_TL,
+ PDF_OP_Tc,
+ PDF_OP_Td,
+ PDF_OP_Tj,
+ PDF_OP_Tm,
+ PDF_OP_Tr,
+ PDF_OP_Ts,
+ PDF_OP_Tw,
+ PDF_OP_Tz,
+ PDF_OP_W,
+ PDF_OP_Wstar,
+ PDF_OP_b,
+ PDF_OP_bstar,
+ PDF_OP_c,
+ PDF_OP_cm,
+ PDF_OP_cs,
+ PDF_OP_d,
+ PDF_OP_d0,
+ PDF_OP_d1,
+ PDF_OP_f,
+ PDF_OP_fstar,
+ PDF_OP_g,
+ PDF_OP_h,
+ PDF_OP_i,
+ PDF_OP_j,
+ PDF_OP_k,
+ PDF_OP_l,
+ PDF_OP_m,
+ PDF_OP_n,
+ PDF_OP_q,
+ PDF_OP_re,
+ PDF_OP_rg,
+ PDF_OP_ri,
+ PDF_OP_s,
+ PDF_OP_sc,
+ PDF_OP_scn,
+ PDF_OP_v,
+ PDF_OP_w,
+ PDF_OP_y,
+ /* ops in this second section require additional try/catch handling */
+ PDF_OP_Do,
+ PDF_OP_Tf,
+ PDF_OP_gs,
+ PDF_OP_sh,
+ /* END is used to signify end of stream (finalise and close down) */
+ PDF_OP_END,
+ /* And finally we have a max */
+ PDF_OP_MAX
+} PDF_OP;
+
+typedef struct pdf_processor_s {
+ pdf_operator_fn op_table[PDF_OP_MAX];
+ pdf_process_annot_fn process_annot;
+ pdf_process_stream_fn process_stream;
+ pdf_process_contents_fn process_contents;
+} pdf_processor;
+
+typedef struct pdf_process_s
+{
+ const pdf_processor *processor;
+ void *state;
+} pdf_process;
+
+struct pdf_csi_s
+{
+ pdf_document *doc;
+
+ /* Current resource dict and file. These are in here to reduce param
+ * passing. */
+ pdf_obj *rdb;
+ fz_stream *file;
+
+ /* Operator table */
+ pdf_process process;
+
+ /* interpreter stack */
+ pdf_obj *obj;
+ char name[256];
+ unsigned char string[256];
+ int string_len;
+ float stack[32];
+ int top;
+
+ int xbalance;
+ int in_text;
+
+ /* cookie support */
+ fz_cookie *cookie;
+};
+
+static inline void pdf_process_op(pdf_csi *csi, int op, const pdf_process *process)
+{
+ process->processor->op_table[op](csi, process->state);
+}
+
+void pdf_process_contents_object(pdf_csi *csi, pdf_obj *rdb, pdf_obj *contents);
+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);
+
+/* Functions to actually use the pdf_process structures to process pages,
+ * annotations and glyphs */
+void pdf_process_annot(pdf_document *doc, pdf_page *page, pdf_annot *annot, const pdf_process *process, fz_cookie *cookie);
+void pdf_process_page_contents(pdf_document *doc, pdf_page *page, const pdf_process *process, fz_cookie *cookie);
+void pdf_process_glyph(pdf_document *doc, pdf_obj *resources, fz_buffer *contents, pdf_process *process);
+
+#endif
diff --git a/source/pdf/pdf-interpret.c b/source/pdf/pdf-interpret.c
index c01d7ea5..1508e829 100644
--- a/source/pdf/pdf-interpret.c
+++ b/source/pdf/pdf-interpret.c
@@ -1,1123 +1,18 @@
-#include "mupdf/pdf.h"
-
-#define TILE
-
-typedef struct pdf_material_s pdf_material;
-typedef struct pdf_gstate_s pdf_gstate;
-typedef struct pdf_csi_s pdf_csi;
-
-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;
-};
-
-struct pdf_csi_s
-{
- fz_device *dev;
- pdf_document *doc;
-
- int nested_depth;
-
- /* usage mode for optional content groups */
- char *event; /* "View", "Print", "Export" */
-
- /* Current resource dict and file. These are in here to reduce param
- * passing. */
- pdf_obj *rdb;
- fz_stream *file;
-
- /* interpreter stack */
- pdf_obj *obj;
- char name[256];
- unsigned char string[256];
- int string_len;
- float stack[32];
- int top;
-
- int xbalance;
- int in_text;
- int in_hidden_ocg;
-
- /* path object state */
- fz_path *path;
- int clip;
- int clip_even_odd;
-
- /* 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;
-
- /* cookie support */
- fz_cookie *cookie;
-};
-
-static void pdf_run_contents_object(pdf_csi *csi, pdf_obj *rdb, pdf_obj *contents);
-static void pdf_run_xobject(pdf_csi *csi, pdf_obj *resources, pdf_xobject *xobj, const fz_matrix *transform);
-static void pdf_show_pattern(pdf_csi *csi, pdf_pattern *pat, pdf_gstate *pat_gstate, const fz_rect *area, int what);
-
-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;
-}
-
-static int
-pdf_is_hidden_ocg(pdf_obj *ocg, pdf_csi *csi, pdf_obj *rdb)
-{
- char event_state[16];
- pdf_obj *obj, *obj2;
- char *type;
- pdf_ocg_descriptor *desc = csi->doc->ocg;
- fz_context *ctx = csi->dev->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, csi->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, csi->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, rdb);
- if ((combine & 1) == 0)
- hidden = !hidden;
- if (combine & 2)
- on &= hidden;
- else
- on |= hidden;
- }
- }
- else
- {
- on = pdf_is_hidden_ocg(obj, csi, 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;
-}
-
-/*
- * Emit graphics calls to device.
- */
-
-typedef struct softmask_save_s softmask_save;
-
-struct softmask_save_s
-{
- pdf_xobject *softmask;
- fz_matrix ctm;
-};
-
-static pdf_gstate *
-begin_softmask(pdf_csi * csi, softmask_save *save)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
- pdf_xobject *softmask = gstate->softmask;
- fz_rect mask_bbox;
- fz_context *ctx;
- fz_matrix save_tm, save_tlm, save_ctm;
- int save_in_text;
-
- save->softmask = softmask;
- if (softmask == NULL)
- return gstate;
- save->ctm = gstate->softmask_ctm;
- save_ctm = gstate->ctm;
-
- mask_bbox = softmask->bbox;
- ctx = csi->dev->ctx;
- save_tm = csi->tm;
- save_tlm = csi->tlm;
- save_in_text = csi->in_text;
-
- csi->in_text = 0;
- 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(csi->dev, &mask_bbox, gstate->luminosity,
- softmask->colorspace, gstate->softmask_bc);
- fz_try(ctx)
- {
- pdf_run_xobject(csi, 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(csi->dev);
-
- csi->tm = save_tm;
- csi->tlm = save_tlm;
- csi->in_text = save_in_text;
-
- gstate = csi->gstate + csi->gtop;
- gstate->ctm = save_ctm;
-
- return gstate;
-}
-
-static void
-end_softmask(pdf_csi *csi, softmask_save *save)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
-
- if (save->softmask == NULL)
- return;
-
- gstate->softmask = save->softmask;
- gstate->softmask_ctm = save->ctm;
- fz_pop_clip(csi->dev);
-}
-
-static pdf_gstate *
-pdf_begin_group(pdf_csi *csi, const fz_rect *bbox, softmask_save *softmask)
-{
- pdf_gstate *gstate = begin_softmask(csi, softmask);
-
- if (gstate->blendmode)
- fz_begin_group(csi->dev, bbox, 1, 0, gstate->blendmode, 1);
-
- return csi->gstate + csi->gtop;
-}
-
-static void
-pdf_end_group(pdf_csi *csi, softmask_save *softmask)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
-
- if (gstate->blendmode)
- fz_end_group(csi->dev);
-
- end_softmask(csi, softmask);
-}
-
-static void
-pdf_show_shade(pdf_csi *csi, fz_shade *shd)
-{
- fz_context *ctx = csi->dev->ctx;
- pdf_gstate *gstate = csi->gstate + csi->gtop;
- fz_rect bbox;
- softmask_save softmask = { NULL };
-
- if (csi->in_hidden_ocg > 0)
- return;
-
- fz_bound_shade(ctx, shd, &gstate->ctm, &bbox);
-
- gstate = pdf_begin_group(csi, &bbox, &softmask);
-
- /* FIXME: The gstate->ctm in the next line may be wrong; maybe
- * it should be the parent gstates ctm? */
- fz_fill_shade(csi->dev, shd, &gstate->ctm, gstate->fill.alpha);
-
- pdf_end_group(csi, &softmask);
-}
-
-static void
-pdf_show_image(pdf_csi *csi, fz_image *image)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
- fz_matrix image_ctm;
- fz_rect bbox;
- softmask_save softmask = { NULL };
-
- if (csi->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(csi->dev, &bbox, 0, 0, gstate->blendmode, 1);
- fz_clip_image_mask(csi->dev, image->mask, &bbox, &image_ctm);
- }
- else
- gstate = pdf_begin_group(csi, &bbox, &softmask);
-
- if (!image->colorspace)
- {
-
- switch (gstate->fill.kind)
- {
- case PDF_MAT_NONE:
- break;
- case PDF_MAT_COLOR:
- fz_fill_image_mask(csi->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(csi->dev, image, &bbox, &image_ctm);
- pdf_show_pattern(csi, gstate->fill.pattern, &csi->gstate[gstate->fill.gstate_num], &bbox, PDF_FILL);
- fz_pop_clip(csi->dev);
- }
- break;
- case PDF_MAT_SHADE:
- if (gstate->fill.shade)
- {
- fz_clip_image_mask(csi->dev, image, &bbox, &image_ctm);
- fz_fill_shade(csi->dev, gstate->fill.shade, &csi->gstate[gstate->fill.gstate_num].ctm, gstate->fill.alpha);
- fz_pop_clip(csi->dev);
- }
- break;
- }
- }
- else
- {
- fz_fill_image(csi->dev, image, &image_ctm, gstate->fill.alpha);
- }
-
- if (image->mask)
- {
- fz_pop_clip(csi->dev);
- if (gstate->blendmode)
- fz_end_group(csi->dev);
- }
- else
- pdf_end_group(csi, &softmask);
-}
-
-static void
-pdf_show_path(pdf_csi *csi, int doclose, int dofill, int dostroke, int even_odd)
-{
- fz_context *ctx = csi->dev->ctx;
- pdf_gstate *gstate = csi->gstate + csi->gtop;
- fz_path *path;
- fz_rect bbox;
- softmask_save softmask = { NULL };
- int knockout_group = 0;
-
- if (dostroke) {
- if (csi->dev->flags & (FZ_DEVFLAG_STROKECOLOR_UNDEFINED | FZ_DEVFLAG_LINEJOIN_UNDEFINED | FZ_DEVFLAG_LINEWIDTH_UNDEFINED))
- csi->dev->flags |= FZ_DEVFLAG_UNCACHEABLE;
- else if (gstate->stroke_state->dash_len != 0 && csi->dev->flags & (FZ_DEVFLAG_STARTCAP_UNDEFINED | FZ_DEVFLAG_DASHCAP_UNDEFINED | FZ_DEVFLAG_ENDCAP_UNDEFINED))
- csi->dev->flags |= FZ_DEVFLAG_UNCACHEABLE;
- else if (gstate->stroke_state->linejoin == FZ_LINEJOIN_MITER && (csi->dev->flags & FZ_DEVFLAG_MITERLIMIT_UNDEFINED))
- csi->dev->flags |= FZ_DEVFLAG_UNCACHEABLE;
- }
- if (dofill) {
- if (csi->dev->flags & FZ_DEVFLAG_FILLCOLOR_UNDEFINED)
- csi->dev->flags |= FZ_DEVFLAG_UNCACHEABLE;
- }
-
- path = csi->path;
- csi->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 (csi->clip)
- {
- gstate->clip_depth++;
- fz_clip_path(csi->dev, path, &bbox, csi->clip_even_odd, &gstate->ctm);
- csi->clip = 0;
- }
-
- if (csi->in_hidden_ocg > 0)
- dostroke = dofill = 0;
-
- if (dofill || dostroke)
- gstate = pdf_begin_group(csi, &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(csi->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(csi->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(csi->dev, path, &bbox, even_odd, &gstate->ctm);
- pdf_show_pattern(csi, gstate->fill.pattern, &csi->gstate[gstate->fill.gstate_num], &bbox, PDF_FILL);
- fz_pop_clip(csi->dev);
- }
- break;
- case PDF_MAT_SHADE:
- if (gstate->fill.shade)
- {
- fz_clip_path(csi->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(csi->dev, gstate->fill.shade, &csi->gstate[gstate->fill.gstate_num].ctm, gstate->fill.alpha);
- fz_pop_clip(csi->dev);
- }
- break;
- }
- }
-
- if (dostroke)
- {
- switch (gstate->stroke.kind)
- {
- case PDF_MAT_NONE:
- break;
- case PDF_MAT_COLOR:
- fz_stroke_path(csi->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(csi->dev, path, &bbox, gstate->stroke_state, &gstate->ctm);
- pdf_show_pattern(csi, gstate->stroke.pattern, &csi->gstate[gstate->stroke.gstate_num], &bbox, PDF_STROKE);
- fz_pop_clip(csi->dev);
- }
- break;
- case PDF_MAT_SHADE:
- if (gstate->stroke.shade)
- {
- fz_clip_stroke_path(csi->dev, path, &bbox, gstate->stroke_state, &gstate->ctm);
- fz_fill_shade(csi->dev, gstate->stroke.shade, &csi->gstate[gstate->stroke.gstate_num].ctm, gstate->stroke.alpha);
- fz_pop_clip(csi->dev);
- }
- break;
- }
- }
-
- if (knockout_group)
- fz_end_group(csi->dev);
-
- if (dofill || dostroke)
- pdf_end_group(csi, &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_gstate *gstate = csi->gstate + csi->gtop;
- fz_text *text;
- int dofill;
- int dostroke;
- int doclip;
- int doinvisible;
- fz_context *ctx = csi->dev->ctx;
- softmask_save softmask = { NULL };
-
- if (!csi->text)
- return gstate;
- text = csi->text;
- csi->text = NULL;
-
- dofill = dostroke = doclip = doinvisible = 0;
- switch (csi->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 (csi->in_hidden_ocg > 0)
- dostroke = dofill = 0;
-
- fz_try(ctx)
- {
- fz_rect tb = csi->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, &tb, &softmask);
-
- if (doinvisible)
- fz_ignore_text(csi->dev, text, &gstate->ctm);
-
- if (dofill)
- {
- switch (gstate->fill.kind)
- {
- case PDF_MAT_NONE:
- break;
- case PDF_MAT_COLOR:
- fz_fill_text(csi->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(csi->dev, text, &gstate->ctm, 0);
- pdf_show_pattern(csi, gstate->fill.pattern, &csi->gstate[gstate->fill.gstate_num], &tb, PDF_FILL);
- fz_pop_clip(csi->dev);
- }
- break;
- case PDF_MAT_SHADE:
- if (gstate->fill.shade)
- {
- fz_clip_text(csi->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(csi->dev, gstate->fill.shade, &csi->gstate[gstate->fill.gstate_num].ctm, gstate->fill.alpha);
- fz_pop_clip(csi->dev);
- }
- break;
- }
- }
-
- if (dostroke)
- {
- switch (gstate->stroke.kind)
- {
- case PDF_MAT_NONE:
- break;
- case PDF_MAT_COLOR:
- fz_stroke_text(csi->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(csi->dev, text, gstate->stroke_state, &gstate->ctm);
- pdf_show_pattern(csi, gstate->stroke.pattern, &csi->gstate[gstate->stroke.gstate_num], &tb, PDF_STROKE);
- fz_pop_clip(csi->dev);
- }
- break;
- case PDF_MAT_SHADE:
- if (gstate->stroke.shade)
- {
- fz_clip_stroke_text(csi->dev, text, gstate->stroke_state, &gstate->ctm);
- fz_fill_shade(csi->dev, gstate->stroke.shade, &csi->gstate[gstate->stroke.gstate_num].ctm, gstate->stroke.alpha);
- fz_pop_clip(csi->dev);
- }
- break;
- }
- }
-
- if (doclip)
- {
- if (csi->accumulate < 2)
- gstate->clip_depth++;
- fz_clip_text(csi->dev, text, &gstate->ctm, csi->accumulate);
- csi->accumulate = 2;
- }
-
- pdf_end_group(csi, &softmask);
- }
- fz_always(ctx)
- {
- fz_free_text(ctx, text);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
- }
-
- return csi->gstate + csi->gtop;
-}
-
-static void
-pdf_show_char(pdf_csi *csi, int cid)
-{
- fz_context *ctx = csi->dev->ctx;
- pdf_gstate *gstate = csi->gstate + csi->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, &csi->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 && csi->nested_depth > 0) || !fz_glyph_cacheable(ctx, fontdesc->font, gid);
-
- /* flush buffered text if face or matrix or rendermode has changed */
- if (!csi->text ||
- fontdesc->font != csi->text->font ||
- fontdesc->wmode != csi->text->wmode ||
- fabsf(trm.a - csi->text->trm.a) > FLT_EPSILON ||
- fabsf(trm.b - csi->text->trm.b) > FLT_EPSILON ||
- fabsf(trm.c - csi->text->trm.c) > FLT_EPSILON ||
- fabsf(trm.d - csi->text->trm.d) > FLT_EPSILON ||
- gstate->render != csi->text_mode ||
- render_direct)
- {
- gstate = pdf_flush_text(csi);
-
- csi->text = fz_new_text(ctx, fontdesc->font, &trm, fontdesc->wmode);
- csi->text->trm.e = 0;
- csi->text->trm.f = 0;
- csi->text_mode = gstate->render;
- csi->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, csi->dev, fontdesc->font, gid, &composed, gstate, csi->nested_depth);
- }
- else
- {
- fz_union_rect(&csi->text_bbox, &bbox);
-
- /* add glyph to textobject */
- fz_add_text(ctx, csi->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, csi->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(&csi->tm, tx, 0);
- }
-
- if (fontdesc->wmode == 1)
- {
- w1 = v.w * 0.001f;
- ty = w1 * gstate->size + gstate->char_space;
- fz_pre_translate(&csi->tm, 0, ty);
- }
-}
-
-static void
-pdf_show_space(pdf_csi *csi, float tadj)
-{
- fz_context *ctx = csi->dev->ctx;
- pdf_gstate *gstate = csi->gstate + csi->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(&csi->tm, tadj * gstate->scale, 0);
- else
- fz_pre_translate(&csi->tm, 0, tadj);
-}
-
-static void
-pdf_show_string(pdf_csi *csi, unsigned char *buf, int len)
-{
- fz_context *ctx = csi->dev->ctx;
- pdf_gstate *gstate = csi->gstate + csi->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, cid);
- else
- fz_warn(ctx, "cannot encode character with code point %#x", cpt);
- if (cpt == 32 && w == 1)
- pdf_show_space(csi, gstate->word_space);
- }
-}
-
-static void
-pdf_show_text(pdf_csi *csi, pdf_obj *text)
-{
- pdf_gstate *gstate = csi->gstate + csi->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, (unsigned char *)pdf_to_str_buf(item), pdf_to_str_len(item));
- else
- pdf_show_space(csi, - pdf_to_real(item) * gstate->size * 0.001f);
- }
- }
- else if (pdf_is_string(text))
- {
- pdf_show_string(csi, (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 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_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_copy_gstate(fz_context *ctx, pdf_gstate *gs, pdf_gstate *old)
-{
- pdf_drop_gstate(ctx, gs);
- *gs = *old;
- pdf_keep_gstate(ctx, gs);
-}
-
-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);
-}
+#include "pdf-interpret-imp.h"
static pdf_csi *
-pdf_new_csi(pdf_document *doc, fz_device *dev, const fz_matrix *ctm, char *event, fz_cookie *cookie, pdf_gstate *gstate, int nested)
+pdf_new_csi(pdf_document *doc, fz_cookie *cookie, const pdf_process *process)
{
- pdf_csi *csi;
- fz_context *ctx = dev->ctx;
+ pdf_csi *csi = NULL;
+ fz_context *ctx = doc->ctx;
+
+ fz_var(csi);
- csi = fz_malloc_struct(ctx, pdf_csi);
fz_try(ctx)
{
+ csi = fz_malloc_struct(ctx, pdf_csi);
csi->doc = doc;
- csi->dev = dev;
- csi->event = event;
+ csi->in_text = 0;
csi->top = 0;
csi->obj = NULL;
@@ -1125,39 +20,14 @@ pdf_new_csi(pdf_document *doc, fz_device *dev, const fz_matrix *ctm, char *event
csi->string_len = 0;
memset(csi->stack, 0, sizeof csi->stack);
- csi->xbalance = 0;
- csi->in_text = 0;
- csi->in_hidden_ocg = 0;
-
- csi->path = fz_new_path(ctx);
- csi->clip = 0;
- csi->clip_even_odd = 0;
-
- csi->text = NULL;
- csi->tlm = fz_identity;
- csi->tm = fz_identity;
- csi->text_mode = 0;
- csi->accumulate = 1;
-
- csi->gcap = 64;
- csi->gstate = fz_malloc_array(ctx, csi->gcap, sizeof(pdf_gstate));
-
- csi->nested_depth = nested;
- pdf_init_gstate(ctx, &csi->gstate[0], ctm);
- if (gstate)
- {
- pdf_copy_gstate(ctx, &csi->gstate[0], gstate);
- csi->gstate[0].ctm = *ctm;
- }
- csi->gtop = 0;
- csi->gbot = 0;
- csi->gparent = 0;
+ csi->process = *process;
+ csi->xbalance = 0;
csi->cookie = cookie;
}
fz_catch(ctx)
{
- fz_free_path(ctx, csi->path);
+ pdf_process_op(csi, PDF_OP_END, process);
fz_free(ctx, csi);
fz_rethrow(ctx);
}
@@ -1182,1455 +52,14 @@ pdf_clear_stack(pdf_csi *csi)
}
static void
-pdf_gsave(pdf_csi *csi)
-{
- fz_context *ctx = csi->dev->ctx;
-
- if (csi->gtop == csi->gcap-1)
- {
- csi->gstate = fz_resize_array(ctx, csi->gstate, csi->gcap*2, sizeof(pdf_gstate));
- csi->gcap *= 2;
- }
-
- memcpy(&csi->gstate[csi->gtop + 1], &csi->gstate[csi->gtop], sizeof(pdf_gstate));
-
- csi->gtop++;
- pdf_keep_gstate(ctx, &csi->gstate[csi->gtop]);
-}
-
-static void
-pdf_grestore(pdf_csi *csi)
-{
- fz_context *ctx = csi->dev->ctx;
- pdf_gstate *gs = csi->gstate + csi->gtop;
- int clip_depth = gs->clip_depth;
-
- if (csi->gtop <= csi->gbot)
- {
- fz_warn(ctx, "gstate underflow in content stream");
- return;
- }
-
- pdf_drop_gstate(ctx, gs);
- csi->gtop --;
-
- gs = csi->gstate + csi->gtop;
- while (clip_depth > gs->clip_depth)
- {
- fz_try(ctx)
- {
- fz_pop_clip(csi->dev);
- }
- fz_catch(ctx)
- {
- fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
- /* Silently swallow the problem */
- }
- clip_depth--;
- }
-}
-
-static void
pdf_free_csi(pdf_csi *csi)
{
- fz_context *ctx = csi->dev->ctx;
-
- while (csi->gtop)
- pdf_grestore(csi);
-
- pdf_drop_material(ctx, &csi->gstate[0].fill);
- pdf_drop_material(ctx, &csi->gstate[0].stroke);
- if (csi->gstate[0].font)
- pdf_drop_font(ctx, csi->gstate[0].font);
- if (csi->gstate[0].softmask)
- pdf_drop_xobject(ctx, csi->gstate[0].softmask);
- fz_drop_stroke_state(ctx, csi->gstate[0].stroke_state);
-
- while (csi->gstate[0].clip_depth--)
- fz_pop_clip(csi->dev);
-
- if (csi->path) fz_free_path(ctx, csi->path);
- if (csi->text) fz_free_text(ctx, csi->text);
-
- pdf_clear_stack(csi);
-
- fz_free(ctx, csi->gstate);
+ fz_context *ctx = csi->doc->ctx;
+ pdf_process_op(csi, PDF_OP_END, &csi->process);
fz_free(ctx, csi);
}
-/*
- * Material state
- */
-
-static void
-pdf_set_colorspace(pdf_csi *csi, int what, fz_colorspace *colorspace)
-{
- fz_context *ctx = csi->dev->ctx;
- pdf_gstate *gs;
- pdf_material *mat;
-
- gs = pdf_flush_text(csi);
-
- 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, int what, float *v)
-{
- fz_context *ctx = csi->dev->ctx;
- pdf_gstate *gs;
- pdf_material *mat;
- int i;
-
- gs = pdf_flush_text(csi);
-
- 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, int what, fz_shade *shade)
-{
- fz_context *ctx = csi->dev->ctx;
- pdf_gstate *gs;
- pdf_material *mat;
-
- gs = pdf_flush_text(csi);
-
- 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, int what, pdf_pattern *pat, float *v)
-{
- fz_context *ctx = csi->dev->ctx;
- pdf_gstate *gs;
- pdf_material *mat;
-
- gs = pdf_flush_text(csi);
-
- 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 = csi->gparent;
-
- if (v)
- pdf_set_color(csi, what, v);
-}
-
-static void
-pdf_unset_pattern(pdf_csi *csi, int what)
-{
- fz_context *ctx = csi->dev->ctx;
- pdf_gstate *gs = csi->gstate + csi->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;
- }
-}
-
-/*
- * Patterns, XObjects and ExtGState
- */
-
-static void
-pdf_show_pattern(pdf_csi *csi, pdf_pattern *pat, pdf_gstate *pat_gstate, const fz_rect *area, int what)
-{
- fz_context *ctx = csi->dev->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(csi);
- gstate = csi->gstate + csi->gtop;
- /* Patterns are run with the gstate of the parent */
- pdf_copy_pattern_gstate(ctx, gstate, pat_gstate);
-
- if (pat->ismask)
- {
- pdf_unset_pattern(csi, PDF_FILL);
- pdf_unset_pattern(csi, 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(csi, 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 = csi->gparent;
- csi->gparent = csi->gtop-1;
- gparent_save_ctm = csi->gstate[csi->gparent].ctm;
- csi->gstate[csi->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 = csi->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(csi->dev, &local_area, &pat->bbox, pat->xstep, pat->ystep, &ptm);
- gstate->ctm = ptm;
- pdf_gsave(csi);
- pdf_run_contents_object(csi, pat->resources, pat->contents);
- pdf_grestore(csi);
- while (oldtop < csi->gtop)
- pdf_grestore(csi);
- fz_end_tile(csi->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(csi);
- fz_try(ctx)
- {
- pdf_run_contents_object(csi, pat->resources, pat->contents);
- }
- fz_always(ctx)
- {
- pdf_grestore(csi);
- while (oldtop < csi->gtop)
- pdf_grestore(csi);
- }
- fz_catch(ctx)
- {
- fz_rethrow_message(ctx, "cannot render pattern tile");
- }
- }
- }
- }
- }
- fz_always(ctx)
- {
- csi->gstate[csi->gparent].ctm = gparent_save_ctm;
- csi->gparent = gparent_save;
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
- }
-
- pdf_grestore(csi);
-}
-
-static void
-pdf_run_xobject(pdf_csi *csi, pdf_obj *resources, pdf_xobject *xobj, const fz_matrix *transform)
-{
- fz_context *ctx = csi->dev->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;
-
- /* Avoid infinite recursion */
- if (xobj == NULL || pdf_mark_obj(xobj->me))
- return;
-
- fz_var(gstate);
- fz_var(oldtop);
-
- gparent_save = csi->gparent;
- csi->gparent = csi->gtop;
-
- fz_try(ctx)
- {
- pdf_gsave(csi);
-
- gstate = csi->gstate + csi->gtop;
- oldtop = csi->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 = csi->gstate[csi->gparent].ctm;
- csi->gstate[csi->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, &softmask);
-
- fz_begin_group(csi->dev, &bbox,
- xobj->isolated, xobj->knockout, gstate->blendmode, gstate->fill.alpha);
-
- gstate->blendmode = 0;
- gstate->stroke.alpha = 1;
- gstate->fill.alpha = 1;
- }
-
- pdf_gsave(csi); /* Save here so the clippath doesn't persist */
-
- /* clip to the bounds */
- fz_moveto(ctx, csi->path, xobj->bbox.x0, xobj->bbox.y0);
- fz_lineto(ctx, csi->path, xobj->bbox.x1, xobj->bbox.y0);
- fz_lineto(ctx, csi->path, xobj->bbox.x1, xobj->bbox.y1);
- fz_lineto(ctx, csi->path, xobj->bbox.x0, xobj->bbox.y1);
- fz_closepath(ctx, csi->path);
- csi->clip = 1;
- pdf_show_path(csi, 0, 0, 0, 0);
-
- /* run contents */
-
- if (xobj->resources)
- resources = xobj->resources;
-
- pdf_run_contents_object(csi, resources, xobj->contents);
- }
- fz_always(ctx)
- {
- pdf_grestore(csi); /* Remove the clippath */
-
- /* wrap up transparency stacks */
- if (xobj->transparency)
- {
- fz_end_group(csi->dev);
- end_softmask(csi, &softmask);
- }
-
- csi->gstate[csi->gparent].ctm = gparent_save_ctm;
- csi->gparent = gparent_save;
-
- if (gstate)
- {
- while (oldtop < csi->gtop)
- pdf_grestore(csi);
-
- pdf_grestore(csi);
- }
-
- pdf_unmark_obj(xobj->me);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
- }
-
-}
-
-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_obj *rdb, pdf_obj *extgstate)
-{
- fz_context *ctx = csi->dev->ctx;
- pdf_gstate *gstate;
- fz_colorspace *colorspace;
- int i, k, n;
-
- gstate = pdf_flush_text(csi);
-
- 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, csi->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"))
- {
- csi->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"))
- {
- csi->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"))
- {
- csi->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"))
- {
- csi->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");
- }
- }
-}
-
-/*
- * Operators
- */
-
-static void pdf_run_BDC(pdf_csi *csi)
-{
- 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 (csi->in_hidden_ocg > 0)
- {
- csi->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, rdb))
- csi->in_hidden_ocg++;
-}
-
-static void pdf_run_BI(pdf_csi *csi)
-{
- fz_context *ctx = csi->dev->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, 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)
-{
- pdf_show_path(csi, 0, 1, 1, 0);
-}
-
-static void pdf_run_BMC(pdf_csi *csi)
-{
- /* 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 (csi->in_hidden_ocg > 0)
- {
- csi->in_hidden_ocg++;
- }
-}
-
-static void pdf_run_BT(pdf_csi *csi)
-{
- csi->in_text = 1;
- csi->tm = fz_identity;
- csi->tlm = fz_identity;
-}
-
-static void pdf_run_BX(pdf_csi *csi)
-{
- csi->xbalance ++;
-}
-
-static void pdf_run_Bstar(pdf_csi *csi)
-{
- pdf_show_path(csi, 0, 1, 1, 1);
-}
-
-static void pdf_run_cs_imp(pdf_csi *csi, int what)
-{
- fz_context *ctx = csi->dev->ctx;
- fz_colorspace *colorspace;
- pdf_obj *obj, *dict;
- pdf_obj *rdb = csi->rdb;
-
- if (!strcmp(csi->name, "Pattern"))
- {
- pdf_set_pattern(csi, 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, what, colorspace);
-
- fz_drop_colorspace(ctx, colorspace);
- }
-}
-
-static void pdf_run_CS(pdf_csi *csi)
-{
- csi->dev->flags &= ~FZ_DEVFLAG_STROKECOLOR_UNDEFINED;
-
- pdf_run_cs_imp(csi, PDF_STROKE);
-}
-
-static void pdf_run_cs(pdf_csi *csi)
-{
- csi->dev->flags &= ~FZ_DEVFLAG_FILLCOLOR_UNDEFINED;
-
- pdf_run_cs_imp(csi, PDF_FILL);
-}
-
-static void pdf_run_DP(pdf_csi *csi)
-{
-}
-
-static void pdf_run_Do(pdf_csi *csi)
-{
- fz_context *ctx = csi->dev->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, 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)
- {
- pdf_run_xobject(csi, 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 ((csi->dev->hints & FZ_IGNORE_IMAGE) == 0)
- {
- fz_image *img = pdf_load_image(csi->doc, obj);
-
- fz_try(ctx)
- {
- pdf_show_image(csi, 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)
-{
- if (csi->in_hidden_ocg > 0)
- csi->in_hidden_ocg--;
-}
-
-static void pdf_run_ET(pdf_csi *csi)
-{
- pdf_flush_text(csi);
- csi->accumulate = 1;
- csi->in_text = 0;
-}
-
-static void pdf_run_EX(pdf_csi *csi)
-{
- csi->xbalance --;
-}
-
-static void pdf_run_F(pdf_csi *csi)
-{
- pdf_show_path(csi, 0, 1, 0, 0);
-}
-
-static void pdf_run_G(pdf_csi *csi)
-{
- csi->dev->flags &= ~FZ_DEVFLAG_STROKECOLOR_UNDEFINED;
- pdf_set_colorspace(csi, PDF_STROKE, fz_device_gray(csi->dev->ctx));
- pdf_set_color(csi, PDF_STROKE, csi->stack);
-}
-
-static void pdf_run_J(pdf_csi *csi)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
- csi->dev->flags &= ~(FZ_DEVFLAG_STARTCAP_UNDEFINED | FZ_DEVFLAG_DASHCAP_UNDEFINED | FZ_DEVFLAG_ENDCAP_UNDEFINED);
- gstate->stroke_state = fz_unshare_stroke_state(csi->dev->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)
-{
- csi->dev->flags &= ~FZ_DEVFLAG_STROKECOLOR_UNDEFINED;
- pdf_set_colorspace(csi, PDF_STROKE, fz_device_cmyk(csi->dev->ctx));
- pdf_set_color(csi, PDF_STROKE, csi->stack);
-}
-
-static void pdf_run_M(pdf_csi *csi)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
- csi->dev->flags &= ~FZ_DEVFLAG_MITERLIMIT_UNDEFINED;
- gstate->stroke_state = fz_unshare_stroke_state(csi->dev->ctx, gstate->stroke_state);
- gstate->stroke_state->miterlimit = csi->stack[0];
-}
-
-static void pdf_run_MP(pdf_csi *csi)
-{
-}
-
-static void pdf_run_Q(pdf_csi *csi)
-{
- pdf_grestore(csi);
-}
-
-static void pdf_run_RG(pdf_csi *csi)
-{
- csi->dev->flags &= ~FZ_DEVFLAG_STROKECOLOR_UNDEFINED;
- pdf_set_colorspace(csi, PDF_STROKE, fz_device_rgb(csi->dev->ctx));
- pdf_set_color(csi, PDF_STROKE, csi->stack);
-}
-
-static void pdf_run_S(pdf_csi *csi)
-{
- pdf_show_path(csi, 0, 0, 1, 0);
-}
-
-static void pdf_run_SC_imp(pdf_csi *csi, int what, pdf_material *mat)
-{
- fz_context *ctx = csi->dev->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, 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, 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, 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 = csi->gparent;
-}
-
-static void pdf_run_SC(pdf_csi *csi)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
-
- csi->dev->flags &= ~FZ_DEVFLAG_STROKECOLOR_UNDEFINED;
- pdf_run_SC_imp(csi, PDF_STROKE, &gstate->stroke);
-}
-
-static void pdf_run_sc(pdf_csi *csi)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
-
- csi->dev->flags &= ~FZ_DEVFLAG_FILLCOLOR_UNDEFINED;
- pdf_run_SC_imp(csi, PDF_FILL, &gstate->fill);
-}
-
-static void pdf_run_Tc(pdf_csi *csi)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
- gstate->char_space = csi->stack[0];
-}
-
-static void pdf_run_Tw(pdf_csi *csi)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
- gstate->word_space = csi->stack[0];
-}
-
-static void pdf_run_Tz(pdf_csi *csi)
-{
- pdf_gstate *gstate;
- float a = csi->stack[0] / 100;
- gstate = pdf_flush_text(csi);
- gstate->scale = a;
-}
-
-static void pdf_run_TL(pdf_csi *csi)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
- gstate->leading = csi->stack[0];
-}
-
-static void pdf_run_Tf(pdf_csi *csi)
-{
- fz_context *ctx = csi->dev->ctx;
- pdf_obj *rdb = csi->rdb;
- pdf_gstate *gstate = csi->gstate + csi->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, csi->nested_depth);
-}
-
-static void pdf_run_Tr(pdf_csi *csi)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
- gstate->render = csi->stack[0];
-}
-
-static void pdf_run_Ts(pdf_csi *csi)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
- gstate->rise = csi->stack[0];
-}
-
-static void pdf_run_Td(pdf_csi *csi)
-{
- fz_pre_translate(&csi->tlm, csi->stack[0], csi->stack[1]);
- csi->tm = csi->tlm;
-}
-
-static void pdf_run_TD(pdf_csi *csi)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
-
- gstate->leading = -csi->stack[1];
- fz_pre_translate(&csi->tlm, csi->stack[0], csi->stack[1]);
- csi->tm = csi->tlm;
-}
-
-static void pdf_run_Tm(pdf_csi *csi)
-{
- csi->tm.a = csi->stack[0];
- csi->tm.b = csi->stack[1];
- csi->tm.c = csi->stack[2];
- csi->tm.d = csi->stack[3];
- csi->tm.e = csi->stack[4];
- csi->tm.f = csi->stack[5];
- csi->tlm = csi->tm;
-}
-
-static void pdf_run_Tstar(pdf_csi *csi)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
- fz_pre_translate(&csi->tlm, 0, -gstate->leading);
- csi->tm = csi->tlm;
-}
-
-static void pdf_run_Tj(pdf_csi *csi)
-{
- if (csi->string_len)
- pdf_show_string(csi, csi->string, csi->string_len);
- else
- pdf_show_text(csi, csi->obj);
-}
-
-static void pdf_run_TJ(pdf_csi *csi)
-{
- if (csi->string_len)
- pdf_show_string(csi, csi->string, csi->string_len);
- else
- pdf_show_text(csi, csi->obj);
-}
-
-static void pdf_run_W(pdf_csi *csi)
-{
- csi->clip = 1;
- csi->clip_even_odd = 0;
-}
-
-static void pdf_run_Wstar(pdf_csi *csi)
-{
- csi->clip = 1;
- csi->clip_even_odd = 1;
-}
-
-static void pdf_run_b(pdf_csi *csi)
-{
- pdf_show_path(csi, 1, 1, 1, 0);
-}
-
-static void pdf_run_bstar(pdf_csi *csi)
-{
- pdf_show_path(csi, 1, 1, 1, 1);
-}
-
-static void pdf_run_c(pdf_csi *csi)
-{
- 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->dev->ctx, csi->path, a, b, c, d, e, f);
-}
-
-static void pdf_run_cm(pdf_csi *csi)
-{
- pdf_gstate *gstate = csi->gstate + csi->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)
-{
- pdf_gstate *gstate = csi->gstate + csi->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->dev->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)
-{
- if (csi->nested_depth > 1)
- return;
- csi->dev->flags |= FZ_DEVFLAG_COLOR;
-}
-
-static void pdf_run_d1(pdf_csi *csi)
-{
- if (csi->nested_depth > 1)
- return;
- csi->dev->flags |= FZ_DEVFLAG_MASK;
- csi->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)
-{
- pdf_show_path(csi, 0, 1, 0, 0);
-}
-
-static void pdf_run_fstar(pdf_csi *csi)
-{
- pdf_show_path(csi, 0, 1, 0, 1);
-}
-
-static void pdf_run_g(pdf_csi *csi)
-{
- csi->dev->flags &= ~FZ_DEVFLAG_FILLCOLOR_UNDEFINED;
- pdf_set_colorspace(csi, PDF_FILL, fz_device_gray(csi->dev->ctx));
- pdf_set_color(csi, PDF_FILL, csi->stack);
-}
-
-static void pdf_run_gs(pdf_csi *csi)
-{
- pdf_obj *dict;
- pdf_obj *obj;
- fz_context *ctx = csi->dev->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, rdb, obj);
-}
-
-static void pdf_run_h(pdf_csi *csi)
-{
- fz_closepath(csi->dev->ctx, csi->path);
-}
-
-static void pdf_run_i(pdf_csi *csi)
-{
-}
-
-static void pdf_run_j(pdf_csi *csi)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
- csi->dev->flags &= ~FZ_DEVFLAG_LINEJOIN_UNDEFINED;
- gstate->stroke_state = fz_unshare_stroke_state(csi->dev->ctx, gstate->stroke_state);
- gstate->stroke_state->linejoin = csi->stack[0];
-}
-
-static void pdf_run_k(pdf_csi *csi)
-{
- csi->dev->flags &= ~FZ_DEVFLAG_FILLCOLOR_UNDEFINED;
- pdf_set_colorspace(csi, PDF_FILL, fz_device_cmyk(csi->dev->ctx));
- pdf_set_color(csi, PDF_FILL, csi->stack);
-}
-
-static void pdf_run_l(pdf_csi *csi)
-{
- float a, b;
- a = csi->stack[0];
- b = csi->stack[1];
- fz_lineto(csi->dev->ctx, csi->path, a, b);
-}
-
-static void pdf_run_m(pdf_csi *csi)
-{
- float a, b;
- a = csi->stack[0];
- b = csi->stack[1];
- fz_moveto(csi->dev->ctx, csi->path, a, b);
-}
-
-static void pdf_run_n(pdf_csi *csi)
-{
- pdf_show_path(csi, 0, 0, 0, 0);
-}
-
-static void pdf_run_q(pdf_csi *csi)
-{
- pdf_gsave(csi);
-}
-
-static void pdf_run_re(pdf_csi *csi)
-{
- fz_context *ctx = csi->dev->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, csi->path, x, y);
- fz_lineto(ctx, csi->path, x + w, y);
- fz_lineto(ctx, csi->path, x + w, y + h);
- fz_lineto(ctx, csi->path, x, y + h);
- fz_closepath(ctx, csi->path);
-}
-
-static void pdf_run_rg(pdf_csi *csi)
-{
- csi->dev->flags &= ~FZ_DEVFLAG_FILLCOLOR_UNDEFINED;
- pdf_set_colorspace(csi, PDF_FILL, fz_device_rgb(csi->dev->ctx));
- pdf_set_color(csi, PDF_FILL, csi->stack);
-}
-
-static void pdf_run_ri(pdf_csi *csi)
-{
-}
-
-static void pdf_run_s(pdf_csi *csi)
-{
- pdf_show_path(csi, 1, 0, 1, 0);
-}
-
-static void pdf_run_sh(pdf_csi *csi)
-{
- fz_context *ctx = csi->dev->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 ((csi->dev->hints & FZ_IGNORE_SHADE) == 0)
- {
- shd = pdf_load_shading(csi->doc, obj);
-
- fz_try(ctx)
- {
- pdf_show_shade(csi, shd);
- }
- fz_always(ctx)
- {
- fz_drop_shade(ctx, shd);
- }
- fz_catch(ctx)
- {
- fz_rethrow(ctx);
- }
- }
-}
-
-static void pdf_run_v(pdf_csi *csi)
-{
- float a, b, c, d;
- a = csi->stack[0];
- b = csi->stack[1];
- c = csi->stack[2];
- d = csi->stack[3];
- fz_curvetov(csi->dev->ctx, csi->path, a, b, c, d);
-}
-
-static void pdf_run_w(pdf_csi *csi)
-{
- pdf_gstate *gstate;
- gstate = pdf_flush_text(csi); /* linewidth affects stroked text rendering mode */
- csi->dev->flags &= ~FZ_DEVFLAG_LINEWIDTH_UNDEFINED;
- gstate->stroke_state = fz_unshare_stroke_state(csi->dev->ctx, gstate->stroke_state);
- gstate->stroke_state->linewidth = csi->stack[0];
-}
-
-static void pdf_run_y(pdf_csi *csi)
-{
- float a, b, c, d;
- a = csi->stack[0];
- b = csi->stack[1];
- c = csi->stack[2];
- d = csi->stack[3];
- fz_curvetoy(csi->dev->ctx, csi->path, a, b, c, d);
-}
-
-static void pdf_run_squote(pdf_csi *csi)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
-
- fz_pre_translate(&csi->tlm, 0, -gstate->leading);
- csi->tm = csi->tlm;
-
- if (csi->string_len)
- pdf_show_string(csi, csi->string, csi->string_len);
- else
- pdf_show_text(csi, csi->obj);
-}
-
-static void pdf_run_dquote(pdf_csi *csi)
-{
- pdf_gstate *gstate = csi->gstate + csi->gtop;
-
- gstate->word_space = csi->stack[0];
- gstate->char_space = csi->stack[1];
-
- fz_pre_translate(&csi->tlm, 0, -gstate->leading);
- csi->tm = csi->tlm;
-
- if (csi->string_len)
- pdf_show_string(csi, csi->string, csi->string_len);
- else
- pdf_show_text(csi, csi->obj);
-}
-
#define A(a) (a)
#define B(a,b) (a | b << 8)
#define C(a,b,c) (a | b << 8 | c << 16)
@@ -2638,8 +67,9 @@ static void pdf_run_dquote(pdf_csi *csi)
static int
pdf_run_keyword(pdf_csi *csi, char *buf)
{
- fz_context *ctx = csi->dev->ctx;
+ fz_context *ctx = csi->doc->ctx;
int key;
+ PDF_OP op;
key = buf[0];
if (buf[1])
@@ -2655,128 +85,136 @@ pdf_run_keyword(pdf_csi *csi, char *buf)
switch (key)
{
- case A('"'): pdf_run_dquote(csi); break;
- case A('\''): pdf_run_squote(csi); break;
- case A('B'): pdf_run_B(csi); break;
- case B('B','*'): pdf_run_Bstar(csi); break;
- case C('B','D','C'): pdf_run_BDC(csi); break;
- case B('B','I'): pdf_run_BI(csi); break;
- case C('B','M','C'): pdf_run_BMC(csi); break;
- case B('B','T'): pdf_run_BT(csi); break;
- case B('B','X'): pdf_run_BX(csi); break;
- case B('C','S'): pdf_run_CS(csi); break;
- case B('D','P'): pdf_run_DP(csi); break;
- case B('D','o'):
- fz_try(ctx)
- {
- pdf_run_Do(csi);
- }
- fz_catch(ctx)
- {
- fz_rethrow_message(ctx, "cannot draw xobject/image");
- }
+ case A('"'): op = PDF_OP_dquote;break;
+ case A('\''): op = PDF_OP_squote; break;
+ case A('B'): op = PDF_OP_B; break;
+ case B('B','*'): op = PDF_OP_Bstar; break;
+ case C('B','D','C'): op = PDF_OP_BDC; break;
+ case B('B','I'): op = PDF_OP_BI; break;
+ case C('B','M','C'): op = PDF_OP_BMC; break;
+ case B('B','T'):
+ op = PDF_OP_BT;
+ csi->in_text = 1;
break;
- case C('E','M','C'): pdf_run_EMC(csi); break;
- case B('E','T'): pdf_run_ET(csi); break;
- case B('E','X'): pdf_run_EX(csi); break;
- case A('F'): pdf_run_F(csi); break;
- case A('G'): pdf_run_G(csi); break;
- case A('J'): pdf_run_J(csi); break;
- case A('K'): pdf_run_K(csi); break;
- case A('M'): pdf_run_M(csi); break;
- case B('M','P'): pdf_run_MP(csi); break;
- case A('Q'): pdf_run_Q(csi); break;
- case B('R','G'): pdf_run_RG(csi); break;
- case A('S'): pdf_run_S(csi); break;
- case B('S','C'): pdf_run_SC(csi); break;
- case C('S','C','N'): pdf_run_SC(csi); break;
- case B('T','*'): pdf_run_Tstar(csi); break;
- case B('T','D'): pdf_run_TD(csi); break;
- case B('T','J'): pdf_run_TJ(csi); break;
- case B('T','L'): pdf_run_TL(csi); break;
- case B('T','c'): pdf_run_Tc(csi); break;
- case B('T','d'): pdf_run_Td(csi); break;
- case B('T','f'):
- fz_try(ctx)
- {
- pdf_run_Tf(csi);
- }
- fz_catch(ctx)
- {
- fz_rethrow_message(ctx, "cannot set font");
- }
+ case B('B','X'):
+ op = PDF_OP_BX;
+ csi->xbalance++;
break;
- case B('T','j'): pdf_run_Tj(csi); break;
- case B('T','m'): pdf_run_Tm(csi); break;
- case B('T','r'): pdf_run_Tr(csi); break;
- case B('T','s'): pdf_run_Ts(csi); break;
- case B('T','w'): pdf_run_Tw(csi); break;
- case B('T','z'): pdf_run_Tz(csi); break;
- case A('W'): pdf_run_W(csi); break;
- case B('W','*'): pdf_run_Wstar(csi); break;
- case A('b'): pdf_run_b(csi); break;
- case B('b','*'): pdf_run_bstar(csi); break;
- case A('c'): pdf_run_c(csi); break;
- case B('c','m'): pdf_run_cm(csi); break;
- case B('c','s'): pdf_run_cs(csi); break;
- case A('d'): pdf_run_d(csi); break;
- case B('d','0'): pdf_run_d0(csi); break;
- case B('d','1'): pdf_run_d1(csi); break;
- case A('f'): pdf_run_f(csi); break;
- case B('f','*'): pdf_run_fstar(csi); break;
- case A('g'): pdf_run_g(csi); break;
- case B('g','s'):
- fz_try(ctx)
- {
- pdf_run_gs(csi);
- }
- fz_catch(ctx)
- {
- fz_rethrow_message(ctx, "cannot set graphics state");
- }
+ case B('C','S'): op = PDF_OP_CS; break;
+ case B('D','P'): op = PDF_OP_DP; break;
+ case B('D','o'): op = PDF_OP_Do; break;
+ case C('E','M','C'): op = PDF_OP_EMC; break;
+ case B('E','T'):
+ op = PDF_OP_ET;
+ csi->in_text = 0;
break;
- case A('h'): pdf_run_h(csi); break;
- case A('i'): pdf_run_i(csi); break;
- case A('j'): pdf_run_j(csi); break;
- case A('k'): pdf_run_k(csi); break;
- case A('l'): pdf_run_l(csi); break;
- case A('m'): pdf_run_m(csi); break;
- case A('n'): pdf_run_n(csi); break;
- case A('q'): pdf_run_q(csi); break;
- case B('r','e'): pdf_run_re(csi); break;
- case B('r','g'): pdf_run_rg(csi); break;
- case B('r','i'): pdf_run_ri(csi); break;
- case A('s'): pdf_run_s(csi); break;
- case B('s','c'): pdf_run_sc(csi); break;
- case C('s','c','n'): pdf_run_sc(csi); break;
- case B('s','h'):
- fz_try(ctx)
- {
- pdf_run_sh(csi);
- }
- fz_catch(ctx)
- {
- fz_rethrow_message(ctx, "cannot draw shading");
- }
+ case B('E','X'):
+ op = PDF_OP_EX;
+ csi->xbalance--;
break;
- case A('v'): pdf_run_v(csi); break;
- case A('w'): pdf_run_w(csi); break;
- case A('y'): pdf_run_y(csi); break;
+ case A('F'): op = PDF_OP_F; break;
+ case A('G'): op = PDF_OP_G; break;
+ case A('J'): op = PDF_OP_J; break;
+ case A('K'): op = PDF_OP_K; break;
+ case A('M'): op = PDF_OP_M; break;
+ case B('M','P'): op = PDF_OP_MP; break;
+ case A('Q'): op = PDF_OP_Q; break;
+ case B('R','G'): op = PDF_OP_RG; break;
+ case A('S'): op = PDF_OP_S; break;
+ case B('S','C'): op = PDF_OP_SC; break;
+ case C('S','C','N'): op = PDF_OP_SCN; break;
+ case B('T','*'): op = PDF_OP_Tstar; break;
+ case B('T','D'): op = PDF_OP_TD; break;
+ case B('T','J'): op = PDF_OP_TJ; break;
+ case B('T','L'): op = PDF_OP_TL; break;
+ case B('T','c'): op = PDF_OP_Tc; break;
+ case B('T','d'): op = PDF_OP_Td; break;
+ case B('T','f'): op = PDF_OP_Tf; break;
+ case B('T','j'): op = PDF_OP_Tj; break;
+ case B('T','m'): op = PDF_OP_Tm; break;
+ case B('T','r'): op = PDF_OP_Tr; break;
+ case B('T','s'): op = PDF_OP_Ts; break;
+ case B('T','w'): op = PDF_OP_Tw; break;
+ case B('T','z'): op = PDF_OP_Tz; break;
+ case A('W'): op = PDF_OP_W; break;
+ case B('W','*'): op = PDF_OP_Wstar; break;
+ case A('b'): op = PDF_OP_b; break;
+ case B('b','*'): op = PDF_OP_bstar; break;
+ case A('c'): op = PDF_OP_c; break;
+ case B('c','m'): op = PDF_OP_cm; break;
+ case B('c','s'): op = PDF_OP_cs; break;
+ case A('d'): op = PDF_OP_d; break;
+ case B('d','0'): op = PDF_OP_d0; break;
+ case B('d','1'): op = PDF_OP_d1; break;
+ case A('f'): op = PDF_OP_f; break;
+ case B('f','*'): op = PDF_OP_fstar; break;
+ case A('g'): op = PDF_OP_g; break;
+ case B('g','s'): op = PDF_OP_gs; break;
+ case A('h'): op = PDF_OP_h; break;
+ case A('i'): op = PDF_OP_i; break;
+ case A('j'): op = PDF_OP_j; break;
+ case A('k'): op = PDF_OP_k; break;
+ case A('l'): op = PDF_OP_l; break;
+ case A('m'): op = PDF_OP_m; break;
+ case A('n'): op = PDF_OP_n; break;
+ case A('q'): op = PDF_OP_q; break;
+ case B('r','e'): op = PDF_OP_re; break;
+ case B('r','g'): op = PDF_OP_rg; break;
+ case B('r','i'): op = PDF_OP_ri; break;
+ case A('s'): op = PDF_OP_s; break;
+ case B('s','c'): op = PDF_OP_sc; break;
+ case C('s','c','n'): op = PDF_OP_scn; break;
+ case B('s','h'): op = PDF_OP_sh; break;
+ case A('v'): op = PDF_OP_v; break;
+ case A('w'): op = PDF_OP_w; break;
+ case A('y'): op = PDF_OP_y; break;
default:
if (!csi->xbalance)
{
fz_warn(ctx, "unknown keyword: '%s'", buf);
return 1;
}
- break;
+ return 0;
+ }
+
+ if (op < PDF_OP_Do)
+ {
+ pdf_process_op(csi, op, &csi->process);
+ }
+ else if (op < PDF_OP_END)
+ {
+ fz_try(ctx)
+ {
+ pdf_process_op(csi, op, &csi->process);
+ }
+ fz_catch(ctx)
+ {
+ switch (op)
+ {
+ case PDF_OP_Do:
+ fz_rethrow_message(ctx, "cannot draw xobject/image");
+ break;
+ case PDF_OP_Tf:
+ fz_rethrow_message(ctx, "cannot set font");
+ break;
+ case PDF_OP_gs:
+ fz_rethrow_message(ctx, "cannot set graphics state");
+ break;
+ case PDF_OP_sh:
+ fz_rethrow_message(ctx, "cannot draw shading");
+ break;
+ default:
+ fz_rethrow(ctx); /* Should never happen */
+ }
+ }
}
return 0;
}
-static void
-pdf_run_stream(pdf_csi *csi, pdf_lexbuf *buf)
+void
+pdf_process_stream(pdf_csi *csi, pdf_lexbuf *buf)
{
- fz_context *ctx = csi->dev->ctx;
+ fz_context *ctx = csi->doc->ctx;
fz_stream *file = csi->file;
pdf_token tok = PDF_TOK_ERROR;
int in_text_array = 0;
@@ -2973,12 +411,11 @@ pdf_run_stream(pdf_csi *csi, pdf_lexbuf *buf)
*/
static void
-pdf_run_contents_stream(pdf_csi *csi, pdf_obj *rdb, fz_stream *file)
+pdf_process_contents_stream(pdf_csi *csi, pdf_obj *rdb, fz_stream *file)
{
- fz_context *ctx = csi->dev->ctx;
+ fz_context *ctx = csi->doc->ctx;
pdf_lexbuf *buf;
int save_in_text;
- int save_gbot;
pdf_obj *save_obj;
pdf_obj *save_rdb = csi->rdb;
fz_stream *save_file = csi->file;
@@ -2992,18 +429,17 @@ pdf_run_contents_stream(pdf_csi *csi, pdf_obj *rdb, fz_stream *file)
pdf_lexbuf_init(ctx, buf, PDF_LEXBUF_SMALL);
save_in_text = csi->in_text;
csi->in_text = 0;
- save_gbot = csi->gbot;
- csi->gbot = csi->gtop;
save_obj = csi->obj;
csi->obj = NULL;
csi->rdb = rdb;
csi->file = file;
fz_try(ctx)
{
- pdf_run_stream(csi, buf);
+ csi->process.processor->process_stream(csi, csi->process.state, buf);
}
fz_always(ctx)
{
+ csi->in_text = save_in_text;
pdf_drop_obj(csi->obj);
csi->obj = save_obj;
csi->rdb = save_rdb;
@@ -3016,49 +452,51 @@ pdf_run_contents_stream(pdf_csi *csi, pdf_obj *rdb, fz_stream *file)
fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
fz_warn(ctx, "Content stream parsing error - rendering truncated");
}
- while (csi->gtop > csi->gbot)
- pdf_grestore(csi);
- csi->gbot = save_gbot;
- csi->in_text = save_in_text;
}
-static void
-pdf_run_contents_object(pdf_csi *csi, pdf_obj *rdb, pdf_obj *contents)
+void
+pdf_process_annot(pdf_document *doc, pdf_page *page, pdf_annot *annot, const pdf_process *process, fz_cookie *cookie)
{
- fz_context *ctx = csi->dev->ctx;
- fz_stream *file = NULL;
+ fz_context *ctx = doc->ctx;
+ pdf_csi *csi;
+ int flags;
- if (contents == NULL)
+ flags = pdf_to_int(pdf_dict_gets(annot->obj, "F"));
+
+ /* TODO: NoZoom and NoRotate */
+ if (flags & (1 << 0)) /* Invisible */
+ return;
+ if (flags & (1 << 1)) /* Hidden */
return;
- file = pdf_open_contents_stream(csi->doc, contents);
+ csi = pdf_new_csi(doc, cookie, process);
fz_try(ctx)
{
- pdf_run_contents_stream(csi, rdb, file);
+ csi->process.processor->process_annot(csi, csi->process.state, page->resources, annot);
}
fz_always(ctx)
{
- fz_close(file);
+ pdf_free_csi(csi);
}
fz_catch(ctx)
{
- fz_rethrow(ctx);
+ fz_rethrow_message(ctx, "cannot parse annotation appearance stream");
}
}
-static void
-pdf_run_contents_buffer(pdf_csi *csi, pdf_obj *rdb, fz_buffer *contents)
+void
+pdf_process_contents_object(pdf_csi *csi, pdf_obj *rdb, pdf_obj *contents)
{
- fz_context *ctx = csi->dev->ctx;
+ fz_context *ctx = csi->doc->ctx;
fz_stream *file = NULL;
if (contents == NULL)
return;
- file = fz_open_buffer(ctx, contents);
+ file = pdf_open_contents_stream(csi->doc, contents);
fz_try(ctx)
{
- pdf_run_contents_stream(csi, rdb, file);
+ pdf_process_contents_stream(csi, rdb, file);
}
fz_always(ctx)
{
@@ -3070,151 +508,61 @@ pdf_run_contents_buffer(pdf_csi *csi, pdf_obj *rdb, fz_buffer *contents)
}
}
-static void pdf_run_page_contents_with_usage(pdf_document *doc, pdf_page *page, fz_device *dev, const fz_matrix *ctm, char *event, fz_cookie *cookie)
+static void
+pdf_process_contents_buffer(pdf_csi *csi, pdf_obj *rdb, fz_buffer *contents)
{
- fz_context *ctx = dev->ctx;
- pdf_csi *csi;
- fz_matrix local_ctm;
-
- fz_concat(&local_ctm, &page->ctm, ctm);
+ fz_context *ctx = csi->doc->ctx;
+ fz_stream *file = NULL;
- if (page->transparency)
- {
- fz_rect mediabox = page->mediabox;
- fz_begin_group(dev, fz_transform_rect(&mediabox, &local_ctm), 1, 0, 0, 1);
- }
+ if (contents == NULL)
+ return;
- csi = pdf_new_csi(doc, dev, &local_ctm, event, cookie, NULL, 0);
+ file = fz_open_buffer(ctx, contents);
fz_try(ctx)
{
- /* We need to save an extra level here to allow for level 0
- * to be the 'parent' gstate level. */
- pdf_gsave(csi);
- pdf_run_contents_object(csi, page->resources, page->contents);
+ pdf_process_contents_stream(csi, rdb, file);
}
fz_always(ctx)
{
- while (csi->gtop > 0)
- pdf_grestore(csi);
- pdf_free_csi(csi);
+ fz_close(file);
}
fz_catch(ctx)
{
- fz_rethrow_message(ctx, "cannot parse page content stream");
+ fz_rethrow(ctx);
}
-
- if (page->transparency)
- fz_end_group(dev);
}
-void pdf_run_page_contents(pdf_document *doc, pdf_page *page, fz_device *dev, const fz_matrix *ctm, fz_cookie *cookie)
-{
- pdf_run_page_contents_with_usage(doc, page, dev, ctm, "View", cookie);
- if (page->incomplete & PDF_PAGE_INCOMPLETE_CONTENTS)
- fz_throw(doc->ctx, FZ_ERROR_TRYLATER, "incomplete rendering");
-}
-
-static void pdf_run_annot_with_usage(pdf_document *doc, pdf_page *page, pdf_annot *annot, fz_device *dev, const fz_matrix *ctm, char *event, fz_cookie *cookie)
+void
+pdf_process_page_contents(pdf_document *doc, pdf_page *page, const pdf_process *process, fz_cookie *cookie)
{
- fz_context *ctx = dev->ctx;
+ fz_context *ctx = doc->ctx;
pdf_csi *csi;
- int flags;
- fz_matrix local_ctm;
-
- fz_concat(&local_ctm, &page->ctm, ctm);
-
- flags = pdf_to_int(pdf_dict_gets(annot->obj, "F"));
- /* TODO: NoZoom and NoRotate */
- if (flags & (1 << 0)) /* Invisible */
- return;
- if (flags & (1 << 1)) /* Hidden */
- return;
- if (!strcmp(event, "Print") && !(flags & (1 << 2))) /* Print */
- return;
- if (!strcmp(event, "View") && (flags & (1 << 5))) /* NoView */
- return;
-
- csi = pdf_new_csi(doc, dev, &local_ctm, event, cookie, NULL, 0);
- if (!pdf_is_hidden_ocg(pdf_dict_gets(annot->obj, "OC"), csi, page->resources))
+ csi = pdf_new_csi(doc, cookie, process);
+ fz_try(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(csi);
- pdf_run_xobject(csi, page->resources, annot->ap, &annot->matrix);
- }
- fz_catch(ctx)
- {
- while (csi->gtop > 0)
- pdf_grestore(csi);
- pdf_free_csi(csi);
- fz_rethrow_message(ctx, "cannot parse annotation appearance stream");
- }
+ csi->process.processor->process_contents(csi, csi->process.state, page->resources, page->contents);
}
- pdf_free_csi(csi);
-}
-
-void pdf_run_annot(pdf_document *doc, pdf_page *page, pdf_annot *annot, fz_device *dev, const fz_matrix *ctm, fz_cookie *cookie)
-{
- pdf_run_annot_with_usage(doc, page, annot, dev, ctm, "View", cookie);
- if (page->incomplete & PDF_PAGE_INCOMPLETE_ANNOTS)
- fz_throw(doc->ctx, FZ_ERROR_TRYLATER, "incomplete rendering");
-}
-
-static void pdf_run_page_annots_with_usage(pdf_document *doc, pdf_page *page, fz_device *dev, const fz_matrix *ctm, char *event, fz_cookie *cookie)
-{
- pdf_annot *annot;
-
- if (cookie && cookie->progress_max != -1)
+ fz_always(ctx)
{
- int count = 1;
- for (annot = page->annots; annot; annot = annot->next)
- count++;
- cookie->progress_max += count;
+ pdf_free_csi(csi);
}
-
- for (annot = page->annots; annot; annot = annot->next)
+ fz_catch(ctx)
{
- /* Check the cookie for aborting */
- if (cookie)
- {
- if (cookie->abort)
- break;
- cookie->progress++;
- }
-
- pdf_run_annot_with_usage(doc, page, annot, dev, ctm, event, cookie);
+ fz_rethrow_message(ctx, "cannot parse page content stream");
}
}
void
-pdf_run_page_with_usage(pdf_document *doc, pdf_page *page, fz_device *dev, const fz_matrix *ctm, char *event, fz_cookie *cookie)
+pdf_process_glyph(pdf_document *doc, pdf_obj *resources, fz_buffer *contents, pdf_process *process)
{
- pdf_run_page_contents_with_usage(doc, page, dev, ctm, event, cookie);
- pdf_run_page_annots_with_usage(doc, page, dev, ctm, event, cookie);
- if (page->incomplete)
- fz_throw(doc->ctx, FZ_ERROR_TRYLATER, "incomplete rendering");
-}
-
-void
-pdf_run_page(pdf_document *doc, pdf_page *page, fz_device *dev, const fz_matrix *ctm, fz_cookie *cookie)
-{
- pdf_run_page_with_usage(doc, page, dev, ctm, "View", cookie);
-}
-
-void
-pdf_run_glyph(pdf_document *doc, pdf_obj *resources, fz_buffer *contents, fz_device *dev, const fz_matrix *ctm, void *gstate, int nested_depth)
-{
- pdf_csi *csi = pdf_new_csi(doc, dev, ctm, "View", NULL, gstate, nested_depth+1);
+ pdf_csi *csi;
fz_context *ctx = doc->ctx;
+ csi = pdf_new_csi(doc, NULL, process);
fz_try(ctx)
{
- if (nested_depth > 10)
- fz_throw(ctx, FZ_ERROR_GENERIC, "Too many nestings of Type3 glyphs");
- pdf_run_contents_buffer(csi, resources, contents);
+ pdf_process_contents_buffer(csi, resources, contents);
}
fz_always(ctx)
{
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;
+}
diff --git a/source/pdf/pdf-run.c b/source/pdf/pdf-run.c
new file mode 100644
index 00000000..4ac92bb0
--- /dev/null
+++ b/source/pdf/pdf-run.c
@@ -0,0 +1,105 @@
+#include "pdf-interpret-imp.h"
+
+static void
+pdf_run_annot_with_usage(pdf_document *doc, pdf_page *page, pdf_annot *annot, fz_device *dev, const fz_matrix *ctm, char *event, fz_cookie *cookie)
+{
+ fz_matrix local_ctm;
+ pdf_process process;
+
+ fz_concat(&local_ctm, &page->ctm, ctm);
+
+ pdf_process_run(&process, dev, &local_ctm, event, NULL, 0);
+
+ pdf_process_annot(doc, page, annot, &process, cookie);
+}
+
+static void pdf_run_page_contents_with_usage(pdf_document *doc, pdf_page *page, fz_device *dev, const fz_matrix *ctm, char *event, fz_cookie *cookie)
+{
+ fz_matrix local_ctm;
+ pdf_process process;
+
+ fz_concat(&local_ctm, &page->ctm, ctm);
+
+ if (page->transparency)
+ {
+ fz_rect mediabox = page->mediabox;
+ fz_begin_group(dev, fz_transform_rect(&mediabox, &local_ctm), 1, 0, 0, 1);
+ }
+
+ pdf_process_run(&process, dev, &local_ctm, event, NULL, 0);
+
+ pdf_process_page_contents(doc, page, &process, cookie);
+
+ if (page->transparency)
+ fz_end_group(dev);
+}
+
+void pdf_run_page_contents(pdf_document *doc, pdf_page *page, fz_device *dev, const fz_matrix *ctm, fz_cookie *cookie)
+{
+ pdf_run_page_contents_with_usage(doc, page, dev, ctm, "View", cookie);
+ if (page->incomplete & PDF_PAGE_INCOMPLETE_CONTENTS)
+ fz_throw(doc->ctx, FZ_ERROR_TRYLATER, "incomplete rendering");
+}
+
+
+void pdf_run_annot(pdf_document *doc, pdf_page *page, pdf_annot *annot, fz_device *dev, const fz_matrix *ctm, fz_cookie *cookie)
+{
+ pdf_run_annot_with_usage(doc, page, annot, dev, ctm, "View", cookie);
+ if (page->incomplete & PDF_PAGE_INCOMPLETE_ANNOTS)
+ fz_throw(doc->ctx, FZ_ERROR_TRYLATER, "incomplete rendering");
+}
+
+static void pdf_run_page_annots_with_usage(pdf_document *doc, pdf_page *page, fz_device *dev, const fz_matrix *ctm, char *event, fz_cookie *cookie)
+{
+ pdf_annot *annot;
+
+ if (cookie && cookie->progress_max != -1)
+ {
+ int count = 1;
+ for (annot = page->annots; annot; annot = annot->next)
+ count++;
+ cookie->progress_max += count;
+ }
+
+ for (annot = page->annots; annot; annot = annot->next)
+ {
+ /* Check the cookie for aborting */
+ if (cookie)
+ {
+ if (cookie->abort)
+ break;
+ cookie->progress++;
+ }
+
+ pdf_run_annot_with_usage(doc, page, annot, dev, ctm, event, cookie);
+ }
+}
+
+void
+pdf_run_page_with_usage(pdf_document *doc, pdf_page *page, fz_device *dev, const fz_matrix *ctm, char *event, fz_cookie *cookie)
+{
+ pdf_run_page_contents_with_usage(doc, page, dev, ctm, event, cookie);
+ pdf_run_page_annots_with_usage(doc, page, dev, ctm, event, cookie);
+ if (page->incomplete)
+ fz_throw(doc->ctx, FZ_ERROR_TRYLATER, "incomplete rendering");
+}
+
+void
+pdf_run_page(pdf_document *doc, pdf_page *page, fz_device *dev, const fz_matrix *ctm, fz_cookie *cookie)
+{
+ pdf_run_page_with_usage(doc, page, dev, ctm, "View", cookie);
+}
+
+void
+pdf_run_glyph(pdf_document *doc, pdf_obj *resources, fz_buffer *contents, fz_device *dev, const fz_matrix *ctm, void *gstate, int nested_depth)
+{
+ fz_context *ctx = doc->ctx;
+ pdf_process process;
+
+ if (nested_depth > 10)
+ fz_throw(ctx, FZ_ERROR_GENERIC, "Too many nestings of Type3 glyphs");
+
+ pdf_process_run(&process, dev, ctm, "View", gstate, nested_depth+1);
+
+ pdf_process_glyph(doc, resources, contents, &process);
+}