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