Coverage Report

Created: 2025-06-10 07:27

/src/ghostpdl/pdf/pdf_optcontent.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
/* Optional Content routines */
17
18
#include "pdf_int.h"
19
#include "pdf_stack.h"
20
#include "pdf_misc.h"
21
#include "pdf_font_types.h"
22
#include "pdf_gstate.h"
23
#include "pdf_dict.h"
24
#include "pdf_array.h"
25
#include "pdf_doc.h"
26
#include "pdf_mark.h"
27
#include "pdf_optcontent.h"
28
#include "pdf_loop_detect.h"
29
30
31
/* Find the default value for an ocdict, based on contents of OCProperties */
32
/* NOTE: the spec says that if BaseState is present, it won't be set to "OFF",
33
 * but this doesn't seem to be the case (Bug 691491).  Also, the spec
34
 * says the ON and OFF arrays are redundant in certain cases.  We just
35
 * look at everything anyway.
36
 * Default is going to be visible unless anything here indicates that it
37
 * should be turned off.
38
 */
39
static bool
40
pdfi_get_default_OCG_val(pdf_context *ctx, pdf_dict *ocdict)
41
65.3k
{
42
65.3k
    bool is_visible = true;
43
65.3k
    pdf_dict *D = NULL;
44
65.3k
    pdf_obj *BaseState = NULL;
45
65.3k
    pdf_array *OFF = NULL;
46
65.3k
    pdf_array *ON = NULL;
47
65.3k
    int code;
48
49
65.3k
    if (ctx->OCProperties == NULL)
50
3.45k
        return is_visible;
51
52
61.9k
    code = pdfi_dict_knownget_type(ctx, ctx->OCProperties, "D", PDF_DICT, (pdf_obj **)&D);
53
61.9k
    if (code <= 0)
54
15
        goto cleanup;
55
56
61.8k
    code = pdfi_dict_knownget_type(ctx, D, "BaseState", PDF_NAME, &BaseState);
57
61.8k
    if (code < 0) {
58
0
        goto cleanup;
59
0
    }
60
61.8k
    if (code > 0) {
61
28
        if (pdfi_name_is((pdf_name *)BaseState, "OFF")) {
62
0
            is_visible = false;
63
0
        }
64
28
    }
65
66
61.8k
    if (!is_visible) {
67
0
        code = pdfi_dict_knownget_type(ctx, D, "ON", PDF_ARRAY, (pdf_obj **)&ON);
68
0
        if (code < 0)
69
0
            goto cleanup;
70
0
        if (code > 0) {
71
0
            if (pdfi_array_known(ctx, ON, (pdf_obj *)ocdict, NULL))
72
0
                is_visible = true;
73
0
        }
74
0
    }
75
76
61.8k
    if (is_visible) {
77
61.8k
        code = pdfi_dict_knownget_type(ctx, D, "OFF", PDF_ARRAY, (pdf_obj **)&OFF);
78
61.8k
        if (code < 0)
79
1.26k
            goto cleanup;
80
60.6k
        if (code > 0) {
81
59.6k
            if (pdfi_array_known(ctx, OFF, (pdf_obj *)ocdict, NULL))
82
57.7k
                is_visible = false;
83
59.6k
        }
84
60.6k
    }
85
86
87
61.9k
 cleanup:
88
61.9k
    pdfi_countdown(BaseState);
89
61.9k
    pdfi_countdown(D);
90
61.9k
    pdfi_countdown(OFF);
91
61.9k
    pdfi_countdown(ON);
92
61.9k
    return is_visible;
93
61.8k
}
94
95
/* Check Usage for an OCG */
96
static bool
97
pdfi_oc_check_OCG_usage(pdf_context *ctx, pdf_dict *ocdict)
98
3.79k
{
99
3.79k
    bool is_visible = true;
100
3.79k
    int code;
101
3.79k
    pdf_dict *Usage = NULL;
102
3.79k
    pdf_dict *dict = NULL;
103
3.79k
    pdf_obj *name = NULL;
104
105
    /* Check Usage to see if it has additional info */
106
3.79k
    code = pdfi_dict_knownget_type(ctx, ocdict, "Usage", PDF_DICT, (pdf_obj **)&Usage);
107
3.79k
    if (code <= 0) {
108
        /* No Usage, so we're done */
109
2.83k
        goto cleanup;
110
2.83k
    }
111
112
965
    if (ctx->args.printed) {
113
965
        code = pdfi_dict_knownget_type(ctx, Usage, "Print", PDF_DICT, (pdf_obj **)&dict);
114
965
        if (code <= 0)
115
950
            goto cleanup;
116
15
        code = pdfi_dict_knownget_type(ctx, dict, "PrintState", PDF_NAME, &name);
117
15
        if (code <= 0)
118
0
            goto cleanup;
119
15
    } else {
120
0
        code = pdfi_dict_knownget_type(ctx, Usage, "View", PDF_DICT, (pdf_obj **)&dict);
121
0
        if (code <= 0)
122
0
            goto cleanup;
123
0
        code = pdfi_dict_knownget_type(ctx, dict, "ViewState", PDF_NAME, &name);
124
0
        if (code <= 0)
125
0
            goto cleanup;
126
0
    }
127
15
    if (pdfi_name_strcmp((pdf_name *)name, "OFF") == 0) {
128
0
        is_visible = false;
129
0
    }
130
131
3.79k
 cleanup:
132
3.79k
    pdfi_countdown(Usage);
133
3.79k
    pdfi_countdown(dict);
134
3.79k
    pdfi_countdown(name);
135
136
3.79k
    return is_visible;
137
15
}
138
139
typedef enum {
140
    P_AnyOn,
141
    P_AllOn,
142
    P_AllOff,
143
    P_AnyOff
144
} ocmd_p_type;
145
146
static bool pdfi_oc_default_visibility(pdf_context *ctx)
147
3.35k
{
148
3.35k
    int code;
149
3.35k
    pdf_dict *D_dict = NULL;
150
3.35k
    pdf_name *B = NULL;
151
152
3.35k
    code = pdfi_dict_knownget_type(ctx, ctx->OCProperties, "D", PDF_DICT, (pdf_obj **)&D_dict);
153
3.35k
    if (code < 0 || D_dict == NULL)
154
198
        return true;
155
156
3.15k
    code = pdfi_dict_knownget_type(ctx, D_dict, "BaseState", PDF_NAME, (pdf_obj **)&B);
157
3.15k
    (void)pdfi_countdown(D_dict);
158
3.15k
    D_dict = NULL;
159
3.15k
    if (code < 0 || B == NULL)
160
3.15k
        return true;
161
162
0
    if (pdfi_name_is(B, "OFF")) {
163
0
        (void)pdfi_countdown(B);
164
0
        return false;
165
0
    }
166
167
0
    (void)pdfi_countdown(B);
168
0
    return true;
169
0
}
170
171
static bool
172
pdfi_oc_check_OCMD_array(pdf_context *ctx, pdf_array *array, ocmd_p_type type)
173
64.4k
{
174
64.4k
    bool is_visible, hit = false;
175
64.4k
    uint64_t i;
176
64.4k
    int code;
177
64.4k
    pdf_obj *val = NULL;
178
179
    /* Setup default */
180
64.4k
    switch (type) {
181
64.4k
    case P_AnyOn:
182
64.4k
    case P_AnyOff:
183
64.4k
        is_visible = false;
184
64.4k
        break;
185
0
    case P_AllOn:
186
0
    case P_AllOff:
187
0
        is_visible = true;
188
0
        break;
189
64.4k
    }
190
191
125k
    for (i=0; i<pdfi_array_size(array); i++) {
192
64.4k
        bool vis;
193
194
64.4k
        code = pdfi_array_get(ctx, array, i, &val);
195
64.4k
        if (code < 0) continue;
196
61.4k
        if (pdfi_type_of(val) != PDF_DICT) {
197
194
            dbgprintf1("WARNING: OCMD array contains item type %d, expected PDF_DICT or PDF_NULL\n", pdfi_type_of(val));
198
194
            pdfi_countdown(val);
199
194
            val = NULL;
200
194
            continue;
201
194
        }
202
203
61.2k
        hit = true;
204
61.2k
        vis = pdfi_get_default_OCG_val(ctx, (pdf_dict *)val);
205
61.2k
        switch (type) {
206
61.2k
        case P_AnyOn:
207
            /* visible if any is on */
208
61.2k
            if (vis) {
209
3.69k
                is_visible = true;
210
3.69k
                goto cleanup;
211
3.69k
            }
212
57.6k
            break;
213
57.6k
        case P_AllOn:
214
            /* visible if all on */
215
0
            if (!vis) {
216
0
                is_visible = false;
217
0
                goto cleanup;
218
0
            }
219
0
            break;
220
0
        case P_AllOff:
221
            /* visible if all are off */
222
0
            if (vis) {
223
0
                is_visible = false;
224
0
                goto cleanup;
225
0
            }
226
0
            break;
227
0
        case P_AnyOff:
228
            /* visible if any is off */
229
0
            if (!vis) {
230
0
                is_visible = true;
231
0
                goto cleanup;
232
0
            }
233
0
            break;
234
61.2k
        }
235
57.6k
        pdfi_countdown(val);
236
57.6k
        val = NULL;
237
57.6k
    }
238
239
    /* If the array was empty, or contained only null or deleted entries, then it has no effect
240
     * PDF Reference 1.7 p366, table 4.49, OCGs entry. I'm interpreting this to mean that we should use
241
     * the OCProperties 'D' dictionary, set the visibility state to the BaseState. Since this is an OCMD
242
     * I'm assuming that the ON and OFF arrays (which apply to Optional Content Groups) shouold not be
243
     * consulted. We need to cater for the fact that BaseState is optional (but default is ON). D is
244
     * specified as 'Required' but it seems best not to take any chances on that!
245
     */
246
60.8k
    if (!hit)
247
3.20k
        is_visible = pdfi_oc_default_visibility(ctx);
248
249
64.4k
cleanup:
250
64.4k
    pdfi_countdown(val);
251
64.4k
    return is_visible;
252
60.8k
}
253
254
static bool
255
pdfi_oc_check_OCMD(pdf_context *ctx, pdf_dict *ocdict)
256
65.1k
{
257
65.1k
    bool is_visible = true;
258
65.1k
    int code;
259
65.1k
    pdf_obj *VE = NULL;
260
65.1k
    pdf_obj *obj = NULL;
261
65.1k
    pdf_obj *Pname = NULL;
262
65.1k
    pdf_dict *OCGs_dict = NULL; /* alias, don't need to free */
263
65.1k
    pdf_array *OCGs_array = NULL; /* alias, don't need to free */
264
65.1k
    pdf_dict *UsageDict = NULL, *StateDict = NULL;
265
65.1k
    pdf_obj *State = NULL;
266
65.1k
    ocmd_p_type Ptype = P_AnyOn;
267
268
    /* TODO: We don't support this, so log a warning and ignore */
269
65.1k
    code = pdfi_dict_knownget_type(ctx, ocdict, "VE", PDF_ARRAY, &VE);
270
65.1k
    if (code > 0) {
271
220
        dbgprintf("WARNING: OCMD contains VE, which is not supported (ignoring)\n");
272
220
    }
273
274
65.1k
    code = pdfi_dict_knownget(ctx, ocdict, "OCGs", &obj);
275
65.1k
    if (code <= 0)
276
298
        goto cleanup;
277
64.8k
    if (pdfi_type_of(obj) == PDF_ARRAY) {
278
64.4k
        OCGs_array = (pdf_array *)obj;
279
64.4k
    } else if (pdfi_type_of(obj) == PDF_DICT) {
280
211
        OCGs_dict = (pdf_dict *)obj;
281
211
    } else {
282
149
        is_visible = pdfi_oc_default_visibility(ctx);
283
149
        goto cleanup;
284
149
    }
285
286
64.7k
    code = pdfi_dict_knownget_type(ctx, ocdict, "P", PDF_NAME, &Pname);
287
64.7k
    if (code < 0)
288
0
        goto cleanup;
289
64.7k
    if (code == 0 || pdfi_name_is((pdf_name *)Pname, "AnyOn")) {
290
64.7k
        Ptype = P_AnyOn;
291
64.7k
    } else if (pdfi_name_is((pdf_name *)Pname, "AllOn")) {
292
0
        Ptype = P_AllOn;
293
0
    } else if (pdfi_name_is((pdf_name *)Pname, "AnyOff")) {
294
0
        Ptype = P_AnyOff;
295
0
    } else if (pdfi_name_is((pdf_name *)Pname, "AllOff")) {
296
0
        Ptype = P_AllOff;
297
0
    } else {
298
0
        Ptype = P_AnyOn;
299
0
    }
300
301
64.7k
    if (OCGs_dict) {
302
211
        switch (Ptype) {
303
211
        case P_AnyOn:
304
211
        case P_AllOn:
305
211
            is_visible = pdfi_get_default_OCG_val(ctx, OCGs_dict);
306
211
            break;
307
0
        case P_AllOff:
308
0
        case P_AnyOff:
309
0
            is_visible = !pdfi_get_default_OCG_val(ctx, OCGs_dict);
310
0
            break;
311
211
        }
312
64.4k
    } else {
313
64.4k
        is_visible = pdfi_oc_check_OCMD_array(ctx, OCGs_array, Ptype);
314
64.4k
    }
315
316
64.7k
    if (OCGs_dict) {
317
211
        code = pdfi_dict_knownget_type(ctx, OCGs_dict, "Usage", PDF_DICT, (pdf_obj **)&UsageDict);
318
211
        if (code < 0)
319
0
            goto cleanup;
320
321
211
        if (UsageDict != NULL) {
322
99
            if (ctx->args.printed) {
323
99
                code = pdfi_dict_knownget_type(ctx, UsageDict, "Print", PDF_DICT, (pdf_obj **)&StateDict);
324
99
                if (code < 0)
325
0
                    goto cleanup;
326
99
                if (StateDict) {
327
25
                    code = pdfi_dict_knownget_type(ctx, StateDict, "PrintState", PDF_NAME, &State);
328
25
                    if (code < 0)
329
0
                        goto cleanup;
330
25
                }
331
99
            } else {
332
0
                code = pdfi_dict_knownget_type(ctx, UsageDict, "View", PDF_DICT, (pdf_obj **)&StateDict);
333
0
                if (code < 0)
334
0
                    goto cleanup;
335
0
                if (StateDict) {
336
0
                    code = pdfi_dict_knownget_type(ctx, StateDict, "ViewState", PDF_NAME, &State);
337
0
                    if (code < 0)
338
0
                        goto cleanup;
339
0
                }
340
0
            }
341
99
            if (State) {
342
25
                if (pdfi_name_is((const pdf_name *)State, "ON"))
343
25
                    is_visible = true;
344
0
                else
345
0
                    if (pdfi_name_is((const pdf_name *)State, "OFF"))
346
0
                        is_visible = false;
347
0
                    else
348
0
                        pdfi_set_error(ctx, 0, NULL, E_PDF_BAD_VALUE, "pdfi_oc_check_OCMD", "Usage Dictionary State is neither ON nor OFF");
349
25
            }
350
99
        }
351
211
    }
352
353
65.1k
 cleanup:
354
65.1k
    pdfi_countdown(State);
355
65.1k
    pdfi_countdown(StateDict);
356
65.1k
    pdfi_countdown(UsageDict);
357
65.1k
    pdfi_countdown(VE);
358
65.1k
    pdfi_countdown(obj);
359
65.1k
    pdfi_countdown(Pname);
360
361
65.1k
    return is_visible;
362
64.7k
}
363
364
/* Check if an OCG or OCMD is visible, passing in OC dict */
365
bool
366
pdfi_oc_is_ocg_visible(pdf_context *ctx, pdf_dict *ocdict)
367
69.3k
{
368
69.3k
    pdf_name *type = NULL;
369
69.3k
    bool is_visible = true;
370
69.3k
    int code;
371
372
    /* Type can be either OCMD or OCG.
373
     */
374
69.3k
    code = pdfi_dict_knownget_type(ctx, ocdict, "Type", PDF_NAME, (pdf_obj **)&type);
375
69.3k
    if (code <= 0)
376
69
        goto cleanup;
377
378
69.2k
    if (pdfi_name_is(type, "OCMD")) {
379
65.1k
        is_visible = pdfi_oc_check_OCMD(ctx, ocdict);
380
65.1k
    } else if (pdfi_name_is(type, "OCG")) {
381
3.85k
        is_visible = pdfi_get_default_OCG_val(ctx, ocdict);
382
3.85k
        if (is_visible)
383
3.79k
            is_visible = pdfi_oc_check_OCG_usage(ctx, ocdict);
384
3.85k
    } else {
385
235
        char str[100];
386
235
        memcpy(str, (const char *)type->data, type->length < 100 ? type->length : 99);
387
235
        str[type->length < 100 ? type->length : 99] = '\0';
388
235
        dbgprintf1("WARNING: OC dict type is %s, expected OCG or OCMD\n", str);
389
235
    }
390
391
69.3k
 cleanup:
392
69.3k
    pdfi_countdown(type);
393
394
69.3k
    if (ctx->args.pdfdebug) {
395
0
        outprintf(ctx->memory, "OCG: OC Dict %d %s visible\n", ocdict->object_num,
396
0
                  is_visible ? "IS" : "IS NOT");
397
0
    }
398
69.3k
    return is_visible;
399
69.2k
}
400
401
206k
#define NUM_CONTENT_LEVELS 100
402
typedef struct {
403
    byte *flags;
404
    uint64_t num_off;
405
    uint64_t max_flags;
406
} pdfi_oc_levels_t;
407
408
static int pdfi_oc_levels_init(pdf_context *ctx, pdfi_oc_levels_t **levels)
409
103k
{
410
103k
    byte *data;
411
103k
    pdfi_oc_levels_t *new;
412
413
103k
    *levels = NULL;
414
415
103k
    new = (pdfi_oc_levels_t *)gs_alloc_bytes(ctx->memory, sizeof(pdfi_oc_levels_t),
416
103k
                                             "pdfi_oc_levels_init (levels)");
417
103k
    if (!new)
418
0
        return_error(gs_error_VMerror);
419
420
103k
    data = (byte *)gs_alloc_bytes(ctx->memory, NUM_CONTENT_LEVELS, "pdfi_oc_levels_init (data)");
421
103k
    if (!data) {
422
0
        gs_free_object(ctx->memory, new, "pdfi_oc_levels_init (levels (error))");
423
0
        return_error(gs_error_VMerror);
424
0
    }
425
103k
    memset(data, 0, NUM_CONTENT_LEVELS);
426
427
103k
    new->flags = data;
428
103k
    new->num_off = 0;
429
103k
    new->max_flags = NUM_CONTENT_LEVELS;
430
103k
    *levels = new;
431
432
103k
    return 0;
433
103k
}
434
435
static int pdfi_oc_levels_free(pdf_context *ctx, pdfi_oc_levels_t *levels)
436
125k
{
437
125k
    if (!levels)
438
21.7k
        return 0;
439
103k
    gs_free_object(ctx->memory, levels->flags, "pdfi_oc_levels_free (flags)");
440
103k
    gs_free_object(ctx->memory, levels, "pdfi_oc_levels_free (levels)");
441
442
103k
    return 0;
443
125k
}
444
445
static int pdfi_oc_levels_set(pdf_context *ctx, pdfi_oc_levels_t *levels, uint64_t index)
446
57.6k
{
447
57.6k
    byte *new = NULL;
448
57.6k
    uint64_t newmax;
449
450
57.6k
    if (index > levels->max_flags - 1) {
451
        /* Expand the flags buffer */
452
0
        newmax = levels->max_flags + NUM_CONTENT_LEVELS;
453
0
        if (index > newmax)
454
0
            return_error(gs_error_Fatal); /* shouldn't happen */
455
0
        new = gs_alloc_bytes(ctx->memory, newmax, "pdfi_oc_levels_set (new data)");
456
0
        if (!new)
457
0
            return_error(gs_error_VMerror);
458
0
        memset(new, 0, newmax);
459
0
        memcpy(new, levels->flags, levels->max_flags);
460
0
        gs_free_object(ctx->memory, levels->flags, "pdfi_oc_levels_set (old data)");
461
0
        levels->flags = new;
462
0
        levels->max_flags += NUM_CONTENT_LEVELS;
463
0
    }
464
465
57.6k
    if (levels->flags[index] == 0)
466
57.6k
        levels->num_off ++;
467
57.6k
    levels->flags[index] = 1;
468
57.6k
    return 0;
469
57.6k
}
470
471
static int pdfi_oc_levels_clear(pdf_context *ctx, pdfi_oc_levels_t *levels, uint64_t index)
472
527k
{
473
527k
    if (index > levels->max_flags - 1)
474
0
        return -1;
475
527k
    if (levels->flags[index] != 0)
476
57.5k
        levels->num_off --;
477
527k
    levels->flags[index] = 0;
478
527k
    return 0;
479
527k
}
480
481
482
/* Test if content is turned off for this element.
483
 */
484
bool pdfi_oc_is_off(pdf_context *ctx)
485
12.1M
{
486
12.1M
    pdfi_oc_levels_t *levels = (pdfi_oc_levels_t *)ctx->OFFlevels;
487
12.1M
    uint64_t num_off = levels->num_off;
488
489
12.1M
    return (num_off != 0);
490
12.1M
}
491
492
int pdfi_oc_init(pdf_context *ctx)
493
103k
{
494
103k
    int code;
495
496
103k
    ctx->BMClevel = 0;
497
103k
    if (ctx->OFFlevels) {
498
32.7k
        pdfi_oc_levels_free(ctx, ctx->OFFlevels);
499
32.7k
        ctx->OFFlevels = NULL;
500
32.7k
    }
501
103k
    code = pdfi_oc_levels_init(ctx, (pdfi_oc_levels_t **)&ctx->OFFlevels);
502
103k
    if (code < 0)
503
0
        return code;
504
505
103k
    return 0;
506
103k
}
507
508
int pdfi_oc_free(pdf_context *ctx)
509
92.4k
{
510
92.4k
    int code;
511
512
92.4k
    code = pdfi_oc_levels_free(ctx, (pdfi_oc_levels_t *)ctx->OFFlevels);
513
92.4k
    ctx->OFFlevels = NULL;
514
92.4k
    return code;
515
92.4k
}
516
517
int pdfi_op_MP(pdf_context *ctx)
518
2.90k
{
519
2.90k
    pdf_obj *o = NULL;
520
2.90k
    int code = 0;
521
522
2.90k
    if (pdfi_count_stack(ctx) < 1)
523
75
        return_error(gs_error_stackunderflow);
524
525
2.82k
    if (!ctx->device_state.writepdfmarks || !ctx->args.preservemarkedcontent) {
526
2.53k
        pdfi_pop(ctx, 1);
527
2.53k
        goto exit;
528
2.53k
    }
529
530
291
    o = ctx->stack_top[-1];
531
291
    pdfi_countup(o);
532
291
    pdfi_pop(ctx, 1);
533
534
291
    if (pdfi_type_of(o) != PDF_NAME) {
535
0
        code = gs_note_error(gs_error_typecheck);
536
0
        goto exit;
537
0
    }
538
539
291
    code = pdfi_pdfmark_from_objarray(ctx, &o, 1, NULL, "MP");
540
291
    ctx->BMClevel ++;
541
542
2.82k
exit:
543
2.82k
    pdfi_countdown(o);
544
2.82k
    return code;
545
291
}
546
547
int pdfi_op_DP(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict)
548
338
{
549
338
    pdf_name *properties = NULL;
550
338
    int code = 0;
551
338
    pdf_obj **objarray = NULL, *o = NULL;
552
553
338
    if (pdfi_count_stack(ctx) < 2) {
554
13
        pdfi_clearstack(ctx);
555
13
        return gs_note_error(gs_error_stackunderflow);
556
13
    }
557
558
325
    if (!ctx->device_state.writepdfmarks || !ctx->args.preservemarkedcontent) {
559
273
        pdfi_pop(ctx, 2); /* pop args */
560
273
        goto exit;
561
273
    }
562
563
52
    if (pdfi_type_of(ctx->stack_top[-2]) != PDF_NAME) {
564
0
        pdfi_pop(ctx, 2); /* pop args */
565
0
        code = gs_note_error(gs_error_typecheck);
566
0
        goto exit;
567
0
    }
568
569
52
    objarray = (pdf_obj **)gs_alloc_bytes(ctx->memory, 2 * sizeof(pdf_obj *), "pdfi_op_DP");
570
52
    if (objarray == NULL) {
571
0
        code = gs_note_error(gs_error_VMerror);
572
0
        goto exit;
573
0
    }
574
575
52
    objarray[0] = ctx->stack_top[-2];
576
52
    pdfi_countup(objarray[0]);
577
52
    o = ctx->stack_top[-1];
578
52
    pdfi_countup(o);
579
52
    pdfi_pop(ctx, 2); /* pop args */
580
581
52
    switch (pdfi_type_of(o)) {
582
0
        case PDF_NAME:
583
0
            code = pdfi_find_resource(ctx, (unsigned char *)"Properties", (pdf_name *)o, stream_dict, page_dict, (pdf_obj **)&properties);
584
0
            if(code < 0)
585
0
                goto exit;
586
0
            if (pdfi_type_of(properties) != PDF_DICT) {
587
0
                code = gs_note_error(gs_error_typecheck);
588
0
                goto exit;
589
0
            }
590
0
            objarray[1] = (pdf_obj *)properties;
591
0
            break;
592
52
        case PDF_DICT:
593
52
            objarray[1] = o;
594
52
            break;
595
0
        default:
596
0
            code = gs_note_error(gs_error_VMerror);
597
0
            goto exit;
598
52
    }
599
600
52
    code = pdfi_pdfmark_from_objarray(ctx, objarray, 2, NULL, "DP");
601
602
325
 exit:
603
325
    if (objarray != NULL) {
604
52
        pdfi_countdown(objarray[0]);
605
52
        gs_free_object(ctx->memory, objarray, "free pdfi_op_DP");
606
52
    }
607
325
    pdfi_countdown(o);
608
325
    pdfi_countdown(properties);
609
325
    return code;
610
52
}
611
612
/* begin marked content sequence */
613
int pdfi_op_BMC(pdf_context *ctx)
614
22.9k
{
615
22.9k
    pdf_obj *o = NULL;
616
22.9k
    int code = 0;
617
618
    /* This will also prevent us writing out an EMC if the BMC is in any way invalid */
619
22.9k
    ctx->BDCWasOC = true;
620
621
22.9k
    if (pdfi_count_stack(ctx) < 1)
622
14
        return_error(gs_error_stackunderflow);
623
624
22.9k
    if (!ctx->device_state.writepdfmarks || !ctx->args.preservemarkedcontent) {
625
        /* Need to increment the BMCLevel anyway, as the EMC will count it down.
626
         * If we already have a BDC, then that effectively will turn it on.
627
         */
628
21.5k
        ctx->BMClevel ++;
629
21.5k
        pdfi_pop(ctx, 1);
630
21.5k
        goto exit;
631
21.5k
    }
632
633
1.40k
    o = ctx->stack_top[-1];
634
1.40k
    pdfi_countup(o);
635
1.40k
    pdfi_pop(ctx, 1);
636
637
1.40k
    if (pdfi_type_of(o) != PDF_NAME) {
638
1
        code = gs_note_error(gs_error_typecheck);
639
1
        goto exit;
640
1
    }
641
642
1.39k
    ctx->BDCWasOC = false;
643
1.39k
    code = pdfi_pdfmark_from_objarray(ctx, &o, 1, NULL, "BMC");
644
1.39k
    ctx->BMClevel ++;
645
646
22.9k
exit:
647
22.9k
    pdfi_countdown(o);
648
22.9k
    return code;
649
1.39k
}
650
651
/* begin marked content sequence with property list */
652
int pdfi_op_BDC(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict)
653
513k
{
654
513k
    pdf_name *tag = NULL;
655
513k
    pdf_name *properties = NULL;
656
513k
    pdf_dict *oc_dict = NULL;
657
513k
    int code = 0;
658
513k
    bool ocg_is_visible;
659
513k
    pdf_obj **objarray = NULL, *o = NULL;
660
513k
    pdf_indirect_ref *dictref = NULL;
661
662
    /* This will also prevent us writing out an EMC if the BDC is in any way invalid */
663
513k
    ctx->BDCWasOC = true;
664
665
513k
    if (pdfi_count_stack(ctx) < 2) {
666
8.94k
        pdfi_clearstack(ctx);
667
8.94k
        return gs_note_error(gs_error_stackunderflow);
668
8.94k
    }
669
670
504k
    ctx->BMClevel ++;
671
672
504k
    tag = (pdf_name *)ctx->stack_top[-2];
673
504k
    pdfi_countup(tag);
674
504k
    o = ctx->stack_top[-1];
675
504k
    pdfi_countup(o);
676
504k
    pdfi_pop(ctx, 2);
677
678
504k
    if (pdfi_type_of(tag) != PDF_NAME)
679
3.63k
        goto exit;
680
681
501k
    if (!pdfi_name_is(tag, "OC"))
682
331k
        ctx->BDCWasOC = false;
683
684
501k
    if (ctx->device_state.writepdfmarks && ctx->args.preservemarkedcontent && (!ctx->BDCWasOC || ctx->device_state.WantsOptionalContent)) {
685
47.4k
        objarray = (pdf_obj **)gs_alloc_bytes(ctx->memory, 2 * sizeof(pdf_obj *), "pdfi_op_BDC");
686
47.4k
        if (objarray == NULL) {
687
0
            code = gs_note_error(gs_error_VMerror);
688
0
            goto exit;
689
0
        }
690
691
47.4k
        objarray[0] = (pdf_obj *)tag;
692
693
47.4k
        switch (pdfi_type_of(o)) {
694
22.6k
            case PDF_NAME:
695
22.6k
                code = pdfi_find_resource(ctx, (unsigned char *)"Properties", (pdf_name *)o, stream_dict, page_dict, (pdf_obj **)&oc_dict);
696
22.6k
                if(code < 0)
697
13.5k
                    goto exit;
698
9.16k
                if (pdfi_type_of(oc_dict) != PDF_DICT) {
699
115
                    code = gs_note_error(gs_error_typecheck);
700
115
                    goto exit;
701
115
                }
702
                /* If we are producing PDF/A we must not include any Metadata, as that
703
                 * requires us to modify the XMP Metadata, which we don't know how to do.
704
                 */
705
9.04k
                if (ctx->args.PDFA > 0) {
706
0
                    uint64_t index = 0;
707
0
                    pdf_name *Key = NULL;
708
0
                    pdf_obj *Value = NULL;
709
710
0
                    code = pdfi_dict_first(ctx, oc_dict, (pdf_obj **)&Key, &Value, &index);
711
0
                    if (code < 0) {
712
0
                        if (code == gs_error_undefined)
713
0
                            code = 0;
714
0
                        goto exit;
715
0
                    }
716
0
                    while (code >= 0) {
717
0
                        if (pdfi_name_is(Key, "Metadata")) {
718
0
                            pdfi_dict_delete_pair(ctx, oc_dict, Key);
719
0
                        }
720
0
                        pdfi_countdown(Key);
721
0
                        Key = NULL;
722
0
                        pdfi_countdown(Value);
723
0
                        Value = NULL;
724
725
0
                        code = pdfi_dict_next(ctx, oc_dict, (pdf_obj **)&Key, &Value, &index);
726
0
                        if (code == gs_error_undefined) {
727
0
                            code = 0;
728
0
                            break;
729
0
                        }
730
0
                    }
731
0
                }
732
9.04k
                if (pdfi_dict_entries(oc_dict) == 0)
733
9
                    goto exit;
734
735
9.03k
                code = pdfi_pdfmark_dict(ctx, oc_dict);
736
9.03k
                if (code < 0)
737
52
                    goto exit;
738
739
                /* Create an indirect ref for the dict */
740
8.98k
                code = pdfi_object_alloc(ctx, PDF_INDIRECT, 0, (pdf_obj **)&dictref);
741
8.98k
                if (code < 0) goto exit;
742
8.98k
                pdfi_countup(dictref);
743
8.98k
                dictref->ref_object_num = oc_dict->object_num;
744
8.98k
                dictref->ref_generation_num = oc_dict->generation_num;
745
8.98k
                dictref->is_marking = true;
746
747
8.98k
                objarray[1] = (pdf_obj *)dictref;
748
8.98k
                break;
749
24.7k
            case PDF_DICT:
750
24.7k
                objarray[1] = o;
751
24.7k
                break;
752
48
            default:
753
48
                code = gs_note_error(gs_error_VMerror);
754
48
                goto exit;
755
47.4k
        }
756
757
33.7k
        code = pdfi_pdfmark_from_objarray(ctx, objarray, 2, NULL, "BDC");
758
33.7k
        goto exit;
759
47.4k
    }
760
761
453k
    if (pdfi_name_is(tag, "OC")) {
762
        /* Check if first arg is a name and handle it if so */
763
        /* TODO: spec says it could also be an inline dict that we should be able to handle,
764
         * but I am just matching what gs does for now, and it doesn't handle that case.
765
         */
766
147k
        properties = (pdf_name *)o;
767
147k
        if (pdfi_type_of(properties) != PDF_NAME)
768
6
            goto exit;
769
770
147k
        code = pdfi_loop_detector_mark(ctx);
771
        /* If it's a name, look it up in Properties */
772
147k
        code = pdfi_find_resource(ctx, (unsigned char *)"Properties", properties,
773
147k
                                  (pdf_dict *)stream_dict, page_dict, (pdf_obj **)&oc_dict);
774
147k
        (void)pdfi_loop_detector_cleartomark(ctx);
775
147k
        if (code != 0)
776
77.6k
            goto exit;
777
70.1k
        if (pdfi_type_of(oc_dict) != PDF_DICT)
778
1.68k
            goto exit;
779
780
68.4k
        ocg_is_visible = pdfi_oc_is_ocg_visible(ctx, oc_dict);
781
68.4k
        if (!ocg_is_visible)
782
57.6k
            code = pdfi_oc_levels_set(ctx, ctx->OFFlevels, ctx->BMClevel);
783
784
68.4k
    }
785
786
504k
exit:
787
504k
    if (objarray != NULL)
788
47.4k
        gs_free_object(ctx->memory, objarray, "free pdfi_op_BDC");
789
504k
    pdfi_countdown(dictref);
790
504k
    pdfi_countdown(o);
791
504k
    pdfi_countdown(tag);
792
504k
    pdfi_countdown(oc_dict);
793
504k
    return code;
794
453k
}
795
796
/* end marked content sequence */
797
int pdfi_op_EMC(pdf_context *ctx)
798
527k
{
799
527k
    int code, code1 = 0;
800
801
527k
    if (ctx->device_state.writepdfmarks && ctx->args.preservemarkedcontent && (!ctx->BDCWasOC || ctx->device_state.WantsOptionalContent))
802
48.3k
        code1 = pdfi_pdfmark_from_objarray(ctx, NULL, 0, NULL, "EMC");
803
804
527k
    code = pdfi_oc_levels_clear(ctx, ctx->OFFlevels, ctx->BMClevel);
805
527k
    if (code == 0)
806
527k
        code = code1;
807
808
    /* TODO: Should we flag error on too many EMC? */
809
527k
    if (ctx->BMClevel > 0)
810
515k
        ctx->BMClevel --;
811
527k
    return code;
812
527k
}