1 // Copyright 2014 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/include/fpdfdoc/fpdf_doc.h"
8
FPDFDOC_OCG_FindGroup(const CPDF_Object * pObject,const CPDF_Dictionary * pGroupDict)9 static int32_t FPDFDOC_OCG_FindGroup(const CPDF_Object* pObject,
10 const CPDF_Dictionary* pGroupDict) {
11 if (!pObject || !pGroupDict)
12 return -1;
13
14 if (const CPDF_Array* pArray = pObject->AsArray()) {
15 FX_DWORD dwCount = pArray->GetCount();
16 for (FX_DWORD i = 0; i < dwCount; i++) {
17 if (pArray->GetDict(i) == pGroupDict)
18 return i;
19 }
20 return -1;
21 }
22 return pObject->GetDict() == pGroupDict ? 0 : -1;
23 }
FPDFDOC_OCG_HasIntent(const CPDF_Dictionary * pDict,const CFX_ByteStringC & csElement,const CFX_ByteStringC & csDef="")24 static FX_BOOL FPDFDOC_OCG_HasIntent(const CPDF_Dictionary* pDict,
25 const CFX_ByteStringC& csElement,
26 const CFX_ByteStringC& csDef = "") {
27 CPDF_Object* pIntent = pDict->GetElementValue("Intent");
28 if (!pIntent) {
29 return csElement == csDef;
30 }
31 CFX_ByteString bsIntent;
32 if (CPDF_Array* pArray = pIntent->AsArray()) {
33 FX_DWORD dwCount = pArray->GetCount();
34 for (FX_DWORD i = 0; i < dwCount; i++) {
35 bsIntent = pArray->GetString(i);
36 if (bsIntent == "All" || bsIntent == csElement)
37 return TRUE;
38 }
39 return FALSE;
40 }
41 bsIntent = pIntent->GetString();
42 return bsIntent == "All" || bsIntent == csElement;
43 }
FPDFDOC_OCG_GetConfig(CPDF_Document * pDoc,const CPDF_Dictionary * pOCGDict,const CFX_ByteStringC & bsState)44 static CPDF_Dictionary* FPDFDOC_OCG_GetConfig(CPDF_Document* pDoc,
45 const CPDF_Dictionary* pOCGDict,
46 const CFX_ByteStringC& bsState) {
47 FXSYS_assert(pDoc && pOCGDict);
48 CPDF_Dictionary* pOCProperties = pDoc->GetRoot()->GetDict("OCProperties");
49 if (!pOCProperties) {
50 return NULL;
51 }
52 CPDF_Array* pOCGs = pOCProperties->GetArray("OCGs");
53 if (!pOCGs) {
54 return NULL;
55 }
56 if (FPDFDOC_OCG_FindGroup(pOCGs, pOCGDict) < 0) {
57 return NULL;
58 }
59 CPDF_Dictionary* pConfig = pOCProperties->GetDict("D");
60 CPDF_Array* pConfigs = pOCProperties->GetArray("Configs");
61 if (pConfigs) {
62 CPDF_Dictionary* pFind;
63 int32_t iCount = pConfigs->GetCount();
64 for (int32_t i = 0; i < iCount; i++) {
65 pFind = pConfigs->GetDict(i);
66 if (!pFind) {
67 continue;
68 }
69 if (!FPDFDOC_OCG_HasIntent(pFind, "View", "View")) {
70 continue;
71 }
72 pConfig = pFind;
73 break;
74 }
75 }
76 return pConfig;
77 }
FPDFDOC_OCG_GetUsageTypeString(CPDF_OCContext::UsageType eType)78 static CFX_ByteString FPDFDOC_OCG_GetUsageTypeString(
79 CPDF_OCContext::UsageType eType) {
80 CFX_ByteString csState = "View";
81 if (eType == CPDF_OCContext::Design) {
82 csState = "Design";
83 } else if (eType == CPDF_OCContext::Print) {
84 csState = "Print";
85 } else if (eType == CPDF_OCContext::Export) {
86 csState = "Export";
87 }
88 return csState;
89 }
CPDF_OCContext(CPDF_Document * pDoc,UsageType eUsageType)90 CPDF_OCContext::CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType) {
91 FXSYS_assert(pDoc);
92 m_pDocument = pDoc;
93 m_eUsageType = eUsageType;
94 }
~CPDF_OCContext()95 CPDF_OCContext::~CPDF_OCContext() {
96 m_OCGStates.clear();
97 }
LoadOCGStateFromConfig(const CFX_ByteStringC & csConfig,const CPDF_Dictionary * pOCGDict,FX_BOOL & bValidConfig) const98 FX_BOOL CPDF_OCContext::LoadOCGStateFromConfig(const CFX_ByteStringC& csConfig,
99 const CPDF_Dictionary* pOCGDict,
100 FX_BOOL& bValidConfig) const {
101 CPDF_Dictionary* pConfig =
102 FPDFDOC_OCG_GetConfig(m_pDocument, pOCGDict, csConfig);
103 if (!pConfig) {
104 return TRUE;
105 }
106 bValidConfig = TRUE;
107 FX_BOOL bState = pConfig->GetString("BaseState", "ON") != "OFF";
108 CPDF_Array* pArray = pConfig->GetArray("ON");
109 if (pArray) {
110 if (FPDFDOC_OCG_FindGroup(pArray, pOCGDict) >= 0) {
111 bState = TRUE;
112 }
113 }
114 pArray = pConfig->GetArray("OFF");
115 if (pArray) {
116 if (FPDFDOC_OCG_FindGroup(pArray, pOCGDict) >= 0) {
117 bState = FALSE;
118 }
119 }
120 pArray = pConfig->GetArray("AS");
121 if (pArray) {
122 CFX_ByteString csFind = csConfig + "State";
123 int32_t iCount = pArray->GetCount();
124 for (int32_t i = 0; i < iCount; i++) {
125 CPDF_Dictionary* pUsage = pArray->GetDict(i);
126 if (!pUsage) {
127 continue;
128 }
129 if (pUsage->GetString("Event", "View") != csConfig) {
130 continue;
131 }
132 CPDF_Array* pOCGs = pUsage->GetArray("OCGs");
133 if (!pOCGs) {
134 continue;
135 }
136 if (FPDFDOC_OCG_FindGroup(pOCGs, pOCGDict) < 0) {
137 continue;
138 }
139 CPDF_Dictionary* pState = pUsage->GetDict(csConfig);
140 if (!pState) {
141 continue;
142 }
143 bState = pState->GetString(csFind) != "OFF";
144 }
145 }
146 return bState;
147 }
LoadOCGState(const CPDF_Dictionary * pOCGDict) const148 FX_BOOL CPDF_OCContext::LoadOCGState(const CPDF_Dictionary* pOCGDict) const {
149 if (!FPDFDOC_OCG_HasIntent(pOCGDict, "View", "View")) {
150 return TRUE;
151 }
152 CFX_ByteString csState = FPDFDOC_OCG_GetUsageTypeString(m_eUsageType);
153 CPDF_Dictionary* pUsage = pOCGDict->GetDict("Usage");
154 if (pUsage) {
155 CPDF_Dictionary* pState = pUsage->GetDict(csState);
156 if (pState) {
157 CFX_ByteString csFind = csState + "State";
158 if (pState->KeyExist(csFind)) {
159 return pState->GetString(csFind) != "OFF";
160 }
161 }
162 if (csState != "View") {
163 pState = pUsage->GetDict("View");
164 if (pState && pState->KeyExist("ViewState")) {
165 return pState->GetString("ViewState") != "OFF";
166 }
167 }
168 }
169 FX_BOOL bDefValid = FALSE;
170 return LoadOCGStateFromConfig(csState, pOCGDict, bDefValid);
171 }
172
GetOCGVisible(const CPDF_Dictionary * pOCGDict)173 FX_BOOL CPDF_OCContext::GetOCGVisible(const CPDF_Dictionary* pOCGDict) {
174 if (!pOCGDict)
175 return FALSE;
176
177 const auto it = m_OCGStates.find(pOCGDict);
178 if (it != m_OCGStates.end())
179 return it->second;
180
181 FX_BOOL bState = LoadOCGState(pOCGDict);
182 m_OCGStates[pOCGDict] = bState;
183 return bState;
184 }
185
GetOCGVE(CPDF_Array * pExpression,FX_BOOL bFromConfig,int nLevel)186 FX_BOOL CPDF_OCContext::GetOCGVE(CPDF_Array* pExpression,
187 FX_BOOL bFromConfig,
188 int nLevel) {
189 if (nLevel > 32) {
190 return FALSE;
191 }
192 if (!pExpression) {
193 return FALSE;
194 }
195 int32_t iCount = pExpression->GetCount();
196 CPDF_Object* pOCGObj;
197 CFX_ByteString csOperator = pExpression->GetString(0);
198 if (csOperator == "Not") {
199 pOCGObj = pExpression->GetElementValue(1);
200 if (!pOCGObj)
201 return FALSE;
202 if (CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
203 return !(bFromConfig ? LoadOCGState(pDict) : GetOCGVisible(pDict));
204 if (CPDF_Array* pArray = pOCGObj->AsArray())
205 return !GetOCGVE(pArray, bFromConfig, nLevel + 1);
206 return FALSE;
207 }
208 if (csOperator == "Or" || csOperator == "And") {
209 FX_BOOL bValue = FALSE;
210 for (int32_t i = 1; i < iCount; i++) {
211 pOCGObj = pExpression->GetElementValue(1);
212 if (!pOCGObj) {
213 continue;
214 }
215 FX_BOOL bItem = FALSE;
216 if (CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
217 bItem = bFromConfig ? LoadOCGState(pDict) : GetOCGVisible(pDict);
218 else if (CPDF_Array* pArray = pOCGObj->AsArray())
219 bItem = GetOCGVE(pArray, bFromConfig, nLevel + 1);
220
221 if (i == 1) {
222 bValue = bItem;
223 } else {
224 if (csOperator == "Or") {
225 bValue = bValue || bItem;
226 } else {
227 bValue = bValue && bItem;
228 }
229 }
230 }
231 return bValue;
232 }
233 return FALSE;
234 }
LoadOCMDState(const CPDF_Dictionary * pOCMDDict,FX_BOOL bFromConfig)235 FX_BOOL CPDF_OCContext::LoadOCMDState(const CPDF_Dictionary* pOCMDDict,
236 FX_BOOL bFromConfig) {
237 CPDF_Array* pVE = pOCMDDict->GetArray("VE");
238 if (pVE) {
239 return GetOCGVE(pVE, bFromConfig);
240 }
241 CFX_ByteString csP = pOCMDDict->GetString("P", "AnyOn");
242 CPDF_Object* pOCGObj = pOCMDDict->GetElementValue("OCGs");
243 if (!pOCGObj)
244 return TRUE;
245 if (const CPDF_Dictionary* pDict = pOCGObj->AsDictionary())
246 return bFromConfig ? LoadOCGState(pDict) : GetOCGVisible(pDict);
247
248 CPDF_Array* pArray = pOCGObj->AsArray();
249 if (!pArray)
250 return TRUE;
251
252 FX_BOOL bState = FALSE;
253 if (csP == "AllOn" || csP == "AllOff") {
254 bState = TRUE;
255 }
256 int32_t iCount = pArray->GetCount();
257 for (int32_t i = 0; i < iCount; i++) {
258 FX_BOOL bItem = TRUE;
259 CPDF_Dictionary* pItemDict = pArray->GetDict(i);
260 if (pItemDict)
261 bItem = bFromConfig ? LoadOCGState(pItemDict) : GetOCGVisible(pItemDict);
262
263 if ((csP == "AnyOn" && bItem) || (csP == "AnyOff" && !bItem))
264 return TRUE;
265 if ((csP == "AllOn" && !bItem) || (csP == "AllOff" && bItem))
266 return FALSE;
267 }
268 return bState;
269 }
CheckOCGVisible(const CPDF_Dictionary * pOCGDict)270 FX_BOOL CPDF_OCContext::CheckOCGVisible(const CPDF_Dictionary* pOCGDict) {
271 if (!pOCGDict) {
272 return TRUE;
273 }
274 CFX_ByteString csType = pOCGDict->GetString("Type", "OCG");
275 if (csType == "OCG") {
276 return GetOCGVisible(pOCGDict);
277 }
278 return LoadOCMDState(pOCGDict, FALSE);
279 }
ResetOCContext()280 void CPDF_OCContext::ResetOCContext() {
281 m_OCGStates.clear();
282 }
283