Coverage Report

Created: 2025-06-10 07:27

/src/ghostpdl/pdf/pdf_trans.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2019-2025 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
/* Transparency support */
17
18
#include "pdf_int.h"
19
#include "pdf_stack.h"
20
#include "pdf_trans.h"
21
#include "pdf_dict.h"
22
#include "pdf_colour.h"
23
#include "pdf_font_types.h"
24
#include "pdf_gstate.h"
25
#include "pdf_array.h"
26
#include "pdf_image.h"
27
#include "pdf_device.h"
28
#include "pdf_misc.h"
29
#include "pdf_func.h"
30
31
#include "gstparam.h"
32
#include "gsicc_manage.h"       /* For gs_currentoverrideicc() */
33
#include "gscoord.h"            /* For gs_setmatrix()*/
34
#include "gsstate.h"            /* For gs_currentstrokeoverprint() and others */
35
#include "gspath.h"             /* For gs_clippath() */
36
#include "gsicc_cache.h"        /* For gsicc_profiles_equal() */
37
38
/* Implement the TransferFunction using a Function. */
39
static int
40
pdfi_tf_using_function(double in_val, float *out, void *proc_data)
41
91.6k
{
42
91.6k
    float in = in_val;
43
91.6k
    gs_function_t *const pfn = proc_data;
44
45
91.6k
    return gs_function_evaluate(pfn, &in, out);
46
91.6k
}
47
48
static void
49
pdfi_set_GrayBackground(gs_transparency_mask_params_t *params)
50
1.58k
{
51
1.58k
    float num;
52
53
    /* This uses definition from PLRM2 6.2.1 and 6.2.2 */
54
    /* TODO: We are assuming 3 components is RGB and 4 components is CMYK,
55
       which might not strictly be true?  But it provides a better
56
       estimated value than doing nothing.
57
    */
58
1.58k
    switch(params->Background_components) {
59
24
    case 3:
60
        /* RGB: currentgray = 0.3*R + 0.59*G + 0.11*B */
61
24
        params->GrayBackground = (0.3 * params->Background[0] +
62
24
                                  0.59 * params->Background[1] +
63
24
                                  0.11 * params->Background[2]);
64
24
        break;
65
770
    case 4:
66
        /* CMYK: currentgray = 1.0 – min (1.0, .3*C + .59*M + .11*Y + K)
67
        */
68
770
        num = 0.3*params->Background[0] + 0.59*params->Background[1] +
69
770
            0.11*params->Background[2] + params->Background[3];
70
770
        if (num > 1)
71
722
            num = 1;
72
770
        params->GrayBackground = 1 - num;
73
770
        break;
74
788
    case 1:
75
788
        params->GrayBackground = params->Background[0];
76
788
        break;
77
0
    default:
78
        /* No clue... */
79
0
        params->GrayBackground = 0;
80
1.58k
    }
81
1.58k
}
82
83
/* (see pdf_draw.ps/execmaskgroup) */
84
static int pdfi_trans_set_mask(pdf_context *ctx, pdfi_int_gstate *igs, int colorindex)
85
10.2k
{
86
10.2k
    int code = 0, code1 = 0;
87
10.2k
    pdf_dict *SMask = igs->SMask;
88
10.2k
    gs_color_space *pcs = NULL;
89
10.2k
    gs_rect bbox;
90
10.2k
    gs_transparency_mask_params_t params;
91
10.2k
    pdf_array *BBox = NULL;
92
10.2k
    pdf_array *Matrix = NULL;
93
10.2k
    pdf_array *a = NULL;
94
10.2k
    pdf_array *BC = NULL;
95
10.2k
    pdf_stream *G_stream = NULL;
96
10.2k
    pdf_dict *G_stream_dict = NULL;
97
10.2k
    pdf_dict *Group = NULL;
98
10.2k
    pdf_obj *TR = NULL;
99
10.2k
    gs_function_t *gsfunc = NULL;
100
10.2k
    pdf_name *n = NULL;
101
10.2k
    pdf_name *S = NULL;
102
10.2k
    pdf_obj *CS = NULL;
103
10.2k
    gs_matrix save_matrix, GroupMat, group_Matrix;
104
10.2k
    gs_transparency_mask_subtype_t subtype = TRANSPARENCY_MASK_Luminosity;
105
10.2k
    bool Processed, ProcessedKnown = 0;
106
10.2k
    bool save_OverrideICC = gs_currentoverrideicc(ctx->pgs);
107
10.2k
    gs_gstate *saved_gs = NULL;
108
10.2k
    gs_rect clip_bbox;
109
10.2k
    int empty = 0;
110
111
#if DEBUG_TRANSPARENCY
112
    dbgmprintf(ctx->memory, "pdfi_trans_set_mask (.execmaskgroup) BEGIN\n");
113
#endif
114
10.2k
    memset(&params, 0, sizeof(params));
115
116
    /* Following the logic of the ps code, cram a /Processed key in the SMask dict to
117
     * track whether it's already been processed.
118
     */
119
10.2k
    code = pdfi_dict_knownget_bool(ctx, SMask, "Processed", &Processed);
120
10.2k
    if (code > 0) {
121
5.81k
        if (Processed) {
122
#if DEBUG_TRANSPARENCY
123
          dbgmprintf(ctx->memory, "SMask already built, skipping\n");
124
#endif
125
1.62k
          code = 0;
126
1.62k
          goto exit;
127
1.62k
        }
128
4.19k
        ProcessedKnown = 1;
129
4.19k
    }
130
131
8.67k
    gs_setoverrideicc(ctx->pgs, true);
132
133
    /* If /Processed not in the dict, put it there */
134
8.67k
    if (code == 0) {
135
4.48k
        code = pdfi_dict_put_bool(ctx, SMask, "Processed", false);
136
4.48k
        if (code < 0)
137
0
            goto exit;
138
4.48k
        ProcessedKnown = 1;
139
4.48k
    }
140
141
    /* See pdf1.7 pg 553 (pain in the butt to find this!) */
142
8.67k
    code = pdfi_dict_knownget_type(ctx, SMask, "Type", PDF_NAME, (pdf_obj **)&n);
143
8.67k
    if (code == 0 || (code > 0 && pdfi_name_is(n, "Mask"))) {
144
        /* G is transparency group XObject (required) */
145
8.57k
        code = pdfi_dict_knownget_type(ctx, SMask, "G", PDF_STREAM, (pdf_obj **)&G_stream);
146
8.57k
        if (code <= 0) {
147
3.71k
            pdfi_trans_end_smask_notify(ctx);
148
3.71k
            code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_SMASK_MISSING_G, "pdfi_trans_set_mask", "");
149
3.71k
            goto exit;
150
3.71k
        }
151
152
4.85k
        code = pdfi_dict_from_obj(ctx, (pdf_obj *)G_stream, &G_stream_dict);
153
4.85k
        if (code < 0)
154
0
            goto exit;
155
156
        /* S is a subtype name (required) */
157
4.85k
        code = pdfi_dict_knownget_type(ctx, SMask, "S", PDF_NAME, (pdf_obj **)&S);
158
4.85k
        if (code <= 0) {
159
217
            subtype = TRANSPARENCY_MASK_Luminosity;
160
217
            pdfi_set_warning(ctx, 0, NULL, W_PDF_SMASK_MISSING_S, "pdfi_trans_set_mask", "");
161
217
        }
162
4.64k
        else if (pdfi_name_is(S, "Luminosity")) {
163
4.18k
            subtype = TRANSPARENCY_MASK_Luminosity;
164
4.18k
        } else if (pdfi_name_is(S, "Alpha")) {
165
450
            subtype = TRANSPARENCY_MASK_Alpha;
166
450
        } else {
167
10
            subtype = TRANSPARENCY_MASK_Luminosity;
168
10
            pdfi_set_warning(ctx, 0, NULL, W_PDF_SMASK_UNKNOWN_S, "pdfi_trans_set_mask", "");
169
10
        }
170
171
        /* TR is transfer function (Optional) */
172
4.85k
        code = pdfi_dict_knownget(ctx, SMask, "TR", (pdf_obj **)&TR);
173
4.85k
        if (code > 0) {
174
407
            switch (pdfi_type_of(TR)) {
175
6
                case PDF_DICT:
176
407
                case PDF_STREAM:
177
407
                    code = pdfi_build_function(ctx, &gsfunc, NULL, 1, TR, NULL);
178
407
                    if (code < 0)
179
49
                        goto exit;
180
358
                    if (gsfunc->params.m != 1 || gsfunc->params.n != 1) {
181
0
                        pdfi_free_function(ctx, gsfunc);
182
0
                        gsfunc = NULL;
183
0
                        pdfi_set_warning(ctx, 0, NULL, W_PDF_SMASK_INVALID_TR, "pdfi_trans_set_mask", "");
184
0
                    }
185
358
                    break;
186
0
                case PDF_NAME:
187
0
                    if (!pdfi_name_is((pdf_name *)TR, "Identity")) {
188
0
                        pdfi_set_warning(ctx, 0, NULL, W_PDF_SMASK_UNKNOWN_TR, "pdfi_trans_set_mask", "");
189
0
                    }
190
0
                    break;
191
0
                default:
192
0
                    pdfi_set_warning(ctx, 0, NULL, W_PDF_SMASK_UNKNOWN_TR_TYPE, "pdfi_trans_set_mask", "");
193
407
            }
194
407
        }
195
196
        /* BC is Background Color array (Optional) */
197
4.80k
        code = pdfi_dict_knownget_type(ctx, SMask, "BC", PDF_ARRAY, (pdf_obj **)&BC);
198
4.80k
        if (code < 0)
199
78
            goto exit;
200
201
4.73k
        code = pdfi_dict_knownget_type(ctx, G_stream_dict, "BBox", PDF_ARRAY, (pdf_obj **)&BBox);
202
4.73k
        if (code < 0)
203
8
            goto exit;
204
4.72k
        code = pdfi_array_to_gs_rect(ctx, BBox, &bbox);
205
4.72k
        if (code < 0)
206
13
            goto exit;
207
208
4.70k
        gs_trans_mask_params_init(&params, subtype);
209
4.70k
        params.replacing = true;
210
4.70k
        if (gsfunc) {
211
358
            params.TransferFunction = pdfi_tf_using_function;
212
358
            params.TransferFunction_data = gsfunc;
213
358
        }
214
215
        /* Need to set just the ctm (GroupMat) from the saved GroupGState, to
216
           have gs_begin_transparency_mask work correctly.  Or at least that's
217
           what the PS code comments claim (see pdf_draw.ps/.execmaskgroup)
218
        */
219
4.70k
        gs_currentmatrix(ctx->pgs, &save_matrix);
220
4.70k
        gs_currentmatrix(igs->GroupGState, &GroupMat);
221
4.70k
        gs_setmatrix(ctx->pgs, &GroupMat);
222
223
4.70k
        code = pdfi_dict_knownget_type(ctx, G_stream_dict, "Matrix", PDF_ARRAY, (pdf_obj **)&Matrix);
224
4.70k
        if (code < 0)
225
0
            goto exit;
226
4.70k
        code = pdfi_array_to_gs_matrix(ctx, Matrix, &group_Matrix);
227
4.70k
        if (code < 0)
228
5
            goto exit;
229
230
        /* Transform the BBox by the Matrix */
231
4.70k
        pdfi_bbox_transform(ctx, &bbox, &group_Matrix);
232
233
4.70k
        if (gs_clip_bounds_in_user_space(ctx->pgs, &clip_bbox) >= 0)
234
4.65k
        {
235
4.65k
            rect_intersect(bbox, clip_bbox);
236
4.65k
            if (bbox.p.x >= bbox.q.x || bbox.p.y >= bbox.q.y)
237
1.15k
            {
238
                /* If the bbox is illegal, we still need to set up an empty mask.
239
                 * We can't just skip forwards as everything will now be unmasked!
240
                 * We can skip doing the actual work though. */
241
1.15k
                empty = 1;
242
1.15k
            }
243
4.65k
        }
244
245
        /* CS is in the dict "Group" inside the dict "G" */
246
        /* TODO: Not sure if this is a required thing or just one possibility */
247
4.70k
        code = pdfi_dict_knownget_type(ctx, G_stream_dict, "Group", PDF_DICT, (pdf_obj **)&Group);
248
4.70k
        if (code < 0)
249
1.86k
            goto exit;
250
2.83k
        if (code > 0) {
251
            /* TODO: Stuff with colorspace, see .execmaskgroup */
252
2.78k
            code = pdfi_dict_knownget(ctx, Group, "CS", &CS);
253
2.78k
            if (code < 0) {
254
25
                code = pdfi_dict_knownget(ctx, Group, "ColorSpace", &CS);
255
25
                if (code < 0) {
256
0
                    pdfi_set_error(ctx, 0, NULL, E_PDF_GROUP_NO_CS, "pdfi_trans_set_mask", (char *)"*** Defaulting to currrent colour space");
257
0
                    goto exit;
258
0
                }
259
25
                pdfi_set_warning(ctx, 0, NULL, W_PDF_GROUP_HAS_COLORSPACE, "pdfi_trans_set_mask", NULL);
260
25
            }
261
2.78k
            if (code > 0) {
262
2.73k
                code = pdfi_create_colorspace(ctx, CS, (pdf_dict *)ctx->main_stream,
263
2.73k
                                              ctx->page.CurrentPageDict, &pcs, false);
264
2.73k
                params.ColorSpace = pcs;
265
2.73k
                if (code < 0)
266
105
                    goto exit;
267
2.73k
            }
268
52
            else
269
52
                   params.ColorSpace = NULL;
270
2.78k
        } else {
271
            /* GS and Adobe will ignore the whole mask in this case, so we do the same.
272
            */
273
52
            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");
274
52
            goto exit;
275
52
        }
276
277
        /* If there's a BC, put it in the params */
278
2.68k
        if (BC) {
279
1.60k
            if (params.ColorSpace == NULL) {
280
25
                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)
281
0
                    goto exit;
282
1.58k
            } else {
283
1.58k
                int i, components = pdfi_array_size(BC);
284
1.58k
                double num;
285
286
1.58k
                if (components > GS_CLIENT_COLOR_MAX_COMPONENTS) {
287
0
                    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)
288
0
                        goto exit;
289
1.58k
                } else {
290
1.58k
                    if (gs_color_space_num_components(params.ColorSpace) != components) {
291
0
                        pdfi_set_warning(ctx, 0, NULL, W_PDF_GROUP_BAD_BC, "pdfi_trans_set_mask", NULL);
292
0
                        components = min(components, gs_color_space_num_components(params.ColorSpace));
293
0
                    }
294
295
5.52k
                    for (i=0; i < components; i++) {
296
3.94k
                        code = pdfi_array_get_number(ctx, BC, i, &num);
297
3.94k
                        if (code < 0)
298
0
                            break;
299
3.94k
                        params.Background[i] = (float)num;
300
3.94k
                    }
301
1.58k
                    params.Background_components = components;
302
303
                    /* TODO: Not sure how to handle this...  recheck PS code (pdf_draw.ps/gssmask) */
304
                    /* This should be "currentgray" for the color that we put in params.ColorSpace,
305
                     * It looks super-convoluted to actually get this value.  Really?
306
                     * (see zcurrentgray())
307
                     * For now, use simple definition from PLRM2 and assume it is RGB or CMYK
308
                     */
309
1.58k
                    pdfi_set_GrayBackground(&params);
310
1.58k
                }
311
1.58k
            }
312
1.60k
        }
313
314
        /* When we call gs_begin_transparency_mask, the pdf14 compisitor will perform
315
         * a horrid pass through the graphics state, replacing "default" colorspaces
316
         * with "ps" ones. This is required, apparently, to give us proper rendering
317
         * of SMasks. Bug 705993 shows a case where the current colorspace differs
318
         * from the colorspace that will be used once the igs->GroupGState is swapped
319
         * in. Simply proceeding to call gs_begin_transparency_mask here, and then
320
         * setting the igs->GroupGState, leaves us with a colorspace where the
321
         * "default" -> "ps" change has not taken place, and we get incorrect rendering.
322
         *
323
         * As the igs->GroupGState can only be applied AFTER the
324
         * gs_begin_transparancy_mask call (that is, I couldn't make it work applying
325
         * it beforehand!), this means we need to manually swap in the colorspaces
326
         * and colors, and be careful to restore them afterwards.
327
         *
328
         * NOTE: Attempts to gsave/grestore around the gs_begin_transparency_mask/
329
         * gs_end_transparency_mask calls fail, showing diffs. My suspicion is that
330
         * this actually disables the softmask that we've just rendered!
331
         */
332
2.68k
        if (code >= 0) {
333
            /* Override the colorspace if specified */
334
2.68k
            saved_gs = gs_gstate_copy(ctx->pgs, ctx->memory);
335
2.68k
            if (saved_gs == NULL)
336
0
                code = gs_note_error(gs_error_VMerror);
337
2.68k
            if (code >= 0)
338
2.68k
                code = pdfi_gs_setcolorspace(ctx, igs->GroupGState->color[0].color_space);
339
2.68k
            if (code >= 0)
340
2.68k
                code = gs_setcolor(ctx->pgs, igs->GroupGState->color[0].ccolor);
341
2.68k
            gs_swapcolors_quick(ctx->pgs);
342
2.68k
            if (code >= 0)
343
2.68k
                code = pdfi_gs_setcolorspace(ctx, igs->GroupGState->color[1].color_space);
344
2.68k
            if (code >= 0)
345
2.68k
                code = gs_setcolor(ctx->pgs, igs->GroupGState->color[1].ccolor);
346
2.68k
            gs_swapcolors_quick(ctx->pgs);
347
2.68k
        }
348
349
2.68k
        if (code >= 0)
350
2.68k
            code = gs_begin_transparency_mask(ctx->pgs, &params, &bbox, false);
351
352
2.68k
        if (code >= 0) {
353
2.68k
            if (!empty)
354
2.53k
                code = pdfi_form_execgroup(ctx, ctx->page.CurrentPageDict, G_stream,
355
2.53k
                                           igs->GroupGState, NULL, NULL, &group_Matrix);
356
2.68k
            code1 = gs_end_transparency_mask(ctx->pgs, colorindex);
357
2.68k
            if (code == 0)
358
2.68k
                code = code1;
359
2.68k
        }
360
361
2.68k
        if (saved_gs) {
362
2.68k
            code1 = pdfi_gs_setcolorspace(ctx, saved_gs->color[0].color_space);
363
2.68k
            if (code >= 0) code = code1;
364
2.68k
            code1 = gs_setcolor(ctx->pgs, saved_gs->color[0].ccolor);
365
2.68k
            if (code >= 0) code = code1;
366
2.68k
            gs_swapcolors_quick(ctx->pgs);
367
2.68k
            code1 = pdfi_gs_setcolorspace(ctx, saved_gs->color[1].color_space);
368
2.68k
            if (code >= 0) code = code1;
369
2.68k
            code1 = gs_setcolor(ctx->pgs, saved_gs->color[1].ccolor);
370
2.68k
            if (code >= 0) code = code1;
371
2.68k
            gs_swapcolors_quick(ctx->pgs);
372
2.68k
        }
373
374
        /* Put back the matrix (we couldn't just rely on gsave/grestore for whatever reason,
375
         * according to PS code anyway...
376
         */
377
2.68k
        gs_setmatrix(ctx->pgs, &save_matrix);
378
379
        /* Set Processed flag */
380
2.68k
        if (code == 0 && ProcessedKnown)
381
2.68k
        {
382
2.68k
            code = pdfi_dict_put_bool(ctx, SMask, "Processed", true);
383
2.68k
            if (code < 0)
384
0
                goto exit;
385
2.68k
        }
386
2.68k
    } else {
387
        /* take action on a non-/Mask entry. What does this mean ? What do we need to do */
388
97
        pdfi_set_warning(ctx, 0, NULL, W_PDF_SMASK_UNKNOWN_TYPE, "pdfi_trans_set_mask", "");
389
97
    }
390
391
10.2k
 exit:
392
10.2k
    gs_gstate_free(saved_gs);
393
10.2k
    gs_setoverrideicc(ctx->pgs, save_OverrideICC);
394
10.2k
    if (gsfunc)
395
358
        pdfi_free_function(ctx, gsfunc);
396
10.2k
    if (pcs)
397
2.62k
        rc_decrement_cs(pcs, "pdfi_trans_set_mask");
398
10.2k
    pdfi_countdown(n);
399
10.2k
    pdfi_countdown(S);
400
10.2k
    pdfi_countdown(Group);
401
10.2k
    pdfi_countdown(G_stream);
402
10.2k
    pdfi_countdown(a);
403
10.2k
    pdfi_countdown(BC);
404
10.2k
    pdfi_countdown(TR);
405
10.2k
    pdfi_countdown(BBox);
406
10.2k
    pdfi_countdown(Matrix);
407
10.2k
    pdfi_countdown(CS);
408
#if DEBUG_TRANSPARENCY
409
    dbgmprintf(ctx->memory, "pdfi_trans_set_mask (.execmaskgroup) END\n");
410
#endif
411
10.2k
    return code;
412
8.67k
}
413
414
/* Wrapper around gs call to setup the transparency params correctly */
415
static int pdfi_gs_begin_transparency_group(gs_gstate * pgs,
416
                                       gs_transparency_group_params_t *params,
417
                                       const gs_rect *pbbox, pdf14_compositor_operations group_type)
418
79.1k
{
419
79.1k
    if (gs_getalphaisshape(pgs)) {
420
2.88k
        params->group_shape = gs_getfillconstantalpha(pgs);
421
2.88k
        params->group_opacity = 1.0;
422
76.2k
    } else {
423
76.2k
        params->group_opacity = gs_getfillconstantalpha(pgs);
424
76.2k
        params->group_shape = 1.0;
425
76.2k
    }
426
427
79.1k
    return gs_begin_transparency_group(pgs, params, pbbox, group_type);
428
79.1k
}
429
430
static int pdfi_transparency_group_common(pdf_context *ctx, pdf_dict *page_dict,
431
                                          pdf_dict *group_dict,
432
                                          gs_rect *bbox, pdf14_compositor_operations group_type)
433
20.2k
{
434
20.2k
    gs_transparency_group_params_t params;
435
20.2k
    pdf_obj *CS = NULL;
436
20.2k
    bool b;
437
20.2k
    int code;
438
439
20.2k
    gs_trans_group_params_init(&params, 1.0);
440
    /*    gs_setopacityalpha(ctx->pgs, ctx->pgs->fillconstantalpha); */
441
442
    /* It seems the flag for Isolated is /I */
443
20.2k
    code = pdfi_dict_get_bool(ctx, group_dict, "I", &b);
444
20.2k
    if (code < 0 && code != gs_error_undefined)
445
0
        return_error(code);
446
20.2k
    if (code == gs_error_undefined)
447
6.53k
        params.Isolated = false;
448
13.6k
    else
449
13.6k
        params.Isolated = b;
450
451
    /* It seems the flag for Knockout is /K */
452
20.2k
    code = pdfi_dict_get_bool(ctx, group_dict, "K", &b);
453
20.2k
    if (code < 0 && code != gs_error_undefined)
454
0
        goto exit;
455
20.2k
    if (code == gs_error_undefined)
456
6.54k
        params.Knockout = false;
457
13.6k
    else
458
13.6k
        params.Knockout = b;
459
460
20.2k
    params.image_with_SMask = false;
461
20.2k
    params.ColorSpace = NULL;
462
463
20.2k
    code = pdfi_dict_knownget(ctx, group_dict, "CS", &CS);
464
20.2k
    if (code == 0) {
465
        /* Didn't find a /CS key, try again using /ColorSpace */
466
2.84k
        code = pdfi_dict_knownget(ctx, group_dict, "ColorSpace", &CS);
467
2.84k
    }
468
20.2k
    if (code > 0 && pdfi_type_of(CS) != PDF_NULL) {
469
16.9k
        code = pdfi_setcolorspace(ctx, CS, group_dict, page_dict);
470
16.9k
        if (code < 0)
471
166
            goto exit;
472
16.7k
        params.ColorSpace = gs_currentcolorspace(ctx->pgs);
473
16.7k
    } else {
474
3.26k
        params.ColorSpace = NULL;
475
3.26k
    }
476
477
20.2k
 exit:
478
20.2k
    pdfi_countdown(CS);
479
20.2k
    if (code < 0)
480
297
        return_error(code);
481
482
19.9k
    return pdfi_gs_begin_transparency_group(ctx->pgs, &params, (const gs_rect *)bbox, group_type);
483
20.2k
}
484
485
static bool pdfi_outputprofile_matches_oiprofile(pdf_context *ctx)
486
0
{
487
0
    cmm_dev_profile_t *profile_struct;
488
0
    int code;
489
0
    int k;
490
491
0
    code = dev_proc(ctx->pgs->device, get_profile)(ctx->pgs->device,  &profile_struct);
492
0
    if (code < 0)
493
0
        return true;  /* Assume they match by default and in error condition */
494
495
0
    if (profile_struct->oi_profile == NULL)
496
0
        return true; /* no OI profile so no special case to worry about */
497
0
    else {
498
        /* Check the device profile(s). If any of them do not match, then
499
           we assume there is not a match and it may be necessary to
500
           use the pdf14 device to prerender to the OI profile */
501
0
        for (k = 0; k < NUM_DEVICE_PROFILES; k++) {
502
0
            if (profile_struct->device_profile[k] != NULL) {
503
0
                if (!gsicc_profiles_equal(profile_struct->oi_profile, profile_struct->device_profile[k]))
504
0
                    return false;
505
0
            }
506
0
        }
507
0
        return true;
508
0
    }
509
0
}
510
511
/* Begin a simple group
512
 * pathbbox -- bbox to use, but can be NULL
513
 */
514
int pdfi_trans_begin_simple_group(pdf_context *ctx, gs_rect *pathbbox,
515
                                  bool stroked_bbox, bool isolated, bool knockout)
516
14.8k
{
517
14.8k
    gs_transparency_group_params_t params;
518
14.8k
    gs_rect bbox;
519
14.8k
    int code;
520
521
14.8k
    gs_trans_group_params_init(&params, 1.0);
522
14.8k
    params.Isolated = isolated;
523
14.8k
    params.Knockout = knockout;
524
525
14.8k
    if (!pathbbox) {
526
12.7k
        code = pdfi_get_current_bbox(ctx, &bbox, stroked_bbox);
527
12.7k
        if (code < 0)
528
1.50k
            return code;
529
11.2k
        pathbbox = &bbox;
530
11.2k
    }
531
532
13.3k
    code = pdfi_gs_begin_transparency_group(ctx->pgs, &params, pathbbox, PDF14_BEGIN_TRANS_GROUP);
533
13.3k
    if (code >=  0)
534
13.3k
        ctx->current_stream_save.group_depth++;
535
13.3k
    return code;
536
14.8k
}
537
538
int pdfi_trans_begin_page_group(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *group_dict)
539
5.83k
{
540
5.83k
    gs_rect bbox;
541
5.83k
    int code;
542
543
5.83k
    if (group_dict == NULL)
544
0
        return_error(gs_error_undefined);
545
546
5.83k
    code = pdfi_gsave(ctx);
547
5.83k
    bbox.p.x = ctx->page.Size[0];
548
5.83k
    bbox.p.y = ctx->page.Size[1];
549
5.83k
    bbox.q.x = ctx->page.Size[2];
550
5.83k
    bbox.q.y = ctx->page.Size[3];
551
552
5.83k
    code = pdfi_transparency_group_common(ctx, page_dict, group_dict, &bbox, PDF14_BEGIN_TRANS_PAGE_GROUP);
553
5.83k
    if (code < 0)
554
115
        pdfi_grestore(ctx);
555
5.72k
    else
556
5.72k
        ctx->current_stream_save.group_depth++;
557
558
5.83k
    return code;
559
5.83k
}
560
561
int pdfi_trans_begin_form_group(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *form_dict)
562
15.2k
{
563
15.2k
    pdf_obj *group_obj = NULL;
564
15.2k
    gs_rect bbox;
565
15.2k
    pdf_array *BBox = NULL;
566
15.2k
    int code;
567
15.2k
    pdf_dict *group_dict = NULL;
568
569
    /* TODO: Maybe sometimes this is actually a stream?
570
     * Otherwise should just fetch it as a dict.
571
     * Anyway this will work for either dict or stream
572
     */
573
15.2k
    code = pdfi_dict_get(ctx, form_dict, "Group", &group_obj);
574
15.2k
    if (code < 0)
575
309
        return_error(code);
576
577
14.9k
    code = pdfi_dict_from_obj(ctx, (pdf_obj *)group_obj, &group_dict);
578
14.9k
    if (code < 0)
579
528
        goto exit;
580
581
14.3k
    code = pdfi_gsave(ctx);
582
14.3k
    code = pdfi_dict_knownget_type(ctx, form_dict, "BBox", PDF_ARRAY, (pdf_obj **)&BBox);
583
14.3k
    if (code < 0)
584
0
        goto exit;
585
14.3k
    if (code > 0) {
586
14.3k
        code = pdfi_array_to_gs_rect(ctx, BBox, &bbox);
587
14.3k
        if (code < 0)
588
0
            goto exit;
589
14.3k
    } else {
590
1
        bbox.p.x = 0;
591
1
        bbox.p.y = 0;
592
1
        bbox.q.x = 0;
593
1
        bbox.q.y = 0;
594
1
    }
595
596
14.3k
    code = pdfi_transparency_group_common(ctx, page_dict, group_dict, &bbox, PDF14_BEGIN_TRANS_GROUP);
597
14.3k
    if (code < 0)
598
182
        pdfi_grestore(ctx);
599
14.2k
    else
600
14.2k
        ctx->current_stream_save.group_depth++;
601
602
14.9k
 exit:
603
14.9k
    pdfi_countdown(BBox);
604
14.9k
    pdfi_countdown(group_obj);
605
14.9k
    return code;
606
14.3k
}
607
608
609
int pdfi_trans_end_group(pdf_context *ctx)
610
19.9k
{
611
19.9k
    int code;
612
613
19.9k
    code = gs_end_transparency_group(ctx->pgs);
614
19.9k
    if (code < 0)
615
0
        pdfi_grestore(ctx);
616
19.9k
    else
617
19.9k
        code = pdfi_grestore(ctx);
618
619
19.9k
    ctx->current_stream_save.group_depth--;
620
621
19.9k
    return code;
622
19.9k
}
623
624
/* Ends group with no grestore */
625
int pdfi_trans_end_simple_group(pdf_context *ctx)
626
13.3k
{
627
13.3k
    int code;
628
629
13.3k
    code = gs_end_transparency_group(ctx->pgs);
630
13.3k
    ctx->current_stream_save.group_depth--;
631
632
13.3k
    return code;
633
13.3k
}
634
635
636
int pdfi_trans_begin_isolated_group(pdf_context *ctx, bool image_with_SMask, gs_color_space *pcs)
637
45.8k
{
638
45.8k
    gs_transparency_group_params_t params;
639
45.8k
    gs_rect bbox;
640
45.8k
    gs_rect clip_box;
641
642
45.8k
    gs_trans_group_params_init(&params, 1.0);
643
644
45.8k
    params.ColorSpace = pcs;
645
45.8k
    params.Isolated = true;
646
45.8k
    params.Knockout = false;
647
45.8k
    params.image_with_SMask = image_with_SMask;
648
45.8k
    bbox.p.x = 0;
649
45.8k
    bbox.p.y = 0;
650
45.8k
    bbox.q.x = 1;
651
45.8k
    bbox.q.y = 1;
652
45.8k
    if (gs_clip_bounds_in_user_space(ctx->pgs, &clip_box) >= 0)
653
45.8k
        rect_intersect(bbox, clip_box);
654
655
45.8k
    return pdfi_gs_begin_transparency_group(ctx->pgs, &params, &bbox, PDF14_BEGIN_TRANS_GROUP);
656
45.8k
}
657
658
int pdfi_trans_end_isolated_group(pdf_context *ctx)
659
45.8k
{
660
45.8k
    return gs_end_transparency_group(ctx->pgs);
661
45.8k
}
662
663
664
/* This notifies the compositor that we're done with an smask.  Seems hacky.
665
 * See pdf_draw.ps/doimagesmask.
666
 */
667
int pdfi_trans_end_smask_notify(pdf_context *ctx)
668
50.6k
{
669
50.6k
    gs_transparency_mask_params_t params;
670
50.6k
    gs_rect bbox;
671
672
50.6k
    gs_trans_mask_params_init(&params, TRANSPARENCY_MASK_None);
673
50.6k
    params.replacing = true;
674
675
50.6k
    bbox.p.x = 0;
676
50.6k
    bbox.p.y = 0;
677
50.6k
    bbox.q.x = 0;
678
50.6k
    bbox.q.y = 0;
679
680
50.6k
    return gs_begin_transparency_mask(ctx->pgs, &params, &bbox, false);
681
50.6k
}
682
683
/* Setup whether or not we need to support overprint (for device)
684
 * Check for:
685
 *   1) how many components the device has (CMYK or other)
686
 *   2) whether it is a device that has transparency support
687
 *   3) Overprint mode (enable, disable, simulate)
688
 * Based on pdf_main.ps/pdfshowpage_finish
689
 * Test file (run with -dOverprint=/simulate)
690
 *    tests_private/pdf/uploads/op_trans_spot_testing.pdf
691
 */
692
void pdfi_trans_set_needs_OP(pdf_context *ctx)
693
103k
{
694
103k
    bool is_cmyk;
695
103k
    bool device_transparency = false;
696
697
    /* PS code checks for >= 4 components... */
698
103k
    is_cmyk = ctx->pgs->device->color_info.num_components >= 4;
699
700
103k
    device_transparency = pdfi_device_check_param_bool(ctx->pgs->device, "HaveTransparency");
701
702
103k
    ctx->page.needs_OP = false;
703
103k
    ctx->page.simulate_op = false;
704
103k
    switch(ctx->pgs->device->icc_struct->overprint_control) {
705
0
    case gs_overprint_control_disable:
706
        /* Use defaults */
707
0
        break;
708
0
    case gs_overprint_control_simulate:
709
0
        if (!device_transparency && ctx->page.has_OP) {
710
0
            if (is_cmyk) {
711
                /* If the page has spots and the device is not spot capable OR
712
                   if the output intent profile is to be used, but we have
713
                   a device output profile that is different, then we will be
714
                   doing simulation with the pdf14 device buffer */
715
0
                if ((ctx->page.num_spots > 0  && !ctx->device_state.spot_capable) ||
716
0
                    !pdfi_outputprofile_matches_oiprofile(ctx)) {
717
0
                    ctx->page.needs_OP = true;
718
0
                    ctx->page.simulate_op = true;
719
0
                }
720
0
            } else {
721
0
                ctx->page.needs_OP = true;
722
0
                ctx->page.simulate_op = true;
723
0
            }
724
0
        }
725
0
        break;
726
103k
    case gs_overprint_control_enable:
727
103k
    default:
728
103k
        if (!is_cmyk || device_transparency)
729
93.8k
            ctx->page.needs_OP = false;
730
9.52k
        else
731
9.52k
            ctx->page.needs_OP = true;
732
103k
        break;
733
103k
    }
734
735
103k
    if(ctx->args.pdfdebug)
736
103k
        dbgmprintf2(ctx->memory, "Page %s Overprint, Simulate is %s\n",
737
103k
                    ctx->page.needs_OP ? "NEEDS" : "does NOT NEED",
738
103k
                    ctx->page.simulate_op ? "TRUE" : "FALSE");
739
103k
}
740
741
/* Figures out if current colorspace is okay for Overprint (see pdf_ops.ps/okOPcs and setupOPtrans) */
742
bool pdfi_trans_okOPcs(pdf_context *ctx)
743
227k
{
744
227k
    gs_color_space_index csi;
745
746
227k
    csi = pdfi_currentcolorspace(ctx, 0);
747
748
227k
    switch (csi) {
749
32.8k
    case gs_color_space_index_DeviceGray:
750
54.3k
    case gs_color_space_index_DeviceCMYK:
751
54.4k
    case gs_color_space_index_DeviceN:
752
55.3k
    case gs_color_space_index_Separation:
753
        /* These are colorspaces that don't require special handling for overprint.
754
         * (pdf1.7 pg 259,578 may apply)
755
         * According to mvrhel, DeviceGray should also be included (see comment in gx_set_overprint_ICC()).
756
         * Sample: 030_Gray_K_black_OP_x1a.pdf (??)
757
         */
758
#if DEBUG_TRANSPARENCY
759
        dbgmprintf1(ctx->memory, "Colorspace is %d, OKAY for OVERPRINT\n", csi);
760
#endif
761
55.3k
        return true;
762
172k
    default:
763
#if DEBUG_TRANSPARENCY
764
        dbgmprintf1(ctx->memory, "Colorspace is %d, NOT OKAY for OVERPRINT\n", csi);
765
#endif
766
172k
        return false;
767
227k
    }
768
769
0
    return false;
770
227k
}
771
772
int pdfi_trans_setup(pdf_context *ctx, pdfi_trans_state_t *state, gs_rect *bbox,
773
                           pdfi_transparency_caller_t caller)
774
6.12M
{
775
6.12M
    pdfi_int_gstate *igs = (pdfi_int_gstate *)ctx->pgs->client_data;
776
6.12M
    int code;
777
6.12M
    bool stroked_bbox;
778
6.12M
    bool current_overprint;
779
6.12M
    bool okOPcs = false;
780
6.12M
    bool ChangeBM = false;
781
6.12M
    gs_blend_mode_t  mode = gs_currentblendmode(ctx->pgs);  /* quite warning */
782
6.12M
    bool need_group = false;
783
784
6.12M
    memset(state, 0, sizeof(*state));
785
786
6.12M
    if (!ctx->page.has_transparency)
787
4.90M
        return 0;
788
789
1.21M
    if (ctx->page.needs_OP) {
790
222k
        okOPcs = pdfi_trans_okOPcs(ctx);
791
222k
        if (okOPcs) {
792
55.2k
            if (caller == TRANSPARENCY_Caller_Stroke)
793
19.1k
                current_overprint = gs_currentstrokeoverprint(ctx->pgs);
794
36.1k
            else {
795
36.1k
                current_overprint = gs_currentfilloverprint(ctx->pgs);
796
36.1k
                if (caller == TRANSPARENCY_Caller_FillStroke)
797
1.24k
                    current_overprint |= gs_currentstrokeoverprint(ctx->pgs);
798
36.1k
            }
799
55.2k
            ChangeBM = current_overprint;
800
55.2k
            mode = gs_currentblendmode(ctx->pgs);
801
55.2k
            if (mode != BLEND_MODE_Normal && mode != BLEND_MODE_Compatible)
802
357
                need_group = ChangeBM;
803
54.9k
            else
804
54.9k
                need_group = false;
805
167k
        } else {
806
167k
            need_group = false;
807
167k
        }
808
222k
        need_group = need_group || (igs->SMask != NULL);
809
995k
    } else {
810
995k
        if (caller == TRANSPARENCY_Caller_Image || igs->SMask == NULL)
811
987k
            need_group = false;
812
7.83k
        else
813
7.83k
            need_group = true;
814
995k
    }
815
816
1.21M
    code = pdfi_trans_set_params(ctx);
817
1.21M
    if (code != 0)
818
2.03k
        return 0;
819
820
1.21M
    if (!need_group && !ChangeBM)
821
1.20M
        return 0;
822
823
    /* TODO: error handling... */
824
9.20k
    if (need_group) {
825
6.26k
        bool isolated = false;
826
6.26k
        mode = gs_currentblendmode(ctx->pgs);
827
828
6.26k
        stroked_bbox = (caller == TRANSPARENCY_Caller_Stroke || caller == TRANSPARENCY_Caller_FillStroke);
829
830
        /* When changing to compatible overprint bm, the group pushed must be non-isolated. The exception
831
           is if we have a softmask AND the blend mode is not normal and not compatible.
832
           See /setupOPtrans in pdf_ops.ps  */
833
6.26k
        if (igs->SMask != NULL && mode != BLEND_MODE_Normal && mode != BLEND_MODE_Compatible)
834
3.07k
            isolated = true;
835
6.26k
        code = pdfi_trans_begin_simple_group(ctx, bbox, stroked_bbox, isolated, false);
836
837
        /* Group was not pushed if error */
838
6.26k
        if (code >= 0)
839
4.76k
            state->GroupPushed = true;
840
841
6.26k
        state->saveStrokeAlpha = gs_getstrokeconstantalpha(ctx->pgs);
842
6.26k
        state->saveFillAlpha = gs_getfillconstantalpha(ctx->pgs);
843
6.26k
        code = gs_setfillconstantalpha(ctx->pgs, 1.0);
844
6.26k
        code = gs_setstrokeconstantalpha(ctx->pgs, 1.0);
845
6.26k
    }
846
847
    /* If we are in a fill stroke situation, do not change the blend mode.
848
       We can have situations where the fill and the stroke have different
849
       blending needs (one may need compatible overprint and the other may
850
       need the normal mode) This is all handled in the fill stroke logic */
851
9.20k
    if (ChangeBM && caller != TRANSPARENCY_Caller_FillStroke) {
852
3.01k
        state->saveBM = mode;
853
3.01k
        state->ChangeBM = true;
854
3.01k
        code = gs_setblendmode(ctx->pgs, BLEND_MODE_CompatibleOverprint);
855
3.01k
    }
856
9.20k
    return code;
857
1.21M
}
858
859
int pdfi_trans_required(pdf_context *ctx)
860
40.4M
{
861
40.4M
    gs_blend_mode_t mode;
862
863
40.4M
    if (!ctx->page.has_transparency)
864
30.8M
        return 0;
865
866
9.56M
    mode = gs_currentblendmode(ctx->pgs);
867
9.56M
    if ((mode == BLEND_MODE_Normal || mode == BLEND_MODE_Compatible) &&
868
9.56M
        ctx->pgs->fillconstantalpha == 1 &&
869
9.56M
        ctx->pgs->strokeconstantalpha == 1 &&
870
9.56M
        ((pdfi_int_gstate *)ctx->pgs->client_data)->SMask == NULL)
871
9.52M
        return 0;
872
873
37.4k
    return 1;
874
9.56M
}
875
876
int pdfi_trans_setup_text(pdf_context *ctx, pdfi_trans_state_t *state, bool is_show)
877
19.9M
{
878
19.9M
    int Trmode;
879
19.9M
    int code, code1;
880
19.9M
    gs_rect bbox;
881
882
19.9M
    if (!pdfi_trans_required(ctx))
883
19.9M
        return 0;
884
885
13.9k
    Trmode = gs_currenttextrenderingmode(ctx->pgs);
886
13.9k
    code = gs_gsave(ctx->pgs);
887
13.9k
    if (code < 0) goto exit;
888
889
13.9k
    if (is_show) {
890
13.9k
        code = gs_clippath(ctx->pgs);
891
13.9k
    } else {
892
0
        code = gs_strokepath(ctx->pgs);
893
0
    }
894
13.9k
    if (code >= 0)
895
13.9k
        code = gs_upathbbox(ctx->pgs, &bbox, false);
896
13.9k
    if (code < 0) {
897
        /* Just set bbox to [0,0,0,0] */
898
12
        bbox.p.x = bbox.p.y = bbox.q.x = bbox.q.y = 0.0;
899
12
        code = 0;
900
12
    }
901
13.9k
    code1 = gs_grestore(ctx->pgs);
902
13.9k
    if (code == 0) code = code1;
903
13.9k
    if (code < 0) goto exit;
904
905
13.9k
    switch (Trmode) {
906
13.8k
    case 0:
907
13.8k
        code = pdfi_trans_setup(ctx, state, &bbox, TRANSPARENCY_Caller_Fill);
908
13.8k
        break;
909
52
    default:
910
        /* TODO: All the others */
911
52
        code = pdfi_trans_setup(ctx, state, &bbox, TRANSPARENCY_Caller_Fill);
912
52
        break;
913
13.9k
    }
914
915
13.9k
 exit:
916
13.9k
    return code;
917
13.9k
}
918
919
int pdfi_trans_teardown_text(pdf_context *ctx, pdfi_trans_state_t *state)
920
19.9M
{
921
19.9M
    if (!pdfi_trans_required(ctx))
922
19.9M
         return 0;
923
924
13.9k
    return pdfi_trans_teardown(ctx, state);
925
19.9M
}
926
927
int pdfi_trans_teardown(pdf_context *ctx, pdfi_trans_state_t *state)
928
6.12M
{
929
6.12M
    int code = 0;
930
931
6.12M
    if (!ctx->page.has_transparency)
932
4.90M
        return 0;
933
934
1.21M
    if (state->GroupPushed) {
935
4.76k
        code = pdfi_trans_end_simple_group(ctx);
936
4.76k
        code = gs_setstrokeconstantalpha(ctx->pgs, state->saveStrokeAlpha);
937
4.76k
        code = gs_setfillconstantalpha(ctx->pgs, state->saveFillAlpha);
938
4.76k
    }
939
940
1.21M
    if (gs_currentblendmode(ctx->pgs) == BLEND_MODE_CompatibleOverprint)
941
3.01k
        code = gs_setblendmode(ctx->pgs, state->saveBM);
942
943
1.21M
    return code;
944
6.12M
}
945
946
int pdfi_trans_set_params(pdf_context *ctx)
947
1.65M
{
948
1.65M
    int code = 0;
949
1.65M
    pdfi_int_gstate *igs = (pdfi_int_gstate *)ctx->pgs->client_data;
950
1.65M
    gs_transparency_channel_selector_t csel;
951
952
1.65M
    if (ctx->page.has_transparency) {
953
1.47M
        if (gs_getalphaisshape(ctx->pgs))
954
6.98k
            csel = TRANSPARENCY_CHANNEL_Shape;
955
1.46M
        else
956
1.46M
            csel = TRANSPARENCY_CHANNEL_Opacity;
957
1.47M
        if (igs->SMask) {
958
10.2k
            code = pdfi_trans_set_mask(ctx, igs, csel);
959
10.2k
        }
960
1.47M
    }
961
962
1.65M
    return code;
963
1.65M
}