diff options
author | Robin Watts <robin.watts@artifex.com> | 2014-03-03 18:07:02 +0000 |
---|---|---|
committer | Robin Watts <robin.watts@artifex.com> | 2014-03-04 11:26:47 +0000 |
commit | b83d0c4c1ad97c0ff68fcbfaf4aceb5fc7e4e642 (patch) | |
tree | 615cf067be3d23d06cf34533e788ba76a4e0a9d7 /source | |
parent | 484f10977e73fbafe1bfc2eda30d4426e75df265 (diff) | |
download | mupdf-b83d0c4c1ad97c0ff68fcbfaf4aceb5fc7e4e642.tar.xz |
Add pdf_process interface.
Currently the only processing we can do of PDF pages is to run
them through an fz_device. We introduce new "pdf_process"
functionality here to enable us to do more things.
We define a pdf_processor structure with a set of function
pointers in, one per PDF operator, together with functions
for processing xobjects etc. The guts of pdf_run_page_contents
and pdf_run_annot operations are then extracted to give
pdf_process_page_contents and pdf_process_annot, and the
originals implemented in terms of these.
This commit contains just one instance of a pdf_processor, namely
the "run" processor, which contains the original code refactored.
The graphical state (and device pointer) is now part of private data
to the run operator set, rather than being in pdf_csi.
Diffstat (limited to 'source')
-rw-r--r-- | source/pdf/pdf-interpret-imp.h | 151 | ||||
-rw-r--r-- | source/pdf/pdf-interpret.c | 3008 | ||||
-rw-r--r-- | source/pdf/pdf-op-run.c | 2838 | ||||
-rw-r--r-- | source/pdf/pdf-run.c | 105 |
4 files changed, 3272 insertions, 2830 deletions
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); +} |