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