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 ByteStringView& csElement, 28 const ByteStringView& csDef) { 29 CPDF_Object* pIntent = pDict->GetDirectObjectFor("Intent"); 30 if (!pIntent) 31 return csElement == csDef; 32 33 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 ByteString GetUsageTypeString(CPDF_OCContext::UsageType eType) { 74 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 ByteString& csConfig, 103 const CPDF_Dictionary* pOCGDict) const { 104 CPDF_Dictionary* pConfig = GetConfig(m_pDocument.Get(), 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 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 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 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 (size_t 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 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 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 // At least one entry of OCGs needs to be a valid dictionary for it to be 258 // considered present. See "OCGs" in table 4.49 in the PDF 1.7 spec. 259 bool bValidEntrySeen = false; 260 for (size_t i = 0; i < pArray->GetCount(); i++) { 261 bool bItem = true; 262 CPDF_Dictionary* pItemDict = pArray->GetDictAt(i); 263 if (!pItemDict) 264 continue; 265 266 bValidEntrySeen = true; 267 bItem = GetOCGVisible(pItemDict); 268 269 if ((csP == "AnyOn" && bItem) || (csP == "AnyOff" && !bItem)) 270 return true; 271 if ((csP == "AllOn" && !bItem) || (csP == "AllOff" && bItem)) 272 return false; 273 } 274 275 return !bValidEntrySeen || bState; 276 } 277 278 bool CPDF_OCContext::CheckOCGVisible(const CPDF_Dictionary* pOCGDict) { 279 if (!pOCGDict) 280 return true; 281 282 ByteString csType = pOCGDict->GetStringFor("Type", "OCG"); 283 if (csType == "OCG") 284 return GetOCGVisible(pOCGDict); 285 return LoadOCMDState(pOCGDict); 286 } 287