1 // Copyright 2016 The PDFium Authors 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 <iterator> 10 #include <utility> 11 12 #include "build/build_config.h" 13 #include "constants/stream_dict_common.h" 14 #include "core/fpdfapi/parser/cpdf_dictionary.h" 15 #include "core/fpdfapi/parser/cpdf_name.h" 16 #include "core/fpdfapi/parser/cpdf_object.h" 17 #include "core/fpdfapi/parser/cpdf_stream.h" 18 #include "core/fpdfapi/parser/cpdf_string.h" 19 #include "core/fpdfapi/parser/fpdf_parser_decode.h" 20 #include "core/fxcrt/fx_system.h" 21 #include "third_party/base/check.h" 22 #include "third_party/base/notreached.h" 23 24 namespace { 25 26 #if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) ChangeSlashToPlatform(const wchar_t * str)27WideString ChangeSlashToPlatform(const wchar_t* str) { 28 WideString result; 29 while (*str) { 30 if (*str == '/') { 31 #if BUILDFLAG(IS_APPLE) 32 result += L':'; 33 #else 34 result += L'\\'; 35 #endif 36 } else { 37 result += *str; 38 } 39 str++; 40 } 41 return result; 42 } 43 ChangeSlashToPDF(const wchar_t * str)44WideString ChangeSlashToPDF(const wchar_t* str) { 45 WideString result; 46 while (*str) { 47 if (*str == '\\' || *str == ':') 48 result += L'/'; 49 else 50 result += *str; 51 52 str++; 53 } 54 return result; 55 } 56 #endif // BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) 57 58 } // namespace 59 CPDF_FileSpec(RetainPtr<const CPDF_Object> pObj)60CPDF_FileSpec::CPDF_FileSpec(RetainPtr<const CPDF_Object> pObj) 61 : m_pObj(std::move(pObj)) { 62 DCHECK(m_pObj); 63 } 64 65 CPDF_FileSpec::~CPDF_FileSpec() = default; 66 DecodeFileName(const WideString & filepath)67WideString CPDF_FileSpec::DecodeFileName(const WideString& filepath) { 68 if (filepath.GetLength() <= 1) 69 return WideString(); 70 71 #if BUILDFLAG(IS_APPLE) 72 if (filepath.First(sizeof("/Mac") - 1) == WideStringView(L"/Mac")) 73 return ChangeSlashToPlatform(filepath.c_str() + 1); 74 return ChangeSlashToPlatform(filepath.c_str()); 75 #elif BUILDFLAG(IS_WIN) 76 77 if (filepath[0] != L'/') 78 return ChangeSlashToPlatform(filepath.c_str()); 79 if (filepath[1] == L'/') 80 return ChangeSlashToPlatform(filepath.c_str() + 1); 81 if (filepath[2] == L'/') { 82 WideString result; 83 result += filepath[1]; 84 result += L':'; 85 result += ChangeSlashToPlatform(filepath.c_str() + 2); 86 return result; 87 } 88 WideString result; 89 result += L'\\'; 90 result += ChangeSlashToPlatform(filepath.c_str()); 91 return result; 92 #else 93 return WideString(filepath); 94 #endif 95 } 96 GetFileName() const97WideString CPDF_FileSpec::GetFileName() const { 98 WideString csFileName; 99 if (const CPDF_Dictionary* pDict = m_pObj->AsDictionary()) { 100 RetainPtr<const CPDF_String> pUF = 101 ToString(pDict->GetDirectObjectFor("UF")); 102 if (pUF) 103 csFileName = pUF->GetUnicodeText(); 104 if (csFileName.IsEmpty()) { 105 RetainPtr<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->GetByteStringFor("FS") == "URL") 111 return csFileName; 112 113 if (csFileName.IsEmpty()) { 114 for (const auto* key : {"DOS", "Mac", "Unix"}) { 115 RetainPtr<const CPDF_String> pValue = 116 ToString(pDict->GetDirectObjectFor(key)); 117 if (pValue) { 118 csFileName = 119 WideString::FromDefANSI(pValue->GetString().AsStringView()); 120 break; 121 } 122 } 123 } 124 } else if (const CPDF_String* pString = m_pObj->AsString()) { 125 csFileName = WideString::FromDefANSI(pString->GetString().AsStringView()); 126 } 127 return DecodeFileName(csFileName); 128 } 129 GetFileStream() const130RetainPtr<const CPDF_Stream> CPDF_FileSpec::GetFileStream() const { 131 const CPDF_Dictionary* pDict = m_pObj->AsDictionary(); 132 if (!pDict) 133 return nullptr; 134 135 // Get the embedded files dictionary. 136 RetainPtr<const CPDF_Dictionary> pFiles = pDict->GetDictFor("EF"); 137 if (!pFiles) 138 return nullptr; 139 140 // List of keys to check for the file specification string. 141 // Follows the same precedence order as GetFileName(). 142 static constexpr const char* kKeys[] = {"UF", "F", "DOS", "Mac", "Unix"}; 143 size_t end = pDict->GetByteStringFor("FS") == "URL" ? 2 : std::size(kKeys); 144 for (size_t i = 0; i < end; ++i) { 145 ByteString key = kKeys[i]; 146 if (!pDict->GetUnicodeTextFor(key).IsEmpty()) { 147 RetainPtr<const CPDF_Stream> pStream = pFiles->GetStreamFor(key); 148 if (pStream) 149 return pStream; 150 } 151 } 152 return nullptr; 153 } 154 GetParamsDict() const155RetainPtr<const CPDF_Dictionary> CPDF_FileSpec::GetParamsDict() const { 156 RetainPtr<const CPDF_Stream> pStream = GetFileStream(); 157 if (!pStream) 158 return nullptr; 159 160 RetainPtr<const CPDF_Dictionary> pDict = pStream->GetDict(); 161 return pDict ? pDict->GetDictFor("Params") : nullptr; 162 } 163 GetMutableParamsDict()164RetainPtr<CPDF_Dictionary> CPDF_FileSpec::GetMutableParamsDict() { 165 return pdfium::WrapRetain( 166 const_cast<CPDF_Dictionary*>(GetParamsDict().Get())); 167 } 168 EncodeFileName(const WideString & filepath)169WideString CPDF_FileSpec::EncodeFileName(const WideString& filepath) { 170 if (filepath.GetLength() <= 1) 171 return WideString(); 172 173 #if BUILDFLAG(IS_WIN) 174 if (filepath[1] == L':') { 175 WideString result(L'/'); 176 result += filepath[0]; 177 if (filepath[2] != L'\\') 178 result += L'/'; 179 180 result += ChangeSlashToPDF(filepath.c_str() + 2); 181 return result; 182 } 183 if (filepath[0] == L'\\' && filepath[1] == L'\\') 184 return ChangeSlashToPDF(filepath.c_str() + 1); 185 186 if (filepath[0] == L'\\') 187 return L'/' + ChangeSlashToPDF(filepath.c_str()); 188 return ChangeSlashToPDF(filepath.c_str()); 189 #elif BUILDFLAG(IS_APPLE) 190 if (filepath.First(sizeof("Mac") - 1).EqualsASCII("Mac")) 191 return L'/' + ChangeSlashToPDF(filepath.c_str()); 192 return ChangeSlashToPDF(filepath.c_str()); 193 #else 194 return WideString(filepath); 195 #endif 196 } 197