1 // Copyright 2016 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/fpdfapi/parser/cpdf_array.h"
8 
9 #include <set>
10 #include <utility>
11 
12 #include "core/fpdfapi/parser/cpdf_boolean.h"
13 #include "core/fpdfapi/parser/cpdf_name.h"
14 #include "core/fpdfapi/parser/cpdf_number.h"
15 #include "core/fpdfapi/parser/cpdf_reference.h"
16 #include "core/fpdfapi/parser/cpdf_stream.h"
17 #include "core/fpdfapi/parser/cpdf_string.h"
18 #include "core/fxcrt/fx_stream.h"
19 #include "third_party/base/logging.h"
20 #include "third_party/base/ptr_util.h"
21 #include "third_party/base/stl_util.h"
22 
23 CPDF_Array::CPDF_Array() = default;
24 
CPDF_Array(const WeakPtr<ByteStringPool> & pPool)25 CPDF_Array::CPDF_Array(const WeakPtr<ByteStringPool>& pPool) : m_pPool(pPool) {}
26 
~CPDF_Array()27 CPDF_Array::~CPDF_Array() {
28   // Break cycles for cyclic references.
29   m_ObjNum = kInvalidObjNum;
30   for (auto& it : m_Objects) {
31     if (it && it->GetObjNum() == kInvalidObjNum)
32       it.Leak();
33   }
34 }
35 
GetType() const36 CPDF_Object::Type CPDF_Array::GetType() const {
37   return kArray;
38 }
39 
IsArray() const40 bool CPDF_Array::IsArray() const {
41   return true;
42 }
43 
AsArray()44 CPDF_Array* CPDF_Array::AsArray() {
45   return this;
46 }
47 
AsArray() const48 const CPDF_Array* CPDF_Array::AsArray() const {
49   return this;
50 }
51 
Clone() const52 RetainPtr<CPDF_Object> CPDF_Array::Clone() const {
53   return CloneObjectNonCyclic(false);
54 }
55 
CloneNonCyclic(bool bDirect,std::set<const CPDF_Object * > * pVisited) const56 RetainPtr<CPDF_Object> CPDF_Array::CloneNonCyclic(
57     bool bDirect,
58     std::set<const CPDF_Object*>* pVisited) const {
59   pVisited->insert(this);
60   auto pCopy = pdfium::MakeRetain<CPDF_Array>();
61   for (const auto& pValue : m_Objects) {
62     if (!pdfium::ContainsKey(*pVisited, pValue.Get())) {
63       std::set<const CPDF_Object*> visited(*pVisited);
64       if (auto obj = pValue->CloneNonCyclic(bDirect, &visited))
65         pCopy->m_Objects.push_back(std::move(obj));
66     }
67   }
68   return pCopy;
69 }
70 
GetRect() const71 CFX_FloatRect CPDF_Array::GetRect() const {
72   CFX_FloatRect rect;
73   if (m_Objects.size() != 4)
74     return rect;
75 
76   rect.left = GetNumberAt(0);
77   rect.bottom = GetNumberAt(1);
78   rect.right = GetNumberAt(2);
79   rect.top = GetNumberAt(3);
80   return rect;
81 }
82 
GetMatrix() const83 CFX_Matrix CPDF_Array::GetMatrix() const {
84   if (m_Objects.size() != 6)
85     return CFX_Matrix();
86 
87   return CFX_Matrix(GetNumberAt(0), GetNumberAt(1), GetNumberAt(2),
88                     GetNumberAt(3), GetNumberAt(4), GetNumberAt(5));
89 }
90 
GetObjectAt(size_t index)91 CPDF_Object* CPDF_Array::GetObjectAt(size_t index) {
92   if (index >= m_Objects.size())
93     return nullptr;
94   return m_Objects[index].Get();
95 }
96 
GetObjectAt(size_t index) const97 const CPDF_Object* CPDF_Array::GetObjectAt(size_t index) const {
98   if (index >= m_Objects.size())
99     return nullptr;
100   return m_Objects[index].Get();
101 }
102 
GetDirectObjectAt(size_t index)103 CPDF_Object* CPDF_Array::GetDirectObjectAt(size_t index) {
104   if (index >= m_Objects.size())
105     return nullptr;
106   return m_Objects[index]->GetDirect();
107 }
108 
GetDirectObjectAt(size_t index) const109 const CPDF_Object* CPDF_Array::GetDirectObjectAt(size_t index) const {
110   if (index >= m_Objects.size())
111     return nullptr;
112   return m_Objects[index]->GetDirect();
113 }
114 
GetStringAt(size_t index) const115 ByteString CPDF_Array::GetStringAt(size_t index) const {
116   if (index >= m_Objects.size())
117     return ByteString();
118   return m_Objects[index]->GetString();
119 }
120 
GetUnicodeTextAt(size_t index) const121 WideString CPDF_Array::GetUnicodeTextAt(size_t index) const {
122   if (index >= m_Objects.size())
123     return WideString();
124   return m_Objects[index]->GetUnicodeText();
125 }
126 
GetBooleanAt(size_t index,bool bDefault) const127 bool CPDF_Array::GetBooleanAt(size_t index, bool bDefault) const {
128   if (index >= m_Objects.size())
129     return bDefault;
130   const CPDF_Object* p = m_Objects[index].Get();
131   return ToBoolean(p) ? p->GetInteger() != 0 : bDefault;
132 }
133 
GetIntegerAt(size_t index) const134 int CPDF_Array::GetIntegerAt(size_t index) const {
135   if (index >= m_Objects.size())
136     return 0;
137   return m_Objects[index]->GetInteger();
138 }
139 
GetNumberAt(size_t index) const140 float CPDF_Array::GetNumberAt(size_t index) const {
141   if (index >= m_Objects.size())
142     return 0;
143   return m_Objects[index]->GetNumber();
144 }
145 
GetDictAt(size_t index)146 CPDF_Dictionary* CPDF_Array::GetDictAt(size_t index) {
147   CPDF_Object* p = GetDirectObjectAt(index);
148   if (!p)
149     return nullptr;
150   if (CPDF_Dictionary* pDict = p->AsDictionary())
151     return pDict;
152   if (CPDF_Stream* pStream = p->AsStream())
153     return pStream->GetDict();
154   return nullptr;
155 }
156 
GetDictAt(size_t index) const157 const CPDF_Dictionary* CPDF_Array::GetDictAt(size_t index) const {
158   const CPDF_Object* p = GetDirectObjectAt(index);
159   if (!p)
160     return nullptr;
161   if (const CPDF_Dictionary* pDict = p->AsDictionary())
162     return pDict;
163   if (const CPDF_Stream* pStream = p->AsStream())
164     return pStream->GetDict();
165   return nullptr;
166 }
167 
GetStreamAt(size_t index)168 CPDF_Stream* CPDF_Array::GetStreamAt(size_t index) {
169   return ToStream(GetDirectObjectAt(index));
170 }
171 
GetStreamAt(size_t index) const172 const CPDF_Stream* CPDF_Array::GetStreamAt(size_t index) const {
173   return ToStream(GetDirectObjectAt(index));
174 }
175 
GetArrayAt(size_t index)176 CPDF_Array* CPDF_Array::GetArrayAt(size_t index) {
177   return ToArray(GetDirectObjectAt(index));
178 }
179 
GetArrayAt(size_t index) const180 const CPDF_Array* CPDF_Array::GetArrayAt(size_t index) const {
181   return ToArray(GetDirectObjectAt(index));
182 }
183 
Clear()184 void CPDF_Array::Clear() {
185   CHECK(!IsLocked());
186   m_Objects.clear();
187 }
188 
RemoveAt(size_t index)189 void CPDF_Array::RemoveAt(size_t index) {
190   CHECK(!IsLocked());
191   if (index < m_Objects.size())
192     m_Objects.erase(m_Objects.begin() + index);
193 }
194 
ConvertToIndirectObjectAt(size_t index,CPDF_IndirectObjectHolder * pHolder)195 void CPDF_Array::ConvertToIndirectObjectAt(size_t index,
196                                            CPDF_IndirectObjectHolder* pHolder) {
197   CHECK(!IsLocked());
198   if (index >= m_Objects.size())
199     return;
200 
201   if (!m_Objects[index] || m_Objects[index]->IsReference())
202     return;
203 
204   CPDF_Object* pNew = pHolder->AddIndirectObject(std::move(m_Objects[index]));
205   m_Objects[index] = pNew->MakeReference(pHolder);
206 }
207 
SetAt(size_t index,RetainPtr<CPDF_Object> pObj)208 CPDF_Object* CPDF_Array::SetAt(size_t index, RetainPtr<CPDF_Object> pObj) {
209   CHECK(!IsLocked());
210   ASSERT(!pObj || pObj->IsInline());
211   if (index >= m_Objects.size()) {
212     NOTREACHED();
213     return nullptr;
214   }
215   CPDF_Object* pRet = pObj.Get();
216   m_Objects[index] = std::move(pObj);
217   return pRet;
218 }
219 
InsertAt(size_t index,RetainPtr<CPDF_Object> pObj)220 CPDF_Object* CPDF_Array::InsertAt(size_t index, RetainPtr<CPDF_Object> pObj) {
221   CHECK(!IsLocked());
222   CHECK(!pObj || pObj->IsInline());
223   CPDF_Object* pRet = pObj.Get();
224   if (index >= m_Objects.size()) {
225     // Allocate space first.
226     m_Objects.resize(index + 1);
227     m_Objects[index] = std::move(pObj);
228   } else {
229     // Directly insert.
230     m_Objects.insert(m_Objects.begin() + index, std::move(pObj));
231   }
232   return pRet;
233 }
234 
Add(RetainPtr<CPDF_Object> pObj)235 CPDF_Object* CPDF_Array::Add(RetainPtr<CPDF_Object> pObj) {
236   CHECK(!IsLocked());
237   CHECK(!pObj || pObj->IsInline());
238   CPDF_Object* pRet = pObj.Get();
239   m_Objects.push_back(std::move(pObj));
240   return pRet;
241 }
242 
WriteTo(IFX_ArchiveStream * archive,const CPDF_Encryptor * encryptor) const243 bool CPDF_Array::WriteTo(IFX_ArchiveStream* archive,
244                          const CPDF_Encryptor* encryptor) const {
245   if (!archive->WriteString("["))
246     return false;
247 
248   for (size_t i = 0; i < size(); ++i) {
249     if (!GetObjectAt(i)->WriteTo(archive, encryptor))
250       return false;
251   }
252   return archive->WriteString("]");
253 }
254 
CPDF_ArrayLocker(const CPDF_Array * pArray)255 CPDF_ArrayLocker::CPDF_ArrayLocker(const CPDF_Array* pArray)
256     : m_pArray(pArray) {
257   m_pArray->m_LockCount++;
258 }
259 
~CPDF_ArrayLocker()260 CPDF_ArrayLocker::~CPDF_ArrayLocker() {
261   m_pArray->m_LockCount--;
262 }
263