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_ 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 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 56 CPDF_FileSpec::CPDF_FileSpec(CPDF_Object* pObj) : m_pObj(pObj) { 57 ASSERT(m_pObj); 58 } 59 60 CPDF_FileSpec::~CPDF_FileSpec() {} 61 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 92 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 119 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 144 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 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 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