blob: a18924c1dcbeeebdae6e6fe6278b8b1525ed5c8e [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 "core/include/fpdfdoc/fpdf_doc.h"
static int32_t FPDFDOC_OCG_FindGroup(const CPDF_Object* pObject,
const CPDF_Dictionary* pGroupDict) {
if (!pObject || !pGroupDict)
return -1;
if (const CPDF_Array* pArray = pObject->AsArray()) {
FX_DWORD dwCount = pArray->GetCount();
for (FX_DWORD i = 0; i < dwCount; i++) {
if (pArray->GetDictAt(i) == pGroupDict)
return i;
}
return -1;
}
return pObject->GetDict() == pGroupDict ? 0 : -1;
}
static FX_BOOL FPDFDOC_OCG_HasIntent(const CPDF_Dictionary* pDict,
const CFX_ByteStringC& csElement,
const CFX_ByteStringC& csDef = "") {
CPDF_Object* pIntent = pDict->GetElementValue("Intent");
if (!pIntent) {
return csElement == csDef;
}
CFX_ByteString bsIntent;
if (CPDF_Array* pArray = pIntent->AsArray()) {
FX_DWORD dwCount = pArray->GetCount();
for (FX_DWORD i = 0; i < dwCount; i++) {
bsIntent = pArray->GetStringAt(i);
if (bsIntent == "All" || bsIntent == csElement)
return TRUE;
}
return FALSE;
}
bsIntent = pIntent->GetString();
return bsIntent == "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()->GetDictBy("OCProperties");
if (!pOCProperties) {
return NULL;
}
CPDF_Array* pOCGs = pOCProperties->GetArrayBy("OCGs");
if (!pOCGs) {
return NULL;
}
if (FPDFDOC_OCG_FindGroup(pOCGs, pOCGDict) < 0) {
return NULL;
}
CPDF_Dictionary* pConfig = pOCProperties->GetDictBy("D");
CPDF_Array* pConfigs = pOCProperties->GetArrayBy("Configs");
if (pConfigs) {
CPDF_Dictionary* pFind;
int32_t iCount = pConfigs->GetCount();
for (int32_t i = 0; i < iCount; i++) {
pFind = pConfigs->GetDictAt(i);
if (!pFind) {
continue;
}
if (!FPDFDOC_OCG_HasIntent(pFind, "View", "View")) {
continue;
}
pConfig = pFind;
break;
}
}
return pConfig;
}
static CFX_ByteString FPDFDOC_OCG_GetUsageTypeString(
CPDF_OCContext::UsageType eType) {
CFX_ByteString csState = "View";
if (eType == CPDF_OCContext::Design) {
csState = "Design";
} else if (eType == CPDF_OCContext::Print) {
csState = "Print";
} else if (eType == CPDF_OCContext::Export) {
csState = "Export";
}
return csState;
}
CPDF_OCContext::CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType) {
FXSYS_assert(pDoc);
m_pDocument = pDoc;
m_eUsageType = eUsageType;
}
CPDF_OCContext::~CPDF_OCContext() {
m_OCGStates.clear();
}
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->GetStringBy("BaseState", "ON") != "OFF";
CPDF_Array* pArray = pConfig->GetArrayBy("ON");
if (pArray) {
if (FPDFDOC_OCG_FindGroup(pArray, pOCGDict) >= 0) {
bState = TRUE;
}
}
pArray = pConfig->GetArrayBy("OFF");
if (pArray) {
if (FPDFDOC_OCG_FindGroup(pArray, pOCGDict) >= 0) {
bState = FALSE;
}
}
pArray = pConfig->GetArrayBy("AS");
if (pArray) {
CFX_ByteString csFind = csConfig + "State";
int32_t iCount = pArray->GetCount();
for (int32_t i = 0; i < iCount; i++) {
CPDF_Dictionary* pUsage = pArray->GetDictAt(i);
if (!pUsage) {
continue;
}
if (pUsage->GetStringBy("Event", "View") != csConfig) {
continue;
}
CPDF_Array* pOCGs = pUsage->GetArrayBy("OCGs");
if (!pOCGs) {
continue;
}
if (FPDFDOC_OCG_FindGroup(pOCGs, pOCGDict) < 0) {
continue;
}
CPDF_Dictionary* pState = pUsage->GetDictBy(csConfig);
if (!pState) {
continue;
}
bState = pState->GetStringBy(csFind) != "OFF";
}
}
return bState;
}
FX_BOOL CPDF_OCContext::LoadOCGState(const CPDF_Dictionary* pOCGDict) const {
if (!FPDFDOC_OCG_HasIntent(pOCGDict, "View", "View")) {
return TRUE;
}
CFX_ByteString csState = FPDFDOC_OCG_GetUsageTypeString(m_eUsageType);
CPDF_Dictionary* pUsage = pOCGDict->GetDictBy("Usage");
if (pUsage) {
CPDF_Dictionary* pState = pUsage->GetDictBy(csState);
if (pState) {
CFX_ByteString csFind = csState + "State";
if (pState->KeyExist(csFind)) {
return pState->GetStringBy(csFind) != "OFF";
}
}
if (csState != "View") {
pState = pUsage->GetDictBy("View");
if (pState && pState->KeyExist("ViewState")) {
return pState->GetStringBy("ViewState") != "OFF";
}
}
}
FX_BOOL bDefValid = FALSE;
return LoadOCGStateFromConfig(csState, pOCGDict, bDefValid);
}
FX_BOOL CPDF_OCContext::GetOCGVisible(const CPDF_Dictionary* pOCGDict) {
if (!pOCGDict)
return FALSE;
const auto it = m_OCGStates.find(pOCGDict);
if (it != m_OCGStates.end())
return it->second;
FX_BOOL bState = LoadOCGState(pOCGDict);
m_OCGStates[pOCGDict] = bState;
return bState;
}
FX_BOOL CPDF_OCContext::GetOCGVE(CPDF_Array* pExpression,
FX_BOOL bFromConfig,
int nLevel) {
if (nLevel > 32) {
return FALSE;
}
if (!pExpression) {
return FALSE;
}
int32_t iCount = pExpression->GetCount();
CPDF_Object* pOCGObj;
CFX_ByteString csOperator = pExpression->GetStringAt(0);
if (csOperator == "Not") {
pOCGObj = pExpression->GetElementValue(1);
if (!pOCGObj)
return FALSE;
if (CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
return !(bFromConfig ? LoadOCGState(pDict) : GetOCGVisible(pDict));
if (CPDF_Array* pArray = pOCGObj->AsArray())
return !GetOCGVE(pArray, bFromConfig, nLevel + 1);
return FALSE;
}
if (csOperator == "Or" || csOperator == "And") {
FX_BOOL bValue = FALSE;
for (int32_t i = 1; i < iCount; i++) {
pOCGObj = pExpression->GetElementValue(1);
if (!pOCGObj) {
continue;
}
FX_BOOL bItem = FALSE;
if (CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
bItem = bFromConfig ? LoadOCGState(pDict) : GetOCGVisible(pDict);
else if (CPDF_Array* pArray = pOCGObj->AsArray())
bItem = GetOCGVE(pArray, bFromConfig, nLevel + 1);
if (i == 1) {
bValue = bItem;
} else {
if (csOperator == "Or") {
bValue = bValue || bItem;
} else {
bValue = bValue && bItem;
}
}
}
return bValue;
}
return FALSE;
}
FX_BOOL CPDF_OCContext::LoadOCMDState(const CPDF_Dictionary* pOCMDDict,
FX_BOOL bFromConfig) {
CPDF_Array* pVE = pOCMDDict->GetArrayBy("VE");
if (pVE) {
return GetOCGVE(pVE, bFromConfig);
}
CFX_ByteString csP = pOCMDDict->GetStringBy("P", "AnyOn");
CPDF_Object* pOCGObj = pOCMDDict->GetElementValue("OCGs");
if (!pOCGObj)
return TRUE;
if (const CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
return bFromConfig ? LoadOCGState(pDict) : GetOCGVisible(pDict);
CPDF_Array* pArray = pOCGObj->AsArray();
if (!pArray)
return TRUE;
FX_BOOL bState = FALSE;
if (csP == "AllOn" || csP == "AllOff") {
bState = TRUE;
}
int32_t iCount = pArray->GetCount();
for (int32_t i = 0; i < iCount; i++) {
FX_BOOL bItem = TRUE;
CPDF_Dictionary* pItemDict = pArray->GetDictAt(i);
if (pItemDict)
bItem = bFromConfig ? LoadOCGState(pItemDict) : GetOCGVisible(pItemDict);
if ((csP == "AnyOn" && bItem) || (csP == "AnyOff" && !bItem))
return TRUE;
if ((csP == "AllOn" && !bItem) || (csP == "AllOff" && bItem))
return FALSE;
}
return bState;
}
FX_BOOL CPDF_OCContext::CheckOCGVisible(const CPDF_Dictionary* pOCGDict) {
if (!pOCGDict) {
return TRUE;
}
CFX_ByteString csType = pOCGDict->GetStringBy("Type", "OCG");
if (csType == "OCG") {
return GetOCGVisible(pOCGDict);
}
return LoadOCMDState(pOCGDict, FALSE);
}
void CPDF_OCContext::ResetOCContext() {
m_OCGStates.clear();
}