Home | History | Annotate | Download | only in fpdfdoc
      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