You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
279 lines
7.5 KiB
279 lines
7.5 KiB
// 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/cpdf_occontext.h" |
|
|
|
#include "core/fpdfapi/page/cpdf_pageobject.h" |
|
#include "core/fpdfapi/parser/cpdf_array.h" |
|
#include "core/fpdfapi/parser/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->GetDirectObjectFor("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()->GetDictFor("OCProperties"); |
|
if (!pOCProperties) |
|
return nullptr; |
|
|
|
CPDF_Array* pOCGs = pOCProperties->GetArrayFor("OCGs"); |
|
if (!pOCGs) |
|
return nullptr; |
|
|
|
if (FindGroup(pOCGs, pOCGDict) < 0) |
|
return nullptr; |
|
|
|
CPDF_Dictionary* pConfig = pOCProperties->GetDictFor("D"); |
|
CPDF_Array* pConfigs = pOCProperties->GetArrayFor("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->GetStringFor("BaseState", "ON") != "OFF"; |
|
CPDF_Array* pArray = pConfig->GetArrayFor("ON"); |
|
if (pArray) { |
|
if (FindGroup(pArray, pOCGDict) >= 0) |
|
bState = true; |
|
} |
|
pArray = pConfig->GetArrayFor("OFF"); |
|
if (pArray) { |
|
if (FindGroup(pArray, pOCGDict) >= 0) |
|
bState = false; |
|
} |
|
pArray = pConfig->GetArrayFor("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->GetStringFor("Event", "View") != csConfig) |
|
continue; |
|
|
|
CPDF_Array* pOCGs = pUsage->GetArrayFor("OCGs"); |
|
if (!pOCGs) |
|
continue; |
|
|
|
if (FindGroup(pOCGs, pOCGDict) < 0) |
|
continue; |
|
|
|
CPDF_Dictionary* pState = pUsage->GetDictFor(csConfig); |
|
if (!pState) |
|
continue; |
|
|
|
bState = pState->GetStringFor(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->GetDictFor("Usage"); |
|
if (pUsage) { |
|
CPDF_Dictionary* pState = pUsage->GetDictFor(csState); |
|
if (pState) { |
|
CFX_ByteString csFind = csState + "State"; |
|
if (pState->KeyExist(csFind)) |
|
return pState->GetStringFor(csFind) != "OFF"; |
|
} |
|
if (csState != "View") { |
|
pState = pUsage->GetDictFor("View"); |
|
if (pState && pState->KeyExist("ViewState")) |
|
return pState->GetStringFor("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->GetArrayFor("VE"); |
|
if (pVE) |
|
return GetOCGVE(pVE, 0); |
|
|
|
CFX_ByteString csP = pOCMDDict->GetStringFor("P", "AnyOn"); |
|
CPDF_Object* pOCGObj = pOCMDDict->GetDirectObjectFor("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->GetStringFor("Type", "OCG"); |
|
if (csType == "OCG") |
|
return GetOCGVisible(pOCGDict); |
|
return LoadOCMDState(pOCGDict); |
|
}
|
|
|