• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
FindGroup(const CPDF_Array * pArray,const CPDF_Dictionary * pGroupDict)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 
HasIntent(const CPDF_Dictionary * pDict,const ByteStringView & csElement,const ByteStringView & csDef)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 
GetConfig(CPDF_Document * pDoc,const CPDF_Dictionary * pOCGDict)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 
GetUsageTypeString(CPDF_OCContext::UsageType eType)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 
CPDF_OCContext(CPDF_Document * pDoc,UsageType eUsageType)94 CPDF_OCContext::CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType)
95     : m_pDocument(pDoc), m_eUsageType(eUsageType) {
96   ASSERT(pDoc);
97 }
98 
~CPDF_OCContext()99 CPDF_OCContext::~CPDF_OCContext() {}
100 
LoadOCGStateFromConfig(const ByteString & csConfig,const CPDF_Dictionary * pOCGDict) const101 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 
LoadOCGState(const CPDF_Dictionary * pOCGDict) const148 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 
GetOCGVisible(const CPDF_Dictionary * pOCGDict)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 
CheckObjectVisible(const CPDF_PageObject * pObj)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 
GetOCGVE(CPDF_Array * pExpression,int nLevel)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 
LoadOCMDState(const CPDF_Dictionary * pOCMDDict)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 
CheckOCGVisible(const CPDF_Dictionary * pOCGDict)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