Home | History | Annotate | Download | only in fpdfdoc
      1 // Copyright 2016 PDFium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
      6 
      7 #include "core/fpdfdoc/cpdf_occontext.h"
      8 
      9 #include "core/fpdfapi/page/cpdf_pageobject.h"
     10 #include "core/fpdfapi/parser/cpdf_array.h"
     11 #include "core/fpdfapi/parser/cpdf_document.h"
     12 
     13 namespace {
     14 
     15 int32_t FindGroup(const CPDF_Array* pArray, const CPDF_Dictionary* pGroupDict) {
     16   if (!pArray || !pGroupDict)
     17     return -1;
     18 
     19   for (size_t i = 0; i < pArray->GetCount(); i++) {
     20     if (pArray->GetDictAt(i) == pGroupDict)
     21       return i;
     22   }
     23   return -1;
     24 }
     25 
     26 bool HasIntent(const CPDF_Dictionary* pDict,
     27                const CFX_ByteStringC& csElement,
     28                const CFX_ByteStringC& csDef) {
     29   CPDF_Object* pIntent = pDict->GetDirectObjectFor("Intent");
     30   if (!pIntent)
     31     return csElement == csDef;
     32 
     33   CFX_ByteString bsIntent;
     34   if (CPDF_Array* pArray = pIntent->AsArray()) {
     35     for (size_t i = 0; i < pArray->GetCount(); i++) {
     36       bsIntent = pArray->GetStringAt(i);
     37       if (bsIntent == "All" || bsIntent == csElement)
     38         return true;
     39     }
     40     return false;
     41   }
     42   bsIntent = pIntent->GetString();
     43   return bsIntent == "All" || bsIntent == csElement;
     44 }
     45 
     46 CPDF_Dictionary* GetConfig(CPDF_Document* pDoc,
     47                            const CPDF_Dictionary* pOCGDict) {
     48   ASSERT(pOCGDict);
     49   CPDF_Dictionary* pOCProperties = pDoc->GetRoot()->GetDictFor("OCProperties");
     50   if (!pOCProperties)
     51     return nullptr;
     52 
     53   CPDF_Array* pOCGs = pOCProperties->GetArrayFor("OCGs");
     54   if (!pOCGs)
     55     return nullptr;
     56 
     57   if (FindGroup(pOCGs, pOCGDict) < 0)
     58     return nullptr;
     59 
     60   CPDF_Dictionary* pConfig = pOCProperties->GetDictFor("D");
     61   CPDF_Array* pConfigs = pOCProperties->GetArrayFor("Configs");
     62   if (!pConfigs)
     63     return pConfig;
     64 
     65   for (size_t i = 0; i < pConfigs->GetCount(); i++) {
     66     CPDF_Dictionary* pFind = pConfigs->GetDictAt(i);
     67     if (pFind && HasIntent(pFind, "View", "View"))
     68       return pFind;
     69   }
     70   return pConfig;
     71 }
     72 
     73 CFX_ByteString GetUsageTypeString(CPDF_OCContext::UsageType eType) {
     74   CFX_ByteString csState;
     75   switch (eType) {
     76     case CPDF_OCContext::Design:
     77       csState = "Design";
     78       break;
     79     case CPDF_OCContext::Print:
     80       csState = "Print";
     81       break;
     82     case CPDF_OCContext::Export:
     83       csState = "Export";
     84       break;
     85     default:
     86       csState = "View";
     87       break;
     88   }
     89   return csState;
     90 }
     91 
     92 }  // namespace
     93 
     94 CPDF_OCContext::CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType)
     95     : m_pDocument(pDoc), m_eUsageType(eUsageType) {
     96   ASSERT(pDoc);
     97 }
     98 
     99 CPDF_OCContext::~CPDF_OCContext() {}
    100 
    101 bool CPDF_OCContext::LoadOCGStateFromConfig(
    102     const CFX_ByteString& csConfig,
    103     const CPDF_Dictionary* pOCGDict) const {
    104   CPDF_Dictionary* pConfig = GetConfig(m_pDocument, pOCGDict);
    105   if (!pConfig)
    106     return true;
    107 
    108   bool bState = pConfig->GetStringFor("BaseState", "ON") != "OFF";
    109   CPDF_Array* pArray = pConfig->GetArrayFor("ON");
    110   if (pArray) {
    111     if (FindGroup(pArray, pOCGDict) >= 0)
    112       bState = true;
    113   }
    114   pArray = pConfig->GetArrayFor("OFF");
    115   if (pArray) {
    116     if (FindGroup(pArray, pOCGDict) >= 0)
    117       bState = false;
    118   }
    119   pArray = pConfig->GetArrayFor("AS");
    120   if (!pArray)
    121     return bState;
    122 
    123   CFX_ByteString csFind = csConfig + "State";
    124   for (size_t i = 0; i < pArray->GetCount(); i++) {
    125     CPDF_Dictionary* pUsage = pArray->GetDictAt(i);
    126     if (!pUsage)
    127       continue;
    128 
    129     if (pUsage->GetStringFor("Event", "View") != csConfig)
    130       continue;
    131 
    132     CPDF_Array* pOCGs = pUsage->GetArrayFor("OCGs");
    133     if (!pOCGs)
    134       continue;
    135 
    136     if (FindGroup(pOCGs, pOCGDict) < 0)
    137       continue;
    138 
    139     CPDF_Dictionary* pState = pUsage->GetDictFor(csConfig);
    140     if (!pState)
    141       continue;
    142 
    143     bState = pState->GetStringFor(csFind) != "OFF";
    144   }
    145   return bState;
    146 }
    147 
    148 bool CPDF_OCContext::LoadOCGState(const CPDF_Dictionary* pOCGDict) const {
    149   if (!HasIntent(pOCGDict, "View", "View"))
    150     return true;
    151 
    152   CFX_ByteString csState = GetUsageTypeString(m_eUsageType);
    153   CPDF_Dictionary* pUsage = pOCGDict->GetDictFor("Usage");
    154   if (pUsage) {
    155     CPDF_Dictionary* pState = pUsage->GetDictFor(csState);
    156     if (pState) {
    157       CFX_ByteString csFind = csState + "State";
    158       if (pState->KeyExist(csFind))
    159         return pState->GetStringFor(csFind) != "OFF";
    160     }
    161     if (csState != "View") {
    162       pState = pUsage->GetDictFor("View");
    163       if (pState && pState->KeyExist("ViewState"))
    164         return pState->GetStringFor("ViewState") != "OFF";
    165     }
    166   }
    167   return LoadOCGStateFromConfig(csState, pOCGDict);
    168 }
    169 
    170 bool CPDF_OCContext::GetOCGVisible(const CPDF_Dictionary* pOCGDict) {
    171   if (!pOCGDict)
    172     return false;
    173 
    174   const auto it = m_OCGStates.find(pOCGDict);
    175   if (it != m_OCGStates.end())
    176     return it->second;
    177 
    178   bool bState = LoadOCGState(pOCGDict);
    179   m_OCGStates[pOCGDict] = bState;
    180   return bState;
    181 }
    182 
    183 bool CPDF_OCContext::CheckObjectVisible(const CPDF_PageObject* pObj) {
    184   for (int i = 0; i < pObj->m_ContentMark.CountItems(); i++) {
    185     const CPDF_ContentMarkItem& item = pObj->m_ContentMark.GetItem(i);
    186     if (item.GetName() == "OC" &&
    187         item.GetParamType() == CPDF_ContentMarkItem::PropertiesDict &&
    188         !CheckOCGVisible(item.GetParam())) {
    189       return false;
    190     }
    191   }
    192   return true;
    193 }
    194 
    195 bool CPDF_OCContext::GetOCGVE(CPDF_Array* pExpression, int nLevel) {
    196   if (nLevel > 32 || !pExpression)
    197     return false;
    198 
    199   CFX_ByteString csOperator = pExpression->GetStringAt(0);
    200   if (csOperator == "Not") {
    201     CPDF_Object* pOCGObj = pExpression->GetDirectObjectAt(1);
    202     if (!pOCGObj)
    203       return false;
    204     if (CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
    205       return !GetOCGVisible(pDict);
    206     if (CPDF_Array* pArray = pOCGObj->AsArray())
    207       return !GetOCGVE(pArray, nLevel + 1);
    208     return false;
    209   }
    210 
    211   if (csOperator != "Or" && csOperator != "And")
    212     return false;
    213 
    214   bool bValue = false;
    215   for (size_t i = 1; i < pExpression->GetCount(); i++) {
    216     CPDF_Object* pOCGObj = pExpression->GetDirectObjectAt(1);
    217     if (!pOCGObj)
    218       continue;
    219 
    220     bool bItem = false;
    221     if (CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
    222       bItem = GetOCGVisible(pDict);
    223     else if (CPDF_Array* pArray = pOCGObj->AsArray())
    224       bItem = GetOCGVE(pArray, nLevel + 1);
    225 
    226     if (i == 1) {
    227       bValue = bItem;
    228     } else {
    229       if (csOperator == "Or") {
    230         bValue = bValue || bItem;
    231       } else {
    232         bValue = bValue && bItem;
    233       }
    234     }
    235   }
    236   return bValue;
    237 }
    238 
    239 bool CPDF_OCContext::LoadOCMDState(const CPDF_Dictionary* pOCMDDict) {
    240   CPDF_Array* pVE = pOCMDDict->GetArrayFor("VE");
    241   if (pVE)
    242     return GetOCGVE(pVE, 0);
    243 
    244   CFX_ByteString csP = pOCMDDict->GetStringFor("P", "AnyOn");
    245   CPDF_Object* pOCGObj = pOCMDDict->GetDirectObjectFor("OCGs");
    246   if (!pOCGObj)
    247     return true;
    248 
    249   if (const CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
    250     return GetOCGVisible(pDict);
    251 
    252   CPDF_Array* pArray = pOCGObj->AsArray();
    253   if (!pArray)
    254     return true;
    255 
    256   bool bState = (csP == "AllOn" || csP == "AllOff");
    257   for (size_t i = 0; i < pArray->GetCount(); i++) {
    258     bool bItem = true;
    259     CPDF_Dictionary* pItemDict = pArray->GetDictAt(i);
    260     if (pItemDict)
    261       bItem = GetOCGVisible(pItemDict);
    262 
    263     if ((csP == "AnyOn" && bItem) || (csP == "AnyOff" && !bItem))
    264       return true;
    265     if ((csP == "AllOn" && !bItem) || (csP == "AllOff" && bItem))
    266       return false;
    267   }
    268   return bState;
    269 }
    270 
    271 bool CPDF_OCContext::CheckOCGVisible(const CPDF_Dictionary* pOCGDict) {
    272   if (!pOCGDict)
    273     return true;
    274 
    275   CFX_ByteString csType = pOCGDict->GetStringFor("Type", "OCG");
    276   if (csType == "OCG")
    277     return GetOCGVisible(pOCGDict);
    278   return LoadOCMDState(pOCGDict);
    279 }
    280