• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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