Home | History | Annotate | Download | only in fpdfdoc
      1 // Copyright 2014 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/include/fpdfdoc/fpdf_doc.h"
      8 
      9 static int32_t FPDFDOC_OCG_FindGroup(const CPDF_Object* pObject,
     10                                      const CPDF_Dictionary* pGroupDict) {
     11   if (!pObject || !pGroupDict)
     12     return -1;
     13 
     14   if (const CPDF_Array* pArray = pObject->AsArray()) {
     15     FX_DWORD dwCount = pArray->GetCount();
     16     for (FX_DWORD i = 0; i < dwCount; i++) {
     17       if (pArray->GetDict(i) == pGroupDict)
     18         return i;
     19     }
     20     return -1;
     21   }
     22   return pObject->GetDict() == pGroupDict ? 0 : -1;
     23 }
     24 static FX_BOOL FPDFDOC_OCG_HasIntent(const CPDF_Dictionary* pDict,
     25                                      const CFX_ByteStringC& csElement,
     26                                      const CFX_ByteStringC& csDef = "") {
     27   CPDF_Object* pIntent = pDict->GetElementValue("Intent");
     28   if (!pIntent) {
     29     return csElement == csDef;
     30   }
     31   CFX_ByteString bsIntent;
     32   if (CPDF_Array* pArray = pIntent->AsArray()) {
     33     FX_DWORD dwCount = pArray->GetCount();
     34     for (FX_DWORD i = 0; i < dwCount; i++) {
     35       bsIntent = pArray->GetString(i);
     36       if (bsIntent == "All" || bsIntent == csElement)
     37         return TRUE;
     38     }
     39     return FALSE;
     40   }
     41   bsIntent = pIntent->GetString();
     42   return bsIntent == "All" || bsIntent == csElement;
     43 }
     44 static CPDF_Dictionary* FPDFDOC_OCG_GetConfig(CPDF_Document* pDoc,
     45                                               const CPDF_Dictionary* pOCGDict,
     46                                               const CFX_ByteStringC& bsState) {
     47   FXSYS_assert(pDoc && pOCGDict);
     48   CPDF_Dictionary* pOCProperties = pDoc->GetRoot()->GetDict("OCProperties");
     49   if (!pOCProperties) {
     50     return NULL;
     51   }
     52   CPDF_Array* pOCGs = pOCProperties->GetArray("OCGs");
     53   if (!pOCGs) {
     54     return NULL;
     55   }
     56   if (FPDFDOC_OCG_FindGroup(pOCGs, pOCGDict) < 0) {
     57     return NULL;
     58   }
     59   CPDF_Dictionary* pConfig = pOCProperties->GetDict("D");
     60   CPDF_Array* pConfigs = pOCProperties->GetArray("Configs");
     61   if (pConfigs) {
     62     CPDF_Dictionary* pFind;
     63     int32_t iCount = pConfigs->GetCount();
     64     for (int32_t i = 0; i < iCount; i++) {
     65       pFind = pConfigs->GetDict(i);
     66       if (!pFind) {
     67         continue;
     68       }
     69       if (!FPDFDOC_OCG_HasIntent(pFind, "View", "View")) {
     70         continue;
     71       }
     72       pConfig = pFind;
     73       break;
     74     }
     75   }
     76   return pConfig;
     77 }
     78 static CFX_ByteString FPDFDOC_OCG_GetUsageTypeString(
     79     CPDF_OCContext::UsageType eType) {
     80   CFX_ByteString csState = "View";
     81   if (eType == CPDF_OCContext::Design) {
     82     csState = "Design";
     83   } else if (eType == CPDF_OCContext::Print) {
     84     csState = "Print";
     85   } else if (eType == CPDF_OCContext::Export) {
     86     csState = "Export";
     87   }
     88   return csState;
     89 }
     90 CPDF_OCContext::CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType) {
     91   FXSYS_assert(pDoc);
     92   m_pDocument = pDoc;
     93   m_eUsageType = eUsageType;
     94 }
     95 CPDF_OCContext::~CPDF_OCContext() {
     96   m_OCGStates.clear();
     97 }
     98 FX_BOOL CPDF_OCContext::LoadOCGStateFromConfig(const CFX_ByteStringC& csConfig,
     99                                                const CPDF_Dictionary* pOCGDict,
    100                                                FX_BOOL& bValidConfig) const {
    101   CPDF_Dictionary* pConfig =
    102       FPDFDOC_OCG_GetConfig(m_pDocument, pOCGDict, csConfig);
    103   if (!pConfig) {
    104     return TRUE;
    105   }
    106   bValidConfig = TRUE;
    107   FX_BOOL bState = pConfig->GetString("BaseState", "ON") != "OFF";
    108   CPDF_Array* pArray = pConfig->GetArray("ON");
    109   if (pArray) {
    110     if (FPDFDOC_OCG_FindGroup(pArray, pOCGDict) >= 0) {
    111       bState = TRUE;
    112     }
    113   }
    114   pArray = pConfig->GetArray("OFF");
    115   if (pArray) {
    116     if (FPDFDOC_OCG_FindGroup(pArray, pOCGDict) >= 0) {
    117       bState = FALSE;
    118     }
    119   }
    120   pArray = pConfig->GetArray("AS");
    121   if (pArray) {
    122     CFX_ByteString csFind = csConfig + "State";
    123     int32_t iCount = pArray->GetCount();
    124     for (int32_t i = 0; i < iCount; i++) {
    125       CPDF_Dictionary* pUsage = pArray->GetDict(i);
    126       if (!pUsage) {
    127         continue;
    128       }
    129       if (pUsage->GetString("Event", "View") != csConfig) {
    130         continue;
    131       }
    132       CPDF_Array* pOCGs = pUsage->GetArray("OCGs");
    133       if (!pOCGs) {
    134         continue;
    135       }
    136       if (FPDFDOC_OCG_FindGroup(pOCGs, pOCGDict) < 0) {
    137         continue;
    138       }
    139       CPDF_Dictionary* pState = pUsage->GetDict(csConfig);
    140       if (!pState) {
    141         continue;
    142       }
    143       bState = pState->GetString(csFind) != "OFF";
    144     }
    145   }
    146   return bState;
    147 }
    148 FX_BOOL CPDF_OCContext::LoadOCGState(const CPDF_Dictionary* pOCGDict) const {
    149   if (!FPDFDOC_OCG_HasIntent(pOCGDict, "View", "View")) {
    150     return TRUE;
    151   }
    152   CFX_ByteString csState = FPDFDOC_OCG_GetUsageTypeString(m_eUsageType);
    153   CPDF_Dictionary* pUsage = pOCGDict->GetDict("Usage");
    154   if (pUsage) {
    155     CPDF_Dictionary* pState = pUsage->GetDict(csState);
    156     if (pState) {
    157       CFX_ByteString csFind = csState + "State";
    158       if (pState->KeyExist(csFind)) {
    159         return pState->GetString(csFind) != "OFF";
    160       }
    161     }
    162     if (csState != "View") {
    163       pState = pUsage->GetDict("View");
    164       if (pState && pState->KeyExist("ViewState")) {
    165         return pState->GetString("ViewState") != "OFF";
    166       }
    167     }
    168   }
    169   FX_BOOL bDefValid = FALSE;
    170   return LoadOCGStateFromConfig(csState, pOCGDict, bDefValid);
    171 }
    172 
    173 FX_BOOL CPDF_OCContext::GetOCGVisible(const CPDF_Dictionary* pOCGDict) {
    174   if (!pOCGDict)
    175     return FALSE;
    176 
    177   const auto it = m_OCGStates.find(pOCGDict);
    178   if (it != m_OCGStates.end())
    179     return it->second;
    180 
    181   FX_BOOL bState = LoadOCGState(pOCGDict);
    182   m_OCGStates[pOCGDict] = bState;
    183   return bState;
    184 }
    185 
    186 FX_BOOL CPDF_OCContext::GetOCGVE(CPDF_Array* pExpression,
    187                                  FX_BOOL bFromConfig,
    188                                  int nLevel) {
    189   if (nLevel > 32) {
    190     return FALSE;
    191   }
    192   if (!pExpression) {
    193     return FALSE;
    194   }
    195   int32_t iCount = pExpression->GetCount();
    196   CPDF_Object* pOCGObj;
    197   CFX_ByteString csOperator = pExpression->GetString(0);
    198   if (csOperator == "Not") {
    199     pOCGObj = pExpression->GetElementValue(1);
    200     if (!pOCGObj)
    201       return FALSE;
    202     if (CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
    203       return !(bFromConfig ? LoadOCGState(pDict) : GetOCGVisible(pDict));
    204     if (CPDF_Array* pArray = pOCGObj->AsArray())
    205       return !GetOCGVE(pArray, bFromConfig, nLevel + 1);
    206     return FALSE;
    207   }
    208   if (csOperator == "Or" || csOperator == "And") {
    209     FX_BOOL bValue = FALSE;
    210     for (int32_t i = 1; i < iCount; i++) {
    211       pOCGObj = pExpression->GetElementValue(1);
    212       if (!pOCGObj) {
    213         continue;
    214       }
    215       FX_BOOL bItem = FALSE;
    216       if (CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
    217         bItem = bFromConfig ? LoadOCGState(pDict) : GetOCGVisible(pDict);
    218       else if (CPDF_Array* pArray = pOCGObj->AsArray())
    219         bItem = GetOCGVE(pArray, bFromConfig, nLevel + 1);
    220 
    221       if (i == 1) {
    222         bValue = bItem;
    223       } else {
    224         if (csOperator == "Or") {
    225           bValue = bValue || bItem;
    226         } else {
    227           bValue = bValue && bItem;
    228         }
    229       }
    230     }
    231     return bValue;
    232   }
    233   return FALSE;
    234 }
    235 FX_BOOL CPDF_OCContext::LoadOCMDState(const CPDF_Dictionary* pOCMDDict,
    236                                       FX_BOOL bFromConfig) {
    237   CPDF_Array* pVE = pOCMDDict->GetArray("VE");
    238   if (pVE) {
    239     return GetOCGVE(pVE, bFromConfig);
    240   }
    241   CFX_ByteString csP = pOCMDDict->GetString("P", "AnyOn");
    242   CPDF_Object* pOCGObj = pOCMDDict->GetElementValue("OCGs");
    243   if (!pOCGObj)
    244     return TRUE;
    245   if (const CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
    246     return bFromConfig ? LoadOCGState(pDict) : GetOCGVisible(pDict);
    247 
    248   CPDF_Array* pArray = pOCGObj->AsArray();
    249   if (!pArray)
    250     return TRUE;
    251 
    252   FX_BOOL bState = FALSE;
    253   if (csP == "AllOn" || csP == "AllOff") {
    254     bState = TRUE;
    255   }
    256   int32_t iCount = pArray->GetCount();
    257   for (int32_t i = 0; i < iCount; i++) {
    258     FX_BOOL bItem = TRUE;
    259     CPDF_Dictionary* pItemDict = pArray->GetDict(i);
    260     if (pItemDict)
    261       bItem = bFromConfig ? LoadOCGState(pItemDict) : 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 FX_BOOL CPDF_OCContext::CheckOCGVisible(const CPDF_Dictionary* pOCGDict) {
    271   if (!pOCGDict) {
    272     return TRUE;
    273   }
    274   CFX_ByteString csType = pOCGDict->GetString("Type", "OCG");
    275   if (csType == "OCG") {
    276     return GetOCGVisible(pOCGDict);
    277   }
    278   return LoadOCMDState(pOCGDict, FALSE);
    279 }
    280 void CPDF_OCContext::ResetOCContext() {
    281   m_OCGStates.clear();
    282 }
    283