/src/ghostpdl/pdf/pdf_optcontent.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2019-2022 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., 1305 Grant Avenue - Suite 200, Novato, |
13 | | CA 94945, U.S.A., +1(415)492-9861, 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 | | |
29 | | |
30 | | /* Find the default value for an ocdict, based on contents of OCProperties */ |
31 | | /* NOTE: the spec says that if BaseState is present, it won't be set to "OFF", |
32 | | * but this doesn't seem to be the case (Bug 691491). Also, the spec |
33 | | * says the ON and OFF arrays are redundant in certain cases. We just |
34 | | * look at everything anyway. |
35 | | * Default is going to be visible unless anything here indicates that it |
36 | | * should be turned off. |
37 | | */ |
38 | | static bool |
39 | | pdfi_get_default_OCG_val(pdf_context *ctx, pdf_dict *ocdict) |
40 | 19.7k | { |
41 | 19.7k | bool is_visible = true; |
42 | 19.7k | pdf_dict *D = NULL; |
43 | 19.7k | pdf_obj *BaseState = NULL; |
44 | 19.7k | pdf_array *OFF = NULL; |
45 | 19.7k | pdf_array *ON = NULL; |
46 | 19.7k | int code; |
47 | | |
48 | 19.7k | if (ctx->OCProperties == NULL) |
49 | 700 | return is_visible; |
50 | | |
51 | 19.0k | code = pdfi_dict_knownget_type(ctx, ctx->OCProperties, "D", PDF_DICT, (pdf_obj **)&D); |
52 | 19.0k | if (code <= 0) |
53 | 2 | goto cleanup; |
54 | | |
55 | 19.0k | code = pdfi_dict_knownget_type(ctx, D, "BaseState", PDF_NAME, &BaseState); |
56 | 19.0k | if (code < 0) { |
57 | 0 | goto cleanup; |
58 | 0 | } |
59 | 19.0k | if (code > 0) { |
60 | 0 | if (pdfi_name_is((pdf_name *)BaseState, "OFF")) { |
61 | 0 | is_visible = false; |
62 | 0 | } |
63 | 0 | } |
64 | | |
65 | 19.0k | if (!is_visible) { |
66 | 0 | code = pdfi_dict_knownget_type(ctx, D, "ON", PDF_ARRAY, (pdf_obj **)&ON); |
67 | 0 | if (code < 0) |
68 | 0 | goto cleanup; |
69 | 0 | if (code > 0) { |
70 | 0 | if (pdfi_array_known(ctx, ON, (pdf_obj *)ocdict, NULL)) |
71 | 0 | is_visible = true; |
72 | 0 | } |
73 | 0 | } |
74 | | |
75 | 19.0k | if (is_visible) { |
76 | 19.0k | code = pdfi_dict_knownget_type(ctx, D, "OFF", PDF_ARRAY, (pdf_obj **)&OFF); |
77 | 19.0k | if (code < 0) |
78 | 2.73k | goto cleanup; |
79 | 16.3k | if (code > 0) { |
80 | 16.0k | if (pdfi_array_known(ctx, OFF, (pdf_obj *)ocdict, NULL)) |
81 | 15.7k | is_visible = false; |
82 | 16.0k | } |
83 | 16.3k | } |
84 | | |
85 | | |
86 | 19.0k | cleanup: |
87 | 19.0k | pdfi_countdown(BaseState); |
88 | 19.0k | pdfi_countdown(D); |
89 | 19.0k | pdfi_countdown(OFF); |
90 | 19.0k | pdfi_countdown(ON); |
91 | 19.0k | return is_visible; |
92 | 19.0k | } |
93 | | |
94 | | /* Check Usage for an OCG */ |
95 | | static bool |
96 | | pdfi_oc_check_OCG_usage(pdf_context *ctx, pdf_dict *ocdict) |
97 | 817 | { |
98 | 817 | bool is_visible = true; |
99 | 817 | int code; |
100 | 817 | pdf_dict *Usage = NULL; |
101 | 817 | pdf_dict *dict = NULL; |
102 | 817 | pdf_obj *name = NULL; |
103 | | |
104 | | /* Check Usage to see if it has additional info */ |
105 | 817 | code = pdfi_dict_knownget_type(ctx, ocdict, "Usage", PDF_DICT, (pdf_obj **)&Usage); |
106 | 817 | if (code <= 0) { |
107 | | /* No Usage, so we're done */ |
108 | 502 | goto cleanup; |
109 | 502 | } |
110 | | |
111 | 315 | if (ctx->args.printed) { |
112 | 315 | code = pdfi_dict_knownget_type(ctx, ocdict, "Print", PDF_DICT, (pdf_obj **)&dict); |
113 | 315 | if (code <= 0) |
114 | 315 | goto cleanup; |
115 | 0 | code = pdfi_dict_knownget_type(ctx, dict, "PrintState", PDF_NAME, &name); |
116 | 0 | if (code <= 0) |
117 | 0 | goto cleanup; |
118 | 0 | } else { |
119 | 0 | code = pdfi_dict_knownget_type(ctx, ocdict, "View", PDF_DICT, (pdf_obj **)&dict); |
120 | 0 | if (code <= 0) |
121 | 0 | goto cleanup; |
122 | 0 | code = pdfi_dict_knownget_type(ctx, dict, "ViewState", PDF_NAME, &name); |
123 | 0 | if (code <= 0) |
124 | 0 | goto cleanup; |
125 | 0 | } |
126 | 0 | if (pdfi_name_strcmp((pdf_name *)name, "OFF")) { |
127 | 0 | is_visible = false; |
128 | 0 | } |
129 | |
|
130 | 817 | cleanup: |
131 | 817 | pdfi_countdown(Usage); |
132 | 817 | pdfi_countdown(dict); |
133 | 817 | pdfi_countdown(name); |
134 | | |
135 | 817 | return is_visible; |
136 | 0 | } |
137 | | |
138 | | typedef enum { |
139 | | P_AnyOn, |
140 | | P_AllOn, |
141 | | P_AllOff, |
142 | | P_AnyOff |
143 | | } ocmd_p_type; |
144 | | |
145 | | static bool |
146 | | pdfi_oc_check_OCMD_array(pdf_context *ctx, pdf_array *array, ocmd_p_type type) |
147 | 19.6k | { |
148 | 19.6k | bool is_visible; |
149 | 19.6k | uint64_t i; |
150 | 19.6k | int code; |
151 | 19.6k | pdf_obj *val = NULL; |
152 | | |
153 | | /* Setup default */ |
154 | 19.6k | switch (type) { |
155 | 19.6k | case P_AnyOn: |
156 | 19.6k | case P_AnyOff: |
157 | 19.6k | is_visible = false; |
158 | 19.6k | break; |
159 | 0 | case P_AllOn: |
160 | 0 | case P_AllOff: |
161 | 0 | is_visible = true; |
162 | 0 | break; |
163 | 19.6k | } |
164 | | |
165 | 36.1k | for (i=0; i<pdfi_array_size(array); i++) { |
166 | 19.6k | bool vis; |
167 | | |
168 | 19.6k | code = pdfi_array_get(ctx, array, i, &val); |
169 | 19.6k | if (code < 0) continue; |
170 | 18.9k | if (pdfi_type_of(val) != PDF_DICT) { |
171 | 0 | dmprintf1(ctx->memory, "WARNING: OCMD array contains item type %d, expected PDF_DICT or PDF_NULL\n", pdfi_type_of(val)); |
172 | 0 | pdfi_countdown(val); |
173 | 0 | val = NULL; |
174 | 0 | continue; |
175 | 0 | } |
176 | | |
177 | 18.9k | vis = pdfi_get_default_OCG_val(ctx, (pdf_dict *)val); |
178 | 18.9k | switch (type) { |
179 | 18.9k | case P_AnyOn: |
180 | | /* visible if any is on */ |
181 | 18.9k | if (vis) { |
182 | 3.16k | is_visible = true; |
183 | 3.16k | goto cleanup; |
184 | 3.16k | } |
185 | 15.7k | break; |
186 | 15.7k | case P_AllOn: |
187 | | /* visible if all on */ |
188 | 0 | if (!vis) { |
189 | 0 | is_visible = false; |
190 | 0 | goto cleanup; |
191 | 0 | } |
192 | 0 | break; |
193 | 0 | case P_AllOff: |
194 | | /* visible if all are off */ |
195 | 0 | if (vis) { |
196 | 0 | is_visible = false; |
197 | 0 | goto cleanup; |
198 | 0 | } |
199 | 0 | break; |
200 | 0 | case P_AnyOff: |
201 | | /* visible if any is off */ |
202 | 0 | if (!vis) { |
203 | 0 | is_visible = true; |
204 | 0 | goto cleanup; |
205 | 0 | } |
206 | 0 | break; |
207 | 18.9k | } |
208 | 15.7k | pdfi_countdown(val); |
209 | 15.7k | val = NULL; |
210 | 15.7k | } |
211 | | |
212 | 19.6k | cleanup: |
213 | 19.6k | pdfi_countdown(val); |
214 | 19.6k | return is_visible; |
215 | 19.6k | } |
216 | | |
217 | | static bool |
218 | | pdfi_oc_check_OCMD(pdf_context *ctx, pdf_dict *ocdict) |
219 | 19.7k | { |
220 | 19.7k | bool is_visible = true; |
221 | 19.7k | int code; |
222 | 19.7k | pdf_obj *VE = NULL; |
223 | 19.7k | pdf_obj *obj = NULL; |
224 | 19.7k | pdf_obj *Pname = NULL; |
225 | 19.7k | pdf_dict *OCGs_dict = NULL; /* alias, don't need to free */ |
226 | 19.7k | pdf_array *OCGs_array = NULL; /* alias, don't need to free */ |
227 | 19.7k | ocmd_p_type Ptype = P_AnyOn; |
228 | | |
229 | | /* TODO: We don't support this, so log a warning and ignore */ |
230 | 19.7k | code = pdfi_dict_knownget_type(ctx, ocdict, "VE", PDF_ARRAY, &VE); |
231 | 19.7k | if (code > 0) { |
232 | 90 | dmprintf(ctx->memory, "WARNING: OCMD contains VE, which is not supported (ignoring)\n"); |
233 | 90 | } |
234 | | |
235 | 19.7k | code = pdfi_dict_knownget(ctx, ocdict, "OCGs", &obj); |
236 | 19.7k | if (code <= 0) |
237 | 91 | goto cleanup; |
238 | 19.6k | if (pdfi_type_of(obj) == PDF_ARRAY) { |
239 | 19.6k | OCGs_array = (pdf_array *)obj; |
240 | 19.6k | } else if (pdfi_type_of(obj) == PDF_DICT) { |
241 | 30 | OCGs_dict = (pdf_dict *)obj; |
242 | 30 | } else { |
243 | 0 | goto cleanup; |
244 | 0 | } |
245 | | |
246 | 19.6k | code = pdfi_dict_knownget_type(ctx, ocdict, "P", PDF_NAME, &Pname); |
247 | 19.6k | if (code < 0) |
248 | 0 | goto cleanup; |
249 | 19.6k | if (code == 0 || pdfi_name_is((pdf_name *)Pname, "AnyOn")) { |
250 | 19.6k | Ptype = P_AnyOn; |
251 | 19.6k | } else if (pdfi_name_is((pdf_name *)Pname, "AllOn")) { |
252 | 0 | Ptype = P_AllOn; |
253 | 0 | } else if (pdfi_name_is((pdf_name *)Pname, "AnyOff")) { |
254 | 0 | Ptype = P_AnyOff; |
255 | 0 | } else if (pdfi_name_is((pdf_name *)Pname, "AllOff")) { |
256 | 0 | Ptype = P_AllOff; |
257 | 0 | } else { |
258 | 0 | Ptype = P_AnyOn; |
259 | 0 | } |
260 | | |
261 | 19.6k | if (OCGs_dict) { |
262 | 30 | switch (Ptype) { |
263 | 30 | case P_AnyOn: |
264 | 30 | case P_AllOn: |
265 | 30 | is_visible = pdfi_get_default_OCG_val(ctx, OCGs_dict); |
266 | 30 | break; |
267 | 0 | case P_AllOff: |
268 | 0 | case P_AnyOff: |
269 | 0 | is_visible = !pdfi_get_default_OCG_val(ctx, OCGs_dict); |
270 | 0 | break; |
271 | 30 | } |
272 | 19.6k | } else { |
273 | 19.6k | is_visible = pdfi_oc_check_OCMD_array(ctx, OCGs_array, Ptype); |
274 | 19.6k | } |
275 | | |
276 | 19.7k | cleanup: |
277 | 19.7k | pdfi_countdown(VE); |
278 | 19.7k | pdfi_countdown(obj); |
279 | 19.7k | pdfi_countdown(Pname); |
280 | | |
281 | 19.7k | return is_visible; |
282 | 19.6k | } |
283 | | |
284 | | /* Check if an OCG or OCMD is visible, passing in OC dict */ |
285 | | bool |
286 | | pdfi_oc_is_ocg_visible(pdf_context *ctx, pdf_dict *ocdict) |
287 | 20.6k | { |
288 | 20.6k | pdf_name *type = NULL; |
289 | 20.6k | bool is_visible = true; |
290 | 20.6k | int code; |
291 | | |
292 | | /* Type can be either OCMD or OCG. |
293 | | */ |
294 | 20.6k | code = pdfi_dict_knownget_type(ctx, ocdict, "Type", PDF_NAME, (pdf_obj **)&type); |
295 | 20.6k | if (code <= 0) |
296 | 5 | goto cleanup; |
297 | | |
298 | 20.6k | if (pdfi_name_is(type, "OCMD")) { |
299 | 19.7k | is_visible = pdfi_oc_check_OCMD(ctx, ocdict); |
300 | 19.7k | } else if (pdfi_name_is(type, "OCG")) { |
301 | 841 | is_visible = pdfi_get_default_OCG_val(ctx, ocdict); |
302 | 841 | if (is_visible) |
303 | 817 | is_visible = pdfi_oc_check_OCG_usage(ctx, ocdict); |
304 | 841 | } else { |
305 | 26 | char str[100]; |
306 | 26 | memcpy(str, (const char *)type->data, type->length < 100 ? type->length : 99); |
307 | 26 | str[type->length < 100 ? type->length : 99] = '\0'; |
308 | 26 | dmprintf1(ctx->memory, "WARNING: OC dict type is %s, expected OCG or OCMD\n", str); |
309 | 26 | } |
310 | | |
311 | 20.6k | cleanup: |
312 | 20.6k | pdfi_countdown(type); |
313 | | |
314 | 20.6k | if (ctx->args.pdfdebug) { |
315 | 0 | dmprintf2(ctx->memory, "OCG: OC Dict %d %s visible\n", ocdict->object_num, |
316 | 0 | is_visible ? "IS" : "IS NOT"); |
317 | 0 | } |
318 | 20.6k | return is_visible; |
319 | 20.6k | } |
320 | | |
321 | 86.7k | #define NUM_CONTENT_LEVELS 100 |
322 | | typedef struct { |
323 | | byte *flags; |
324 | | uint64_t num_off; |
325 | | uint64_t max_flags; |
326 | | } pdfi_oc_levels_t; |
327 | | |
328 | | static int pdfi_oc_levels_init(pdf_context *ctx, pdfi_oc_levels_t **levels) |
329 | 43.3k | { |
330 | 43.3k | byte *data; |
331 | 43.3k | pdfi_oc_levels_t *new; |
332 | | |
333 | 43.3k | *levels = NULL; |
334 | | |
335 | 43.3k | new = (pdfi_oc_levels_t *)gs_alloc_bytes(ctx->memory, sizeof(pdfi_oc_levels_t), |
336 | 43.3k | "pdfi_oc_levels_init (levels)"); |
337 | 43.3k | if (!new) |
338 | 0 | return_error(gs_error_VMerror); |
339 | | |
340 | 43.3k | data = (byte *)gs_alloc_bytes(ctx->memory, NUM_CONTENT_LEVELS, "pdfi_oc_levels_init (data)"); |
341 | 43.3k | if (!data) { |
342 | 0 | gs_free_object(ctx->memory, new, "pdfi_oc_levels_init (levels (error))"); |
343 | 0 | return_error(gs_error_VMerror); |
344 | 0 | } |
345 | 43.3k | memset(data, 0, NUM_CONTENT_LEVELS); |
346 | | |
347 | 43.3k | new->flags = data; |
348 | 43.3k | new->num_off = 0; |
349 | 43.3k | new->max_flags = NUM_CONTENT_LEVELS; |
350 | 43.3k | *levels = new; |
351 | | |
352 | 43.3k | return 0; |
353 | 43.3k | } |
354 | | |
355 | | static int pdfi_oc_levels_free(pdf_context *ctx, pdfi_oc_levels_t *levels) |
356 | 59.7k | { |
357 | 59.7k | if (!levels) |
358 | 16.3k | return 0; |
359 | 43.3k | gs_free_object(ctx->memory, levels->flags, "pdfi_oc_levels_free (flags)"); |
360 | 43.3k | gs_free_object(ctx->memory, levels, "pdfi_oc_levels_free (levels)"); |
361 | | |
362 | 43.3k | return 0; |
363 | 59.7k | } |
364 | | |
365 | | static int pdfi_oc_levels_set(pdf_context *ctx, pdfi_oc_levels_t *levels, uint64_t index) |
366 | 16.5k | { |
367 | 16.5k | byte *new = NULL; |
368 | 16.5k | uint64_t newmax; |
369 | | |
370 | 16.5k | if (index > levels->max_flags - 1) { |
371 | | /* Expand the flags buffer */ |
372 | 0 | newmax = levels->max_flags + NUM_CONTENT_LEVELS; |
373 | 0 | if (index > newmax) |
374 | 0 | return_error(gs_error_Fatal); /* shouldn't happen */ |
375 | 0 | new = gs_alloc_bytes(ctx->memory, newmax, "pdfi_oc_levels_set (new data)"); |
376 | 0 | if (!new) |
377 | 0 | return_error(gs_error_VMerror); |
378 | 0 | memset(new, 0, newmax); |
379 | 0 | memcpy(new, levels->flags, levels->max_flags); |
380 | 0 | gs_free_object(ctx->memory, levels->flags, "pdfi_oc_levels_set (old data)"); |
381 | 0 | levels->flags = new; |
382 | 0 | levels->max_flags += NUM_CONTENT_LEVELS; |
383 | 0 | } |
384 | | |
385 | 16.5k | if (levels->flags[index] == 0) |
386 | 16.5k | levels->num_off ++; |
387 | 16.5k | levels->flags[index] = 1; |
388 | 16.5k | return 0; |
389 | 16.5k | } |
390 | | |
391 | | static int pdfi_oc_levels_clear(pdf_context *ctx, pdfi_oc_levels_t *levels, uint64_t index) |
392 | 114k | { |
393 | 114k | if (index > levels->max_flags - 1) |
394 | 0 | return -1; |
395 | 114k | if (levels->flags[index] != 0) |
396 | 16.4k | levels->num_off --; |
397 | 114k | levels->flags[index] = 0; |
398 | 114k | return 0; |
399 | 114k | } |
400 | | |
401 | | |
402 | | /* Test if content is turned off for this element. |
403 | | */ |
404 | | bool pdfi_oc_is_off(pdf_context *ctx) |
405 | 4.54M | { |
406 | 4.54M | pdfi_oc_levels_t *levels = (pdfi_oc_levels_t *)ctx->OFFlevels; |
407 | 4.54M | uint64_t num_off = levels->num_off; |
408 | | |
409 | 4.54M | return (num_off != 0); |
410 | 4.54M | } |
411 | | |
412 | | int pdfi_oc_init(pdf_context *ctx) |
413 | 43.3k | { |
414 | 43.3k | int code; |
415 | | |
416 | 43.3k | ctx->BMClevel = 0; |
417 | 43.3k | if (ctx->OFFlevels) { |
418 | 18.9k | pdfi_oc_levels_free(ctx, ctx->OFFlevels); |
419 | 18.9k | ctx->OFFlevels = NULL; |
420 | 18.9k | } |
421 | 43.3k | code = pdfi_oc_levels_init(ctx, (pdfi_oc_levels_t **)&ctx->OFFlevels); |
422 | 43.3k | if (code < 0) |
423 | 0 | return code; |
424 | | |
425 | 43.3k | return 0; |
426 | 43.3k | } |
427 | | |
428 | | int pdfi_oc_free(pdf_context *ctx) |
429 | 40.8k | { |
430 | 40.8k | int code; |
431 | | |
432 | 40.8k | code = pdfi_oc_levels_free(ctx, (pdfi_oc_levels_t *)ctx->OFFlevels); |
433 | 40.8k | ctx->OFFlevels = NULL; |
434 | 40.8k | return code; |
435 | 40.8k | } |
436 | | |
437 | | int pdfi_op_MP(pdf_context *ctx) |
438 | 921 | { |
439 | 921 | pdf_obj *o = NULL; |
440 | 921 | int code = 0; |
441 | | |
442 | 921 | if (pdfi_count_stack(ctx) < 1) |
443 | 32 | return_error(gs_error_stackunderflow); |
444 | | |
445 | 889 | if (!ctx->device_state.writepdfmarks || !ctx->args.preservemarkedcontent) |
446 | 889 | goto exit; |
447 | | |
448 | 0 | o = ctx->stack_top[-1]; |
449 | 0 | pdfi_countup(o); |
450 | 0 | pdfi_pop(ctx, 1); |
451 | |
|
452 | 0 | if (pdfi_type_of(o) != PDF_NAME) { |
453 | 0 | code = gs_note_error(gs_error_typecheck); |
454 | 0 | goto exit; |
455 | 0 | } |
456 | | |
457 | 0 | code = pdfi_pdfmark_from_objarray(ctx, &o, 1, NULL, "MP"); |
458 | 0 | ctx->BMClevel ++; |
459 | |
|
460 | 889 | exit: |
461 | 889 | pdfi_countdown(o); |
462 | 889 | return code; |
463 | 0 | } |
464 | | |
465 | | int pdfi_op_DP(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) |
466 | 754 | { |
467 | 754 | pdf_name *properties = NULL; |
468 | 754 | int code = 0; |
469 | 754 | pdf_obj **objarray = NULL, *o = NULL; |
470 | | |
471 | 754 | if (pdfi_count_stack(ctx) < 2) { |
472 | 0 | pdfi_clearstack(ctx); |
473 | 0 | return gs_note_error(gs_error_stackunderflow); |
474 | 0 | } |
475 | | |
476 | 754 | if (!ctx->device_state.writepdfmarks || !ctx->args.preservemarkedcontent) { |
477 | 754 | pdfi_pop(ctx, 2); /* pop args */ |
478 | 754 | goto exit; |
479 | 754 | } |
480 | | |
481 | 0 | if (pdfi_type_of(ctx->stack_top[-2]) != PDF_NAME) { |
482 | 0 | pdfi_pop(ctx, 2); /* pop args */ |
483 | 0 | code = gs_note_error(gs_error_typecheck); |
484 | 0 | goto exit; |
485 | 0 | } |
486 | | |
487 | 0 | objarray = (pdf_obj **)gs_alloc_bytes(ctx->memory, 2 * sizeof(pdf_obj *), "pdfi_op_DP"); |
488 | 0 | if (objarray == NULL) { |
489 | 0 | code = gs_note_error(gs_error_VMerror); |
490 | 0 | goto exit; |
491 | 0 | } |
492 | | |
493 | 0 | objarray[0] = ctx->stack_top[-2]; |
494 | 0 | pdfi_countup(objarray[0]); |
495 | 0 | o = ctx->stack_top[-2]; |
496 | 0 | pdfi_countup(o); |
497 | 0 | pdfi_pop(ctx, 2); /* pop args */ |
498 | |
|
499 | 0 | switch (pdfi_type_of(o)) { |
500 | 0 | case PDF_NAME: |
501 | 0 | code = pdfi_find_resource(ctx, (unsigned char *)"Properties", (pdf_name *)o, stream_dict, page_dict, (pdf_obj **)&properties); |
502 | 0 | if(code < 0) |
503 | 0 | goto exit; |
504 | 0 | if (pdfi_type_of(properties) != PDF_DICT) { |
505 | 0 | code = gs_note_error(gs_error_typecheck); |
506 | 0 | goto exit; |
507 | 0 | } |
508 | 0 | objarray[1] = (pdf_obj *)properties; |
509 | 0 | break; |
510 | 0 | case PDF_DICT: |
511 | 0 | objarray[1] = o; |
512 | 0 | break; |
513 | 0 | default: |
514 | 0 | code = gs_note_error(gs_error_VMerror); |
515 | 0 | goto exit; |
516 | 0 | } |
517 | | |
518 | 0 | code = pdfi_pdfmark_from_objarray(ctx, objarray, 2, NULL, "DP"); |
519 | |
|
520 | 754 | exit: |
521 | 754 | if (objarray != NULL) { |
522 | 0 | pdfi_countdown(objarray[0]); |
523 | 0 | gs_free_object(ctx->memory, objarray, "free pdfi_op_DP"); |
524 | 0 | } |
525 | 754 | pdfi_countdown(o); |
526 | 754 | pdfi_countdown(properties); |
527 | 754 | return code; |
528 | 0 | } |
529 | | |
530 | | /* begin marked content sequence */ |
531 | | int pdfi_op_BMC(pdf_context *ctx) |
532 | 6.37k | { |
533 | 6.37k | pdf_obj *o = NULL; |
534 | 6.37k | int code = 0; |
535 | | |
536 | | /* This will also prevent us writing out an EMC if the BMC is in any way invalid */ |
537 | 6.37k | ctx->BDCWasOC = true; |
538 | | |
539 | 6.37k | if (pdfi_count_stack(ctx) < 1) |
540 | 0 | return_error(gs_error_stackunderflow); |
541 | | |
542 | 6.37k | if (!ctx->device_state.writepdfmarks || !ctx->args.preservemarkedcontent) { |
543 | 6.37k | pdfi_pop(ctx, 1); |
544 | 6.37k | goto exit; |
545 | 6.37k | } |
546 | | |
547 | 0 | o = ctx->stack_top[-1]; |
548 | 0 | pdfi_countup(o); |
549 | 0 | pdfi_pop(ctx, 1); |
550 | |
|
551 | 0 | if (pdfi_type_of(o) != PDF_NAME) { |
552 | 0 | code = gs_note_error(gs_error_typecheck); |
553 | 0 | goto exit; |
554 | 0 | } |
555 | | |
556 | 0 | ctx->BDCWasOC = false; |
557 | 0 | code = pdfi_pdfmark_from_objarray(ctx, &o, 1, NULL, "BMC"); |
558 | 0 | ctx->BMClevel ++; |
559 | |
|
560 | 6.37k | exit: |
561 | 6.37k | pdfi_countdown(o); |
562 | 6.37k | return code; |
563 | 0 | } |
564 | | |
565 | | /* begin marked content sequence with property list */ |
566 | | int pdfi_op_BDC(pdf_context *ctx, pdf_dict *stream_dict, pdf_dict *page_dict) |
567 | 109k | { |
568 | 109k | pdf_name *tag = NULL; |
569 | 109k | pdf_name *properties = NULL; |
570 | 109k | pdf_dict *oc_dict = NULL; |
571 | 109k | int code = 0; |
572 | 109k | bool ocg_is_visible; |
573 | 109k | pdf_obj **objarray = NULL, *o = NULL;; |
574 | | |
575 | | /* This will also prevent us writing out an EMC if the BDC is in any way invalid */ |
576 | 109k | ctx->BDCWasOC = true; |
577 | | |
578 | 109k | if (pdfi_count_stack(ctx) < 2) { |
579 | 422 | pdfi_clearstack(ctx); |
580 | 422 | return gs_note_error(gs_error_stackunderflow); |
581 | 422 | } |
582 | | |
583 | 108k | ctx->BMClevel ++; |
584 | | |
585 | 108k | tag = (pdf_name *)ctx->stack_top[-2]; |
586 | 108k | pdfi_countup(tag); |
587 | 108k | o = ctx->stack_top[-1]; |
588 | 108k | pdfi_countup(o); |
589 | 108k | pdfi_pop(ctx, 2); |
590 | | |
591 | 108k | if (pdfi_type_of(tag) != PDF_NAME) |
592 | 541 | goto exit; |
593 | | |
594 | 108k | if (!pdfi_name_is(tag, "OC")) { |
595 | 63.9k | ctx->BDCWasOC = false; |
596 | 63.9k | if (!ctx->device_state.writepdfmarks || !ctx->args.preservemarkedcontent) |
597 | 63.9k | goto exit; |
598 | | |
599 | 0 | objarray = (pdf_obj **)gs_alloc_bytes(ctx->memory, 2 * sizeof(pdf_obj *), "pdfi_op_BDC"); |
600 | 0 | if (objarray == NULL) { |
601 | 0 | code = gs_note_error(gs_error_VMerror); |
602 | 0 | goto exit; |
603 | 0 | } |
604 | | |
605 | 0 | objarray[0] = (pdf_obj *)tag; |
606 | |
|
607 | 0 | switch (pdfi_type_of(o)) { |
608 | 0 | case PDF_NAME: |
609 | 0 | code = pdfi_find_resource(ctx, (unsigned char *)"Properties", (pdf_name *)o, stream_dict, page_dict, (pdf_obj **)&oc_dict); |
610 | 0 | if(code < 0) |
611 | 0 | goto exit; |
612 | 0 | if (pdfi_type_of(oc_dict) != PDF_DICT) { |
613 | 0 | code = gs_note_error(gs_error_typecheck); |
614 | 0 | goto exit; |
615 | 0 | } |
616 | 0 | objarray[1] = (pdf_obj *)oc_dict; |
617 | 0 | break; |
618 | 0 | case PDF_DICT: |
619 | 0 | objarray[1] = o; |
620 | 0 | break; |
621 | 0 | default: |
622 | 0 | code = gs_note_error(gs_error_VMerror); |
623 | 0 | goto exit; |
624 | 0 | } |
625 | | |
626 | 0 | code = pdfi_pdfmark_from_objarray(ctx, objarray, 2, NULL, "BDC"); |
627 | 0 | goto exit; |
628 | 0 | } |
629 | | |
630 | | /* Check if first arg is a name and handle it if so */ |
631 | | /* TODO: spec says it could also be an inline dict that we should be able to handle, |
632 | | * but I am just matching what gs does for now, and it doesn't handle that case. |
633 | | */ |
634 | 44.4k | properties = (pdf_name *)o; |
635 | 44.4k | if (pdfi_type_of(properties) != PDF_NAME) |
636 | 3 | goto exit; |
637 | | |
638 | | /* If it's a name, look it up in Properties */ |
639 | 44.4k | code = pdfi_find_resource(ctx, (unsigned char *)"Properties", properties, |
640 | 44.4k | (pdf_dict *)stream_dict, page_dict, (pdf_obj **)&oc_dict); |
641 | 44.4k | if (code != 0) |
642 | 23.4k | goto exit; |
643 | 20.9k | if (pdfi_type_of(oc_dict) != PDF_DICT) |
644 | 283 | goto exit; |
645 | | |
646 | | /* Now we have an OC dict, see if it's visible */ |
647 | 20.6k | ocg_is_visible = pdfi_oc_is_ocg_visible(ctx, oc_dict); |
648 | 20.6k | if (!ocg_is_visible) |
649 | 16.5k | code = pdfi_oc_levels_set(ctx, ctx->OFFlevels, ctx->BMClevel); |
650 | | |
651 | 108k | exit: |
652 | 108k | if (objarray != NULL) |
653 | 0 | gs_free_object(ctx->memory, objarray, "free pdfi_op_BDC"); |
654 | 108k | pdfi_countdown(o); |
655 | 108k | pdfi_countdown(tag); |
656 | 108k | pdfi_countdown(oc_dict); |
657 | 108k | return code; |
658 | 20.6k | } |
659 | | |
660 | | /* end marked content sequence */ |
661 | | int pdfi_op_EMC(pdf_context *ctx) |
662 | 114k | { |
663 | 114k | int code, code1 = 0; |
664 | | |
665 | 114k | if (ctx->device_state.writepdfmarks && ctx->args.preservemarkedcontent && !ctx->BDCWasOC) |
666 | 0 | code1 = pdfi_pdfmark_from_objarray(ctx, NULL, 0, NULL, "EMC"); |
667 | | |
668 | 114k | code = pdfi_oc_levels_clear(ctx, ctx->OFFlevels, ctx->BMClevel); |
669 | 114k | if (code == 0) |
670 | 114k | code = code1; |
671 | | |
672 | | /* TODO: Should we flag error on too many EMC? */ |
673 | 114k | if (ctx->BMClevel > 0) |
674 | 107k | ctx->BMClevel --; |
675 | 114k | return code; |
676 | 114k | } |