|  | // Copyright 2016 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/fpdfdoc/include/cpdf_occontext.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" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | int32_t 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 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* 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 (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 && HasIntent(pFind, "View", "View")) | 
|  | return pFind; | 
|  | } | 
|  | return pConfig; | 
|  | } | 
|  |  | 
|  | CFX_ByteString 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 = GetConfig(m_pDocument, pOCGDict); | 
|  | if (!pConfig) | 
|  | return true; | 
|  |  | 
|  | bool bState = pConfig->GetStringBy("BaseState", "ON") != "OFF"; | 
|  | CPDF_Array* pArray = pConfig->GetArrayBy("ON"); | 
|  | if (pArray) { | 
|  | if (FindGroup(pArray, pOCGDict) >= 0) | 
|  | bState = true; | 
|  | } | 
|  | pArray = pConfig->GetArrayBy("OFF"); | 
|  | if (pArray) { | 
|  | if (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 (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 (!HasIntent(pOCGDict, "View", "View")) | 
|  | return true; | 
|  |  | 
|  | CFX_ByteString csState = 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) { | 
|  | for (int i = 0; i < pObj->m_ContentMark.CountItems(); i++) { | 
|  | const CPDF_ContentMarkItem& item = pObj->m_ContentMark.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); | 
|  | } |