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 "core/fpdfapi/parser/cpdf_dictionary.h"
12 #include "core/fpdfapi/parser/cpdf_name.h"
13 #include "core/fpdfapi/parser/cpdf_object.h"
14 #include "core/fpdfapi/parser/cpdf_stream.h"
15 #include "core/fpdfapi/parser/cpdf_string.h"
16 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
17 #include "core/fxcrt/fx_system.h"
18
19 namespace {
20
21 #if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_ || \
22 _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
ChangeSlashToPlatform(const wchar_t * str)23 WideString ChangeSlashToPlatform(const wchar_t* str) {
24 WideString result;
25 while (*str) {
26 if (*str == '/') {
27 #if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
28 result += L':';
29 #else
30 result += L'\\';
31 #endif
32 } else {
33 result += *str;
34 }
35 str++;
36 }
37 return result;
38 }
39
ChangeSlashToPDF(const wchar_t * str)40 WideString ChangeSlashToPDF(const wchar_t* str) {
41 WideString result;
42 while (*str) {
43 if (*str == '\\' || *str == ':')
44 result += L'/';
45 else
46 result += *str;
47
48 str++;
49 }
50 return result;
51 }
52 #endif // _FX_PLATFORM_APPLE_ || _FX_PLATFORM_WINDOWS_
53
54 } // namespace
55
CPDF_FileSpec(CPDF_Object * pObj)56 CPDF_FileSpec::CPDF_FileSpec(CPDF_Object* pObj) : m_pObj(pObj) {
57 ASSERT(m_pObj);
58 }
59
~CPDF_FileSpec()60 CPDF_FileSpec::~CPDF_FileSpec() {}
61
DecodeFileName(const WideString & filepath)62 WideString CPDF_FileSpec::DecodeFileName(const WideString& filepath) {
63 if (filepath.GetLength() <= 1)
64 return WideString();
65
66 #if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
67 if (filepath.Left(sizeof("/Mac") - 1) == WideStringView(L"/Mac"))
68 return ChangeSlashToPlatform(filepath.c_str() + 1);
69 return ChangeSlashToPlatform(filepath.c_str());
70 #elif _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
71
72 if (filepath[0] != L'/')
73 return ChangeSlashToPlatform(filepath.c_str());
74 if (filepath[1] == L'/')
75 return ChangeSlashToPlatform(filepath.c_str() + 1);
76 if (filepath[2] == L'/') {
77 WideString result;
78 result += filepath[1];
79 result += L':';
80 result += ChangeSlashToPlatform(filepath.c_str() + 2);
81 return result;
82 }
83 WideString result;
84 result += L'\\';
85 result += ChangeSlashToPlatform(filepath.c_str());
86 return result;
87 #else
88 return WideString(filepath);
89 #endif
90 }
91
GetFileName() const92 WideString CPDF_FileSpec::GetFileName() const {
93 WideString csFileName;
94 if (CPDF_Dictionary* pDict = m_pObj->AsDictionary()) {
95 csFileName = pDict->GetUnicodeTextFor("UF");
96 if (csFileName.IsEmpty()) {
97 csFileName =
98 WideString::FromLocal(pDict->GetStringFor("F").AsStringView());
99 }
100 if (pDict->GetStringFor("FS") == "URL")
101 return csFileName;
102
103 if (csFileName.IsEmpty()) {
104 constexpr const char* keys[] = {"DOS", "Mac", "Unix"};
105 for (const auto* key : keys) {
106 if (pDict->KeyExist(key)) {
107 csFileName =
108 WideString::FromLocal(pDict->GetStringFor(key).AsStringView());
109 break;
110 }
111 }
112 }
113 } else if (m_pObj->IsString()) {
114 csFileName = WideString::FromLocal(m_pObj->GetString().AsStringView());
115 }
116 return DecodeFileName(csFileName);
117 }
118
GetFileStream() const119 CPDF_Stream* CPDF_FileSpec::GetFileStream() const {
120 CPDF_Dictionary* pDict = m_pObj->AsDictionary();
121 if (!pDict)
122 return nullptr;
123
124 // Get the embedded files dictionary.
125 CPDF_Dictionary* pFiles = pDict->GetDictFor("EF");
126 if (!pFiles)
127 return nullptr;
128
129 // Get the file stream of the highest precedence with its file specification
130 // string available. Follows the same precedence order as GetFileName().
131 constexpr const char* keys[] = {"UF", "F", "DOS", "Mac", "Unix"};
132 size_t end = pDict->GetStringFor("FS") == "URL" ? 2 : FX_ArraySize(keys);
133 for (size_t i = 0; i < end; ++i) {
134 const ByteString& key = keys[i];
135 if (!pDict->GetUnicodeTextFor(key).IsEmpty()) {
136 CPDF_Stream* pStream = pFiles->GetStreamFor(key);
137 if (pStream)
138 return pStream;
139 }
140 }
141 return nullptr;
142 }
143
GetParamsDict() const144 CPDF_Dictionary* CPDF_FileSpec::GetParamsDict() const {
145 CPDF_Stream* pStream = GetFileStream();
146 if (!pStream)
147 return nullptr;
148
149 CPDF_Dictionary* pDict = pStream->GetDict();
150 if (!pDict)
151 return nullptr;
152
153 return pDict->GetDictFor("Params");
154 }
155
EncodeFileName(const WideString & filepath)156 WideString CPDF_FileSpec::EncodeFileName(const WideString& filepath) {
157 if (filepath.GetLength() <= 1)
158 return WideString();
159
160 #if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
161 if (filepath[1] == L':') {
162 WideString result(L'/');
163 result += filepath[0];
164 if (filepath[2] != L'\\')
165 result += L'/';
166
167 result += ChangeSlashToPDF(filepath.c_str() + 2);
168 return result;
169 }
170 if (filepath[0] == L'\\' && filepath[1] == L'\\')
171 return ChangeSlashToPDF(filepath.c_str() + 1);
172
173 if (filepath[0] == L'\\')
174 return L'/' + ChangeSlashToPDF(filepath.c_str());
175 return ChangeSlashToPDF(filepath.c_str());
176 #elif _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
177 if (filepath.Left(sizeof("Mac") - 1) == L"Mac")
178 return L'/' + ChangeSlashToPDF(filepath.c_str());
179 return ChangeSlashToPDF(filepath.c_str());
180 #else
181 return WideString(filepath);
182 #endif
183 }
184
SetFileName(const WideString & wsFileName)185 void CPDF_FileSpec::SetFileName(const WideString& wsFileName) {
186 WideString wsStr = EncodeFileName(wsFileName);
187 if (m_pObj->IsString()) {
188 m_pObj->SetString(ByteString::FromUnicode(wsStr));
189 } else if (CPDF_Dictionary* pDict = m_pObj->AsDictionary()) {
190 pDict->SetNewFor<CPDF_String>("F", ByteString::FromUnicode(wsStr), false);
191 pDict->SetNewFor<CPDF_String>("UF", PDF_EncodeText(wsStr), false);
192 }
193 }
194