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