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_name.h"
13 #include "core/fpdfapi/parser/cpdf_number.h"
14 #include "core/fpdfapi/parser/cpdf_reference.h"
15 #include "core/fpdfapi/parser/cpdf_stream.h"
16 #include "core/fpdfapi/parser/cpdf_string.h"
17 #include "core/fxcrt/fx_stream.h"
18 #include "third_party/base/logging.h"
19 #include "third_party/base/stl_util.h"
20 
CPDF_Array()21 CPDF_Array::CPDF_Array() {}
22 
CPDF_Array(const WeakPtr<ByteStringPool> & pPool)23 CPDF_Array::CPDF_Array(const WeakPtr<ByteStringPool>& pPool) : m_pPool(pPool) {}
24 
~CPDF_Array()25 CPDF_Array::~CPDF_Array() {
26   // Break cycles for cyclic references.
27   m_ObjNum = kInvalidObjNum;
28   for (auto& it : m_Objects) {
29     if (it && it->GetObjNum() == kInvalidObjNum)
30       it.release();
31   }
32 }
33 
GetType() const34 CPDF_Object::Type CPDF_Array::GetType() const {
35   return ARRAY;
36 }
37 
IsArray() const38 bool CPDF_Array::IsArray() const {
39   return true;
40 }
41 
AsArray()42 CPDF_Array* CPDF_Array::AsArray() {
43   return this;
44 }
45 
AsArray() const46 const CPDF_Array* CPDF_Array::AsArray() const {
47   return this;
48 }
49 
Clone() const50 std::unique_ptr<CPDF_Object> CPDF_Array::Clone() const {
51   return CloneObjectNonCyclic(false);
52 }
53 
CloneNonCyclic(bool bDirect,std::set<const CPDF_Object * > * pVisited) const54 std::unique_ptr<CPDF_Object> CPDF_Array::CloneNonCyclic(
55     bool bDirect,
56     std::set<const CPDF_Object*>* pVisited) const {
57   pVisited->insert(this);
58   auto pCopy = pdfium::MakeUnique<CPDF_Array>();
59   for (const auto& pValue : m_Objects) {
60     if (!pdfium::ContainsKey(*pVisited, pValue.get())) {
61       std::set<const CPDF_Object*> visited(*pVisited);
62       if (auto obj = pValue->CloneNonCyclic(bDirect, &visited))
63         pCopy->m_Objects.push_back(std::move(obj));
64     }
65   }
66   return std::move(pCopy);
67 }
68 
GetRect()69 CFX_FloatRect CPDF_Array::GetRect() {
70   CFX_FloatRect rect;
71   if (!IsArray() || m_Objects.size() != 4)
72     return rect;
73 
74   rect.left = GetNumberAt(0);
75   rect.bottom = GetNumberAt(1);
76   rect.right = GetNumberAt(2);
77   rect.top = GetNumberAt(3);
78   return rect;
79 }
80 
GetMatrix()81 CFX_Matrix CPDF_Array::GetMatrix() {
82   CFX_Matrix matrix;
83   if (!IsArray() || m_Objects.size() != 6)
84     return CFX_Matrix();
85 
86   return CFX_Matrix(GetNumberAt(0), GetNumberAt(1), GetNumberAt(2),
87                     GetNumberAt(3), GetNumberAt(4), GetNumberAt(5));
88 }
89 
GetObjectAt(size_t i) const90 CPDF_Object* CPDF_Array::GetObjectAt(size_t i) const {
91   if (i >= m_Objects.size())
92     return nullptr;
93   return m_Objects[i].get();
94 }
95 
GetDirectObjectAt(size_t i) const96 CPDF_Object* CPDF_Array::GetDirectObjectAt(size_t i) const {
97   if (i >= m_Objects.size())
98     return nullptr;
99   return m_Objects[i]->GetDirect();
100 }
101 
GetStringAt(size_t i) const102 ByteString CPDF_Array::GetStringAt(size_t i) const {
103   if (i >= m_Objects.size())
104     return ByteString();
105   return m_Objects[i]->GetString();
106 }
107 
GetUnicodeTextAt(size_t i) const108 WideString CPDF_Array::GetUnicodeTextAt(size_t i) const {
109   if (i >= m_Objects.size())
110     return WideString();
111   return m_Objects[i]->GetUnicodeText();
112 }
113 
GetIntegerAt(size_t i) const114 int CPDF_Array::GetIntegerAt(size_t i) const {
115   if (i >= m_Objects.size())
116     return 0;
117   return m_Objects[i]->GetInteger();
118 }
119 
GetNumberAt(size_t i) const120 float CPDF_Array::GetNumberAt(size_t i) const {
121   if (i >= m_Objects.size())
122     return 0;
123   return m_Objects[i]->GetNumber();
124 }
125 
GetDictAt(size_t i) const126 CPDF_Dictionary* CPDF_Array::GetDictAt(size_t i) const {
127   CPDF_Object* p = GetDirectObjectAt(i);
128   if (!p)
129     return nullptr;
130   if (CPDF_Dictionary* pDict = p->AsDictionary())
131     return pDict;
132   if (CPDF_Stream* pStream = p->AsStream())
133     return pStream->GetDict();
134   return nullptr;
135 }
136 
GetStreamAt(size_t i) const137 CPDF_Stream* CPDF_Array::GetStreamAt(size_t i) const {
138   return ToStream(GetDirectObjectAt(i));
139 }
140 
GetArrayAt(size_t i) const141 CPDF_Array* CPDF_Array::GetArrayAt(size_t i) const {
142   return ToArray(GetDirectObjectAt(i));
143 }
144 
Clear()145 void CPDF_Array::Clear() {
146   m_Objects.clear();
147 }
148 
RemoveAt(size_t i)149 void CPDF_Array::RemoveAt(size_t i) {
150   if (i < m_Objects.size())
151     m_Objects.erase(m_Objects.begin() + i);
152 }
153 
ConvertToIndirectObjectAt(size_t i,CPDF_IndirectObjectHolder * pHolder)154 void CPDF_Array::ConvertToIndirectObjectAt(size_t i,
155                                            CPDF_IndirectObjectHolder* pHolder) {
156   if (i >= m_Objects.size())
157     return;
158 
159   if (!m_Objects[i] || m_Objects[i]->IsReference())
160     return;
161 
162   CPDF_Object* pNew = pHolder->AddIndirectObject(std::move(m_Objects[i]));
163   m_Objects[i] = pdfium::MakeUnique<CPDF_Reference>(pHolder, pNew->GetObjNum());
164 }
165 
SetAt(size_t i,std::unique_ptr<CPDF_Object> pObj)166 CPDF_Object* CPDF_Array::SetAt(size_t i, std::unique_ptr<CPDF_Object> pObj) {
167   ASSERT(IsArray());
168   ASSERT(!pObj || pObj->IsInline());
169   if (i >= m_Objects.size()) {
170     NOTREACHED();
171     return nullptr;
172   }
173   CPDF_Object* pRet = pObj.get();
174   m_Objects[i] = std::move(pObj);
175   return pRet;
176 }
177 
InsertAt(size_t index,std::unique_ptr<CPDF_Object> pObj)178 CPDF_Object* CPDF_Array::InsertAt(size_t index,
179                                   std::unique_ptr<CPDF_Object> pObj) {
180   ASSERT(IsArray());
181   CHECK(!pObj || pObj->IsInline());
182   CPDF_Object* pRet = pObj.get();
183   if (index >= m_Objects.size()) {
184     // Allocate space first.
185     m_Objects.resize(index + 1);
186     m_Objects[index] = std::move(pObj);
187   } else {
188     // Directly insert.
189     m_Objects.insert(m_Objects.begin() + index, std::move(pObj));
190   }
191   return pRet;
192 }
193 
Add(std::unique_ptr<CPDF_Object> pObj)194 CPDF_Object* CPDF_Array::Add(std::unique_ptr<CPDF_Object> pObj) {
195   ASSERT(IsArray());
196   CHECK(!pObj || pObj->IsInline());
197   CPDF_Object* pRet = pObj.get();
198   m_Objects.push_back(std::move(pObj));
199   return pRet;
200 }
201 
WriteTo(IFX_ArchiveStream * archive) const202 bool CPDF_Array::WriteTo(IFX_ArchiveStream* archive) const {
203   if (!archive->WriteString("["))
204     return false;
205 
206   for (size_t i = 0; i < GetCount(); ++i) {
207     CPDF_Object* pElement = GetObjectAt(i);
208     if (!pElement->IsInline()) {
209       if (!archive->WriteString(" ") ||
210           !archive->WriteDWord(pElement->GetObjNum()) ||
211           !archive->WriteString(" 0 R")) {
212         return false;
213       }
214     } else if (!pElement->WriteTo(archive)) {
215       return false;
216     }
217   }
218   return archive->WriteString("]");
219 }
220