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"
8static 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}
28static 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}
49static 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}
82static 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}
94CPDF_OCContext::CPDF_OCContext(CPDF_Document *pDoc, UsageType eUsageType)
95{
96    FXSYS_assert(pDoc != NULL);
97    m_pDocument = pDoc;
98    m_eUsageType = eUsageType;
99}
100CPDF_OCContext::~CPDF_OCContext()
101{
102    m_OCGStates.RemoveAll();
103}
104FX_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}
152FX_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}
177FX_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}
190FX_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}
241FX_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}
285FX_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}
297void CPDF_OCContext::ResetOCContext()
298{
299    m_OCGStates.RemoveAll();
300}
301