Coverage Report

Created: 2026-07-24 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/devices/vector/gdevpdft.c
Line
Count
Source
1
/* Copyright (C) 2001-2026 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
17
/* transparency processing for PDF-writing driver */
18
#include "gx.h"
19
#include "string_.h"
20
#include "gserrors.h"
21
#include "gstrans.h"
22
#include "gscolor2.h"
23
#include "gzstate.h"
24
#include "gdevpdfx.h"
25
#include "gdevpdfg.h"
26
#include "gdevpdfo.h"
27
#include "gsccolor.h"
28
#include "gsicc_manage.h"
29
#include "gdevpdfc.h"
30
31
static int
32
pdf_make_soft_mask_dict(gx_device_pdf * pdev, const gs_pdf14trans_params_t * pparams)
33
301
{
34
301
    pdf_resource_t *pres_soft_mask_dict = 0;
35
301
    cos_dict_t *soft_mask_dict;
36
301
    int code;
37
38
    /* Fixme : merge redundant objects. */
39
301
    code = pdf_alloc_resource(pdev, resourceSoftMaskDict, gs_no_id, &pres_soft_mask_dict, -1);
40
301
    if (code < 0)
41
0
        return code;
42
301
    cos_become(pres_soft_mask_dict->object, cos_type_dict);
43
301
    pdev->pres_soft_mask_dict = pres_soft_mask_dict;
44
301
    soft_mask_dict = (cos_dict_t *)pres_soft_mask_dict->object;
45
301
    code = cos_dict_put_c_key_string(soft_mask_dict, "/S",
46
301
            pparams->subtype == TRANSPARENCY_MASK_Alpha ? (byte *)"/Alpha" : (byte *)"/Luminosity",
47
301
            pparams->subtype == TRANSPARENCY_MASK_Alpha ? 6 : 11);
48
301
    if (code < 0)
49
0
        return code;
50
301
    if (pparams->Background_components) {
51
206
        cos_array_t *Background;
52
53
206
        Background = cos_array_from_floats(pdev, pparams->Background,
54
206
                    pparams->Background_components, "pdf_write_soft_mask_dict");
55
206
        if (Background == NULL)
56
0
            return_error(gs_error_VMerror);
57
206
        code = cos_dict_put_c_key_object(soft_mask_dict, "/BC", (cos_object_t *)Background);
58
206
        if (code < 0)
59
0
            return code;
60
206
    }
61
301
    if (pdev->CompatibilityLevel <= 1.7 && pparams->transfer_function != NULL && pdev->params.TransferFunctionInfo == tfi_Preserve) {
62
35
        int64_t id;
63
35
        char buf[20];
64
65
35
        code = pdf_write_function(pdev, pparams->transfer_function, &id);
66
35
        if (code < 0)
67
0
            return code;
68
35
        gs_snprintf(buf, sizeof(buf), " %"PRId64" 0 R", id);
69
35
        code = cos_dict_put_c_key_string(soft_mask_dict, "/TR", (const byte *)buf, strlen(buf));
70
35
        if (code < 0)
71
0
            return code;
72
35
    }
73
301
    return 0;
74
75
301
}
76
77
static int WriteDefaultSpaces(gx_device_pdf * pdev, const gs_gstate * pgs, cos_value_t *cs_value)
78
0
{
79
0
    cmm_profile_t *pprofile = NULL;
80
0
    gs_color_space cs, *pcs;
81
0
    gs_color_space_type t;
82
0
    cos_array_t *pca;
83
0
    cos_dict_t *Resources = NULL, *ColorSpaces = NULL;
84
0
    int code = 0;
85
0
    const cos_value_t *pcos = NULL;
86
0
    pdf_color_space_t *ppcs;
87
0
    pdf_resource_t *pres = NULL;
88
0
    gs_color_space_index target;
89
90
    /* Check type before using */
91
0
    if (cs_value->value_type != COS_VALUE_CONST)
92
0
        return_error(gs_error_typecheck);
93
    /* Compare its value to find out which space we want */
94
0
    if (strncmp((const char *)cs_value->contents.chars.data, "/DeviceGray", 11) == 0)
95
0
        target = gs_color_space_index_DeviceGray;
96
0
    else
97
0
        if (strncmp((const char *)cs_value->contents.chars.data, "/DeviceRGB", 10) == 0)
98
0
            target = gs_color_space_index_DeviceRGB;
99
0
        else
100
0
            if (strncmp((const char *)cs_value->contents.chars.data, "/DeviceCMYK", 11) == 0)
101
0
                target = gs_color_space_index_DeviceCMYK;
102
0
            else
103
0
                return_error(gs_error_rangecheck);
104
105
    /* Retrieve/Create the Resources->ColorSpace dictionary in the Pages tree */
106
0
    Resources = (cos_dict_t *)cos_dict_find(pdev->Pages, (const byte *)"/Resources", 10);
107
0
    if (Resources == NULL) {
108
0
        Resources = cos_dict_alloc(pdev, "WriteDefaultSpaces");
109
0
        if (Resources == NULL)
110
0
            return_error(gs_error_undefined);
111
0
        code = cos_dict_put_c_key_object(pdev->Pages, "/Resources", (cos_object_t *)Resources);
112
0
        if (code < 0)
113
0
            return code;
114
0
    }
115
116
    /* Then the ColorSpace sub-dictionary */
117
0
    ColorSpaces = (cos_dict_t *)cos_dict_find(Resources, (const byte *)"/ColorSpace", 11);
118
0
    if(ColorSpaces == NULL) {
119
0
        ColorSpaces = cos_dict_alloc(pdev, "WriteDefaultSpaces");
120
0
        if (ColorSpaces == NULL)
121
0
            return_error(gs_error_undefined);
122
0
        code = cos_dict_put_c_key_object(Resources, "/ColorSpace", (cos_object_t *)ColorSpaces);
123
0
        if (code < 0)
124
0
            return code;
125
0
    }
126
127
    /* Now check to see if we already have a /Default space, if so just exit. */
128
0
    switch(target) {
129
0
        case gs_color_space_index_DeviceGray:
130
0
            pcos = cos_dict_find_c_key(ColorSpaces, "/DefaultGray");
131
0
            break;
132
0
        case gs_color_space_index_DeviceRGB:
133
0
            pcos = cos_dict_find_c_key(ColorSpaces, "/DefaultRGB");
134
0
            break;
135
0
        case gs_color_space_index_DeviceCMYK:
136
0
            pcos = cos_dict_find_c_key(ColorSpaces, "/DefaultCMYK");
137
0
            break;
138
0
        default:
139
0
            return_error(gs_error_rangecheck);
140
0
            break;
141
0
    }
142
    /* If we've already written the relevant Default space, don't write another one! */
143
0
    if (pcos != NULL)
144
0
        return 0;
145
146
    /* This is all a bit hairy, but there seems to be no easy way to achieve this
147
     * Probably could export some static functions from the ICC code, but this works too. Just manufacture
148
     * enough of a colour space to retrieve the ICC profile.
149
     */
150
0
    cs.cmm_icc_profile_data = NULL;
151
0
    t.index = target;
152
0
    cs.type = &t;
153
0
    pprofile = gsicc_get_gscs_profile(&cs, pgs->icc_manager);
154
0
    if (pprofile == NULL)
155
0
        return_error(gs_error_undefined);
156
    /* gsicc_get_gscs_profile() does not count up the returned reference. We
157
     * deliberately don't count it up here; we're only using it temporarily.
158
     */
159
160
    /* Now manufacture a colour space to attach the profile to. */
161
0
    pcs = gs_cspace_new_ICC(pdev->memory, (gs_gstate *)pgs, 4);
162
0
    if (pcs == NULL)
163
0
        return_error(gs_error_undefined);
164
165
0
    switch(target) {
166
0
        case gs_color_space_index_DeviceGray:
167
0
            pcs->ICC_Alternate_space = gs_ICC_Alternate_DeviceGray;
168
0
            break;
169
0
        case gs_color_space_index_DeviceRGB:
170
0
            pcs->ICC_Alternate_space = gs_ICC_Alternate_DeviceRGB;
171
0
            break;
172
0
        case gs_color_space_index_DeviceCMYK:
173
0
            pcs->ICC_Alternate_space = gs_ICC_Alternate_DeviceCMYK;
174
0
            break;
175
0
        default:
176
0
            return_error(gs_error_undefined);
177
0
    }
178
179
0
    pcs->cmm_icc_profile_data = pprofile;
180
181
    /* Create an array. */
182
0
    pca = cos_array_alloc(pdev, "WriteDefaultSpaces");
183
0
    if (pca == 0) {
184
0
        rc_decrement(pcs, "WriteDefaultSpaces");
185
0
        return_error(gs_error_VMerror);
186
0
    }
187
188
    /* Turn the array in to a PDF colour space, using the GS colour space */
189
0
    code = pdf_iccbased_color_space(pdev, pgs, NULL, pcs, pca);
190
0
    if (code < 0) {
191
0
        rc_decrement(pcs, "WriteDefaultSpaces");
192
0
        cos_release((cos_object_t *)pca, "WriteDefaultSpaces");
193
0
        return code;
194
0
    }
195
196
    /*
197
     * Register the color space as a resource, since it must be referenced
198
     * by name rather than directly.
199
     */
200
0
    {
201
0
        if (code < 0 ||
202
0
            (code = pdf_alloc_resource(pdev, resourceColorSpace, pcs->id,
203
0
                                       &pres, -1)) < 0
204
0
            ) {
205
0
            COS_FREE(pca, "pdf_color_space");
206
0
            return code;
207
0
        }
208
0
        pdf_reserve_object_id(pdev, pres, 0);
209
210
0
        switch(target) {
211
0
            case gs_color_space_index_DeviceGray:
212
0
                memcpy(pres->rname, "DefaultGray", 11);
213
0
                pres->rname[11] = 0;
214
0
                break;
215
0
            case gs_color_space_index_DeviceRGB:
216
0
                memcpy(pres->rname, "DefaultRGB", 10);
217
0
                pres->rname[10] = 0;
218
0
                break;
219
0
            case gs_color_space_index_DeviceCMYK:
220
0
                memcpy(pres->rname, "DefaultCMYK", 11);
221
0
                pres->rname[11] = 0;
222
0
                break;
223
0
            default:
224
0
                 return_error(gs_error_undefined);
225
0
        }
226
227
0
        ppcs = (pdf_color_space_t *)pres;
228
0
        ppcs->serialized = 0;
229
0
        ppcs->serialized_size = 0;
230
0
        ppcs->ranges = 0;
231
0
        pca->id = pres->object->id;
232
0
        COS_FREE(pres->object, "pdf_color_space");
233
0
        pres->object = (cos_object_t *)pca;
234
0
        cos_write_object(COS_OBJECT(pca), pdev, resourceColorSpace);
235
0
    }
236
237
0
    code = pdf_add_resource(pdev, Resources, "/ColorSpace", pres);
238
239
0
    rc_decrement(pcs, "WriteDefaultSpaces");
240
0
    cos_release((cos_object_t *)pca, "WriteDefaultSpaces");
241
0
    return code;
242
0
}
243
244
static int
245
pdf_make_group_dict(gx_device_pdf * pdev, const gs_pdf14trans_params_t * pparams,
246
                            const gs_gstate * pgs, cos_dict_t **pdict)
247
10.0k
{
248
10.0k
    pdf_resource_t *pres_group;
249
10.0k
    cos_dict_t *group_dict;
250
10.0k
    int code = 0;
251
10.0k
    cos_value_t cs_value;
252
253
10.0k
    code = pdf_alloc_resource(pdev, resourceGroup, gs_no_id, &pres_group, -1);
254
10.0k
    if (code < 0)
255
0
        return code;
256
10.0k
    cos_become(pres_group->object, cos_type_dict);
257
10.0k
    group_dict = (cos_dict_t *)pres_group->object;
258
10.0k
    code = cos_dict_put_c_key_string(group_dict, "/Type", (const byte *)"/Group", 6);
259
10.0k
    if (code < 0)
260
0
        return code;
261
10.0k
    code = cos_dict_put_c_key_string(group_dict, "/S", (const byte *)"/Transparency", 13);
262
10.0k
    if (code < 0)
263
0
        return code;
264
10.0k
    if (pparams->Isolated) {
265
7.53k
        code = cos_dict_put_c_key_bool(group_dict, "/I", true);
266
7.53k
        if (code < 0)
267
0
            return code;
268
7.53k
    }
269
10.0k
    if (pparams->Knockout) {
270
3
        code = cos_dict_put_c_key_bool(group_dict, "/K", true);
271
3
        if (code < 0)
272
0
            return code;
273
3
    }
274
    /* Note that we should not add in the graphic state
275
       color space for the group color if there was not
276
       a group color specified.
277
       In this case, the parent group is inherited from
278
       the previous group or the device color space */
279
10.0k
    if (pgs != NULL && pparams->ColorSpace != NULL) {
280
2.61k
        const gs_color_space *cs = pparams->ColorSpace;
281
282
2.61k
        if (pparams->ColorSpace == NULL)
283
0
            code = pdf_color_space_named(pdev, pgs, &cs_value, NULL, cs,
284
0
                    &pdf_color_space_names, false, NULL, 0, false);
285
2.61k
        else
286
2.61k
            code = pdf_color_space_named(pdev, pgs, &cs_value, NULL, pparams->ColorSpace,
287
2.61k
                    &pdf_color_space_names, false, NULL, 0, false);
288
2.61k
        if (code < 0)
289
0
            return code;
290
2.61k
        switch(pdev->params.BlendConversionStrategy) {
291
0
            case bcs_Managed:
292
0
                if (pdev->params.ColorConversionStrategy != ccs_LeaveColorUnchanged) {
293
0
                    if (pdev->params.ColorConversionStrategy < ccs_CMYK || pdev->params.ColorConversionStrategy == ccs_ByObjectType) {
294
0
                        dmprintf(pdev->memory, "\tCannot use DeviceIndependentColor for a Blending colour space.\n\tCannot use /Managed or /Simple replacement strategy with DeviceIndependentColor.\n\tChange ColorConversionStrategy or BlendConversionStrategy.\n\n");
295
0
                        return_error(gs_error_rangecheck);
296
0
                    }
297
0
                    code = WriteDefaultSpaces(pdev, pgs, &cs_value);
298
0
                }
299
0
                if (code < 0)
300
0
                    return code;
301
                /* Fall through */
302
0
            case bcs_None:
303
0
                code = cos_dict_put_c_key(group_dict, "/CS", &cs_value);
304
0
                break;
305
2.61k
            case bcs_Simple:
306
2.61k
                switch(pdev->params.ColorConversionStrategy) {
307
2.61k
                    case ccs_LeaveColorUnchanged:
308
2.61k
                        code = cos_dict_put_c_key(group_dict, "/CS", &cs_value);
309
2.61k
                        break;
310
0
                    case ccs_UseDeviceIndependentColor:
311
0
                    case ccs_UseDeviceIndependentColorForImages:
312
0
                    case ccs_ByObjectType:
313
0
                    case ccs_sRGB:
314
0
                        dmprintf(pdev->memory, "\tCannot use DeviceIndependentColor for a Blending colour space.\n\tCannot use /Managed or /Simple replacement strategy with DeviceIndependentColor.\n\tChange ColorConversionStrategy or BlendConversionStrategy.\n\n");
315
0
                        return_error(gs_error_rangecheck);
316
0
                        break;
317
318
0
                    case ccs_CMYK:
319
0
                        code = cos_dict_put_c_key_string(group_dict, "/CS", (const byte *)"/DeviceCMYK", 11);
320
0
                        break;
321
0
                    case ccs_Gray:
322
0
                        code = cos_dict_put_c_key_string(group_dict, "/CS", (const byte *)"/DeviceGray", 11);
323
0
                        break;
324
0
                    case ccs_RGB:
325
0
                        code = cos_dict_put_c_key_string(group_dict, "/CS", (const byte *)"/DeviceRGB", 10);
326
0
                        break;
327
2.61k
                }
328
2.61k
                break;
329
2.61k
        }
330
2.61k
    }
331
10.0k
    if (code < 0)
332
0
        return code;
333
10.0k
    group_dict = NULL; /* The next line invalidates it. */
334
10.0k
    code = pdf_substitute_resource(pdev, &pres_group, resourceGroup, NULL, false);
335
10.0k
    if (code < 0)
336
0
        return code;
337
10.0k
    pres_group->where_used |= pdev->used_mask;
338
10.0k
    *pdict = (cos_dict_t *)pres_group->object;
339
10.0k
    return 0;
340
10.0k
}
341
342
static int
343
pdf_make_form_dict(gx_device_pdf * pdev, const gs_pdf14trans_params_t * pparams,
344
                            const gs_gstate * pgs,
345
                            const cos_dict_t *group_dict, cos_dict_t *form_dict)
346
3.24k
{
347
3.24k
    cos_array_t *bbox_array;
348
3.24k
    float bbox[4];
349
3.24k
    gs_rect bbox_rect;
350
3.24k
    int code;
351
352
3.24k
    code = gs_bbox_transform(&pparams->bbox, &ctm_only(pgs), &bbox_rect);
353
3.24k
    if (code < 0)
354
0
        return code;
355
3.24k
    bbox[0] = bbox_rect.p.x;
356
3.24k
    bbox[1] = bbox_rect.p.y;
357
3.24k
    bbox[2] = bbox_rect.q.x;
358
3.24k
    bbox[3] = bbox_rect.q.y;
359
3.24k
    code = cos_dict_put_c_key_string(form_dict, "/Type", (const byte *)"/XObject", 8);
360
3.24k
    if (code < 0)
361
0
        return code;
362
3.24k
    code = cos_dict_put_c_key_string(form_dict, "/Subtype", (const byte *)"/Form", 5);
363
3.24k
    if (code < 0)
364
0
        return code;
365
3.24k
    code = cos_dict_put_c_key_int(form_dict, "/FormType", 1);
366
3.24k
    if (code < 0)
367
0
        return code;
368
3.24k
    code = cos_dict_put_c_key_string(form_dict, "/Matrix", (const byte *)"[1 0 0 1 0 0]", 13);
369
3.24k
    if (code < 0)
370
0
        return code;
371
3.24k
    bbox_array = cos_array_from_floats(pdev, bbox, 4, "pdf_begin_transparency_group");
372
3.24k
    if (bbox_array == NULL)
373
0
        return_error(gs_error_VMerror);
374
3.24k
    code = cos_dict_put_c_key_object(form_dict, "/BBox", (cos_object_t *)bbox_array);
375
3.24k
    if (code < 0)
376
0
        return code;
377
3.24k
    if (pdev->PendingOC != 0) {
378
29
        if (pdev->CompatibilityLevel < 1.4999) {
379
0
            if (pdev->PDFA) {
380
0
                switch (pdev->PDFACompatibilityPolicy) {
381
0
                    case 0:
382
0
                        emprintf(pdev->memory,
383
0
                                 "Optional Content not valid in this version of PDF, reverting to normal PDF output\n");
384
0
                        pdev->AbortPDFAX = true;
385
0
                        pdev->PDFA = 0;
386
0
                        break;
387
0
                    case 1:
388
0
                        emprintf(pdev->memory,
389
0
                                 "Optional Content not valid in this version of PDF. Dropping feature to preserve PDF/A compatibility\n");
390
0
                        break;
391
0
                    case 2:
392
0
                        emprintf(pdev->memory,
393
0
                                 "Optional Content not valid in this version of PDF,  aborting conversion\n");
394
0
                        return_error (gs_error_typecheck);
395
0
                        break;
396
0
                    default:
397
0
                        emprintf(pdev->memory,
398
0
                                 "Optional Content not valid in this version of PDF, unrecognised PDFACompatibilityLevel,\nreverting to normal PDF output\n");
399
0
                        pdev->AbortPDFAX = true;
400
0
                        pdev->PDFA = 0;
401
0
                        break;
402
0
                }
403
0
            } else {
404
0
                emprintf(pdev->memory,
405
0
                         "Optional Content not valid in this version of PDF. Dropping feature to preserve compatibility\n");
406
0
            }
407
29
        } else {
408
29
            char str[256];
409
29
            gs_param_string param;
410
29
            cos_object_t *pco = NULL;
411
412
29
            param.data = (const byte *)pdev->PendingOC;
413
29
            param.size = strlen(pdev->PendingOC);
414
29
            code = pdf_refer_named(pdev, &param, &pco);
415
29
            if(code < 0)
416
0
                return code;
417
418
29
            gs_snprintf(str, sizeof(str), "%"PRId64" 0 R", pco->id);
419
29
            code = cos_dict_put_string_copy(form_dict, "/OC", str);
420
29
            if (code < 0)
421
0
                return code;
422
423
29
            gs_free_object(pdev->memory->non_gc_memory, pdev->PendingOC, "");
424
29
            pdev->PendingOC = NULL;
425
29
        }
426
29
    }
427
3.24k
    return cos_dict_put_c_key_object(form_dict, "/Group", (cos_object_t *)group_dict);
428
3.24k
}
429
430
static int
431
pdf_begin_transparency_group(gs_gstate * pgs, gx_device_pdf * pdev,
432
                                const gs_pdf14trans_params_t * pparams, bool page_group)
433
10.0k
{
434
10.0k
    cos_dict_t *group_dict;
435
10.0k
    int code;
436
437
10.0k
    if (pgs == NULL)
438
0
        return_error(gs_error_unregistered); /* Must not happen. */
439
10.0k
    code = pdf_make_group_dict(pdev, pparams, pgs, &group_dict);
440
10.0k
    if (code < 0)
441
0
        return code;
442
10.0k
    code = pdf_open_page(pdev, PDF_IN_STREAM);
443
10.0k
    if (code < 0)
444
0
        return code;
445
10.0k
    code = pdf_check_soft_mask(pdev, pgs);
446
10.0k
    if (code < 0)
447
0
        return code;
448
10.0k
    if (pdf_must_put_clip_path(pdev, pgs->clip_path)) {
449
1.74k
        code = pdf_put_clip_path(pdev, pgs->clip_path);
450
1.74k
        if (code < 0)
451
0
            return code;
452
1.74k
    }
453
10.0k
    if (page_group)
454
937
        pdev->pages[pdev->next_page].group_id = group_dict->id;
455
9.15k
    else if (pparams->image_with_SMask) {
456
        /* An internal group for the image implementation.
457
           See doimagesmask in gs/lib/pdf_draw.ps .
458
           Just set a flag for skipping pdf_end_transparency_group. */
459
5.91k
        pdev->image_with_SMask |= 1 << ++pdev->FormDepth;
460
5.91k
        pdev->PatternsSinceForm = 0;
461
5.91k
    } else {
462
3.24k
        pdf_resource_t *pres, *pres_gstate = NULL;
463
3.24k
        cos_dict_t *pcd = NULL, *pcd_Resources = NULL;
464
465
3.24k
        code = pdf_prepare_drawing(pdev, pgs, &pres_gstate, false);
466
3.24k
        if (code < 0)
467
0
            return code;
468
3.24k
        code = pdf_end_gstate(pdev, pres_gstate);
469
3.24k
        if (code < 0)
470
0
            return code;
471
3.24k
        code = pdf_enter_substream(pdev, resourceXObject,
472
3.24k
                gs_no_id, &pres, false, pdev->params.CompressPages);
473
3.24k
        if (code < 0)
474
0
            return code;
475
3.24k
        pdev->FormDepth++;
476
3.24k
        pdev->PatternsSinceForm = 0;
477
3.24k
        code = pdf_make_form_dict(pdev, pparams, pgs, group_dict, (cos_dict_t *)pres->object);
478
3.24k
        if (code < 0)
479
0
            return code;
480
481
        /* Create a Resources dictionary and add it to the form dictionary */
482
3.24k
        pcd = cos_stream_dict((cos_stream_t *)pres->object);
483
3.24k
        pcd_Resources = cos_dict_alloc(pdev, "pdf_group(Resources)");
484
3.24k
        if (pcd == NULL || pcd_Resources == NULL)
485
0
            return_error(gs_error_VMerror);
486
3.24k
        code = cos_dict_put_c_key_object(pcd, "/Resources", COS_OBJECT(pcd_Resources));
487
3.24k
        pdev->substream_Resources = pcd_Resources;
488
3.24k
        return code;
489
3.24k
    }
490
6.85k
    return 0;
491
10.0k
}
492
493
static int
494
pdf_end_transparency_group(gs_gstate * pgs, gx_device_pdf * pdev)
495
9.79k
{
496
9.79k
    int bottom = (pdev->ResourcesBeforeUsage ? 1 : 0);
497
498
9.79k
    if (!is_in_page(pdev) && pdev->sbstack_depth == 0)
499
0
        return 0; /* A Group definition at the page level, handled separately. */
500
9.79k
    if (pdev->image_with_SMask & (1 << pdev->FormDepth)) {
501
        /* An internal group for the image implementation.
502
           See pdf_begin_transparency_group. */
503
5.91k
        pdev->image_with_SMask &= ~(1 << pdev->FormDepth--);
504
5.91k
        pdev->PatternsSinceForm = 0;
505
5.91k
        return 0;
506
5.91k
    } else if (pdev->sbstack_depth == bottom) {
507
        /* We're closing the page group. */
508
937
        if (pdev->pages[pdev->next_page].group_id == 0)
509
0
            return_error(gs_error_unregistered); /* Must not happen. */
510
937
        return 0;
511
2.93k
    } else {
512
2.93k
        pdf_resource_t *pres = pdev->accumulating_substream_resource;
513
2.93k
        int code;
514
2.93k
        uint ignore;
515
516
2.93k
        if (pres == NULL)
517
0
            return_error(gs_error_unregistered);
518
2.93k
        pdev->FormDepth--;
519
2.93k
        pdev->PatternsSinceForm = 0;
520
2.93k
        code = pdf_exit_substream(pdev);
521
2.93k
        if (code < 0)
522
0
            return code;
523
2.93k
        code = pdf_substitute_resource(pdev, &pres, resourceXObject, NULL, false);
524
2.93k
        if (code < 0)
525
0
            return code;
526
        /* We need to update the 'where_used' field, in case we substituted a resource */
527
2.93k
        pres->where_used |= pdev->used_mask;
528
2.93k
        sputc(pdev->strm,'/');
529
2.93k
        sputs(pdev->strm, (const byte *)pres->rname, strlen(pres->rname), &ignore);
530
2.93k
        sputs(pdev->strm, (const byte *)" Do\n", 4, &ignore);
531
2.93k
        code = pdf_add_resource(pdev, pdev->substream_Resources, "/XObject", pres);
532
2.93k
        return code;
533
2.93k
    }
534
9.79k
}
535
536
static int
537
pdf_begin_transparency_mask(gs_gstate * pgs, gx_device_pdf * pdev,
538
                                const gs_pdf14trans_params_t * pparams)
539
3.53k
{
540
3.53k
    if (pparams->subtype == TRANSPARENCY_MASK_None) {
541
3.23k
        int code;
542
3.23k
        pdf_resource_t *pres = 0L;
543
544
        /* reset the soft mask ID. Apparently this is only used by pdfwrite, if we don't
545
         * reset it, then the pdf_prepare_drawing code doesn't know that the SMask has
546
         * changed, and so doesn't write out the GState
547
         */
548
3.23k
        pgs->soft_mask_id = 0;
549
3.23k
        code = pdf_prepare_drawing(pdev, pgs, &pres, false);
550
3.23k
        if (code == gs_error_interrupt) {
551
            /* */
552
            /* Not in an appropriate context.  Do not restore the soft_mask_id.
553
               Otherwise any group push that occurs following this will use that
554
               softmask, which clearly should be NONE here.
555
             */
556
            /* ignore return code, we don't care about this graphics state as we aren't
557
             * emitting it anyway
558
             */
559
582
            pdf_end_gstate(pdev, pres);
560
582
            return 0;
561
582
        }
562
2.64k
        if (code < 0)
563
0
            return code;
564
2.64k
        code = pdf_end_gstate(pdev, pres);
565
2.64k
        if (code < 0)
566
0
            return code;
567
2.64k
        return 0;
568
2.64k
    }
569
301
    if (pparams->mask_is_image) {
570
        /* HACK :
571
            The control comes here when
572
            the PDF interpreter will make the PS interpreter
573
            to interprete the mask for filling the transparency buffer
574
            with an SMask image.
575
            Since we handle Type 3 images as a high level objects,
576
            we don't install the transparency buffer here
577
            and need to skip the image enumeration for the SMask.
578
            However we have no right method for skipping
579
            an image enumeration due to possible side effect
580
            of the image data proc in Postscript language.
581
            Therefore we do enumerate the image mask and accumulate
582
            it as a PDF stream, but don't create a reference to it.
583
            Later it will be enumerated once again as a part of SMask-ed image,
584
            and the pdfwrite image handler will recognize duplicated images
585
            and won't create the second stream for same image.
586
587
            We could make a special workaround for
588
            skipping mask images either in the graphics library or
589
            in the PS code of the PDF interpreter,
590
            but we don't want to complicate things now.
591
            The performance leak for the second enumeration
592
            shouldn't be harmful.
593
594
            So now just set a flag for pdf_end_and_do_image.
595
        */
596
0
        pdev->image_mask_skip = true;
597
0
        return 0;
598
301
    } else {
599
301
        int code;
600
601
301
        pdev->smask_construction = true;
602
301
        code = pdf_make_soft_mask_dict(pdev, pparams);
603
301
        if (code < 0)
604
0
            return code;
605
301
        code = pdf_open_page(pdev, PDF_IN_STREAM);
606
301
        if (code < 0)
607
0
            return code;
608
301
        return pdf_begin_transparency_group(pgs, pdev, pparams, 0);
609
301
    }
610
301
}
611
612
static int
613
pdf_end_transparency_mask(gs_gstate * pgs, gx_device_pdf * pdev,
614
                                const gs_pdf14trans_params_t * pparams)
615
301
{
616
301
    pdev->smask_construction = false;
617
301
    if (pdev->image_mask_skip)
618
0
        pdev->image_mask_skip = false;
619
301
    else {
620
301
        pdf_resource_t *pres = pdev->accumulating_substream_resource;
621
301
        int code;
622
301
        char buf[20];
623
624
301
        if (pres == NULL)
625
0
            return_error(gs_error_unregistered);
626
301
        code = pdf_exit_substream(pdev);
627
301
        if (code < 0)
628
0
            return code;
629
301
        code = pdf_substitute_resource(pdev, &pres, resourceXObject, NULL, false);
630
301
        if (code < 0)
631
0
            return 0;
632
        /* We need to update the 'where_used' field, in case we substituted a resource */
633
301
        pres->where_used |= pdev->used_mask;
634
301
        gs_snprintf(buf, sizeof(buf), "%"PRId64" 0 R", pdf_resource_id(pres));
635
301
        if (pdev->pres_soft_mask_dict == 0L) {
636
            /* something went horribly wrong, we have an 'end' wihtout a matching 'begin'
637
             * Give up, throw an error.
638
             */
639
0
            return_error(gs_error_undefined);
640
0
        }
641
301
        code = cos_dict_put_c_key_string((cos_dict_t *)pdev->pres_soft_mask_dict->object,
642
301
                "/G", (const byte *)buf, strlen(buf));
643
301
        if (code < 0)
644
0
            return code;
645
301
        code = pdf_substitute_resource(pdev, &pdev->pres_soft_mask_dict,
646
301
                                        resourceSoftMaskDict, NULL, false);
647
301
        if (code < 0)
648
0
            return code;
649
301
        pdev->pres_soft_mask_dict->where_used |= pdev->used_mask;
650
301
        pgs->soft_mask_id = pdev->pres_soft_mask_dict->object->id;
651
301
        pdev->pres_soft_mask_dict = NULL;
652
        /* We called pdf_start_trnasparency_group (see pdf_begin_transparency_mask
653
         * above) but we don't call pdf_end_transparency_group, so we must reduce
654
         * the FormDepth ourselves.
655
         */
656
301
        pdev->FormDepth--;
657
301
        pdev->PatternsSinceForm = 0;
658
301
    }
659
301
    return 0;
660
301
}
661
662
static int
663
pdf_set_blend_params(gs_gstate * pgs, gx_device_pdf * dev,
664
                                const gs_pdf14trans_params_t * pparams)
665
0
{
666
0
    return 0;
667
0
}
668
669
int
670
gdev_pdf_composite(gx_device *dev,
671
    gx_device **pcdev, const gs_composite_t *pct,
672
    gs_gstate *pgs, gs_memory_t *memory, gx_device *cdev)
673
427k
{
674
427k
    gx_device_pdf *pdev = (gx_device_pdf *)dev;
675
676
427k
    if (pdev->HaveTransparency && pdev->CompatibilityLevel >= 1.4 &&
677
415k
            pct->type->comp_id == GX_COMPOSITOR_PDF14_TRANS &&
678
181k
            pdev->PDFA != 1) {
679
181k
        gs_pdf14trans_t *pcte = (gs_pdf14trans_t *)pct;
680
181k
        gs_pdf14trans_params_t *params = &pcte->params;
681
682
181k
        *pcdev = dev;
683
181k
        switch(params->pdf14_op) {
684
1.84k
            case PDF14_PUSH_DEVICE:
685
1.84k
                return 0;
686
1.84k
            case PDF14_POP_DEVICE:
687
1.84k
                return 0;
688
0
            case PDF14_ABORT_DEVICE:
689
0
                return 0;
690
937
            case PDF14_BEGIN_TRANS_PAGE_GROUP:
691
937
                return pdf_begin_transparency_group(pgs, pdev, params, 1);
692
8.85k
            case PDF14_BEGIN_TRANS_GROUP:
693
8.85k
                return pdf_begin_transparency_group(pgs, pdev, params, 0);
694
9.79k
            case PDF14_END_TRANS_GROUP:
695
9.79k
                return pdf_end_transparency_group(pgs, pdev);
696
75.5k
            case PDF14_BEGIN_TRANS_TEXT_GROUP:
697
75.5k
                return 0;
698
78.5k
            case PDF14_END_TRANS_TEXT_GROUP:
699
78.5k
                return 0;
700
3.53k
            case PDF14_BEGIN_TRANS_MASK:
701
3.53k
                return pdf_begin_transparency_mask(pgs, pdev, params);
702
301
            case PDF14_END_TRANS_MASK:
703
301
                return pdf_end_transparency_mask(pgs, pdev, params);
704
0
            case PDF14_SET_BLEND_PARAMS:
705
0
                return pdf_set_blend_params(pgs, pdev, params);
706
0
            case PDF14_PUSH_TRANS_STATE:
707
0
                return 0;
708
163
            case PDF14_POP_TRANS_STATE:
709
163
                return 0;
710
301
            case PDF14_PUSH_SMASK_COLOR:
711
301
                return 0;
712
301
            case PDF14_POP_SMASK_COLOR:
713
301
                return 0;
714
715
0
            default :
716
0
                return_error(gs_error_unregistered); /* Must not happen. */
717
181k
        }
718
0
        return 0;
719
181k
    }
720
245k
    return psdf_composite(dev, pcdev, pct, pgs, memory, cdev);
721
427k
}
722
723
/* We're not sure why the folllowing device methods are never called.
724
   Stub them for a while. */
725
726
int
727
gdev_pdf_begin_transparency_group(gx_device *dev,
728
    const gs_transparency_group_params_t *ptgp,
729
    const gs_rect *pbbox,
730
    gs_gstate *pgs, gs_memory_t *mem)
731
0
{
732
0
    return 0;
733
0
}
734
735
int
736
gdev_pdf_end_transparency_group(gx_device *dev,
737
    gs_gstate *pgs)
738
0
{
739
0
    return 0;
740
0
}
741
742
int
743
gdev_pdf_begin_transparency_mask(gx_device *dev,
744
    const gx_transparency_mask_params_t *ptmp,
745
    const gs_rect *pbbox,
746
    gs_gstate *pgs, gs_memory_t *mem)
747
0
{
748
0
    return 0;
749
0
}
750
751
int
752
gdev_pdf_end_transparency_mask(gx_device *dev,
753
    gs_gstate *pgs)
754
0
{
755
0
    return 0;
756
0
}