Files
DocumentServer-v-9.2.0/core/PdfFile/lib/xpdf/OptionalContent.cc
Yajbir Singh f1b860b25c
Some checks failed
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

538 lines
12 KiB
C++

//========================================================================
//
// OptionalContent.cc
//
// Copyright 2008-2013 Glyph & Cog, LLC
//
//========================================================================
#include <aconf.h>
#ifdef USE_GCC_PRAGMAS
#pragma implementation
#endif
#include "gmempp.h"
#include "GString.h"
#include "GList.h"
#include "Error.h"
#include "Object.h"
#include "PDFDoc.h"
#include "TextString.h"
#include "OptionalContent.h"
//------------------------------------------------------------------------
#define ocPolicyAllOn 1
#define ocPolicyAnyOn 2
#define ocPolicyAnyOff 3
#define ocPolicyAllOff 4
//------------------------------------------------------------------------
// Max depth of nested visibility expressions. This is used to catch
// infinite loops in the visibility expression object structure.
#define visibilityExprRecursionLimit 50
// Max depth of nested display nodes. This is used to catch infinite
// loops in the "Order" object structure.
#define displayNodeRecursionLimit 50
//------------------------------------------------------------------------
OptionalContent::OptionalContent(PDFDoc *doc) {
Object *ocProps;
Object ocgList, defView, uad, obj1, obj2, obj3, obj4;
Ref ref1;
OptionalContentGroup *ocg;
int i, j;
xref = doc->getXRef();
ocgs = new GList();
display = NULL;
if ((ocProps = doc->getCatalog()->getOCProperties())->isDict()) {
if (ocProps->dictLookup("OCGs", &ocgList)->isArray()) {
//----- read the OCG list
for (i = 0; i < ocgList.arrayGetLength(); ++i) {
if (ocgList.arrayGetNF(i, &obj1)->isRef()) {
ref1 = obj1.getRef();
obj1.fetch(xref, &obj2);
if ((ocg = OptionalContentGroup::parse(&ref1, &obj2))) {
ocgs->append(ocg);
}
obj2.free();
}
obj1.free();
}
//----- read the default viewing OCCD
if (ocProps->dictLookup("D", &defView)->isDict()) {
//----- read the usage app dicts
if (defView.dictLookup("AS", &obj1)->isArray()) {
for (i = 0; i < obj1.arrayGetLength(); ++i) {
if (obj1.arrayGet(i, &uad)->isDict()) {
if (uad.dictLookup("Event", &obj2)->isName("View")) {
if (uad.dictLookup("OCGs", &obj3)->isArray()) {
for (j = 0; j < obj3.arrayGetLength(); ++j) {
if (obj3.arrayGetNF(j, &obj4)->isRef()) {
ref1 = obj4.getRef();
if ((ocg = findOCG(&ref1))) {
ocg->setInViewUsageAppDict();
}
}
obj4.free();
}
}
obj3.free();
}
obj2.free();
}
uad.free();
}
}
obj1.free();
//----- initial state from OCCD
if (defView.dictLookup("OFF", &obj1)->isArray()) {
for (i = 0; i < obj1.arrayGetLength(); ++i) {
if (obj1.arrayGetNF(i, &obj2)->isRef()) {
ref1 = obj2.getRef();
if ((ocg = findOCG(&ref1))) {
ocg->setState(gFalse);
} else {
error(errSyntaxError, -1,
"Invalid OCG reference in OFF array in default viewing OCCD");
}
}
obj2.free();
}
}
obj1.free();
//----- initial state from OCG usage dict
for (i = 0; i < ocgs->getLength(); ++i) {
ocg = (OptionalContentGroup *)ocgs->get(i);
if (ocg->getInViewUsageAppDict() &&
ocg->getViewState() != ocUsageUnset) {
ocg->setState(ocg->getViewState() == ocUsageOn);
}
}
//----- display order
if (defView.dictLookup("Order", &obj1)->isArray()) {
display = OCDisplayNode::parse(&obj1, this, xref);
}
obj1.free();
} else {
error(errSyntaxError, -1, "Missing or invalid default viewing OCCD");
}
defView.free();
}
ocgList.free();
}
if (!display) {
display = new OCDisplayNode();
}
}
OptionalContent::~OptionalContent() {
deleteGList(ocgs, OptionalContentGroup);
delete display;
}
int OptionalContent::getNumOCGs() {
return ocgs->getLength();
}
OptionalContentGroup *OptionalContent::getOCG(int idx) {
return (OptionalContentGroup *)ocgs->get(idx);
}
OptionalContentGroup *OptionalContent::findOCG(Ref *ref) {
OptionalContentGroup *ocg;
int i;
for (i = 0; i < ocgs->getLength(); ++i) {
ocg = (OptionalContentGroup *)ocgs->get(i);
if (ocg->matches(ref)) {
return ocg;
}
}
return NULL;
}
GBool OptionalContent::evalOCObject(Object *obj, GBool *visible) {
OptionalContentGroup *ocg;
int policy;
Ref ref;
Object obj2, obj3, obj4, obj5;
GBool gotOCG;
int i;
if (obj->isNull()) {
return gFalse;
}
if (obj->isRef()) {
ref = obj->getRef();
if ((ocg = findOCG(&ref))) {
*visible = ocg->getState();
return gTrue;
}
}
obj->fetch(xref, &obj2);
if (!obj2.isDict("OCMD")) {
obj2.free();
return gFalse;
}
if (obj2.dictLookup("VE", &obj3)->isArray()) {
*visible = evalOCVisibilityExpr(&obj3, 0);
obj3.free();
} else {
obj3.free();
policy = ocPolicyAnyOn;
if (obj2.dictLookup("P", &obj3)->isName()) {
if (obj3.isName("AllOn")) {
policy = ocPolicyAllOn;
} else if (obj3.isName("AnyOn")) {
policy = ocPolicyAnyOn;
} else if (obj3.isName("AnyOff")) {
policy = ocPolicyAnyOff;
} else if (obj3.isName("AllOff")) {
policy = ocPolicyAllOff;
}
}
obj3.free();
obj2.dictLookupNF("OCGs", &obj3);
ocg = NULL;
if (obj3.isRef()) {
ref = obj3.getRef();
ocg = findOCG(&ref);
}
if (ocg) {
*visible = (policy == ocPolicyAllOn || policy == ocPolicyAnyOn) ?
ocg->getState() : !ocg->getState();
} else {
*visible = policy == ocPolicyAllOn || policy == ocPolicyAllOff;
if (!obj3.fetch(xref, &obj4)->isArray()) {
obj4.free();
obj3.free();
obj2.free();
return gFalse;
}
gotOCG = gFalse;
for (i = 0; i < obj4.arrayGetLength(); ++i) {
obj4.arrayGetNF(i, &obj5);
if (obj5.isRef()) {
ref = obj5.getRef();
if ((ocg = findOCG(&ref))) {
gotOCG = gTrue;
switch (policy) {
case ocPolicyAllOn:
*visible = *visible && ocg->getState();
break;
case ocPolicyAnyOn:
*visible = *visible || ocg->getState();
break;
case ocPolicyAnyOff:
*visible = *visible || !ocg->getState();
break;
case ocPolicyAllOff:
*visible = *visible && !ocg->getState();
break;
}
}
}
obj5.free();
}
if (!gotOCG) {
// if the "OCGs" array is "empty or contains references only
// to null or deleted objects", this OCMD doesn't have any
// effect
obj4.free();
obj3.free();
obj2.free();
return gFalse;
}
obj4.free();
}
obj3.free();
}
obj2.free();
return gTrue;
}
GBool OptionalContent::evalOCVisibilityExpr(Object *expr, int recursion) {
OptionalContentGroup *ocg;
Object expr2, op, obj;
Ref ref;
GBool ret;
int i;
if (recursion > visibilityExprRecursionLimit) {
error(errSyntaxError, -1,
"Loop detected in optional content visibility expression");
return gTrue;
}
if (expr->isRef()) {
ref = expr->getRef();
if ((ocg = findOCG(&ref))) {
return ocg->getState();
}
}
expr->fetch(xref, &expr2);
if (!expr2.isArray() || expr2.arrayGetLength() < 1) {
error(errSyntaxError, -1,
"Invalid optional content visibility expression");
expr2.free();
return gTrue;
}
expr2.arrayGet(0, &op);
if (op.isName("Not")) {
if (expr2.arrayGetLength() == 2) {
expr2.arrayGetNF(1, &obj);
ret = !evalOCVisibilityExpr(&obj, recursion + 1);
obj.free();
} else {
error(errSyntaxError, -1,
"Invalid optional content visibility expression");
ret = gTrue;
}
} else if (op.isName("And")) {
ret = gTrue;
for (i = 1; i < expr2.arrayGetLength() && ret; ++i) {
expr2.arrayGetNF(i, &obj);
ret = evalOCVisibilityExpr(&obj, recursion + 1);
obj.free();
}
} else if (op.isName("Or")) {
ret = gFalse;
for (i = 1; i < expr2.arrayGetLength() && !ret; ++i) {
expr2.arrayGetNF(i, &obj);
ret = evalOCVisibilityExpr(&obj, recursion + 1);
obj.free();
}
} else {
error(errSyntaxError, -1,
"Invalid optional content visibility expression");
ret = gTrue;
}
op.free();
expr2.free();
return ret;
}
//------------------------------------------------------------------------
OptionalContentGroup *OptionalContentGroup::parse(Ref *refA, Object *obj) {
TextString *nameA;
Object obj1, obj2, obj3;
OCUsageState viewStateA, printStateA;
if (!obj->isDict()) {
return NULL;
}
if (!obj->dictLookup("Name", &obj1)->isString()) {
error(errSyntaxError, -1, "Missing or invalid Name in OCG");
obj1.free();
return NULL;
}
nameA = new TextString(obj1.getString());
obj1.free();
viewStateA = printStateA = ocUsageUnset;
if (obj->dictLookup("Usage", &obj1)->isDict()) {
if (obj1.dictLookup("View", &obj2)->isDict()) {
if (obj2.dictLookup("ViewState", &obj3)->isName()) {
if (obj3.isName("ON")) {
viewStateA = ocUsageOn;
} else {
viewStateA = ocUsageOff;
}
}
obj3.free();
}
obj2.free();
if (obj1.dictLookup("Print", &obj2)->isDict()) {
if (obj2.dictLookup("PrintState", &obj3)->isName()) {
if (obj3.isName("ON")) {
printStateA = ocUsageOn;
} else {
printStateA = ocUsageOff;
}
}
obj3.free();
}
obj2.free();
}
obj1.free();
return new OptionalContentGroup(refA, nameA, viewStateA, printStateA);
}
OptionalContentGroup::OptionalContentGroup(Ref *refA, TextString *nameA,
OCUsageState viewStateA,
OCUsageState printStateA) {
ref = *refA;
name = nameA;
viewState = viewStateA;
printState = printStateA;
state = gTrue;
inViewUsageAppDict = gFalse;
}
OptionalContentGroup::~OptionalContentGroup() {
delete name;
}
GBool OptionalContentGroup::matches(Ref *refA) {
return refA->num == ref.num && refA->gen == ref.gen;
}
Unicode *OptionalContentGroup::getName() {
return name->getUnicode();
}
int OptionalContentGroup::getNameLength() {
return name->getLength();
}
//------------------------------------------------------------------------
OCDisplayNode *OCDisplayNode::parse(Object *obj, OptionalContent *oc,
XRef *xref, int recursion) {
Object obj2, obj3;
Ref ref;
OptionalContentGroup *ocgA;
OCDisplayNode *node, *child;
int i;
if (recursion > displayNodeRecursionLimit) {
error(errSyntaxError, -1, "Loop detected in optional content order");
return NULL;
}
if (obj->isRef()) {
ref = obj->getRef();
if ((ocgA = oc->findOCG(&ref))) {
return new OCDisplayNode(ocgA);
}
}
obj->fetch(xref, &obj2);
if (!obj2.isArray()) {
obj2.free();
return NULL;
}
i = 0;
if (obj2.arrayGetLength() >= 1) {
if (obj2.arrayGet(0, &obj3)->isString()) {
node = new OCDisplayNode(obj3.getString());
i = 1;
} else {
node = new OCDisplayNode();
}
obj3.free();
} else {
node = new OCDisplayNode();
}
for (; i < obj2.arrayGetLength(); ++i) {
obj2.arrayGetNF(i, &obj3);
if ((child = OCDisplayNode::parse(&obj3, oc, xref, recursion + 1))) {
if (!child->ocg && !child->name && node->getNumChildren() > 0) {
if (child->getNumChildren() > 0) {
node->getChild(node->getNumChildren() - 1)->
addChildren(child->takeChildren());
}
delete child;
} else {
node->addChild(child);
}
}
obj3.free();
}
obj2.free();
return node;
}
OCDisplayNode::OCDisplayNode() {
name = new TextString();
ocg = NULL;
parent = NULL;
children = NULL;
}
OCDisplayNode::OCDisplayNode(GString *nameA) {
name = new TextString(nameA);
ocg = NULL;
children = NULL;
}
OCDisplayNode::OCDisplayNode(OptionalContentGroup *ocgA) {
name = new TextString(ocgA->name);
ocg = ocgA;
children = NULL;
}
void OCDisplayNode::addChild(OCDisplayNode *child) {
if (!children) {
children = new GList();
}
children->append(child);
child->parent = this;
}
void OCDisplayNode::addChildren(GList *childrenA) {
int i;
if (!children) {
children = new GList();
}
children->append(childrenA);
for (i = 0; i < childrenA->getLength(); ++i) {
((OCDisplayNode *)childrenA->get(i))->parent = this;
}
delete childrenA;
}
GList *OCDisplayNode::takeChildren() {
GList *childrenA;
int i;
childrenA = children;
children = NULL;
for (i = 0; i < childrenA->getLength(); ++i) {
((OCDisplayNode *)childrenA->get(i))->parent = NULL;
}
return childrenA;
}
OCDisplayNode::~OCDisplayNode() {
delete name;
if (children) {
deleteGList(children, OCDisplayNode);
}
}
Unicode *OCDisplayNode::getName() {
return name->getUnicode();
}
int OCDisplayNode::getNameLength() {
return name->getLength();
}
int OCDisplayNode::getNumChildren() {
if (!children) {
return 0;
}
return children->getLength();
}
OCDisplayNode *OCDisplayNode::getChild(int idx) {
return (OCDisplayNode *)children->get(idx);
}