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_dictionary.h"
8
9#include <set>
10#include <utility>
11
12#include "core/fpdfapi/parser/cpdf_array.h"
13#include "core/fpdfapi/parser/cpdf_boolean.h"
14#include "core/fpdfapi/parser/cpdf_crypto_handler.h"
15#include "core/fpdfapi/parser/cpdf_name.h"
16#include "core/fpdfapi/parser/cpdf_number.h"
17#include "core/fpdfapi/parser/cpdf_reference.h"
18#include "core/fpdfapi/parser/cpdf_stream.h"
19#include "core/fpdfapi/parser/cpdf_string.h"
20#include "core/fpdfapi/parser/fpdf_parser_decode.h"
21#include "core/fxcrt/fx_stream.h"
22#include "third_party/base/logging.h"
23#include "third_party/base/stl_util.h"
24
25CPDF_Dictionary::CPDF_Dictionary()
26    : CPDF_Dictionary(WeakPtr<ByteStringPool>()) {}
27
28CPDF_Dictionary::CPDF_Dictionary(const WeakPtr<ByteStringPool>& pPool)
29    : m_pPool(pPool) {}
30
31CPDF_Dictionary::~CPDF_Dictionary() {
32  // Mark the object as deleted so that it will not be deleted again,
33  // and break cyclic references.
34  m_ObjNum = kInvalidObjNum;
35  for (auto& it : m_Map) {
36    if (it.second && it.second->GetObjNum() == kInvalidObjNum)
37      it.second.release();
38  }
39}
40
41CPDF_Object::Type CPDF_Dictionary::GetType() const {
42  return DICTIONARY;
43}
44
45CPDF_Dictionary* CPDF_Dictionary::GetDict() const {
46  // The method should be made non-const if we want to not be const.
47  // See bug #234.
48  return const_cast<CPDF_Dictionary*>(this);
49}
50
51bool CPDF_Dictionary::IsDictionary() const {
52  return true;
53}
54
55CPDF_Dictionary* CPDF_Dictionary::AsDictionary() {
56  return this;
57}
58
59const CPDF_Dictionary* CPDF_Dictionary::AsDictionary() const {
60  return this;
61}
62
63std::unique_ptr<CPDF_Object> CPDF_Dictionary::Clone() const {
64  return CloneObjectNonCyclic(false);
65}
66
67std::unique_ptr<CPDF_Object> CPDF_Dictionary::CloneNonCyclic(
68    bool bDirect,
69    std::set<const CPDF_Object*>* pVisited) const {
70  pVisited->insert(this);
71  auto pCopy = pdfium::MakeUnique<CPDF_Dictionary>(m_pPool);
72  for (const auto& it : *this) {
73    if (!pdfium::ContainsKey(*pVisited, it.second.get())) {
74      std::set<const CPDF_Object*> visited(*pVisited);
75      if (auto obj = it.second->CloneNonCyclic(bDirect, &visited))
76        pCopy->m_Map.insert(std::make_pair(it.first, std::move(obj)));
77    }
78  }
79  return std::move(pCopy);
80}
81
82CPDF_Object* CPDF_Dictionary::GetObjectFor(const ByteString& key) const {
83  auto it = m_Map.find(key);
84  return it != m_Map.end() ? it->second.get() : nullptr;
85}
86
87CPDF_Object* CPDF_Dictionary::GetDirectObjectFor(const ByteString& key) const {
88  CPDF_Object* p = GetObjectFor(key);
89  return p ? p->GetDirect() : nullptr;
90}
91
92ByteString CPDF_Dictionary::GetStringFor(const ByteString& key) const {
93  CPDF_Object* p = GetObjectFor(key);
94  return p ? p->GetString() : ByteString();
95}
96
97WideString CPDF_Dictionary::GetUnicodeTextFor(const ByteString& key) const {
98  CPDF_Object* p = GetObjectFor(key);
99  if (CPDF_Reference* pRef = ToReference(p))
100    p = pRef->GetDirect();
101  return p ? p->GetUnicodeText() : WideString();
102}
103
104ByteString CPDF_Dictionary::GetStringFor(const ByteString& key,
105                                         const ByteString& def) const {
106  CPDF_Object* p = GetObjectFor(key);
107  return p ? p->GetString() : ByteString(def);
108}
109
110int CPDF_Dictionary::GetIntegerFor(const ByteString& key) const {
111  CPDF_Object* p = GetObjectFor(key);
112  return p ? p->GetInteger() : 0;
113}
114
115int CPDF_Dictionary::GetIntegerFor(const ByteString& key, int def) const {
116  CPDF_Object* p = GetObjectFor(key);
117  return p ? p->GetInteger() : def;
118}
119
120float CPDF_Dictionary::GetNumberFor(const ByteString& key) const {
121  CPDF_Object* p = GetObjectFor(key);
122  return p ? p->GetNumber() : 0;
123}
124
125bool CPDF_Dictionary::GetBooleanFor(const ByteString& key,
126                                    bool bDefault) const {
127  CPDF_Object* p = GetObjectFor(key);
128  return ToBoolean(p) ? p->GetInteger() != 0 : bDefault;
129}
130
131CPDF_Dictionary* CPDF_Dictionary::GetDictFor(const ByteString& key) const {
132  CPDF_Object* p = GetDirectObjectFor(key);
133  if (!p)
134    return nullptr;
135  if (CPDF_Dictionary* pDict = p->AsDictionary())
136    return pDict;
137  if (CPDF_Stream* pStream = p->AsStream())
138    return pStream->GetDict();
139  return nullptr;
140}
141
142CPDF_Array* CPDF_Dictionary::GetArrayFor(const ByteString& key) const {
143  return ToArray(GetDirectObjectFor(key));
144}
145
146CPDF_Stream* CPDF_Dictionary::GetStreamFor(const ByteString& key) const {
147  return ToStream(GetDirectObjectFor(key));
148}
149
150CFX_FloatRect CPDF_Dictionary::GetRectFor(const ByteString& key) const {
151  CFX_FloatRect rect;
152  CPDF_Array* pArray = GetArrayFor(key);
153  if (pArray)
154    rect = pArray->GetRect();
155  return rect;
156}
157
158CFX_Matrix CPDF_Dictionary::GetMatrixFor(const ByteString& key) const {
159  CFX_Matrix matrix;
160  CPDF_Array* pArray = GetArrayFor(key);
161  if (pArray)
162    matrix = pArray->GetMatrix();
163  return matrix;
164}
165
166bool CPDF_Dictionary::KeyExist(const ByteString& key) const {
167  return pdfium::ContainsKey(m_Map, key);
168}
169
170bool CPDF_Dictionary::IsSignatureDict() const {
171  return CPDF_CryptoHandler::IsSignatureDictionary(this);
172}
173
174CPDF_Object* CPDF_Dictionary::SetFor(const ByteString& key,
175                                     std::unique_ptr<CPDF_Object> pObj) {
176  if (!pObj) {
177    m_Map.erase(key);
178    return nullptr;
179  }
180  ASSERT(pObj->IsInline());
181  CPDF_Object* pRet = pObj.get();
182  m_Map[MaybeIntern(key)] = std::move(pObj);
183  return pRet;
184}
185
186void CPDF_Dictionary::ConvertToIndirectObjectFor(
187    const ByteString& key,
188    CPDF_IndirectObjectHolder* pHolder) {
189  auto it = m_Map.find(key);
190  if (it == m_Map.end() || it->second->IsReference())
191    return;
192
193  CPDF_Object* pObj = pHolder->AddIndirectObject(std::move(it->second));
194  it->second = pdfium::MakeUnique<CPDF_Reference>(pHolder, pObj->GetObjNum());
195}
196
197std::unique_ptr<CPDF_Object> CPDF_Dictionary::RemoveFor(const ByteString& key) {
198  std::unique_ptr<CPDF_Object> result;
199  auto it = m_Map.find(key);
200  if (it != m_Map.end()) {
201    result = std::move(it->second);
202    m_Map.erase(it);
203  }
204  return result;
205}
206
207void CPDF_Dictionary::ReplaceKey(const ByteString& oldkey,
208                                 const ByteString& newkey) {
209  auto old_it = m_Map.find(oldkey);
210  if (old_it == m_Map.end())
211    return;
212
213  auto new_it = m_Map.find(newkey);
214  if (new_it == old_it)
215    return;
216
217  m_Map[MaybeIntern(newkey)] = std::move(old_it->second);
218  m_Map.erase(old_it);
219}
220
221void CPDF_Dictionary::SetRectFor(const ByteString& key,
222                                 const CFX_FloatRect& rect) {
223  CPDF_Array* pArray = SetNewFor<CPDF_Array>(key);
224  pArray->AddNew<CPDF_Number>(rect.left);
225  pArray->AddNew<CPDF_Number>(rect.bottom);
226  pArray->AddNew<CPDF_Number>(rect.right);
227  pArray->AddNew<CPDF_Number>(rect.top);
228}
229
230void CPDF_Dictionary::SetMatrixFor(const ByteString& key,
231                                   const CFX_Matrix& matrix) {
232  CPDF_Array* pArray = SetNewFor<CPDF_Array>(key);
233  pArray->AddNew<CPDF_Number>(matrix.a);
234  pArray->AddNew<CPDF_Number>(matrix.b);
235  pArray->AddNew<CPDF_Number>(matrix.c);
236  pArray->AddNew<CPDF_Number>(matrix.d);
237  pArray->AddNew<CPDF_Number>(matrix.e);
238  pArray->AddNew<CPDF_Number>(matrix.f);
239}
240
241ByteString CPDF_Dictionary::MaybeIntern(const ByteString& str) {
242  return m_pPool ? m_pPool->Intern(str) : str;
243}
244
245bool CPDF_Dictionary::WriteTo(IFX_ArchiveStream* archive) const {
246  if (!archive->WriteString("<<"))
247    return false;
248
249  for (const auto& it : *this) {
250    const ByteString& key = it.first;
251    CPDF_Object* pValue = it.second.get();
252    if (!archive->WriteString("/") ||
253        !archive->WriteString(PDF_NameEncode(key).AsStringView())) {
254      return false;
255    }
256
257    if (!pValue->IsInline()) {
258      if (!archive->WriteString(" ") ||
259          !archive->WriteDWord(pValue->GetObjNum()) ||
260          !archive->WriteString(" 0 R")) {
261        return false;
262      }
263    } else if (!pValue->WriteTo(archive)) {
264      return false;
265    }
266  }
267  return archive->WriteString(">>");
268}
269