• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The PDFium Authors
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/fpdfapi/page/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_dictionary.h"
12 #include "core/fpdfapi/parser/cpdf_document.h"
13 #include "third_party/base/check.h"
14 
15 namespace {
16 
HasIntent(const CPDF_Dictionary * pDict,ByteStringView csElement,ByteStringView csDef)17 bool HasIntent(const CPDF_Dictionary* pDict,
18                ByteStringView csElement,
19                ByteStringView csDef) {
20   RetainPtr<const CPDF_Object> pIntent = pDict->GetDirectObjectFor("Intent");
21   if (!pIntent)
22     return csElement == csDef;
23 
24   ByteString bsIntent;
25   if (const CPDF_Array* pArray = pIntent->AsArray()) {
26     for (size_t i = 0; i < pArray->size(); i++) {
27       bsIntent = pArray->GetByteStringAt(i);
28       if (bsIntent == "All" || bsIntent == csElement)
29         return true;
30     }
31     return false;
32   }
33   bsIntent = pIntent->GetString();
34   return bsIntent == "All" || bsIntent == csElement;
35 }
36 
GetConfig(CPDF_Document * pDoc,const CPDF_Dictionary * pOCGDict)37 RetainPtr<const CPDF_Dictionary> GetConfig(CPDF_Document* pDoc,
38                                            const CPDF_Dictionary* pOCGDict) {
39   DCHECK(pOCGDict);
40   RetainPtr<const CPDF_Dictionary> pOCProperties =
41       pDoc->GetRoot()->GetDictFor("OCProperties");
42   if (!pOCProperties)
43     return nullptr;
44 
45   RetainPtr<const CPDF_Array> pOCGs = pOCProperties->GetArrayFor("OCGs");
46   if (!pOCGs)
47     return nullptr;
48 
49   if (!pOCGs->Contains(pOCGDict))
50     return nullptr;
51 
52   RetainPtr<const CPDF_Dictionary> pConfig = pOCProperties->GetDictFor("D");
53   RetainPtr<const CPDF_Array> pConfigArray =
54       pOCProperties->GetArrayFor("Configs");
55   if (!pConfigArray)
56     return pConfig;
57 
58   for (size_t i = 0; i < pConfigArray->size(); i++) {
59     RetainPtr<const CPDF_Dictionary> pFind = pConfigArray->GetDictAt(i);
60     if (pFind && HasIntent(pFind.Get(), "View", ""))
61       return pFind;
62   }
63   return pConfig;
64 }
65 
GetUsageTypeString(CPDF_OCContext::UsageType eType)66 ByteString GetUsageTypeString(CPDF_OCContext::UsageType eType) {
67   ByteString csState;
68   switch (eType) {
69     case CPDF_OCContext::kDesign:
70       csState = "Design";
71       break;
72     case CPDF_OCContext::kPrint:
73       csState = "Print";
74       break;
75     case CPDF_OCContext::kExport:
76       csState = "Export";
77       break;
78     default:
79       csState = "View";
80       break;
81   }
82   return csState;
83 }
84 
85 }  // namespace
86 
CPDF_OCContext(CPDF_Document * pDoc,UsageType eUsageType)87 CPDF_OCContext::CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType)
88     : m_pDocument(pDoc), m_eUsageType(eUsageType) {
89   DCHECK(pDoc);
90 }
91 
92 CPDF_OCContext::~CPDF_OCContext() = default;
93 
LoadOCGStateFromConfig(const ByteString & csConfig,const CPDF_Dictionary * pOCGDict) const94 bool CPDF_OCContext::LoadOCGStateFromConfig(
95     const ByteString& csConfig,
96     const CPDF_Dictionary* pOCGDict) const {
97   RetainPtr<const CPDF_Dictionary> pConfig = GetConfig(m_pDocument, pOCGDict);
98   if (!pConfig)
99     return true;
100 
101   bool bState = pConfig->GetByteStringFor("BaseState", "ON") != "OFF";
102   RetainPtr<const CPDF_Array> pArray = pConfig->GetArrayFor("ON");
103   if (pArray && pArray->Contains(pOCGDict))
104     bState = true;
105 
106   pArray = pConfig->GetArrayFor("OFF");
107   if (pArray && pArray->Contains(pOCGDict))
108     bState = false;
109 
110   pArray = pConfig->GetArrayFor("AS");
111   if (!pArray)
112     return bState;
113 
114   ByteString csFind = csConfig + "State";
115   for (size_t i = 0; i < pArray->size(); i++) {
116     RetainPtr<const CPDF_Dictionary> pUsage = pArray->GetDictAt(i);
117     if (!pUsage)
118       continue;
119 
120     if (pUsage->GetByteStringFor("Event", "View") != csConfig)
121       continue;
122 
123     RetainPtr<const CPDF_Array> pOCGs = pUsage->GetArrayFor("OCGs");
124     if (!pOCGs)
125       continue;
126 
127     if (!pOCGs->Contains(pOCGDict))
128       continue;
129 
130     RetainPtr<const CPDF_Dictionary> pState = pUsage->GetDictFor(csConfig);
131     if (!pState)
132       continue;
133 
134     bState = pState->GetByteStringFor(csFind) != "OFF";
135   }
136   return bState;
137 }
138 
LoadOCGState(const CPDF_Dictionary * pOCGDict) const139 bool CPDF_OCContext::LoadOCGState(const CPDF_Dictionary* pOCGDict) const {
140   if (!HasIntent(pOCGDict, "View", "View"))
141     return true;
142 
143   ByteString csState = GetUsageTypeString(m_eUsageType);
144   RetainPtr<const CPDF_Dictionary> pUsage = pOCGDict->GetDictFor("Usage");
145   if (pUsage) {
146     RetainPtr<const CPDF_Dictionary> pState = pUsage->GetDictFor(csState);
147     if (pState) {
148       ByteString csFind = csState + "State";
149       if (pState->KeyExist(csFind))
150         return pState->GetByteStringFor(csFind) != "OFF";
151     }
152     if (csState != "View") {
153       pState = pUsage->GetDictFor("View");
154       if (pState && pState->KeyExist("ViewState"))
155         return pState->GetByteStringFor("ViewState") != "OFF";
156     }
157   }
158   return LoadOCGStateFromConfig(csState, pOCGDict);
159 }
160 
GetOCGVisible(const CPDF_Dictionary * pOCGDict) const161 bool CPDF_OCContext::GetOCGVisible(const CPDF_Dictionary* pOCGDict) const {
162   if (!pOCGDict)
163     return false;
164 
165   const auto it = m_OGCStateCache.find(pOCGDict);
166   if (it != m_OGCStateCache.end())
167     return it->second;
168 
169   bool bState = LoadOCGState(pOCGDict);
170   m_OGCStateCache[pdfium::WrapRetain(pOCGDict)] = bState;
171   return bState;
172 }
173 
CheckPageObjectVisible(const CPDF_PageObject * pObj) const174 bool CPDF_OCContext::CheckPageObjectVisible(const CPDF_PageObject* pObj) const {
175   const CPDF_ContentMarks* pMarks = pObj->GetContentMarks();
176   for (size_t i = 0; i < pMarks->CountItems(); ++i) {
177     const CPDF_ContentMarkItem* item = pMarks->GetItem(i);
178     if (item->GetName() == "OC" &&
179         item->GetParamType() == CPDF_ContentMarkItem::kPropertiesDict &&
180         !CheckOCGDictVisible(item->GetParam().Get())) {
181       return false;
182     }
183   }
184   return true;
185 }
186 
GetOCGVE(const CPDF_Array * pExpression,int nLevel) const187 bool CPDF_OCContext::GetOCGVE(const CPDF_Array* pExpression, int nLevel) const {
188   if (nLevel > 32 || !pExpression)
189     return false;
190 
191   ByteString csOperator = pExpression->GetByteStringAt(0);
192   if (csOperator == "Not") {
193     RetainPtr<const CPDF_Object> pOCGObj = pExpression->GetDirectObjectAt(1);
194     if (!pOCGObj)
195       return false;
196     if (const CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
197       return !GetOCGVisible(pDict);
198     if (const CPDF_Array* pArray = pOCGObj->AsArray())
199       return !GetOCGVE(pArray, nLevel + 1);
200     return false;
201   }
202 
203   if (csOperator != "Or" && csOperator != "And")
204     return false;
205 
206   bool bValue = false;
207   for (size_t i = 1; i < pExpression->size(); i++) {
208     RetainPtr<const CPDF_Object> pOCGObj = pExpression->GetDirectObjectAt(i);
209     if (!pOCGObj)
210       continue;
211 
212     bool bItem = false;
213     if (const CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
214       bItem = GetOCGVisible(pDict);
215     else if (const CPDF_Array* pArray = pOCGObj->AsArray())
216       bItem = GetOCGVE(pArray, nLevel + 1);
217 
218     if (i == 1) {
219       bValue = bItem;
220     } else {
221       if (csOperator == "Or") {
222         bValue = bValue || bItem;
223       } else {
224         bValue = bValue && bItem;
225       }
226     }
227   }
228   return bValue;
229 }
230 
LoadOCMDState(const CPDF_Dictionary * pOCMDDict) const231 bool CPDF_OCContext::LoadOCMDState(const CPDF_Dictionary* pOCMDDict) const {
232   RetainPtr<const CPDF_Array> pVE = pOCMDDict->GetArrayFor("VE");
233   if (pVE) {
234     return GetOCGVE(pVE.Get(), 0);
235   }
236 
237   ByteString csP = pOCMDDict->GetByteStringFor("P", "AnyOn");
238   RetainPtr<const CPDF_Object> pOCGObj = pOCMDDict->GetDirectObjectFor("OCGs");
239   if (!pOCGObj)
240     return true;
241 
242   if (const CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
243     return GetOCGVisible(pDict);
244 
245   const CPDF_Array* pArray = pOCGObj->AsArray();
246   if (!pArray)
247     return true;
248 
249   bool bState = (csP == "AllOn" || csP == "AllOff");
250   // At least one entry of OCGs needs to be a valid dictionary for it to be
251   // considered present. See "OCGs" in table 4.49 in the PDF 1.7 spec.
252   bool bValidEntrySeen = false;
253   for (size_t i = 0; i < pArray->size(); i++) {
254     bool bItem = true;
255     RetainPtr<const CPDF_Dictionary> pItemDict = pArray->GetDictAt(i);
256     if (!pItemDict)
257       continue;
258 
259     bValidEntrySeen = true;
260     bItem = GetOCGVisible(pItemDict.Get());
261 
262     if ((csP == "AnyOn" && bItem) || (csP == "AnyOff" && !bItem))
263       return true;
264     if ((csP == "AllOn" && !bItem) || (csP == "AllOff" && bItem))
265       return false;
266   }
267 
268   return !bValidEntrySeen || bState;
269 }
270 
CheckOCGDictVisible(const CPDF_Dictionary * pOCGDict) const271 bool CPDF_OCContext::CheckOCGDictVisible(
272     const CPDF_Dictionary* pOCGDict) const {
273   if (!pOCGDict)
274     return true;
275 
276   ByteString csType = pOCGDict->GetByteStringFor("Type", "OCG");
277   if (csType == "OCG")
278     return GetOCGVisible(pOCGDict);
279   return LoadOCMDState(pOCGDict);
280 }
281