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