blob: 1906603178dacaedc1556534edc892767bc532af [file] [log] [blame]
// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#include "../../include/fpdfdoc/fpdf_doc.h"
static int32_t FPDFDOC_OCG_FindGroup(const CPDF_Object *pObject, const CPDF_Dictionary *pGroupDict)
{
if (pObject == NULL || pGroupDict == NULL) {
return -1;
}
int32_t iType = pObject->GetType();
if (iType == PDFOBJ_ARRAY) {
FX_DWORD dwCount = ((CPDF_Array*)pObject)->GetCount();
for (FX_DWORD i = 0; i < dwCount; i++) {
if (((CPDF_Array*)pObject)->GetDict(i) == pGroupDict) {
return i;
}
}
return -1;
}
if (pObject->GetDict() == pGroupDict) {
return 0;
}
return -1;
}
static FX_BOOL FPDFDOC_OCG_HasIntent(const CPDF_Dictionary *pDict, const CFX_ByteStringC& csElement, const CFX_ByteStringC& csDef = FX_BSTRC(""))
{
FXSYS_assert(pDict != NULL);
CPDF_Object *pIntent = pDict->GetElementValue(FX_BSTRC("Intent"));
if (pIntent == NULL) {
return csElement == csDef;
}
CFX_ByteString bsIntent;
if (pIntent->GetType() == PDFOBJ_ARRAY) {
FX_DWORD dwCount = ((CPDF_Array*)pIntent)->GetCount();
for (FX_DWORD i = 0; i < dwCount; i++) {
bsIntent = ((CPDF_Array*)pIntent)->GetString(i);
if (bsIntent == FX_BSTRC("All") || bsIntent == csElement) {
return TRUE;
}
}
return FALSE;
}
bsIntent = pIntent->GetString();
return bsIntent == FX_BSTRC("All") || bsIntent == csElement;
}
static CPDF_Dictionary* FPDFDOC_OCG_GetConfig(CPDF_Document *pDoc, const CPDF_Dictionary *pOCGDict, const CFX_ByteStringC& bsState)
{
FXSYS_assert(pDoc && pOCGDict);
CPDF_Dictionary *pOCProperties = pDoc->GetRoot()->GetDict(FX_BSTRC("OCProperties"));
if (!pOCProperties) {
return NULL;
}
CPDF_Array *pOCGs = pOCProperties->GetArray(FX_BSTRC("OCGs"));
if (!pOCGs) {
return NULL;
}
if (FPDFDOC_OCG_FindGroup(pOCGs, pOCGDict) < 0) {
return NULL;
}
CPDF_Dictionary *pConfig = pOCProperties->GetDict(FX_BSTRC("D"));
CPDF_Array *pConfigs = pOCProperties->GetArray(FX_BSTRC("Configs"));
if (pConfigs) {
CPDF_Dictionary *pFind;
int32_t iCount = pConfigs->GetCount();
for (int32_t i = 0; i < iCount; i ++) {
pFind = pConfigs->GetDict(i);
if (!pFind) {
continue;
}
if (!FPDFDOC_OCG_HasIntent(pFind, FX_BSTRC("View"), FX_BSTRC("View"))) {
continue;
}
pConfig = pFind;
break;
}
}
return pConfig;
}
static CFX_ByteString FPDFDOC_OCG_GetUsageTypeString(CPDF_OCContext::UsageType eType)
{
CFX_ByteString csState = FX_BSTRC("View");
if (eType == CPDF_OCContext::Design) {
csState = FX_BSTRC("Design");
} else if (eType == CPDF_OCContext::Print) {
csState = FX_BSTRC("Print");
} else if (eType == CPDF_OCContext::Export) {
csState = FX_BSTRC("Export");
}
return csState;
}
CPDF_OCContext::CPDF_OCContext(CPDF_Document *pDoc, UsageType eUsageType)
{
FXSYS_assert(pDoc != NULL);
m_pDocument = pDoc;
m_eUsageType = eUsageType;
}
CPDF_OCContext::~CPDF_OCContext()
{
m_OCGStates.RemoveAll();
}
FX_BOOL CPDF_OCContext::LoadOCGStateFromConfig(const CFX_ByteStringC& csConfig, const CPDF_Dictionary *pOCGDict, FX_BOOL &bValidConfig) const
{
CPDF_Dictionary *pConfig = FPDFDOC_OCG_GetConfig(m_pDocument, pOCGDict, csConfig);
if (!pConfig) {
return TRUE;
}
bValidConfig = TRUE;
FX_BOOL bState = pConfig->GetString(FX_BSTRC("BaseState"), FX_BSTRC("ON")) != FX_BSTRC("OFF");
CPDF_Array *pArray = pConfig->GetArray(FX_BSTRC("ON"));
if (pArray) {
if (FPDFDOC_OCG_FindGroup(pArray, pOCGDict) >= 0) {
bState = TRUE;
}
}
pArray = pConfig->GetArray(FX_BSTRC("OFF"));
if (pArray) {
if (FPDFDOC_OCG_FindGroup(pArray, pOCGDict) >= 0) {
bState = FALSE;
}
}
pArray = pConfig->GetArray(FX_BSTRC("AS"));
if (pArray) {
CFX_ByteString csFind = csConfig + FX_BSTRC("State");
int32_t iCount = pArray->GetCount();
for (int32_t i = 0; i < iCount; i ++) {
CPDF_Dictionary *pUsage = pArray->GetDict(i);
if (!pUsage) {
continue;
}
if (pUsage->GetString(FX_BSTRC("Event"), FX_BSTRC("View")) != csConfig) {
continue;
}
CPDF_Array *pOCGs = pUsage->GetArray(FX_BSTRC("OCGs"));
if (!pOCGs) {
continue;
}
if (FPDFDOC_OCG_FindGroup(pOCGs, pOCGDict) < 0) {
continue;
}
CPDF_Dictionary *pState = pUsage->GetDict(csConfig);
if (!pState) {
continue;
}
bState = pState->GetString(csFind) != FX_BSTRC("OFF");
}
}
return bState;
}
FX_BOOL CPDF_OCContext::LoadOCGState(const CPDF_Dictionary *pOCGDict) const
{
if (!FPDFDOC_OCG_HasIntent(pOCGDict, FX_BSTRC("View"), FX_BSTRC("View"))) {
return TRUE;
}
CFX_ByteString csState = FPDFDOC_OCG_GetUsageTypeString(m_eUsageType);
CPDF_Dictionary *pUsage = pOCGDict->GetDict(FX_BSTRC("Usage"));
if (pUsage) {
CPDF_Dictionary *pState = pUsage->GetDict(csState);
if (pState) {
CFX_ByteString csFind = csState + FX_BSTRC("State");
if (pState->KeyExist(csFind)) {
return pState->GetString(csFind) != FX_BSTRC("OFF");
}
}
if (csState != FX_BSTRC("View")) {
pState = pUsage->GetDict(FX_BSTRC("View"));
if (pState && pState->KeyExist(FX_BSTRC("ViewState"))) {
return pState->GetString(FX_BSTRC("ViewState")) != FX_BSTRC("OFF");
}
}
}
FX_BOOL bDefValid = FALSE;
return LoadOCGStateFromConfig(csState, pOCGDict, bDefValid);
}
FX_BOOL CPDF_OCContext::GetOCGVisible(const CPDF_Dictionary *pOCGDict)
{
if (!pOCGDict) {
return FALSE;
}
void* bState = NULL;
if (m_OCGStates.Lookup(pOCGDict, bState)) {
return (uintptr_t)bState != 0;
}
bState = (void*)(uintptr_t)LoadOCGState(pOCGDict);
m_OCGStates.SetAt(pOCGDict, bState);
return (uintptr_t)bState != 0;
}
FX_BOOL CPDF_OCContext::GetOCGVE(CPDF_Array *pExpression, FX_BOOL bFromConfig, int nLevel)
{
if (nLevel > 32) {
return FALSE;
}
if (pExpression == NULL) {
return FALSE;
}
int32_t iCount = pExpression->GetCount();
CPDF_Object *pOCGObj;
CFX_ByteString csOperator = pExpression->GetString(0);
if (csOperator == FX_BSTRC("Not")) {
pOCGObj = pExpression->GetElementValue(1);
if (pOCGObj == NULL) {
return FALSE;
}
if (pOCGObj->GetType() == PDFOBJ_DICTIONARY) {
return !(bFromConfig ? LoadOCGState((CPDF_Dictionary*)pOCGObj) : GetOCGVisible((CPDF_Dictionary*)pOCGObj));
} else if (pOCGObj->GetType() == PDFOBJ_ARRAY) {
return !GetOCGVE((CPDF_Array*)pOCGObj, bFromConfig, nLevel + 1);
} else {
return FALSE;
}
}
if (csOperator == FX_BSTRC("Or") || csOperator == FX_BSTRC("And")) {
FX_BOOL bValue = FALSE;
for (int32_t i = 1; i < iCount; i ++) {
pOCGObj = pExpression->GetElementValue(1);
if (pOCGObj == NULL) {
continue;
}
FX_BOOL bItem = FALSE;
if (pOCGObj->GetType() == PDFOBJ_DICTIONARY) {
bItem = bFromConfig ? LoadOCGState((CPDF_Dictionary*)pOCGObj) : GetOCGVisible((CPDF_Dictionary*)pOCGObj);
} else if (pOCGObj->GetType() == PDFOBJ_ARRAY) {
bItem = GetOCGVE((CPDF_Array*)pOCGObj, bFromConfig, nLevel + 1);
}
if (i == 1) {
bValue = bItem;
} else {
if (csOperator == FX_BSTRC("Or")) {
bValue = bValue || bItem;
} else {
bValue = bValue && bItem;
}
}
}
return bValue;
}
return FALSE;
}
FX_BOOL CPDF_OCContext::LoadOCMDState(const CPDF_Dictionary *pOCMDDict, FX_BOOL bFromConfig)
{
FXSYS_assert(pOCMDDict != NULL);
CPDF_Array *pVE = pOCMDDict->GetArray(FX_BSTRC("VE"));
if (pVE != NULL) {
return GetOCGVE(pVE, bFromConfig);
}
CFX_ByteString csP = pOCMDDict->GetString(FX_BSTRC("P"), FX_BSTRC("AnyOn"));
CPDF_Object *pOCGObj = pOCMDDict->GetElementValue(FX_BSTRC("OCGs"));
if (pOCGObj == NULL) {
return TRUE;
}
if (pOCGObj->GetType() == PDFOBJ_DICTIONARY) {
return bFromConfig ? LoadOCGState((CPDF_Dictionary*)pOCGObj) : GetOCGVisible((CPDF_Dictionary*)pOCGObj);
}
if (pOCGObj->GetType() != PDFOBJ_ARRAY) {
return TRUE;
}
FX_BOOL bState = FALSE;
if (csP == FX_BSTRC("AllOn") || csP == FX_BSTRC("AllOff")) {
bState = TRUE;
}
int32_t iCount = ((CPDF_Array*)pOCGObj)->GetCount();
for (int32_t i = 0; i < iCount; i ++) {
FX_BOOL bItem = TRUE;
CPDF_Dictionary* pItemDict = ((CPDF_Array*)pOCGObj)->GetDict(i);
if (pItemDict) {
bItem = bFromConfig ? LoadOCGState(pItemDict) : GetOCGVisible(pItemDict);
}
if (csP == FX_BSTRC("AnyOn") && bItem) {
return TRUE;
}
if (csP == FX_BSTRC("AnyOff") && !bItem) {
return TRUE;
}
if (csP == FX_BSTRC("AllOn") && !bItem) {
return FALSE;
}
if (csP == FX_BSTRC("AllOff") && bItem) {
return FALSE;
}
}
return bState;
}
FX_BOOL CPDF_OCContext::CheckOCGVisible(const CPDF_Dictionary *pOCGDict)
{
if (pOCGDict == NULL) {
return TRUE;
}
CFX_ByteString csType = pOCGDict->GetString(FX_BSTRC("Type"), FX_BSTRC("OCG"));
if (csType == FX_BSTRC("OCG")) {
return GetOCGVisible(pOCGDict);
} else {
return LoadOCMDState(pOCGDict, FALSE);
}
}
void CPDF_OCContext::ResetOCContext()
{
m_OCGStates.RemoveAll();
}