• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)29 WideString 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)45 WideString 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)60 CPDF_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)67 WideString 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() const101 WideString 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() const134 RetainPtr<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() const160 RetainPtr<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()165 RetainPtr<CPDF_Dictionary> CPDF_FileSpec::GetMutableParamsDict() {
166   return pdfium::WrapRetain(
167       const_cast<CPDF_Dictionary*>(GetParamsDict().Get()));
168 }
169 
EncodeFileName(const WideString & filepath)170 WideString 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