summaryrefslogtreecommitdiff
path: root/pdf/pdf_interpret.c
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2013-05-09 19:54:49 +0100
committerRobin Watts <robin.watts@artifex.com>2013-05-15 13:16:56 +0100
commitbd30dda062582e6172d5c1db8fe1470d644c1d05 (patch)
treebb10c81b39bd623f78be790f6c681c1c2eecf58f /pdf/pdf_interpret.c
parentd80869b6422fc0d3541b8f2750e2f8deaaf38f4e (diff)
downloadmupdf-bd30dda062582e6172d5c1db8fe1470d644c1d05.tar.xz
PDF Pattern gstate fix.
The PDF Reference manual is very confusing about what gstate should be used to run patterns in. Essentially, my experiments seem to suggest that as we run through a PDF page, at the start of executing each stream, we should remember the current gstate as the 'parent' gstate. Then whenever we instantiate a pattern (via scn etc), we should set the pattern to remember that gstate. When we come to render the pattern, the pattern should be rendered using the remembered gstate, not the current one. This causes many progressions in our tests.
Diffstat (limited to 'pdf/pdf_interpret.c')
-rw-r--r--pdf/pdf_interpret.c267
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");
}