1 //========================================================================
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 //========================================================================
17 #ifdef USE_GCC_PRAGMAS
18 #pragma implementation
22 #include "goo/GooString.h"
23 #include "goo/GooList.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
) :
41 // we need to parse the dictionary here, and build optionalContentGroups
43 optionalContentGroups
= new GooList();
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");
55 // we now enumerate over the ocgList, and build up the optionalContentGroups list.
56 for(int i
= 0; i
< ocgList
.arrayGetLength(); ++i
) {
58 ocgList
.arrayGet(i
, &ocg
);
63 OptionalContentGroup
*thisOptionalContentGroup
= new OptionalContentGroup(ocg
.getDict());
65 ocgList
.arrayGetNF(i
, &ocg
);
66 // TODO: we should create a lookup map from Ref to the OptionalContentGroup
67 thisOptionalContentGroup
->setRef( ocg
.getRef() );
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();
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
);
97 defaultOcgConfig
.dictLookup("ON", &on
);
99 // ON is an optional element
100 for (int i
= 0; i
< on
.arrayGetLength(); ++i
) {
102 on
.arrayGetNF(i
, &reference
);
103 if (!reference
.isRef()) {
104 // there can be null entries
108 OptionalContentGroup
*group
= findOcgByRef( reference
.getRef() );
111 error(errSyntaxWarning
, -1, "Couldn't find group for reference");
114 group
->setState(OptionalContentGroup::On
);
120 defaultOcgConfig
.dictLookup("OFF", &off
);
122 // OFF is an optional element
123 for (int i
= 0; i
< off
.arrayGetLength(); ++i
) {
125 off
.arrayGetNF(i
, &reference
);
126 if (!reference
.isRef()) {
127 // there can be null entries
131 OptionalContentGroup
*group
= findOcgByRef( reference
.getRef() );
134 error(errSyntaxWarning
, -1, "Couldn't find group for reference to set OFF");
137 group
->setState(OptionalContentGroup::Off
);
142 defaultOcgConfig
.dictLookup("Order", &order
);
143 defaultOcgConfig
.dictLookup("RBGroups", &rbgroups
);
146 defaultOcgConfig
.free();
151 deleteGooList(optionalContentGroups
, OptionalContentGroup
);
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
) ) {
179 OCDisplayNode
*OCGs::getDisplayRoot()
185 display
= OCDisplayNode::parse(&order
, this, m_xref
);
190 bool OCGs::optContentIsVisible( Object
*dictRef
)
200 if (dictRef
->isNull())
203 if (dictRef
->isRef()) {
204 OptionalContentGroup
*oc
= findOcgByRef(dictRef
->getRef());
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() );
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);
222 dict
->lookupNF("OCGs", &ocg
);
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() );
236 } else if (ocg
.isRef()) {
237 OptionalContentGroup
*oc
= findOcgByRef( ocg
.getRef() );
238 if ( oc
&& oc
->getState() == OptionalContentGroup::Off
) {
247 } else if ( dictType
.isName("OCG") ) {
248 OptionalContentGroup
* oc
= findOcgByRef( dictRef
->getRef() );
249 if ( oc
&& oc
->getState() == OptionalContentGroup::Off
) {
255 // printf("visibility: %s\n", result? "on" : "off");
259 GBool
OCGs::evalOCVisibilityExpr(Object
*expr
, int recursion
) {
260 OptionalContentGroup
*ocg
;
261 Object expr2
, op
, obj
;
265 if (recursion
> visibilityExprRecursionLimit
) {
266 error(errSyntaxError
, -1,
267 "Loop detected in optional content visibility expression");
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");
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);
289 error(errSyntaxError
, -1,
290 "Invalid optional content visibility expression");
293 } else if (op
.isName("And")) {
295 for (i
= 1; i
< expr2
.arrayGetLength() && ret
; ++i
) {
296 expr2
.arrayGetNF(i
, &obj
);
297 ret
= evalOCVisibilityExpr(&obj
, recursion
+ 1);
300 } else if (op
.isName("Or")) {
302 for (i
= 1; i
< expr2
.arrayGetLength() && !ret
; ++i
) {
303 expr2
.arrayGetNF(i
, &obj
);
304 ret
= evalOCVisibilityExpr(&obj
, recursion
+ 1);
308 error(errSyntaxError
, -1,
309 "Invalid optional content visibility expression");
317 bool OCGs::allOn( Array
*ocgArray
)
319 for (int i
= 0; i
< ocgArray
->getLength(); ++i
) {
321 ocgArray
->getNF(i
, &ocgItem
);
322 if (ocgItem
.isRef()) {
323 OptionalContentGroup
* oc
= findOcgByRef( ocgItem
.getRef() );
324 if ( oc
&& oc
->getState() == OptionalContentGroup::Off
) {
332 bool OCGs::allOff( Array
*ocgArray
)
334 for (int i
= 0; i
< ocgArray
->getLength(); ++i
) {
336 ocgArray
->getNF(i
, &ocgItem
);
337 if (ocgItem
.isRef()) {
338 OptionalContentGroup
* oc
= findOcgByRef( ocgItem
.getRef() );
339 if ( oc
&& oc
->getState() == OptionalContentGroup::On
) {
347 bool OCGs::anyOn( Array
*ocgArray
)
349 for (int i
= 0; i
< ocgArray
->getLength(); ++i
) {
351 ocgArray
->getNF(i
, &ocgItem
);
352 if (ocgItem
.isRef()) {
353 OptionalContentGroup
* oc
= findOcgByRef( ocgItem
.getRef() );
354 if ( oc
&& oc
->getState() == OptionalContentGroup::On
) {
362 bool OCGs::anyOff( Array
*ocgArray
)
364 for (int i
= 0; i
< ocgArray
->getLength(); ++i
) {
366 ocgArray
->getNF(i
, &ocgItem
);
367 if (ocgItem
.isRef()) {
368 OptionalContentGroup
* oc
= findOcgByRef( ocgItem
.getRef() );
369 if ( oc
&& oc
->getState() == OptionalContentGroup::Off
) {
377 //------------------------------------------------------------------------
379 OptionalContentGroup::OptionalContentGroup(Dict
*ocgDict
) : m_name(NULL
)
381 Object obj1
, obj2
, obj3
;
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");
387 m_name
= new GooString( ocgName
.getString() );
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
;
398 viewState
= ocUsageOff
;
404 if (obj1
.dictLookup("Print", &obj2
)->isDict()) {
405 if (obj2
.dictLookup("PrintState", &obj3
)->isName()) {
406 if (obj3
.isName("ON")) {
407 printState
= ocUsageOn
;
409 printState
= ocUsageOff
;
419 OptionalContentGroup::OptionalContentGroup(GooString
*label
)
425 GooString
* OptionalContentGroup::getName() const
430 void OptionalContentGroup::setRef(const Ref ref
)
435 Ref
OptionalContentGroup::getRef() const
440 OptionalContentGroup::~OptionalContentGroup()
445 //------------------------------------------------------------------------
447 OCDisplayNode
*OCDisplayNode::parse(Object
*obj
, OCGs
*oc
,
448 XRef
*xref
, int recursion
) {
450 OptionalContentGroup
*ocgA
;
451 OCDisplayNode
*node
, *child
;
454 if (recursion
> displayNodeRecursionLimit
) {
455 error(errSyntaxError
, -1, "Loop detected in optional content order");
459 if ((ocgA
= oc
->findOcgByRef(obj
->getRef()))) {
460 return new OCDisplayNode(ocgA
);
463 obj
->fetch(xref
, &obj2
);
464 if (!obj2
.isArray()) {
469 if (obj2
.arrayGetLength() >= 1) {
470 if (obj2
.arrayGet(0, &obj3
)->isString()) {
471 node
= new OCDisplayNode(obj3
.getString());
474 node
= new OCDisplayNode();
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());
487 node
->addChild(child
);
496 OCDisplayNode::OCDisplayNode() {
502 OCDisplayNode::OCDisplayNode(GooString
*nameA
) {
503 name
= new GooString(nameA
);
508 OCDisplayNode::OCDisplayNode(OptionalContentGroup
*ocgA
) {
509 name
= (ocgA
->getName()) ? ocgA
->getName()->copy() : NULL
;
514 void OCDisplayNode::addChild(OCDisplayNode
*child
) {
516 children
= new GooList();
518 children
->append(child
);
521 void OCDisplayNode::addChildren(GooList
*childrenA
) {
523 children
= new GooList();
525 children
->append(childrenA
);
529 GooList
*OCDisplayNode::takeChildren() {
532 childrenA
= children
;
537 OCDisplayNode::~OCDisplayNode() {
540 deleteGooList(children
, OCDisplayNode
);
544 int OCDisplayNode::getNumChildren() {
548 return children
->getLength();
551 OCDisplayNode
*OCDisplayNode::getChild(int idx
) {
552 return (OCDisplayNode
*)children
->get(idx
);