1 #include "mupdf/fitz.h"
2 #include "mupdf/pdf.h"
3 
4 #include <string.h>
5 
6 /*
7 	Notes on OCGs etc.
8 
9 	PDF Documents may contain Optional Content Groups. Which of
10 	these is shown at any given time is dependent on which
11 	Optional Content Configuration Dictionary is in force at the
12 	time.
13 
14 	A pdf_document, once loaded, contains some state saying which
15 	OCGs are enabled/disabled, and which 'Intent' (or 'Intents')
16 	a file is being used for. This information is held outside of
17 	the actual PDF file.
18 
19 	An Intent (just 'View' or 'Design' or 'All', according to
20 	PDF 2.0, but theoretically more) says which OCGs to consider
21 	or ignore in calculating the visibility of content. The
22 	Intent (or Intents, for there can be an array) is set by the
23 	current OCCD.
24 
25 	When first loaded, we turn all OCGs on, then load the default
26 	OCCD. This may turn some OCGs off, and sets the document Intent.
27 
28 	Callers can ask how many OCCDs there are, read the names/creators
29 	for each, and then select any one of them. That updates which
30 	OCGs are selected, and resets the Intent.
31 
32 	Once an OCCD has been selected, a caller can enumerate the
33 	'displayable configuration'. This is a list of labels/radio
34 	buttons/check buttons that can be used to enable/disable
35 	given OCGs. The caller can then enable/disable OCGs by
36 	asking to select (or toggle) given entries in that list.
37 
38 	Thus the handling of radio button groups, and 'locked'
39 	elements is kept within the core of MuPDF.
40 
41 	Finally, the caller can set the 'usage' for a document. This
42 	can be 'View', 'Print', or 'Export'.
43 */
44 
45 typedef struct
46 {
47 	pdf_obj *obj;
48 	int state;
49 } pdf_ocg_entry;
50 
51 typedef struct
52 {
53 	int ocg;
54 	const char *name;
55 	int depth;
56 	unsigned int button_flags : 2;
57 	unsigned int locked : 1;
58 } pdf_ocg_ui;
59 
60 struct pdf_ocg_descriptor
61 {
62 	int current;
63 	int num_configs;
64 
65 	int len;
66 	pdf_ocg_entry *ocgs;
67 
68 	pdf_obj *intent;
69 	const char *usage;
70 
71 	int num_ui_entries;
72 	pdf_ocg_ui *ui;
73 };
74 
75 int
pdf_count_layer_configs(fz_context * ctx,pdf_document * doc)76 pdf_count_layer_configs(fz_context *ctx, pdf_document *doc)
77 {
78 	/* If no OCProperties, then no OCGs */
79 	if (!doc || !doc->ocg)
80 		return 0;
81 	return doc->ocg->num_configs;
82 }
83 
84 static int
count_entries(fz_context * ctx,pdf_obj * obj)85 count_entries(fz_context *ctx, pdf_obj *obj)
86 {
87 	int len = pdf_array_len(ctx, obj);
88 	int i;
89 	int count = 0;
90 
91 	for (i = 0; i < len; i++)
92 	{
93 		pdf_obj *o = pdf_array_get(ctx, obj, i);
94 		if (pdf_mark_obj(ctx, o))
95 			continue;
96 		fz_try(ctx)
97 			count += (pdf_is_array(ctx, o) ? count_entries(ctx, o) : 1);
98 		fz_always(ctx)
99 			pdf_unmark_obj(ctx, o);
100 		fz_catch(ctx)
101 			fz_rethrow(ctx);
102 	}
103 	return count;
104 }
105 
106 static pdf_ocg_ui *
populate_ui(fz_context * ctx,pdf_ocg_descriptor * desc,pdf_ocg_ui * ui,pdf_obj * order,int depth,pdf_obj * rbgroups,pdf_obj * locked)107 populate_ui(fz_context *ctx, pdf_ocg_descriptor *desc, pdf_ocg_ui *ui, pdf_obj *order, int depth, pdf_obj *rbgroups, pdf_obj *locked)
108 {
109 	int len = pdf_array_len(ctx, order);
110 	int i, j;
111 
112 	for (i = 0; i < len; i++)
113 	{
114 		pdf_obj *o = pdf_array_get(ctx, order, i);
115 		if (pdf_is_array(ctx, o))
116 		{
117 			if (pdf_mark_obj(ctx, o))
118 				continue;
119 
120 			fz_try(ctx)
121 				ui = populate_ui(ctx, desc, ui, o, depth+1, rbgroups, locked);
122 			fz_always(ctx)
123 				pdf_unmark_obj(ctx, o);
124 			fz_catch(ctx)
125 				fz_rethrow(ctx);
126 
127 			continue;
128 		}
129 		ui->depth = depth;
130 		if (pdf_is_string(ctx, o))
131 		{
132 			ui->ocg = -1;
133 			ui->name = pdf_to_str_buf(ctx, o);
134 			ui->button_flags = PDF_LAYER_UI_LABEL;
135 			ui->locked = 1;
136 			ui++;
137 			continue;
138 		}
139 
140 		for (j = 0; j < desc->len; j++)
141 		{
142 			if (!pdf_objcmp_resolve(ctx, o, desc->ocgs[j].obj))
143 				break;
144 		}
145 		if (j == desc->len)
146 			continue; /* OCG not found in main list! Just ignore it */
147 		ui->ocg = j;
148 		ui->name = pdf_dict_get_string(ctx, o, PDF_NAME(Name), NULL);
149 		ui->button_flags = pdf_array_contains(ctx, o, rbgroups) ? PDF_LAYER_UI_RADIOBOX : PDF_LAYER_UI_CHECKBOX;
150 		ui->locked = pdf_array_contains(ctx, o, locked);
151 		ui++;
152 	}
153 	return ui;
154 }
155 
156 static void
drop_ui(fz_context * ctx,pdf_ocg_descriptor * desc)157 drop_ui(fz_context *ctx, pdf_ocg_descriptor *desc)
158 {
159 	if (!desc)
160 		return;
161 
162 	fz_free(ctx, desc->ui);
163 	desc->ui = NULL;
164 }
165 
166 static void
load_ui(fz_context * ctx,pdf_ocg_descriptor * desc,pdf_obj * ocprops,pdf_obj * occg)167 load_ui(fz_context *ctx, pdf_ocg_descriptor *desc, pdf_obj *ocprops, pdf_obj *occg)
168 {
169 	pdf_obj *order;
170 	pdf_obj *rbgroups;
171 	pdf_obj *locked;
172 	int count;
173 
174 	/* Count the number of entries */
175 	order = pdf_dict_get(ctx, occg, PDF_NAME(Order));
176 	if (!order)
177 		order = pdf_dict_getp(ctx, ocprops, "D/Order");
178 	count = count_entries(ctx, order);
179 	rbgroups = pdf_dict_get(ctx, occg, PDF_NAME(RBGroups));
180 	if (!rbgroups)
181 		rbgroups = pdf_dict_getp(ctx, ocprops, "D/RBGroups");
182 	locked = pdf_dict_get(ctx, occg, PDF_NAME(Locked));
183 
184 	desc->num_ui_entries = count;
185 	if (desc->num_ui_entries == 0)
186 		return;
187 
188 	desc->ui = Memento_label(fz_calloc(ctx, count, sizeof(pdf_ocg_ui)), "pdf_ocg_ui");
189 	fz_try(ctx)
190 	{
191 		(void)populate_ui(ctx, desc, desc->ui, order, 0, rbgroups, locked);
192 	}
193 	fz_catch(ctx)
194 	{
195 		drop_ui(ctx, desc);
196 		fz_rethrow(ctx);
197 	}
198 }
199 
200 void
pdf_select_layer_config(fz_context * ctx,pdf_document * doc,int config)201 pdf_select_layer_config(fz_context *ctx, pdf_document *doc, int config)
202 {
203 	int i, j, len, len2;
204 	pdf_ocg_descriptor *desc = doc->ocg;
205 	pdf_obj *obj, *cobj;
206 	pdf_obj *name;
207 
208 	obj = pdf_dict_get(ctx, pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)), PDF_NAME(OCProperties));
209 	if (!obj)
210 	{
211 		if (config == 0)
212 			return;
213 		else
214 			fz_throw(ctx, FZ_ERROR_GENERIC, "Unknown Layer config (None known!)");
215 	}
216 
217 	cobj = pdf_array_get(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Configs)), config);
218 	if (!cobj)
219 	{
220 		if (config != 0)
221 			fz_throw(ctx, FZ_ERROR_GENERIC, "Illegal Layer config");
222 		cobj = pdf_dict_get(ctx, obj, PDF_NAME(D));
223 		if (!cobj)
224 			fz_throw(ctx, FZ_ERROR_GENERIC, "No default Layer config");
225 	}
226 
227 	pdf_drop_obj(ctx, desc->intent);
228 	desc->intent = pdf_keep_obj(ctx, pdf_dict_get(ctx, cobj, PDF_NAME(Intent)));
229 
230 	len = desc->len;
231 	name = pdf_dict_get(ctx, cobj, PDF_NAME(BaseState));
232 	if (pdf_name_eq(ctx, name, PDF_NAME(Unchanged)))
233 	{
234 		/* Do nothing */
235 	}
236 	else if (pdf_name_eq(ctx, name, PDF_NAME(OFF)))
237 	{
238 		for (i = 0; i < len; i++)
239 		{
240 			desc->ocgs[i].state = 0;
241 		}
242 	}
243 	else /* Default to ON */
244 	{
245 		for (i = 0; i < len; i++)
246 		{
247 			desc->ocgs[i].state = 1;
248 		}
249 	}
250 
251 	obj = pdf_dict_get(ctx, cobj, PDF_NAME(ON));
252 	len2 = pdf_array_len(ctx, obj);
253 	for (i = 0; i < len2; i++)
254 	{
255 		pdf_obj *o = pdf_array_get(ctx, obj, i);
256 		for (j=0; j < len; j++)
257 		{
258 			if (!pdf_objcmp_resolve(ctx, desc->ocgs[j].obj, o))
259 			{
260 				desc->ocgs[j].state = 1;
261 				break;
262 			}
263 		}
264 	}
265 
266 	obj = pdf_dict_get(ctx, cobj, PDF_NAME(OFF));
267 	len2 = pdf_array_len(ctx, obj);
268 	for (i = 0; i < len2; i++)
269 	{
270 		pdf_obj *o = pdf_array_get(ctx, obj, i);
271 		for (j=0; j < len; j++)
272 		{
273 			if (!pdf_objcmp_resolve(ctx, desc->ocgs[j].obj, o))
274 			{
275 				desc->ocgs[j].state = 0;
276 				break;
277 			}
278 		}
279 	}
280 
281 	desc->current = config;
282 
283 	drop_ui(ctx, desc);
284 	load_ui(ctx, desc, obj, cobj);
285 }
286 
287 void
pdf_layer_config_info(fz_context * ctx,pdf_document * doc,int config_num,pdf_layer_config * info)288 pdf_layer_config_info(fz_context *ctx, pdf_document *doc, int config_num, pdf_layer_config *info)
289 {
290 	pdf_obj *ocprops;
291 	pdf_obj *obj;
292 
293 	if (!info)
294 		return;
295 
296 	info->name = NULL;
297 	info->creator = NULL;
298 
299 	if (doc == NULL || doc->ocg == NULL)
300 		return;
301 	if (config_num < 0 || config_num >= doc->ocg->num_configs)
302 		fz_throw(ctx, FZ_ERROR_GENERIC, "Invalid layer config number");
303 
304 	ocprops = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/OCProperties");
305 	if (!ocprops)
306 		return;
307 
308 	obj = pdf_dict_get(ctx, ocprops, PDF_NAME(Configs));
309 	if (pdf_is_array(ctx, obj))
310 		obj = pdf_array_get(ctx, obj, config_num);
311 	else if (config_num == 0)
312 		obj = pdf_dict_get(ctx, ocprops, PDF_NAME(D));
313 	else
314 		fz_throw(ctx, FZ_ERROR_GENERIC, "Invalid layer config number");
315 
316 	info->creator = pdf_dict_get_string(ctx, obj, PDF_NAME(Creator), NULL);
317 	info->name = pdf_dict_get_string(ctx, obj, PDF_NAME(Name), NULL);
318 }
319 
320 void
pdf_drop_ocg(fz_context * ctx,pdf_document * doc)321 pdf_drop_ocg(fz_context *ctx, pdf_document *doc)
322 {
323 	pdf_ocg_descriptor *desc;
324 	int i;
325 
326 	if (!doc)
327 		return;
328 	desc = doc->ocg;
329 	if (!desc)
330 		return;
331 
332 	drop_ui(ctx, desc);
333 	pdf_drop_obj(ctx, desc->intent);
334 	for (i = 0; i < desc->len; i++)
335 		pdf_drop_obj(ctx, desc->ocgs[i].obj);
336 	fz_free(ctx, desc->ocgs);
337 	fz_free(ctx, desc);
338 }
339 
340 static void
clear_radio_group(fz_context * ctx,pdf_document * doc,pdf_obj * ocg)341 clear_radio_group(fz_context *ctx, pdf_document *doc, pdf_obj *ocg)
342 {
343 	pdf_obj *rbgroups = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/OCProperties/RBGroups");
344 	int len, i;
345 
346 	len = pdf_array_len(ctx, rbgroups);
347 	for (i = 0; i < len; i++)
348 	{
349 		pdf_obj *group = pdf_array_get(ctx, rbgroups, i);
350 
351 		if (pdf_array_contains(ctx, ocg, group))
352 		{
353 			int len2 = pdf_array_len(ctx, group);
354 			int j;
355 
356 			for (j = 0; j < len2; j++)
357 			{
358 				pdf_obj *g = pdf_array_get(ctx, group, j);
359 				int k;
360 				for (k = 0; k < doc->ocg->len; k++)
361 				{
362 					pdf_ocg_entry *s = &doc->ocg->ocgs[k];
363 
364 					if (!pdf_objcmp_resolve(ctx, s->obj, g))
365 						s->state = 0;
366 				}
367 			}
368 		}
369 	}
370 }
371 
pdf_count_layer_config_ui(fz_context * ctx,pdf_document * doc)372 int pdf_count_layer_config_ui(fz_context *ctx, pdf_document *doc)
373 {
374 	if (doc == NULL || doc->ocg == NULL)
375 		return 0;
376 
377 	return doc->ocg->num_ui_entries;
378 }
379 
pdf_select_layer_config_ui(fz_context * ctx,pdf_document * doc,int ui)380 void pdf_select_layer_config_ui(fz_context *ctx, pdf_document *doc, int ui)
381 {
382 	pdf_ocg_ui *entry;
383 
384 	if (doc == NULL || doc->ocg == NULL)
385 		return;
386 
387 	if (ui < 0 || ui >= doc->ocg->num_ui_entries)
388 		fz_throw(ctx, FZ_ERROR_GENERIC, "Out of range UI entry selected");
389 
390 	entry = &doc->ocg->ui[ui];
391 	if (entry->button_flags != PDF_LAYER_UI_RADIOBOX &&
392 		entry->button_flags != PDF_LAYER_UI_CHECKBOX)
393 		return;
394 	if (entry->locked)
395 		return;
396 
397 	if (entry->button_flags == PDF_LAYER_UI_RADIOBOX)
398 		clear_radio_group(ctx, doc, doc->ocg->ocgs[entry->ocg].obj);
399 
400 	doc->ocg->ocgs[entry->ocg].state = 1;
401 }
402 
pdf_toggle_layer_config_ui(fz_context * ctx,pdf_document * doc,int ui)403 void pdf_toggle_layer_config_ui(fz_context *ctx, pdf_document *doc, int ui)
404 {
405 	pdf_ocg_ui *entry;
406 	int selected;
407 
408 	if (doc == NULL || doc->ocg == NULL)
409 		return;
410 
411 	if (ui < 0 || ui >= doc->ocg->num_ui_entries)
412 		fz_throw(ctx, FZ_ERROR_GENERIC, "Out of range UI entry toggled");
413 
414 	entry = &doc->ocg->ui[ui];
415 	if (entry->button_flags != PDF_LAYER_UI_RADIOBOX &&
416 		entry->button_flags != PDF_LAYER_UI_CHECKBOX)
417 		return;
418 	if (entry->locked)
419 		return;
420 
421 	selected = doc->ocg->ocgs[entry->ocg].state;
422 
423 	if (entry->button_flags == PDF_LAYER_UI_RADIOBOX)
424 		clear_radio_group(ctx, doc, doc->ocg->ocgs[entry->ocg].obj);
425 
426 	doc->ocg->ocgs[entry->ocg].state = !selected;
427 }
428 
pdf_deselect_layer_config_ui(fz_context * ctx,pdf_document * doc,int ui)429 void pdf_deselect_layer_config_ui(fz_context *ctx, pdf_document *doc, int ui)
430 {
431 	pdf_ocg_ui *entry;
432 
433 	if (doc == NULL || doc->ocg == NULL)
434 		return;
435 
436 	if (ui < 0 || ui >= doc->ocg->num_ui_entries)
437 		fz_throw(ctx, FZ_ERROR_GENERIC, "Out of range UI entry deselected");
438 
439 	entry = &doc->ocg->ui[ui];
440 	if (entry->button_flags != PDF_LAYER_UI_RADIOBOX &&
441 		entry->button_flags != PDF_LAYER_UI_CHECKBOX)
442 		return;
443 	if (entry->locked)
444 		return;
445 
446 	doc->ocg->ocgs[entry->ocg].state = 0;
447 }
448 
449 void
pdf_layer_config_ui_info(fz_context * ctx,pdf_document * doc,int ui,pdf_layer_config_ui * info)450 pdf_layer_config_ui_info(fz_context *ctx, pdf_document *doc, int ui, pdf_layer_config_ui *info)
451 {
452 	pdf_ocg_ui *entry;
453 
454 	if (!info)
455 		return;
456 
457 	info->depth = 0;
458 	info->locked = 0;
459 	info->selected = 0;
460 	info->text = NULL;
461 	info->type = 0;
462 
463 	if (doc == NULL || doc->ocg == NULL)
464 		return;
465 
466 	if (ui < 0 || ui >= doc->ocg->num_ui_entries)
467 		fz_throw(ctx, FZ_ERROR_GENERIC, "Out of range UI entry selected");
468 
469 	entry = &doc->ocg->ui[ui];
470 	info->type = entry->button_flags;
471 	info->depth = entry->depth;
472 	info->selected = doc->ocg->ocgs[entry->ocg].state;
473 	info->locked = entry->locked;
474 	info->text = entry->name;
475 }
476 
477 static int
ocg_intents_include(fz_context * ctx,pdf_ocg_descriptor * desc,const char * name)478 ocg_intents_include(fz_context *ctx, pdf_ocg_descriptor *desc, const char *name)
479 {
480 	int i, len;
481 
482 	if (strcmp(name, "All") == 0)
483 		return 1;
484 
485 	/* In the absence of a specified intent, it's 'View' */
486 	if (!desc->intent)
487 		return (strcmp(name, "View") == 0);
488 
489 	if (pdf_is_name(ctx, desc->intent))
490 	{
491 		const char *intent = pdf_to_name(ctx, desc->intent);
492 		if (strcmp(intent, "All") == 0)
493 			return 1;
494 		return (strcmp(intent, name) == 0);
495 	}
496 	if (!pdf_is_array(ctx, desc->intent))
497 		return 0;
498 
499 	len = pdf_array_len(ctx, desc->intent);
500 	for (i=0; i < len; i++)
501 	{
502 		const char *intent = pdf_to_name(ctx, pdf_array_get(ctx, desc->intent, i));
503 		if (strcmp(intent, "All") == 0)
504 			return 1;
505 		if (strcmp(intent, name) == 0)
506 			return 1;
507 	}
508 	return 0;
509 }
510 
511 int
pdf_is_hidden_ocg(fz_context * ctx,pdf_ocg_descriptor * desc,pdf_obj * rdb,const char * usage,pdf_obj * ocg)512 pdf_is_hidden_ocg(fz_context *ctx, pdf_ocg_descriptor *desc, pdf_obj *rdb, const char *usage, pdf_obj *ocg)
513 {
514 	char event_state[16];
515 	pdf_obj *obj, *obj2, *type;
516 
517 	/* Avoid infinite recursions */
518 	if (pdf_obj_marked(ctx, ocg))
519 		return 0;
520 
521 	/* If no usage, everything is visible */
522 	if (!usage)
523 		return 0;
524 
525 	/* If no ocg descriptor, everything is visible */
526 	if (!desc)
527 		return 0;
528 
529 	/* If we've been handed a name, look it up in the properties. */
530 	if (pdf_is_name(ctx, ocg))
531 	{
532 		ocg = pdf_dict_get(ctx, pdf_dict_get(ctx, rdb, PDF_NAME(Properties)), ocg);
533 	}
534 	/* If we haven't been given an ocg at all, then we're visible */
535 	if (!ocg)
536 		return 0;
537 
538 	fz_strlcpy(event_state, usage, sizeof event_state);
539 	fz_strlcat(event_state, "State", sizeof event_state);
540 
541 	type = pdf_dict_get(ctx, ocg, PDF_NAME(Type));
542 
543 	if (pdf_name_eq(ctx, type, PDF_NAME(OCG)))
544 	{
545 		/* An Optional Content Group */
546 		int default_value = 0;
547 		int len = desc->len;
548 		int i;
549 		pdf_obj *es;
550 
551 		/* by default an OCG is visible, unless it's explicitly hidden */
552 		for (i = 0; i < len; i++)
553 		{
554 			if (!pdf_objcmp_resolve(ctx, desc->ocgs[i].obj, ocg))
555 			{
556 				default_value = !desc->ocgs[i].state;
557 				break;
558 			}
559 		}
560 
561 		/* Check Intents; if our intent is not part of the set given
562 		 * by the current config, we should ignore it. */
563 		obj = pdf_dict_get(ctx, ocg, PDF_NAME(Intent));
564 		if (pdf_is_name(ctx, obj))
565 		{
566 			/* If it doesn't match, it's hidden */
567 			if (ocg_intents_include(ctx, desc, pdf_to_name(ctx, obj)) == 0)
568 				return 1;
569 		}
570 		else if (pdf_is_array(ctx, obj))
571 		{
572 			int match = 0;
573 			len = pdf_array_len(ctx, obj);
574 			for (i=0; i<len; i++) {
575 				match |= ocg_intents_include(ctx, desc, pdf_to_name(ctx, pdf_array_get(ctx, obj, i)));
576 				if (match)
577 					break;
578 			}
579 			/* If we don't match any, it's hidden */
580 			if (match == 0)
581 				return 1;
582 		}
583 		else
584 		{
585 			/* If it doesn't match, it's hidden */
586 			if (ocg_intents_include(ctx, desc, "View") == 0)
587 				return 1;
588 		}
589 
590 		/* FIXME: Currently we do a very simple check whereby we look
591 		 * at the Usage object (an Optional Content Usage Dictionary)
592 		 * and check to see if the corresponding 'event' key is on
593 		 * or off.
594 		 *
595 		 * Really we should only look at Usage dictionaries that
596 		 * correspond to entries in the AS list in the OCG config.
597 		 * Given that we don't handle Zoom or User, or Language
598 		 * dicts, this is not really a problem. */
599 		obj = pdf_dict_get(ctx, ocg, PDF_NAME(Usage));
600 		if (!pdf_is_dict(ctx, obj))
601 			return default_value;
602 		/* FIXME: Should look at Zoom (and return hidden if out of
603 		 * max/min range) */
604 		/* FIXME: Could provide hooks to the caller to check if
605 		 * User is appropriate - if not return hidden. */
606 		obj2 = pdf_dict_gets(ctx, obj, usage);
607 		es = pdf_dict_gets(ctx, obj2, event_state);
608 		if (pdf_name_eq(ctx, es, PDF_NAME(OFF)))
609 		{
610 			return 1;
611 		}
612 		if (pdf_name_eq(ctx, es, PDF_NAME(ON)))
613 		{
614 			return 0;
615 		}
616 		return default_value;
617 	}
618 	else if (pdf_name_eq(ctx, type, PDF_NAME(OCMD)))
619 	{
620 		/* An Optional Content Membership Dictionary */
621 		pdf_obj *name;
622 		int combine, on = 0;
623 
624 		obj = pdf_dict_get(ctx, ocg, PDF_NAME(VE));
625 		if (pdf_is_array(ctx, obj)) {
626 			/* FIXME: Calculate visibility from array */
627 			return 0;
628 		}
629 		name = pdf_dict_get(ctx, ocg, PDF_NAME(P));
630 		/* Set combine; Bit 0 set => AND, Bit 1 set => true means
631 		 * Off, otherwise true means On */
632 		if (pdf_name_eq(ctx, name, PDF_NAME(AllOn)))
633 		{
634 			combine = 1;
635 		}
636 		else if (pdf_name_eq(ctx, name, PDF_NAME(AnyOff)))
637 		{
638 			combine = 2;
639 		}
640 		else if (pdf_name_eq(ctx, name, PDF_NAME(AllOff)))
641 		{
642 			combine = 3;
643 		}
644 		else /* Assume it's the default (AnyOn) */
645 		{
646 			combine = 0;
647 		}
648 
649 		if (pdf_mark_obj(ctx, ocg))
650 			return 0; /* Should never happen */
651 		fz_try(ctx)
652 		{
653 			obj = pdf_dict_get(ctx, ocg, PDF_NAME(OCGs));
654 			on = combine & 1;
655 			if (pdf_is_array(ctx, obj)) {
656 				int i, len;
657 				len = pdf_array_len(ctx, obj);
658 				for (i = 0; i < len; i++)
659 				{
660 					int hidden = pdf_is_hidden_ocg(ctx, desc, rdb, usage, pdf_array_get(ctx, obj, i));
661 					if ((combine & 1) == 0)
662 						hidden = !hidden;
663 					if (combine & 2)
664 						on &= hidden;
665 					else
666 						on |= hidden;
667 				}
668 			}
669 			else
670 			{
671 				on = pdf_is_hidden_ocg(ctx, desc, rdb, usage, obj);
672 				if ((combine & 1) == 0)
673 					on = !on;
674 			}
675 		}
676 		fz_always(ctx)
677 		{
678 			pdf_unmark_obj(ctx, ocg);
679 		}
680 		fz_catch(ctx)
681 		{
682 			fz_rethrow(ctx);
683 		}
684 		return !on;
685 	}
686 	/* No idea what sort of object this is - be visible */
687 	return 0;
688 }
689 
690 void
pdf_read_ocg(fz_context * ctx,pdf_document * doc)691 pdf_read_ocg(fz_context *ctx, pdf_document *doc)
692 {
693 	pdf_obj *obj, *ocg, *configs;
694 	int len, i, num_configs;
695 	pdf_ocg_descriptor *desc;
696 
697 	fz_var(desc);
698 
699 	obj = pdf_dict_get(ctx, pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)), PDF_NAME(OCProperties));
700 	if (!obj)
701 		return;
702 
703 	configs = pdf_dict_get(ctx, obj, PDF_NAME(Configs));
704 	if (configs == NULL)
705 		num_configs = 1;
706 	else if (!pdf_is_array(ctx, configs))
707 		fz_throw(ctx, FZ_ERROR_GENERIC, "Invalid Configs value");
708 	else
709 		num_configs = pdf_array_len(ctx, configs);
710 
711 	ocg = pdf_dict_get(ctx, obj, PDF_NAME(OCGs));
712 	if (!ocg || !pdf_is_array(ctx, ocg))
713 		/* Not ever supposed to happen, but live with it. */
714 		return;
715 	len = pdf_array_len(ctx, ocg);
716 
717 	desc = fz_malloc_struct(ctx, pdf_ocg_descriptor);
718 	desc->ocgs = NULL;
719 
720 	fz_try(ctx)
721 	{
722 		desc->num_configs = num_configs;
723 		desc->len = len;
724 		desc->ocgs = fz_calloc(ctx, len, sizeof(*desc->ocgs));
725 		desc->intent = NULL;
726 		for (i=0; i < len; i++)
727 		{
728 			pdf_obj *o = pdf_array_get(ctx, ocg, i);
729 			desc->ocgs[i].obj = pdf_keep_obj(ctx, o);
730 			desc->ocgs[i].state = 1;
731 		}
732 		doc->ocg = desc;
733 	}
734 	fz_catch(ctx)
735 	{
736 		fz_free(ctx, desc->ocgs);
737 		fz_free(ctx, desc);
738 		fz_rethrow(ctx);
739 	}
740 
741 	pdf_select_layer_config(ctx, doc, 0);
742 }
743 
744 void
pdf_set_layer_config_as_default(fz_context * ctx,pdf_document * doc)745 pdf_set_layer_config_as_default(fz_context *ctx, pdf_document *doc)
746 {
747 	pdf_obj *ocprops, *d, *order, *on, *configs, *rbgroups;
748 	int k;
749 
750 	if (doc == NULL || doc->ocg == NULL)
751 		return;
752 
753 	ocprops = pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/OCProperties");
754 	if (!ocprops)
755 		return;
756 
757 	/* All files with OCGs are required to have a D entry */
758 	d = pdf_dict_get(ctx, ocprops, PDF_NAME(D));
759 	if (d == NULL)
760 		return;
761 
762 	pdf_dict_put(ctx, d, PDF_NAME(BaseState), PDF_NAME(OFF));
763 
764 	/* We are about to delete RBGroups and Order, from D. These are
765 	 * both the underlying defaults for other configs, so copy the
766 	 * current values out to any config that doesn't have one
767 	 * already. */
768 	order = pdf_dict_get(ctx, d, PDF_NAME(Order));
769 	rbgroups = pdf_dict_get(ctx, d, PDF_NAME(RBGroups));
770 	configs = pdf_dict_get(ctx, ocprops, PDF_NAME(Configs));
771 	if (configs)
772 	{
773 		int len = pdf_array_len(ctx, configs);
774 		for (k=0; k < len; k++)
775 		{
776 			pdf_obj *config = pdf_array_get(ctx, configs, k);
777 
778 			if (order && !pdf_dict_get(ctx, config, PDF_NAME(Order)))
779 				pdf_dict_put(ctx, config, PDF_NAME(Order), order);
780 			if (rbgroups && !pdf_dict_get(ctx, config, PDF_NAME(RBGroups)))
781 				pdf_dict_put(ctx, config, PDF_NAME(RBGroups), rbgroups);
782 		}
783 	}
784 
785 	/* Offer all the layers in the UI */
786 	order = pdf_new_array(ctx, doc, 4);
787 	on = pdf_new_array(ctx, doc, 4);
788 	for (k = 0; k < doc->ocg->len; k++)
789 	{
790 		pdf_ocg_entry *s = &doc->ocg->ocgs[k];
791 
792 		pdf_array_push(ctx, order, s->obj);
793 		if (s->state)
794 			pdf_array_push(ctx, on, s->obj);
795 	}
796 	pdf_dict_put(ctx, d, PDF_NAME(Order), order);
797 	pdf_dict_put(ctx, d, PDF_NAME(ON), on);
798 	pdf_dict_del(ctx, d, PDF_NAME(OFF));
799 	pdf_dict_del(ctx, d, PDF_NAME(AS));
800 	pdf_dict_put(ctx, d, PDF_NAME(Intent), PDF_NAME(View));
801 	pdf_dict_del(ctx, d, PDF_NAME(Name));
802 	pdf_dict_del(ctx, d, PDF_NAME(Creator));
803 	pdf_dict_del(ctx, d, PDF_NAME(RBGroups));
804 	pdf_dict_del(ctx, d, PDF_NAME(Locked));
805 
806 	pdf_dict_del(ctx, ocprops, PDF_NAME(Configs));
807 }
808