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/fpdfdoc/cpdf_action.h"
8
9 #include "constants/stream_dict_common.h"
10 #include "core/fpdfapi/parser/cpdf_array.h"
11 #include "core/fpdfapi/parser/cpdf_dictionary.h"
12 #include "core/fpdfapi/parser/cpdf_document.h"
13 #include "core/fpdfapi/parser/cpdf_name.h"
14 #include "core/fpdfdoc/cpdf_filespec.h"
15 #include "core/fpdfdoc/cpdf_nametree.h"
16
17 namespace {
18
19 const char* const g_sATypes[] = {
20 "Unknown", "GoTo", "GoToR", "GoToE", "Launch",
21 "Thread", "URI", "Sound", "Movie", "Hide",
22 "Named", "SubmitForm", "ResetForm", "ImportData", "JavaScript",
23 "SetOCGState", "Rendition", "Trans", "GoTo3DView", nullptr};
24
25 } // namespace
26
CPDF_Action(const CPDF_Dictionary * pDict)27 CPDF_Action::CPDF_Action(const CPDF_Dictionary* pDict) : m_pDict(pDict) {}
28
29 CPDF_Action::CPDF_Action(const CPDF_Action& that) = default;
30
31 CPDF_Action::~CPDF_Action() = default;
32
GetType() const33 CPDF_Action::ActionType CPDF_Action::GetType() const {
34 if (!m_pDict)
35 return Unknown;
36
37 // Validate |m_pDict|. Type is optional, but must be valid if present.
38 const CPDF_Object* pType = m_pDict->GetObjectFor("Type");
39 if (pType) {
40 const CPDF_Name* pName = pType->AsName();
41 if (!pName || pName->GetString() != "Action")
42 return Unknown;
43 }
44
45 ByteString csType = m_pDict->GetStringFor("S");
46 if (csType.IsEmpty())
47 return Unknown;
48
49 for (int i = 0; g_sATypes[i]; ++i) {
50 if (csType == g_sATypes[i])
51 return static_cast<ActionType>(i);
52 }
53 return Unknown;
54 }
55
GetDest(CPDF_Document * pDoc) const56 CPDF_Dest CPDF_Action::GetDest(CPDF_Document* pDoc) const {
57 ActionType type = GetType();
58 if (type != GoTo && type != GoToR)
59 return CPDF_Dest();
60
61 const CPDF_Object* pDest = m_pDict->GetDirectObjectFor("D");
62 if (!pDest)
63 return CPDF_Dest();
64 if (pDest->IsString() || pDest->IsName()) {
65 CPDF_NameTree name_tree(pDoc, "Dests");
66 return CPDF_Dest(name_tree.LookupNamedDest(pDoc, pDest->GetUnicodeText()));
67 }
68 if (const CPDF_Array* pArray = pDest->AsArray())
69 return CPDF_Dest(pArray);
70
71 return CPDF_Dest();
72 }
73
GetFilePath() const74 WideString CPDF_Action::GetFilePath() const {
75 ActionType type = GetType();
76 if (type != GoToR && type != Launch && type != SubmitForm &&
77 type != ImportData) {
78 return WideString();
79 }
80
81 const CPDF_Object* pFile = m_pDict->GetDirectObjectFor(pdfium::stream::kF);
82 if (pFile)
83 return CPDF_FileSpec(pFile).GetFileName();
84
85 if (type != Launch)
86 return WideString();
87
88 const CPDF_Dictionary* pWinDict = m_pDict->GetDictFor("Win");
89 if (!pWinDict)
90 return WideString();
91
92 return WideString::FromDefANSI(
93 pWinDict->GetStringFor(pdfium::stream::kF).AsStringView());
94 }
95
GetURI(const CPDF_Document * pDoc) const96 ByteString CPDF_Action::GetURI(const CPDF_Document* pDoc) const {
97 ActionType type = GetType();
98 if (type != URI)
99 return ByteString();
100
101 ByteString csURI = m_pDict->GetStringFor("URI");
102 const CPDF_Dictionary* pRoot = pDoc->GetRoot();
103 const CPDF_Dictionary* pURI = pRoot->GetDictFor("URI");
104 if (pURI) {
105 auto result = csURI.Find(":");
106 if (!result.has_value() || result.value() == 0) {
107 auto* pBase = pURI->GetDirectObjectFor("Base");
108 if (pBase && (pBase->IsString() || pBase->IsStream()))
109 csURI = pBase->GetString() + csURI;
110 }
111 }
112 return csURI;
113 }
114
GetHideStatus() const115 bool CPDF_Action::GetHideStatus() const {
116 return m_pDict->GetBooleanFor("H", true);
117 }
118
GetNamedAction() const119 ByteString CPDF_Action::GetNamedAction() const {
120 return m_pDict->GetStringFor("N");
121 }
122
GetFlags() const123 uint32_t CPDF_Action::GetFlags() const {
124 return m_pDict->GetIntegerFor("Flags");
125 }
126
GetAllFields() const127 std::vector<const CPDF_Object*> CPDF_Action::GetAllFields() const {
128 std::vector<const CPDF_Object*> result;
129 if (!m_pDict)
130 return result;
131
132 ByteString csType = m_pDict->GetStringFor("S");
133 const CPDF_Object* pFields = csType == "Hide"
134 ? m_pDict->GetDirectObjectFor("T")
135 : m_pDict->GetArrayFor("Fields");
136 if (!pFields)
137 return result;
138
139 if (pFields->IsDictionary() || pFields->IsString()) {
140 result.push_back(pFields);
141 } else if (const CPDF_Array* pArray = pFields->AsArray()) {
142 for (size_t i = 0; i < pArray->size(); ++i) {
143 const CPDF_Object* pObj = pArray->GetDirectObjectAt(i);
144 if (pObj)
145 result.push_back(pObj);
146 }
147 }
148 return result;
149 }
150
MaybeGetJavaScript() const151 Optional<WideString> CPDF_Action::MaybeGetJavaScript() const {
152 const CPDF_Object* pObject = GetJavaScriptObject();
153 if (!pObject)
154 return pdfium::nullopt;
155 return pObject->GetUnicodeText();
156 }
157
GetJavaScript() const158 WideString CPDF_Action::GetJavaScript() const {
159 const CPDF_Object* pObject = GetJavaScriptObject();
160 return pObject ? pObject->GetUnicodeText() : WideString();
161 }
162
GetSubActionsCount() const163 size_t CPDF_Action::GetSubActionsCount() const {
164 if (!m_pDict || !m_pDict->KeyExist("Next"))
165 return 0;
166
167 const CPDF_Object* pNext = m_pDict->GetDirectObjectFor("Next");
168 if (!pNext)
169 return 0;
170 if (pNext->IsDictionary())
171 return 1;
172 const CPDF_Array* pArray = pNext->AsArray();
173 return pArray ? pArray->size() : 0;
174 }
175
GetSubAction(size_t iIndex) const176 CPDF_Action CPDF_Action::GetSubAction(size_t iIndex) const {
177 if (!m_pDict || !m_pDict->KeyExist("Next"))
178 return CPDF_Action(nullptr);
179
180 const CPDF_Object* pNext = m_pDict->GetDirectObjectFor("Next");
181 if (const CPDF_Array* pArray = ToArray(pNext))
182 return CPDF_Action(pArray->GetDictAt(iIndex));
183 if (const CPDF_Dictionary* pDict = ToDictionary(pNext)) {
184 if (iIndex == 0)
185 return CPDF_Action(pDict);
186 }
187 return CPDF_Action(nullptr);
188 }
189
GetJavaScriptObject() const190 const CPDF_Object* CPDF_Action::GetJavaScriptObject() const {
191 if (!m_pDict)
192 return nullptr;
193
194 const CPDF_Object* pJS = m_pDict->GetDirectObjectFor("JS");
195 return (pJS && (pJS->IsString() || pJS->IsStream())) ? pJS : nullptr;
196 }
197