1 //========================================================================
2 //
3 // OptionalContent.cc
4 //
5 // Copyright 2008-2013 Glyph & Cog, LLC
6 //
7 //========================================================================
8 
9 #include <aconf.h>
10 
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14 
15 #include "gmempp.h"
16 #include "GString.h"
17 #include "GList.h"
18 #include "Error.h"
19 #include "Object.h"
20 #include "PDFDoc.h"
21 #include "TextString.h"
22 #include "OptionalContent.h"
23 
24 //------------------------------------------------------------------------
25 
26 #define ocPolicyAllOn  1
27 #define ocPolicyAnyOn  2
28 #define ocPolicyAnyOff 3
29 #define ocPolicyAllOff 4
30 
31 //------------------------------------------------------------------------
32 
33 // Max depth of nested visibility expressions.  This is used to catch
34 // infinite loops in the visibility expression object structure.
35 #define visibilityExprRecursionLimit 50
36 
37 // Max depth of nested display nodes.  This is used to catch infinite
38 // loops in the "Order" object structure.
39 #define displayNodeRecursionLimit 50
40 
41 //------------------------------------------------------------------------
42 
OptionalContent(PDFDoc * doc)43 OptionalContent::OptionalContent(PDFDoc *doc) {
44   Object *ocProps;
45   Object ocgList, defView, uad, obj1, obj2, obj3, obj4;
46   Ref ref1;
47   OptionalContentGroup *ocg;
48   int i, j;
49 
50   xref = doc->getXRef();
51   ocgs = new GList();
52   display = NULL;
53 
54   if ((ocProps = doc->getCatalog()->getOCProperties())->isDict()) {
55     if (ocProps->dictLookup("OCGs", &ocgList)->isArray()) {
56 
57       //----- read the OCG list
58       for (i = 0; i < ocgList.arrayGetLength(); ++i) {
59 	if (ocgList.arrayGetNF(i, &obj1)->isRef()) {
60 	  ref1 = obj1.getRef();
61 	  obj1.fetch(xref, &obj2);
62 	  if ((ocg = OptionalContentGroup::parse(&ref1, &obj2))) {
63 	    ocgs->append(ocg);
64 	  }
65 	  obj2.free();
66 	}
67 	obj1.free();
68       }
69 
70       //----- read the default viewing OCCD
71       if (ocProps->dictLookup("D", &defView)->isDict()) {
72 
73 	//----- read the usage app dicts
74 	if (defView.dictLookup("AS", &obj1)->isArray()) {
75 	  for (i = 0; i < obj1.arrayGetLength(); ++i) {
76 	    if (obj1.arrayGet(i, &uad)->isDict()) {
77 	      if (uad.dictLookup("Event", &obj2)->isName("View")) {
78 		if (uad.dictLookup("OCGs", &obj3)->isArray()) {
79 		  for (j = 0; j < obj3.arrayGetLength(); ++j) {
80 		    if (obj3.arrayGetNF(j, &obj4)->isRef()) {
81 		      ref1 = obj4.getRef();
82 		      if ((ocg = findOCG(&ref1))) {
83 			ocg->setInViewUsageAppDict();
84 		      }
85 		    }
86 		    obj4.free();
87 		  }
88 		}
89 		obj3.free();
90 	      }
91 	      obj2.free();
92 	    }
93 	    uad.free();
94 	  }
95 	}
96 	obj1.free();
97 
98 	//----- initial state from OCCD
99 	if (defView.dictLookup("OFF", &obj1)->isArray()) {
100 	  for (i = 0; i < obj1.arrayGetLength(); ++i) {
101 	    if (obj1.arrayGetNF(i, &obj2)->isRef()) {
102 	      ref1 = obj2.getRef();
103 	      if ((ocg = findOCG(&ref1))) {
104 		ocg->setState(gFalse);
105 	      } else {
106 		error(errSyntaxError, -1,
107 		      "Invalid OCG reference in OFF array in default viewing OCCD");
108 	      }
109 	    }
110 	    obj2.free();
111 	  }
112 	}
113 	obj1.free();
114 
115 	//----- initial state from OCG usage dict
116 	for (i = 0; i < ocgs->getLength(); ++i) {
117 	  ocg = (OptionalContentGroup *)ocgs->get(i);
118 	  if (ocg->getInViewUsageAppDict() &&
119 	      ocg->getViewState() != ocUsageUnset) {
120 	    ocg->setState(ocg->getViewState() == ocUsageOn);
121 	  }
122 	}
123 
124 	//----- display order
125 	if (defView.dictLookup("Order", &obj1)->isArray()) {
126 	  display = OCDisplayNode::parse(&obj1, this, xref);
127 	}
128 	obj1.free();
129 
130       } else {
131 	error(errSyntaxError, -1, "Missing or invalid default viewing OCCD");
132       }
133       defView.free();
134 
135     }
136     ocgList.free();
137   }
138 
139   if (!display) {
140     display = new OCDisplayNode();
141   }
142 }
143 
~OptionalContent()144 OptionalContent::~OptionalContent() {
145   deleteGList(ocgs, OptionalContentGroup);
146   delete display;
147 }
148 
getNumOCGs()149 int OptionalContent::getNumOCGs() {
150   return ocgs->getLength();
151 }
152 
getOCG(int idx)153 OptionalContentGroup *OptionalContent::getOCG(int idx) {
154   return (OptionalContentGroup *)ocgs->get(idx);
155 }
156 
findOCG(Ref * ref)157 OptionalContentGroup *OptionalContent::findOCG(Ref *ref) {
158   OptionalContentGroup *ocg;
159   int i;
160 
161   for (i = 0; i < ocgs->getLength(); ++i) {
162     ocg = (OptionalContentGroup *)ocgs->get(i);
163     if (ocg->matches(ref)) {
164       return ocg;
165     }
166   }
167   return NULL;
168 }
169 
evalOCObject(Object * obj,GBool * visible)170 GBool OptionalContent::evalOCObject(Object *obj, GBool *visible) {
171   OptionalContentGroup *ocg;
172   int policy;
173   Ref ref;
174   Object obj2, obj3, obj4, obj5;
175   GBool gotOCG;
176   int i;
177 
178   if (obj->isNull()) {
179     return gFalse;
180   }
181   if (obj->isRef()) {
182     ref = obj->getRef();
183     if ((ocg = findOCG(&ref))) {
184       *visible = ocg->getState();
185       return gTrue;
186     }
187   }
188   obj->fetch(xref, &obj2);
189   if (!obj2.isDict("OCMD")) {
190     obj2.free();
191     return gFalse;
192   }
193   if (obj2.dictLookup("VE", &obj3)->isArray()) {
194     *visible = evalOCVisibilityExpr(&obj3, 0);
195     obj3.free();
196   } else {
197     obj3.free();
198     policy = ocPolicyAnyOn;
199     if (obj2.dictLookup("P", &obj3)->isName()) {
200       if (obj3.isName("AllOn")) {
201 	policy = ocPolicyAllOn;
202       } else if (obj3.isName("AnyOn")) {
203 	policy = ocPolicyAnyOn;
204       } else if (obj3.isName("AnyOff")) {
205 	policy = ocPolicyAnyOff;
206       } else if (obj3.isName("AllOff")) {
207 	policy = ocPolicyAllOff;
208       }
209     }
210     obj3.free();
211     obj2.dictLookupNF("OCGs", &obj3);
212     ocg = NULL;
213     if (obj3.isRef()) {
214       ref = obj3.getRef();
215       ocg = findOCG(&ref);
216     }
217     if (ocg) {
218       *visible = (policy == ocPolicyAllOn || policy == ocPolicyAnyOn) ?
219 	           ocg->getState() : !ocg->getState();
220     } else {
221       *visible = policy == ocPolicyAllOn || policy == ocPolicyAllOff;
222       if (!obj3.fetch(xref, &obj4)->isArray()) {
223 	obj4.free();
224 	obj3.free();
225 	obj2.free();
226 	return gFalse;
227       }
228       gotOCG = gFalse;
229       for (i = 0; i < obj4.arrayGetLength(); ++i) {
230 	obj4.arrayGetNF(i, &obj5);
231 	if (obj5.isRef()) {
232 	  ref = obj5.getRef();
233 	  if ((ocg = findOCG(&ref))) {
234 	    gotOCG = gTrue;
235 	    switch (policy) {
236 	    case ocPolicyAllOn:
237 	      *visible = *visible && ocg->getState();
238 	      break;
239 	    case ocPolicyAnyOn:
240 	      *visible = *visible || ocg->getState();
241 	      break;
242 	    case ocPolicyAnyOff:
243 	      *visible = *visible || !ocg->getState();
244 	      break;
245 	    case ocPolicyAllOff:
246 	      *visible = *visible && !ocg->getState();
247 	      break;
248 	    }
249 	  }
250 	}
251 	obj5.free();
252       }
253       if (!gotOCG) {
254 	// if the "OCGs" array is "empty or contains references only
255 	// to null or deleted objects", this OCMD doesn't have any
256 	// effect
257 	obj4.free();
258 	obj3.free();
259 	obj2.free();
260 	return gFalse;
261       }
262       obj4.free();
263     }
264     obj3.free();
265   }
266   obj2.free();
267   return gTrue;
268 }
269 
evalOCVisibilityExpr(Object * expr,int recursion)270 GBool OptionalContent::evalOCVisibilityExpr(Object *expr, int recursion) {
271   OptionalContentGroup *ocg;
272   Object expr2, op, obj;
273   Ref ref;
274   GBool ret;
275   int i;
276 
277   if (recursion > visibilityExprRecursionLimit) {
278     error(errSyntaxError, -1,
279 	  "Loop detected in optional content visibility expression");
280     return gTrue;
281   }
282   if (expr->isRef()) {
283     ref = expr->getRef();
284     if ((ocg = findOCG(&ref))) {
285       return ocg->getState();
286     }
287   }
288   expr->fetch(xref, &expr2);
289   if (!expr2.isArray() || expr2.arrayGetLength() < 1) {
290     error(errSyntaxError, -1,
291 	  "Invalid optional content visibility expression");
292     expr2.free();
293     return gTrue;
294   }
295   expr2.arrayGet(0, &op);
296   if (op.isName("Not")) {
297     if (expr2.arrayGetLength() == 2) {
298       expr2.arrayGetNF(1, &obj);
299       ret = !evalOCVisibilityExpr(&obj, recursion + 1);
300       obj.free();
301     } else {
302       error(errSyntaxError, -1,
303 	    "Invalid optional content visibility expression");
304       ret = gTrue;
305     }
306   } else if (op.isName("And")) {
307     ret = gTrue;
308     for (i = 1; i < expr2.arrayGetLength() && ret; ++i) {
309       expr2.arrayGetNF(i, &obj);
310       ret = evalOCVisibilityExpr(&obj, recursion + 1);
311       obj.free();
312     }
313   } else if (op.isName("Or")) {
314     ret = gFalse;
315     for (i = 1; i < expr2.arrayGetLength() && !ret; ++i) {
316       expr2.arrayGetNF(i, &obj);
317       ret = evalOCVisibilityExpr(&obj, recursion + 1);
318       obj.free();
319     }
320   } else {
321     error(errSyntaxError, -1,
322 	  "Invalid optional content visibility expression");
323     ret = gTrue;
324   }
325   op.free();
326   expr2.free();
327   return ret;
328 }
329 
330 //------------------------------------------------------------------------
331 
parse(Ref * refA,Object * obj)332 OptionalContentGroup *OptionalContentGroup::parse(Ref *refA, Object *obj) {
333   TextString *nameA;
334   Object obj1, obj2, obj3;
335   OCUsageState viewStateA, printStateA;
336 
337   if (!obj->isDict()) {
338     return NULL;
339   }
340   if (!obj->dictLookup("Name", &obj1)->isString()) {
341     error(errSyntaxError, -1, "Missing or invalid Name in OCG");
342     obj1.free();
343     return NULL;
344   }
345   nameA = new TextString(obj1.getString());
346   obj1.free();
347 
348   viewStateA = printStateA = ocUsageUnset;
349   if (obj->dictLookup("Usage", &obj1)->isDict()) {
350     if (obj1.dictLookup("View", &obj2)->isDict()) {
351       if (obj2.dictLookup("ViewState", &obj3)->isName()) {
352 	if (obj3.isName("ON")) {
353 	  viewStateA = ocUsageOn;
354 	} else {
355 	  viewStateA = ocUsageOff;
356 	}
357       }
358       obj3.free();
359     }
360     obj2.free();
361     if (obj1.dictLookup("Print", &obj2)->isDict()) {
362       if (obj2.dictLookup("PrintState", &obj3)->isName()) {
363 	if (obj3.isName("ON")) {
364 	  printStateA = ocUsageOn;
365 	} else {
366 	  printStateA = ocUsageOff;
367 	}
368       }
369       obj3.free();
370     }
371     obj2.free();
372   }
373   obj1.free();
374 
375   return new OptionalContentGroup(refA, nameA, viewStateA, printStateA);
376 }
377 
OptionalContentGroup(Ref * refA,TextString * nameA,OCUsageState viewStateA,OCUsageState printStateA)378 OptionalContentGroup::OptionalContentGroup(Ref *refA, TextString *nameA,
379 					   OCUsageState viewStateA,
380 					   OCUsageState printStateA) {
381   ref = *refA;
382   name = nameA;
383   viewState = viewStateA;
384   printState = printStateA;
385   state = gTrue;
386   inViewUsageAppDict = gFalse;
387 }
388 
~OptionalContentGroup()389 OptionalContentGroup::~OptionalContentGroup() {
390   delete name;
391 }
392 
matches(Ref * refA)393 GBool OptionalContentGroup::matches(Ref *refA) {
394   return refA->num == ref.num && refA->gen == ref.gen;
395 }
396 
getName()397 Unicode *OptionalContentGroup::getName() {
398   return name->getUnicode();
399 }
400 
getNameLength()401 int OptionalContentGroup::getNameLength() {
402   return name->getLength();
403 }
404 
405 //------------------------------------------------------------------------
406 
parse(Object * obj,OptionalContent * oc,XRef * xref,int recursion)407 OCDisplayNode *OCDisplayNode::parse(Object *obj, OptionalContent *oc,
408 				    XRef *xref, int recursion) {
409   Object obj2, obj3;
410   Ref ref;
411   OptionalContentGroup *ocgA;
412   OCDisplayNode *node, *child;
413   int i;
414 
415   if (recursion > displayNodeRecursionLimit) {
416     error(errSyntaxError, -1, "Loop detected in optional content order");
417     return NULL;
418   }
419   if (obj->isRef()) {
420     ref = obj->getRef();
421     if ((ocgA = oc->findOCG(&ref))) {
422       return new OCDisplayNode(ocgA);
423     }
424   }
425   obj->fetch(xref, &obj2);
426   if (!obj2.isArray()) {
427     obj2.free();
428     return NULL;
429   }
430   i = 0;
431   if (obj2.arrayGetLength() >= 1) {
432     if (obj2.arrayGet(0, &obj3)->isString()) {
433       node = new OCDisplayNode(obj3.getString());
434       i = 1;
435     } else {
436       node = new OCDisplayNode();
437     }
438     obj3.free();
439   } else {
440     node = new OCDisplayNode();
441   }
442   for (; i < obj2.arrayGetLength(); ++i) {
443     obj2.arrayGetNF(i, &obj3);
444     if ((child = OCDisplayNode::parse(&obj3, oc, xref, recursion + 1))) {
445       if (!child->ocg && !child->name && node->getNumChildren() > 0) {
446 	if (child->getNumChildren() > 0) {
447 	  node->getChild(node->getNumChildren() - 1)->
448 	            addChildren(child->takeChildren());
449 	}
450 	delete child;
451       } else {
452 	node->addChild(child);
453       }
454     }
455     obj3.free();
456   }
457   obj2.free();
458   return node;
459 }
460 
OCDisplayNode()461 OCDisplayNode::OCDisplayNode() {
462   name = new TextString();
463   ocg = NULL;
464   parent = NULL;
465   children = NULL;
466 }
467 
OCDisplayNode(GString * nameA)468 OCDisplayNode::OCDisplayNode(GString *nameA) {
469   name = new TextString(nameA);
470   ocg = NULL;
471   children = NULL;
472 }
473 
OCDisplayNode(OptionalContentGroup * ocgA)474 OCDisplayNode::OCDisplayNode(OptionalContentGroup *ocgA) {
475   name = new TextString(ocgA->name);
476   ocg = ocgA;
477   children = NULL;
478 }
479 
addChild(OCDisplayNode * child)480 void OCDisplayNode::addChild(OCDisplayNode *child) {
481   if (!children) {
482     children = new GList();
483   }
484   children->append(child);
485   child->parent = this;
486 }
487 
addChildren(GList * childrenA)488 void OCDisplayNode::addChildren(GList *childrenA) {
489   int i;
490 
491   if (!children) {
492     children = new GList();
493   }
494   children->append(childrenA);
495   for (i = 0; i < childrenA->getLength(); ++i) {
496     ((OCDisplayNode *)childrenA->get(i))->parent = this;
497   }
498   delete childrenA;
499 }
500 
takeChildren()501 GList *OCDisplayNode::takeChildren() {
502   GList *childrenA;
503   int i;
504 
505   childrenA = children;
506   children = NULL;
507   for (i = 0; i < childrenA->getLength(); ++i) {
508     ((OCDisplayNode *)childrenA->get(i))->parent = NULL;
509   }
510   return childrenA;
511 }
512 
~OCDisplayNode()513 OCDisplayNode::~OCDisplayNode() {
514   delete name;
515   if (children) {
516     deleteGList(children, OCDisplayNode);
517   }
518 }
519 
getName()520 Unicode *OCDisplayNode::getName() {
521   return name->getUnicode();
522 }
523 
getNameLength()524 int OCDisplayNode::getNameLength() {
525   return name->getLength();
526 }
527 
getNumChildren()528 int OCDisplayNode::getNumChildren() {
529   if (!children) {
530     return 0;
531   }
532   return children->getLength();
533 }
534 
getChild(int idx)535 OCDisplayNode *OCDisplayNode::getChild(int idx) {
536   return (OCDisplayNode *)children->get(idx);
537 }
538