diff options
-rw-r--r-- | pdf/pdf_interpret.c | 267 |
1 files changed, 162 insertions, 105 deletions
diff --git a/pdf/pdf_interpret.c b/pdf/pdf_interpret.c index cebc853d..2a50116e 100644 --- a/pdf/pdf_interpret.c +++ b/pdf/pdf_interpret.c @@ -27,6 +27,7 @@ struct pdf_material_s fz_colorspace *colorspace; pdf_pattern *pattern; fz_shade *shade; + int gstate_num; float alpha; float v[FZ_MAX_COLORS]; }; @@ -97,11 +98,11 @@ struct pdf_csi_s int accumulate; /* graphics state */ - fz_matrix top_ctm; pdf_gstate *gstate; int gcap; int gtop; int gbot; + int gparent; /* cookie support */ fz_cookie *cookie; @@ -109,7 +110,7 @@ struct pdf_csi_s 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, const fz_rect *area, int what); +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) @@ -438,6 +439,8 @@ pdf_show_shade(pdf_csi *csi, fz_shade *shd) 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); @@ -486,7 +489,7 @@ pdf_show_image(pdf_csi *csi, fz_image *image) if (gstate->fill.pattern) { fz_clip_image_mask(csi->dev, image, &bbox, &image_ctm); - pdf_show_pattern(csi, gstate->fill.pattern, &bbox, PDF_FILL); + pdf_show_pattern(csi, gstate->fill.pattern, &csi->gstate[gstate->fill.gstate_num], &bbox, PDF_FILL); fz_pop_clip(csi->dev); } break; @@ -494,7 +497,7 @@ pdf_show_image(pdf_csi *csi, fz_image *image) if (gstate->fill.shade) { fz_clip_image_mask(csi->dev, image, &bbox, &image_ctm); - fz_fill_shade(csi->dev, gstate->fill.shade, &gstate->ctm, gstate->fill.alpha); + fz_fill_shade(csi->dev, gstate->fill.shade, &csi->gstate[gstate->fill.gstate_num].ctm, gstate->fill.alpha); fz_pop_clip(csi->dev); } break; @@ -574,7 +577,7 @@ pdf_show_path(pdf_csi *csi, int doclose, int dofill, int dostroke, int even_odd) if (gstate->fill.pattern) { fz_clip_path(csi->dev, path, NULL, even_odd, &gstate->ctm); - pdf_show_pattern(csi, gstate->fill.pattern, &bbox, PDF_FILL); + pdf_show_pattern(csi, gstate->fill.pattern, &csi->gstate[gstate->fill.gstate_num], &bbox, PDF_FILL); fz_pop_clip(csi->dev); } break; @@ -582,7 +585,8 @@ pdf_show_path(pdf_csi *csi, int doclose, int dofill, int dostroke, int even_odd) if (gstate->fill.shade) { fz_clip_path(csi->dev, path, NULL, even_odd, &gstate->ctm); - fz_fill_shade(csi->dev, gstate->fill.shade, &csi->top_ctm, gstate->fill.alpha); + /* 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; @@ -603,7 +607,7 @@ pdf_show_path(pdf_csi *csi, int doclose, int dofill, int dostroke, int even_odd) if (gstate->stroke.pattern) { fz_clip_stroke_path(csi->dev, path, &bbox, gstate->stroke_state, &gstate->ctm); - pdf_show_pattern(csi, gstate->stroke.pattern, &bbox, PDF_STROKE); + pdf_show_pattern(csi, gstate->stroke.pattern, &csi->gstate[gstate->stroke.gstate_num], &bbox, PDF_STROKE); fz_pop_clip(csi->dev); } break; @@ -611,7 +615,7 @@ pdf_show_path(pdf_csi *csi, int doclose, int dofill, int dostroke, int even_odd) 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->top_ctm, gstate->stroke.alpha); + fz_fill_shade(csi->dev, gstate->stroke.shade, &csi->gstate[gstate->stroke.gstate_num].ctm, gstate->stroke.alpha); fz_pop_clip(csi->dev); } break; @@ -697,7 +701,7 @@ pdf_flush_text(pdf_csi *csi) if (gstate->fill.pattern) { fz_clip_text(csi->dev, text, &gstate->ctm, 0); - pdf_show_pattern(csi, gstate->fill.pattern, &tb, PDF_FILL); + pdf_show_pattern(csi, gstate->fill.pattern, &csi->gstate[gstate->fill.gstate_num], &tb, PDF_FILL); fz_pop_clip(csi->dev); } break; @@ -705,7 +709,8 @@ pdf_flush_text(pdf_csi *csi) if (gstate->fill.shade) { fz_clip_text(csi->dev, text, &gstate->ctm, 0); - fz_fill_shade(csi->dev, gstate->fill.shade, &csi->top_ctm, gstate->fill.alpha); + /* 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; @@ -726,7 +731,7 @@ pdf_flush_text(pdf_csi *csi) if (gstate->stroke.pattern) { fz_clip_stroke_text(csi->dev, text, gstate->stroke_state, &gstate->ctm); - pdf_show_pattern(csi, gstate->stroke.pattern, &tb, PDF_STROKE); + pdf_show_pattern(csi, gstate->stroke.pattern, &csi->gstate[gstate->stroke.gstate_num], &tb, PDF_STROKE); fz_pop_clip(csi->dev); } break; @@ -734,7 +739,7 @@ pdf_flush_text(pdf_csi *csi) 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->top_ctm, gstate->stroke.alpha); + fz_fill_shade(csi->dev, gstate->stroke.shade, &csi->gstate[gstate->stroke.gstate_num].ctm, gstate->stroke.alpha); fz_pop_clip(csi->dev); } break; @@ -969,6 +974,7 @@ pdf_init_gstate(fz_context *ctx, pdf_gstate *gs, const fz_matrix *ctm) 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; /* No fz_keep_colorspace as static */ @@ -976,6 +982,7 @@ pdf_init_gstate(fz_context *ctx, pdf_gstate *gs, const fz_matrix *ctm) 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; @@ -1017,18 +1024,47 @@ pdf_drop_material(fz_context *ctx, pdf_material *mat) } static void -copy_state(fz_context *ctx, pdf_gstate *gs, pdf_gstate *old) +pdf_keep_gstate(fz_context *ctx, pdf_gstate *gs) { - gs->stroke = old->stroke; - gs->fill = old->fill; + 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; gs->font = old->font; gs->softmask = old->softmask; fz_drop_stroke_state(ctx, gs->stroke_state); gs->stroke_state = fz_keep_stroke_state(ctx, old->stroke_state); - pdf_keep_material(ctx, &gs->stroke); - pdf_keep_material(ctx, &gs->fill); if (gs->font) pdf_keep_font(ctx, gs->font); if (gs->softmask) @@ -1071,13 +1107,16 @@ pdf_new_csi(pdf_document *xref, fz_device *dev, const fz_matrix *ctm, char *even csi->gcap = 64; csi->gstate = fz_malloc_array(ctx, csi->gcap, sizeof(pdf_gstate)); - csi->top_ctm = *ctm; csi->nested_depth = nested; pdf_init_gstate(ctx, &csi->gstate[0], ctm); if (gstate) - copy_state(ctx, &csi->gstate[0], gstate); + { + pdf_copy_gstate(ctx, &csi->gstate[0], gstate); + csi->gstate[0].ctm = *ctm; + } csi->gtop = 0; csi->gbot = 0; + csi->gparent = 0; csi->cookie = cookie; } @@ -1111,7 +1150,6 @@ static void pdf_gsave(pdf_csi *csi) { fz_context *ctx = csi->dev->ctx; - pdf_gstate *gs; if (csi->gtop == csi->gcap-1) { @@ -1122,14 +1160,7 @@ pdf_gsave(pdf_csi *csi) memcpy(&csi->gstate[csi->gtop + 1], &csi->gstate[csi->gtop], sizeof(pdf_gstate)); csi->gtop++; - gs = &csi->gstate[csi->gtop]; - 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); + pdf_keep_gstate(ctx, &csi->gstate[csi->gtop]); } static void @@ -1145,14 +1176,7 @@ pdf_grestore(pdf_csi *csi) return; } - 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); - + pdf_drop_gstate(ctx, gs); csi->gtop --; gs = csi->gstate + csi->gtop; @@ -1292,6 +1316,7 @@ pdf_set_pattern(pdf_csi *csi, int what, pdf_pattern *pat, float *v) mat->pattern = pdf_keep_pattern(ctx, pat); else mat->pattern = NULL; + mat->gstate_num = csi->gparent; if (v) pdf_set_color(csi, what, v); @@ -1318,12 +1343,12 @@ pdf_unset_pattern(pdf_csi *csi, int what) */ static void -pdf_show_pattern(pdf_csi *csi, pdf_pattern *pat, const fz_rect *area, int what) +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; - fz_matrix ptm, invptm; - fz_matrix oldtopctm; + int gparent_save; + fz_matrix ptm, invptm, gparent_save_ctm; int x0, y0, x1, y1; float fx0, fy0, fx1, fy1; int oldtop; @@ -1331,6 +1356,8 @@ pdf_show_pattern(pdf_csi *csi, pdf_pattern *pat, const fz_rect *area, int what) 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) { @@ -1362,84 +1389,97 @@ pdf_show_pattern(pdf_csi *csi, pdf_pattern *pat, const fz_rect *area, int what) gstate->softmask = NULL; } - fz_concat(&ptm, &pat->matrix, &csi->top_ctm); + fz_concat(&ptm, &pat->matrix, &pat_gstate->ctm); fz_invert_matrix(&invptm, &ptm); - /* patterns are painted using the ctm in effect at the beginning - * of the content stream. 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); + /* 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; + 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; - oldtopctm = csi->top_ctm; - oldtop = csi->gtop; + 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) + /* 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) + if (0) #endif - { - fz_begin_tile(csi->dev, &local_area, &pat->bbox, pat->xstep, pat->ystep, &ptm); - gstate->ctm = ptm; - csi->top_ctm = gstate->ctm; - pdf_gsave(csi); - pdf_run_contents_object(csi, pat->resources, pat->contents); - pdf_grestore(csi); - while (oldtop < csi->gtop) + { + 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); - fz_end_tile(csi->dev); - } - else - { - int x, y; + 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); + /* 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++) + for (y = y0; y < y1; y++) { - gstate->ctm = ptm; - fz_pre_translate(&gstate->ctm, x * pat->xstep, y * pat->ystep); - csi->top_ctm = gstate->ctm; - pdf_gsave(csi); - fz_try(ctx) - { - pdf_run_contents_object(csi, pat->resources, pat->contents); - } - fz_always(ctx) + for (x = x0; x < x1; x++) { - pdf_grestore(csi); - while (oldtop < csi->gtop) + 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); - } - fz_catch(ctx) - { - csi->top_ctm = oldtopctm; - fz_throw(ctx, "cannot render pattern tile"); + while (oldtop < csi->gtop) + pdf_grestore(csi); + } + fz_catch(ctx) + { + fz_throw(ctx, "cannot render pattern tile"); + } } } } } - csi->top_ctm = oldtopctm; + fz_always(ctx) + { + csi->gstate[csi->gparent].ctm = gparent_save_ctm; + csi->gparent = gparent_save; + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } pdf_grestore(csi); } @@ -1449,11 +1489,12 @@ pdf_run_xobject(pdf_csi *csi, pdf_obj *resources, pdf_xobject *xobj, const fz_ma { fz_context *ctx = csi->dev->ctx; pdf_gstate *gstate = NULL; - fz_matrix oldtopctm; int oldtop = 0; int popmask; fz_matrix local_transform = *transform; softmask_save softmask = { NULL }; + int gparent_save; + fz_matrix gparent_save_ctm; /* Avoid infinite recursion */ if (xobj == NULL || pdf_obj_mark(xobj->me)) @@ -1462,6 +1503,9 @@ pdf_run_xobject(pdf_csi *csi, pdf_obj *resources, pdf_xobject *xobj, const fz_ma fz_var(gstate); fz_var(oldtop); + gparent_save = csi->gparent; + csi->gparent = csi->gtop; + fz_try(ctx) { pdf_gsave(csi); @@ -1474,6 +1518,10 @@ pdf_run_xobject(pdf_csi *csi, pdf_obj *resources, pdf_xobject *xobj, const fz_ma 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) { @@ -1501,9 +1549,6 @@ pdf_run_xobject(pdf_csi *csi, pdf_obj *resources, pdf_xobject *xobj, const fz_ma /* run contents */ - oldtopctm = csi->top_ctm; - csi->top_ctm = gstate->ctm; - if (xobj->resources) resources = xobj->resources; @@ -1511,10 +1556,11 @@ pdf_run_xobject(pdf_csi *csi, pdf_obj *resources, pdf_xobject *xobj, const fz_ma } fz_always(ctx) { + csi->gstate[csi->gparent].ctm = gparent_save_ctm; + csi->gparent = gparent_save; + if (gstate) { - csi->top_ctm = oldtopctm; - while (oldtop < csi->gtop) pdf_grestore(csi); @@ -2075,6 +2121,7 @@ static void pdf_run_SC_imp(pdf_csi *csi, pdf_obj *rdb, int what, pdf_material *m case PDF_MAT_SHADE: fz_throw(ctx, "cannot set color in shade objects"); } + mat->gstate_num = csi->gparent; } static void pdf_run_SC(pdf_csi *csi, pdf_obj *rdb) @@ -2921,10 +2968,15 @@ static void pdf_run_page_contents_with_usage(pdf_document *xref, pdf_page *page, csi = pdf_new_csi(xref, dev, &local_ctm, event, cookie, NULL, 0); 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); } fz_always(ctx) { + while (csi->gtop > 0) + pdf_grestore(csi); pdf_free_csi(csi); } fz_catch(ctx) @@ -2967,10 +3019,15 @@ static void pdf_run_annot_with_usage(pdf_document *xref, pdf_page *page, pdf_ann { 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_throw(ctx, "cannot parse annotation appearance stream"); } |