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