Coverage Report

Created: 2025-06-24 07:01

/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
95.4k
{
42
95.4k
    float in = in_val;
43
95.4k
    gs_function_t *const pfn = proc_data;
44
45
95.4k
    return gs_function_evaluate(pfn, &in, out);
46
95.4k
}
47
48
static void
49
pdfi_set_GrayBackground(gs_transparency_mask_params_t *params)
50
1.63k
{
51
1.63k
    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.63k
    switch(params->Background_components) {
59
26
    case 3:
60
        /* RGB: currentgray = 0.3*R + 0.59*G + 0.11*B */
61
26
        params->GrayBackground = (0.3 * params->Background[0] +
62
26
                                  0.59 * params->Background[1] +
63
26
                                  0.11 * params->Background[2]);
64
26
        break;
65
781
    case 4:
66
        /* CMYK: currentgray = 1.0 – min (1.0, .3*C + .59*M + .11*Y + K)
67
        */
68
781
        num = 0.3*params->Background[0] + 0.59*params->Background[1] +
69
781
            0.11*params->Background[2] + params->Background[3];
70
781
        if (num > 1)
71
727
            num = 1;
72
781
        params->GrayBackground = 1 - num;
73
781
        break;
74
830
    case 1:
75
830
        params->GrayBackground = params->Background[0];
76
830
        break;
77
0
    default:
78
        /* No clue... */
79
0
        params->GrayBackground = 0;
80
1.63k
    }
81
1.63k
}
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.0k
{
86
10.0k
    int code = 0, code1 = 0;
87
10.0k
    pdf_dict *SMask = igs->SMask;
88
10.0k
    gs_color_space *pcs = NULL;
89
10.0k
    gs_rect bbox;
90
10.0k
    gs_transparency_mask_params_t params;
91
10.0k
    pdf_array *BBox = NULL;
92
10.0k
    pdf_array *Matrix = NULL;
93
10.0k
    pdf_array *a = NULL;
94
10.0k
    pdf_array *BC = NULL;
95
10.0k
    pdf_stream *G_stream = NULL;
96
10.0k
    pdf_dict *G_stream_dict = NULL;
97
10.0k
    pdf_dict *Group = NULL;
98
10.0k
    pdf_obj *TR = NULL;
99
10.0k
    gs_function_t *gsfunc = NULL;
100
10.0k
    pdf_name *n = NULL;
101
10.0k
    pdf_name *S = NULL;
102
10.0k
    pdf_obj *CS = NULL;
103
10.0k
    gs_matrix save_matrix, GroupMat, group_Matrix;
104
10.0k
    gs_transparency_mask_subtype_t subtype = TRANSPARENCY_MASK_Luminosity;
105
10.0k
    bool Processed, ProcessedKnown = 0;
106
10.0k
    bool save_OverrideICC = gs_currentoverrideicc(ctx->pgs);
107
10.0k
    gs_gstate *saved_gs = NULL;
108
10.0k
    gs_rect clip_bbox;
109
10.0k
    int empty = 0;
110
111
#if DEBUG_TRANSPARENCY
112
    dbgmprintf(ctx->memory, "pdfi_trans_set_mask (.execmaskgroup) BEGIN\n");
113
#endif
114
10.0k
    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.0k
    code = pdfi_dict_knownget_bool(ctx, SMask, "Processed", &Processed);
120
10.0k
    if (code > 0) {
121
5.48k
        if (Processed) {
122
#if DEBUG_TRANSPARENCY
123
          dbgmprintf(ctx->memory, "SMask already built, skipping\n");
124
#endif
125
1.57k
          code = 0;
126
1.57k
          goto exit;
127
1.57k
        }
128
3.91k
        ProcessedKnown = 1;
129
3.91k
    }
130
131
8.48k
    gs_setoverrideicc(ctx->pgs, true);
132
133
    /* If /Processed not in the dict, put it there */
134
8.48k
    if (code == 0) {
135
4.57k
        code = pdfi_dict_put_bool(ctx, SMask, "Processed", false);
136
4.57k
        if (code < 0)
137
0
            goto exit;
138
4.57k
        ProcessedKnown = 1;
139
4.57k
    }
140
141
    /* See pdf1.7 pg 553 (pain in the butt to find this!) */
142
8.48k
    code = pdfi_dict_knownget_type(ctx, SMask, "Type", PDF_NAME, (pdf_obj **)&n);
143
8.48k
    if (code == 0 || (code > 0 && pdfi_name_is(n, "Mask"))) {
144
        /* G is transparency group XObject (required) */
145
8.38k
        code = pdfi_dict_knownget_type(ctx, SMask, "G", PDF_STREAM, (pdf_obj **)&G_stream);
146
8.38k
        if (code <= 0) {
147
3.73k
            pdfi_trans_end_smask_notify(ctx);
148
3.73k
            code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_SMASK_MISSING_G, "pdfi_trans_set_mask", "");
149
3.73k
            goto exit;
150
3.73k
        }
151
152
4.65k
        code = pdfi_dict_from_obj(ctx, (pdf_obj *)G_stream, &G_stream_dict);
153
4.65k
        if (code < 0)
154
0
            goto exit;
155
156
        /* S is a subtype name (required) */
157
4.65k
        code = pdfi_dict_knownget_type(ctx, SMask, "S", PDF_NAME, (pdf_obj **)&S);
158
4.65k
        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.43k
        else if (pdfi_name_is(S, "Luminosity")) {
163
3.96k
            subtype = TRANSPARENCY_MASK_Luminosity;
164
3.96k
        } else if (pdfi_name_is(S, "Alpha")) {
165
465
            subtype = TRANSPARENCY_MASK_Alpha;
166
465
        } else {
167
9
            subtype = TRANSPARENCY_MASK_Luminosity;
168
9
            pdfi_set_warning(ctx, 0, NULL, W_PDF_SMASK_UNKNOWN_S, "pdfi_trans_set_mask", "");
169
9
        }
170
171
        /* TR is transfer function (Optional) */
172
4.65k
        code = pdfi_dict_knownget(ctx, SMask, "TR", (pdf_obj **)&TR);
173
4.65k
        if (code > 0) {
174
422
            switch (pdfi_type_of(TR)) {
175
6
                case PDF_DICT:
176
422
                case PDF_STREAM:
177
422
                    code = pdfi_build_function(ctx, &gsfunc, NULL, 1, TR, NULL);
178
422
                    if (code < 0)
179
49
                        goto exit;
180
373
                    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
373
                    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
422
            }
194
422
        }
195
196
        /* BC is Background Color array (Optional) */
197
4.60k
        code = pdfi_dict_knownget_type(ctx, SMask, "BC", PDF_ARRAY, (pdf_obj **)&BC);
198
4.60k
        if (code < 0)
199
75
            goto exit;
200
201
4.52k
        code = pdfi_dict_knownget_type(ctx, G_stream_dict, "BBox", PDF_ARRAY, (pdf_obj **)&BBox);
202
4.52k
        if (code < 0)
203
8
            goto exit;
204
4.52k
        code = pdfi_array_to_gs_rect(ctx, BBox, &bbox);
205
4.52k
        if (code < 0)
206
15
            goto exit;
207
208
4.50k
        gs_trans_mask_params_init(&params, subtype);
209
4.50k
        params.replacing = true;
210
4.50k
        if (gsfunc) {
211
373
            params.TransferFunction = pdfi_tf_using_function;
212
373
            params.TransferFunction_data = gsfunc;
213
373
        }
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.50k
        gs_currentmatrix(ctx->pgs, &save_matrix);
220
4.50k
        gs_currentmatrix(igs->GroupGState, &GroupMat);
221
4.50k
        gs_setmatrix(ctx->pgs, &GroupMat);
222
223
4.50k
        code = pdfi_dict_knownget_type(ctx, G_stream_dict, "Matrix", PDF_ARRAY, (pdf_obj **)&Matrix);
224
4.50k
        if (code < 0)
225
0
            goto exit;
226
4.50k
        code = pdfi_array_to_gs_matrix(ctx, Matrix, &group_Matrix);
227
4.50k
        if (code < 0)
228
5
            goto exit;
229
230
        /* Transform the BBox by the Matrix */
231
4.50k
        pdfi_bbox_transform(ctx, &bbox, &group_Matrix);
232
233
4.50k
        if (gs_clip_bounds_in_user_space(ctx->pgs, &clip_bbox) >= 0)
234
4.44k
        {
235
4.44k
            rect_intersect(bbox, clip_bbox);
236
4.44k
            if (bbox.p.x >= bbox.q.x || bbox.p.y >= bbox.q.y)
237
928
            {
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
928
                empty = 1;
242
928
            }
243
4.44k
        }
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.50k
        code = pdfi_dict_knownget_type(ctx, G_stream_dict, "Group", PDF_DICT, (pdf_obj **)&Group);
248
4.50k
        if (code < 0)
249
1.53k
            goto exit;
250
2.97k
        if (code > 0) {
251
            /* TODO: Stuff with colorspace, see .execmaskgroup */
252
2.90k
            code = pdfi_dict_knownget(ctx, Group, "CS", &CS);
253
2.90k
            if (code < 0) {
254
26
                code = pdfi_dict_knownget(ctx, Group, "ColorSpace", &CS);
255
26
                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
26
                pdfi_set_warning(ctx, 0, NULL, W_PDF_GROUP_HAS_COLORSPACE, "pdfi_trans_set_mask", NULL);
260
26
            }
261
2.90k
            if (code > 0) {
262
2.85k
                code = pdfi_create_colorspace(ctx, CS, (pdf_dict *)ctx->main_stream,
263
2.85k
                                              ctx->page.CurrentPageDict, &pcs, false);
264
2.85k
                params.ColorSpace = pcs;
265
2.85k
                if (code < 0)
266
137
                    goto exit;
267
2.85k
            }
268
51
            else
269
51
                   params.ColorSpace = NULL;
270
2.90k
        } else {
271
            /* GS and Adobe will ignore the whole mask in this case, so we do the same.
272
            */
273
69
            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
69
            goto exit;
275
69
        }
276
277
        /* If there's a BC, put it in the params */
278
2.76k
        if (BC) {
279
1.66k
            if (params.ColorSpace == NULL) {
280
26
                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.63k
            } else {
283
1.63k
                int i, components = pdfi_array_size(BC);
284
1.63k
                double num;
285
286
1.63k
                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.63k
                } else {
290
1.63k
                    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.66k
                    for (i=0; i < components; i++) {
296
4.03k
                        code = pdfi_array_get_number(ctx, BC, i, &num);
297
4.03k
                        if (code < 0)
298
0
                            break;
299
4.03k
                        params.Background[i] = (float)num;
300
4.03k
                    }
301
1.63k
                    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.63k
                    pdfi_set_GrayBackground(&params);
310
1.63k
                }
311
1.63k
            }
312
1.66k
        }
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.76k
        if (code >= 0) {
333
            /* Override the colorspace if specified */
334
2.76k
            saved_gs = gs_gstate_copy(ctx->pgs, ctx->memory);
335
2.76k
            if (saved_gs == NULL)
336
0
                code = gs_note_error(gs_error_VMerror);
337
2.76k
            if (code >= 0)
338
2.76k
                code = pdfi_gs_setcolorspace(ctx, igs->GroupGState->color[0].color_space);
339
2.76k
            if (code >= 0)
340
2.76k
                code = gs_setcolor(ctx->pgs, igs->GroupGState->color[0].ccolor);
341
2.76k
            gs_swapcolors_quick(ctx->pgs);
342
2.76k
            if (code >= 0)
343
2.76k
                code = pdfi_gs_setcolorspace(ctx, igs->GroupGState->color[1].color_space);
344
2.76k
            if (code >= 0)
345
2.76k
                code = gs_setcolor(ctx->pgs, igs->GroupGState->color[1].ccolor);
346
2.76k
            gs_swapcolors_quick(ctx->pgs);
347
2.76k
        }
348
349
2.76k
        if (code >= 0)
350
2.76k
            code = gs_begin_transparency_mask(ctx->pgs, &params, &bbox, false);
351
352
2.76k
        if (code >= 0) {
353
2.76k
            if (!empty)
354
2.62k
                code = pdfi_form_execgroup(ctx, ctx->page.CurrentPageDict, G_stream,
355
2.62k
                                           igs->GroupGState, NULL, NULL, &group_Matrix);
356
2.76k
            code1 = gs_end_transparency_mask(ctx->pgs, colorindex);
357
2.76k
            if (code == 0)
358
2.76k
                code = code1;
359
2.76k
        }
360
361
2.76k
        if (saved_gs) {
362
2.76k
            code1 = pdfi_gs_setcolorspace(ctx, saved_gs->color[0].color_space);
363
2.76k
            if (code >= 0) code = code1;
364
2.76k
            code1 = gs_setcolor(ctx->pgs, saved_gs->color[0].ccolor);
365
2.76k
            if (code >= 0) code = code1;
366
2.76k
            gs_swapcolors_quick(ctx->pgs);
367
2.76k
            code1 = pdfi_gs_setcolorspace(ctx, saved_gs->color[1].color_space);
368
2.76k
            if (code >= 0) code = code1;
369
2.76k
            code1 = gs_setcolor(ctx->pgs, saved_gs->color[1].ccolor);
370
2.76k
            if (code >= 0) code = code1;
371
2.76k
            gs_swapcolors_quick(ctx->pgs);
372
2.76k
        }
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.76k
        gs_setmatrix(ctx->pgs, &save_matrix);
378
379
        /* Set Processed flag */
380
2.76k
        if (code == 0 && ProcessedKnown)
381
2.76k
        {
382
2.76k
            code = pdfi_dict_put_bool(ctx, SMask, "Processed", true);
383
2.76k
            if (code < 0)
384
0
                goto exit;
385
2.76k
        }
386
2.76k
    } else {
387
        /* take action on a non-/Mask entry. What does this mean ? What do we need to do */
388
98
        pdfi_set_warning(ctx, 0, NULL, W_PDF_SMASK_UNKNOWN_TYPE, "pdfi_trans_set_mask", "");
389
98
    }
390
391
10.0k
 exit:
392
10.0k
    gs_gstate_free(saved_gs);
393
10.0k
    gs_setoverrideicc(ctx->pgs, save_OverrideICC);
394
10.0k
    if (gsfunc)
395
373
        pdfi_free_function(ctx, gsfunc);
396
10.0k
    if (pcs)
397
2.71k
        rc_decrement_cs(pcs, "pdfi_trans_set_mask");
398
10.0k
    pdfi_countdown(n);
399
10.0k
    pdfi_countdown(S);
400
10.0k
    pdfi_countdown(Group);
401
10.0k
    pdfi_countdown(G_stream);
402
10.0k
    pdfi_countdown(a);
403
10.0k
    pdfi_countdown(BC);
404
10.0k
    pdfi_countdown(TR);
405
10.0k
    pdfi_countdown(BBox);
406
10.0k
    pdfi_countdown(Matrix);
407
10.0k
    pdfi_countdown(CS);
408
#if DEBUG_TRANSPARENCY
409
    dbgmprintf(ctx->memory, "pdfi_trans_set_mask (.execmaskgroup) END\n");
410
#endif
411
10.0k
    return code;
412
8.48k
}
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
80.5k
{
419
80.5k
    if (gs_getalphaisshape(pgs)) {
420
3.12k
        params->group_shape = gs_getfillconstantalpha(pgs);
421
3.12k
        params->group_opacity = 1.0;
422
77.4k
    } else {
423
77.4k
        params->group_opacity = gs_getfillconstantalpha(pgs);
424
77.4k
        params->group_shape = 1.0;
425
77.4k
    }
426
427
80.5k
    return gs_begin_transparency_group(pgs, params, pbbox, group_type);
428
80.5k
}
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.4k
{
434
20.4k
    gs_transparency_group_params_t params;
435
20.4k
    pdf_obj *CS = NULL;
436
20.4k
    bool b;
437
20.4k
    int code;
438
439
20.4k
    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.4k
    code = pdfi_dict_get_bool(ctx, group_dict, "I", &b);
444
20.4k
    if (code < 0 && code != gs_error_undefined)
445
0
        return_error(code);
446
20.4k
    if (code == gs_error_undefined)
447
6.14k
        params.Isolated = false;
448
14.3k
    else
449
14.3k
        params.Isolated = b;
450
451
    /* It seems the flag for Knockout is /K */
452
20.4k
    code = pdfi_dict_get_bool(ctx, group_dict, "K", &b);
453
20.4k
    if (code < 0 && code != gs_error_undefined)
454
0
        goto exit;
455
20.4k
    if (code == gs_error_undefined)
456
6.10k
        params.Knockout = false;
457
14.3k
    else
458
14.3k
        params.Knockout = b;
459
460
20.4k
    params.image_with_SMask = false;
461
20.4k
    params.ColorSpace = NULL;
462
463
20.4k
    code = pdfi_dict_knownget(ctx, group_dict, "CS", &CS);
464
20.4k
    if (code == 0) {
465
        /* Didn't find a /CS key, try again using /ColorSpace */
466
3.02k
        code = pdfi_dict_knownget(ctx, group_dict, "ColorSpace", &CS);
467
3.02k
    }
468
20.4k
    if (code > 0 && pdfi_type_of(CS) != PDF_NULL) {
469
17.0k
        code = pdfi_setcolorspace(ctx, CS, group_dict, page_dict);
470
17.0k
        if (code < 0)
471
167
            goto exit;
472
16.8k
        params.ColorSpace = gs_currentcolorspace(ctx->pgs);
473
16.8k
    } else {
474
3.42k
        params.ColorSpace = NULL;
475
3.42k
    }
476
477
20.4k
 exit:
478
20.4k
    pdfi_countdown(CS);
479
20.4k
    if (code < 0)
480
301
        return_error(code);
481
482
20.1k
    return pdfi_gs_begin_transparency_group(ctx->pgs, &params, (const gs_rect *)bbox, group_type);
483
20.4k
}
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
15.8k
{
517
15.8k
    gs_transparency_group_params_t params;
518
15.8k
    gs_rect bbox;
519
15.8k
    int code;
520
521
15.8k
    gs_trans_group_params_init(&params, 1.0);
522
15.8k
    params.Isolated = isolated;
523
15.8k
    params.Knockout = knockout;
524
525
15.8k
    if (!pathbbox) {
526
13.7k
        code = pdfi_get_current_bbox(ctx, &bbox, stroked_bbox);
527
13.7k
        if (code < 0)
528
1.43k
            return code;
529
12.3k
        pathbbox = &bbox;
530
12.3k
    }
531
532
14.3k
    code = pdfi_gs_begin_transparency_group(ctx->pgs, &params, pathbbox, PDF14_BEGIN_TRANS_GROUP);
533
14.3k
    if (code >=  0)
534
14.3k
        ctx->current_stream_save.group_depth++;
535
14.3k
    return code;
536
15.8k
}
537
538
int pdfi_trans_begin_page_group(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *group_dict)
539
5.86k
{
540
5.86k
    gs_rect bbox;
541
5.86k
    int code;
542
543
5.86k
    if (group_dict == NULL)
544
0
        return_error(gs_error_undefined);
545
546
5.86k
    code = pdfi_gsave(ctx);
547
5.86k
    bbox.p.x = ctx->page.Size[0];
548
5.86k
    bbox.p.y = ctx->page.Size[1];
549
5.86k
    bbox.q.x = ctx->page.Size[2];
550
5.86k
    bbox.q.y = ctx->page.Size[3];
551
552
5.86k
    code = pdfi_transparency_group_common(ctx, page_dict, group_dict, &bbox, PDF14_BEGIN_TRANS_PAGE_GROUP);
553
5.86k
    if (code < 0)
554
114
        pdfi_grestore(ctx);
555
5.75k
    else
556
5.75k
        ctx->current_stream_save.group_depth++;
557
558
5.86k
    return code;
559
5.86k
}
560
561
int pdfi_trans_begin_form_group(pdf_context *ctx, pdf_dict *page_dict, pdf_dict *form_dict)
562
15.5k
{
563
15.5k
    pdf_obj *group_obj = NULL;
564
15.5k
    gs_rect bbox;
565
15.5k
    pdf_array *BBox = NULL;
566
15.5k
    int code;
567
15.5k
    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.5k
    code = pdfi_dict_get(ctx, form_dict, "Group", &group_obj);
574
15.5k
    if (code < 0)
575
417
        return_error(code);
576
577
15.1k
    code = pdfi_dict_from_obj(ctx, (pdf_obj *)group_obj, &group_dict);
578
15.1k
    if (code < 0)
579
528
        goto exit;
580
581
14.5k
    code = pdfi_gsave(ctx);
582
14.5k
    code = pdfi_dict_knownget_type(ctx, form_dict, "BBox", PDF_ARRAY, (pdf_obj **)&BBox);
583
14.5k
    if (code < 0)
584
0
        goto exit;
585
14.5k
    if (code > 0) {
586
14.5k
        code = pdfi_array_to_gs_rect(ctx, BBox, &bbox);
587
14.5k
        if (code < 0)
588
0
            goto exit;
589
14.5k
    } 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.5k
    code = pdfi_transparency_group_common(ctx, page_dict, group_dict, &bbox, PDF14_BEGIN_TRANS_GROUP);
597
14.5k
    if (code < 0)
598
187
        pdfi_grestore(ctx);
599
14.4k
    else
600
14.4k
        ctx->current_stream_save.group_depth++;
601
602
15.1k
 exit:
603
15.1k
    pdfi_countdown(BBox);
604
15.1k
    pdfi_countdown(group_obj);
605
15.1k
    return code;
606
14.5k
}
607
608
609
int pdfi_trans_end_group(pdf_context *ctx)
610
20.1k
{
611
20.1k
    int code;
612
613
20.1k
    code = gs_end_transparency_group(ctx->pgs);
614
20.1k
    if (code < 0)
615
0
        pdfi_grestore(ctx);
616
20.1k
    else
617
20.1k
        code = pdfi_grestore(ctx);
618
619
20.1k
    ctx->current_stream_save.group_depth--;
620
621
20.1k
    return code;
622
20.1k
}
623
624
/* Ends group with no grestore */
625
int pdfi_trans_end_simple_group(pdf_context *ctx)
626
14.3k
{
627
14.3k
    int code;
628
629
14.3k
    code = gs_end_transparency_group(ctx->pgs);
630
14.3k
    ctx->current_stream_save.group_depth--;
631
632
14.3k
    return code;
633
14.3k
}
634
635
636
int pdfi_trans_begin_isolated_group(pdf_context *ctx, bool image_with_SMask, gs_color_space *pcs)
637
45.9k
{
638
45.9k
    gs_transparency_group_params_t params;
639
45.9k
    gs_rect bbox;
640
45.9k
    gs_rect clip_box;
641
642
45.9k
    gs_trans_group_params_init(&params, 1.0);
643
644
45.9k
    params.ColorSpace = pcs;
645
45.9k
    params.Isolated = true;
646
45.9k
    params.Knockout = false;
647
45.9k
    params.image_with_SMask = image_with_SMask;
648
45.9k
    bbox.p.x = 0;
649
45.9k
    bbox.p.y = 0;
650
45.9k
    bbox.q.x = 1;
651
45.9k
    bbox.q.y = 1;
652
45.9k
    if (gs_clip_bounds_in_user_space(ctx->pgs, &clip_box) >= 0)
653
45.9k
        rect_intersect(bbox, clip_box);
654
655
45.9k
    return pdfi_gs_begin_transparency_group(ctx->pgs, &params, &bbox, PDF14_BEGIN_TRANS_GROUP);
656
45.9k
}
657
658
int pdfi_trans_end_isolated_group(pdf_context *ctx)
659
45.9k
{
660
45.9k
    return gs_end_transparency_group(ctx->pgs);
661
45.9k
}
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
53.2k
{
669
53.2k
    gs_transparency_mask_params_t params;
670
53.2k
    gs_rect bbox;
671
672
53.2k
    gs_trans_mask_params_init(&params, TRANSPARENCY_MASK_None);
673
53.2k
    params.replacing = true;
674
675
53.2k
    bbox.p.x = 0;
676
53.2k
    bbox.p.y = 0;
677
53.2k
    bbox.q.x = 0;
678
53.2k
    bbox.q.y = 0;
679
680
53.2k
    return gs_begin_transparency_mask(ctx->pgs, &params, &bbox, false);
681
53.2k
}
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
105k
{
694
105k
    bool is_cmyk;
695
105k
    bool device_transparency = false;
696
697
    /* PS code checks for >= 4 components... */
698
105k
    is_cmyk = ctx->pgs->device->color_info.num_components >= 4;
699
700
105k
    device_transparency = pdfi_device_check_param_bool(ctx->pgs->device, "HaveTransparency");
701
702
105k
    ctx->page.needs_OP = false;
703
105k
    ctx->page.simulate_op = false;
704
105k
    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
105k
    case gs_overprint_control_enable:
727
105k
    default:
728
105k
        if (!is_cmyk || device_transparency)
729
91.7k
            ctx->page.needs_OP = false;
730
13.3k
        else
731
13.3k
            ctx->page.needs_OP = true;
732
105k
        break;
733
105k
    }
734
735
105k
    if(ctx->args.pdfdebug)
736
105k
        dbgmprintf2(ctx->memory, "Page %s Overprint, Simulate is %s\n",
737
105k
                    ctx->page.needs_OP ? "NEEDS" : "does NOT NEED",
738
105k
                    ctx->page.simulate_op ? "TRUE" : "FALSE");
739
105k
}
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
235k
{
744
235k
    gs_color_space_index csi;
745
746
235k
    csi = pdfi_currentcolorspace(ctx, 0);
747
748
235k
    switch (csi) {
749
35.2k
    case gs_color_space_index_DeviceGray:
750
56.7k
    case gs_color_space_index_DeviceCMYK:
751
56.9k
    case gs_color_space_index_DeviceN:
752
57.9k
    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
57.9k
        return true;
762
177k
    default:
763
#if DEBUG_TRANSPARENCY
764
        dbgmprintf1(ctx->memory, "Colorspace is %d, NOT OKAY for OVERPRINT\n", csi);
765
#endif
766
177k
        return false;
767
235k
    }
768
769
0
    return false;
770
235k
}
771
772
int pdfi_trans_setup(pdf_context *ctx, pdfi_trans_state_t *state, gs_rect *bbox,
773
                           pdfi_transparency_caller_t caller)
774
5.84M
{
775
5.84M
    pdfi_int_gstate *igs = (pdfi_int_gstate *)ctx->pgs->client_data;
776
5.84M
    int code;
777
5.84M
    bool stroked_bbox;
778
5.84M
    bool current_overprint;
779
5.84M
    bool okOPcs = false;
780
5.84M
    bool ChangeBM = false;
781
5.84M
    gs_blend_mode_t  mode = gs_currentblendmode(ctx->pgs);  /* quite warning */
782
5.84M
    bool need_group = false;
783
784
5.84M
    memset(state, 0, sizeof(*state));
785
786
5.84M
    if (!ctx->page.has_transparency)
787
4.87M
        return 0;
788
789
965k
    if (ctx->page.needs_OP) {
790
229k
        okOPcs = pdfi_trans_okOPcs(ctx);
791
229k
        if (okOPcs) {
792
57.8k
            if (caller == TRANSPARENCY_Caller_Stroke)
793
20.6k
                current_overprint = gs_currentstrokeoverprint(ctx->pgs);
794
37.2k
            else {
795
37.2k
                current_overprint = gs_currentfilloverprint(ctx->pgs);
796
37.2k
                if (caller == TRANSPARENCY_Caller_FillStroke)
797
1.28k
                    current_overprint |= gs_currentstrokeoverprint(ctx->pgs);
798
37.2k
            }
799
57.8k
            ChangeBM = current_overprint;
800
57.8k
            mode = gs_currentblendmode(ctx->pgs);
801
57.8k
            if (mode != BLEND_MODE_Normal && mode != BLEND_MODE_Compatible)
802
563
                need_group = ChangeBM;
803
57.3k
            else
804
57.3k
                need_group = false;
805
171k
        } else {
806
171k
            need_group = false;
807
171k
        }
808
229k
        need_group = need_group || (igs->SMask != NULL);
809
735k
    } else {
810
735k
        if (caller == TRANSPARENCY_Caller_Image || igs->SMask == NULL)
811
728k
            need_group = false;
812
7.33k
        else
813
7.33k
            need_group = true;
814
735k
    }
815
816
965k
    code = pdfi_trans_set_params(ctx);
817
965k
    if (code != 0)
818
1.73k
        return 0;
819
820
963k
    if (!need_group && !ChangeBM)
821
953k
        return 0;
822
823
    /* TODO: error handling... */
824
10.4k
    if (need_group) {
825
6.22k
        bool isolated = false;
826
6.22k
        mode = gs_currentblendmode(ctx->pgs);
827
828
6.22k
        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.22k
        if (igs->SMask != NULL && mode != BLEND_MODE_Normal && mode != BLEND_MODE_Compatible)
834
3.17k
            isolated = true;
835
6.22k
        code = pdfi_trans_begin_simple_group(ctx, bbox, stroked_bbox, isolated, false);
836
837
        /* Group was not pushed if error */
838
6.22k
        if (code >= 0)
839
4.78k
            state->GroupPushed = true;
840
841
6.22k
        state->saveStrokeAlpha = gs_getstrokeconstantalpha(ctx->pgs);
842
6.22k
        state->saveFillAlpha = gs_getfillconstantalpha(ctx->pgs);
843
6.22k
        code = gs_setfillconstantalpha(ctx->pgs, 1.0);
844
6.22k
        code = gs_setstrokeconstantalpha(ctx->pgs, 1.0);
845
6.22k
    }
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
10.4k
    if (ChangeBM && caller != TRANSPARENCY_Caller_FillStroke) {
852
4.32k
        state->saveBM = mode;
853
4.32k
        state->ChangeBM = true;
854
4.32k
        code = gs_setblendmode(ctx->pgs, BLEND_MODE_CompatibleOverprint);
855
4.32k
    }
856
10.4k
    return code;
857
963k
}
858
859
int pdfi_trans_required(pdf_context *ctx)
860
39.8M
{
861
39.8M
    gs_blend_mode_t mode;
862
863
39.8M
    if (!ctx->page.has_transparency)
864
30.5M
        return 0;
865
866
9.27M
    mode = gs_currentblendmode(ctx->pgs);
867
9.27M
    if ((mode == BLEND_MODE_Normal || mode == BLEND_MODE_Compatible) &&
868
9.27M
        ctx->pgs->fillconstantalpha == 1 &&
869
9.27M
        ctx->pgs->strokeconstantalpha == 1 &&
870
9.27M
        ((pdfi_int_gstate *)ctx->pgs->client_data)->SMask == NULL)
871
9.23M
        return 0;
872
873
43.2k
    return 1;
874
9.27M
}
875
876
int pdfi_trans_setup_text(pdf_context *ctx, pdfi_trans_state_t *state, bool is_show)
877
19.6M
{
878
19.6M
    int Trmode;
879
19.6M
    int code, code1;
880
19.6M
    gs_rect bbox;
881
882
19.6M
    if (!pdfi_trans_required(ctx))
883
19.6M
        return 0;
884
885
16.6k
    Trmode = gs_currenttextrenderingmode(ctx->pgs);
886
16.6k
    code = gs_gsave(ctx->pgs);
887
16.6k
    if (code < 0) goto exit;
888
889
16.6k
    if (is_show) {
890
16.6k
        code = gs_clippath(ctx->pgs);
891
16.6k
    } else {
892
0
        code = gs_strokepath(ctx->pgs);
893
0
    }
894
16.6k
    if (code >= 0)
895
16.6k
        code = gs_upathbbox(ctx->pgs, &bbox, false);
896
16.6k
    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
16.6k
    code1 = gs_grestore(ctx->pgs);
902
16.6k
    if (code == 0) code = code1;
903
16.6k
    if (code < 0) goto exit;
904
905
16.6k
    switch (Trmode) {
906
16.6k
    case 0:
907
16.6k
        code = pdfi_trans_setup(ctx, state, &bbox, TRANSPARENCY_Caller_Fill);
908
16.6k
        break;
909
55
    default:
910
        /* TODO: All the others */
911
55
        code = pdfi_trans_setup(ctx, state, &bbox, TRANSPARENCY_Caller_Fill);
912
55
        break;
913
16.6k
    }
914
915
16.6k
 exit:
916
16.6k
    return code;
917
16.6k
}
918
919
int pdfi_trans_teardown_text(pdf_context *ctx, pdfi_trans_state_t *state)
920
19.6M
{
921
19.6M
    if (!pdfi_trans_required(ctx))
922
19.6M
         return 0;
923
924
16.6k
    return pdfi_trans_teardown(ctx, state);
925
19.6M
}
926
927
int pdfi_trans_teardown(pdf_context *ctx, pdfi_trans_state_t *state)
928
5.84M
{
929
5.84M
    int code = 0;
930
931
5.84M
    if (!ctx->page.has_transparency)
932
4.87M
        return 0;
933
934
965k
    if (state->GroupPushed) {
935
4.78k
        code = pdfi_trans_end_simple_group(ctx);
936
4.78k
        code = gs_setstrokeconstantalpha(ctx->pgs, state->saveStrokeAlpha);
937
4.78k
        code = gs_setfillconstantalpha(ctx->pgs, state->saveFillAlpha);
938
4.78k
    }
939
940
965k
    if (gs_currentblendmode(ctx->pgs) == BLEND_MODE_CompatibleOverprint)
941
4.32k
        code = gs_setblendmode(ctx->pgs, state->saveBM);
942
943
965k
    return code;
944
5.84M
}
945
946
int pdfi_trans_set_params(pdf_context *ctx)
947
1.41M
{
948
1.41M
    int code = 0;
949
1.41M
    pdfi_int_gstate *igs = (pdfi_int_gstate *)ctx->pgs->client_data;
950
1.41M
    gs_transparency_channel_selector_t csel;
951
952
1.41M
    if (ctx->page.has_transparency) {
953
1.22M
        if (gs_getalphaisshape(ctx->pgs))
954
6.87k
            csel = TRANSPARENCY_CHANNEL_Shape;
955
1.22M
        else
956
1.22M
            csel = TRANSPARENCY_CHANNEL_Opacity;
957
1.22M
        if (igs->SMask) {
958
10.0k
            code = pdfi_trans_set_mask(ctx, igs, csel);
959
10.0k
        }
960
1.22M
    }
961
962
1.41M
    return code;
963
1.41M
}