summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKen Sharp <ken.sharp@artifex.com>2018-12-20 16:09:26 +0000
committerKen Sharp <ken.sharp@artifex.com>2018-12-20 16:31:09 +0000
commit48008d0fbcdb26f1deac44f47eb6b0551e4ea17e (patch)
treeb7aa8bdb197386f1e0b8f8568a0f1e5137b0ea10
parent16b7aba969bc1d607d80ddab5c8d2430bd36b6a0 (diff)
downloadghostpdl-48008d0fbcdb26f1deac44f47eb6b0551e4ea17e.tar.gz
pdfwrite/PDF interpreter/transparency - identify page level groups
Bug #700376 "Color change after reprocessing PDF with Ghostscript" The problem here lies in identifying the difference between a /Group entry in the Page dictionary, and a Group encountered as part of an operation during the course of a Page content stream. To the rendering code, there is no difference, but pdfwrite needs to be able to identify Page level groups so that it can attach them to the Page dictionary when it writes them out. Previously we've been doing this by detecting the fact that we haven't started writing anything to the page stream. However, this is simply not good enough, in the case of this bug report, the first thing that happens is the file runs a form, which has a Group. Because we haven't written anything to the page yet, we think its a page level Group and emit it as such. The problem is that in the original, we had set a constant alpha of 0.55. When we run the form that is self-contained and uses an alpha of 1, but because its in a form, the result of rendering the form is composited with the page so far (currently nothing) with teh result that the form contents are drawn 50% opaque. When we come to handle this in pdfwrite, the fact that we think the Group for the Form is at the page level causes us to write the Form content stream in the page (and the Group for the Form is also put in the Page dictionary), and we fail to emit a constant alpha, because the form resets it to 1 before executing any marks. That means we 'optimise out' the change to 0.55 and the form is drawn 100% opaque. This commit introduces a new transparency compositor operation 'PDF14_BEGIN_TRANS_PAGE_GROUP', the rendering code has been modified to treat this as exactly equivalent to a 'PDF14_BEGIN_TRANS_GROUP' so that there are no rendering differences. The pdfwrite device, however, can use it to identify with certainty that a given Group is at the Page level and treat it specially. This results in 5 files in the test suite showing progressions, mostly subtle, but one quite marked difference. The files are: Altona_Technical_v20_x4.pdf, Bug691783.pdf, Bug691982.pdf, CATX3146.pdf, x_-_no_compression_bomb.pdf *only* when processed through the pdfwrite device.
-rw-r--r--Resource/Init/pdf_draw.ps11
-rw-r--r--Resource/Init/pdf_main.ps2
-rw-r--r--base/gdevp14.c15
-rw-r--r--base/gstrans.c4
-rw-r--r--base/gstrans.h3
-rw-r--r--devices/vector/gdevpdft.c13
-rw-r--r--psi/ztrans.c21
-rw-r--r--xps/xpsgradient.c2
-rw-r--r--xps/xpsimage.c2
-rw-r--r--xps/xpsopacity.c2
10 files changed, 54 insertions, 21 deletions
diff --git a/Resource/Init/pdf_draw.ps b/Resource/Init/pdf_draw.ps
index f0b3aed6f..cf727bc81 100644
--- a/Resource/Init/pdf_draw.ps
+++ b/Resource/Init/pdf_draw.ps
@@ -714,6 +714,17 @@ def
.begintransparencygroup
} bind executeonly def
+/.beginpagegroup { % groupdict bbox .beginformgroup -
+ exch mark exch % bbox mark groupdict
+ dup /CS knownoget { resolvecolorspace dup setgcolorspace /CS exch 3 2 roll} if
+ dup /I knownoget { /Isolated exch 3 2 roll } if
+ dup /K knownoget { /Knockout exch 3 2 roll } if
+ pop .dicttomark
+ % Stack: bbox paramdict
+ exch aload pop
+ .begintransparencypagegroup
+} bind executeonly def
+
% .paintgroupform implements the Form PaintProc in the case where the
% Form XObject dictionary includes a Group key. See .paintform below.
/.paintgroupform { % <resdict> <stream> <formdict> .paintgroupform -
diff --git a/Resource/Init/pdf_main.ps b/Resource/Init/pdf_main.ps
index 00da47a48..977c5d5ba 100644
--- a/Resource/Init/pdf_main.ps
+++ b/Resource/Init/pdf_main.ps
@@ -2655,7 +2655,7 @@ currentdict /PDF2PS_matrix_key undef
} {
1 index get_media_box pop /MediaBox exch
} ifelse
- oforce_elems normrect_elems fix_empty_rect_elems 4 array astore .beginformgroup
+ oforce_elems normrect_elems fix_empty_rect_elems 4 array astore .beginpagegroup
showpagecontents
.endtransparencygroup
} {
diff --git a/base/gdevp14.c b/base/gdevp14.c
index c4dd6cac9..6e27a07aa 100644
--- a/base/gdevp14.c
+++ b/base/gdevp14.c
@@ -3747,6 +3747,7 @@ gx_update_pdf14_compositor(gx_device * pdev, gs_gstate * pgs,
pdf14_close(pdev);
}
break;
+ case PDF14_BEGIN_TRANS_PAGE_GROUP:
case PDF14_BEGIN_TRANS_GROUP:
code = gx_begin_transparency_group(pgs, pdev, &params);
break;
@@ -3882,7 +3883,7 @@ pdf14_push_text_group(gx_device *dev, gs_gstate *pgs, gx_path *path,
if (code < 0)
return code;
}
- code = gs_begin_transparency_group(pgs, &params, &bbox);
+ code = gs_begin_transparency_group(pgs, &params, &bbox, PDF14_BEGIN_TRANS_GROUP);
if (code < 0)
return code;
gs_setopacityalpha(pgs, opacity);
@@ -5975,6 +5976,7 @@ c_pdf14trans_write(const gs_composite_t * pct, byte * data, uint * psize,
if (smask_level == 0 && trans_group_level == 0)
pdf14_needed = cdev->page_pdf14_needed;
break; /* No data */
+ case PDF14_BEGIN_TRANS_PAGE_GROUP:
case PDF14_BEGIN_TRANS_GROUP:
pdf14_needed = true; /* the compositor will be needed while reading */
trans_group_level++;
@@ -6187,6 +6189,7 @@ c_pdf14trans_read(gs_composite_t * * ppct, const byte * data,
break;
case PDF14_POP_TRANS_STATE:
break;
+ case PDF14_BEGIN_TRANS_PAGE_GROUP:
case PDF14_BEGIN_TRANS_GROUP:
/*
* We are currently not using the bbox or the colorspace so they were
@@ -6362,13 +6365,13 @@ find_opening_op(int opening_op, gs_composite_t **ppcte,
if (op != PDF14_SET_BLEND_PARAMS) {
if (opening_op == PDF14_BEGIN_TRANS_MASK)
return COMP_ENQUEUE;
- if (opening_op == PDF14_BEGIN_TRANS_GROUP) {
+ if (opening_op == PDF14_BEGIN_TRANS_GROUP || opening_op == PDF14_BEGIN_TRANS_PAGE_GROUP || opening_op == PDF14_BEGIN_TRANS_PAGE_GROUP) {
if (op != PDF14_BEGIN_TRANS_MASK && op != PDF14_END_TRANS_MASK)
return COMP_ENQUEUE;
}
if (opening_op == PDF14_PUSH_DEVICE) {
if (op != PDF14_BEGIN_TRANS_MASK && op != PDF14_END_TRANS_MASK &&
- op != PDF14_BEGIN_TRANS_GROUP && op != PDF14_END_TRANS_GROUP &&
+ op != PDF14_BEGIN_TRANS_GROUP && op != PDF14_BEGIN_TRANS_PAGE_GROUP && op != PDF14_END_TRANS_GROUP &&
op != PDF14_END_TRANS_TEXT_GROUP)
return COMP_ENQUEUE;
}
@@ -6441,6 +6444,7 @@ c_pdf14trans_is_closing(const gs_composite_t * composite_action, gs_composite_t
return COMP_DROP_QUEUE;
return state;
}
+ case PDF14_BEGIN_TRANS_PAGE_GROUP:
case PDF14_BEGIN_TRANS_GROUP:
return COMP_ENQUEUE;
case PDF14_END_TRANS_GROUP:
@@ -7235,6 +7239,7 @@ pdf14_clist_create_compositor(gx_device * dev, gx_device ** pcdev,
if (code < 0)
return code;
break;
+ case PDF14_BEGIN_TRANS_PAGE_GROUP:
case PDF14_BEGIN_TRANS_GROUP:
/*
* Keep track of any changes made in the blending parameters.
@@ -7829,7 +7834,7 @@ pdf14_clist_begin_typed_image(gx_device * dev, const gs_gstate * pgs,
tgp.text_group = 0;
/* This will handle the compositor command */
gs_begin_transparency_group((gs_gstate *) pgs_noconst, &tgp,
- &bbox_out);
+ &bbox_out, PDF14_BEGIN_TRANS_GROUP);
ptile->ttrans->image_render = penum->render;
penum->render = &pdf14_pattern_trans_render;
ptile->trans_group_popped = false;
@@ -7955,6 +7960,7 @@ c_pdf14trans_clist_write_update(const gs_composite_t * pcte, gx_device * dev,
code = clist_writer_check_empty_cropping_stack(cdev);
break;
+ case PDF14_BEGIN_TRANS_PAGE_GROUP:
case PDF14_BEGIN_TRANS_GROUP:
{ /* HACK: store mask_id into our params for subsequent
calls of c_pdf14trans_write. To do this we must
@@ -8166,6 +8172,7 @@ c_pdf14trans_get_cropping(const gs_composite_t *pcte, int *ry, int *rheight,
case PDF14_PUSH_DEVICE: return ALLBANDS; /* Applies to all bands. */
case PDF14_POP_DEVICE: return ALLBANDS; /* Applies to all bands. */
case PDF14_ABORT_DEVICE: return ALLBANDS; /* Applies to all bands */
+ case PDF14_BEGIN_TRANS_PAGE_GROUP:
case PDF14_BEGIN_TRANS_GROUP:
{ gs_int_rect rect;
diff --git a/base/gstrans.c b/base/gstrans.c
index 1a1cd480c..b407c42f0 100644
--- a/base/gstrans.c
+++ b/base/gstrans.c
@@ -210,7 +210,7 @@ gs_update_trans_marking_params(gs_gstate * pgs)
int
gs_begin_transparency_group(gs_gstate *pgs,
const gs_transparency_group_params_t *ptgp,
- const gs_rect *pbbox)
+ const gs_rect *pbbox, pdf14_compositor_operations group_type)
{
gs_pdf14trans_params_t params = { 0 };
const gs_color_space *blend_color_space;
@@ -225,7 +225,7 @@ gs_begin_transparency_group(gs_gstate *pgs,
* create_compositor. This will pass the data to the PDF 1.4
* transparency device.
*/
- params.pdf14_op = PDF14_BEGIN_TRANS_GROUP;
+ params.pdf14_op = group_type;
params.Isolated = ptgp->Isolated;
params.Knockout = ptgp->Knockout;
params.image_with_SMask = ptgp->image_with_SMask;
diff --git a/base/gstrans.h b/base/gstrans.h
index c236ae9e8..b8434bc69 100644
--- a/base/gstrans.h
+++ b/base/gstrans.h
@@ -33,6 +33,7 @@ typedef enum {
PDF14_ABORT_DEVICE,
PDF14_BEGIN_TRANS_GROUP,
PDF14_END_TRANS_GROUP,
+ PDF14_BEGIN_TRANS_PAGE_GROUP,
PDF14_BEGIN_TRANS_TEXT_GROUP,
PDF14_END_TRANS_TEXT_GROUP,
PDF14_BEGIN_TRANS_MASK,
@@ -180,7 +181,7 @@ int gs_update_trans_marking_params(gs_gstate * pgs);
int gs_begin_transparency_group(gs_gstate * pgs,
const gs_transparency_group_params_t *ptgp,
- const gs_rect *pbbox);
+ const gs_rect *pbbox, pdf14_compositor_operations group_type);
int gs_end_transparency_group(gs_gstate *pgs);
diff --git a/devices/vector/gdevpdft.c b/devices/vector/gdevpdft.c
index 92a16c325..cc858546a 100644
--- a/devices/vector/gdevpdft.c
+++ b/devices/vector/gdevpdft.c
@@ -167,7 +167,7 @@ pdf_make_form_dict(gx_device_pdf * pdev, const gs_pdf14trans_params_t * pparams,
static int
pdf_begin_transparency_group(gs_gstate * pgs, gx_device_pdf * pdev,
- const gs_pdf14trans_params_t * pparams)
+ const gs_pdf14trans_params_t * pparams, bool page_group)
{
cos_dict_t *group_dict;
bool in_page = is_in_page(pdev);
@@ -189,7 +189,7 @@ pdf_begin_transparency_group(gs_gstate * pgs, gx_device_pdf * pdev,
if (code < 0)
return code;
}
- if (!in_page)
+ if (page_group)
pdev->pages[pdev->next_page].group_id = group_dict->id;
else if (pparams->image_with_SMask) {
/* An internal group for the image implementation.
@@ -232,7 +232,7 @@ pdf_end_transparency_group(gs_gstate * pgs, gx_device_pdf * pdev)
{
int bottom = (pdev->ResourcesBeforeUsage ? 1 : 0);
- if (!is_in_page(pdev))
+ if (!is_in_page(pdev) && pdev->sbstack_depth == 0)
return 0; /* corresponds to check in pdf_begin_transparency_group */
if (pdev->image_with_SMask & (1 << pdev->FormDepth)) {
/* An internal group for the image implementation.
@@ -336,7 +336,7 @@ pdf_begin_transparency_mask(gs_gstate * pgs, gx_device_pdf * pdev,
code = pdf_open_page(pdev, PDF_IN_STREAM);
if (code < 0)
return code;
- return pdf_begin_transparency_group(pgs, pdev, pparams);
+ return pdf_begin_transparency_group(pgs, pdev, pparams, 0);
}
}
@@ -414,9 +414,10 @@ gdev_pdf_create_compositor(gx_device *dev,
return 0;
case PDF14_ABORT_DEVICE:
return 0;
+ case PDF14_BEGIN_TRANS_PAGE_GROUP:
+ return pdf_begin_transparency_group(pgs, pdev, params, 1);
case PDF14_BEGIN_TRANS_GROUP:
-
- return pdf_begin_transparency_group(pgs, pdev, params);
+ return pdf_begin_transparency_group(pgs, pdev, params, 0);
case PDF14_END_TRANS_GROUP:
return pdf_end_transparency_group(pgs, pdev);
case PDF14_BEGIN_TRANS_TEXT_GROUP:
diff --git a/psi/ztrans.c b/psi/ztrans.c
index 0550a1028..96a4a593d 100644
--- a/psi/ztrans.c
+++ b/psi/ztrans.c
@@ -205,9 +205,7 @@ mask_op(i_ctx_t *i_ctx_p,
}
-/* <paramdict> <llx> <lly> <urx> <ury> .begintransparencygroup - */
-static int
-zbegintransparencygroup(i_ctx_t *i_ctx_p)
+static int common_transparency_group(i_ctx_t *i_ctx_p, pdf14_compositor_operations group_type)
{
os_ptr op = osp;
os_ptr dop = op - 4;
@@ -247,13 +245,27 @@ zbegintransparencygroup(i_ctx_t *i_ctx_p)
params.ColorSpace = NULL;
}
}
- code = gs_begin_transparency_group(igs, &params, &bbox);
+ code = gs_begin_transparency_group(igs, &params, &bbox, group_type);
if (code < 0)
return code;
pop(5);
return code;
}
+/* <paramdict> <llx> <lly> <urx> <ury> .beginpagegroup - */
+static int
+zbegintransparencypagegroup(i_ctx_t *i_ctx_p)
+{
+ return common_transparency_group(i_ctx_p, PDF14_BEGIN_TRANS_PAGE_GROUP);
+}
+
+/* <paramdict> <llx> <lly> <urx> <ury> .begintransparencygroup - */
+static int
+zbegintransparencygroup(i_ctx_t *i_ctx_p)
+{
+ return common_transparency_group(i_ctx_p, PDF14_BEGIN_TRANS_GROUP);
+}
+
/* - .endtransparencygroup - */
static int
zendtransparencygroup(i_ctx_t *i_ctx_p)
@@ -674,6 +686,7 @@ const op_def ztrans1_op_defs[] = {
};
const op_def ztrans2_op_defs[] = {
{"5.begintransparencygroup", zbegintransparencygroup},
+ {"5.begintransparencypagegroup", zbegintransparencypagegroup},
{"0.endtransparencygroup", zendtransparencygroup},
{ "0.endtransparencytextgroup", zendtransparencytextgroup },
{ "0.begintransparencytextgroup", zbegintransparencytextgroup },
diff --git a/xps/xpsgradient.c b/xps/xpsgradient.c
index df5d26711..cd9d3a981 100644
--- a/xps/xpsgradient.c
+++ b/xps/xpsgradient.c
@@ -998,7 +998,7 @@ xps_parse_gradient_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dic
gs_end_transparency_mask(ctx->pgs, TRANSPARENCY_CHANNEL_Opacity);
gs_trans_group_params_init(&tgp);
- gs_begin_transparency_group(ctx->pgs, &tgp, &bbox);
+ gs_begin_transparency_group(ctx->pgs, &tgp, &bbox, PDF14_BEGIN_TRANS_GROUP);
code = draw(ctx, root, spread_method, color_func);
if (code)
{
diff --git a/xps/xpsimage.c b/xps/xpsimage.c
index 7ea6a545e..16399118f 100644
--- a/xps/xpsimage.c
+++ b/xps/xpsimage.c
@@ -329,7 +329,7 @@ xps_paint_image_brush(xps_context_t *ctx, char *base_uri, xps_resource_t *dict,
gs_setcolorspace(ctx->pgs, image->colorspace);
gs_setblendmode(ctx->pgs, BLEND_MODE_Normal);
gs_trans_group_params_init(&tgp);
- gs_begin_transparency_group(ctx->pgs, &tgp, &bbox);
+ gs_begin_transparency_group(ctx->pgs, &tgp, &bbox, PDF14_BEGIN_TRANS_GROUP);
code = xps_paint_image_brush_imp(ctx, image, 0);
if (code < 0)
{
diff --git a/xps/xpsopacity.c b/xps/xpsopacity.c
index 536c91806..e3a0b082c 100644
--- a/xps/xpsopacity.c
+++ b/xps/xpsopacity.c
@@ -123,7 +123,7 @@ xps_begin_opacity(xps_context_t *ctx, char *base_uri, xps_resource_t *dict,
}
gs_trans_group_params_init(&tgp);
- gs_begin_transparency_group(ctx->pgs, &tgp, &bbox);
+ gs_begin_transparency_group(ctx->pgs, &tgp, &bbox, PDF14_BEGIN_TRANS_GROUP);
return 0;
}