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