// 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/fpdfapi/fpdf_page/cpdf_contentmarkdata.h"
|
#include "core/fpdfapi/fpdf_page/include/cpdf_pageobject.h"
|
#include "core/fpdfapi/fpdf_parser/include/cpdf_array.h"
|
#include "core/fpdfapi/fpdf_parser/include/cpdf_document.h"
|
#include "core/fpdfdoc/include/fpdf_doc.h"
|
|
namespace {
|
|
int32_t FPDFDOC_OCG_FindGroup(const CPDF_Array* pArray,
|
const CPDF_Dictionary* pGroupDict) {
|
if (!pArray || !pGroupDict)
|
return -1;
|
|
for (size_t i = 0; i < pArray->GetCount(); i++) {
|
if (pArray->GetDictAt(i) == pGroupDict)
|
return i;
|
}
|
return -1;
|
}
|
|
bool FPDFDOC_OCG_HasIntent(const CPDF_Dictionary* pDict,
|
const CFX_ByteStringC& csElement,
|
const CFX_ByteStringC& csDef) {
|
CPDF_Object* pIntent = pDict->GetDirectObjectBy("Intent");
|
if (!pIntent)
|
return csElement == csDef;
|
|
CFX_ByteString bsIntent;
|
if (CPDF_Array* pArray = pIntent->AsArray()) {
|
for (size_t i = 0; i < pArray->GetCount(); i++) {
|
bsIntent = pArray->GetStringAt(i);
|
if (bsIntent == "All" || bsIntent == csElement)
|
return true;
|
}
|
return false;
|
}
|
bsIntent = pIntent->GetString();
|
return bsIntent == "All" || bsIntent == csElement;
|
}
|
|
CPDF_Dictionary* FPDFDOC_OCG_GetConfig(CPDF_Document* pDoc,
|
const CPDF_Dictionary* pOCGDict) {
|
ASSERT(pOCGDict);
|
CPDF_Dictionary* pOCProperties = pDoc->GetRoot()->GetDictBy("OCProperties");
|
if (!pOCProperties)
|
return nullptr;
|
|
CPDF_Array* pOCGs = pOCProperties->GetArrayBy("OCGs");
|
if (!pOCGs)
|
return nullptr;
|
|
if (FPDFDOC_OCG_FindGroup(pOCGs, pOCGDict) < 0)
|
return nullptr;
|
|
CPDF_Dictionary* pConfig = pOCProperties->GetDictBy("D");
|
CPDF_Array* pConfigs = pOCProperties->GetArrayBy("Configs");
|
if (!pConfigs)
|
return pConfig;
|
|
for (size_t i = 0; i < pConfigs->GetCount(); i++) {
|
CPDF_Dictionary* pFind = pConfigs->GetDictAt(i);
|
if (pFind && FPDFDOC_OCG_HasIntent(pFind, "View", "View"))
|
return pFind;
|
}
|
return pConfig;
|
}
|
|
CFX_ByteString FPDFDOC_OCG_GetUsageTypeString(CPDF_OCContext::UsageType eType) {
|
CFX_ByteString csState;
|
switch (eType) {
|
case CPDF_OCContext::Design:
|
csState = "Design";
|
break;
|
case CPDF_OCContext::Print:
|
csState = "Print";
|
break;
|
case CPDF_OCContext::Export:
|
csState = "Export";
|
break;
|
default:
|
csState = "View";
|
break;
|
}
|
return csState;
|
}
|
|
} // namespace
|
|
CPDF_OCContext::CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType)
|
: m_pDocument(pDoc), m_eUsageType(eUsageType) {
|
ASSERT(pDoc);
|
}
|
|
CPDF_OCContext::~CPDF_OCContext() {
|
}
|
|
bool CPDF_OCContext::LoadOCGStateFromConfig(
|
const CFX_ByteString& csConfig,
|
const CPDF_Dictionary* pOCGDict) const {
|
CPDF_Dictionary* pConfig = FPDFDOC_OCG_GetConfig(m_pDocument, pOCGDict);
|
if (!pConfig)
|
return true;
|
|
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)
|
return bState;
|
|
CFX_ByteString csFind = csConfig + "State";
|
for (size_t i = 0; i < pArray->GetCount(); 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;
|
}
|
|
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";
|
}
|
}
|
}
|
return LoadOCGStateFromConfig(csState, pOCGDict);
|
}
|
|
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;
|
|
bool bState = LoadOCGState(pOCGDict);
|
m_OCGStates[pOCGDict] = bState;
|
return bState;
|
}
|
|
bool CPDF_OCContext::CheckObjectVisible(const CPDF_PageObject* pObj) {
|
const CPDF_ContentMarkData* pData = pObj->m_ContentMark.GetObject();
|
for (int i = 0; i < pData->CountItems(); i++) {
|
const CPDF_ContentMarkItem& item = pData->GetItem(i);
|
if (item.GetName() == "OC" &&
|
item.GetParamType() == CPDF_ContentMarkItem::PropertiesDict &&
|
!CheckOCGVisible(item.GetParam())) {
|
return false;
|
}
|
}
|
return true;
|
}
|
|
bool CPDF_OCContext::GetOCGVE(CPDF_Array* pExpression, int nLevel) {
|
if (nLevel > 32 || !pExpression)
|
return false;
|
|
CFX_ByteString csOperator = pExpression->GetStringAt(0);
|
if (csOperator == "Not") {
|
CPDF_Object* pOCGObj = pExpression->GetDirectObjectAt(1);
|
if (!pOCGObj)
|
return false;
|
if (CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
|
return !GetOCGVisible(pDict);
|
if (CPDF_Array* pArray = pOCGObj->AsArray())
|
return !GetOCGVE(pArray, nLevel + 1);
|
return false;
|
}
|
|
if (csOperator != "Or" && csOperator != "And")
|
return false;
|
|
bool bValue = false;
|
for (size_t i = 1; i < pExpression->GetCount(); i++) {
|
CPDF_Object* pOCGObj = pExpression->GetDirectObjectAt(1);
|
if (!pOCGObj)
|
continue;
|
|
bool bItem = false;
|
if (CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
|
bItem = GetOCGVisible(pDict);
|
else if (CPDF_Array* pArray = pOCGObj->AsArray())
|
bItem = GetOCGVE(pArray, nLevel + 1);
|
|
if (i == 1) {
|
bValue = bItem;
|
} else {
|
if (csOperator == "Or") {
|
bValue = bValue || bItem;
|
} else {
|
bValue = bValue && bItem;
|
}
|
}
|
}
|
return bValue;
|
}
|
|
bool CPDF_OCContext::LoadOCMDState(const CPDF_Dictionary* pOCMDDict) {
|
CPDF_Array* pVE = pOCMDDict->GetArrayBy("VE");
|
if (pVE)
|
return GetOCGVE(pVE, 0);
|
|
CFX_ByteString csP = pOCMDDict->GetStringBy("P", "AnyOn");
|
CPDF_Object* pOCGObj = pOCMDDict->GetDirectObjectBy("OCGs");
|
if (!pOCGObj)
|
return true;
|
|
if (const CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
|
return GetOCGVisible(pDict);
|
|
CPDF_Array* pArray = pOCGObj->AsArray();
|
if (!pArray)
|
return true;
|
|
bool bState = (csP == "AllOn" || csP == "AllOff");
|
for (size_t i = 0; i < pArray->GetCount(); i++) {
|
bool bItem = true;
|
CPDF_Dictionary* pItemDict = pArray->GetDictAt(i);
|
if (pItemDict)
|
bItem = GetOCGVisible(pItemDict);
|
|
if ((csP == "AnyOn" && bItem) || (csP == "AnyOff" && !bItem))
|
return true;
|
if ((csP == "AllOn" && !bItem) || (csP == "AllOff" && bItem))
|
return false;
|
}
|
return bState;
|
}
|
|
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);
|
}
|