All Rights Reserved.
This software is provided AS-IS with no warranty, either express or
implied.
This software is distributed under license and may not be copied,
modified or distributed except as expressly authorized under the terms
of the license contained in the file LICENSE in this distribution.
Refer to licensing information at http://www.artifex.com or contact
Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
CA 94129, USA, for further information.
*/
#include "pdf_int.h"
#include "pdf_stack.h"
#include "pdf_trans.h"
#include "pdf_dict.h"
#include "pdf_colour.h"
#include "pdf_font_types.h"
#include "pdf_gstate.h"
#include "pdf_array.h"
#include "pdf_image.h"
#include "pdf_device.h"
#include "pdf_misc.h"
#include "pdf_func.h"
#include "gstparam.h"
#include "gsicc_manage.h"
#include "gscoord.h"
#include "gsstate.h"
#include "gspath.h"
#include "gsicc_cache.h"
static int
pdfi_tf_using_function(double in_val, float *out, void *proc_data)
{
float in = in_val;
gs_function_t *const pfn = proc_data;
return gs_function_evaluate(pfn, &in, out);
}
static void
pdfi_set_GrayBackground(gs_transparency_mask_params_t *params)
{
float num;
which might not strictly be true? But it provides a better
estimated value than doing nothing.
*/
switch(params->Background_components) {
case 3:
params->GrayBackground = (0.3 * params->Background[0] +
0.59 * params->Background[1] +
0.11 * params->Background[2]);
break;
case 4:
*/
num = 0.3*params->Background[0] + 0.59*params->Background[1] +
0.11*params->Background[2] + params->Background[3];
if (num > 1)
num = 1;
params->GrayBackground = 1 - num;
break;
case 1:
params->GrayBackground = params->Background[0];
break;
default:
params->GrayBackground = 0;
}
}
static int pdfi_trans_set_mask(pdf_context *ctx, pdfi_int_gstate *igs, int colorindex)
{
int code = 0, code1 = 0;
pdf_dict *SMask = igs->SMask;
gs_color_space *pcs = NULL;
gs_rect bbox;
gs_transparency_mask_params_t params;
pdf_array *BBox = NULL;
pdf_array *Matrix = NULL;
pdf_array *a = NULL;
pdf_array *BC = NULL;
pdf_stream *G_stream = NULL;
pdf_dict *G_stream_dict = NULL;
pdf_dict *Group = NULL;
pdf_obj *TR = NULL;
gs_function_t *gsfunc = NULL;
pdf_name *n = NULL;
pdf_name *S = NULL;
pdf_obj *CS = NULL;
gs_matrix save_matrix, GroupMat, group_Matrix;
gs_transparency_mask_subtype_t subtype = TRANSPARENCY_MASK_Luminosity;
bool Processed, ProcessedKnown = 0;
bool save_OverrideICC = gs_currentoverrideicc(ctx->pgs);
gs_gstate *saved_gs = NULL;
#if DEBUG_TRANSPARENCY
dbgmprintf(ctx->memory, "pdfi_trans_set_mask (.execmaskgroup) BEGIN\n");
#endif
memset(¶ms, 0, sizeof(params));
* track whether it's already been processed.
*/
code = pdfi_dict_knownget_bool(ctx, SMask, "Processed", &Processed);
if (code > 0) {
if (Processed) {
#if DEBUG_TRANSPARENCY
dbgmprintf(ctx->memory, "SMask already built, skipping\n");
#endif
code = 0;
goto exit;
}
ProcessedKnown = 1;
}
gs_setoverrideicc(ctx->pgs, true);
if (code == 0) {
code = pdfi_dict_put_bool(ctx, SMask, "Processed", false);
if (code < 0)
goto exit;
ProcessedKnown = 1;
}
code = pdfi_dict_knownget_type(ctx, SMask, "Type", PDF_NAME, (pdf_obj **)&n);
if (code == 0 || (code > 0 && pdfi_name_is(n, "Mask"))) {
code = pdfi_dict_knownget_type(ctx, SMask, "G", PDF_STREAM, (pdf_obj **)&G_stream);
if (code <= 0) {
pdfi_trans_end_smask_notify(ctx);
code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_SMASK_MISSING_G, "pdfi_trans_set_mask", "");
goto exit;
}
code = pdfi_dict_from_obj(ctx, (pdf_obj *)G_stream, &G_stream_dict);
if (code < 0)
goto exit;
code = pdfi_dict_knownget_type(ctx, SMask, "S", PDF_NAME, (pdf_obj **)&S);
if (code <= 0) {
subtype = TRANSPARENCY_MASK_Luminosity;
pdfi_set_warning(ctx, 0, NULL, W_PDF_SMASK_MISSING_S, "pdfi_trans_set_mask", "");
}
else if (pdfi_name_is(S, "Luminosity")) {
subtype = TRANSPARENCY_MASK_Luminosity;
} else if (pdfi_name_is(S, "Alpha")) {
subtype = TRANSPARENCY_MASK_Alpha;
} else {
subtype = TRANSPARENCY_MASK_Luminosity;
pdfi_set_warning(ctx, 0, NULL, W_PDF_SMASK_UNKNOWN_S, "pdfi_trans_set_mask", "");
}
code = pdfi_dict_knownget(ctx, SMask, "TR", (pdf_obj **)&TR);
if (code > 0) {
switch (pdfi_type_of(TR)) {
case PDF_DICT:
case PDF_STREAM:
code = pdfi_build_function(ctx, &gsfunc, NULL, 1, TR, NULL);
if (code < 0)
goto exit;
if (gsfunc->params.m != 1 || gsfunc->params.n != 1) {
pdfi_free_function(ctx, gsfunc);
gsfunc = NULL;
pdfi_set_warning(ctx, 0, NULL, W_PDF_SMASK_INVALID_TR, "pdfi_trans_set_mask", "");
}
break;
case PDF_NAME:
if (!pdfi_name_is((pdf_name *)TR, "Identity")) {
pdfi_set_warning(ctx, 0, NULL, W_PDF_SMASK_UNKNOWN_TR, "pdfi_trans_set_mask", "");
}
break;
default:
pdfi_set_warning(ctx, 0, NULL, W_PDF_SMASK_UNKNOWN_TR_TYPE, "pdfi_trans_set_mask", "");
}
}
code = pdfi_dict_knownget_type(ctx, SMask, "BC", PDF_ARRAY, (pdf_obj **)&BC);
if (code < 0)
goto exit;
code = pdfi_dict_knownget_type(ctx, G_stream_dict, "BBox", PDF_ARRAY, (pdf_obj **)&BBox);
if (code < 0)
goto exit;
code = pdfi_array_to_gs_rect(ctx, BBox, &bbox);
if (code < 0)
goto exit;
gs_trans_mask_params_init(¶ms, subtype);
params.replacing = true;
if (gsfunc) {
params.TransferFunction = pdfi_tf_using_function;
params.TransferFunction_data = gsfunc;
}
have gs_begin_transparency_mask work correctly. Or at least that's
what the PS code comments claim (see pdf_draw.ps/.execmaskgroup)
*/
gs_currentmatrix(ctx->pgs, &save_matrix);
gs_currentmatrix(igs->GroupGState, &GroupMat);
gs_setmatrix(ctx->pgs, &GroupMat);
code = pdfi_dict_knownget_type(ctx, G_stream_dict, "Matrix", PDF_ARRAY, (pdf_obj **)&Matrix);
if (code < 0)
goto exit;
code = pdfi_array_to_gs_matrix(ctx, Matrix, &group_Matrix);
if (code < 0)
goto exit;
pdfi_bbox_transform(ctx, &bbox, &group_Matrix);
code = pdfi_dict_knownget_type(ctx, G_stream_dict, "Group", PDF_DICT, (pdf_obj **)&Group);
if (code < 0)
goto exit;
if (code > 0) {
code = pdfi_dict_knownget(ctx, Group, "CS", &CS);
if (code < 0) {
code = pdfi_dict_knownget(ctx, Group, "ColorSpace", &CS);
if (code < 0) {
pdfi_set_error(ctx, 0, NULL, E_PDF_GROUP_NO_CS, "pdfi_trans_set_mask", (char *)"*** Defaulting to currrent colour space");
goto exit;
}
pdfi_set_warning(ctx, 0, NULL, W_PDF_GROUP_HAS_COLORSPACE, "pdfi_trans_set_mask", NULL);
}
if (code > 0) {
code = pdfi_create_colorspace(ctx, CS, (pdf_dict *)ctx->main_stream,
ctx->page.CurrentPageDict, &pcs, false);
params.ColorSpace = pcs;
if (code < 0)
goto exit;
}
else
params.ColorSpace = NULL;
} else {
*/
pdfi_set_error(ctx, 0, NULL, E_PDF_INVALID_TRANS_XOBJECT, "pdfi_trans_set_mask", (char *)"*** Error: Ignoring a transparency group XObject without /Group attribute");
goto exit;
}
if (BC) {
if (params.ColorSpace == NULL) {
if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_undefined), NULL, E_PDF_GROUP_BAD_BC_NO_CS, "pdfi_trans_set_mask", NULL)) < 0)
goto exit;
} else {
int i, components = pdfi_array_size(BC);
double num;
if (components > GS_CLIENT_COLOR_MAX_COMPONENTS) {
if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_limitcheck), NULL, E_PDF_GROUP_BAD_BC_TOO_BIG, "pdfi_trans_set_mask", NULL)) < 0)
goto exit;
} else {
if (gs_color_space_num_components(params.ColorSpace) != components) {
pdfi_set_warning(ctx, 0, NULL, W_PDF_GROUP_BAD_BC, "pdfi_trans_set_mask", NULL);
components = min(components, gs_color_space_num_components(params.ColorSpace));
}
for (i=0; i < components; i++) {
code = pdfi_array_get_number(ctx, BC, i, &num);
if (code < 0)
break;
params.Background[i] = (float)num;
}
params.Background_components = components;
* It looks super-convoluted to actually get this value. Really?
* (see zcurrentgray())
* For now, use simple definition from PLRM2 and assume it is RGB or CMYK
*/
pdfi_set_GrayBackground(¶ms);
}
}
}
* a horrid pass through the graphics state, replacing "default" colorspaces
* with "ps" ones. This is required, apparently, to give us proper rendering
* of SMasks. Bug 705993 shows a case where the current colorspace differs
* from the colorspace that will be used once the igs->GroupGState is swapped
* in. Simply proceeding to call gs_begin_transparency_mask here, and then
* setting the igs->GroupGState, leaves us with a colorspace where the
* "default" -> "ps" change has not taken place, and we get incorrect rendering.
*
* As the igs->GroupGState can only be applied AFTER the
* gs_begin_transparancy_mask call (that is, I couldn't make it work applying
* it beforehand!), this means we need to manually swap in the colorspaces
* and colors, and be careful to restore them afterwards.
*
* NOTE: Attempts to gsave/grestore around the gs_begin_transparency_mask/
* gs_end_transparency_mask calls fail, showing diffs. My suspicion is that
* this actually disables the softmask that we've just rendered!
*/
if (code >= 0) {
saved_gs = gs_gstate_copy(ctx->pgs, ctx->memory);
if (saved_gs == NULL)
code = gs_note_error(gs_error_VMerror);
if (code >= 0)
code = pdfi_gs_setcolorspace(ctx, igs->GroupGState->color[0].color_space);
if (code >= 0)
code = gs_setcolor(ctx->pgs, igs->GroupGState->color[0].ccolor);
gs_swapcolors_quick(ctx->pgs);
if (code >= 0)
code = pdfi_gs_setcolorspace(ctx, igs->GroupGState->color[1].color_space);
if (code >= 0)
code = gs_setcolor(ctx->pgs, igs->GroupGState->color[1].ccolor);
gs_swapcolors_quick(ctx->pgs);
}
if (code >= 0)
code = gs_begin_transparency_mask(ctx->pgs, ¶ms, &bbox, false);
if (code >= 0) {
code = pdfi_form_execgroup(ctx, ctx->page.CurrentPageDict, G_stream,
igs->GroupGState, NULL, NULL, &group_Matrix);
code1 = gs_end_transparency_mask(ctx->pgs, colorindex);
if (code == 0)
code = code1;
}
if (saved_gs) {
code1 = pdfi_gs_setcolorspace(ctx, saved_gs->color[0].color_space);
if (code >= 0) code = code1;
code1 = gs_setcolor(ctx->pgs, saved_gs->color[0].ccolor);
if (code >= 0) code = code1;
gs_swapcolors_quick(ctx->pgs);
code1 = pdfi_gs_setcolorspace(ctx, saved_gs->color[1].color_space);
if (code >= 0) code = code1;
code1 = gs_setcolor(ctx->pgs, saved_gs->color[1].ccolor);
if (code >= 0) code = code1;
gs_swapcolors_quick(ctx->pgs);
}
* according to PS code anyway...
*/
gs_setmatrix(ctx->pgs, &save_matrix);
if (code == 0 && ProcessedKnown)
{
code = pdfi_dict_put_bool(ctx, SMask, "Processed", true);
if (code < 0)
goto exit;
}
} else {
pdfi_set_warning(ctx, 0, NULL, W_PDF_SMASK_UNKNOWN_TYPE, "pdfi_trans_set_mask", "");
}
exit:
gs_gstate_free(saved_gs);
gs_setoverrideicc(ctx->pgs, save_OverrideICC);
if (gsfunc)
pdfi_free_function(ctx, gsfunc);
if (pcs)
rc_decrement_cs(pcs, "pdfi_trans_set_mask");
pdfi_countdown(n);
pdfi_countdown(S);
pdfi_countdown(Group);
pdfi_countdown(G_stream);
pdfi_countdown(a);
pdfi_countdown(BC);
pdfi_countdown(TR);
pdfi_countdown(BBox);
pdfi_countdown(Matrix);
pdfi_countdown(CS);
#if DEBUG_TRANSPARENCY
dbgmprintf(ctx->memory, "pdfi_trans_set_mask (.execmaskgroup) END\n");
#endif
return code;
}
static int pdfi_gs_begin_transparency_group(gs_gstate * pgs,
gs_transparency_group_params_t *params,
const gs_rect *pbbox, pdf14_compositor_operations group_type)
{
if (gs_getalphaisshape(pgs)) {
params->group_shape = gs_getfillconstantalpha(pgs);
params->group_opacity = 1.0;
} else {
params->group_opacity = gs_getfillconstantalpha(pgs);
params->group_shape = 1.0;
}
return gs_begin_transparency_group(pgs, params, pbbox, group_type);
}
static int pdfi_transparency_group_common(pdf_context *ctx, pdf_dict *page_dict,
pdf_dict *group_dict,
gs_rect *bbox, pdf14_compositor_operations group_type)
{
gs_transparency_group_params_t params;
pdf_obj *CS = NULL;
bool b;
int code;
gs_trans_group_params_init(¶ms, 1.0);
code = pdfi_dict_get_bool(ctx, group_dict, "I", &b);
if (code < 0 && code != gs_error_undefined)
return_error(code);
if (code == gs_error_undefined)
params.Isolated = false;
else
params.Isolated = b;
code = pdfi_dict_get_bool(ctx, group_dict, "K", &b);
if (code < 0 && code != gs_error_undefined)
goto exit;
if (code == gs_error_undefined)
params.Knockout = false;
else
params.Knockout = b;
params.image_with_SMask = false;
params.ColorSpace = NULL;
code = pdfi_dict_knownget(ctx, group_dict, "CS", &CS);
if (code == 0) {
code = pdfi_dict_knownget(ctx, group_dict, "ColorSpace", &CS);
}
if (code > 0 && pdfi_type_of(CS) != PDF_NULL) {
code = pdfi_setcolorspace(ctx, CS, group_dict, page_dict);
if (code < 0)
goto exit;
params.ColorSpace = gs_currentcolorspace(ctx->pgs);
} else {
params.ColorSpace = NULL;
}
exit:
pdfi_countdown(CS);
if (code < 0)
return_error(code);
return pdfi_gs_begin_transparency_group(ctx->pgs, ¶ms, (const gs_rect *)bbox, group_type);
}
static bool pdfi_outputprofile_matches_oiprofile(pdf_context *ctx)
{
cmm_dev_profile_t *profile_struct;
int code;
int k;
code = dev_proc(ctx->pgs->device, get_profile)(ctx->pgs->device, &profile_struct);
if (code < 0)
return true;
if (profile_struct->oi_profile == NULL)
return true;
else {
we assume there is not a match and it may be necessary to
use the pdf14 device to prerender to the OI profile */
for (k = 0; k < NUM_DEVICE_PROFILES; k++) {
if (profile_struct->device_profile[k] != NULL) {
if (!gsicc_profiles_equal(profile_struct->oi_profile, profile_struct->device_profile[k]))
return false;
}
}
return true;
}
}
* pathbbox -- bbox to use, but can be NULL
*/
int pdfi_trans_begin_simple_group(pdf_context *ctx, gs_rect *pathbbox,
bool stroked_bbox, bool isolated, bool knockout)
{
gs_transparency_group_params_t params;
gs_rect bbox;
int code;
gs_trans_group_params_init(¶ms, 1.0);
params.Isolated = isolated;
params.Knockout = knockout;
if (!pathbbox) {
code = pdfi_get_current_bbox(ctx, &bbox, stroked_bbox);
if (code < 0)
return code;
pathbbox = &bbox;
}
code = pdfi_gs_begin_transparency_group(ctx->pgs, ¶ms, pathbbox, PDF14_BEGIN_TRANS_GROUP);
if (code >= 0)
ctx->current_stream_save.group_depth++;
return code;
}
int pdfi_trans_begin_page_group(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *group_dict)
{
gs_rect bbox;
int code;
if (group_dict == NULL)
return_error(gs_error_undefined);
code = pdfi_gsave(ctx);
bbox.p.x = ctx->page.Size[0];
bbox.p.y = ctx->page.Size[1];
bbox.q.x = ctx->page.Size[2];
bbox.q.y = ctx->page.Size[3];
code = pdfi_transparency_group_common(ctx, page_dict, group_dict, &bbox, PDF14_BEGIN_TRANS_PAGE_GROUP);
if (code < 0)
pdfi_grestore(ctx);
else
ctx->current_stream_save.group_depth++;
return code;
}
int pdfi_trans_begin_form_group(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *form_dict)
{
pdf_obj *group_obj = NULL;
gs_rect bbox;
pdf_array *BBox = NULL;
int code;
pdf_dict *group_dict = NULL;
* Otherwise should just fetch it as a dict.
* Anyway this will work for either dict or stream
*/
code = pdfi_dict_get(ctx, form_dict, "Group", &group_obj);
if (code < 0)
return_error(code);
code = pdfi_dict_from_obj(ctx, (pdf_obj *)group_obj, &group_dict);
if (code < 0)
goto exit;
code = pdfi_gsave(ctx);
code = pdfi_dict_knownget_type(ctx, form_dict, "BBox", PDF_ARRAY, (pdf_obj **)&BBox);
if (code < 0)
goto exit;
if (code > 0) {
code = pdfi_array_to_gs_rect(ctx, BBox, &bbox);
if (code < 0)
goto exit;
} else {
bbox.p.x = 0;
bbox.p.y = 0;
bbox.q.x = 0;
bbox.q.y = 0;
}
code = pdfi_transparency_group_common(ctx, page_dict, group_dict, &bbox, PDF14_BEGIN_TRANS_GROUP);
if (code < 0)
pdfi_grestore(ctx);
else
ctx->current_stream_save.group_depth++;
exit:
pdfi_countdown(BBox);
pdfi_countdown(group_obj);
return code;
}
int pdfi_trans_end_group(pdf_context *ctx)
{
int code;
code = gs_end_transparency_group(ctx->pgs);
if (code < 0)
pdfi_grestore(ctx);
else
code = pdfi_grestore(ctx);
ctx->current_stream_save.group_depth--;
return code;
}
int pdfi_trans_end_simple_group(pdf_context *ctx)
{
int code;
code = gs_end_transparency_group(ctx->pgs);
ctx->current_stream_save.group_depth--;
return code;
}
int pdfi_trans_begin_isolated_group(pdf_context *ctx, bool image_with_SMask, gs_color_space *pcs)
{
gs_transparency_group_params_t params;
gs_rect bbox;
gs_trans_group_params_init(¶ms, 1.0);
params.ColorSpace = pcs;
params.Isolated = true;
params.Knockout = false;
params.image_with_SMask = image_with_SMask;
bbox.p.x = 0;
bbox.p.y = 0;
bbox.q.x = 1;
bbox.q.y = 1;
return pdfi_gs_begin_transparency_group(ctx->pgs, ¶ms, &bbox, PDF14_BEGIN_TRANS_GROUP);
}
int pdfi_trans_end_isolated_group(pdf_context *ctx)
{
return gs_end_transparency_group(ctx->pgs);
}
* See pdf_draw.ps/doimagesmask.
*/
int pdfi_trans_end_smask_notify(pdf_context *ctx)
{
gs_transparency_mask_params_t params;
gs_rect bbox;
gs_trans_mask_params_init(¶ms, TRANSPARENCY_MASK_None);
params.replacing = true;
bbox.p.x = 0;
bbox.p.y = 0;
bbox.q.x = 0;
bbox.q.y = 0;
return gs_begin_transparency_mask(ctx->pgs, ¶ms, &bbox, false);
}
* Check for:
* 1) how many components the device has (CMYK or other)
* 2) whether it is a device that has transparency support
* 3) Overprint mode (enable, disable, simulate)
* Based on pdf_main.ps/pdfshowpage_finish
* Test file (run with -dOverprint=/simulate)
* tests_private/pdf/uploads/op_trans_spot_testing.pdf
*/
void pdfi_trans_set_needs_OP(pdf_context *ctx)
{
bool is_cmyk;
bool device_transparency = false;
is_cmyk = ctx->pgs->device->color_info.num_components >= 4;
device_transparency = pdfi_device_check_param_bool(ctx->pgs->device, "HaveTransparency");
ctx->page.needs_OP = false;
ctx->page.simulate_op = false;
switch(ctx->pgs->device->icc_struct->overprint_control) {
case gs_overprint_control_disable:
break;
case gs_overprint_control_simulate:
if (!device_transparency && ctx->page.has_OP) {
if (is_cmyk) {
if the output intent profile is to be used, but we have
a device output profile that is different, then we will be
doing simulation with the pdf14 device buffer */
if ((ctx->page.num_spots > 0 && !ctx->device_state.spot_capable) ||
!pdfi_outputprofile_matches_oiprofile(ctx)) {
ctx->page.needs_OP = true;
ctx->page.simulate_op = true;
}
} else {
ctx->page.needs_OP = true;
ctx->page.simulate_op = true;
}
}
break;
case gs_overprint_control_enable:
default:
if (!is_cmyk || device_transparency)
ctx->page.needs_OP = false;
else
ctx->page.needs_OP = true;
break;
}
if(ctx->args.pdfdebug)
dbgmprintf2(ctx->memory, "Page %s Overprint, Simulate is %s\n",
ctx->page.needs_OP ? "NEEDS" : "does NOT NEED",
ctx->page.simulate_op ? "TRUE" : "FALSE");
}
bool pdfi_trans_okOPcs(pdf_context *ctx)
{
gs_color_space_index csi;
csi = pdfi_currentcolorspace(ctx, 0);
switch (csi) {
case gs_color_space_index_DeviceGray:
case gs_color_space_index_DeviceCMYK:
case gs_color_space_index_DeviceN:
case gs_color_space_index_Separation:
* (pdf1.7 pg 259,578 may apply)
* According to mvrhel, DeviceGray should also be included (see comment in gx_set_overprint_ICC()).
* Sample: 030_Gray_K_black_OP_x1a.pdf (??)
*/
#if DEBUG_TRANSPARENCY
dbgmprintf1(ctx->memory, "Colorspace is %d, OKAY for OVERPRINT\n", csi);
#endif
return true;
default:
#if DEBUG_TRANSPARENCY
dbgmprintf1(ctx->memory, "Colorspace is %d, NOT OKAY for OVERPRINT\n", csi);
#endif
return false;
}
return false;
}
int pdfi_trans_setup(pdf_context *ctx, pdfi_trans_state_t *state, gs_rect *bbox,
pdfi_transparency_caller_t caller)
{
pdfi_int_gstate *igs = (pdfi_int_gstate *)ctx->pgs->client_data;
int code;
bool stroked_bbox;
bool current_overprint;
bool okOPcs = false;
bool ChangeBM = false;
gs_blend_mode_t mode = gs_currentblendmode(ctx->pgs);
bool need_group = false;
memset(state, 0, sizeof(*state));
if (!ctx->page.has_transparency)
return 0;
if (ctx->page.needs_OP) {
okOPcs = pdfi_trans_okOPcs(ctx);
if (okOPcs) {
if (caller == TRANSPARENCY_Caller_Stroke)
current_overprint = gs_currentstrokeoverprint(ctx->pgs);
else {
current_overprint = gs_currentfilloverprint(ctx->pgs);
if (caller == TRANSPARENCY_Caller_FillStroke)
current_overprint |= gs_currentstrokeoverprint(ctx->pgs);
}
ChangeBM = current_overprint;
mode = gs_currentblendmode(ctx->pgs);
if (mode != BLEND_MODE_Normal && mode != BLEND_MODE_Compatible)
need_group = ChangeBM;
else
need_group = false;
} else {
need_group = false;
}
need_group = need_group || (igs->SMask != NULL);
} else {
if (caller == TRANSPARENCY_Caller_Image || igs->SMask == NULL)
need_group = false;
else
need_group = true;
}
code = pdfi_trans_set_params(ctx);
if (code != 0)
return 0;
if (!need_group && !ChangeBM)
return 0;
if (need_group) {
bool isolated = false;
mode = gs_currentblendmode(ctx->pgs);
stroked_bbox = (caller == TRANSPARENCY_Caller_Stroke || caller == TRANSPARENCY_Caller_FillStroke);
is if we have a softmask AND the blend mode is not normal and not compatible.
See /setupOPtrans in pdf_ops.ps */
if (igs->SMask != NULL && mode != BLEND_MODE_Normal && mode != BLEND_MODE_Compatible)
isolated = true;
code = pdfi_trans_begin_simple_group(ctx, bbox, stroked_bbox, isolated, false);
if (code >= 0)
state->GroupPushed = true;
state->saveStrokeAlpha = gs_getstrokeconstantalpha(ctx->pgs);
state->saveFillAlpha = gs_getfillconstantalpha(ctx->pgs);
code = gs_setfillconstantalpha(ctx->pgs, 1.0);
code = gs_setstrokeconstantalpha(ctx->pgs, 1.0);
}
We can have situations where the fill and the stroke have different
blending needs (one may need compatible overprint and the other may
need the normal mode) This is all handled in the fill stroke logic */
if (ChangeBM && caller != TRANSPARENCY_Caller_FillStroke) {
state->saveBM = mode;
state->ChangeBM = true;
code = gs_setblendmode(ctx->pgs, BLEND_MODE_CompatibleOverprint);
}
return code;
}
int pdfi_trans_required(pdf_context *ctx)
{
gs_blend_mode_t mode;
if (!ctx->page.has_transparency)
return 0;
mode = gs_currentblendmode(ctx->pgs);
if ((mode == BLEND_MODE_Normal || mode == BLEND_MODE_Compatible) &&
ctx->pgs->fillconstantalpha == 1 &&
ctx->pgs->strokeconstantalpha == 1 &&
((pdfi_int_gstate *)ctx->pgs->client_data)->SMask == NULL)
return 0;
return 1;
}
int pdfi_trans_setup_text(pdf_context *ctx, pdfi_trans_state_t *state, bool is_show)
{
int Trmode;
int code, code1;
gs_rect bbox;
if (!pdfi_trans_required(ctx))
return 0;
Trmode = gs_currenttextrenderingmode(ctx->pgs);
code = gs_gsave(ctx->pgs);
if (code < 0) goto exit;
if (is_show) {
code = gs_clippath(ctx->pgs);
} else {
code = gs_strokepath(ctx->pgs);
}
if (code >= 0)
code = gs_upathbbox(ctx->pgs, &bbox, false);
if (code < 0) {
bbox.p.x = bbox.p.y = bbox.q.x = bbox.q.y = 0.0;
code = 0;
}
code1 = gs_grestore(ctx->pgs);
if (code == 0) code = code1;
if (code < 0) goto exit;
switch (Trmode) {
case 0:
code = pdfi_trans_setup(ctx, state, &bbox, TRANSPARENCY_Caller_Fill);
break;
default:
code = pdfi_trans_setup(ctx, state, &bbox, TRANSPARENCY_Caller_Fill);
break;
}
exit:
return code;
}
int pdfi_trans_teardown_text(pdf_context *ctx, pdfi_trans_state_t *state)
{
if (!pdfi_trans_required(ctx))
return 0;
return pdfi_trans_teardown(ctx, state);
}
int pdfi_trans_teardown(pdf_context *ctx, pdfi_trans_state_t *state)
{
int code = 0;
if (!ctx->page.has_transparency)
return 0;
if (state->GroupPushed) {
code = pdfi_trans_end_simple_group(ctx);
code = gs_setstrokeconstantalpha(ctx->pgs, state->saveStrokeAlpha);
code = gs_setfillconstantalpha(ctx->pgs, state->saveFillAlpha);
}
if (gs_currentblendmode(ctx->pgs) == BLEND_MODE_CompatibleOverprint)
code = gs_setblendmode(ctx->pgs, state->saveBM);
return code;
}
int pdfi_trans_set_params(pdf_context *ctx)
{
int code = 0;
pdfi_int_gstate *igs = (pdfi_int_gstate *)ctx->pgs->client_data;
gs_transparency_channel_selector_t csel;
if (ctx->page.has_transparency) {
if (gs_getalphaisshape(ctx->pgs))
csel = TRANSPARENCY_CHANNEL_Shape;
else
csel = TRANSPARENCY_CHANNEL_Opacity;
if (igs->SMask) {
code = pdfi_trans_set_mask(ctx, igs, csel);
}
}
return code;
}