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