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