beta-0.89.2
[luatex.git] / source / libs / poppler / poppler-src / poppler / OptionalContent.cc
blobe0d49bc9a5feb300145aa7aa862da54e7d90bfda
1 //========================================================================
2 //
3 // OptionalContent.cc
4 //
5 // Copyright 2007 Brad Hards <bradh@kde.org>
6 // Copyright 2008 Pino Toscano <pino@kde.org>
7 // Copyright 2008, 2010 Carlos Garcia Campos <carlosgc@gnome.org>
8 // Copyright 2008, 2010, 2011 Albert Astals Cid <aacid@kde.org>
9 // Copyright 2008 Mark Kaplan <mkaplan@finjan.com>
11 // Released under the GPL (version 2, or later, at your option)
13 //========================================================================
15 #include <config.h>
17 #ifdef USE_GCC_PRAGMAS
18 #pragma implementation
19 #endif
21 #include "goo/gmem.h"
22 #include "goo/GooString.h"
23 #include "goo/GooList.h"
24 #include "Error.h"
25 // #include "PDFDocEncoding.h"
26 #include "OptionalContent.h"
28 // Max depth of nested visibility expressions. This is used to catch
29 // infinite loops in the visibility expression object structure.
30 #define visibilityExprRecursionLimit 50
32 // Max depth of nested display nodes. This is used to catch infinite
33 // loops in the "Order" object structure.
34 #define displayNodeRecursionLimit 50
36 //------------------------------------------------------------------------
38 OCGs::OCGs(Object *ocgObject, XRef *xref) :
39 m_xref(xref)
41 // we need to parse the dictionary here, and build optionalContentGroups
42 ok = gTrue;
43 optionalContentGroups = new GooList();
44 display = NULL;
46 Object ocgList;
47 ocgObject->dictLookup("OCGs", &ocgList);
48 if (!ocgList.isArray()) {
49 error(errSyntaxError, -1, "Expected the optional content group list, but wasn't able to find it, or it isn't an Array");
50 ocgList.free();
51 ok = gFalse;
52 return;
55 // we now enumerate over the ocgList, and build up the optionalContentGroups list.
56 for(int i = 0; i < ocgList.arrayGetLength(); ++i) {
57 Object ocg;
58 ocgList.arrayGet(i, &ocg);
59 if (!ocg.isDict()) {
60 ocg.free();
61 break;
63 OptionalContentGroup *thisOptionalContentGroup = new OptionalContentGroup(ocg.getDict());
64 ocg.free();
65 ocgList.arrayGetNF(i, &ocg);
66 // TODO: we should create a lookup map from Ref to the OptionalContentGroup
67 thisOptionalContentGroup->setRef( ocg.getRef() );
68 ocg.free();
69 // the default is ON - we change state later, depending on BaseState, ON and OFF
70 thisOptionalContentGroup->setState(OptionalContentGroup::On);
71 optionalContentGroups->append(thisOptionalContentGroup);
74 Object defaultOcgConfig;
75 ocgObject->dictLookup("D", &defaultOcgConfig);
76 if (!defaultOcgConfig.isDict()) {
77 error(errSyntaxError, -1, "Expected the default config, but wasn't able to find it, or it isn't a Dictionary");
78 defaultOcgConfig.free();
79 ocgList.free();
80 ok = gFalse;
81 return;
84 Object baseState;
85 defaultOcgConfig.dictLookup("BaseState", &baseState);
86 if (baseState.isName("OFF")) {
87 for (int i = 0; i < optionalContentGroups->getLength(); ++i) {
88 OptionalContentGroup *group;
90 group = (OptionalContentGroup *)optionalContentGroups->get(i);
91 group->setState(OptionalContentGroup::Off);
94 baseState.free();
96 Object on;
97 defaultOcgConfig.dictLookup("ON", &on);
98 if (on.isArray()) {
99 // ON is an optional element
100 for (int i = 0; i < on.arrayGetLength(); ++i) {
101 Object reference;
102 on.arrayGetNF(i, &reference);
103 if (!reference.isRef()) {
104 // there can be null entries
105 reference.free();
106 break;
108 OptionalContentGroup *group = findOcgByRef( reference.getRef() );
109 reference.free();
110 if (!group) {
111 error(errSyntaxWarning, -1, "Couldn't find group for reference");
112 break;
114 group->setState(OptionalContentGroup::On);
117 on.free();
119 Object off;
120 defaultOcgConfig.dictLookup("OFF", &off);
121 if (off.isArray()) {
122 // OFF is an optional element
123 for (int i = 0; i < off.arrayGetLength(); ++i) {
124 Object reference;
125 off.arrayGetNF(i, &reference);
126 if (!reference.isRef()) {
127 // there can be null entries
128 reference.free();
129 break;
131 OptionalContentGroup *group = findOcgByRef( reference.getRef() );
132 reference.free();
133 if (!group) {
134 error(errSyntaxWarning, -1, "Couldn't find group for reference to set OFF");
135 break;
137 group->setState(OptionalContentGroup::Off);
140 off.free();
142 defaultOcgConfig.dictLookup("Order", &order);
143 defaultOcgConfig.dictLookup("RBGroups", &rbgroups);
145 ocgList.free();
146 defaultOcgConfig.free();
149 OCGs::~OCGs()
151 deleteGooList(optionalContentGroups, OptionalContentGroup);
152 order.free();
153 if (display)
154 delete display;
155 rbgroups.free();
159 bool OCGs::hasOCGs()
161 return ( optionalContentGroups->getLength() > 0 );
164 OptionalContentGroup* OCGs::findOcgByRef( const Ref &ref)
166 //TODO: make this more efficient
167 OptionalContentGroup *ocg = NULL;
168 for (int i=0; i < optionalContentGroups->getLength(); ++i) {
169 ocg = (OptionalContentGroup*)optionalContentGroups->get(i);
170 if ( (ocg->getRef().num == ref.num) && (ocg->getRef().gen == ref.gen) ) {
171 return ocg;
175 // not found
176 return NULL;
179 OCDisplayNode *OCGs::getDisplayRoot()
181 if (display)
182 return display;
184 if (order.isArray())
185 display = OCDisplayNode::parse(&order, this, m_xref);
187 return display;
190 bool OCGs::optContentIsVisible( Object *dictRef )
192 Object dictObj;
193 Dict *dict;
194 Object dictType;
195 Object ocg;
196 Object policy;
197 Object ve;
198 bool result = true;
200 if (dictRef->isNull())
201 return result;
203 if (dictRef->isRef()) {
204 OptionalContentGroup *oc = findOcgByRef(dictRef->getRef());
205 if (oc)
206 return oc->getState() == OptionalContentGroup::On;
209 dictRef->fetch( m_xref, &dictObj );
210 if ( ! dictObj.isDict() ) {
211 error(errSyntaxWarning, -1, "Unexpected oc reference target: {0:d}", dictObj.getType() );
212 dictObj.free();
213 return result;
215 dict = dictObj.getDict();
216 // printf("checking if optContent is visible\n");
217 dict->lookup("Type", &dictType);
218 if (dictType.isName("OCMD")) {
219 if (dict->lookup("VE", &ve)->isArray()) {
220 result = evalOCVisibilityExpr(&ve, 0);
221 } else {
222 dict->lookupNF("OCGs", &ocg);
223 if (ocg.isArray()) {
224 dict->lookup("P", &policy);
225 if (policy.isName("AllOn")) {
226 result = allOn( ocg.getArray() );
227 } else if (policy.isName("AllOff")) {
228 result = allOff( ocg.getArray() );
229 } else if (policy.isName("AnyOff")) {
230 result = anyOff( ocg.getArray() );
231 } else if ( (!policy.isName()) || (policy.isName("AnyOn") ) ) {
232 // this is the default
233 result = anyOn( ocg.getArray() );
235 policy.free();
236 } else if (ocg.isRef()) {
237 OptionalContentGroup *oc = findOcgByRef( ocg.getRef() );
238 if ( oc && oc->getState() == OptionalContentGroup::Off ) {
239 result = false;
240 } else {
241 result = true ;
244 ocg.free();
246 ve.free();
247 } else if ( dictType.isName("OCG") ) {
248 OptionalContentGroup* oc = findOcgByRef( dictRef->getRef() );
249 if ( oc && oc->getState() == OptionalContentGroup::Off ) {
250 result=false;
253 dictType.free();
254 dictObj.free();
255 // printf("visibility: %s\n", result? "on" : "off");
256 return result;
259 GBool OCGs::evalOCVisibilityExpr(Object *expr, int recursion) {
260 OptionalContentGroup *ocg;
261 Object expr2, op, obj;
262 GBool ret;
263 int i;
265 if (recursion > visibilityExprRecursionLimit) {
266 error(errSyntaxError, -1,
267 "Loop detected in optional content visibility expression");
268 return gTrue;
270 if (expr->isRef()) {
271 if ((ocg = findOcgByRef(expr->getRef()))) {
272 return ocg->getState() == OptionalContentGroup::On;
275 expr->fetch(m_xref, &expr2);
276 if (!expr2.isArray() || expr2.arrayGetLength() < 1) {
277 error(errSyntaxError, -1,
278 "Invalid optional content visibility expression");
279 expr2.free();
280 return gTrue;
282 expr2.arrayGet(0, &op);
283 if (op.isName("Not")) {
284 if (expr2.arrayGetLength() == 2) {
285 expr2.arrayGetNF(1, &obj);
286 ret = !evalOCVisibilityExpr(&obj, recursion + 1);
287 obj.free();
288 } else {
289 error(errSyntaxError, -1,
290 "Invalid optional content visibility expression");
291 ret = gTrue;
293 } else if (op.isName("And")) {
294 ret = gTrue;
295 for (i = 1; i < expr2.arrayGetLength() && ret; ++i) {
296 expr2.arrayGetNF(i, &obj);
297 ret = evalOCVisibilityExpr(&obj, recursion + 1);
298 obj.free();
300 } else if (op.isName("Or")) {
301 ret = gFalse;
302 for (i = 1; i < expr2.arrayGetLength() && !ret; ++i) {
303 expr2.arrayGetNF(i, &obj);
304 ret = evalOCVisibilityExpr(&obj, recursion + 1);
305 obj.free();
307 } else {
308 error(errSyntaxError, -1,
309 "Invalid optional content visibility expression");
310 ret = gTrue;
312 op.free();
313 expr2.free();
314 return ret;
317 bool OCGs::allOn( Array *ocgArray )
319 for (int i = 0; i < ocgArray->getLength(); ++i) {
320 Object ocgItem;
321 ocgArray->getNF(i, &ocgItem);
322 if (ocgItem.isRef()) {
323 OptionalContentGroup* oc = findOcgByRef( ocgItem.getRef() );
324 if ( oc && oc->getState() == OptionalContentGroup::Off ) {
325 return false;
329 return true;
332 bool OCGs::allOff( Array *ocgArray )
334 for (int i = 0; i < ocgArray->getLength(); ++i) {
335 Object ocgItem;
336 ocgArray->getNF(i, &ocgItem);
337 if (ocgItem.isRef()) {
338 OptionalContentGroup* oc = findOcgByRef( ocgItem.getRef() );
339 if ( oc && oc->getState() == OptionalContentGroup::On ) {
340 return false;
344 return true;
347 bool OCGs::anyOn( Array *ocgArray )
349 for (int i = 0; i < ocgArray->getLength(); ++i) {
350 Object ocgItem;
351 ocgArray->getNF(i, &ocgItem);
352 if (ocgItem.isRef()) {
353 OptionalContentGroup* oc = findOcgByRef( ocgItem.getRef() );
354 if ( oc && oc->getState() == OptionalContentGroup::On ) {
355 return true;
359 return false;
362 bool OCGs::anyOff( Array *ocgArray )
364 for (int i = 0; i < ocgArray->getLength(); ++i) {
365 Object ocgItem;
366 ocgArray->getNF(i, &ocgItem);
367 if (ocgItem.isRef()) {
368 OptionalContentGroup* oc = findOcgByRef( ocgItem.getRef() );
369 if ( oc && oc->getState() == OptionalContentGroup::Off ) {
370 return true;
374 return false;
377 //------------------------------------------------------------------------
379 OptionalContentGroup::OptionalContentGroup(Dict *ocgDict) : m_name(NULL)
381 Object obj1, obj2, obj3;
382 Object ocgName;
383 ocgDict->lookup("Name", &ocgName);
384 if (!ocgName.isString()) {
385 error(errSyntaxWarning, -1, "Expected the name of the OCG, but wasn't able to find it, or it isn't a String");
386 } else {
387 m_name = new GooString( ocgName.getString() );
389 ocgName.free();
391 viewState = printState = ocUsageUnset;
392 if (ocgDict->lookup("Usage", &obj1)->isDict()) {
393 if (obj1.dictLookup("View", &obj2)->isDict()) {
394 if (obj2.dictLookup("ViewState", &obj3)->isName()) {
395 if (obj3.isName("ON")) {
396 viewState = ocUsageOn;
397 } else {
398 viewState = ocUsageOff;
401 obj3.free();
403 obj2.free();
404 if (obj1.dictLookup("Print", &obj2)->isDict()) {
405 if (obj2.dictLookup("PrintState", &obj3)->isName()) {
406 if (obj3.isName("ON")) {
407 printState = ocUsageOn;
408 } else {
409 printState = ocUsageOff;
412 obj3.free();
414 obj2.free();
416 obj1.free();
419 OptionalContentGroup::OptionalContentGroup(GooString *label)
421 m_name = label;
422 m_state = On;
425 GooString* OptionalContentGroup::getName() const
427 return m_name;
430 void OptionalContentGroup::setRef(const Ref ref)
432 m_ref = ref;
435 Ref OptionalContentGroup::getRef() const
437 return m_ref;
440 OptionalContentGroup::~OptionalContentGroup()
442 delete m_name;
445 //------------------------------------------------------------------------
447 OCDisplayNode *OCDisplayNode::parse(Object *obj, OCGs *oc,
448 XRef *xref, int recursion) {
449 Object obj2, obj3;
450 OptionalContentGroup *ocgA;
451 OCDisplayNode *node, *child;
452 int i;
454 if (recursion > displayNodeRecursionLimit) {
455 error(errSyntaxError, -1, "Loop detected in optional content order");
456 return NULL;
458 if (obj->isRef()) {
459 if ((ocgA = oc->findOcgByRef(obj->getRef()))) {
460 return new OCDisplayNode(ocgA);
463 obj->fetch(xref, &obj2);
464 if (!obj2.isArray()) {
465 obj2.free();
466 return NULL;
468 i = 0;
469 if (obj2.arrayGetLength() >= 1) {
470 if (obj2.arrayGet(0, &obj3)->isString()) {
471 node = new OCDisplayNode(obj3.getString());
472 i = 1;
473 } else {
474 node = new OCDisplayNode();
476 obj3.free();
477 } else {
478 node = new OCDisplayNode();
480 for (; i < obj2.arrayGetLength(); ++i) {
481 obj2.arrayGetNF(i, &obj3);
482 if ((child = OCDisplayNode::parse(&obj3, oc, xref, recursion + 1))) {
483 if (!child->ocg && !child->name && node->getNumChildren() > 0) {
484 node->getChild(node->getNumChildren() - 1)->addChildren(child->takeChildren());
485 delete child;
486 } else {
487 node->addChild(child);
490 obj3.free();
492 obj2.free();
493 return node;
496 OCDisplayNode::OCDisplayNode() {
497 name = NULL;
498 ocg = NULL;
499 children = NULL;
502 OCDisplayNode::OCDisplayNode(GooString *nameA) {
503 name = new GooString(nameA);
504 ocg = NULL;
505 children = NULL;
508 OCDisplayNode::OCDisplayNode(OptionalContentGroup *ocgA) {
509 name = (ocgA->getName()) ? ocgA->getName()->copy() : NULL;
510 ocg = ocgA;
511 children = NULL;
514 void OCDisplayNode::addChild(OCDisplayNode *child) {
515 if (!children) {
516 children = new GooList();
518 children->append(child);
521 void OCDisplayNode::addChildren(GooList *childrenA) {
522 if (!children) {
523 children = new GooList();
525 children->append(childrenA);
526 delete childrenA;
529 GooList *OCDisplayNode::takeChildren() {
530 GooList *childrenA;
532 childrenA = children;
533 children = NULL;
534 return childrenA;
537 OCDisplayNode::~OCDisplayNode() {
538 gfree(name);
539 if (children) {
540 deleteGooList(children, OCDisplayNode);
544 int OCDisplayNode::getNumChildren() {
545 if (!children) {
546 return 0;
548 return children->getLength();
551 OCDisplayNode *OCDisplayNode::getChild(int idx) {
552 return (OCDisplayNode *)children->get(idx);