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_filespec.h"
8
9 #include <vector>
10
11 #include "build/build_config.h"
12 #include "constants/stream_dict_common.h"
13 #include "core/fpdfapi/parser/cpdf_dictionary.h"
14 #include "core/fpdfapi/parser/cpdf_name.h"
15 #include "core/fpdfapi/parser/cpdf_object.h"
16 #include "core/fpdfapi/parser/cpdf_stream.h"
17 #include "core/fpdfapi/parser/cpdf_string.h"
18 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
19 #include "core/fxcrt/fx_system.h"
20
21 namespace {
22
23 #if defined(OS_MACOSX) || defined(OS_WIN)
ChangeSlashToPlatform(const wchar_t * str)24 WideString ChangeSlashToPlatform(const wchar_t* str) {
25 WideString result;
26 while (*str) {
27 if (*str == '/') {
28 #if defined(OS_MACOSX)
29 result += L':';
30 #else
31 result += L'\\';
32 #endif
33 } else {
34 result += *str;
35 }
36 str++;
37 }
38 return result;
39 }
40
ChangeSlashToPDF(const wchar_t * str)41 WideString ChangeSlashToPDF(const wchar_t* str) {
42 WideString result;
43 while (*str) {
44 if (*str == '\\' || *str == ':')
45 result += L'/';
46 else
47 result += *str;
48
49 str++;
50 }
51 return result;
52 }
53 #endif // defined(OS_MACOSX) || defined(OS_WIN)
54
55 } // namespace
56
CPDF_FileSpec(const CPDF_Object * pObj)57 CPDF_FileSpec::CPDF_FileSpec(const CPDF_Object* pObj) : m_pObj(pObj) {
58 ASSERT(m_pObj);
59 }
60
CPDF_FileSpec(CPDF_Object * pObj)61 CPDF_FileSpec::CPDF_FileSpec(CPDF_Object* pObj)
62 : m_pObj(pObj), m_pWritableObj(pObj) {
63 ASSERT(m_pObj);
64 }
65
~CPDF_FileSpec()66 CPDF_FileSpec::~CPDF_FileSpec() {}
67
DecodeFileName(const WideString & filepath)68 WideString CPDF_FileSpec::DecodeFileName(const WideString& filepath) {
69 if (filepath.GetLength() <= 1)
70 return WideString();
71
72 #if defined(OS_MACOSX)
73 if (filepath.First(sizeof("/Mac") - 1) == WideStringView(L"/Mac"))
74 return ChangeSlashToPlatform(filepath.c_str() + 1);
75 return ChangeSlashToPlatform(filepath.c_str());
76 #elif defined(OS_WIN)
77
78 if (filepath[0] != L'/')
79 return ChangeSlashToPlatform(filepath.c_str());
80 if (filepath[1] == L'/')
81 return ChangeSlashToPlatform(filepath.c_str() + 1);
82 if (filepath[2] == L'/') {
83 WideString result;
84 result += filepath[1];
85 result += L':';
86 result += ChangeSlashToPlatform(filepath.c_str() + 2);
87 return result;
88 }
89 WideString result;
90 result += L'\\';
91 result += ChangeSlashToPlatform(filepath.c_str());
92 return result;
93 #else
94 return WideString(filepath);
95 #endif
96 }
97
GetFileName() const98 WideString CPDF_FileSpec::GetFileName() const {
99 WideString csFileName;
100 if (const CPDF_Dictionary* pDict = m_pObj->AsDictionary()) {
101 const CPDF_String* pUF = ToString(pDict->GetDirectObjectFor("UF"));
102 if (pUF)
103 csFileName = pUF->GetUnicodeText();
104 if (csFileName.IsEmpty()) {
105 const CPDF_String* pK =
106 ToString(pDict->GetDirectObjectFor(pdfium::stream::kF));
107 if (pK)
108 csFileName = WideString::FromDefANSI(pK->GetString().AsStringView());
109 }
110 if (pDict->GetStringFor("FS") == "URL")
111 return csFileName;
112
113 if (csFileName.IsEmpty()) {
114 for (const auto* key : {"DOS", "Mac", "Unix"}) {
115 const CPDF_String* pValue = ToString(pDict->GetDirectObjectFor(key));
116 if (pValue) {
117 csFileName =
118 WideString::FromDefANSI(pValue->GetString().AsStringView());
119 break;
120 }
121 }
122 }
123 } else if (const CPDF_String* pString = m_pObj->AsString()) {
124 csFileName = WideString::FromDefANSI(pString->GetString().AsStringView());
125 }
126 return DecodeFileName(csFileName);
127 }
128
GetFileStream() const129 const CPDF_Stream* CPDF_FileSpec::GetFileStream() const {
130 const CPDF_Dictionary* pDict = m_pObj->AsDictionary();
131 if (!pDict)
132 return nullptr;
133
134 // Get the embedded files dictionary.
135 const CPDF_Dictionary* pFiles = pDict->GetDictFor("EF");
136 if (!pFiles)
137 return nullptr;
138
139 // List of keys to check for the file specification string.
140 // Follows the same precedence order as GetFileName().
141 static constexpr const char* kKeys[] = {"UF", "F", "DOS", "Mac", "Unix"};
142 size_t end = pDict->GetStringFor("FS") == "URL" ? 2 : FX_ArraySize(kKeys);
143 for (size_t i = 0; i < end; ++i) {
144 ByteString key = kKeys[i];
145 if (!pDict->GetUnicodeTextFor(key).IsEmpty()) {
146 const CPDF_Stream* pStream = pFiles->GetStreamFor(key);
147 if (pStream)
148 return pStream;
149 }
150 }
151 return nullptr;
152 }
153
GetFileStream()154 CPDF_Stream* CPDF_FileSpec::GetFileStream() {
155 return const_cast<CPDF_Stream*>(
156 static_cast<const CPDF_FileSpec*>(this)->GetFileStream());
157 }
158
GetParamsDict() const159 const CPDF_Dictionary* CPDF_FileSpec::GetParamsDict() const {
160 const CPDF_Stream* pStream = GetFileStream();
161 if (!pStream)
162 return nullptr;
163
164 const CPDF_Dictionary* pDict = pStream->GetDict();
165 return pDict ? pDict->GetDictFor("Params") : nullptr;
166 }
167
GetParamsDict()168 CPDF_Dictionary* CPDF_FileSpec::GetParamsDict() {
169 return const_cast<CPDF_Dictionary*>(
170 static_cast<const CPDF_FileSpec*>(this)->GetParamsDict());
171 }
172
EncodeFileName(const WideString & filepath)173 WideString CPDF_FileSpec::EncodeFileName(const WideString& filepath) {
174 if (filepath.GetLength() <= 1)
175 return WideString();
176
177 #if defined(OS_WIN)
178 if (filepath[1] == L':') {
179 WideString result(L'/');
180 result += filepath[0];
181 if (filepath[2] != L'\\')
182 result += L'/';
183
184 result += ChangeSlashToPDF(filepath.c_str() + 2);
185 return result;
186 }
187 if (filepath[0] == L'\\' && filepath[1] == L'\\')
188 return ChangeSlashToPDF(filepath.c_str() + 1);
189
190 if (filepath[0] == L'\\')
191 return L'/' + ChangeSlashToPDF(filepath.c_str());
192 return ChangeSlashToPDF(filepath.c_str());
193 #elif defined(OS_MACOSX)
194 if (filepath.First(sizeof("Mac") - 1).EqualsASCII("Mac"))
195 return L'/' + ChangeSlashToPDF(filepath.c_str());
196 return ChangeSlashToPDF(filepath.c_str());
197 #else
198 return WideString(filepath);
199 #endif
200 }
201
SetFileName(const WideString & wsFileName)202 void CPDF_FileSpec::SetFileName(const WideString& wsFileName) {
203 if (!m_pWritableObj) {
204 NOTREACHED();
205 return;
206 }
207
208 WideString wsStr = EncodeFileName(wsFileName);
209 if (m_pObj->IsString()) {
210 m_pWritableObj->SetString(wsStr.ToDefANSI());
211 } else if (CPDF_Dictionary* pDict = m_pWritableObj->AsDictionary()) {
212 pDict->SetNewFor<CPDF_String>(pdfium::stream::kF, wsStr.ToDefANSI(), false);
213 pDict->SetNewFor<CPDF_String>("UF", wsStr);
214 }
215 }
216